From 44896b1680688d1873f8fb934a4378207a997daa Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Sat, 16 Dec 2017 07:45:37 +0000 Subject: [PATCH] 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) --- src/gopheros/device/acpi/aml/entity.go | 506 ---------- src/gopheros/device/acpi/aml/entity/entity.go | 945 ++++++++++++++++++ .../device/acpi/aml/entity/entity_test.go | 185 ++++ src/gopheros/device/acpi/aml/entity/opcode.go | 238 +++++ .../device/acpi/aml/entity/opcode_test.go | 129 +++ src/gopheros/device/acpi/aml/entity_test.go | 173 ---- 6 files changed, 1497 insertions(+), 679 deletions(-) delete mode 100644 src/gopheros/device/acpi/aml/entity.go create mode 100644 src/gopheros/device/acpi/aml/entity/entity.go create mode 100644 src/gopheros/device/acpi/aml/entity/entity_test.go delete mode 100644 src/gopheros/device/acpi/aml/entity_test.go diff --git a/src/gopheros/device/acpi/aml/entity.go b/src/gopheros/device/acpi/aml/entity.go deleted file mode 100644 index 6a70839..0000000 --- a/src/gopheros/device/acpi/aml/entity.go +++ /dev/null @@ -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 -} diff --git a/src/gopheros/device/acpi/aml/entity/entity.go b/src/gopheros/device/acpi/aml/entity/entity.go new file mode 100644 index 0000000..94d7ef6 --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/entity.go @@ -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) +} diff --git a/src/gopheros/device/acpi/aml/entity/entity_test.go b/src/gopheros/device/acpi/aml/entity/entity_test.go new file mode 100644 index 0000000..5122cd5 --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/entity_test.go @@ -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) + } + } +} diff --git a/src/gopheros/device/acpi/aml/entity/opcode.go b/src/gopheros/device/acpi/aml/entity/opcode.go index 7c0bf67..a9c608c 100644 --- a/src/gopheros/device/acpi/aml/entity/opcode.go +++ b/src/gopheros/device/acpi/aml/entity/opcode.go @@ -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 { diff --git a/src/gopheros/device/acpi/aml/entity/opcode_test.go b/src/gopheros/device/acpi/aml/entity/opcode_test.go index 3cf0e4c..6f89051 100644 --- a/src/gopheros/device/acpi/aml/entity/opcode_test.go +++ b/src/gopheros/device/acpi/aml/entity/opcode_test.go @@ -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 diff --git a/src/gopheros/device/acpi/aml/entity_test.go b/src/gopheros/device/acpi/aml/entity_test.go deleted file mode 100644 index 97ea402..0000000 --- a/src/gopheros/device/acpi/aml/entity_test.go +++ /dev/null @@ -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, - }, - { - ®ionEntity{}, - []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) - } -}