1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00

acpi: move entities to the entity pkg, export them and add missing entities

This commit moves the AML entity definitions into the entity package and
makes them exportable so we can reference them from other packages.

In addition, the commit adds some missing entity structs that were
previously treated as generic entities (e.g. Processor, PowerResource
and ThermalZone).

Finally, this commit cleans the definitions and adds missing struct
attributes for the various field types (Field, IndexField, BankField)
This commit is contained in:
Achilleas Anagnostopoulos 2017-12-16 07:45:37 +00:00
parent 89923eb481
commit 44896b1680
6 changed files with 1497 additions and 679 deletions

View File

@ -1,506 +0,0 @@
package aml
import (
"gopheros/kernel/kfmt"
"io"
)
type resolver interface {
Resolve(io.Writer, ScopeEntity) bool
}
// Entity is an interface implemented by all AML entities.
type Entity interface {
Name() string
Parent() ScopeEntity
TableHandle() uint8
setTableHandle(uint8)
getOpcode() opcode
setOpcode(opcode)
setParent(ScopeEntity)
getArgs() []interface{}
setArg(uint8, interface{}) bool
}
// ScopeEntity is an interface that is implemented by entities that define an
// AML scope.
type ScopeEntity interface {
Entity
Children() []Entity
Append(Entity) bool
removeChild(Entity)
lastChild() Entity
}
// unnamedEntity defines an unnamed entity that can be attached to a parent scope.
type unnamedEntity struct {
tableHandle uint8
op opcode
args []interface{}
parent ScopeEntity
}
func (ent *unnamedEntity) getOpcode() opcode { return ent.op }
func (ent *unnamedEntity) setOpcode(op opcode) { ent.op = op }
func (ent *unnamedEntity) Name() string { return "" }
func (ent *unnamedEntity) Parent() ScopeEntity { return ent.parent }
func (ent *unnamedEntity) setParent(parent ScopeEntity) { ent.parent = parent }
func (ent *unnamedEntity) getArgs() []interface{} { return ent.args }
func (ent *unnamedEntity) setArg(_ uint8, arg interface{}) bool {
ent.args = append(ent.args, arg)
return true
}
func (ent *unnamedEntity) TableHandle() uint8 { return ent.tableHandle }
func (ent *unnamedEntity) setTableHandle(h uint8) { ent.tableHandle = h }
// namedEntity is a named entity that can be attached to the parent scope. The
// setArg() implementation for this type expects arg at index 0 to contain the
// entity name.
type namedEntity struct {
tableHandle uint8
op opcode
args []interface{}
parent ScopeEntity
name string
}
func (ent *namedEntity) getOpcode() opcode { return ent.op }
func (ent *namedEntity) setOpcode(op opcode) { ent.op = op }
func (ent *namedEntity) Name() string { return ent.name }
func (ent *namedEntity) Parent() ScopeEntity { return ent.parent }
func (ent *namedEntity) setParent(parent ScopeEntity) { ent.parent = parent }
func (ent *namedEntity) getArgs() []interface{} { return ent.args }
func (ent *namedEntity) setArg(argIndex uint8, arg interface{}) bool {
// arg 0 is the entity name
if argIndex == 0 {
var ok bool
ent.name, ok = arg.(string)
return ok
}
ent.args = append(ent.args, arg)
return true
}
func (ent *namedEntity) TableHandle() uint8 { return ent.tableHandle }
func (ent *namedEntity) setTableHandle(h uint8) { ent.tableHandle = h }
// constEntity is an optionally-named entity which always
// evaluates to a constant value. Calls to setArg for
// argument index 0 will memoize the argument value that is
// stored inside this entity.
type constEntity struct {
name string
tableHandle uint8
op opcode
args []interface{}
parent ScopeEntity
val interface{}
}
func (ent *constEntity) getOpcode() opcode { return ent.op }
func (ent *constEntity) setOpcode(op opcode) {
ent.op = op
// special const opcode cases that have an implicit value
switch ent.op {
case opZero:
ent.val = uint64(0)
case opOne:
ent.val = uint64(1)
case opOnes:
ent.val = uint64(1<<64 - 1)
}
}
func (ent *constEntity) Name() string { return ent.name }
func (ent *constEntity) Parent() ScopeEntity { return ent.parent }
func (ent *constEntity) setParent(parent ScopeEntity) { ent.parent = parent }
func (ent *constEntity) getArgs() []interface{} { return ent.args }
func (ent *constEntity) setArg(argIndex uint8, arg interface{}) bool {
ent.val = arg
return argIndex == 0
}
func (ent *constEntity) TableHandle() uint8 { return ent.tableHandle }
func (ent *constEntity) setTableHandle(h uint8) { ent.tableHandle = h }
// scopeEntity is an optionally named entity that defines a scope.
type scopeEntity struct {
tableHandle uint8
op opcode
args []interface{}
parent ScopeEntity
name string
children []Entity
}
func (ent *scopeEntity) getOpcode() opcode { return ent.op }
func (ent *scopeEntity) setOpcode(op opcode) { ent.op = op }
func (ent *scopeEntity) Name() string { return ent.name }
func (ent *scopeEntity) Parent() ScopeEntity { return ent.parent }
func (ent *scopeEntity) setParent(parent ScopeEntity) { ent.parent = parent }
func (ent *scopeEntity) getArgs() []interface{} { return ent.args }
func (ent *scopeEntity) setArg(argIndex uint8, arg interface{}) bool {
// arg 0 *may* be the entity name. If it's not a string just add it to
// the arg list.
if argIndex == 0 {
var ok bool
if ent.name, ok = arg.(string); ok {
return true
}
}
ent.args = append(ent.args, arg)
return true
}
func (ent *scopeEntity) Children() []Entity { return ent.children }
func (ent *scopeEntity) Append(child Entity) bool {
child.setParent(ent)
ent.children = append(ent.children, child)
return true
}
func (ent *scopeEntity) lastChild() Entity { return ent.children[len(ent.children)-1] }
func (ent *scopeEntity) removeChild(child Entity) {
for index := 0; index < len(ent.children); index++ {
if ent.children[index] == child {
ent.children = append(ent.children[:index], ent.children[index+1:]...)
return
}
}
}
func (ent *scopeEntity) TableHandle() uint8 { return ent.tableHandle }
func (ent *scopeEntity) setTableHandle(h uint8) { ent.tableHandle = h }
// bufferEntity defines a buffer object.
type bufferEntity struct {
unnamedEntity
size interface{}
data []byte
}
func (ent *bufferEntity) setArg(argIndex uint8, arg interface{}) bool {
switch argIndex {
case 0: // size
ent.size = arg
return true
case 1: // data
if byteSlice, ok := arg.([]byte); ok {
ent.data = byteSlice
return true
}
}
return false
}
// bufferFieldEntity describes a bit/byte/word/dword/qword or arbitrary length
// buffer field.
type bufferFieldEntity struct {
namedEntity
}
func (ent *bufferFieldEntity) setArg(argIndex uint8, arg interface{}) bool {
// opCreateField specifies the name using the arg at index 3 while
// opCreateXXXField (byte, word e.t.c) specifies the name using the
// arg at index 2
if (ent.op == opCreateField && argIndex == 3) || argIndex == 2 {
var ok bool
ent.name, ok = arg.(string)
return ok
}
ent.args = append(ent.args, arg)
return true
}
// RegionSpace describes the memory space where a region is located.
type RegionSpace uint8
// The list of supported RegionSpace values.
const (
RegionSpaceSystemMemory RegionSpace = iota
RegionSpaceSystemIO
RegionSpacePCIConfig
RegionSpaceEmbeddedControl
RegionSpaceSMBus
RegionSpacePCIBarTarget
RegionSpaceIPMI
)
// regionEntity defines a region located at a particular space (e.g in memory,
// an embedded controller, the SMBus e.t.c).
type regionEntity struct {
namedEntity
space RegionSpace
}
func (ent *regionEntity) setArg(argIndex uint8, arg interface{}) bool {
var ok bool
switch argIndex {
case 0:
ok = ent.namedEntity.setArg(argIndex, arg)
case 1:
// the parser will convert ByteData types to uint64
var space uint64
space, ok = arg.(uint64)
ent.space = RegionSpace(space)
case 2, 3:
ent.args = append(ent.args, arg)
ok = true
}
return ok
}
// FieldAccessType specifies the type of access (byte, word, e.t.c) used to
// read/write to a field.
type FieldAccessType uint8
// The list of supported FieldAccessType values.
const (
FieldAccessTypeAny FieldAccessType = iota
FieldAccessTypeByte
FieldAccessTypeWord
FieldAccessTypeDword
FieldAccessTypeQword
FieldAccessTypeBuffer
)
// FieldUpdateRule specifies how a field value is updated when a write uses
// a value with a smaller width than the field.
type FieldUpdateRule uint8
// The list of supported FieldUpdateRule values.
const (
FieldUpdateRulePreserve FieldUpdateRule = iota
FieldUpdateRuleWriteAsOnes
FieldUpdateRuleWriteAsZeros
)
// FieldAccessAttrib specifies additional information about a particular field
// access.
type FieldAccessAttrib uint8
// The list of supported FieldAccessAttrib values.
const (
FieldAccessAttribQuick FieldAccessAttrib = 0x02
FieldAccessAttribSendReceive = 0x04
FieldAccessAttribByte = 0x06
FieldAccessAttribWord = 0x08
FieldAccessAttribBlock = 0x0a
FieldAccessAttribBytes = 0x0b // byteCount contains the number of bytes
FieldAccessAttribProcessCall = 0x0c
FieldAccessAttribBlockProcessCall = 0x0d
FieldAccessAttribRawBytes = 0x0e // byteCount contains the number of bytes
FieldAccessAttribRawProcessBytes = 0x0f // byteCount contains the number of bytes
)
// fieldEntity is a named object that encapsulates the data shared between regular
// fields and index fields.
type fieldEntity struct {
namedEntity
bitOffset uint32
bitWidth uint32
lock bool
updateRule FieldUpdateRule
// accessAttrib is valid if accessType is BufferAcc
// for the SMB or GPIO OpRegions.
accessAttrib FieldAccessAttrib
accessType FieldAccessType
// byteCount is valid when accessAttrib is one of:
// Bytes, RawBytes or RawProcessBytes
byteCount uint8
}
// fieldUnitEntity is a field defined inside an operating region.
type fieldUnitEntity struct {
fieldEntity
// The connection which this field references.
connectionName string
resolvedConnection Entity
// The region which this field references.
regionName string
resolvedRegion *regionEntity
}
func (ent *fieldUnitEntity) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
var ok bool
if ent.connectionName != "" && ent.resolvedConnection == nil {
if ent.resolvedConnection = scopeFind(ent.parent, rootNs, ent.connectionName); ent.resolvedConnection == nil {
kfmt.Fprintf(errWriter, "[field %s] could not resolve connection reference: %s\n", ent.name, ent.connectionName)
return false
}
}
if ent.resolvedRegion == nil {
if ent.resolvedRegion, ok = scopeFind(ent.parent, rootNs, ent.regionName).(*regionEntity); !ok {
kfmt.Fprintf(errWriter, "[field %s] could not resolve referenced region: %s\n", ent.name, ent.regionName)
}
}
return ent.resolvedRegion != nil
}
// indexFieldEntity is a special field that groups together two field units so a
// index/data register pattern can be implemented. To write a value to an
// indexField, the interpreter must first write the appropriate offset to
// the indexRegister (using the alignment specifid by accessType) and then
// write the actual value to the dataRegister.
type indexFieldEntity struct {
fieldEntity
// The connection which this field references.
connectionName string
resolvedConnection Entity
indexRegName string
indexReg *fieldUnitEntity
dataRegName string
dataReg *fieldUnitEntity
}
func (ent *indexFieldEntity) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
var ok bool
if ent.connectionName != "" && ent.resolvedConnection == nil {
if ent.resolvedConnection = scopeFind(ent.parent, rootNs, ent.connectionName); ent.resolvedConnection == nil {
kfmt.Fprintf(errWriter, "[field %s] could not resolve connection reference: %s\n", ent.name, ent.connectionName)
return false
}
}
if ent.indexReg == nil {
if ent.indexReg, ok = scopeFind(ent.parent, rootNs, ent.indexRegName).(*fieldUnitEntity); !ok {
kfmt.Fprintf(errWriter, "[indexField %s] could not resolve referenced index register: %s\n", ent.name, ent.indexRegName)
}
}
if ent.dataReg == nil {
if ent.dataReg, ok = scopeFind(ent.parent, rootNs, ent.dataRegName).(*fieldUnitEntity); !ok {
kfmt.Fprintf(errWriter, "[dataField %s] could not resolve referenced data register: %s\n", ent.name, ent.dataRegName)
}
}
return ent.indexReg != nil && ent.dataReg != nil
}
// namedReference holds a named reference to an AML symbol. The spec allows
// the symbol not to be defined at the time when the reference is parsed. In
// such a case (forward reference) it will be resolved after the entire AML
// stream has successfully been parsed.
type namedReference struct {
unnamedEntity
targetName string
target Entity
}
func (ref *namedReference) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
if ref.target == nil {
if ref.target = scopeFind(ref.parent, rootNs, ref.targetName); ref.target == nil {
kfmt.Fprintf(errWriter, "could not resolve referenced symbol: %s (parent: %s)\n", ref.targetName, ref.parent.Name())
return false
}
}
return true
}
// methodInvocationEntity describes an AML method invocation.
type methodInvocationEntity struct {
unnamedEntity
methodName string
method *Method
}
func (m *methodInvocationEntity) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
if m.method == nil {
var isMethod bool
if m.method, isMethod = scopeFind(m.parent, rootNs, m.methodName).(*Method); !isMethod {
kfmt.Fprintf(errWriter, "could not resolve merenced method: %s (parent: %s)\n", m.methodName, m.parent.Name())
return false
}
}
return true
}
// Method defines an invocable AML method.
type Method struct {
scopeEntity
tableHandle uint8
argCount uint8
serialized bool
syncLevel uint8
}
func (m *Method) getOpcode() opcode { return opMethod }
// Device defines a device.
type Device struct {
scopeEntity
tableHandle uint8
// The methodMap keeps track of all methods exposed by this device.
methodMap map[string]*Method
}
func (d *Device) getOpcode() opcode { return opDevice }
func (d *Device) setTableHandle(h uint8) { d.tableHandle = h }
// TableHandle returns the handle of the ACPI table that defines this device.
func (d *Device) TableHandle() uint8 { return d.tableHandle }
// mutexEntity represents a named mutex object
type mutexEntity struct {
parent ScopeEntity
// isGlobal is set to true for the pre-defined global mutex (\_GL object)
isGlobal bool
name string
syncLevel uint8
tableHandle uint8
}
func (ent *mutexEntity) getOpcode() opcode { return opMutex }
func (ent *mutexEntity) setOpcode(op opcode) {}
func (ent *mutexEntity) Name() string { return ent.name }
func (ent *mutexEntity) Parent() ScopeEntity { return ent.parent }
func (ent *mutexEntity) setParent(parent ScopeEntity) { ent.parent = parent }
func (ent *mutexEntity) getArgs() []interface{} { return nil }
func (ent *mutexEntity) setArg(argIndex uint8, arg interface{}) bool {
var ok bool
switch argIndex {
case 0:
// arg 0 is the mutex name
ent.name, ok = arg.(string)
case 1:
// arg1 is the sync level (bits 0:3)
var syncLevel uint64
syncLevel, ok = arg.(uint64)
ent.syncLevel = uint8(syncLevel) & 0xf
}
return ok
}
func (ent *mutexEntity) TableHandle() uint8 { return ent.tableHandle }
func (ent *mutexEntity) setTableHandle(h uint8) { ent.tableHandle = h }
// eventEntity represents a named ACPI sync event.
type eventEntity struct {
namedEntity
}

View File

@ -0,0 +1,945 @@
package entity
import "gopheros/kernel"
// Entity is an interface implemented by all AML entities.
type Entity interface {
// Opcode returns the AML op associated with this entity.
Opcode() AMLOpcode
// Name returns the entity's name or an empty string if no name is
// associated with the entity.
Name() string
// Parent returns the Container of this entity.
Parent() Container
// SetParent updates the parent container reference.
SetParent(Container)
// TableHandle returns the handle of the ACPI table where this entity
// was defined.
TableHandle() uint8
// Args returns the argument list for this entity.
Args() []interface{}
// SetArg adds an argument value at the specified argument index.
SetArg(uint8, interface{}) bool
}
// Container is an interface that is implemented by entities contain a
// collection of other Entities and define an AML scope.
type Container interface {
Entity
// Children returns the list of entities that are children of this
// container.
Children() []Entity
// Append adds an entity to a container.
Append(Entity) bool
// Remove searches the child list for an entity and removes it if found.
Remove(Entity)
// Last returns the last entity that was added to this container.
Last() Entity
}
type FieldAccessTypeProvider interface {
// DefaultAccessType returns the default FieldAccessType for any field unit
// defined by this field.
DefaultAccessType() FieldAccessType
}
// LazyRefResolver is an interface implemented by entities that contain symbol
// references that are lazily resolved after the full AML entity tree has been
// parsed.
type LazyRefResolver interface {
// ResolveSymbolRefs receives as input the root of the AML entity tree and
// attempts to resolve any symbol references using the scope searching rules
// defined by the ACPI spec.
ResolveSymbolRefs(Container) *kernel.Error
}
// Generic describes an entity without a name.
type Generic struct {
_ uint8
tableHandle uint8
op AMLOpcode
args []interface{}
parent Container
}
// NewGeneric returns a new generic AML entity.
func NewGeneric(op AMLOpcode, tableHandle uint8) *Generic {
return &Generic{
op: op,
tableHandle: tableHandle,
}
}
// Opcode returns the AML op associated with this entity.
func (ent *Generic) Opcode() AMLOpcode { return ent.op }
// Name returns the entity's name. For this type of entity it always returns
// an empty string.
func (ent *Generic) Name() string { return "" }
// Parent returns the Container of this entity.
func (ent *Generic) Parent() Container { return ent.parent }
// SetParent updates the parent container reference.
func (ent *Generic) SetParent(parent Container) { ent.parent = parent }
// TableHandle returns the handle of the ACPI table where this entity was
// defined.
func (ent *Generic) TableHandle() uint8 { return ent.tableHandle }
// Args returns the argument list for this entity.
func (ent *Generic) Args() []interface{} { return ent.args }
// SetArg adds an argument value at the specified argument index.
func (ent *Generic) SetArg(_ uint8, arg interface{}) bool {
ent.args = append(ent.args, arg)
return true
}
// GenericNamed describes an entity whose name is specified as the argument at
// index zero.
type GenericNamed struct {
Generic
name string
}
// NewGenericNamed returns a new generic named AML entity.
func NewGenericNamed(op AMLOpcode, tableHandle uint8) *GenericNamed {
return &GenericNamed{
Generic: Generic{
op: op,
tableHandle: tableHandle,
},
}
}
// Name returns the entity's name.
func (ent *GenericNamed) Name() string { return ent.name }
// SetArg adds an argument value at the specified argument index.
func (ent *GenericNamed) SetArg(argIndex uint8, arg interface{}) bool {
// arg 0 is the entity name
if argIndex == 0 {
var ok bool
ent.name, ok = arg.(string)
return ok
}
ent.args = append(ent.args, arg)
return true
}
// Const is an optionally named entity that contains a constant uint64 or
// string value.
type Const struct {
GenericNamed
Value interface{}
}
// NewConst creates a new AML constant entity.
func NewConst(op AMLOpcode, tableHandle uint8, initialValue interface{}) *Const {
return &Const{
GenericNamed: GenericNamed{
Generic: Generic{
op: op,
tableHandle: tableHandle,
},
},
Value: initialValue,
}
}
// SetName allows the caller to override the name for a Const entity.
func (ent *Const) SetName(name string) { ent.name = name }
// SetArg adds an argument value at the specified argument index.
func (ent *Const) SetArg(argIndex uint8, arg interface{}) bool {
// Const entities accept at most one arg
ent.Value = arg
return argIndex == 0
}
// Scope is an optionally named entity that groups together multiple entities.
type Scope struct {
GenericNamed
children []Entity
}
// NewScope creates a new AML named scope entity.
func NewScope(op AMLOpcode, tableHandle uint8, name string) *Scope {
return &Scope{
GenericNamed: GenericNamed{
Generic: Generic{
op: op,
tableHandle: tableHandle,
},
name: name,
},
}
}
// Children returns the list of entities that are children of this container.
func (ent *Scope) Children() []Entity { return ent.children }
// Append adds an entity to a container.
func (ent *Scope) Append(child Entity) bool {
child.SetParent(ent)
ent.children = append(ent.children, child)
return true
}
// Remove searches the child list for an entity and removes it if found.
func (ent *Scope) Remove(child Entity) {
for index := 0; index < len(ent.children); index++ {
if ent.children[index] == child {
ent.children = append(ent.children[:index], ent.children[index+1:]...)
return
}
}
}
// Last returns the last entity that was added to this container.
func (ent *Scope) Last() Entity { return ent.children[len(ent.children)-1] }
// Buffer defines an AML buffer entity. The entity fields specify a size (arg
// 0) and an optional initializer.
type Buffer struct {
Generic
size interface{}
data []byte
}
// NewBuffer creates a new AML buffer entity.
func NewBuffer(tableHandle uint8) *Buffer {
return &Buffer{
Generic: Generic{
op: OpBuffer,
tableHandle: tableHandle,
},
}
}
// SetArg adds an argument value at the specified argument index.
func (ent *Buffer) SetArg(argIndex uint8, arg interface{}) bool {
switch argIndex {
case 0: // size
ent.size = arg
return true
case 1: // data
if byteSlice, ok := arg.([]byte); ok {
ent.data = byteSlice
return true
}
}
return false
}
// BufferField describes a bit/byte/word/dword/qword or arbitrary length
// region within a Buffer.
type BufferField struct {
GenericNamed
SourceBuf interface{}
Index interface{}
NumBits interface{}
}
// NewBufferField creates a new AML buffer field entity.
func NewBufferField(op AMLOpcode, tableHandle uint8, bits uint64) *BufferField {
return &BufferField{
GenericNamed: GenericNamed{
Generic: Generic{
op: op,
tableHandle: tableHandle,
},
},
NumBits: bits,
}
}
// SetArg adds an argument value at the specified argument index.
func (ent *BufferField) SetArg(argIndex uint8, arg interface{}) bool {
switch argIndex {
case 0:
ent.SourceBuf = arg
case 1:
ent.Index = arg
case 2, 3:
// opCreateField specifies the name using the arg at index 3
// while opCreateXXXField (byte, word e.t.c) specifies the name
// using the arg at index 2
var ok bool
if ent.name, ok = arg.(string); !ok {
ent.NumBits = arg
}
}
return argIndex <= 3
}
// RegionSpace describes the memory space where a region is located.
type RegionSpace uint8
// The list of supported RegionSpace values.
const (
RegionSpaceSystemMemory RegionSpace = iota
RegionSpaceSystemIO
RegionSpacePCIConfig
RegionSpaceEmbeddedControl
RegionSpaceSMBus
RegionSpacePCIBarTarget
RegionSpaceIPMI
)
// Region defines a region located at a particular space (e.g in memory, an
// embedded controller, the SMBus e.t.c).
type Region struct {
GenericNamed
Space RegionSpace
Offset interface{}
Len interface{}
}
// NewRegion creates a new AML region entity.
func NewRegion(tableHandle uint8) *Region {
return &Region{
GenericNamed: GenericNamed{
Generic: Generic{
op: OpOpRegion,
tableHandle: tableHandle,
},
},
}
}
// SetArg adds an argument value at the specified argument index.
func (ent *Region) SetArg(argIndex uint8, arg interface{}) bool {
var ok bool
switch argIndex {
case 0:
ok = ent.GenericNamed.SetArg(argIndex, arg)
case 1:
// the parser will convert ByteData types to uint64
var space uint64
space, ok = arg.(uint64)
ent.Space = RegionSpace(space)
case 2:
ent.Offset = arg
ok = true
case 3:
ent.Len = arg
ok = true
}
return ok
}
// FieldAccessType specifies the type of access (byte, word, e.t.c) used to
// read/write to a field.
type FieldAccessType uint8
// The list of supported FieldAccessType values.
const (
FieldAccessTypeAny FieldAccessType = iota
FieldAccessTypeByte
FieldAccessTypeWord
FieldAccessTypeDword
FieldAccessTypeQword
FieldAccessTypeBuffer
)
// FieldLockRule specifies what type of locking is required when accesing field.
type FieldLockRule uint8
// The list of supported FieldLockRule values.
const (
FieldLockRuleNoLock FieldLockRule = iota
FieldLockRuleLock
)
// FieldUpdateRule specifies how a field value is updated when a write uses
// a value with a smaller width than the field.
type FieldUpdateRule uint8
// The list of supported FieldUpdateRule values.
const (
FieldUpdateRulePreserve FieldUpdateRule = iota
FieldUpdateRuleWriteAsOnes
FieldUpdateRuleWriteAsZeros
)
// FieldAccessAttrib specifies additional information about a particular field
// access.
type FieldAccessAttrib uint8
// The list of supported FieldAccessAttrib values.
const (
FieldAccessAttribQuick FieldAccessAttrib = 0x02
FieldAccessAttribSendReceive = 0x04
FieldAccessAttribByte = 0x06
FieldAccessAttribWord = 0x08
FieldAccessAttribBlock = 0x0a
FieldAccessAttribBytes = 0x0b // byteCount contains the number of bytes
FieldAccessAttribProcessCall = 0x0c
FieldAccessAttribBlockProcessCall = 0x0d
FieldAccessAttribRawBytes = 0x0e // byteCount contains the number of bytes
FieldAccessAttribRawProcessBytes = 0x0f // byteCount contains the number of bytes
)
// Field is anobject that controls access to a host operating region. It is
// referenced by a list of FieldUnit objects that appear as siblings of a Field
// in the same scope.
type Field struct {
Generic
// The region which this field references.
RegionName string
Region *Region
AccessType FieldAccessType
LockRule FieldLockRule
UpdateRule FieldUpdateRule
}
// NewField creates a new AML field entity.
func NewField(tableHandle uint8) *Field {
return &Field{
Generic: Generic{
op: OpField,
tableHandle: tableHandle,
},
}
}
// DefaultAccessType returns the default FieldAccessType for any field unit
// defined by this field.
func (ent *Field) DefaultAccessType() FieldAccessType {
return ent.AccessType
}
// SetArg adds an argument value at the specified argument index.
func (ent *Field) SetArg(argIndex uint8, arg interface{}) bool {
var (
ok bool
uintVal uint64
)
switch argIndex {
case 0:
ent.RegionName, ok = arg.(string)
case 1:
uintVal, ok = arg.(uint64)
ent.AccessType = FieldAccessType(uintVal & 0xf) // access type; bits[0:3]
ent.LockRule = FieldLockRule((uintVal >> 4) & 0x1) // lock; bit 4
ent.UpdateRule = FieldUpdateRule((uintVal >> 5) & 0x3) // update rule; bits[5:6]
}
return ok
}
// IndexField is a special field that groups together two field units so a
// index/data register pattern can be implemented. To write a value to an
// IndexField, the interpreter must first write the appropriate offset to
// the IndexRegister (using the alignment specifid by AccessType) and then
// write the actual value to the DataRegister.
type IndexField struct {
Generic
IndexRegName string
IndexReg *FieldUnit
DataRegName string
DataReg *FieldUnit
AccessType FieldAccessType
LockRule FieldLockRule
UpdateRule FieldUpdateRule
}
// NewIndexField creates a new AML index field entity.
func NewIndexField(tableHandle uint8) *IndexField {
return &IndexField{
Generic: Generic{
op: OpIndexField,
tableHandle: tableHandle,
},
}
}
// DefaultAccessType returns the default FieldAccessType for any field unit
// defined by this field.
func (ent *IndexField) DefaultAccessType() FieldAccessType {
return ent.AccessType
}
// SetArg adds an argument value at the specified argument index.
func (ent *IndexField) SetArg(argIndex uint8, arg interface{}) bool {
var (
ok bool
uintVal uint64
)
switch argIndex {
case 0:
ent.IndexRegName, ok = arg.(string)
case 1:
ent.DataRegName, ok = arg.(string)
case 2:
uintVal, ok = arg.(uint64)
ent.AccessType = FieldAccessType(uintVal & 0xf) // access type; bits[0:3]
ent.LockRule = FieldLockRule((uintVal >> 4) & 0x1) // lock; bit 4
ent.UpdateRule = FieldUpdateRule((uintVal >> 5) & 0x3) // update rule; bits[5:6]
}
return ok
}
// BankField is a special field where a bank register must be used to select
// the appropriate bank region before accessing its contents.
type BankField struct {
Generic
// The region which this field references.
RegionName string
Region *Region
// The bank name which controls access to field units defined within this field.
BankFieldUnitName string
BankFieldUnit *FieldUnit
// The value that needs to be written to the bank field before accessing any field unit.
BankFieldUnitValue interface{}
AccessType FieldAccessType
LockRule FieldLockRule
UpdateRule FieldUpdateRule
}
// NewBankField creates a new AML bank field entity.
func NewBankField(tableHandle uint8) *BankField {
return &BankField{
Generic: Generic{
op: OpBankField,
tableHandle: tableHandle,
},
}
}
// DefaultAccessType returns the default FieldAccessType for any field unit
// defined by this field.
func (ent *BankField) DefaultAccessType() FieldAccessType {
return ent.AccessType
}
// SetArg adds an argument value at the specified argument index.
func (ent *BankField) SetArg(argIndex uint8, arg interface{}) bool {
var (
ok bool
uintVal uint64
)
switch argIndex {
case 0:
ent.RegionName, ok = arg.(string)
case 1:
ent.BankFieldUnitName, ok = arg.(string)
case 2:
ent.BankFieldUnitValue, ok = arg, true
case 3:
uintVal, ok = arg.(uint64)
ent.AccessType = FieldAccessType(uintVal & 0xf) // access type; bits[0:3]
ent.LockRule = FieldLockRule((uintVal >> 4) & 0x1) // lock; bit 4
ent.UpdateRule = FieldUpdateRule((uintVal >> 5) & 0x3) // update rule; bits[5:6]
}
return ok
}
// FieldUnit describes a sub-region inside a parent field.
type FieldUnit struct {
GenericNamed
// Depending on what field this unit belongs to this will be a pointer
// to one of: Field, BankField, IndexField
Field interface{}
// The access type to use. Inherited by parent field unless explicitly
// changed via a directive in the field unit definition list.
AccessType FieldAccessType
// AccessAttrib is valid if AccessType is BufferAcc for the SMB or GPIO OpRegions.
AccessAttrib FieldAccessAttrib
// ByteCount is valid when AccessAttrib is one of: Bytes, RawBytes or RawProcessBytes
ByteCount uint8
// Field offset in parent region and its width.
BitOffset uint32
BitWidth uint32
// The connection resource for field access references (serial bus or GPIO).
ConnectionName string
Connection Entity
}
// NewFieldUnit creates a new field unit entity.
func NewFieldUnit(tableHandle uint8, name string) *FieldUnit {
return &FieldUnit{
GenericNamed: GenericNamed{
Generic: Generic{
op: OpFieldUnit,
tableHandle: tableHandle,
},
name: name,
},
}
}
// Reference holds a named reference to an AML symbol. The spec allows the
// symbol not to be defined at the time when the reference is parsed. In such a
// case (forward reference) it will be resolved after the entire AML stream has
// successfully been parsed.
type Reference struct {
Generic
TargetName string
Target Entity
}
// NewReference creates a new reference to a named entity.
func NewReference(tableHandle uint8, target string) *Reference {
return &Reference{
Generic: Generic{
op: OpName,
tableHandle: tableHandle,
},
TargetName: target,
}
}
// Method describes an invocable AML method.
type Method struct {
Scope
ArgCount uint8
Serialized bool
SyncLevel uint8
}
// NewMethod creats a new AML method entity.
func NewMethod(tableHandle uint8, name string) *Method {
return &Method{
Scope: Scope{
GenericNamed: GenericNamed{
Generic: Generic{
op: OpMethod,
tableHandle: tableHandle,
},
name: name,
},
},
}
}
// SetArg adds an argument value at the specified argument index.
func (ent *Method) SetArg(argIndex uint8, arg interface{}) bool {
var (
ok bool
uintVal uint64
)
switch argIndex {
case 0:
// Arg0 is the name but it is actually defined when creating the entity
ok = true
case 1:
// arg1 is the method flags
uintVal, ok = arg.(uint64)
ent.ArgCount = (uint8(uintVal) & 0x7) // bits[0:2]
ent.Serialized = (uint8(uintVal)>>3)&0x1 == 0x1 // bit 3
ent.SyncLevel = (uint8(uintVal) >> 4) & 0xf // bits[4:7]
}
return ok
}
// Invocation describes an AML method invocation.
type Invocation struct {
Generic
MethodName string
MethodDef *Method
}
// NewInvocation creates a new method invocation object.
func NewInvocation(tableHandle uint8, name string) *Invocation {
return &Invocation{
Generic: Generic{
op: OpMethodInvocation,
tableHandle: tableHandle,
},
MethodName: name,
}
}
// Device defines an AML device entity.
type Device struct {
Scope
}
// NewDevice creates a new device object.
func NewDevice(tableHandle uint8, name string) *Device {
return &Device{
Scope: Scope{
GenericNamed: GenericNamed{
Generic: Generic{
op: OpDevice,
tableHandle: tableHandle,
},
name: name,
},
},
}
}
// Processor describes a AML processor entity. According to the spec, the use
// of processor operators is deprecated and processors should be declared as
// Device entities instead.
type Processor struct {
Scope
// A unique ID for this processor.
ID uint8
// The length of the processor register block. According to the spec,
// this field may be zero.
RegBlockLen uint8
// The I/O address of the process register block.
RegBlockAddr uint32
}
// NewProcessor creates a new processor object.
func NewProcessor(tableHandle uint8, name string) *Processor {
return &Processor{
Scope: Scope{
GenericNamed: GenericNamed{
Generic: Generic{
op: OpProcessor,
tableHandle: tableHandle,
},
name: name,
},
},
}
}
// SetArg adds an argument value at the specified argument index.
func (ent *Processor) SetArg(argIndex uint8, arg interface{}) bool {
var (
ok bool
uintVal uint64
)
switch argIndex {
case 0:
// Arg0 is the name but it is actually defined when creating the entity
ok = true
case 1:
// arg1 is the processor ID (ByteData)
uintVal, ok = arg.(uint64)
ent.ID = uint8(uintVal)
case 2:
// arg2 is the processor I/O reg block address (Dword)
uintVal, ok = arg.(uint64)
ent.RegBlockAddr = uint32(uintVal)
case 3:
// arg3 is the processor I/O reg block address len (ByteData)
uintVal, ok = arg.(uint64)
ent.RegBlockLen = uint8(uintVal)
}
return ok
}
// PowerResource describes a AML power resource entity.
type PowerResource struct {
Scope
// The deepest system sleep level OSPM must maintain to keep this power
// resource on (0 equates to S0, 1 equates to S1, and so on).
SystemLevel uint8
// ResourceOrder provides the system with the order in which Power
// Resources must be enabled or disabled. Each unique resourceorder
// value represents a level, and any number of power resources may have
// the same level. Power Resource levels are enabled from low values to
// high values and are disabled from high values to low values.
ResourceOrder uint16
}
// NewPowerResource creates a new power resource object.
func NewPowerResource(tableHandle uint8, name string) *PowerResource {
return &PowerResource{
Scope: Scope{
GenericNamed: GenericNamed{
Generic: Generic{
op: OpPowerRes,
tableHandle: tableHandle,
},
name: name,
},
},
}
}
// SetArg adds an argument value at the specified argument index.
func (ent *PowerResource) SetArg(argIndex uint8, arg interface{}) bool {
var (
ok bool
uintVal uint64
)
switch argIndex {
case 0:
// Arg0 is the name but it is actually defined when creating the entity
ok = true
case 1:
// arg1 is the system level (ByteData)
uintVal, ok = arg.(uint64)
ent.SystemLevel = uint8(uintVal)
case 2:
// arg2 is the resource order (WordData)
uintVal, ok = arg.(uint64)
ent.ResourceOrder = uint16(uintVal)
}
return ok
}
// ThermalZone describes a AML thermal zone entity.
type ThermalZone struct {
Scope
}
// NewThermalZone creates a new thermal zone object.
func NewThermalZone(tableHandle uint8, name string) *ThermalZone {
return &ThermalZone{
Scope: Scope{
GenericNamed: GenericNamed{
Generic: Generic{
op: OpThermalZone,
tableHandle: tableHandle,
},
name: name,
},
},
}
}
// Mutex describes a AML mutex entity.
type Mutex struct {
GenericNamed
// IsGlobal is set to true for the pre-defined global mutex (\_GL object)
IsGlobal bool
SyncLevel uint8
}
// NewMutex creates a new mutex object.
func NewMutex(tableHandle uint8) *Mutex {
return &Mutex{
GenericNamed: GenericNamed{
Generic: Generic{
op: OpMutex,
tableHandle: tableHandle,
},
},
}
}
// SetArg adds an argument value at the specified argument index.
func (ent *Mutex) SetArg(argIndex uint8, arg interface{}) bool {
var ok bool
switch argIndex {
case 0:
// arg 0 is the mutex name
ent.name, ok = arg.(string)
case 1:
// arg1 is the sync level (bits 0:3)
var syncLevel uint64
syncLevel, ok = arg.(uint64)
ent.SyncLevel = uint8(syncLevel) & 0xf
}
return ok
}
// Event represents a named ACPI sync event.
type Event struct {
GenericNamed
}
// NewEvent creates a new event object.
func NewEvent(tableHandle uint8) *Event {
return &Event{
GenericNamed: GenericNamed{
Generic: Generic{
op: OpEvent,
tableHandle: tableHandle,
},
},
}
}
// Package is an entity that contains one of the following entity types:
// - constant data objects (int, string, buffer or package)
// - named references to data objects (int, string, buffer, buffer field,
// field unit or package)
// - named references to non-data objects (device, event, method, mutex, region
// power resource, processor or thermal zone)
type Package struct {
Generic
// The number of elements in the package. In most cases, the package
// length is known at compile-time and will be emitted as a const
// value. However, the standard also allows dynamic definition of
// package elements (e.g. inside a method). In the latter case (or if
// the package contains more that 255 elements) this will be a
// expression that the VM needs to evaluate as an integer value.
NumElements interface{}
}
// NewPackage creates a new package entity with the OpPackage or the
// OpVarPackage opcodes.
func NewPackage(op AMLOpcode, tableHandle uint8) *Package {
return &Package{
Generic: Generic{
op: op,
tableHandle: tableHandle,
},
}
}
// SetArg adds an argument value at the specified argument index.
func (ent *Package) SetArg(argIndex uint8, arg interface{}) bool {
// Package entities define the number of elements as the first arg.
if argIndex == 0 {
ent.NumElements = arg
return true
}
return ent.Generic.SetArg(argIndex, arg)
}

View File

@ -0,0 +1,185 @@
package entity
import (
"reflect"
"testing"
)
func TestEntityMethods(t *testing.T) {
namedConst := NewConst(OpDwordPrefix, 42, "foo")
namedConst.SetName("TAG0")
specs := []struct {
ent Entity
expOp AMLOpcode
expName string
}{
{NewGeneric(OpNoop, 42), OpNoop, ""},
{NewGenericNamed(OpAcquire, 42), OpAcquire, ""},
{namedConst, OpDwordPrefix, "TAG0"},
{NewScope(OpScope, 42, "_SB_"), OpScope, "_SB_"},
{NewBuffer(42), OpBuffer, ""},
{NewBufferField(OpCreateByteField, 42, 8), OpCreateByteField, ""},
{NewField(42), OpField, ""},
{NewIndexField(42), OpIndexField, ""},
{NewBankField(42), OpBankField, ""},
{NewReference(42, "TRG0"), OpName, ""},
{NewMethod(42, "FOO0"), OpMethod, "FOO0"},
{NewInvocation(42, "MTH0"), OpMethodInvocation, ""},
{NewMutex(42), OpMutex, ""},
{NewDevice(42, "DEV0"), OpDevice, "DEV0"},
{NewProcessor(42, "CPU0"), OpProcessor, "CPU0"},
{NewPowerResource(42, "POW0"), OpPowerRes, "POW0"},
{NewThermalZone(42, "THE0"), OpThermalZone, "THE0"},
{NewEvent(42), OpEvent, ""},
{NewRegion(42), OpOpRegion, ""},
{NewFieldUnit(42, "FOO0"), OpFieldUnit, "FOO0"},
{NewPackage(OpPackage, 42), OpPackage, ""},
}
t.Run("opcode and name getter", func(t *testing.T) {
for specIndex, spec := range specs {
if got := spec.ent.Opcode(); got != spec.expOp {
t.Errorf("[spec %d] expected to get back opcode %d; got %d", specIndex, spec.expOp, got)
}
if got := spec.ent.Name(); got != spec.expName {
t.Errorf("[spec %d] expected to get name: %q; got %q", specIndex, spec.expName, got)
}
}
})
t.Run("table handle getter", func(t *testing.T) {
exp := uint8(42)
for specIndex, spec := range specs {
if got := spec.ent.TableHandle(); got != exp {
t.Errorf("[spec %d] expected to get back handle %d; got %d", specIndex, exp, got)
}
}
})
t.Run("append/remove/get parent methods", func(t *testing.T) {
parent := NewScope(OpScope, 2, "_SB_")
parent.name = `\`
for specIndex, spec := range specs {
parent.Append(spec.ent)
if got := spec.ent.Parent(); got != parent {
t.Errorf("[spec %d] expected to get back parent %v; got %v", specIndex, parent, got)
}
if got := parent.Last(); got != spec.ent {
t.Errorf("[spec %d] expected parent's last entity to be the one just appended", specIndex)
}
parent.Remove(spec.ent)
}
if got := len(parent.Children()); got != 0 {
t.Fatalf("expected parent not to have any child nodes; got %d", got)
}
})
}
func TestEntityArgAssignment(t *testing.T) {
specs := []struct {
ent Entity
argList []interface{}
expArgList []interface{}
limitedArgs bool
}{
{
NewGeneric(1, 2),
[]interface{}{"foo", 1, "bar"},
[]interface{}{"foo", 1, "bar"},
false,
},
{
NewGenericNamed(1, 2),
[]interface{}{"foo", 1, "bar"},
[]interface{}{1, "bar"}, // GenericNamed uses arg0 as the name
false,
},
{
NewConst(1, 2, 3),
[]interface{}{"foo"},
nil, // Const populates its internal state using the arg 0
true,
},
{
NewBuffer(2),
[]interface{}{1, []byte{}},
nil, // Buffer populates its internal state using the first 2 args
true,
},
{
NewBufferField(OpCreateDWordField, 2, 32),
[]interface{}{"a", "b", "c"},
nil, // Buffer populates its internal state using the first 3 args (opCreateDwordField)
false,
},
{
NewBufferField(1, 2, 0),
[]interface{}{"a", "b", 10, "c"},
nil, // Buffer populates its internal state using the first 4 args (opCreateField)
true,
},
{
NewRegion(2),
[]interface{}{"REG0", uint64(0x4), 0, 10},
nil, // Region populates its internal state using the first 4 args
true,
},
{
NewMutex(2),
[]interface{}{"MUT0", uint64(1)},
nil, // Mutex populates its internal state using the first 2 args
true,
},
{
NewProcessor(2, "CPU0"),
[]interface{}{uint64(1), uint64(0xdeadc0de), uint64(0)},
nil, // Processor populates its internal state using the first 3 args
true,
},
{
NewPowerResource(2, "POW0"),
[]interface{}{uint64(2), uint64(1)},
nil, // PowerResource populates its internal state using the first 2 args
true,
},
{
NewMethod(2, "MTH0"),
[]interface{}{"arg0 ignored", uint64(0x42)},
nil, // Method populates its internal state using the first 2 args
true,
},
{
NewPackage(OpPackage, 2),
[]interface{}{uint64(1), NewConst(OpDwordPrefix, 2, uint64(42))},
[]interface{}{NewConst(OpDwordPrefix, 2, uint64(42))},
false,
},
}
nextSpec:
for specIndex, spec := range specs {
for i, arg := range spec.argList {
if !spec.ent.SetArg(uint8(i), arg) {
t.Errorf("[spec %d] error setting arg %d", specIndex, i)
continue nextSpec
}
}
if spec.limitedArgs {
if spec.ent.SetArg(uint8(len(spec.argList)), nil) {
t.Errorf("[spec %d] expected additional calls to setArg to return false", specIndex)
continue nextSpec
}
}
if got := spec.ent.Args(); !reflect.DeepEqual(got, spec.expArgList) {
t.Errorf("[spec %d] expected to get back arg list %v; got %v", specIndex, spec.expArgList, got)
}
}
}

View File

@ -122,8 +122,246 @@ const (
OpIndexField = AMLOpcode(0xff + 0x86)
OpBankField = AMLOpcode(0xff + 0x87)
OpDataRegion = AMLOpcode(0xff + 0x88)
// Special internal opcodes which are not part of the spec; these are
// for internal use by the AML interpreter.
OpFieldUnit = AMLOpcode(0xff + 0xfd)
OpMethodInvocation = AMLOpcode(0xff + 0xfe)
)
// String implements fmt.Stringer for the AMLOpcode type.
func (op AMLOpcode) String() string {
switch op {
case OpZero:
return "Zero"
case OpOne:
return "One"
case OpAlias:
return "Alias"
case OpName:
return "Name"
case OpBytePrefix:
return "Byte"
case OpWordPrefix:
return "Word"
case OpDwordPrefix:
return "Dword"
case OpStringPrefix:
return "String"
case OpQwordPrefix:
return "Qword"
case OpScope:
return "Scope"
case OpBuffer:
return "Buffer"
case OpPackage:
return "Package"
case OpVarPackage:
return "VarPackage"
case OpMethod:
return "Method"
case OpExternal:
return "External"
case OpLocal0:
return "Local0"
case OpLocal1:
return "Local1"
case OpLocal2:
return "Local2"
case OpLocal3:
return "Local3"
case OpLocal4:
return "Local4"
case OpLocal5:
return "Local5"
case OpLocal6:
return "Local6"
case OpLocal7:
return "Local7"
case OpArg0:
return "Arg0"
case OpArg1:
return "Arg1"
case OpArg2:
return "Arg2"
case OpArg3:
return "Arg3"
case OpArg4:
return "Arg4"
case OpArg5:
return "Arg5"
case OpArg6:
return "Arg6"
case OpStore:
return "Store"
case OpRefOf:
return "RefOf"
case OpAdd:
return "Add"
case OpConcat:
return "Concat"
case OpSubtract:
return "Subtract"
case OpIncrement:
return "Increment"
case OpDecrement:
return "Decrement"
case OpMultiply:
return "Multiply"
case OpDivide:
return "Divide"
case OpShiftLeft:
return "ShiftLeft"
case OpShiftRight:
return "ShiftRight"
case OpAnd:
return "And"
case OpNand:
return "Nand"
case OpOr:
return "Or"
case OpNor:
return "Nor"
case OpXor:
return "Xor"
case OpNot:
return "Not"
case OpFindSetLeftBit:
return "FindSetLeftBit"
case OpFindSetRightBit:
return "FindSetRightBit"
case OpDerefOf:
return "DerefOf"
case OpConcatRes:
return "ConcatRes"
case OpMod:
return "Mod"
case OpNotify:
return "Notify"
case OpSizeOf:
return "SizeOf"
case OpIndex:
return "Index"
case OpMatch:
return "Match"
case OpCreateDWordField:
return "CreateDWordField"
case OpCreateWordField:
return "CreateWordField"
case OpCreateByteField:
return "CreateByteField"
case OpCreateBitField:
return "CreateBitField"
case OpObjectType:
return "ObjectType"
case OpCreateQWordField:
return "CreateQWordField"
case OpLand:
return "Land"
case OpLor:
return "Lor"
case OpLnot:
return "Lnot"
case OpLEqual:
return "LEqual"
case OpLGreater:
return "LGreater"
case OpLLess:
return "LLess"
case OpToBuffer:
return "ToBuffer"
case OpToDecimalString:
return "ToDecimalString"
case OpToHexString:
return "ToHexString"
case OpToInteger:
return "ToInteger"
case OpToString:
return "ToString"
case OpCopyObject:
return "CopyObject"
case OpMid:
return "Mid"
case OpContinue:
return "Continue"
case OpIf:
return "If"
case OpElse:
return "Else"
case OpWhile:
return "While"
case OpNoop:
return "Noop"
case OpReturn:
return "Return"
case OpBreak:
return "Break"
case OpBreakPoint:
return "BreakPoint"
case OpOnes:
return "Ones"
case OpMutex:
return "Mutex"
case OpEvent:
return "Event"
case OpCondRefOf:
return "CondRefOf"
case OpCreateField:
return "CreateField"
case OpLoadTable:
return "LoadTable"
case OpLoad:
return "Load"
case OpStall:
return "Stall"
case OpSleep:
return "Sleep"
case OpAcquire:
return "Acquire"
case OpSignal:
return "Signal"
case OpWait:
return "Wait"
case OpReset:
return "Reset"
case OpRelease:
return "Release"
case OpFromBCD:
return "FromBCD"
case OpToBCD:
return "ToBCD"
case OpUnload:
return "Unload"
case OpRevision:
return "Revision"
case OpDebug:
return "Debug"
case OpFatal:
return "Fatal"
case OpTimer:
return "Timer"
case OpOpRegion:
return "OpRegion"
case OpField:
return "Field"
case OpDevice:
return "Device"
case OpProcessor:
return "Processor"
case OpPowerRes:
return "PowerRes"
case OpThermalZone:
return "ThermalZone"
case OpIndexField:
return "IndexField"
case OpBankField:
return "BankField"
case OpDataRegion:
return "DataRegion"
default:
return "unknown"
}
}
// OpIsLocalArg returns true if this opcode represents any of the supported local
// function args 0 to 7.
func OpIsLocalArg(op AMLOpcode) bool {

View File

@ -2,6 +2,135 @@ package entity
import "testing"
func TestOpcodeToString(t *testing.T) {
opcodeList := []AMLOpcode{
OpZero,
OpOne,
OpAlias,
OpName,
OpBytePrefix,
OpWordPrefix,
OpDwordPrefix,
OpStringPrefix,
OpQwordPrefix,
OpScope,
OpBuffer,
OpPackage,
OpVarPackage,
OpMethod,
OpExternal,
OpLocal0,
OpLocal1,
OpLocal2,
OpLocal3,
OpLocal4,
OpLocal5,
OpLocal6,
OpLocal7,
OpArg0,
OpArg1,
OpArg2,
OpArg3,
OpArg4,
OpArg5,
OpArg6,
OpStore,
OpRefOf,
OpAdd,
OpConcat,
OpSubtract,
OpIncrement,
OpDecrement,
OpMultiply,
OpDivide,
OpShiftLeft,
OpShiftRight,
OpAnd,
OpNand,
OpOr,
OpNor,
OpXor,
OpNot,
OpFindSetLeftBit,
OpFindSetRightBit,
OpDerefOf,
OpConcatRes,
OpMod,
OpNotify,
OpSizeOf,
OpIndex,
OpMatch,
OpCreateDWordField,
OpCreateWordField,
OpCreateByteField,
OpCreateBitField,
OpObjectType,
OpCreateQWordField,
OpLand,
OpLor,
OpLnot,
OpLEqual,
OpLGreater,
OpLLess,
OpToBuffer,
OpToDecimalString,
OpToHexString,
OpToInteger,
OpToString,
OpCopyObject,
OpMid,
OpContinue,
OpIf,
OpElse,
OpWhile,
OpNoop,
OpReturn,
OpBreak,
OpBreakPoint,
OpOnes,
OpMutex,
OpEvent,
OpCondRefOf,
OpCreateField,
OpLoadTable,
OpLoad,
OpStall,
OpSleep,
OpAcquire,
OpSignal,
OpWait,
OpReset,
OpRelease,
OpFromBCD,
OpToBCD,
OpUnload,
OpRevision,
OpDebug,
OpFatal,
OpTimer,
OpOpRegion,
OpField,
OpDevice,
OpProcessor,
OpPowerRes,
OpThermalZone,
OpIndexField,
OpBankField,
OpDataRegion,
}
for specIndex, op := range opcodeList {
if op.String() == "unknown" {
t.Errorf("[spec %d] op 0x%x String() returned \"unknown\"", specIndex, op)
}
}
// Also test invalid opcode
if got := AMLOpcode(0xffff).String(); got != "unknown" {
t.Fatalf("expected String() for invalid opcode to return \"unknown\"; got: %q", got)
}
}
func TestOpcodeIsX(t *testing.T) {
specs := []struct {
op AMLOpcode

View File

@ -1,173 +0,0 @@
package aml
import (
"io/ioutil"
"reflect"
"testing"
)
func TestEntityMethods(t *testing.T) {
specs := []Entity{
&unnamedEntity{},
&constEntity{},
&scopeEntity{},
&bufferEntity{},
&fieldUnitEntity{},
&indexFieldEntity{},
&namedReference{},
&methodInvocationEntity{},
&Method{},
&Device{},
&mutexEntity{},
&eventEntity{},
}
t.Run("table handle methods", func(t *testing.T) {
exp := uint8(42)
for specIndex, spec := range specs {
spec.setTableHandle(exp)
if got := spec.TableHandle(); got != exp {
t.Errorf("[spec %d] expected to get back handle %d; got %d", specIndex, exp, got)
}
}
})
t.Run("append/remove/get parent methods", func(t *testing.T) {
parent := &scopeEntity{name: `\`}
for specIndex, spec := range specs {
parent.Append(spec)
if got := spec.Parent(); got != parent {
t.Errorf("[spec %d] expected to get back parent %v; got %v", specIndex, parent, got)
}
parent.removeChild(spec)
}
if got := len(parent.Children()); got != 0 {
t.Fatalf("expected parent not to have any child nodes; got %d", got)
}
})
}
func TestEntityArgAssignment(t *testing.T) {
specs := []struct {
ent Entity
argList []interface{}
expArgList []interface{}
limitedArgs bool
}{
{
&unnamedEntity{},
[]interface{}{"foo", 1, "bar"},
[]interface{}{"foo", 1, "bar"},
false,
},
{
&constEntity{},
[]interface{}{"foo"},
nil, // constEntity populates its internal state using the 1st arg
true,
},
{
&scopeEntity{},
[]interface{}{"foo", 1, 2, 3},
[]interface{}{1, 2, 3}, // scopeEntity will treat arg0 as the scope name if it is a string
false,
},
{
&bufferEntity{},
[]interface{}{1, []byte{}},
nil, // bufferEntity populates its internal state using the first 2 args
true,
},
{
&regionEntity{},
[]interface{}{"REG0", uint64(0x4), 0, 10},
[]interface{}{0, 10}, // region populates its internal state using the first 2 args
true,
},
{
&mutexEntity{},
[]interface{}{"MUT0", uint64(1)},
nil, // mutexEntity populates its internal state using the first 2 args
true,
},
}
nextSpec:
for specIndex, spec := range specs {
for i, arg := range spec.argList {
if !spec.ent.setArg(uint8(i), arg) {
t.Errorf("[spec %d] error setting arg %d", specIndex, i)
continue nextSpec
}
}
if spec.limitedArgs {
if spec.ent.setArg(uint8(len(spec.argList)), nil) {
t.Errorf("[spec %d] expected additional calls to setArg to return false", specIndex)
continue nextSpec
}
}
if got := spec.ent.getArgs(); !reflect.DeepEqual(got, spec.expArgList) {
t.Errorf("[spec %d] expected to get back arg list %v; got %v", specIndex, spec.expArgList, got)
}
}
}
func TestEntityResolveErrors(t *testing.T) {
scope := &scopeEntity{name: `\`}
specs := []resolver{
// Unknown connection entity
&fieldUnitEntity{connectionName: "CON0"},
// Unknown region
&fieldUnitEntity{connectionName: `\`, regionName: "REG0"},
// Unknown connection entity
&indexFieldEntity{connectionName: "CON0"},
// Unknown index register
&indexFieldEntity{connectionName: `\`, indexRegName: "IND0"},
// Unknown data register
&indexFieldEntity{connectionName: `\`, indexRegName: `\`, dataRegName: "DAT0"},
// Unknown reference
&namedReference{unnamedEntity: unnamedEntity{parent: scope}, targetName: "TRG0"},
// Unknown method name
&methodInvocationEntity{unnamedEntity: unnamedEntity{parent: scope}, methodName: "MTH0"},
}
for specIndex, spec := range specs {
if spec.Resolve(ioutil.Discard, scope) {
t.Errorf("[spec %d] expected Resolve() to fail", specIndex)
}
}
}
func TestMethodInvocationResolver(t *testing.T) {
scope := &scopeEntity{name: `\`}
scope.Append(&Method{
scopeEntity: scopeEntity{
name: "MTH0",
},
})
validInv := &methodInvocationEntity{
methodName: "MTH0",
}
invalidInv := &methodInvocationEntity{
methodName: "FOO0",
}
scope.Append(validInv)
scope.Append(invalidInv)
if !validInv.Resolve(ioutil.Discard, scope) {
t.Fatal("expected method invocation to resolve method", validInv.methodName)
}
if invalidInv.Resolve(ioutil.Discard, scope) {
t.Fatal("expected method invocation NOT to resolve method", invalidInv.methodName)
}
}