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..5e2b85e --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/entity.go @@ -0,0 +1,1044 @@ +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 +} + +// FieldAccessTypeProvider is an interface implemented by all field entities. +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 +} + +// 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. +func (ent *Field) ResolveSymbolRefs(rootNS Container) *kernel.Error { + var ok bool + if ent.Region, ok = FindInScope(ent.Parent(), rootNS, ent.RegionName).(*Region); !ok { + return &kernel.Error{ + Module: "acpi_aml_resolver", + Message: "could not resolve referenced field region: " + ent.RegionName, + } + } + + return nil +} + +// 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 +} + +// 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. +func (ent *IndexField) ResolveSymbolRefs(rootNS Container) *kernel.Error { + var ok bool + + if ent.IndexReg, ok = FindInScope(ent.Parent(), rootNS, ent.IndexRegName).(*FieldUnit); !ok { + return &kernel.Error{ + Module: "acpi_aml_resolver", + Message: "could not resolve referenced index register: " + ent.IndexRegName, + } + } + + if ent.DataReg, ok = FindInScope(ent.Parent(), rootNS, ent.DataRegName).(*FieldUnit); !ok { + return &kernel.Error{ + Module: "acpi_aml_resolver", + Message: "could not resolve referenced data register: " + ent.DataRegName, + } + } + + return nil +} + +// 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 +} + +// 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. +func (ent *BankField) ResolveSymbolRefs(rootNS Container) *kernel.Error { + var ok bool + + if ent.Region, ok = FindInScope(ent.Parent(), rootNS, ent.RegionName).(*Region); !ok { + return &kernel.Error{ + Module: "acpi_aml_resolver", + Message: "could not resolve referenced field region: " + ent.RegionName, + } + } + + if ent.BankFieldUnit, ok = FindInScope(ent.Parent(), rootNS, ent.BankFieldUnitName).(*FieldUnit); !ok { + return &kernel.Error{ + Module: "acpi_aml_resolver", + Message: "could not resolve referenced bank register field: " + ent.BankFieldUnitName, + } + } + + return nil +} + +// 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, + }, + } +} + +// 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. +func (ent *FieldUnit) ResolveSymbolRefs(rootNS Container) *kernel.Error { + if ent.ConnectionName == "" { + return nil + } + + if ent.Connection = FindInScope(ent.Parent(), rootNS, ent.ConnectionName); ent.Connection == nil { + return &kernel.Error{ + Module: "acpi_aml_resolver", + Message: "[field unit: " + ent.Name() + "] could not resolve connection reference: " + ent.ConnectionName, + } + } + + return nil +} + +// 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, + } +} + +// 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. +func (ent *Reference) ResolveSymbolRefs(rootNS Container) *kernel.Error { + if ent.Target = FindInScope(ent.Parent(), rootNS, ent.TargetName); ent.Target == nil { + return &kernel.Error{ + Module: "acpi_aml_vm", + Message: "could not resolve referenced symbol: " + ent.TargetName + "; parent: " + ent.Parent().Name(), + } + } + + return nil +} + +// Method describes an invocable AML method. +type Method struct { + Scope + + ArgCount uint8 + Serialized bool + SyncLevel uint8 + + BodyStartOffset uint32 + BodyEndOffset uint32 +} + +// 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 + + Method *Method +} + +// NewInvocation creates a new method invocation object. +func NewInvocation(tableHandle uint8, method *Method, args []interface{}) *Invocation { + return &Invocation{ + Generic: Generic{ + op: OpMethodInvocation, + tableHandle: tableHandle, + args: args, + }, + Method: method, + } +} + +// 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..9df2eed --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/entity_test.go @@ -0,0 +1,295 @@ +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, NewMethod(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) + } + }) + + t.Run("FieldAccessTypeProvider implementers", func(t *testing.T) { + for specIndex, spec := range specs { + provider, ok := spec.ent.(FieldAccessTypeProvider) + if !ok { + continue + } + + if exp, got := FieldAccessTypeAny, provider.DefaultAccessType(); got != exp { + t.Errorf("[spec %d] expected provider to return access type: %d; got %d", specIndex, exp, 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, + }, + { + NewField(2), + []interface{}{"REG0", uint64(128)}, + nil, // Field populates its internal state using the first 2 args + true, + }, + { + NewIndexField(2), + []interface{}{"REG0", "DAT0", uint64(128)}, + nil, // IndexField populates its internal state using the first 3 args + true, + }, + { + NewBankField(2), + []interface{}{"REG0", "BNK0", uint64(0xf00f), uint64(128)}, + nil, // BankField populates its internal state using the first 4 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.Args(); !reflect.DeepEqual(got, spec.expArgList) { + t.Errorf("[spec %d] expected to get back arg list %v; got %v", specIndex, spec.expArgList, got) + } + } +} + +func TestLazySymbolResolver(t *testing.T) { + root := NewScope(OpScope, 42, `\`) + reg0 := NewRegion(42) + reg0.SetArg(0, "REG0") + root.Append(reg0) + root.Append(NewFieldUnit(42, "FLD0")) + root.Append(NewMethod(42, "MTH0")) + + specs := []struct { + ent Entity + expErr string + }{ + { + &Field{RegionName: "MISSING"}, + "could not resolve referenced field region: MISSING", + }, + { + &Field{RegionName: "REG0"}, + "", + }, + { + &IndexField{IndexRegName: "UNKNOWN"}, + "could not resolve referenced index register: UNKNOWN", + }, + { + &IndexField{IndexRegName: "FLD0", DataRegName: "UNKNOWN"}, + "could not resolve referenced data register: UNKNOWN", + }, + { + &IndexField{IndexRegName: "FLD0", DataRegName: "FLD0"}, + "", + }, + { + &BankField{RegionName: "MISSING"}, + "could not resolve referenced field region: MISSING", + }, + { + &BankField{RegionName: "REG0", BankFieldUnitName: "UNKNOWN"}, + "could not resolve referenced bank register field: UNKNOWN", + }, + { + &BankField{RegionName: "REG0", BankFieldUnitName: "FLD0"}, + "", + }, + { + &FieldUnit{ + GenericNamed: GenericNamed{name: "FLD0"}, + ConnectionName: "MISSING", + }, + "[field unit: FLD0] could not resolve connection reference: MISSING", + }, + { + // No connection reference + &FieldUnit{}, + "", + }, + { + &FieldUnit{ConnectionName: "FLD0"}, + "", + }, + { + &Reference{TargetName: "MISSING"}, + `could not resolve referenced symbol: MISSING; parent: \`, + }, + { + &Reference{TargetName: "FLD0"}, + "", + }, + } + + for specIndex, spec := range specs { + root.Append(spec.ent) + err := spec.ent.(LazyRefResolver).ResolveSymbolRefs(root) + if spec.expErr != "" && (err == nil || err.Message != spec.expErr) { + t.Errorf("[spec %d] expected ResolveReferences to return error %q; got: %v", specIndex, spec.expErr, err) + } + } +} diff --git a/src/gopheros/device/acpi/aml/entity/opcode.go b/src/gopheros/device/acpi/aml/entity/opcode.go new file mode 100644 index 0000000..0a3abf2 --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/opcode.go @@ -0,0 +1,430 @@ +package entity + +// AMLOpcode describes an AML opcode. While AML supports 256 opcodes, some of +// them are specified using a combination of an extension prefix and a code. To +// map each opcode into a single unique value the parser uses an uint16 +// representation of the opcode values. +type AMLOpcode uint16 + +// List of AML opcodes +const ( + // Regular opcode list + OpZero = AMLOpcode(0x00) + OpOne = AMLOpcode(0x01) + OpAlias = AMLOpcode(0x06) + OpName = AMLOpcode(0x08) + OpBytePrefix = AMLOpcode(0x0a) + OpWordPrefix = AMLOpcode(0x0b) + OpDwordPrefix = AMLOpcode(0x0c) + OpStringPrefix = AMLOpcode(0x0d) + OpQwordPrefix = AMLOpcode(0x0e) + OpScope = AMLOpcode(0x10) + OpBuffer = AMLOpcode(0x11) + OpPackage = AMLOpcode(0x12) + OpVarPackage = AMLOpcode(0x13) + OpMethod = AMLOpcode(0x14) + OpExternal = AMLOpcode(0x15) + OpLocal0 = AMLOpcode(0x60) + OpLocal1 = AMLOpcode(0x61) + OpLocal2 = AMLOpcode(0x62) + OpLocal3 = AMLOpcode(0x63) + OpLocal4 = AMLOpcode(0x64) + OpLocal5 = AMLOpcode(0x65) + OpLocal6 = AMLOpcode(0x66) + OpLocal7 = AMLOpcode(0x67) + OpArg0 = AMLOpcode(0x68) + OpArg1 = AMLOpcode(0x69) + OpArg2 = AMLOpcode(0x6a) + OpArg3 = AMLOpcode(0x6b) + OpArg4 = AMLOpcode(0x6c) + OpArg5 = AMLOpcode(0x6d) + OpArg6 = AMLOpcode(0x6e) + OpStore = AMLOpcode(0x70) + OpRefOf = AMLOpcode(0x71) + OpAdd = AMLOpcode(0x72) + OpConcat = AMLOpcode(0x73) + OpSubtract = AMLOpcode(0x74) + OpIncrement = AMLOpcode(0x75) + OpDecrement = AMLOpcode(0x76) + OpMultiply = AMLOpcode(0x77) + OpDivide = AMLOpcode(0x78) + OpShiftLeft = AMLOpcode(0x79) + OpShiftRight = AMLOpcode(0x7a) + OpAnd = AMLOpcode(0x7b) + OpNand = AMLOpcode(0x7c) + OpOr = AMLOpcode(0x7d) + OpNor = AMLOpcode(0x7e) + OpXor = AMLOpcode(0x7f) + OpNot = AMLOpcode(0x80) + OpFindSetLeftBit = AMLOpcode(0x81) + OpFindSetRightBit = AMLOpcode(0x82) + OpDerefOf = AMLOpcode(0x83) + OpConcatRes = AMLOpcode(0x84) + OpMod = AMLOpcode(0x85) + OpNotify = AMLOpcode(0x86) + OpSizeOf = AMLOpcode(0x87) + OpIndex = AMLOpcode(0x88) + OpMatch = AMLOpcode(0x89) + OpCreateDWordField = AMLOpcode(0x8a) + OpCreateWordField = AMLOpcode(0x8b) + OpCreateByteField = AMLOpcode(0x8c) + OpCreateBitField = AMLOpcode(0x8d) + OpObjectType = AMLOpcode(0x8e) + OpCreateQWordField = AMLOpcode(0x8f) + OpLand = AMLOpcode(0x90) + OpLor = AMLOpcode(0x91) + OpLnot = AMLOpcode(0x92) + OpLEqual = AMLOpcode(0x93) + OpLGreater = AMLOpcode(0x94) + OpLLess = AMLOpcode(0x95) + OpToBuffer = AMLOpcode(0x96) + OpToDecimalString = AMLOpcode(0x97) + OpToHexString = AMLOpcode(0x98) + OpToInteger = AMLOpcode(0x99) + OpToString = AMLOpcode(0x9c) + OpCopyObject = AMLOpcode(0x9d) + OpMid = AMLOpcode(0x9e) + OpContinue = AMLOpcode(0x9f) + OpIf = AMLOpcode(0xa0) + OpElse = AMLOpcode(0xa1) + OpWhile = AMLOpcode(0xa2) + OpNoop = AMLOpcode(0xa3) + OpReturn = AMLOpcode(0xa4) + OpBreak = AMLOpcode(0xa5) + OpBreakPoint = AMLOpcode(0xcc) + OpOnes = AMLOpcode(0xff) + // Extended opcodes + OpMutex = AMLOpcode(0xff + 0x01) + OpEvent = AMLOpcode(0xff + 0x02) + OpCondRefOf = AMLOpcode(0xff + 0x12) + OpCreateField = AMLOpcode(0xff + 0x13) + OpLoadTable = AMLOpcode(0xff + 0x1f) + OpLoad = AMLOpcode(0xff + 0x20) + OpStall = AMLOpcode(0xff + 0x21) + OpSleep = AMLOpcode(0xff + 0x22) + OpAcquire = AMLOpcode(0xff + 0x23) + OpSignal = AMLOpcode(0xff + 0x24) + OpWait = AMLOpcode(0xff + 0x25) + OpReset = AMLOpcode(0xff + 0x26) + OpRelease = AMLOpcode(0xff + 0x27) + OpFromBCD = AMLOpcode(0xff + 0x28) + OpToBCD = AMLOpcode(0xff + 0x29) + OpUnload = AMLOpcode(0xff + 0x2a) + OpRevision = AMLOpcode(0xff + 0x30) + OpDebug = AMLOpcode(0xff + 0x31) + OpFatal = AMLOpcode(0xff + 0x32) + OpTimer = AMLOpcode(0xff + 0x33) + OpOpRegion = AMLOpcode(0xff + 0x80) + OpField = AMLOpcode(0xff + 0x81) + OpDevice = AMLOpcode(0xff + 0x82) + OpProcessor = AMLOpcode(0xff + 0x83) + OpPowerRes = AMLOpcode(0xff + 0x84) + OpThermalZone = AMLOpcode(0xff + 0x85) + 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 { + return op >= OpLocal0 && op <= OpLocal7 +} + +// OpIsMethodArg returns true if this opcode represents any of the supported +// input function args 0 to 6. +func OpIsMethodArg(op AMLOpcode) bool { + return op >= OpArg0 && op <= OpArg6 +} + +// OpIsArg returns true if this opcode is either a local or a method arg. +func OpIsArg(op AMLOpcode) bool { + return OpIsLocalArg(op) || OpIsMethodArg(op) +} + +// OpIsDataObject returns true if this opcode is part of a DataObject definition +// +// Grammar: +// DataObject := ComputationalData | DefPackage | DefVarPackage +// ComputationalData := ByteConst | WordConst | DWordConst | QWordConst | String | ConstObj | RevisionOp | DefBuffer +// ConstObj := ZeroOp | OneOp | OnesOp +func OpIsDataObject(op AMLOpcode) bool { + switch op { + case OpBytePrefix, OpWordPrefix, OpDwordPrefix, OpQwordPrefix, OpStringPrefix, + OpZero, OpOne, OpOnes, OpRevision, OpBuffer, OpPackage, OpVarPackage: + return true + default: + return false + } +} + +// OpIsType2 returns true if this is a Type2Opcode. +// +// Grammar: +// Type2Opcode := DefAcquire | DefAdd | DefAnd | DefBuffer | DefConcat | +// DefConcatRes | DefCondRefOf | DefCopyObject | DefDecrement | +// DefDerefOf | DefDivide | DefFindSetLeftBit | DefFindSetRightBit | +// DefFromBCD | DefIncrement | DefIndex | DefLAnd | DefLEqual | +// DefLGreater | DefLGreaterEqual | DefLLess | DefLLessEqual | DefMid | +// DefLNot | DefLNotEqual | DefLoadTable | DefLOr | DefMatch | DefMod | +// DefMultiply | DefNAnd | DefNOr | DefNot | DefObjectType | DefOr | +// DefPackage | DefVarPackage | DefRefOf | DefShiftLeft | DefShiftRight | +// DefSizeOf | DefStore | DefSubtract | DefTimer | DefToBCD | DefToBuffer | +// DefToDecimalString | DefToHexString | DefToInteger | DefToString | +// DefWait | DefXOr +func OpIsType2(op AMLOpcode) bool { + switch op { + case OpAcquire, OpAdd, OpAnd, OpBuffer, OpConcat, + OpConcatRes, OpCondRefOf, OpCopyObject, OpDecrement, + OpDerefOf, OpDivide, OpFindSetLeftBit, OpFindSetRightBit, + OpFromBCD, OpIncrement, OpIndex, OpLand, OpLEqual, + OpLGreater, OpLLess, OpMid, + OpLnot, OpLoadTable, OpLor, OpMatch, OpMod, + OpMultiply, OpNand, OpNor, OpNot, OpObjectType, OpOr, + OpPackage, OpVarPackage, OpRefOf, OpShiftLeft, OpShiftRight, + OpSizeOf, OpStore, OpSubtract, OpTimer, OpToBCD, OpToBuffer, + OpToDecimalString, OpToHexString, OpToInteger, OpToString, + OpWait, OpXor: + return true + default: + return false + } +} diff --git a/src/gopheros/device/acpi/aml/entity/opcode_test.go b/src/gopheros/device/acpi/aml/entity/opcode_test.go new file mode 100644 index 0000000..23c0bdd --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/opcode_test.go @@ -0,0 +1,237 @@ +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 + testFn func(AMLOpcode) bool + want bool + }{ + // OpIsLocalArg + {OpLocal0, OpIsLocalArg, true}, + {OpLocal1, OpIsLocalArg, true}, + {OpLocal2, OpIsLocalArg, true}, + {OpLocal3, OpIsLocalArg, true}, + {OpLocal4, OpIsLocalArg, true}, + {OpLocal5, OpIsLocalArg, true}, + {OpLocal6, OpIsLocalArg, true}, + {OpLocal7, OpIsLocalArg, true}, + {OpArg0, OpIsLocalArg, false}, + {OpDivide, OpIsLocalArg, false}, + // OpIsMethodArg + {OpArg0, OpIsMethodArg, true}, + {OpArg1, OpIsMethodArg, true}, + {OpArg2, OpIsMethodArg, true}, + {OpArg3, OpIsMethodArg, true}, + {OpArg4, OpIsMethodArg, true}, + {OpArg5, OpIsMethodArg, true}, + {OpArg6, OpIsMethodArg, true}, + {OpLocal7, OpIsMethodArg, false}, + {OpIf, OpIsMethodArg, false}, + // OpIsArg + {OpLocal5, OpIsArg, true}, + {OpArg1, OpIsArg, true}, + {OpDivide, OpIsArg, false}, + // OpIsType2 + {OpAcquire, OpIsType2, true}, + {OpAdd, OpIsType2, true}, + {OpAnd, OpIsType2, true}, + {OpBuffer, OpIsType2, true}, + {OpConcat, OpIsType2, true}, + {OpConcatRes, OpIsType2, true}, + {OpCondRefOf, OpIsType2, true}, + {OpCopyObject, OpIsType2, true}, + {OpDecrement, OpIsType2, true}, + {OpDerefOf, OpIsType2, true}, + {OpDivide, OpIsType2, true}, + {OpFindSetLeftBit, OpIsType2, true}, + {OpFindSetRightBit, OpIsType2, true}, + {OpFromBCD, OpIsType2, true}, + {OpIncrement, OpIsType2, true}, + {OpIndex, OpIsType2, true}, + {OpLand, OpIsType2, true}, + {OpLEqual, OpIsType2, true}, + {OpLGreater, OpIsType2, true}, + {OpLLess, OpIsType2, true}, + {OpMid, OpIsType2, true}, + {OpLnot, OpIsType2, true}, + {OpLoadTable, OpIsType2, true}, + {OpLor, OpIsType2, true}, + {OpMatch, OpIsType2, true}, + {OpMod, OpIsType2, true}, + {OpMultiply, OpIsType2, true}, + {OpNand, OpIsType2, true}, + {OpNor, OpIsType2, true}, + {OpNot, OpIsType2, true}, + {OpObjectType, OpIsType2, true}, + {OpOr, OpIsType2, true}, + {OpPackage, OpIsType2, true}, + {OpVarPackage, OpIsType2, true}, + {OpRefOf, OpIsType2, true}, + {OpShiftLeft, OpIsType2, true}, + {OpShiftRight, OpIsType2, true}, + {OpSizeOf, OpIsType2, true}, + {OpStore, OpIsType2, true}, + {OpSubtract, OpIsType2, true}, + {OpTimer, OpIsType2, true}, + {OpToBCD, OpIsType2, true}, + {OpToBuffer, OpIsType2, true}, + {OpToDecimalString, OpIsType2, true}, + {OpToHexString, OpIsType2, true}, + {OpToInteger, OpIsType2, true}, + {OpToString, OpIsType2, true}, + {OpWait, OpIsType2, true}, + {OpXor, OpIsType2, true}, + {OpBytePrefix, OpIsType2, false}, + // OpIsDataObject + {OpBytePrefix, OpIsDataObject, true}, + {OpWordPrefix, OpIsDataObject, true}, + {OpDwordPrefix, OpIsDataObject, true}, + {OpQwordPrefix, OpIsDataObject, true}, + {OpStringPrefix, OpIsDataObject, true}, + {OpZero, OpIsDataObject, true}, + {OpOne, OpIsDataObject, true}, + {OpOnes, OpIsDataObject, true}, + {OpRevision, OpIsDataObject, true}, + {OpBuffer, OpIsDataObject, true}, + {OpPackage, OpIsDataObject, true}, + {OpVarPackage, OpIsDataObject, true}, + {OpLor, OpIsDataObject, false}, + } + + for specIndex, spec := range specs { + if got := spec.testFn(spec.op); got != spec.want { + t.Errorf("[spec %d] opcode %q: expected to get %t; got %t", specIndex, spec.op, spec.want, got) + } + } +} diff --git a/src/gopheros/device/acpi/aml/scope.go b/src/gopheros/device/acpi/aml/entity/scope.go similarity index 54% rename from src/gopheros/device/acpi/aml/scope.go rename to src/gopheros/device/acpi/aml/entity/scope.go index 4057fea..8537950 100644 --- a/src/gopheros/device/acpi/aml/scope.go +++ b/src/gopheros/device/acpi/aml/entity/scope.go @@ -1,68 +1,14 @@ -package aml +package entity import "strings" -// Visitor is a function invoked by the VM for each AML tree entity that matches -// a particular type. The return value controls whether the children of this -// entity should also be visited. -type Visitor func(depth int, obj Entity) (keepRecursing bool) - -// EntityType defines the type of entity that visitors should inspect. -type EntityType uint8 - -// The list of supported EntityType values. EntityTypeAny works as a wildcard -// allowing the visitor to inspect all entities in the AML tree. -const ( - EntityTypeAny EntityType = iota - EntityTypeDevice - EntityTypeProcessor - EntityTypePowerResource - EntityTypeThermalZone - EntityTypeMethod -) - -// scopeVisit descends a scope hierarchy and invokes visitorFn for each entity -// that matches entType. -func scopeVisit(depth int, ent Entity, entType EntityType, visitorFn Visitor) bool { - op := ent.getOpcode() - switch { - case (entType == EntityTypeAny) || - (entType == EntityTypeDevice && op == opDevice) || - (entType == EntityTypeProcessor && op == opProcessor) || - (entType == EntityTypePowerResource && op == opPowerRes) || - (entType == EntityTypeThermalZone && op == opThermalZone) || - (entType == EntityTypeMethod && op == opMethod): - // If the visitor returned false we should not visit the children - if !visitorFn(depth, ent) { - return false - } - - // Visit any args that are also entities - for _, arg := range ent.getArgs() { - if argEnt, isEnt := arg.(Entity); isEnt && !scopeVisit(depth+1, argEnt, entType, visitorFn) { - return false - } - } - } - - switch typ := ent.(type) { - case ScopeEntity: - // If the entity defines a scope we need to visit the child entities. - for _, child := range typ.Children() { - _ = scopeVisit(depth+1, child, entType, visitorFn) - } - } - - return true -} - -// scopeResolvePath examines a path expression and attempts to break it down +// ResolveScopedPath examines a path expression and attempts to break it down // into a parent and child segment. The parent segment is looked up via the // regular scope rules specified in page 252 of the ACPI 6.2 spec. If the // parent scope is found then the function returns back the parent entity and // the name of the child that should be appended to it. If the expression // lookup fails then the function returns nil, "". -func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent ScopeEntity, name string) { +func ResolveScopedPath(curScope, rootScope Container, expr string) (parent Container, name string) { if len(expr) <= 1 { return nil, "" } @@ -75,8 +21,8 @@ func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent Scop return rootScope, expr[1:] case '^': lastHatIndex := strings.LastIndexByte(expr, '^') - if target := scopeFind(curScope, rootScope, expr[:lastHatIndex+1]); target != nil { - return target.(ScopeEntity), expr[lastHatIndex+1:] + if target := FindInScope(curScope, rootScope, expr[:lastHatIndex+1]); target != nil { + return target.(Container), expr[lastHatIndex+1:] } return nil, "" @@ -86,14 +32,14 @@ func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent Scop } // Pattern looks like: \FOO.BAR.BAZ or ^+FOO.BAR.BAZ or FOO.BAR.BAZ - if target := scopeFind(curScope, rootScope, expr[:lastDotIndex]); target != nil { - return target.(ScopeEntity), expr[lastDotIndex+1:] + if target := FindInScope(curScope, rootScope, expr[:lastDotIndex]); target != nil { + return target.(Container), expr[lastDotIndex+1:] } return nil, "" } -// scopeFind attempts to find an object with the given name using the rules +// FindInScope attempts to find an object with the given name using the rules // specified in page 252 of the ACPI 6.2 spec: // // There are two types of namespace paths: an absolute namespace path (that is, @@ -104,7 +50,7 @@ func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent Scop // or Parent Prefixes, ‘^’, the search rules do not apply. If the search rules // do not apply to a relative namespace path, the namespace object is looked up // relative to the current namespace -func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { +func FindInScope(curScope, rootScope Container, name string) Entity { nameLen := len(name) if nameLen == 0 { return nil @@ -113,7 +59,7 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { switch { case name[0] == '\\': // relative to the root scope if nameLen > 1 { - return scopeFindRelative(rootScope, name[1:]) + return findRelativeToScope(rootScope, name[1:]) } // Name was just `\`; this matches the root namespace @@ -130,7 +76,7 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { } default: // Found the start of the name. Look it up relative to curNs - return scopeFindRelative(curScope, name[startIndex:]) + return findRelativeToScope(curScope, name[startIndex:]) } } @@ -139,7 +85,7 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { case strings.ContainsRune(name, '.'): // If the name contains any '.' then we still need to look it // up relative to the current scope - return scopeFindRelative(curScope, name) + return findRelativeToScope(curScope, name) default: // We can apply the search rules described by the spec for s := curScope; s != nil; s = s.Parent() { @@ -155,11 +101,11 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { return nil } -// scopeFindRelative returns the Entity referenced by path relative +// findRelativeToScope returns the Entity referenced by path relative // to the provided Namespace. If the name contains dots, each segment // is used to access a nested namespace. If the path does not point // to a NamedObject then lookupRelativeTo returns back nil. -func scopeFindRelative(ns ScopeEntity, path string) Entity { +func findRelativeToScope(ns Container, path string) Entity { var matchName string matchNextPathSegment: for { @@ -170,12 +116,7 @@ matchNextPathSegment: // Search for a scoped child named "matchName" for _, child := range ns.Children() { - childNs, ok := child.(ScopeEntity) - if !ok { - continue - } - - if childNs.Name() == matchName { + if childNs, ok := child.(Container); ok && childNs.Name() == matchName { ns = childNs continue matchNextPathSegment } diff --git a/src/gopheros/device/acpi/aml/entity/scope_test.go b/src/gopheros/device/acpi/aml/entity/scope_test.go new file mode 100644 index 0000000..5d22117 --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/scope_test.go @@ -0,0 +1,215 @@ +package entity + +import ( + "reflect" + "testing" +) + +func TestResolveScopedPath(t *testing.T) { + scopeMap := genTestScopes() + + specs := []struct { + curScope Container + pathExpr string + wantParent Entity + wantName string + }{ + { + scopeMap["IDE0"].(Container), + `\_SB_`, + scopeMap[`\`], + "_SB_", + }, + { + scopeMap["IDE0"].(Container), + `^FOO`, + scopeMap[`PCI0`], + "FOO", + }, + { + scopeMap["IDE0"].(Container), + `^^FOO`, + scopeMap[`_SB_`], + "FOO", + }, + { + scopeMap["IDE0"].(Container), + `_ADR`, + scopeMap[`IDE0`], + "_ADR", + }, + // Paths with dots + { + scopeMap["IDE0"].(Container), + `\_SB_.PCI0.IDE0._ADR`, + scopeMap[`IDE0`], + "_ADR", + }, + { + scopeMap["PCI0"].(Container), + `IDE0._ADR`, + scopeMap[`IDE0`], + "_ADR", + }, + { + scopeMap["PCI0"].(Container), + `_CRS`, + scopeMap[`PCI0`], + "_CRS", + }, + // Bad queries + { + scopeMap["PCI0"].(Container), + `FOO.BAR.BAZ`, + nil, + "", + }, + { + scopeMap["PCI0"].(Container), + ``, + nil, + "", + }, + { + scopeMap["PCI0"].(Container), + `\`, + nil, + "", + }, + { + scopeMap["PCI0"].(Container), + `^^^^^^^^^BADPATH`, + nil, + "", + }, + } + + root := scopeMap[`\`].(Container) + for specIndex, spec := range specs { + gotParent, gotName := ResolveScopedPath(spec.curScope, root, spec.pathExpr) + if !reflect.DeepEqual(gotParent, spec.wantParent) { + t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.wantParent, gotParent) + continue + } + + if gotName != spec.wantName { + t.Errorf("[spec %d] expected lookup to return node name %q; got %q", specIndex, spec.wantName, gotName) + } + } +} + +func TestFindInScope(t *testing.T) { + scopeMap := genTestScopes() + + specs := []struct { + curScope Container + lookup string + want Entity + }{ + // Search rules do not apply for these cases + { + scopeMap["PCI0"].(Container), + `\`, + scopeMap[`\`], + }, + { + scopeMap["PCI0"].(Container), + "IDE0._ADR", + scopeMap["_ADR"], + }, + { + scopeMap["IDE0"].(Container), + "^^PCI0.IDE0._ADR", + scopeMap["_ADR"], + }, + { + scopeMap["IDE0"].(Container), + `\_SB_.PCI0.IDE0._ADR`, + scopeMap["_ADR"], + }, + { + scopeMap["IDE0"].(Container), + `\_SB_.PCI0`, + scopeMap["PCI0"], + }, + { + scopeMap["IDE0"].(Container), + `^`, + scopeMap["PCI0"], + }, + // Bad queries + { + scopeMap["_SB_"].(Container), + "PCI0.USB._CRS", + nil, + }, + { + scopeMap["IDE0"].(Container), + "^^^^^^^^^^^^^^^^^^^", + nil, + }, + { + scopeMap["IDE0"].(Container), + `^^^^^^^^^^^FOO`, + nil, + }, + { + scopeMap["IDE0"].(Container), + "FOO", + nil, + }, + { + scopeMap["IDE0"].(Container), + "", + nil, + }, + // Search rules apply for these cases + { + scopeMap["IDE0"].(Container), + "_CRS", + scopeMap["_CRS"], + }, + } + + root := scopeMap[`\`].(Container) + for specIndex, spec := range specs { + if got := FindInScope(spec.curScope, root, spec.lookup); !reflect.DeepEqual(got, spec.want) { + t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.want, got) + } + } +} + +func genTestScopes() map[string]Entity { + // Setup the example tree from page 252 of the acpi 6.2 spec + // \ + // SB + // \ + // PCI0 + // | _CRS + // \ + // IDE0 + // | _ADR + ideScope := NewScope(OpScope, 42, `IDE0`) + pciScope := NewScope(OpScope, 42, `PCI0`) + sbScope := NewScope(OpScope, 42, `_SB_`) + rootScope := NewScope(OpScope, 42, `\`) + + adr := NewMethod(42, `_ADR`) + crs := NewMethod(42, `_CRS`) + + // Setup tree + ideScope.Append(adr) + pciScope.Append(crs) + pciScope.Append(ideScope) + sbScope.Append(pciScope) + rootScope.Append(sbScope) + + return map[string]Entity{ + "IDE0": ideScope, + "PCI0": pciScope, + "_SB_": sbScope, + "\\": rootScope, + "_ADR": adr, + "_CRS": crs, + } +} diff --git a/src/gopheros/device/acpi/aml/entity/visitor.go b/src/gopheros/device/acpi/aml/entity/visitor.go new file mode 100644 index 0000000..0b82550 --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/visitor.go @@ -0,0 +1,64 @@ +package entity + +// Visitor is a function invoked by the VM for each AML tree entity that matches +// a particular type. The return value controls whether the children of this +// entity should also be visited. +type Visitor func(depth int, obj Entity) (keepRecursing bool) + +// Type defines the type of entity that visitors should inspect. +type Type uint8 + +// The list of supported Type values. TypeAny works as a wildcard +// allowing the visitor to inspect all entities in the AML tree. +const ( + TypeAny Type = iota + TypeDevice + TypeProcessor + TypePowerResource + TypeThermalZone + TypeMethod + TypeMutex + TypeEvent + TypeField + TypeIndexField + TypeBankField +) + +// Visit descends a scope hierarchy and invokes visitorFn for each entity +// that matches entType. +func Visit(depth int, ent Entity, entType Type, visitorFn Visitor) bool { + op := ent.Opcode() + switch { + case (entType == TypeAny) || + (entType == TypeDevice && op == OpDevice) || + (entType == TypeProcessor && op == OpProcessor) || + (entType == TypePowerResource && op == OpPowerRes) || + (entType == TypeThermalZone && op == OpThermalZone) || + (entType == TypeMethod && op == OpMethod) || + (entType == TypeMutex && op == OpMutex) || + (entType == TypeEvent && op == OpEvent) || + (entType == TypeField && op == OpField) || + (entType == TypeIndexField && op == OpIndexField) || + (entType == TypeBankField && op == OpBankField): + // If the visitor returned false we should not visit the children + if !visitorFn(depth, ent) { + return false + } + + // Visit any args that are also entities + for _, arg := range ent.Args() { + if argEnt, isEnt := arg.(Entity); isEnt && !Visit(depth+1, argEnt, entType, visitorFn) { + return false + } + } + } + + // If the entity defines a scope we need to visit the child entities. + if container, isContainer := ent.(Container); isContainer { + for _, child := range container.Children() { + _ = Visit(depth+1, child, entType, visitorFn) + } + } + + return true +} diff --git a/src/gopheros/device/acpi/aml/entity/visitor_test.go b/src/gopheros/device/acpi/aml/entity/visitor_test.go new file mode 100644 index 0000000..d4368b7 --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/visitor_test.go @@ -0,0 +1,93 @@ +package entity + +import "testing" + +func TestScopeVisit(t *testing.T) { + tableHandle := uint8(42) + keepRecursing := func(Entity) bool { return true } + stopRecursing := func(Entity) bool { return false } + + // Append special entities under IDE0 + root := NewScope(OpScope, tableHandle, "IDE0") + root.Append(NewDevice(tableHandle, "DEV0")) + root.Append(NewProcessor(tableHandle, "FOO0")) + root.Append(NewProcessor(tableHandle, "FOO0")) + root.Append(NewPowerResource(tableHandle, "FOO0")) + root.Append(NewPowerResource(tableHandle, "FOO0")) + root.Append(NewPowerResource(tableHandle, "FOO0")) + root.Append(NewThermalZone(tableHandle, "FOO0")) + root.Append(NewThermalZone(tableHandle, "FOO0")) + root.Append(NewThermalZone(tableHandle, "FOO0")) + root.Append(NewThermalZone(tableHandle, "FOO0")) + root.Append(NewMethod(tableHandle, "MTH0")) + root.Append(NewMethod(tableHandle, "MTH1")) + root.Append(NewMethod(tableHandle, "MTH2")) + root.Append(NewMethod(tableHandle, "MTH3")) + root.Append(NewMethod(tableHandle, "MTH4")) + root.Append(NewMutex(tableHandle)) + root.Append(NewMutex(tableHandle)) + root.Append(NewEvent(tableHandle)) + root.Append(NewEvent(tableHandle)) + root.Append(NewEvent(tableHandle)) + root.Append(NewField(tableHandle)) + root.Append(NewIndexField(tableHandle)) + root.Append(NewBankField(tableHandle)) + root.Append(&Invocation{ + Generic: Generic{ + op: OpMethodInvocation, + args: []interface{}{ + NewConst(OpOne, tableHandle, uint64(1)), + NewConst(OpDwordPrefix, tableHandle, uint64(2)), + }, + }, + }) + + specs := []struct { + searchType Type + keepRecursingFn func(Entity) bool + wantHits int + }{ + {TypeAny, keepRecursing, 27}, + {TypeAny, stopRecursing, 1}, + { + TypeAny, + func(ent Entity) bool { + // Stop recursing after visiting the Invocation entity + _, isInv := ent.(*Invocation) + return !isInv + }, + 25, + }, + { + TypeAny, + func(ent Entity) bool { + // Stop recursing after visiting the first Const entity + _, isConst := ent.(*Const) + return !isConst + }, + 26, + }, + {TypeDevice, keepRecursing, 1}, + {TypeProcessor, keepRecursing, 2}, + {TypePowerResource, keepRecursing, 3}, + {TypeThermalZone, keepRecursing, 4}, + {TypeMethod, keepRecursing, 5}, + {TypeMutex, keepRecursing, 2}, + {TypeEvent, keepRecursing, 3}, + {TypeField, keepRecursing, 1}, + {TypeIndexField, keepRecursing, 1}, + {TypeBankField, keepRecursing, 1}, + } + + for specIndex, spec := range specs { + var hits int + Visit(0, root, spec.searchType, func(_ int, obj Entity) bool { + hits++ + return spec.keepRecursingFn(obj) + }) + + if hits != spec.wantHits { + t.Errorf("[spec %d] expected visitor to be called %d times; got %d", specIndex, spec.wantHits, hits) + } + } +} 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) - } -} diff --git a/src/gopheros/device/acpi/aml/opcode.go b/src/gopheros/device/acpi/aml/opcode.go deleted file mode 100644 index 617fc7a..0000000 --- a/src/gopheros/device/acpi/aml/opcode.go +++ /dev/null @@ -1,269 +0,0 @@ -package aml - -// opcode describes an AML opcode. While AML supports 256 opcodes, some of them -// are specified using a combination of an extension prefix and a code. To map -// each opcode into a single unique value the parser uses an uint16 -// representation of the opcode values. -type opcode uint16 - -// String implements fmt.Stringer for opcode. -func (op opcode) String() string { - for _, entry := range opcodeTable { - if entry.op == op { - return entry.name - } - } - - return "unknown" -} - -// opIsLocalArg returns true if this opcode represents any of the supported local -// function args 0 to 7. -func opIsLocalArg(op opcode) bool { - return op >= opLocal0 && op <= opLocal7 -} - -// opIsMethodArg returns true if this opcode represents any of the supported -// input function args 0 to 6. -func opIsMethodArg(op opcode) bool { - return op >= opArg0 && op <= opArg6 -} - -// opIsArg returns true if this opcode is either a local or a method arg. -func opIsArg(op opcode) bool { - return opIsLocalArg(op) || opIsMethodArg(op) -} - -// opIsDataObject returns true if this opcode is part of a DataObject definition -// -// Grammar: -// DataObject := ComputationalData | DefPackage | DefVarPackage -// ComputationalData := ByteConst | WordConst | DWordConst | QWordConst | String | ConstObj | RevisionOp | DefBuffer -// ConstObj := ZeroOp | OneOp | OnesOp -func opIsDataObject(op opcode) bool { - switch op { - case opBytePrefix, opWordPrefix, opDwordPrefix, opQwordPrefix, opStringPrefix, - opZero, opOne, opOnes, opRevision, opBuffer, opPackage, opVarPackage: - return true - default: - return false - } -} - -// opIsType2 returns true if this is a Type2Opcode. -// -// Grammar: -// Type2Opcode := DefAcquire | DefAdd | DefAnd | DefBuffer | DefConcat | -// DefConcatRes | DefCondRefOf | DefCopyObject | DefDecrement | -// DefDerefOf | DefDivide | DefFindSetLeftBit | DefFindSetRightBit | -// DefFromBCD | DefIncrement | DefIndex | DefLAnd | DefLEqual | -// DefLGreater | DefLGreaterEqual | DefLLess | DefLLessEqual | DefMid | -// DefLNot | DefLNotEqual | DefLoadTable | DefLOr | DefMatch | DefMod | -// DefMultiply | DefNAnd | DefNOr | DefNot | DefObjectType | DefOr | -// DefPackage | DefVarPackage | DefRefOf | DefShiftLeft | DefShiftRight | -// DefSizeOf | DefStore | DefSubtract | DefTimer | DefToBCD | DefToBuffer | -// DefToDecimalString | DefToHexString | DefToInteger | DefToString | -// DefWait | DefXOr -func opIsType2(op opcode) bool { - switch op { - case opAcquire, opAdd, opAnd, opBuffer, opConcat, - opConcatRes, opCondRefOf, opCopyObject, opDecrement, - opDerefOf, opDivide, opFindSetLeftBit, opFindSetRightBit, - opFromBCD, opIncrement, opIndex, opLand, opLEqual, - opLGreater, opLLess, opMid, - opLnot, opLoadTable, opLor, opMatch, opMod, - opMultiply, opNand, opNor, opNot, opObjectType, opOr, - opPackage, opVarPackage, opRefOf, opShiftLeft, opShiftRight, - opSizeOf, opStore, opSubtract, opTimer, opToBCD, opToBuffer, - opToDecimalString, opToHexString, opToInteger, opToString, - opWait, opXor: - return true - default: - return false - } -} - -// opIsBufferField returens true if this opcode describes a -// buffer field creation operation. -func opIsBufferField(op opcode) bool { - switch op { - case opCreateField, opCreateBitField, opCreateByteField, opCreateWordField, opCreateDWordField, opCreateQWordField: - return true - default: - return false - } -} - -// objType represents the object types that are supported by the AML parser. -type objType uint8 - -// The list of AML object types. -const ( - objTypeAny objType = iota - objTypeInteger - objTypeString - objTypeBuffer - objTypePackage - objTypeDevice - objTypeEvent - objTypeMethod - objTypeMutex - objTypeRegion - objTypePower - objTypeProcessor - objTypeThermal - objTypeBufferField - objTypeLocalRegionField - objTypeLocalBankField - objTypeLocalReference - objTypeLocalAlias - objTypeLocalScope - objTypeLocalVariable - objTypeMethodArgument -) - -// opFlag specifies a list of OR-able flags that describe the object -// type/attributes generated by a particular opcode. -type opFlag uint16 - -const ( - opFlagNone opFlag = 1 << iota - opFlagHasPkgLen - opFlagNamed - opFlagConstant - opFlagReference - opFlagArithmetic - opFlagCreate - opFlagReturn - opFlagExecutable - opFlagNoOp - opFlagScoped -) - -// is returns true if f is set in this opFlag. -func (fl opFlag) is(f opFlag) bool { - return (fl & f) != 0 -} - -// opArgFlags encodes up to 7 opArgFlag values in a uint64 value. -type opArgFlags uint64 - -// argCount returns the number of encoded args in the given flag. -func (fl opArgFlags) argCount() (count uint8) { - // Each argument is specified using 8 bits with 0x0 indicating the end of the - // argument list - for ; fl&0xf != 0; fl, count = fl>>8, count+1 { - } - - return count -} - -// arg returns the arg flags for argument "num" where num is the 0-based index -// of the argument to return. The allowed values for num are 0-6. -func (fl opArgFlags) arg(num uint8) opArgFlag { - return opArgFlag((fl >> (num * 8)) & 0xf) -} - -// contains returns true if the arg flags contain any argument with type x. -func (fl opArgFlags) contains(x opArgFlag) bool { - // Each argument is specified using 8 bits with 0x0 indicating the end of the - // argument list - for ; fl&0xf != 0; fl >>= 8 { - if opArgFlag(fl&0xf) == x { - return true - } - } - - return false -} - -// opArgFlag represents the type of an argument expected by a particular opcode. -type opArgFlag uint8 - -// The list of supported opArgFlag values. -const ( - _ opArgFlag = iota - opArgTermList - opArgTermObj - opArgByteList - opArgPackage - opArgString - opArgByteData - opArgWord - opArgDword - opArgQword - opArgNameString - opArgSuperName - opArgSimpleName - opArgDataRefObj - opArgTarget - opArgFieldList -) - -// String implements fmt.Stringer for opArgFlag. -func (fl opArgFlag) String() string { - switch fl { - case opArgTermList: - return "opArgTermList" - case opArgTermObj: - return "opArgTermObj" - case opArgByteList: - return "opArgByteList" - case opArgPackage: - return "opArgPackage" - case opArgString: - return "opArgString" - case opArgByteData: - return "opArgByteData" - case opArgWord: - return "opArgWord" - case opArgDword: - return "opArgDword" - case opArgQword: - return "opArgQword" - case opArgNameString: - return "opArgNameString" - case opArgSuperName: - return "opArgSuperName" - case opArgSimpleName: - return "opArgSimpleName" - case opArgDataRefObj: - return "opArgDataRefObj" - case opArgTarget: - return "opArgTarget" - case opArgFieldList: - return "opArgFieldList" - } - return "" -} - -func makeArg0() opArgFlags { return 0 } -func makeArg1(arg0 opArgFlag) opArgFlags { return opArgFlags(arg0) } -func makeArg2(arg0, arg1 opArgFlag) opArgFlags { return opArgFlags(arg1)<<8 | opArgFlags(arg0) } -func makeArg3(arg0, arg1, arg2 opArgFlag) opArgFlags { - return opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) -} -func makeArg4(arg0, arg1, arg2, arg3 opArgFlag) opArgFlags { - return opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) -} -func makeArg5(arg0, arg1, arg2, arg3, arg4 opArgFlag) opArgFlags { - return opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) -} -func makeArg6(arg0, arg1, arg2, arg3, arg4, arg5 opArgFlag) opArgFlags { - return opArgFlags(arg5)<<40 | opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) -} -func makeArg7(arg0, arg1, arg2, arg3, arg4, arg5, arg6 opArgFlag) opArgFlags { - return opArgFlags(arg6)<<48 | opArgFlags(arg5)<<40 | opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) -} - -// opcodeInfo contains all known information about an opcode, -// its argument count and types as well as the type of object -// represented by it. -type opcodeInfo struct { - op opcode - name string - objType objType - - flags opFlag - argFlags opArgFlags -} diff --git a/src/gopheros/device/acpi/aml/opcode_table.go b/src/gopheros/device/acpi/aml/opcode_table.go deleted file mode 100644 index ba8e500..0000000 --- a/src/gopheros/device/acpi/aml/opcode_table.go +++ /dev/null @@ -1,317 +0,0 @@ -package aml - -const ( - badOpcode = 0xff - extOpPrefix = 0x5b - - // Regular opcode list - opZero = opcode(0x00) - opOne = opcode(0x01) - opAlias = opcode(0x06) - opName = opcode(0x08) - opBytePrefix = opcode(0x0a) - opWordPrefix = opcode(0x0b) - opDwordPrefix = opcode(0x0c) - opStringPrefix = opcode(0x0d) - opQwordPrefix = opcode(0x0e) - opScope = opcode(0x10) - opBuffer = opcode(0x11) - opPackage = opcode(0x12) - opVarPackage = opcode(0x13) - opMethod = opcode(0x14) - opExternal = opcode(0x15) - opLocal0 = opcode(0x60) - opLocal1 = opcode(0x61) - opLocal2 = opcode(0x62) - opLocal3 = opcode(0x63) - opLocal4 = opcode(0x64) - opLocal5 = opcode(0x65) - opLocal6 = opcode(0x66) - opLocal7 = opcode(0x67) - opArg0 = opcode(0x68) - opArg1 = opcode(0x69) - opArg2 = opcode(0x6a) - opArg3 = opcode(0x6b) - opArg4 = opcode(0x6c) - opArg5 = opcode(0x6d) - opArg6 = opcode(0x6e) - opStore = opcode(0x70) - opRefOf = opcode(0x71) - opAdd = opcode(0x72) - opConcat = opcode(0x73) - opSubtract = opcode(0x74) - opIncrement = opcode(0x75) - opDecrement = opcode(0x76) - opMultiply = opcode(0x77) - opDivide = opcode(0x78) - opShiftLeft = opcode(0x79) - opShiftRight = opcode(0x7a) - opAnd = opcode(0x7b) - opNand = opcode(0x7c) - opOr = opcode(0x7d) - opNor = opcode(0x7e) - opXor = opcode(0x7f) - opNot = opcode(0x80) - opFindSetLeftBit = opcode(0x81) - opFindSetRightBit = opcode(0x82) - opDerefOf = opcode(0x83) - opConcatRes = opcode(0x84) - opMod = opcode(0x85) - opNotify = opcode(0x86) - opSizeOf = opcode(0x87) - opIndex = opcode(0x88) - opMatch = opcode(0x89) - opCreateDWordField = opcode(0x8a) - opCreateWordField = opcode(0x8b) - opCreateByteField = opcode(0x8c) - opCreateBitField = opcode(0x8d) - opObjectType = opcode(0x8e) - opCreateQWordField = opcode(0x8f) - opLand = opcode(0x90) - opLor = opcode(0x91) - opLnot = opcode(0x92) - opLEqual = opcode(0x93) - opLGreater = opcode(0x94) - opLLess = opcode(0x95) - opToBuffer = opcode(0x96) - opToDecimalString = opcode(0x97) - opToHexString = opcode(0x98) - opToInteger = opcode(0x99) - opToString = opcode(0x9c) - opCopyObject = opcode(0x9d) - opMid = opcode(0x9e) - opContinue = opcode(0x9f) - opIf = opcode(0xa0) - opElse = opcode(0xa1) - opWhile = opcode(0xa2) - opNoop = opcode(0xa3) - opReturn = opcode(0xa4) - opBreak = opcode(0xa5) - opBreakPoint = opcode(0xcc) - opOnes = opcode(0xff) - // Extended opcodes - opMutex = opcode(0xff + 0x01) - opEvent = opcode(0xff + 0x02) - opCondRefOf = opcode(0xff + 0x12) - opCreateField = opcode(0xff + 0x13) - opLoadTable = opcode(0xff + 0x1f) - opLoad = opcode(0xff + 0x20) - opStall = opcode(0xff + 0x21) - opSleep = opcode(0xff + 0x22) - opAcquire = opcode(0xff + 0x23) - opSignal = opcode(0xff + 0x24) - opWait = opcode(0xff + 0x25) - opReset = opcode(0xff + 0x26) - opRelease = opcode(0xff + 0x27) - opFromBCD = opcode(0xff + 0x28) - opToBCD = opcode(0xff + 0x29) - opUnload = opcode(0xff + 0x2a) - opRevision = opcode(0xff + 0x30) - opDebug = opcode(0xff + 0x31) - opFatal = opcode(0xff + 0x32) - opTimer = opcode(0xff + 0x33) - opOpRegion = opcode(0xff + 0x80) - opField = opcode(0xff + 0x81) - opDevice = opcode(0xff + 0x82) - opProcessor = opcode(0xff + 0x83) - opPowerRes = opcode(0xff + 0x84) - opThermalZone = opcode(0xff + 0x85) - opIndexField = opcode(0xff + 0x86) - opBankField = opcode(0xff + 0x87) - opDataRegion = opcode(0xff + 0x88) -) - -// The opcode table contains all opcode-related information that the parser knows. -// This table is modeled after a similar table used in the acpica implementation. -var opcodeTable = []opcodeInfo{ - /*0x00*/ {opZero, "Zero", objTypeInteger, opFlagConstant, makeArg0()}, - /*0x01*/ {opOne, "One", objTypeInteger, opFlagConstant, makeArg0()}, - /*0x02*/ {opAlias, "Alias", objTypeLocalAlias, opFlagNamed, makeArg2(opArgNameString, opArgNameString)}, - /*0x03*/ {opName, "Name", objTypeAny, opFlagNamed, makeArg2(opArgNameString, opArgDataRefObj)}, - /*0x04*/ {opBytePrefix, "Byte", objTypeInteger, opFlagConstant, makeArg1(opArgByteData)}, - /*0x05*/ {opWordPrefix, "Word", objTypeInteger, opFlagConstant, makeArg1(opArgWord)}, - /*0x06*/ {opDwordPrefix, "Dword", objTypeInteger, opFlagConstant, makeArg1(opArgDword)}, - /*0x07*/ {opStringPrefix, "String", objTypeString, opFlagConstant, makeArg1(opArgString)}, - /*0x08*/ {opQwordPrefix, "Qword", objTypeInteger, opFlagConstant, makeArg1(opArgQword)}, - /*0x09*/ {opScope, "Scope", objTypeLocalScope, opFlagNamed, makeArg2(opArgNameString, opArgTermList)}, - /*0x0a*/ {opBuffer, "Buffer", objTypeBuffer, opFlagHasPkgLen, makeArg2(opArgTermObj, opArgByteList)}, - /*0x0b*/ {opPackage, "Package", objTypePackage, opFlagNone, makeArg2(opArgByteData, opArgTermList)}, - /*0x0c*/ {opVarPackage, "VarPackage", objTypePackage, opFlagNone, makeArg2(opArgByteData, opArgTermList)}, - /*0x0d*/ {opMethod, "Method", objTypeMethod, opFlagNamed | opFlagScoped, makeArg3(opArgNameString, opArgByteData, opArgTermList)}, - /*0x0e*/ {opExternal, "External", objTypeAny, opFlagNamed, makeArg3(opArgNameString, opArgByteData, opArgByteData)}, - /*0x0f*/ {opLocal0, "Local0", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0x10*/ {opLocal1, "Local1", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0x11*/ {opLocal2, "Local2", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0x12*/ {opLocal3, "Local3", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0x13*/ {opLocal4, "Local4", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0120*/ {opLocal5, "Local5", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0x15*/ {opLocal6, "Local6", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0x16*/ {opLocal7, "Local7", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0x17*/ {opArg0, "Arg0", objTypeMethodArgument, opFlagExecutable, makeArg0()}, - /*0x18*/ {opArg1, "Arg1", objTypeMethodArgument, opFlagExecutable, makeArg0()}, - /*0x19*/ {opArg2, "Arg2", objTypeMethodArgument, opFlagExecutable, makeArg0()}, - /*0x1a*/ {opArg3, "Arg3", objTypeMethodArgument, opFlagExecutable, makeArg0()}, - /*0x1b*/ {opArg4, "Arg4", objTypeMethodArgument, opFlagExecutable, makeArg0()}, - /*0x1c*/ {opArg5, "Arg5", objTypeMethodArgument, opFlagExecutable, makeArg0()}, - /*0x1d*/ {opArg6, "Arg6", objTypeMethodArgument, opFlagExecutable, makeArg0()}, - /*0x1e*/ {opStore, "Store", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgSuperName)}, - /*0x1f*/ {opRefOf, "RefOf", objTypeAny, opFlagReference | opFlagExecutable, makeArg1(opArgSuperName)}, - /*0x20*/ {opAdd, "Add", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x21*/ {opConcat, "Concat", objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x22*/ {opSubtract, "Subtract", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x23*/ {opIncrement, "Increment", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgSuperName)}, - /*0x24*/ {opDecrement, "Decrement", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgSuperName)}, - /*0x25*/ {opMultiply, "Multiply", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x26*/ {opDivide, "Divide", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTarget, opArgTarget)}, - /*0x27*/ {opShiftLeft, "ShiftLeft", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x28*/ {opShiftRight, "ShiftRight", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x29*/ {opAnd, "And", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x2a*/ {opNand, "Nand", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x2b*/ {opOr, "Or", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x2c*/ {opNor, "Nor", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x2d*/ {opXor, "Xor", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x2e*/ {opNot, "Not", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x2f*/ {opFindSetLeftBit, "FindSetLeftBit", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x30*/ {opFindSetRightBit, "FindSetRightBit", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x31*/ {opDerefOf, "DerefOf", objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, - /*0x32*/ {opConcatRes, "ConcatRes", objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x33*/ {opMod, "Mod", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x34*/ {opNotify, "Notify", objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgTermObj)}, - /*0x35*/ {opSizeOf, "SizeOf", objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, - /*0x36*/ {opIndex, "Index", objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x37*/ {opMatch, "Match", objTypeAny, opFlagExecutable, makeArg6(opArgTermObj, opArgByteData, opArgTermObj, opArgByteData, opArgTermObj, opArgTermObj)}, - /*0x38*/ {opCreateDWordField, "CreateDWordField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, - /*0x39*/ {opCreateWordField, "CreateWordField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, - /*0x3a*/ {opCreateByteField, "CreateByteField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, - /*0x3b*/ {opCreateBitField, "CreateBitField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, - /*0x3c*/ {opObjectType, "ObjectType", objTypeAny, opFlagNone, makeArg1(opArgSuperName)}, - /*0x3d*/ {opCreateQWordField, "CreateQWordField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, - /*0x3e*/ {opLand, "Land", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, - /*0x3f*/ {opLor, "Lor", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, - /*0x40*/ {opLnot, "Lnot", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgTermObj)}, - /*0x41*/ {opLEqual, "LEqual", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, - /*0x42*/ {opLGreater, "LGreater", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, - /*0x43*/ {opLLess, "LLess", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, - /*0x44*/ {opToBuffer, "ToBuffer", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x45*/ {opToDecimalString, "ToDecimalString", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x46*/ {opToHexString, "ToHexString", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x47*/ {opToInteger, "ToInteger", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x48*/ {opToString, "ToString", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x49*/ {opCopyObject, "CopyObject", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgSimpleName)}, - /*0x4a*/ {opMid, "Mid", objTypeAny, opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x4b*/ {opContinue, "Continue", objTypeAny, opFlagExecutable, makeArg0()}, - /*0x4c*/ {opIf, "If", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTermList)}, - /*0x4d*/ {opElse, "Else", objTypeAny, opFlagExecutable | opFlagScoped, makeArg1(opArgTermList)}, - /*0x4e*/ {opWhile, "While", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTermList)}, - /*0x4f*/ {opNoop, "Noop", objTypeAny, opFlagNoOp, makeArg0()}, - /*0x50*/ {opReturn, "Return", objTypeAny, opFlagReturn, makeArg1(opArgTermObj)}, - /*0x51*/ {opBreak, "Break", objTypeAny, opFlagExecutable, makeArg0()}, - /*0x52*/ {opBreakPoint, "BreakPoint", objTypeAny, opFlagNoOp, makeArg0()}, - /*0x53*/ {opOnes, "Ones", objTypeInteger, opFlagConstant, makeArg0()}, - /*0x54*/ {opMutex, "Mutex", objTypeMutex, opFlagNamed, makeArg2(opArgNameString, opArgByteData)}, - /*0x55*/ {opEvent, "Event", objTypeEvent, opFlagNamed, makeArg1(opArgNameString)}, - /*0x56*/ {opCondRefOf, "CondRefOf", objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgSuperName)}, - /*0x57*/ {opCreateField, "CreateField", objTypeBufferField, opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTermObj, opArgNameString)}, - /*0x58*/ {opLoadTable, "LoadTable", objTypeAny, opFlagExecutable, makeArg7(opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj)}, - /*0x59*/ {opLoad, "Load", objTypeAny, opFlagExecutable, makeArg2(opArgNameString, opArgSuperName)}, - /*0x5a*/ {opStall, "Stall", objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, - /*0x5b*/ {opSleep, "Sleep", objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, - /*0x5c*/ {opAcquire, "Acquire", objTypeAny, opFlagExecutable, makeArg2(opArgNameString, opArgSuperName)}, - /*0x5d*/ {opSignal, "Signal", objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, - /*0x5e*/ {opWait, "Wait", objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgTermObj)}, - /*0x5f*/ {opReset, "Reset", objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, - /*0x60*/ {opRelease, "Release", objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, - /*0x61*/ {opFromBCD, "FromBCD", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x62*/ {opToBCD, "ToBCD", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x63*/ {opUnload, "Unload", objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, - /*0x64*/ {opRevision, "Revision", objTypeInteger, opFlagConstant | opFlagExecutable, makeArg0()}, - /*0x65*/ {opDebug, "Debug", objTypeLocalReference, opFlagExecutable, makeArg0()}, - /*0x66*/ {opFatal, "Fatal", objTypeAny, opFlagExecutable, makeArg3(opArgByteData, opArgDword, opArgTermObj)}, - /*0x67*/ {opTimer, "Timer", objTypeAny, opFlagNone, makeArg0()}, - /*0x68*/ {opOpRegion, "OpRegion", objTypeRegion, opFlagNamed, makeArg4(opArgNameString, opArgByteData, opArgTermObj, opArgTermObj)}, - /*0x69*/ {opField, "Field", objTypeAny, opFlagNone, makeArg3(opArgNameString, opArgByteData, opArgFieldList)}, - /*0x6a*/ {opDevice, "Device", objTypeDevice, opFlagNamed | opFlagScoped, makeArg2(opArgNameString, opArgTermList)}, - /*0x6b*/ {opProcessor, "Processor", objTypeProcessor, opFlagNamed | opFlagScoped, makeArg5(opArgNameString, opArgByteData, opArgDword, opArgByteData, opArgTermList)}, - /*0x6c*/ {opPowerRes, "PowerRes", objTypePower, opFlagNamed | opFlagScoped, makeArg4(opArgNameString, opArgByteData, opArgWord, opArgTermList)}, - /*0x6d*/ {opThermalZone, "ThermalZone", objTypeThermal, opFlagNamed | opFlagScoped, makeArg2(opArgNameString, opArgTermList)}, - /*0x6e*/ {opIndexField, "IndexField", objTypeAny, opFlagNone, makeArg4(opArgNameString, opArgNameString, opArgByteData, opArgFieldList)}, - /*0x6f*/ {opBankField, "BankField", objTypeLocalBankField, opFlagNamed, makeArg5(opArgNameString, opArgNameString, opArgTermObj, opArgByteData, opArgFieldList)}, - /*0x70*/ {opDataRegion, "DataRegion", objTypeLocalRegionField, opFlagNamed, makeArg4(opArgNameString, opArgTermObj, opArgTermObj, opArgTermObj)}, -} - -// opcodeMap maps an AML opcode to an entry in the opcode table. Entries with -// the value 0xff indicate an invalid/unsupported opcode. -var opcodeMap = [256]uint8{ - /* 0 1 2 3 4 5 6 7*/ - /*0x00 - 0x07*/ 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x02, 0xff, - /*0x08 - 0x0f*/ 0x03, 0xff, 0x04, 0x05, 0x06, 0x07, 0x08, 0xff, - /*0x10 - 0x17*/ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xff, 0xff, - /*0x18 - 0x1f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x20 - 0x27*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x28 - 0x2f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x30 - 0x37*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x38 - 0x3f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x40 - 0x47*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x48 - 0x4f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x50 - 0x57*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x58 - 0x5f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x60 - 0x67*/ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, - /*0x68 - 0x6f*/ 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0xff, - /*0x70 - 0x77*/ 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, - /*0x78 - 0x7f*/ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, - /*0x80 - 0x87*/ 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, - /*0x88 - 0x8f*/ 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, - /*0x90 - 0x97*/ 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, - /*0x98 - 0x9f*/ 0x46, 0x47, 0x48, 0x49, 0x4a, 0x49, 0x4a, 0x4b, - /*0xa0 - 0xa7*/ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0xff, 0xff, - /*0xa8 - 0xaf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xb0 - 0xb7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xb8 - 0xbf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xc0 - 0xc7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xc8 - 0xcf*/ 0xff, 0xff, 0xff, 0xff, 0x52, 0xff, 0xff, 0xff, - /*0xd0 - 0xd7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xd8 - 0xdf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xe0 - 0xe7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xe8 - 0xef*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xf0 - 0xf7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xf8 - 0xff*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x53, -} - -// extendedOpcodeMap maps an AML extended opcode (extOpPrefix + code) to an -// entry in the opcode table. Entries with the value 0xff indicate an -// invalid/unsupported opcode. -var extendedOpcodeMap = [256]uint8{ - /* 0 1 2 3 4 5 6 7*/ - /*0x00 - 0x07*/ 0xff, 0x54, 0x55, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x08 - 0x0f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x10 - 0x17*/ 0xff, 0xff, 0x56, 0x57, 0xff, 0xff, 0xff, 0xff, - /*0x18 - 0x1f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x58, - /*0x20 - 0x27*/ 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, - /*0x28 - 0x2f*/ 0x61, 0x62, 0x63, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x30 - 0x37*/ 0x64, 0x65, 0x66, 0x67, 0xff, 0xff, 0xff, 0xff, - /*0x38 - 0x3f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x40 - 0x47*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x48 - 0x4f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x50 - 0x57*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x58 - 0x5f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x60 - 0x67*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x68 - 0x6f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x70 - 0x77*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x78 - 0x7f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x80 - 0x87*/ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - /*0x88 - 0x8f*/ 0x70, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x90 - 0x97*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x98 - 0x9f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xa0 - 0xa7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xa8 - 0xaf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xb0 - 0xb7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xb8 - 0xbf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xc0 - 0xc7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xc8 - 0xcf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xd0 - 0xd7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xd8 - 0xdf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xe0 - 0xe7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xe8 - 0xef*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xf0 - 0xf7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xf8 - 0xff*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x53, -} diff --git a/src/gopheros/device/acpi/aml/opcode_test.go b/src/gopheros/device/acpi/aml/opcode_test.go deleted file mode 100644 index 7258759..0000000 --- a/src/gopheros/device/acpi/aml/opcode_test.go +++ /dev/null @@ -1,187 +0,0 @@ -package aml - -import "testing" - -func TestOpcodeToString(t *testing.T) { - if exp, got := "Acquire", opAcquire.String(); got != exp { - t.Fatalf("expected opAcquire.toString() to return %q; got %q", exp, got) - } - - if exp, got := "unknown", opcode(0xffff).String(); got != exp { - t.Fatalf("expected opcode.String() to return %q; got %q", exp, got) - } -} - -func TestOpcodeIsX(t *testing.T) { - specs := []struct { - op opcode - testFn func(opcode) bool - want bool - }{ - // opIsLocalArg - {opLocal0, opIsLocalArg, true}, - {opLocal1, opIsLocalArg, true}, - {opLocal2, opIsLocalArg, true}, - {opLocal3, opIsLocalArg, true}, - {opLocal4, opIsLocalArg, true}, - {opLocal5, opIsLocalArg, true}, - {opLocal6, opIsLocalArg, true}, - {opLocal7, opIsLocalArg, true}, - {opArg0, opIsLocalArg, false}, - {opDivide, opIsLocalArg, false}, - // opIsMethodArg - {opArg0, opIsMethodArg, true}, - {opArg1, opIsMethodArg, true}, - {opArg2, opIsMethodArg, true}, - {opArg3, opIsMethodArg, true}, - {opArg4, opIsMethodArg, true}, - {opArg5, opIsMethodArg, true}, - {opArg6, opIsMethodArg, true}, - {opLocal7, opIsMethodArg, false}, - {opIf, opIsMethodArg, false}, - // opIsArg - {opLocal5, opIsArg, true}, - {opArg1, opIsArg, true}, - {opDivide, opIsArg, false}, - // opIsType2 - {opAcquire, opIsType2, true}, - {opAdd, opIsType2, true}, - {opAnd, opIsType2, true}, - {opBuffer, opIsType2, true}, - {opConcat, opIsType2, true}, - {opConcatRes, opIsType2, true}, - {opCondRefOf, opIsType2, true}, - {opCopyObject, opIsType2, true}, - {opDecrement, opIsType2, true}, - {opDerefOf, opIsType2, true}, - {opDivide, opIsType2, true}, - {opFindSetLeftBit, opIsType2, true}, - {opFindSetRightBit, opIsType2, true}, - {opFromBCD, opIsType2, true}, - {opIncrement, opIsType2, true}, - {opIndex, opIsType2, true}, - {opLand, opIsType2, true}, - {opLEqual, opIsType2, true}, - {opLGreater, opIsType2, true}, - {opLLess, opIsType2, true}, - {opMid, opIsType2, true}, - {opLnot, opIsType2, true}, - {opLoadTable, opIsType2, true}, - {opLor, opIsType2, true}, - {opMatch, opIsType2, true}, - {opMod, opIsType2, true}, - {opMultiply, opIsType2, true}, - {opNand, opIsType2, true}, - {opNor, opIsType2, true}, - {opNot, opIsType2, true}, - {opObjectType, opIsType2, true}, - {opOr, opIsType2, true}, - {opPackage, opIsType2, true}, - {opVarPackage, opIsType2, true}, - {opRefOf, opIsType2, true}, - {opShiftLeft, opIsType2, true}, - {opShiftRight, opIsType2, true}, - {opSizeOf, opIsType2, true}, - {opStore, opIsType2, true}, - {opSubtract, opIsType2, true}, - {opTimer, opIsType2, true}, - {opToBCD, opIsType2, true}, - {opToBuffer, opIsType2, true}, - {opToDecimalString, opIsType2, true}, - {opToHexString, opIsType2, true}, - {opToInteger, opIsType2, true}, - {opToString, opIsType2, true}, - {opWait, opIsType2, true}, - {opXor, opIsType2, true}, - {opBytePrefix, opIsType2, false}, - // opIsDataObject - {opBytePrefix, opIsDataObject, true}, - {opWordPrefix, opIsDataObject, true}, - {opDwordPrefix, opIsDataObject, true}, - {opQwordPrefix, opIsDataObject, true}, - {opStringPrefix, opIsDataObject, true}, - {opZero, opIsDataObject, true}, - {opOne, opIsDataObject, true}, - {opOnes, opIsDataObject, true}, - {opRevision, opIsDataObject, true}, - {opBuffer, opIsDataObject, true}, - {opPackage, opIsDataObject, true}, - {opVarPackage, opIsDataObject, true}, - {opLor, opIsDataObject, false}, - // opIsBufferField - {opCreateField, opIsBufferField, true}, - {opCreateBitField, opIsBufferField, true}, - {opCreateByteField, opIsBufferField, true}, - {opCreateWordField, opIsBufferField, true}, - {opCreateDWordField, opIsBufferField, true}, - {opCreateQWordField, opIsBufferField, true}, - {opRevision, opIsBufferField, false}, - } - - for specIndex, spec := range specs { - if got := spec.testFn(spec.op); got != spec.want { - t.Errorf("[spec %d] opcode %q: expected to get %t; got %t", specIndex, spec.op, spec.want, got) - } - } -} - -func TestOpArgFlagToString(t *testing.T) { - specs := map[opArgFlag]string{ - opArgTermList: "opArgTermList", - opArgTermObj: "opArgTermObj", - opArgByteList: "opArgByteList", - opArgPackage: "opArgPackage", - opArgString: "opArgString", - opArgByteData: "opArgByteData", - opArgWord: "opArgWord", - opArgDword: "opArgDword", - opArgQword: "opArgQword", - opArgNameString: "opArgNameString", - opArgSuperName: "opArgSuperName", - opArgSimpleName: "opArgSimpleName", - opArgDataRefObj: "opArgDataRefObj", - opArgTarget: "opArgTarget", - opArgFieldList: "opArgFieldList", - opArgFlag(0xff): "", - } - - for flag, want := range specs { - if got := flag.String(); got != want { - t.Errorf("expected %q; got %q", want, got) - } - } -} - -// TestFindUnmappedOpcodes is a helper test that pinpoints opcodes that have -// not yet been mapped via an opcode table. This test will be removed once all -// opcodes are supported. -func TestFindUnmappedOpcodes(t *testing.T) { - //t.SkipNow() - for opIndex, opRef := range opcodeMap { - if opRef != badOpcode { - continue - } - - for tabIndex, info := range opcodeTable { - if uint16(info.op) == uint16(opIndex) { - t.Errorf("set opcodeMap[0x%02x] = 0x%02x // %s\n", opIndex, tabIndex, info.op.String()) - break - } - } - } - - for opIndex, opRef := range extendedOpcodeMap { - // 0xff (opOnes) is defined in opcodeTable - if opRef != badOpcode || opIndex == 0 { - continue - } - - opIndex += 0xff - for tabIndex, info := range opcodeTable { - if uint16(info.op) == uint16(opIndex) { - t.Errorf("set extendedOpcodeMap[0x%02x] = 0x%02x // %s\n", opIndex-0xff, tabIndex, info.op.String()) - break - } - } - } -} diff --git a/src/gopheros/device/acpi/aml/parser/opcode_table.go b/src/gopheros/device/acpi/aml/parser/opcode_table.go new file mode 100644 index 0000000..e1333d8 --- /dev/null +++ b/src/gopheros/device/acpi/aml/parser/opcode_table.go @@ -0,0 +1,341 @@ +package parser + +import "gopheros/device/acpi/aml/entity" + +const ( + badOpcode = 0xff + extOpPrefix = 0x5b +) + +// objType represents the object types that are supported by the AML parser. +type objType uint8 + +// The list of AML object types. +const ( + objTypeAny objType = iota + objTypeInteger + objTypeString + objTypeBuffer + objTypePackage + objTypeDevice + objTypeEvent + objTypeMethod + objTypeMutex + objTypeRegion + objTypePower + objTypeProcessor + objTypeThermal + objTypeBufferField + objTypeLocalRegionField + objTypeLocalBankField + objTypeLocalReference + objTypeLocalAlias + objTypeLocalScope + objTypeLocalVariable + objTypeMethodArgument +) + +// opFlag specifies a list of OR-able flags that describe the object +// type/attributes generated by a particular opcode. +type opFlag uint16 + +const ( + opFlagNone opFlag = 1 << iota + opFlagHasPkgLen + opFlagNamed + opFlagConstant + opFlagReference + opFlagArithmetic + opFlagCreate + opFlagReturn + opFlagExecutable + opFlagNoOp + opFlagScoped +) + +// is returns true if f is set in this opFlag. +func (fl opFlag) is(f opFlag) bool { + return (fl & f) == f +} + +// opArgFlags encodes up to 7 opArgFlag values in a uint64 value. +type opArgFlags uint64 + +// argCount returns the number of encoded args in the given flag. +func (fl opArgFlags) argCount() (count uint8) { + // Each argument is specified using 8 bits with 0x0 indicating the end of the + // argument list + for ; fl&0xf != 0; fl, count = fl>>8, count+1 { + } + + return count +} + +// arg returns the arg flags for argument "num" where num is the 0-based index +// of the argument to return. The allowed values for num are 0-6. +func (fl opArgFlags) arg(num uint8) opArgFlag { + return opArgFlag((fl >> (num * 8)) & 0xf) +} + +// contains returns true if the arg flags contain any argument with type x. +func (fl opArgFlags) contains(x opArgFlag) bool { + // Each argument is specified using 8 bits with 0x0 indicating the end of the + // argument list + for ; fl&0xf != 0; fl >>= 8 { + if opArgFlag(fl&0xf) == x { + return true + } + } + + return false +} + +// opArgFlag represents the type of an argument expected by a particular opcode. +type opArgFlag uint8 + +// The list of supported opArgFlag values. +const ( + _ opArgFlag = iota + opArgTermList + opArgTermObj + opArgByteList + opArgString + opArgByteData + opArgWord + opArgDword + opArgQword + opArgNameString + opArgSuperName + opArgSimpleName + opArgDataRefObj + opArgTarget + opArgFieldList +) + +func makeArg0() opArgFlags { return 0 } +func makeArg1(arg0 opArgFlag) opArgFlags { return opArgFlags(arg0) } +func makeArg2(arg0, arg1 opArgFlag) opArgFlags { return opArgFlags(arg1)<<8 | opArgFlags(arg0) } +func makeArg3(arg0, arg1, arg2 opArgFlag) opArgFlags { + return opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) +} +func makeArg4(arg0, arg1, arg2, arg3 opArgFlag) opArgFlags { + return opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) +} +func makeArg5(arg0, arg1, arg2, arg3, arg4 opArgFlag) opArgFlags { + return opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) +} +func makeArg6(arg0, arg1, arg2, arg3, arg4, arg5 opArgFlag) opArgFlags { + return opArgFlags(arg5)<<40 | opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) +} +func makeArg7(arg0, arg1, arg2, arg3, arg4, arg5, arg6 opArgFlag) opArgFlags { + return opArgFlags(arg6)<<48 | opArgFlags(arg5)<<40 | opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) +} + +// opcodeInfo contains all known information about an opcode, +// its argument count and types as well as the type of object +// represented by it. +type opcodeInfo struct { + op entity.AMLOpcode + objType objType + + flags opFlag + argFlags opArgFlags +} + +// Used by Parser.parseMethodBody for deferred parsing of method bodies. +const methodOpInfoIndex = 0x0d + +// The opcode table contains all opcode-related information that the parser knows. +// This table is modeled after a similar table used in the acpica implementation. +var opcodeTable = []opcodeInfo{ + /*0x00*/ {entity.OpZero, objTypeInteger, opFlagConstant, makeArg0()}, + /*0x01*/ {entity.OpOne, objTypeInteger, opFlagConstant, makeArg0()}, + /*0x02*/ {entity.OpAlias, objTypeLocalAlias, opFlagNamed, makeArg2(opArgNameString, opArgNameString)}, + /*0x03*/ {entity.OpName, objTypeAny, opFlagNamed, makeArg2(opArgNameString, opArgDataRefObj)}, + /*0x04*/ {entity.OpBytePrefix, objTypeInteger, opFlagConstant, makeArg1(opArgByteData)}, + /*0x05*/ {entity.OpWordPrefix, objTypeInteger, opFlagConstant, makeArg1(opArgWord)}, + /*0x06*/ {entity.OpDwordPrefix, objTypeInteger, opFlagConstant, makeArg1(opArgDword)}, + /*0x07*/ {entity.OpStringPrefix, objTypeString, opFlagConstant, makeArg1(opArgString)}, + /*0x08*/ {entity.OpQwordPrefix, objTypeInteger, opFlagConstant, makeArg1(opArgQword)}, + /*0x09*/ {entity.OpScope, objTypeLocalScope, opFlagNamed, makeArg2(opArgNameString, opArgTermList)}, + /*0x0a*/ {entity.OpBuffer, objTypeBuffer, opFlagHasPkgLen, makeArg2(opArgTermObj, opArgByteList)}, + /*0x0b*/ {entity.OpPackage, objTypePackage, opFlagNone, makeArg2(opArgByteData, opArgTermList)}, + /*0x0c*/ {entity.OpVarPackage, objTypePackage, opFlagNone, makeArg2(opArgByteData, opArgTermList)}, + /*0x0d*/ {entity.OpMethod, objTypeMethod, opFlagNamed | opFlagScoped, makeArg3(opArgNameString, opArgByteData, opArgTermList)}, + /*0x0e*/ {entity.OpExternal, objTypeAny, opFlagNamed, makeArg3(opArgNameString, opArgByteData, opArgByteData)}, + /*0x0f*/ {entity.OpLocal0, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0x10*/ {entity.OpLocal1, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0x11*/ {entity.OpLocal2, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0x12*/ {entity.OpLocal3, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0x13*/ {entity.OpLocal4, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0120*/ {entity.OpLocal5, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0x15*/ {entity.OpLocal6, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0x16*/ {entity.OpLocal7, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0x17*/ {entity.OpArg0, objTypeMethodArgument, opFlagExecutable, makeArg0()}, + /*0x18*/ {entity.OpArg1, objTypeMethodArgument, opFlagExecutable, makeArg0()}, + /*0x19*/ {entity.OpArg2, objTypeMethodArgument, opFlagExecutable, makeArg0()}, + /*0x1a*/ {entity.OpArg3, objTypeMethodArgument, opFlagExecutable, makeArg0()}, + /*0x1b*/ {entity.OpArg4, objTypeMethodArgument, opFlagExecutable, makeArg0()}, + /*0x1c*/ {entity.OpArg5, objTypeMethodArgument, opFlagExecutable, makeArg0()}, + /*0x1d*/ {entity.OpArg6, objTypeMethodArgument, opFlagExecutable, makeArg0()}, + /*0x1e*/ {entity.OpStore, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgSuperName)}, + /*0x1f*/ {entity.OpRefOf, objTypeAny, opFlagReference | opFlagExecutable, makeArg1(opArgSuperName)}, + /*0x20*/ {entity.OpAdd, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x21*/ {entity.OpConcat, objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x22*/ {entity.OpSubtract, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x23*/ {entity.OpIncrement, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgSuperName)}, + /*0x24*/ {entity.OpDecrement, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgSuperName)}, + /*0x25*/ {entity.OpMultiply, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x26*/ {entity.OpDivide, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTarget, opArgTarget)}, + /*0x27*/ {entity.OpShiftLeft, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x28*/ {entity.OpShiftRight, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x29*/ {entity.OpAnd, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x2a*/ {entity.OpNand, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x2b*/ {entity.OpOr, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x2c*/ {entity.OpNor, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x2d*/ {entity.OpXor, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x2e*/ {entity.OpNot, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x2f*/ {entity.OpFindSetLeftBit, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x30*/ {entity.OpFindSetRightBit, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x31*/ {entity.OpDerefOf, objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, + /*0x32*/ {entity.OpConcatRes, objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x33*/ {entity.OpMod, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x34*/ {entity.OpNotify, objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgTermObj)}, + /*0x35*/ {entity.OpSizeOf, objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, + /*0x36*/ {entity.OpIndex, objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x37*/ {entity.OpMatch, objTypeAny, opFlagExecutable, makeArg6(opArgTermObj, opArgByteData, opArgTermObj, opArgByteData, opArgTermObj, opArgTermObj)}, + /*0x38*/ {entity.OpCreateDWordField, objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, + /*0x39*/ {entity.OpCreateWordField, objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, + /*0x3a*/ {entity.OpCreateByteField, objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, + /*0x3b*/ {entity.OpCreateBitField, objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, + /*0x3c*/ {entity.OpObjectType, objTypeAny, opFlagNone, makeArg1(opArgSuperName)}, + /*0x3d*/ {entity.OpCreateQWordField, objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, + /*0x3e*/ {entity.OpLand, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, + /*0x3f*/ {entity.OpLor, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, + /*0x40*/ {entity.OpLnot, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgTermObj)}, + /*0x41*/ {entity.OpLEqual, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, + /*0x42*/ {entity.OpLGreater, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, + /*0x43*/ {entity.OpLLess, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, + /*0x44*/ {entity.OpToBuffer, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x45*/ {entity.OpToDecimalString, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x46*/ {entity.OpToHexString, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x47*/ {entity.OpToInteger, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x48*/ {entity.OpToString, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x49*/ {entity.OpCopyObject, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgSimpleName)}, + /*0x4a*/ {entity.OpMid, objTypeAny, opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x4b*/ {entity.OpContinue, objTypeAny, opFlagExecutable, makeArg0()}, + /*0x4c*/ {entity.OpIf, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTermList)}, + /*0x4d*/ {entity.OpElse, objTypeAny, opFlagExecutable | opFlagScoped, makeArg1(opArgTermList)}, + /*0x4e*/ {entity.OpWhile, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTermList)}, + /*0x4f*/ {entity.OpNoop, objTypeAny, opFlagNoOp, makeArg0()}, + /*0x50*/ {entity.OpReturn, objTypeAny, opFlagReturn, makeArg1(opArgTermObj)}, + /*0x51*/ {entity.OpBreak, objTypeAny, opFlagExecutable, makeArg0()}, + /*0x52*/ {entity.OpBreakPoint, objTypeAny, opFlagNoOp, makeArg0()}, + /*0x53*/ {entity.OpOnes, objTypeInteger, opFlagConstant, makeArg0()}, + /*0x54*/ {entity.OpMutex, objTypeMutex, opFlagNamed, makeArg2(opArgNameString, opArgByteData)}, + /*0x55*/ {entity.OpEvent, objTypeEvent, opFlagNamed, makeArg1(opArgNameString)}, + /*0x56*/ {entity.OpCondRefOf, objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgSuperName)}, + /*0x57*/ {entity.OpCreateField, objTypeBufferField, opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTermObj, opArgNameString)}, + /*0x58*/ {entity.OpLoadTable, objTypeAny, opFlagExecutable, makeArg7(opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj)}, + /*0x59*/ {entity.OpLoad, objTypeAny, opFlagExecutable, makeArg2(opArgNameString, opArgSuperName)}, + /*0x5a*/ {entity.OpStall, objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, + /*0x5b*/ {entity.OpSleep, objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, + /*0x5c*/ {entity.OpAcquire, objTypeAny, opFlagExecutable, makeArg2(opArgNameString, opArgSuperName)}, + /*0x5d*/ {entity.OpSignal, objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, + /*0x5e*/ {entity.OpWait, objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgTermObj)}, + /*0x5f*/ {entity.OpReset, objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, + /*0x60*/ {entity.OpRelease, objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, + /*0x61*/ {entity.OpFromBCD, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x62*/ {entity.OpToBCD, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x63*/ {entity.OpUnload, objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, + /*0x64*/ {entity.OpRevision, objTypeInteger, opFlagConstant | opFlagExecutable, makeArg0()}, + /*0x65*/ {entity.OpDebug, objTypeLocalReference, opFlagExecutable, makeArg0()}, + /*0x66*/ {entity.OpFatal, objTypeAny, opFlagExecutable, makeArg3(opArgByteData, opArgDword, opArgTermObj)}, + /*0x67*/ {entity.OpTimer, objTypeAny, opFlagNone, makeArg0()}, + /*0x68*/ {entity.OpOpRegion, objTypeRegion, opFlagNamed, makeArg4(opArgNameString, opArgByteData, opArgTermObj, opArgTermObj)}, + /*0x69*/ {entity.OpField, objTypeAny, opFlagNone, makeArg3(opArgNameString, opArgByteData, opArgFieldList)}, + /*0x6a*/ {entity.OpDevice, objTypeDevice, opFlagNamed | opFlagScoped, makeArg2(opArgNameString, opArgTermList)}, + /*0x6b*/ {entity.OpProcessor, objTypeProcessor, opFlagNamed | opFlagScoped, makeArg5(opArgNameString, opArgByteData, opArgDword, opArgByteData, opArgTermList)}, + /*0x6c*/ {entity.OpPowerRes, objTypePower, opFlagNamed | opFlagScoped, makeArg4(opArgNameString, opArgByteData, opArgWord, opArgTermList)}, + /*0x6d*/ {entity.OpThermalZone, objTypeThermal, opFlagNamed | opFlagScoped, makeArg2(opArgNameString, opArgTermList)}, + /*0x6e*/ {entity.OpIndexField, objTypeAny, opFlagNone, makeArg4(opArgNameString, opArgNameString, opArgByteData, opArgFieldList)}, + /*0x6f*/ {entity.OpBankField, objTypeLocalBankField, opFlagNamed, makeArg5(opArgNameString, opArgNameString, opArgTermObj, opArgByteData, opArgFieldList)}, + /*0x70*/ {entity.OpDataRegion, objTypeLocalRegionField, opFlagNamed, makeArg4(opArgNameString, opArgTermObj, opArgTermObj, opArgTermObj)}, +} + +// opcodeMap maps an AML opcode to an entry in the opcode table. Entries with +// the value 0xff indicate an invalid/unsupported opcode. +var opcodeMap = [256]uint8{ + /* 0 1 2 3 4 5 6 7*/ + /*0x00 - 0x07*/ 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x02, 0xff, + /*0x08 - 0x0f*/ 0x03, 0xff, 0x04, 0x05, 0x06, 0x07, 0x08, 0xff, + /*0x10 - 0x17*/ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xff, 0xff, + /*0x18 - 0x1f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x20 - 0x27*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x28 - 0x2f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x30 - 0x37*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x38 - 0x3f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x40 - 0x47*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x48 - 0x4f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x50 - 0x57*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x58 - 0x5f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x60 - 0x67*/ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + /*0x68 - 0x6f*/ 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0xff, + /*0x70 - 0x77*/ 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, + /*0x78 - 0x7f*/ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, + /*0x80 - 0x87*/ 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + /*0x88 - 0x8f*/ 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, + /*0x90 - 0x97*/ 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, + /*0x98 - 0x9f*/ 0x46, 0x47, 0x48, 0x49, 0x4a, 0x49, 0x4a, 0x4b, + /*0xa0 - 0xa7*/ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0xff, 0xff, + /*0xa8 - 0xaf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xb0 - 0xb7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xb8 - 0xbf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xc0 - 0xc7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xc8 - 0xcf*/ 0xff, 0xff, 0xff, 0xff, 0x52, 0xff, 0xff, 0xff, + /*0xd0 - 0xd7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xd8 - 0xdf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xe0 - 0xe7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xe8 - 0xef*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xf0 - 0xf7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xf8 - 0xff*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x53, +} + +// extendedOpcodeMap maps an AML extended opcode (extOpPrefix + code) to an +// entry in the opcode table. Entries with the value 0xff indicate an +// invalid/unsupported opcode. +var extendedOpcodeMap = [256]uint8{ + /* 0 1 2 3 4 5 6 7*/ + /*0x00 - 0x07*/ 0xff, 0x54, 0x55, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x08 - 0x0f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x10 - 0x17*/ 0xff, 0xff, 0x56, 0x57, 0xff, 0xff, 0xff, 0xff, + /*0x18 - 0x1f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x58, + /*0x20 - 0x27*/ 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, + /*0x28 - 0x2f*/ 0x61, 0x62, 0x63, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x30 - 0x37*/ 0x64, 0x65, 0x66, 0x67, 0xff, 0xff, 0xff, 0xff, + /*0x38 - 0x3f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x40 - 0x47*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x48 - 0x4f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x50 - 0x57*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x58 - 0x5f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x60 - 0x67*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x68 - 0x6f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x70 - 0x77*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x78 - 0x7f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x80 - 0x87*/ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + /*0x88 - 0x8f*/ 0x70, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x90 - 0x97*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x98 - 0x9f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xa0 - 0xa7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xa8 - 0xaf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xb0 - 0xb7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xb8 - 0xbf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xc0 - 0xc7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xc8 - 0xcf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xd0 - 0xd7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xd8 - 0xdf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xe0 - 0xe7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xe8 - 0xef*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xf0 - 0xf7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xf8 - 0xff*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x53, +} diff --git a/src/gopheros/device/acpi/aml/parser/opcode_test.go b/src/gopheros/device/acpi/aml/parser/opcode_test.go new file mode 100644 index 0000000..ee5bdd5 --- /dev/null +++ b/src/gopheros/device/acpi/aml/parser/opcode_test.go @@ -0,0 +1,35 @@ +package parser + +import "testing" + +// TestFindUnmappedOpcodes is a helper test that pinpoints opcodes that have +// not yet been mapped via an opcode table. +func TestFindUnmappedOpcodes(t *testing.T) { + for opIndex, opRef := range opcodeMap { + if opRef != badOpcode { + continue + } + + for tabIndex, info := range opcodeTable { + if uint16(info.op) == uint16(opIndex) { + t.Errorf("set opcodeMap[0x%02x] = 0x%02x // %s\n", opIndex, tabIndex, info.op.String()) + break + } + } + } + + for opIndex, opRef := range extendedOpcodeMap { + // 0xff (opOnes) is defined in opcodeTable + if opRef != badOpcode || opIndex == 0 { + continue + } + + opIndex += 0xff + for tabIndex, info := range opcodeTable { + if uint16(info.op) == uint16(opIndex) { + t.Errorf("set extendedOpcodeMap[0x%02x] = 0x%02x // %s\n", opIndex-0xff, tabIndex, info.op.String()) + break + } + } + } +} diff --git a/src/gopheros/device/acpi/aml/parser.go b/src/gopheros/device/acpi/aml/parser/parser.go similarity index 62% rename from src/gopheros/device/acpi/aml/parser.go rename to src/gopheros/device/acpi/aml/parser/parser.go index 2b25bdf..8f4d495 100644 --- a/src/gopheros/device/acpi/aml/parser.go +++ b/src/gopheros/device/acpi/aml/parser/parser.go @@ -1,6 +1,7 @@ -package aml +package parser import ( + "gopheros/device/acpi/aml/entity" "gopheros/device/acpi/table" "gopheros/kernel" "gopheros/kernel/kfmt" @@ -13,28 +14,30 @@ var ( errResolvingEntities = &kernel.Error{Module: "acpi_aml_parser", Message: "AML bytecode contains unresolvable entities"} ) +type parseOpt uint8 + +const ( + parseOptSkipMethodBodies parseOpt = iota + parseOptParseMethodBodies +) + // Parser implements an AML parser. type Parser struct { r amlStreamReader errWriter io.Writer - root ScopeEntity - scopeStack []ScopeEntity + root entity.Container + scopeStack []entity.Container tableName string tableHandle uint8 - // methodArgCount is initialized in a pre-parse step with the names and expected - // number of args for each function declaration. This is required as function - // invocations do not employ any mechanism to indicate the number of args that - // need to be parsed. Moreover, the spec allows for forward function declarations. - methodArgCount map[string]uint8 + parseOptions parseOpt } // NewParser returns a new AML parser instance. -func NewParser(errWriter io.Writer, rootEntity ScopeEntity) *Parser { +func NewParser(errWriter io.Writer, rootEntity entity.Container) *Parser { return &Parser{ - errWriter: errWriter, - root: rootEntity, - methodArgCount: make(map[string]uint8), + errWriter: errWriter, + root: rootEntity, } } @@ -50,12 +53,9 @@ func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDT uint32(unsafe.Sizeof(table.SDTHeader{})), ) - // Pass 1: scan bytecode and locate all method declarations. This allows us to - // properly parse the arguments to method invocations at pass 2 even if the - // the name of the invoked method is a forward reference. - p.detectMethodDeclarations() - - // Pass 2: decode bytecode and build entitites + // Pass 1: decode bytecode and build entitites without recursing into + // function bodies. + p.parseOptions = parseOptSkipMethodBodies p.scopeStack = nil p.scopeEnter(p.root) if !p.parseObjList(header.Length) { @@ -65,25 +65,33 @@ func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDT } p.scopeExit() - // Pass 3: check parents and resolve forward references + // Pass 2: parse method bodies, check entity parents and resolve all + // symbol references var resolveFailed bool - scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool { - // Skip method bodies; their contents will be lazily resolved by the interpreter - if _, isMethod := ent.(*Method); isMethod { + entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool { + if method, isMethod := ent.(*entity.Method); isMethod { + resolveFailed = resolveFailed || !p.parseMethodBody(method) + + // Don't recurse into method bodies; their contents + // will be lazilly resolved by the VM return false } // Populate parents for any entity args that are also entities but are not // linked to a parent (e.g. a package inside a named entity). - for _, arg := range ent.getArgs() { - if argEnt, isArgEnt := arg.(Entity); isArgEnt && argEnt.Parent() == nil { - argEnt.setParent(ent.Parent()) + for _, arg := range ent.Args() { + if argEnt, isArgEnt := arg.(entity.Entity); isArgEnt && argEnt.Parent() == nil { + argEnt.SetParent(ent.Parent()) } } - if res, ok := ent.(resolver); ok && !res.Resolve(p.errWriter, p.root) { - resolveFailed = true - return false + // Resolve any symbol references + if lazyRef, ok := ent.(entity.LazyRefResolver); ok { + if err := lazyRef.ResolveSymbolRefs(p.root); err != nil { + kfmt.Fprintf(p.errWriter, "%s\n", err.Message) + resolveFailed = true + return false + } } return true @@ -96,57 +104,6 @@ func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDT return nil } -// detectMethodDeclarations scans the AML byte-stream looking for function -// declarations. For each discovered function, the method will parse its flags -// and update the methodArgCount map with the number of required arguments. -func (p *Parser) detectMethodDeclarations() { - var ( - next *opcodeInfo - method string - startOffset = p.r.Offset() - curOffset, pkgLen uint32 - flags uint64 - ok bool - ) - - for !p.r.EOF() { - if next, ok = p.nextOpcode(); !ok { - // Skip one byte to the right and try again. Maybe we are stuck inside - // the contents of a string or buffer - _, _ = p.r.ReadByte() - continue - } - - if next.op != opMethod { - continue - } - - // Parse pkg len; if this fails then this is not a method declaration - curOffset = p.r.Offset() - if pkgLen, ok = p.parsePkgLength(); !ok { - continue - } - - // Parse method name - if method, ok = p.parseNameString(); !ok { - continue - } - - // The next byte encodes the method flags which also contains the arg count - // at bits 0:2 - if flags, ok = p.parseNumConstant(1); !ok { - continue - } - - p.methodArgCount[method] = uint8(flags) & 0x7 - - // At this point we can use the pkg length to skip over the term list - p.r.SetOffset(curOffset + pkgLen) - } - - p.r.SetOffset(startOffset) -} - // parseObjList tries to parse an AML object list. Object lists are usually // specified together with a pkgLen block which is used to calculate the max // read offset that the parser may reach. @@ -169,8 +126,7 @@ func (p *Parser) parseObj() bool { ) // If we cannot decode the next opcode then this may be a method - // invocation or a name reference. If neither is the case, we need to - // rewind the stream and parse a method invocation before giving up. + // invocation or a name reference. curOffset = p.r.Offset() if info, ok = p.nextOpcode(); !ok { p.r.SetOffset(curOffset) @@ -187,11 +143,11 @@ func (p *Parser) parseObj() bool { } // If we encounter a named scope we need to look it up and parse the arg list relative to it - switch info.op { - case opScope: + switch { + case info.op == entity.OpScope: return p.parseScope(curOffset + pkgLen) - case opDevice, opMethod: - return p.parseNamespacedObj(info.op, curOffset+pkgLen) + case info.flags.is(opFlagNamed | opFlagScoped): + return p.parseNamespacedObj(info, curOffset+pkgLen) } // Create appropriate object for opcode type and attach it to current scope unless it is @@ -217,33 +173,22 @@ func (p *Parser) parseObj() bool { } // finalizeObj applies post-parse logic for special object types. -func (p *Parser) finalizeObj(op opcode, obj Entity) bool { - obj.setTableHandle(p.tableHandle) - +func (p *Parser) finalizeObj(op entity.AMLOpcode, obj entity.Entity) bool { switch op { - case opElse: + case entity.OpElse: // If this is an else block we need to append it as an argument to the // If block // Pop Else block of the current scope curScope := p.scopeCurrent() - curScope.removeChild(curScope.lastChild()) - prevObj := curScope.lastChild() - if prevObj.getOpcode() != opIf { + curScope.Remove(curScope.Last()) + prevObj := curScope.Last() + if prevObj.Opcode() != entity.OpIf { kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] encountered else block without a matching if block\n", p.tableName, p.r.Offset()) return false } // If predicate(0) then(1) else(2) - prevObj.setArg(2, obj) - case opDevice: - // Build method map - dev := obj.(*Device) - dev.methodMap = make(map[string]*Method) - scopeVisit(0, dev, EntityTypeMethod, func(_ int, ent Entity) bool { - method := ent.(*Method) - dev.methodMap[method.name] = method - return false - }) + prevObj.SetArg(2, obj) } return true @@ -262,14 +207,14 @@ func (p *Parser) parseScope(maxReadOffset uint32) bool { return false } - target := scopeFind(p.scopeCurrent(), p.root, name) + target := entity.FindInScope(p.scopeCurrent(), p.root, name) if target == nil { kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] undefined scope: %s\n", p.tableName, p.r.Offset(), name) return false } - switch target.getOpcode() { - case opDevice, opProcessor, opThermalZone, opPowerRes: + switch target.Opcode() { + case entity.OpDevice, entity.OpProcessor, entity.OpThermalZone, entity.OpPowerRes: // ok default: // Only allow if this is a named scope @@ -279,7 +224,7 @@ func (p *Parser) parseScope(maxReadOffset uint32) bool { } } - p.scopeEnter(target.(ScopeEntity)) + p.scopeEnter(target.(entity.Container)) ok = p.parseObjList(maxReadOffset) p.scopeExit() @@ -287,48 +232,50 @@ func (p *Parser) parseScope(maxReadOffset uint32) bool { } // parseNamespacedObj reads a scope target name from the AML bytestream, -// attaches the device or method (depending on the opcode) object to the -// correct parent scope, enters the device scope and parses the object list -// contained in the device definition. -func (p *Parser) parseNamespacedObj(op opcode, maxReadOffset uint32) bool { +// attaches the appropriate object depending on the opcode to the correct +// parent scope and then parses any contained objects. The contained objects +// will be appended inside the newly constructed scope. +func (p *Parser) parseNamespacedObj(info *opcodeInfo, maxReadOffset uint32) bool { scopeExpr, ok := p.parseNameString() if !ok { return false } - parent, name := scopeResolvePath(p.scopeCurrent(), p.root, scopeExpr) + parent, name := entity.ResolveScopedPath(p.scopeCurrent(), p.root, scopeExpr) if parent == nil { kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] undefined scope target: %s (current scope: %s)\n", p.tableName, p.r.Offset(), scopeExpr, p.scopeCurrent().Name()) return false } - var obj ScopeEntity - switch op { - case opDevice: - obj = &Device{scopeEntity: scopeEntity{name: name}} - case opMethod: - m := &Method{scopeEntity: scopeEntity{name: name}} - - flags, flagOk := p.parseNumConstant(1) - if !flagOk { - return false - } - m.argCount = (uint8(flags) & 0x7) // bits[0:2] - m.serialized = (uint8(flags)>>3)&0x1 == 0x1 // bit 3 - m.syncLevel = (uint8(flags) >> 4) & 0xf // bits[4:7] - - obj = m + var obj entity.Container + switch info.op { + case entity.OpDevice: + obj = entity.NewDevice(p.tableHandle, name) + case entity.OpProcessor: + obj = entity.NewProcessor(p.tableHandle, name) + case entity.OpPowerRes: + obj = entity.NewPowerResource(p.tableHandle, name) + case entity.OpThermalZone: + obj = entity.NewThermalZone(p.tableHandle, name) + case entity.OpMethod: + obj = entity.NewMethod(p.tableHandle, name) + default: + kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] unsupported namespaced op: %s (current scope: %s)\n", p.tableName, p.r.Offset(), info.op.String(), p.scopeCurrent().Name()) + return false } + // Parse any args that follow the name. The last arg is always an ArgTermList parent.Append(obj) - p.scopeEnter(obj) - ok = p.parseObjList(maxReadOffset) - p.scopeExit() + for argIndex := uint8(1); argIndex < info.argFlags.argCount(); argIndex++ { + if !p.parseArg(info, obj, argIndex, info.argFlags.arg(argIndex), maxReadOffset) { + return false + } + } - return ok && p.finalizeObj(op, obj) + return ok && p.finalizeObj(info.op, obj) } -func (p *Parser) parseArg(info *opcodeInfo, obj Entity, argIndex uint8, argType opArgFlag, maxReadOffset uint32) bool { +func (p *Parser) parseArg(info *opcodeInfo, obj entity.Entity, argIndex uint8, argType opArgFlag, maxReadOffset uint32) bool { var ( arg interface{} ok bool @@ -356,22 +303,33 @@ func (p *Parser) parseArg(info *opcodeInfo, obj Entity, argIndex uint8, argType case opArgTarget: arg, ok = p.parseTarget() case opArgTermList: + // If this is a method and the SkipMethodBodies option is set + // then record the body start and end offset so we can parse + // it at a later stage. + if method, isMethod := obj.(*entity.Method); isMethod && p.parseOptions == parseOptSkipMethodBodies { + method.BodyStartOffset = p.r.Offset() + method.BodyEndOffset = maxReadOffset + p.r.SetOffset(maxReadOffset) + return true + } + // If object is a scoped entity enter it's scope before parsing // the term list. Otherwise, create an unnamed scope, attach it // as the next argument to obj and enter that. - if s, isScopeEnt := obj.(ScopeEntity); isScopeEnt { + if s, isScopeEnt := obj.(entity.Container); isScopeEnt { p.scopeEnter(s) } else { - ns := &scopeEntity{op: opScope} + // Create an unnamed scope (e.g if, else, while scope) + ns := entity.NewScope(info.op, p.tableHandle, "") p.scopeEnter(ns) - obj.setArg(argIndex, ns) + obj.SetArg(argIndex, ns) } ok = p.parseObjList(maxReadOffset) p.scopeExit() return ok case opArgFieldList: - return p.parseFieldList(info.op, obj.getArgs(), maxReadOffset) + return p.parseFieldList(obj, maxReadOffset) case opArgByteList: var bl []byte for p.r.Offset() < maxReadOffset { @@ -388,48 +346,90 @@ func (p *Parser) parseArg(info *opcodeInfo, obj Entity, argIndex uint8, argType return false } - return obj.setArg(argIndex, arg) + return obj.SetArg(argIndex, arg) } -func (p *Parser) parseArgObj() (Entity, bool) { +func (p *Parser) parseArgObj() (entity.Entity, bool) { if ok := p.parseObj(); !ok { return nil, false } curScope := p.scopeCurrent() - obj := curScope.lastChild() - curScope.removeChild(obj) + obj := curScope.Last() + curScope.Remove(obj) return obj, true } -func (p *Parser) makeObjForOpcode(info *opcodeInfo) Entity { - var obj Entity +func (p *Parser) makeObjForOpcode(info *opcodeInfo) entity.Entity { + var obj entity.Entity switch { - case info.op == opOpRegion: - obj = new(regionEntity) - case info.op == opBuffer: - obj = new(bufferEntity) - case info.op == opMutex: - obj = new(mutexEntity) - case info.op == opEvent: - obj = new(eventEntity) - case opIsBufferField(info.op): - obj = new(bufferFieldEntity) + case info.op == entity.OpOpRegion: + obj = entity.NewRegion(p.tableHandle) + case info.op == entity.OpBuffer: + obj = entity.NewBuffer(p.tableHandle) + case info.op == entity.OpMutex: + obj = entity.NewMutex(p.tableHandle) + case info.op == entity.OpEvent: + obj = entity.NewEvent(p.tableHandle) + case info.op == entity.OpField: + obj = entity.NewField(p.tableHandle) + case info.op == entity.OpIndexField: + obj = entity.NewIndexField(p.tableHandle) + case info.op == entity.OpBankField: + obj = entity.NewBankField(p.tableHandle) + case info.op == entity.OpCreateField: + obj = entity.NewBufferField(info.op, p.tableHandle, 0) + case info.op == entity.OpCreateBitField: + obj = entity.NewBufferField(info.op, p.tableHandle, 1) + case info.op == entity.OpCreateByteField: + obj = entity.NewBufferField(info.op, p.tableHandle, 8) + case info.op == entity.OpCreateWordField: + obj = entity.NewBufferField(info.op, p.tableHandle, 16) + case info.op == entity.OpCreateDWordField: + obj = entity.NewBufferField(info.op, p.tableHandle, 32) + case info.op == entity.OpCreateQWordField: + obj = entity.NewBufferField(info.op, p.tableHandle, 64) + case info.op == entity.OpZero: + obj = entity.NewConst(info.op, p.tableHandle, uint64(0)) + case info.op == entity.OpOne: + obj = entity.NewConst(info.op, p.tableHandle, uint64(1)) + case info.op == entity.OpOnes: + obj = entity.NewConst(info.op, p.tableHandle, uint64((1<<64)-1)) case info.flags.is(opFlagConstant): - obj = new(constEntity) + obj = entity.NewConst(info.op, p.tableHandle, nil) // will be parsed as an arg + case info.op == entity.OpPackage || info.op == entity.OpVarPackage: + obj = entity.NewPackage(info.op, p.tableHandle) case info.flags.is(opFlagScoped): - obj = new(scopeEntity) + obj = entity.NewScope(info.op, p.tableHandle, "") case info.flags.is(opFlagNamed): - obj = new(namedEntity) + obj = entity.NewGenericNamed(info.op, p.tableHandle) default: - obj = new(unnamedEntity) + obj = entity.NewGeneric(info.op, p.tableHandle) } - obj.setOpcode(info.op) return obj } +// parseMethodBody parses the entities that make up a method's body. After the +// entire AML tree has been parsed, the parser makes a second pass and calls +// parseMethodBody for each Method entity. +// +// By deferring the parsing of the method body, we ensure that the parser can +// lookup the method declarations (even if forward declarations are used) for +// each method invocation. As method declarations contain information about the +// expected argument count, the parser can use this information to properly +// parse the invocation arguments. For more details see: parseNamedRef +func (p *Parser) parseMethodBody(method *entity.Method) bool { + p.parseOptions = parseOptParseMethodBodies + p.scopeEnter(method) + p.r.SetOffset(method.BodyStartOffset) + ok := p.parseArg(&opcodeTable[methodOpInfoIndex], method, 2, opArgTermList, method.BodyEndOffset) + p.scopeExit() + + return ok +} + // parseNamedRef attempts to parse either a method invocation or a named // reference. As AML allows for forward references, the actual contents for // this entity will not be known until the entire AML stream has been parsed. @@ -444,29 +444,31 @@ func (p *Parser) parseNamedRef() bool { return false } - var ( - curOffset uint32 - argIndex uint8 - arg Entity - argList []interface{} - ) + // Check if this is a method invocation + ent := entity.FindInScope(p.scopeCurrent(), p.root, name) + if methodDef, isMethod := ent.(*entity.Method); isMethod { + var ( + curOffset uint32 + argIndex uint8 + arg entity.Entity + argList []interface{} + ) - if argCount, isMethod := p.methodArgCount[name]; isMethod { - for argIndex < argCount && !p.r.EOF() { + for argIndex < methodDef.ArgCount && !p.r.EOF() { // Peek next opcode curOffset = p.r.Offset() nextOpcode, ok := p.nextOpcode() p.r.SetOffset(curOffset) switch { - case ok && (opIsType2(nextOpcode.op) || opIsArg(nextOpcode.op) || opIsDataObject(nextOpcode.op)): + case ok && (entity.OpIsType2(nextOpcode.op) || entity.OpIsArg(nextOpcode.op) || entity.OpIsDataObject(nextOpcode.op)): arg, ok = p.parseArgObj() default: // It may be a nested invocation or named ref ok = p.parseNamedRef() if ok { - arg = p.scopeCurrent().lastChild() - p.scopeCurrent().removeChild(arg) + arg = p.scopeCurrent().Last() + p.scopeCurrent().Remove(arg) } } @@ -481,19 +483,16 @@ func (p *Parser) parseNamedRef() bool { } // Check whether all expected arguments have been parsed - if argIndex != argCount { - kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] unexpected arglist end for method %s invocation: expected %d; got %d\n", p.tableName, p.r.Offset(), name, argCount, argIndex) + if argIndex != methodDef.ArgCount { + kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] unexpected arglist end for method %s invocation: expected %d; got %d\n", p.tableName, p.r.Offset(), name, methodDef.ArgCount, argIndex) return false } - return p.scopeCurrent().Append(&methodInvocationEntity{ - unnamedEntity: unnamedEntity{args: argList}, - methodName: name, - }) + return p.scopeCurrent().Append(entity.NewInvocation(p.tableHandle, methodDef, argList)) } // Otherwise this is a reference to a named entity - return p.scopeCurrent().Append(&namedReference{targetName: name}) + return p.scopeCurrent().Append(entity.NewReference(p.tableHandle, name)) } func (p *Parser) nextOpcode() (*opcodeInfo, bool) { @@ -533,69 +532,30 @@ func (p *Parser) nextOpcode() (*opcodeInfo, bool) { // AccessField := 0x1 AccessType AccessAttrib // ConnectField := 0x02 NameString | 0x02 BufferData // ExtendedAccessField := 0x3 AccessType ExtendedAccessType AccessLength -func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uint32) bool { +func (p *Parser) parseFieldList(fieldEnt entity.Entity, maxReadOffset uint32) bool { var ( - // for fieldUnit, name0 is the region name and name1 is not used; - // for indexField, - name0, name1 string - flags uint64 + ok bool - ok bool - bitWidth uint32 - curBitOffset uint32 - accessAttrib FieldAccessAttrib - accessByteCount uint8 - unitName string - ) + accessType entity.FieldAccessType - switch op { - case opField: // Field := PkgLength Region AccessFlags FieldList - if len(args) != 2 { - kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d, opcode 0x%2x] invalid arg count: %d\n", p.tableName, p.r.Offset(), uint32(op), len(args)) - return false - } - - name0, ok = args[0].(string) - if !ok { - return false - } - - flags, ok = args[1].(uint64) - if !ok { - return false - } - case opIndexField: // Field := PkgLength IndexFieldName DataFieldName AccessFlags FieldList - if len(args) != 3 { - kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d, opcode 0x%2x] invalid arg count: %d\n", p.tableName, p.r.Offset(), uint32(op), len(args)) - return false - } - - name0, ok = args[0].(string) - if !ok { - return false - } - - name1, ok = args[1].(string) - if !ok { - return false - } - - flags, ok = args[2].(uint64) - if !ok { - return false - } - } - - // Decode flags - accessType := FieldAccessType(flags & 0xf) // access type; bits[0:3] - lock := (flags>>4)&0x1 == 0x1 // lock; bit 4 - updateRule := FieldUpdateRule((flags >> 5) & 0x3) // update rule; bits[5:6] - - var ( + bitWidth uint32 + curBitOffset uint32 connectionName string - resolvedConnection Entity + unitName string + resolvedConnection entity.Entity + accessAttrib entity.FieldAccessAttrib + accessByteCount uint8 ) + // Load default field access rule; it applies to all field units unless + // overridden via a directive in the field unit list + if accessProvider, isProvider := fieldEnt.(entity.FieldAccessTypeProvider); isProvider { + accessType = accessProvider.DefaultAccessType() + } else { + // not a field entity + return false + } + for p.r.Offset() < maxReadOffset { next, err := p.r.ReadByte() if err != nil { @@ -616,7 +576,7 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin if err != nil { return false } - accessType = FieldAccessType(next & 0xf) // access type; bits[0:3] + accessType = entity.FieldAccessType(next & 0xf) // access type; bits[0:3] attrib, err := p.r.ReadByte() if err != nil { @@ -626,7 +586,7 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin // To specify AccessAttribBytes, RawBytes and RawProcessBytes // the ASL compiler will emit an ExtendedAccessField opcode. accessByteCount = 0 - accessAttrib = FieldAccessAttrib(attrib) + accessAttrib = entity.FieldAccessAttrib(attrib) continue case 0x2: // ConnectField => <0x2> NameString> | <0x02> TermObj => Buffer @@ -643,7 +603,7 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin if err != nil { return false } - accessType = FieldAccessType(next & 0xf) // access type; bits[0:3] + accessType = entity.FieldAccessType(next & 0xf) // access type; bits[0:3] extAccessAttrib, err := p.r.ReadByte() if err != nil { @@ -657,11 +617,11 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin switch extAccessAttrib { case 0x0b: - accessAttrib = FieldAccessAttribBytes + accessAttrib = entity.FieldAccessAttribBytes case 0xe: - accessAttrib = FieldAccessAttribRawBytes + accessAttrib = entity.FieldAccessAttribRawBytes case 0x0f: - accessAttrib = FieldAccessAttribRawProcessBytes + accessAttrib = entity.FieldAccessAttribRawProcessBytes } default: // NamedField _ = p.r.UnreadByte() @@ -675,55 +635,20 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin } // According to the spec, the field elements are should - // be visible at the same scope as the Field/IndexField - switch op { - case opField: - p.scopeCurrent().Append(&fieldUnitEntity{ - fieldEntity: fieldEntity{ - namedEntity: namedEntity{ - tableHandle: p.tableHandle, - op: op, - name: unitName, - }, - bitOffset: curBitOffset, - bitWidth: bitWidth, - lock: lock, - updateRule: updateRule, - accessType: accessType, - accessAttrib: accessAttrib, - byteCount: accessByteCount, - }, - connectionName: connectionName, - resolvedConnection: resolvedConnection, - regionName: name0, - }) - case opIndexField: - p.scopeCurrent().Append(&indexFieldEntity{ - fieldEntity: fieldEntity{ - namedEntity: namedEntity{ - tableHandle: p.tableHandle, - op: op, - name: unitName, - }, - bitOffset: curBitOffset, - bitWidth: bitWidth, - lock: lock, - updateRule: updateRule, - accessType: accessType, - accessAttrib: accessAttrib, - byteCount: accessByteCount, - }, - connectionName: connectionName, - resolvedConnection: resolvedConnection, - indexRegName: name0, - dataRegName: name1, - }) - } + // be visible at the same scope as the Field that declares them + unit := entity.NewFieldUnit(p.tableHandle, unitName) + unit.Field = fieldEnt + unit.AccessType = accessType + unit.AccessAttrib = accessAttrib + unit.ByteCount = accessByteCount + unit.BitOffset = curBitOffset + unit.BitWidth = bitWidth + unit.ConnectionName = connectionName + unit.Connection = resolvedConnection + p.scopeCurrent().Append(unit) curBitOffset += bitWidth - } - } return ok && p.r.Offset() == maxReadOffset @@ -859,10 +784,8 @@ func (p *Parser) parseSimpleName() (interface{}, bool) { var obj interface{} switch { - case ok && nextOpcode.op >= opLocal0 && nextOpcode.op <= opLocal7: - obj, ok = &unnamedEntity{op: nextOpcode.op}, true - case ok && nextOpcode.op >= opArg0 && nextOpcode.op <= opArg6: - obj, ok = &unnamedEntity{op: nextOpcode.op}, true + case ok && entity.OpIsArg(nextOpcode.op): + obj, ok = entity.NewGeneric(nextOpcode.op, p.tableHandle), true default: // Rewind and try parsing as NameString p.r.SetOffset(curOffset) @@ -890,10 +813,10 @@ func (p *Parser) parseTarget() (interface{}, bool) { if ok { switch { - case nextOpcode.op == opZero: // this is actually a NullName + case nextOpcode.op == entity.OpZero: // this is actually a NullName p.r.SetOffset(curOffset + 1) - return &constEntity{op: opStringPrefix, val: ""}, true - case opIsArg(nextOpcode.op) || nextOpcode.op == opRefOf || nextOpcode.op == opDerefOf || nextOpcode.op == opIndex || nextOpcode.op == opDebug: // LocalObj | ArgObj | Type6 | DebugObj + return entity.NewConst(entity.OpStringPrefix, p.tableHandle, ""), true + case entity.OpIsArg(nextOpcode.op) || nextOpcode.op == entity.OpRefOf || nextOpcode.op == entity.OpDerefOf || nextOpcode.op == entity.OpIndex || nextOpcode.op == entity.OpDebug: // LocalObj | ArgObj | Type6 | DebugObj default: // Unexpected opcode return nil, false @@ -905,8 +828,8 @@ func (p *Parser) parseTarget() (interface{}, bool) { // In this case, this is either a NameString or a control method invocation. if ok := p.parseNamedRef(); ok { - obj := p.scopeCurrent().lastChild() - p.scopeCurrent().removeChild(obj) + obj := p.scopeCurrent().Last() + p.scopeCurrent().Remove(obj) return obj, ok } @@ -996,12 +919,12 @@ func (p *Parser) parseNameString() (string, bool) { } // scopeCurrent returns the currently active scope. -func (p *Parser) scopeCurrent() ScopeEntity { +func (p *Parser) scopeCurrent() entity.Container { return p.scopeStack[len(p.scopeStack)-1] } // scopeEnter enters the given scope. -func (p *Parser) scopeEnter(s ScopeEntity) { +func (p *Parser) scopeEnter(s entity.Container) { p.scopeStack = append(p.scopeStack, s) } diff --git a/src/gopheros/device/acpi/aml/parser_test.go b/src/gopheros/device/acpi/aml/parser/parser_test.go similarity index 66% rename from src/gopheros/device/acpi/aml/parser_test.go rename to src/gopheros/device/acpi/aml/parser/parser_test.go index 654df99..78d3bbf 100644 --- a/src/gopheros/device/acpi/aml/parser_test.go +++ b/src/gopheros/device/acpi/aml/parser/parser_test.go @@ -1,6 +1,7 @@ -package aml +package parser import ( + "gopheros/device/acpi/aml/entity" "gopheros/device/acpi/table" "io/ioutil" "os" @@ -23,19 +24,7 @@ func TestParser(t *testing.T) { tableFiles: spec, } - // Create default scopes - rootNS := &scopeEntity{op: opScope, name: `\`} - rootNS.Append(&scopeEntity{op: opScope, name: `_GPE`}) // General events in GPE register block - rootNS.Append(&scopeEntity{op: opScope, name: `_PR_`}) // ACPI 1.0 processor namespace - rootNS.Append(&scopeEntity{op: opScope, name: `_SB_`}) // System bus with all device objects - rootNS.Append(&scopeEntity{op: opScope, name: `_SI_`}) // System indicators - rootNS.Append(&scopeEntity{op: opScope, name: `_TZ_`}) // ACPI 1.0 thermal zone namespace - - // Inject pre-defined OSPM objects - rootNS.Append(&constEntity{name: "_OS_", val: "gopheros"}) - rootNS.Append(&constEntity{name: "_REV", val: uint64(2)}) - - p := NewParser(os.Stderr, rootNS) + p := NewParser(os.Stderr, genDefaultScopes()) for _, tableName := range spec { tableName = strings.Replace(tableName, ".aml", "", -1) @@ -47,28 +36,78 @@ func TestParser(t *testing.T) { } } +func TestParsingOfMethodBodies(t *testing.T) { + var resolver = mockResolver{ + tableFiles: []string{"parser-testsuite-fwd-decls-DSDT.aml"}, + } + + p := NewParser(os.Stderr, genDefaultScopes()) + tableName := strings.Replace(resolver.tableFiles[0], ".aml", "", -1) + if err := p.ParseAML(0, tableName, resolver.LookupTable(tableName)); err != nil { + t.Fatalf("[%s]: %v", tableName, err) + } + + // Collect invocations + var invocations []*entity.Invocation + entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool { + if inv, isInv := ent.(*entity.Invocation); isInv { + invocations = append(invocations, inv) + } + return true + }) + + specs := []struct { + expParentName string + expArgCount int + }{ + // Call to `\NST1` + {`\`, 1}, + // Call to `\_SB.NST1` + {`_SB_`, 2}, + // Call to `\NST1` (first argument of above invocation) + {`\`, 1}, + } + + if exp, got := len(specs), len(invocations); exp != got { + t.Fatalf("expected parser to produce %d method invocations; got %d", exp, got) + } + + for specIndex, spec := range specs { + if got := invocations[specIndex].Method.Parent().Name(); got != spec.expParentName { + t.Errorf( + "[spec %d] expected invocation to target %s.%s; got %s.%s", + specIndex, + spec.expParentName, invocations[specIndex].Method.Name(), + got, invocations[specIndex].Method.Name(), + ) + } + + if got := len(invocations[specIndex].Args()); got != spec.expArgCount { + t.Errorf( + "[spec %d] expected invocation to target %s.%s to receive %d args; got %d", + specIndex, + spec.expParentName, invocations[specIndex].Method.Name(), spec.expArgCount, + got, + ) + } + } +} + func TestTableHandleAssignment(t *testing.T) { var resolver = mockResolver{tableFiles: []string{"parser-testsuite-DSDT.aml"}} - // Create default scopes - rootNS := &scopeEntity{op: opScope, name: `\`} - rootNS.Append(&scopeEntity{op: opScope, name: `_GPE`}) // General events in GPE register block - rootNS.Append(&scopeEntity{op: opScope, name: `_PR_`}) // ACPI 1.0 processor namespace - rootNS.Append(&scopeEntity{op: opScope, name: `_SB_`}) // System bus with all device objects - rootNS.Append(&scopeEntity{op: opScope, name: `_SI_`}) // System indicators - rootNS.Append(&scopeEntity{op: opScope, name: `_TZ_`}) // ACPI 1.0 thermal zone namespace - + rootNS := genDefaultScopes() p := NewParser(ioutil.Discard, rootNS) - expHandle := uint8(42) + expHandle := uint8(0x0f) tableName := "parser-testsuite-DSDT" if err := p.ParseAML(expHandle, tableName, resolver.LookupTable(tableName)); err != nil { t.Error(err) } // Drop all entities that were assigned the handle value - var unloadList []Entity - scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool { + var unloadList []entity.Entity + entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool { if ent.TableHandle() == expHandle { unloadList = append(unloadList, ent) return false @@ -78,13 +117,13 @@ func TestTableHandleAssignment(t *testing.T) { for _, ent := range unloadList { if p := ent.Parent(); p != nil { - p.removeChild(ent) + p.Remove(ent) } } // We should end up with the original tree var visitedNodes int - scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool { + entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool { visitedNodes++ if ent.TableHandle() == expHandle { t.Errorf("encountered entity that should have been pruned: %#+v", ent) @@ -102,15 +141,7 @@ func TestParserForwardDeclParsing(t *testing.T) { tableFiles: []string{"parser-testsuite-fwd-decls-DSDT.aml"}, } - // Create default scopes - rootNS := &scopeEntity{op: opScope, name: `\`} - rootNS.Append(&scopeEntity{op: opScope, name: `_GPE`}) // General events in GPE register block - rootNS.Append(&scopeEntity{op: opScope, name: `_PR_`}) // ACPI 1.0 processor namespace - rootNS.Append(&scopeEntity{op: opScope, name: `_SB_`}) // System bus with all device objects - rootNS.Append(&scopeEntity{op: opScope, name: `_SI_`}) // System indicators - rootNS.Append(&scopeEntity{op: opScope, name: `_TZ_`}) // ACPI 1.0 thermal zone namespace - - p := NewParser(ioutil.Discard, rootNS) + p := NewParser(ioutil.Discard, genDefaultScopes()) for _, tableName := range resolver.tableFiles { tableName = strings.Replace(tableName, ".aml", "", -1) @@ -172,7 +203,7 @@ func TestParserErrorHandling(t *testing.T) { t.Run("ParseAML errors", func(t *testing.T) { t.Run("parseObjList error", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) // Setup resolver to serve an AML stream containing an invalid opcode header := mockParserPayload(p, []byte{0x5b, 0x00}) @@ -190,12 +221,10 @@ func TestParserErrorHandling(t *testing.T) { }) t.Run("unresolved entities", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) // Inject a reference entity to the tree - p.root.Append(&namedReference{ - targetName: "UNKNOWN", - }) + p.root.Append(entity.NewReference(42, "UNKNOWN")) // Setup resolver to serve an empty AML stream header := mockParserPayload(p, nil) @@ -208,11 +237,11 @@ func TestParserErrorHandling(t *testing.T) { t.Run("parseObj errors", func(t *testing.T) { t.Run("parsePkgLength error", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) // Setup resolver to serve an AML stream containing an incomplete // buffer specification - header := mockParserPayload(p, []byte{byte(opBuffer)}) + header := mockParserPayload(p, []byte{byte(entity.OpBuffer)}) if err := p.ParseAML(uint8(42), "DSDT", header); err == nil { t.Fatal("expected parsePkgLength to return an error") @@ -220,11 +249,11 @@ func TestParserErrorHandling(t *testing.T) { }) t.Run("incomplete object list", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) // Setup resolver to serve an AML stream containing an incomplete // buffer arglist specification - header := mockParserPayload(p, []byte{byte(opBuffer), 0x10}) + header := mockParserPayload(p, []byte{byte(entity.OpBuffer), 0x10}) if err := p.ParseAML(uint8(42), "DSDT", header); err == nil { t.Fatal("expected parsePkgLength to return an error") @@ -234,13 +263,12 @@ func TestParserErrorHandling(t *testing.T) { t.Run("finalizeObj errors", func(t *testing.T) { t.Run("else without matching if", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} - p.root.Append(&constEntity{val: 0x42}) - p.root.Append(&scopeEntity{op: opElse}) + p.root = entity.NewScope(entity.OpScope, 42, `\`) + p.root.Append(entity.NewConst(entity.OpDwordPrefix, 42, uint64(0x42))) // Setup resolver to serve an AML stream containing an // empty else statement without a matching if - header := mockParserPayload(p, []byte{byte(opElse), 0x0}) + header := mockParserPayload(p, []byte{byte(entity.OpElse), 0x0}) if err := p.ParseAML(uint8(42), "DSDT", header); err == nil { t.Fatal("expected finalizeObj to return an error") @@ -251,10 +279,10 @@ func TestParserErrorHandling(t *testing.T) { t.Run("parseScope errors", func(t *testing.T) { t.Run("parseNameString error", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) header := mockParserPayload(p, []byte{ - byte(opScope), + byte(entity.OpScope), 0x10, // pkglen }) @@ -264,10 +292,10 @@ func TestParserErrorHandling(t *testing.T) { }) t.Run("unknown scope", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) header := mockParserPayload(p, []byte{ - byte(opScope), + byte(entity.OpScope), 0x10, // pkglen 'F', 'O', 'O', 'F', }) @@ -278,10 +306,10 @@ func TestParserErrorHandling(t *testing.T) { }) t.Run("nameless scope", func(t *testing.T) { - p.root = &scopeEntity{} + p.root = entity.NewScope(entity.OpScope, 42, ``) header := mockParserPayload(p, []byte{ - byte(opScope), + byte(entity.OpScope), 0x02, // pkglen '\\', // scope name: "\" (root scope) 0x00, // null string @@ -295,58 +323,76 @@ func TestParserErrorHandling(t *testing.T) { t.Run("parseNamespacedObj errors", func(t *testing.T) { t.Run("parseNameString error", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) mockParserPayload(p, nil) - if p.parseNamespacedObj(opDevice, 10) { + devInfo := &opcodeTable[0x6a] + if p.parseNamespacedObj(devInfo, 10) { t.Fatal("expected parseNamespacedObj to return false") } }) t.Run("scope lookup error", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) header := mockParserPayload(p, []byte{'^', 'F', 'A', 'B', 'C'}) p.scopeEnter(p.root) - if p.parseNamespacedObj(opDevice, header.Length) { + devInfo := &opcodeTable[0x6a] + if p.parseNamespacedObj(devInfo, header.Length) { t.Fatal("expected parseNamespacedObj to return false") } }) - t.Run("error parsing method arg count", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + t.Run("unsupported namespaced entity", func(t *testing.T) { + p.root = entity.NewScope(entity.OpScope, 42, `\`) header := mockParserPayload(p, []byte{'F', 'A', 'B', 'C'}) p.scopeEnter(p.root) - if p.parseNamespacedObj(opMethod, header.Length) { + + // We just pass a random non-namespaced opcode table entry to parseNamespacedObj + zeroInfo := &opcodeTable[0x00] + if p.parseNamespacedObj(zeroInfo, header.Length) { + t.Fatal("expected parseNamespacedObj to return false") + } + }) + + t.Run("error parsing args after name", func(t *testing.T) { + p.root = entity.NewScope(entity.OpScope, 42, `\`) + + header := mockParserPayload(p, []byte{'F', 'A', 'B', 'C'}) + + p.scopeEnter(p.root) + methodInfo := &opcodeTable[0x0d] + if p.parseNamespacedObj(methodInfo, header.Length) { t.Fatal("expected parseNamespacedObj to return false") } }) }) t.Run("parseArg bytelist errors", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) mockParserPayload(p, nil) - if p.parseArg(new(opcodeInfo), new(unnamedEntity), 0, opArgByteList, 42) { + if p.parseArg(new(opcodeInfo), entity.NewGeneric(0, 0), 0, opArgByteList, 42) { t.Fatal("expected parseNamespacedObj to return false") } }) t.Run("parseNamedRef errors", func(t *testing.T) { - t.Run("missing args", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} - p.methodArgCount = map[string]uint8{ - "MTHD": 10, - } + t.Run("incorrect args for method", func(t *testing.T) { + p.root = entity.NewScope(entity.OpScope, 42, `\`) + + methodDecl := entity.NewMethod(42, "MTHD") + methodDecl.ArgCount = 5 + p.root.Append(methodDecl) mockParserPayload(p, []byte{ 'M', 'T', 'H', 'D', - byte(opIf), // Incomplete type2 opcode + byte(entity.OpIf), // Incomplete type2 opcode }) p.scopeEnter(p.root) @@ -358,123 +404,149 @@ func TestParserErrorHandling(t *testing.T) { t.Run("parseFieldList errors", func(t *testing.T) { specs := []struct { - op opcode + op entity.AMLOpcode args []interface{} maxReadOffset uint32 payload []byte }{ - // Invalid arg count for opField + // Invalid arg count for entity.OpField { - opField, + entity.OpField, nil, 0, nil, }, - // Wrong arg type for opField + // Wrong arg type for entity.OpField { - opField, + entity.OpField, []interface{}{0, uint64(42)}, 0, nil, }, { - opField, + entity.OpField, []interface{}{"FLD0", uint32(42)}, 0, nil, }, - // Invalid arg count for opIndexField + // Invalid arg count for entity.OpIndexField { - opIndexField, + entity.OpIndexField, nil, 0, nil, }, - // Wrong arg type for opIndexField + // Wrong arg type for entity.OpIndexField { - opIndexField, + entity.OpIndexField, []interface{}{0, "FLD1", "FLD2"}, 0, nil, }, { - opIndexField, + entity.OpIndexField, []interface{}{"FLD0", 0, "FLD2"}, 0, nil, }, { - opIndexField, + entity.OpIndexField, + []interface{}{"FLD0", "FLD1", 0}, + 0, + nil, + }, + // Invalid arg count for entity.OpBankField + { + entity.OpBankField, + nil, + 0, + nil, + }, + // Wrong arg type for entity.OpBankField + { + entity.OpBankField, + []interface{}{0, "FLD1", "FLD2"}, + 0, + nil, + }, + { + entity.OpBankField, + []interface{}{"FLD0", 0, "FLD2"}, + 0, + nil, + }, + { + entity.OpBankField, []interface{}{"FLD0", "FLD1", 0}, 0, nil, }, // unexpected EOF parsing fields { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, nil, }, // reserved field (0x00) with missing pkgLen { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{0x00}, }, // access field (0x01) with missing accessType { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{0x01}, }, // access field (0x01) with missing attribute byte { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{0x01, 0x01}, }, // connect field (0x02) with incomplete TermObject => Buffer arg { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, - []byte{0x02, byte(opBuffer)}, + []byte{0x02, byte(entity.OpBuffer)}, }, // extended access field (0x03) with missing ext. accessType { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{0x03}, }, // extended access field (0x03) with missing ext. attribute byte { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{0x03, 0x01}, }, // extended access field (0x03) with missing access byte count value { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{0x03, 0x01, 0x02}, }, // named field with invalid name { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{0xff}, }, // named field with invalid pkgLen { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{'N', 'A', 'M', 'E'}, @@ -484,10 +556,16 @@ func TestParserErrorHandling(t *testing.T) { for specIndex, spec := range specs { mockParserPayload(p, spec.payload) - if p.parseFieldList(spec.op, spec.args, spec.maxReadOffset) { + if p.parseFieldList(entity.NewField(42), spec.maxReadOffset) { t.Errorf("[spec %d] expected parseFieldLis to return false", specIndex) } } + + t.Run("non-field entity argument", func(t *testing.T) { + if p.parseFieldList(entity.NewDevice(42, "DEV0"), 128) { + t.Fatal("expected parseFieldList to return false when a non-field argument is passed to it") + } + }) }) t.Run("parsePkgLength errors", func(t *testing.T) { @@ -532,7 +610,7 @@ func TestParserErrorHandling(t *testing.T) { t.Run("parseTarget errors", func(t *testing.T) { t.Run("unexpected opcode", func(t *testing.T) { // Unexpected opcode - mockParserPayload(p, []byte{byte(opAnd)}) + mockParserPayload(p, []byte{byte(entity.OpAnd)}) if _, ok := p.parseTarget(); ok { t.Error("expected parseTarget to return false") @@ -585,77 +663,6 @@ func TestParserErrorHandling(t *testing.T) { }) } -func TestDetectMethodDeclarations(t *testing.T) { - p := &Parser{ - errWriter: ioutil.Discard, - } - - validMethod := []byte{ - byte(opMethod), - 5, // pkgLen - 'M', 'T', 'H', 'D', - 2, // flags (2 args) - } - - t.Run("success", func(t *testing.T) { - mockParserPayload(p, validMethod) - p.methodArgCount = make(map[string]uint8) - p.detectMethodDeclarations() - - argCount, inMap := p.methodArgCount["MTHD"] - if !inMap { - t.Error(`detectMethodDeclarations failed to parse method "MTHD"`) - } - - if exp := uint8(2); argCount != exp { - t.Errorf(`expected arg count for "MTHD" to be %d; got %d`, exp, argCount) - } - }) - - t.Run("bad pkgLen", func(t *testing.T) { - mockParserPayload(p, []byte{ - byte(opMethod), - // lead byte bits (6:7) indicate 1 extra byte that is missing - byte(1 << 6), - }) - - p.methodArgCount = make(map[string]uint8) - p.detectMethodDeclarations() - }) - - t.Run("error parsing namestring", func(t *testing.T) { - mockParserPayload(p, append([]byte{ - byte(opMethod), - byte(5), // pkgLen - 10, // bogus char, not part of namestring - }, validMethod...)) - - p.methodArgCount = make(map[string]uint8) - p.detectMethodDeclarations() - - argCount, inMap := p.methodArgCount["MTHD"] - if !inMap { - t.Error(`detectMethodDeclarations failed to parse method "MTHD"`) - } - - if exp := uint8(2); argCount != exp { - t.Errorf(`expected arg count for "MTHD" to be %d; got %d`, exp, argCount) - } - }) - - t.Run("error parsing method flags", func(t *testing.T) { - mockParserPayload(p, []byte{ - byte(opMethod), - byte(5), // pkgLen - 'F', 'O', 'O', 'F', - // Missing flag byte - }) - - p.methodArgCount = make(map[string]uint8) - p.detectMethodDeclarations() - }) -} - func mockParserPayload(p *Parser, payload []byte) *table.SDTHeader { resolver := fixedPayloadResolver{payload} header := resolver.LookupTable("DSDT") @@ -678,7 +685,7 @@ type mockResolver struct { } func (m mockResolver) LookupTable(name string) *table.SDTHeader { - pathToDumps := pkgDir() + "/../table/tabletest/" + pathToDumps := pkgDir() + "/../../table/tabletest/" for _, f := range m.tableFiles { if !strings.Contains(f, name) { continue @@ -709,3 +716,23 @@ func (f fixedPayloadResolver) LookupTable(name string) *table.SDTHeader { return hdr } + +func genDefaultScopes() entity.Container { + rootNS := entity.NewScope(entity.OpScope, 42, `\`) + rootNS.Append(entity.NewScope(entity.OpScope, 42, `_GPE`)) // General events in GPE register block + rootNS.Append(entity.NewScope(entity.OpScope, 42, `_PR_`)) // ACPI 1.0 processor namespace + rootNS.Append(entity.NewScope(entity.OpScope, 42, `_SB_`)) // System bus with all device objects + rootNS.Append(entity.NewScope(entity.OpScope, 42, `_SI_`)) // System indicators + rootNS.Append(entity.NewScope(entity.OpScope, 42, `_TZ_`)) // ACPI 1.0 thermal zone namespace + + // Inject pre-defined OSPM objects + rootNS.Append(namedConst(entity.NewConst(entity.OpStringPrefix, 42, "gopheros"), "_OS_")) + rootNS.Append(namedConst(entity.NewConst(entity.OpStringPrefix, 42, uint64(2)), "_REV")) + + return rootNS +} + +func namedConst(ent *entity.Const, name string) *entity.Const { + ent.SetName(name) + return ent +} diff --git a/src/gopheros/device/acpi/aml/stream_reader.go b/src/gopheros/device/acpi/aml/parser/stream_reader.go similarity index 96% rename from src/gopheros/device/acpi/aml/stream_reader.go rename to src/gopheros/device/acpi/aml/parser/stream_reader.go index 31e6e5b..fc151e3 100644 --- a/src/gopheros/device/acpi/aml/stream_reader.go +++ b/src/gopheros/device/acpi/aml/parser/stream_reader.go @@ -1,4 +1,4 @@ -package aml +package parser import ( "errors" @@ -80,5 +80,8 @@ func (r *amlStreamReader) Offset() uint32 { // SetOffset sets the reader offset to the supplied value. func (r *amlStreamReader) SetOffset(off uint32) { + if max := uint32(len(r.data)); off > max { + off = max + } r.offset = off } diff --git a/src/gopheros/device/acpi/aml/stream_reader_test.go b/src/gopheros/device/acpi/aml/parser/stream_reader_test.go similarity index 99% rename from src/gopheros/device/acpi/aml/stream_reader_test.go rename to src/gopheros/device/acpi/aml/parser/stream_reader_test.go index 560a7ca..8c6c701 100644 --- a/src/gopheros/device/acpi/aml/stream_reader_test.go +++ b/src/gopheros/device/acpi/aml/parser/stream_reader_test.go @@ -1,4 +1,4 @@ -package aml +package parser import ( "io" diff --git a/src/gopheros/device/acpi/aml/scope_test.go b/src/gopheros/device/acpi/aml/scope_test.go deleted file mode 100644 index fd2dc85..0000000 --- a/src/gopheros/device/acpi/aml/scope_test.go +++ /dev/null @@ -1,294 +0,0 @@ -package aml - -import ( - "reflect" - "testing" -) - -func TestScopeVisit(t *testing.T) { - scopeMap := genTestScopes() - root := scopeMap[`\`].(*scopeEntity) - - keepRecursing := func(Entity) bool { return true } - stopRecursing := func(Entity) bool { return false } - - // Append special entities under IDE0 - ide := scopeMap["IDE0"].(*scopeEntity) - ide.Append(&Device{}) - ide.Append(&namedEntity{op: opProcessor}) - ide.Append(&namedEntity{op: opProcessor}) - ide.Append(&namedEntity{op: opPowerRes}) - ide.Append(&namedEntity{op: opPowerRes}) - ide.Append(&namedEntity{op: opPowerRes}) - ide.Append(&namedEntity{op: opThermalZone}) - ide.Append(&namedEntity{op: opThermalZone}) - ide.Append(&namedEntity{op: opThermalZone}) - ide.Append(&namedEntity{op: opThermalZone}) - ide.Append(&Method{}) - ide.Append(&Method{}) - ide.Append(&Method{}) - ide.Append(&Method{}) - ide.Append(&Method{}) - ide.Append(&methodInvocationEntity{ - unnamedEntity: unnamedEntity{ - args: []interface{}{ - &constEntity{val: uint64(1)}, - &constEntity{val: uint64(2)}, - }, - }, - }) - - specs := []struct { - searchType EntityType - keepRecursingFn func(Entity) bool - wantHits int - }{ - {EntityTypeAny, keepRecursing, 24}, - {EntityTypeAny, stopRecursing, 1}, - { - EntityTypeAny, - func(ent Entity) bool { - // Stop recursing after visiting the methodInvocationEntity - _, isInv := ent.(*methodInvocationEntity) - return !isInv - }, - 22, - }, - - { - EntityTypeAny, - func(ent Entity) bool { - // Stop recursing after visiting the first constEntity - _, isConst := ent.(*constEntity) - return !isConst - }, - 23, - }, - {EntityTypeDevice, keepRecursing, 1}, - {EntityTypeProcessor, keepRecursing, 2}, - {EntityTypePowerResource, keepRecursing, 3}, - {EntityTypeThermalZone, keepRecursing, 4}, - {EntityTypeMethod, keepRecursing, 5}, - } - - for specIndex, spec := range specs { - var hits int - scopeVisit(0, root, spec.searchType, func(_ int, obj Entity) bool { - hits++ - return spec.keepRecursingFn(obj) - }) - - if hits != spec.wantHits { - t.Errorf("[spec %d] expected visitor to be called %d times; got %d", specIndex, spec.wantHits, hits) - } - } -} - -func TestScopeResolvePath(t *testing.T) { - scopeMap := genTestScopes() - - specs := []struct { - curScope ScopeEntity - pathExpr string - wantParent Entity - wantName string - }{ - { - scopeMap["IDE0"].(*scopeEntity), - `\_SB_`, - scopeMap[`\`], - "_SB_", - }, - { - scopeMap["IDE0"].(*scopeEntity), - `^FOO`, - scopeMap[`PCI0`], - "FOO", - }, - { - scopeMap["IDE0"].(*scopeEntity), - `^^FOO`, - scopeMap[`_SB_`], - "FOO", - }, - { - scopeMap["IDE0"].(*scopeEntity), - `_ADR`, - scopeMap[`IDE0`], - "_ADR", - }, - // Paths with dots - { - scopeMap["IDE0"].(*scopeEntity), - `\_SB_.PCI0.IDE0._ADR`, - scopeMap[`IDE0`], - "_ADR", - }, - { - scopeMap["PCI0"].(*scopeEntity), - `IDE0._ADR`, - scopeMap[`IDE0`], - "_ADR", - }, - { - scopeMap["PCI0"].(*scopeEntity), - `_CRS`, - scopeMap[`PCI0`], - "_CRS", - }, - // Bad queries - { - scopeMap["PCI0"].(*scopeEntity), - `FOO.BAR.BAZ`, - nil, - "", - }, - { - scopeMap["PCI0"].(*scopeEntity), - ``, - nil, - "", - }, - { - scopeMap["PCI0"].(*scopeEntity), - `\`, - nil, - "", - }, - { - scopeMap["PCI0"].(*scopeEntity), - `^^^^^^^^^BADPATH`, - nil, - "", - }, - } - - root := scopeMap[`\`].(*scopeEntity) - for specIndex, spec := range specs { - gotParent, gotName := scopeResolvePath(spec.curScope, root, spec.pathExpr) - if !reflect.DeepEqual(gotParent, spec.wantParent) { - t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.wantParent, gotParent) - continue - } - - if gotName != spec.wantName { - t.Errorf("[spec %d] expected lookup to return node name %q; got %q", specIndex, spec.wantName, gotName) - } - } -} - -func TestScopeFind(t *testing.T) { - scopeMap := genTestScopes() - - specs := []struct { - curScope ScopeEntity - lookup string - want Entity - }{ - // Search rules do not apply for these cases - { - scopeMap["PCI0"].(*scopeEntity), - `\`, - scopeMap[`\`], - }, - { - scopeMap["PCI0"].(*scopeEntity), - "IDE0._ADR", - scopeMap["_ADR"], - }, - { - scopeMap["IDE0"].(*scopeEntity), - "^^PCI0.IDE0._ADR", - scopeMap["_ADR"], - }, - { - scopeMap["IDE0"].(*scopeEntity), - `\_SB_.PCI0.IDE0._ADR`, - scopeMap["_ADR"], - }, - { - scopeMap["IDE0"].(*scopeEntity), - `\_SB_.PCI0`, - scopeMap["PCI0"], - }, - { - scopeMap["IDE0"].(*scopeEntity), - `^`, - scopeMap["PCI0"], - }, - // Bad queries - { - scopeMap["_SB_"].(*scopeEntity), - "PCI0.USB._CRS", - nil, - }, - { - scopeMap["IDE0"].(*scopeEntity), - "^^^^^^^^^^^^^^^^^^^", - nil, - }, - { - scopeMap["IDE0"].(*scopeEntity), - `^^^^^^^^^^^FOO`, - nil, - }, - { - scopeMap["IDE0"].(*scopeEntity), - "FOO", - nil, - }, - { - scopeMap["IDE0"].(*scopeEntity), - "", - nil, - }, - // Search rules apply for these cases - { - scopeMap["IDE0"].(*scopeEntity), - "_CRS", - scopeMap["_CRS"], - }, - } - - root := scopeMap[`\`].(*scopeEntity) - for specIndex, spec := range specs { - if got := scopeFind(spec.curScope, root, spec.lookup); !reflect.DeepEqual(got, spec.want) { - t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.want, got) - } - } -} - -func genTestScopes() map[string]Entity { - // Setup the example tree from page 252 of the acpi 6.2 spec - // \ - // SB - // \ - // PCI0 - // | _CRS - // \ - // IDE0 - // | _ADR - ideScope := &scopeEntity{name: `IDE0`} - pciScope := &scopeEntity{name: `PCI0`} - sbScope := &scopeEntity{name: `_SB_`} - rootScope := &scopeEntity{name: `\`} - - adr := &namedEntity{name: `_ADR`} - crs := &namedEntity{name: `_CRS`} - - // Setup tree - ideScope.Append(adr) - pciScope.Append(crs) - pciScope.Append(ideScope) - sbScope.Append(pciScope) - rootScope.Append(sbScope) - - return map[string]Entity{ - "IDE0": ideScope, - "PCI0": pciScope, - "_SB_": sbScope, - "\\": rootScope, - "_ADR": adr, - "_CRS": crs, - } -} diff --git a/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.aml b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.aml index 5d278a1..5786a18 100644 Binary files a/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.aml and b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.aml differ diff --git a/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.dsl b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.dsl index a81a1e2..ad8509d 100644 --- a/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.dsl +++ b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.dsl @@ -6,112 +6,147 @@ // virtualbox. DefinitionBlock ("parser-testsuite-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0x00000002) { - OperationRegion (DBG0, SystemIO, 0x3000, 0x04) - Field (DBG0, ByteAcc, NoLock, Preserve) - { - DHE1, 8 - } - - Device (DRV0) + OperationRegion (DBG0, SystemIO, 0x3000, 0x04) + Field (DBG0, ByteAcc, NoLock, Preserve) { - Name (_ADR, Ones) - - // named entity containing qword const - Name (H15F, 0xBADC0FEEDEADC0DE) - Method (_GTF, 0, NotSerialized) // _GTF: Get Task File - { - Return (H15F) - } + DHE1, 8 } - // example from p. 268 of ACPI 6.2 spec - Scope(\_SB){ - OperationRegion(TOP1, GenericSerialBus, 0x00, 0x100) // GenericSerialBus device at command offset 0x00 + Device (DRV0) + { + Name (_ADR, Ones) - Name (SDB0, ResourceTemplate() {}) - Field(TOP1, BufferAcc, NoLock, Preserve){ - Connection(SDB0), // Use the Resource Descriptor defined above - AccessAs(BufferAcc, AttribWord), - FLD0, 8, - FLD1, 8 - } + // named entity containing qword const + Name (H15F, 0xBADC0FEEDEADC0DE) + Method (_GTF, 0, NotSerialized) // _GTF: Get Task File + { + Return (H15F) + } + } - Field(TOP1, BufferAcc, NoLock, Preserve){ - Connection(I2cSerialBus(0x5b,,100000,, "\\_SB",,,,RawDataBuffer(){3,9})), - AccessAs(BufferAcc, AttribBytes(4)), - FLD2, 8, - AccessAs(BufferAcc, AttribRawBytes(3)), - FLD3, 8, - AccessAs(BufferAcc, AttribRawProcessBytes(2)), - FLD4, 8 - } + // example from p. 268 of ACPI 6.2 spec + Scope(\_SB){ + OperationRegion(TOP1, GenericSerialBus, 0x00, 0x100) // GenericSerialBus device at command offset 0x00 + + Name (SDB0, ResourceTemplate() {}) + Field(TOP1, BufferAcc, NoLock, Preserve){ + Connection(SDB0), // Use the Resource Descriptor defined above + AccessAs(BufferAcc, AttribWord), + FLD0, 8, + FLD1, 8 + } + + Field(TOP1, BufferAcc, NoLock, Preserve){ + Connection(I2cSerialBus(0x5b,,100000,, "\\_SB",,,,RawDataBuffer(){3,9})), + AccessAs(BufferAcc, AttribBytes(4)), + FLD2, 8, + AccessAs(BufferAcc, AttribRawBytes(3)), + FLD3, 8, + AccessAs(BufferAcc, AttribRawProcessBytes(2)), + FLD4, 8 } + } - // Other entity types - Event(HLO0) + // Other entity types + Event(HLO0) Mutex(MUT0,1) Signal(HLO0) // Other executable bits Method (EXE0, 1, Serialized) { - Local0 = Revision + Local0 = Revision - // NameString target - Local1 = SizeOf(GLB1) + // NameString target + Local1 = SizeOf(GLB1) - Local0 = "my-handle" - Load(DBG0, Local0) - Unload(Local0) + Local0 = "my-handle" + Load(DBG0, Local0) + Unload(Local0) - // Example from p. 951 of the spec - Store ( - LoadTable ("OEM1", "MYOEM", "TABLE1", "\\_SB.PCI0","MYD", - Package () {0,"\\_SB.PCI0"} - ), Local0 - ) + CreateBitField(Arg0, 0, WFL0) + if(Arg0==0){ + Return(WFL0) + } - FromBCD(9, Arg0) - ToBCD(Arg0, Local1) + CreateByteField(Arg0, 0, WFL1) + if(Arg0==1){ + Return(WFL1) + } - Breakpoint - Debug = "test" - Fatal(0xf0, 0xdeadc0de, 1) + CreateWordField(Arg0, 0, WFL2) + if(Arg0==2){ + Return(WFL2) + } - Reset(HLO0) + CreateDwordField(Arg0, 0, WFL3) + if(Arg0==3){ + Return(WFL3) + } - // Mutex support - Acquire(MUT0, 0xffff) // no timeout - Release(MUT0) + CreateQwordField(Arg0, 0, WFL4) + if(Arg0==4){ + Return(WFL4) + } - // Signal/Wait - Wait(HLO0, 0xffff) + CreateField(Arg0, 0, 13, WFL5) + if(Arg0==5){ + Return(WFL5) + } - // Get monotonic timer value - Local0 = Timer + // Example from p. 951 of the spec + Store ( + LoadTable ("OEM1", "MYOEM", "TABLE1", "\\_SB.PCI0","MYD", + Package () {0,"\\_SB.PCI0"} + ), Local0 + ) - CopyObject(Local0, Local1) - Return(ObjectType(Local1)) + FromBCD(9, Arg0) + ToBCD(Arg0, Local1) + + Breakpoint + Debug = "test" + Fatal(0xf0, 0xdeadc0de, 1) + + Reset(HLO0) + + // Mutex support + Acquire(MUT0, 0xffff) // no timeout + Release(MUT0) + + // Signal/Wait + Wait(HLO0, 0xffff) + + // Get monotonic timer value + Local0 = Timer + + CopyObject(Local0, Local1) + Return(ObjectType(Local1)) } - // Misc regions + // Misc regions - // BankField example from p. 899 of the spec - // Define a 256-byte operational region in SystemIO space and name it GIO0 - OperationRegion (GIO0, SystemIO, 0x125, 0x100) + // BankField example from p. 899 of the spec + // Define a 256-byte operational region in SystemIO space and name it GIO0 + OperationRegion (GIO0, SystemIO, 0x125, 0x100) Field (GIO0, ByteAcc, NoLock, Preserve) { - GLB1, 1, + GLB1, 1, GLB2, 1, Offset (1), // Move to offset for byte 1 BNK1, 4 } - BankField (GIO0, BNK1, 0, ByteAcc, NoLock, Preserve) { - Offset (0x30), - FET0, 1, - FET1, 1 - } + BankField (GIO0, BNK1, 0, ByteAcc, NoLock, Preserve) { + Offset (0x30), + FET0, 1, + FET1, 1 + } - // Data Region - DataTableRegion (REG0, "FOOF", "BAR", "BAZ") + // Data Region + DataTableRegion (REG0, "FOOF", "BAR", "BAZ") + + // Other resources + Processor(CPU0, 1, 0x120, 6){} + PowerResource(PWR0, 0, 0){} + ThermalZone(TZ0){} } diff --git a/src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.aml b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.aml index 00d0f4d..68fbecc 100644 Binary files a/src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.aml and b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.aml differ diff --git a/src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.dsl b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.dsl index 0df4e12..81ebdc1 100644 --- a/src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.dsl +++ b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.dsl @@ -1,10 +1,21 @@ DefinitionBlock ("parser-testsuite-fwd-decls-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0x00000002) { + Scope(\_SB){ + Method (NST1, 2, NotSerialized) + { + Return ("something") + } + } + Method(NST0, 1, NotSerialized) { - // Invoke a method which has not been defined at the time the parser - // reaches this block (forward declaration) - Return(NST1(Arg0)) + // NST1 is declared after NST0 (forward declaration) + NST1(Arg0) + + // This version of NST1 is defined above and has a different signature. + // The parser should be able to resolve it to the correct method and + // parse the correct number of arguments + Return(\_SB.NST1(NST1(123), "arg")) } // The declaration of NST1 in the AML stream occurs after the declaration