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

Merge pull request #61 from achilleasa/refactor-aml-parser-into-multiple-packages

Refactor AML parser into multiple packages and fix parser bugs
This commit is contained in:
Achilleas Anagnostopoulos 2018-01-06 10:53:44 +00:00 committed by GitHub
commit 3396b997e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 3310 additions and 2362 deletions

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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)
}
}
}

View File

@ -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
}
}

View File

@ -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)
}
}
}

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

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

View File

@ -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
}

View File

@ -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,
}

View File

@ -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
}
}
}
}

View File

@ -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,
}

View File

@ -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
}
}
}
}

View File

@ -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),
}
}
@ -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,26 +65,34 @@ 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) {
// 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 {
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
}
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
// Parse any args that follow the name. The last arg is always an ArgTermList
parent.Append(obj)
for argIndex := uint8(1); argIndex < info.argFlags.argCount(); argIndex++ {
if !p.parseArg(info, obj, argIndex, info.argFlags.arg(argIndex), maxReadOffset) {
return false
}
}
parent.Append(obj)
p.scopeEnter(obj)
ok = p.parseObjList(maxReadOffset)
p.scopeExit()
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
}
// 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
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
accessType entity.FieldAccessType
bitWidth uint32
curBitOffset uint32
accessAttrib FieldAccessAttrib
accessByteCount uint8
unitName string
)
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 (
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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -64,6 +64,36 @@ DefinitionBlock ("parser-testsuite-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0
Load(DBG0, Local0)
Unload(Local0)
CreateBitField(Arg0, 0, WFL0)
if(Arg0==0){
Return(WFL0)
}
CreateByteField(Arg0, 0, WFL1)
if(Arg0==1){
Return(WFL1)
}
CreateWordField(Arg0, 0, WFL2)
if(Arg0==2){
Return(WFL2)
}
CreateDwordField(Arg0, 0, WFL3)
if(Arg0==3){
Return(WFL3)
}
CreateQwordField(Arg0, 0, WFL4)
if(Arg0==4){
Return(WFL4)
}
CreateField(Arg0, 0, 13, WFL5)
if(Arg0==5){
Return(WFL5)
}
// Example from p. 951 of the spec
Store (
LoadTable ("OEM1", "MYOEM", "TABLE1", "\\_SB.PCI0","MYD",
@ -114,4 +144,9 @@ DefinitionBlock ("parser-testsuite-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0
// Data Region
DataTableRegion (REG0, "FOOF", "BAR", "BAZ")
// Other resources
Processor(CPU0, 1, 0x120, 6){}
PowerResource(PWR0, 0, 0){}
ThermalZone(TZ0){}
}

View File

@ -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