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..6333c89 --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/entity.go @@ -0,0 +1,946 @@ +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, args []interface{}) *Invocation { + return &Invocation{ + Generic: Generic{ + op: OpMethodInvocation, + tableHandle: tableHandle, + args: args, + }, + 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..692f419 --- /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", nil), 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) - } -}