mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
acpi: remove broken AML parser implementation
The existing parser implementation has several issues and will, in many cases incorrectly parse AML bytestreams that contain (among other things): - ambiguous method calls (same method name defined in multiple scopes) - bank fields - buffer fields where the length arg contains a method call - named objects containing one or more '^' prefixes when defined inside nested Scope elements (e.g. Scope(_SBRG){ Device(^PCIE){...} }) Unfortunately, these issues were discovered quite late while working on the AML interpreter and while an attempt was made to correct some of these (see previous commits), it turns out that the current codebase cannot be refactored to fix all issues. I have therefore decided to get rid of the current implementation and replace it with a new one which will be created from scratch to address all the above issues. This commit just cleans up the codebase so the new parser can be added via a future PR.
This commit is contained in:
parent
3396b997e7
commit
80bd263fc9
File diff suppressed because it is too large
Load Diff
@ -1,295 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,430 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,237 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,138 +0,0 @@
|
|||||||
package entity
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// 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 ResolveScopedPath(curScope, rootScope Container, expr string) (parent Container, name string) {
|
|
||||||
if len(expr) <= 1 {
|
|
||||||
return nil, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pattern looks like \FOO or ^+BAR or BAZ (relative to curScope)
|
|
||||||
lastDotIndex := strings.LastIndexByte(expr, '.')
|
|
||||||
if lastDotIndex == -1 {
|
|
||||||
switch expr[0] {
|
|
||||||
case '\\':
|
|
||||||
return rootScope, expr[1:]
|
|
||||||
case '^':
|
|
||||||
lastHatIndex := strings.LastIndexByte(expr, '^')
|
|
||||||
if target := FindInScope(curScope, rootScope, expr[:lastHatIndex+1]); target != nil {
|
|
||||||
return target.(Container), expr[lastHatIndex+1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, ""
|
|
||||||
default:
|
|
||||||
return curScope, expr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pattern looks like: \FOO.BAR.BAZ or ^+FOO.BAR.BAZ or FOO.BAR.BAZ
|
|
||||||
if target := FindInScope(curScope, rootScope, expr[:lastDotIndex]); target != nil {
|
|
||||||
return target.(Container), expr[lastDotIndex+1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
// one that starts with a ‘\’ prefix), and a relative namespace path (that is,
|
|
||||||
// one that is relative to the current namespace). The namespace search rules
|
|
||||||
// discussed above, only apply to single NameSeg paths, which is a relative
|
|
||||||
// namespace path. For those relative name paths that contain multiple NameSegs
|
|
||||||
// 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 FindInScope(curScope, rootScope Container, name string) Entity {
|
|
||||||
nameLen := len(name)
|
|
||||||
if nameLen == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case name[0] == '\\': // relative to the root scope
|
|
||||||
if nameLen > 1 {
|
|
||||||
return findRelativeToScope(rootScope, name[1:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name was just `\`; this matches the root namespace
|
|
||||||
return rootScope
|
|
||||||
case name[0] == '^': // relative to the parent scope(s)
|
|
||||||
for startIndex := 0; startIndex < nameLen; startIndex++ {
|
|
||||||
switch name[startIndex] {
|
|
||||||
case '^':
|
|
||||||
curScope = curScope.Parent()
|
|
||||||
|
|
||||||
// No parent to visit
|
|
||||||
if curScope == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// Found the start of the name. Look it up relative to curNs
|
|
||||||
return findRelativeToScope(curScope, name[startIndex:])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name was just a sequence of '^'; this matches the last curScope value
|
|
||||||
return curScope
|
|
||||||
case strings.ContainsRune(name, '.'):
|
|
||||||
// If the name contains any '.' then we still need to look it
|
|
||||||
// up relative to the current scope
|
|
||||||
return findRelativeToScope(curScope, name)
|
|
||||||
default:
|
|
||||||
// We can apply the search rules described by the spec
|
|
||||||
for s := curScope; s != nil; s = s.Parent() {
|
|
||||||
for _, child := range s.Children() {
|
|
||||||
if child.Name() == name {
|
|
||||||
return child
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not found
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 findRelativeToScope(ns Container, path string) Entity {
|
|
||||||
var matchName string
|
|
||||||
matchNextPathSegment:
|
|
||||||
for {
|
|
||||||
dotSepIndex := strings.IndexRune(path, '.')
|
|
||||||
if dotSepIndex != -1 {
|
|
||||||
matchName = path[:dotSepIndex]
|
|
||||||
path = path[dotSepIndex+1:]
|
|
||||||
|
|
||||||
// Search for a scoped child named "matchName"
|
|
||||||
for _, child := range ns.Children() {
|
|
||||||
if childNs, ok := child.(Container); ok && childNs.Name() == matchName {
|
|
||||||
ns = childNs
|
|
||||||
continue matchNextPathSegment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Search for a child named "name"
|
|
||||||
for _, child := range ns.Children() {
|
|
||||||
if child.Name() == path {
|
|
||||||
return child
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next segment in the path was not found or last segment not found
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,215 +0,0 @@
|
|||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,341 +0,0 @@
|
|||||||
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,
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,934 +0,0 @@
|
|||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gopheros/device/acpi/aml/entity"
|
|
||||||
"gopheros/device/acpi/table"
|
|
||||||
"gopheros/kernel"
|
|
||||||
"gopheros/kernel/kfmt"
|
|
||||||
"io"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errParsingAML = &kernel.Error{Module: "acpi_aml_parser", Message: "could not parse AML bytecode"}
|
|
||||||
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 entity.Container
|
|
||||||
scopeStack []entity.Container
|
|
||||||
tableName string
|
|
||||||
tableHandle uint8
|
|
||||||
|
|
||||||
parseOptions parseOpt
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewParser returns a new AML parser instance.
|
|
||||||
func NewParser(errWriter io.Writer, rootEntity entity.Container) *Parser {
|
|
||||||
return &Parser{
|
|
||||||
errWriter: errWriter,
|
|
||||||
root: rootEntity,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseAML attempts to parse the AML byte-code contained in the supplied ACPI
|
|
||||||
// table tagging each scoped entity with the supplied table handle. The parser
|
|
||||||
// emits any encountered errors to the specified errWriter.
|
|
||||||
func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDTHeader) *kernel.Error {
|
|
||||||
p.tableHandle = tableHandle
|
|
||||||
p.tableName = tableName
|
|
||||||
p.r.Init(
|
|
||||||
uintptr(unsafe.Pointer(header)),
|
|
||||||
header.Length,
|
|
||||||
uint32(unsafe.Sizeof(table.SDTHeader{})),
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
lastOp, _ := p.r.LastByte()
|
|
||||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] error parsing AML bytecode (last op 0x%x)\n", p.tableName, p.r.Offset()-1, lastOp)
|
|
||||||
return errParsingAML
|
|
||||||
}
|
|
||||||
p.scopeExit()
|
|
||||||
|
|
||||||
// Pass 2: parse method bodies, check entity parents and resolve all
|
|
||||||
// symbol references
|
|
||||||
var resolveFailed bool
|
|
||||||
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.Args() {
|
|
||||||
if argEnt, isArgEnt := arg.(entity.Entity); isArgEnt && argEnt.Parent() == nil {
|
|
||||||
argEnt.SetParent(ent.Parent())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
})
|
|
||||||
|
|
||||||
if resolveFailed {
|
|
||||||
return errResolvingEntities
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
func (p *Parser) parseObjList(maxOffset uint32) bool {
|
|
||||||
for !p.r.EOF() && p.r.Offset() < maxOffset {
|
|
||||||
if !p.parseObj() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) parseObj() bool {
|
|
||||||
var (
|
|
||||||
curOffset uint32
|
|
||||||
pkgLen uint32
|
|
||||||
info *opcodeInfo
|
|
||||||
ok bool
|
|
||||||
)
|
|
||||||
|
|
||||||
// If we cannot decode the next opcode then this may be a method
|
|
||||||
// invocation or a name reference.
|
|
||||||
curOffset = p.r.Offset()
|
|
||||||
if info, ok = p.nextOpcode(); !ok {
|
|
||||||
p.r.SetOffset(curOffset)
|
|
||||||
return p.parseNamedRef()
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPkgLen := info.flags.is(opFlagHasPkgLen) || info.argFlags.contains(opArgTermList) || info.argFlags.contains(opArgFieldList)
|
|
||||||
|
|
||||||
if hasPkgLen {
|
|
||||||
curOffset = p.r.Offset()
|
|
||||||
if pkgLen, ok = p.parsePkgLength(); !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we encounter a named scope we need to look it up and parse the arg list relative to it
|
|
||||||
switch {
|
|
||||||
case info.op == entity.OpScope:
|
|
||||||
return p.parseScope(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
|
|
||||||
// a device named scope in which case it may define a relative scope name
|
|
||||||
obj := p.makeObjForOpcode(info)
|
|
||||||
p.scopeCurrent().Append(obj)
|
|
||||||
|
|
||||||
if argCount := info.argFlags.argCount(); argCount > 0 {
|
|
||||||
for argIndex := uint8(0); argIndex < argCount; argIndex++ {
|
|
||||||
if !p.parseArg(
|
|
||||||
info,
|
|
||||||
obj,
|
|
||||||
argIndex,
|
|
||||||
info.argFlags.arg(argIndex),
|
|
||||||
curOffset+pkgLen,
|
|
||||||
) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return p.finalizeObj(info.op, obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// finalizeObj applies post-parse logic for special object types.
|
|
||||||
func (p *Parser) finalizeObj(op entity.AMLOpcode, obj entity.Entity) bool {
|
|
||||||
switch op {
|
|
||||||
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.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)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseScope reads a scope name from the AML bytestream, enters it and parses
|
|
||||||
// an objlist relative to it. The referenced scope must be one of:
|
|
||||||
// - one of the pre-defined scopes
|
|
||||||
// - device
|
|
||||||
// - processor
|
|
||||||
// - thermal zone
|
|
||||||
// - power resource
|
|
||||||
func (p *Parser) parseScope(maxReadOffset uint32) bool {
|
|
||||||
name, ok := p.parseNameString()
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
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.Opcode() {
|
|
||||||
case entity.OpDevice, entity.OpProcessor, entity.OpThermalZone, entity.OpPowerRes:
|
|
||||||
// ok
|
|
||||||
default:
|
|
||||||
// Only allow if this is a named scope
|
|
||||||
if target.Name() == "" {
|
|
||||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] %s does not refer to a scoped object\n", p.tableName, p.r.Offset(), name)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.scopeEnter(target.(entity.Container))
|
|
||||||
ok = p.parseObjList(maxReadOffset)
|
|
||||||
p.scopeExit()
|
|
||||||
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseNamespacedObj reads a scope target name from the AML bytestream,
|
|
||||||
// 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 := 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 entity.Container
|
|
||||||
switch info.op {
|
|
||||||
case entity.OpDevice:
|
|
||||||
obj = entity.NewDevice(p.tableHandle, name)
|
|
||||||
case entity.OpProcessor:
|
|
||||||
obj = entity.NewProcessor(p.tableHandle, name)
|
|
||||||
case entity.OpPowerRes:
|
|
||||||
obj = entity.NewPowerResource(p.tableHandle, name)
|
|
||||||
case entity.OpThermalZone:
|
|
||||||
obj = entity.NewThermalZone(p.tableHandle, name)
|
|
||||||
case entity.OpMethod:
|
|
||||||
obj = entity.NewMethod(p.tableHandle, name)
|
|
||||||
default:
|
|
||||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] unsupported namespaced op: %s (current scope: %s)\n", p.tableName, p.r.Offset(), info.op.String(), p.scopeCurrent().Name())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse any args that follow the name. The last arg is always an ArgTermList
|
|
||||||
parent.Append(obj)
|
|
||||||
for argIndex := uint8(1); argIndex < info.argFlags.argCount(); argIndex++ {
|
|
||||||
if !p.parseArg(info, obj, argIndex, info.argFlags.arg(argIndex), maxReadOffset) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok && p.finalizeObj(info.op, obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) parseArg(info *opcodeInfo, obj entity.Entity, argIndex uint8, argType opArgFlag, maxReadOffset uint32) bool {
|
|
||||||
var (
|
|
||||||
arg interface{}
|
|
||||||
ok bool
|
|
||||||
)
|
|
||||||
|
|
||||||
switch argType {
|
|
||||||
case opArgNameString:
|
|
||||||
arg, ok = p.parseNameString()
|
|
||||||
case opArgByteData:
|
|
||||||
arg, ok = p.parseNumConstant(1)
|
|
||||||
case opArgWord:
|
|
||||||
arg, ok = p.parseNumConstant(2)
|
|
||||||
case opArgDword:
|
|
||||||
arg, ok = p.parseNumConstant(4)
|
|
||||||
case opArgQword:
|
|
||||||
arg, ok = p.parseNumConstant(8)
|
|
||||||
case opArgString:
|
|
||||||
arg, ok = p.parseString()
|
|
||||||
case opArgTermObj, opArgDataRefObj:
|
|
||||||
arg, ok = p.parseArgObj()
|
|
||||||
case opArgSimpleName:
|
|
||||||
arg, ok = p.parseSimpleName()
|
|
||||||
case opArgSuperName:
|
|
||||||
arg, ok = p.parseSuperName()
|
|
||||||
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.(entity.Container); isScopeEnt {
|
|
||||||
p.scopeEnter(s)
|
|
||||||
} else {
|
|
||||||
// Create an unnamed scope (e.g if, else, while scope)
|
|
||||||
ns := entity.NewScope(info.op, p.tableHandle, "")
|
|
||||||
p.scopeEnter(ns)
|
|
||||||
obj.SetArg(argIndex, ns)
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = p.parseObjList(maxReadOffset)
|
|
||||||
p.scopeExit()
|
|
||||||
return ok
|
|
||||||
case opArgFieldList:
|
|
||||||
return p.parseFieldList(obj, maxReadOffset)
|
|
||||||
case opArgByteList:
|
|
||||||
var bl []byte
|
|
||||||
for p.r.Offset() < maxReadOffset {
|
|
||||||
b, err := p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
bl = append(bl, b)
|
|
||||||
}
|
|
||||||
arg, ok = bl, true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj.SetArg(argIndex, arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) parseArgObj() (entity.Entity, bool) {
|
|
||||||
if ok := p.parseObj(); !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
curScope := p.scopeCurrent()
|
|
||||||
obj := curScope.Last()
|
|
||||||
curScope.Remove(obj)
|
|
||||||
return obj, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) makeObjForOpcode(info *opcodeInfo) entity.Entity {
|
|
||||||
var obj entity.Entity
|
|
||||||
|
|
||||||
switch {
|
|
||||||
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 = 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 = entity.NewScope(info.op, p.tableHandle, "")
|
|
||||||
case info.flags.is(opFlagNamed):
|
|
||||||
obj = entity.NewGenericNamed(info.op, p.tableHandle)
|
|
||||||
default:
|
|
||||||
obj = entity.NewGeneric(info.op, p.tableHandle)
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
//
|
|
||||||
// Grammar:
|
|
||||||
// MethodInvocation := NameString TermArgList
|
|
||||||
// TermArgList = Nothing | TermArg TermArgList
|
|
||||||
// TermArg = Type2Opcode | DataObject | ArgObj | LocalObj | MethodInvocation
|
|
||||||
func (p *Parser) parseNamedRef() bool {
|
|
||||||
name, ok := p.parseNameString()
|
|
||||||
if !ok {
|
|
||||||
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.Entity
|
|
||||||
argList []interface{}
|
|
||||||
)
|
|
||||||
|
|
||||||
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 && (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().Last()
|
|
||||||
p.scopeCurrent().Remove(arg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No more TermArgs to parse
|
|
||||||
if !ok {
|
|
||||||
p.r.SetOffset(curOffset)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
argList = append(argList, arg)
|
|
||||||
argIndex++
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether all expected arguments have been parsed
|
|
||||||
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(entity.NewInvocation(p.tableHandle, methodDef, argList))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise this is a reference to a named entity
|
|
||||||
return p.scopeCurrent().Append(entity.NewReference(p.tableHandle, name))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) nextOpcode() (*opcodeInfo, bool) {
|
|
||||||
next, err := p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
if next != extOpPrefix {
|
|
||||||
index := opcodeMap[next]
|
|
||||||
if index == badOpcode {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return &opcodeTable[index], true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan next byte to figure out the opcode
|
|
||||||
if next, err = p.r.ReadByte(); err != nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
index := extendedOpcodeMap[next]
|
|
||||||
if index == badOpcode {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return &opcodeTable[index], true
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseFieldList parses a list of FieldElements until the reader reaches
|
|
||||||
// maxReadOffset and appends them to the current scope. Depending on the opcode
|
|
||||||
// this method will emit either fieldUnit objects or indexField objects
|
|
||||||
//
|
|
||||||
// Grammar:
|
|
||||||
// FieldElement := NamedField | ReservedField | AccessField | ExtendedAccessField | ConnectField
|
|
||||||
// NamedField := NameSeg PkgLength
|
|
||||||
// ReservedField := 0x00 PkgLength
|
|
||||||
// AccessField := 0x1 AccessType AccessAttrib
|
|
||||||
// ConnectField := 0x02 NameString | 0x02 BufferData
|
|
||||||
// ExtendedAccessField := 0x3 AccessType ExtendedAccessType AccessLength
|
|
||||||
func (p *Parser) parseFieldList(fieldEnt entity.Entity, maxReadOffset uint32) bool {
|
|
||||||
var (
|
|
||||||
ok bool
|
|
||||||
|
|
||||||
accessType entity.FieldAccessType
|
|
||||||
|
|
||||||
bitWidth uint32
|
|
||||||
curBitOffset uint32
|
|
||||||
connectionName string
|
|
||||||
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 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch next {
|
|
||||||
case 0x00: // ReservedField; generated by the Offset() command
|
|
||||||
bitWidth, ok = p.parsePkgLength()
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
curBitOffset += bitWidth
|
|
||||||
continue
|
|
||||||
case 0x1: // AccessField; set access attributes for following fields
|
|
||||||
next, err := p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
accessType = entity.FieldAccessType(next & 0xf) // access type; bits[0:3]
|
|
||||||
|
|
||||||
attrib, err := p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// To specify AccessAttribBytes, RawBytes and RawProcessBytes
|
|
||||||
// the ASL compiler will emit an ExtendedAccessField opcode.
|
|
||||||
accessByteCount = 0
|
|
||||||
accessAttrib = entity.FieldAccessAttrib(attrib)
|
|
||||||
|
|
||||||
continue
|
|
||||||
case 0x2: // ConnectField => <0x2> NameString> | <0x02> TermObj => Buffer
|
|
||||||
curOffset := p.r.Offset()
|
|
||||||
if connectionName, ok = p.parseNameString(); !ok {
|
|
||||||
// Rewind and try parsing it as an object
|
|
||||||
p.r.SetOffset(curOffset)
|
|
||||||
if resolvedConnection, ok = p.parseArgObj(); !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 0x3: // ExtendedAccessField => <0x03> AccessType ExtendedAccessAttrib AccessLength
|
|
||||||
next, err := p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
accessType = entity.FieldAccessType(next & 0xf) // access type; bits[0:3]
|
|
||||||
|
|
||||||
extAccessAttrib, err := p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
accessByteCount, err = p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch extAccessAttrib {
|
|
||||||
case 0x0b:
|
|
||||||
accessAttrib = entity.FieldAccessAttribBytes
|
|
||||||
case 0xe:
|
|
||||||
accessAttrib = entity.FieldAccessAttribRawBytes
|
|
||||||
case 0x0f:
|
|
||||||
accessAttrib = entity.FieldAccessAttribRawProcessBytes
|
|
||||||
}
|
|
||||||
default: // NamedField
|
|
||||||
_ = p.r.UnreadByte()
|
|
||||||
if unitName, ok = p.parseNameString(); !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
bitWidth, ok = p.parsePkgLength()
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// According to the spec, the field elements are should
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// parsePkgLength parses a PkgLength value from the AML bytestream.
|
|
||||||
func (p *Parser) parsePkgLength() (uint32, bool) {
|
|
||||||
lead, err := p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// The high 2 bits of the lead byte indicate how many bytes follow.
|
|
||||||
var pkgLen uint32
|
|
||||||
switch lead >> 6 {
|
|
||||||
case 0:
|
|
||||||
pkgLen = uint32(lead)
|
|
||||||
case 1:
|
|
||||||
b1, err := p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// lead bits 0-3 are the lsb of the length nybble
|
|
||||||
pkgLen = uint32(b1)<<4 | uint32(lead&0xf)
|
|
||||||
case 2:
|
|
||||||
b1, err := p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
b2, err := p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// lead bits 0-3 are the lsb of the length nybble
|
|
||||||
pkgLen = uint32(b2)<<12 | uint32(b1)<<4 | uint32(lead&0xf)
|
|
||||||
case 3:
|
|
||||||
b1, err := p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
b2, err := p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
b3, err := p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// lead bits 0-3 are the lsb of the length nybble
|
|
||||||
pkgLen = uint32(b3)<<20 | uint32(b2)<<12 | uint32(b1)<<4 | uint32(lead&0xf)
|
|
||||||
}
|
|
||||||
|
|
||||||
return pkgLen, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseNumConstant parses a byte/word/dword or qword value from the AML bytestream.
|
|
||||||
func (p *Parser) parseNumConstant(numBytes uint8) (uint64, bool) {
|
|
||||||
var (
|
|
||||||
next byte
|
|
||||||
err error
|
|
||||||
res uint64
|
|
||||||
)
|
|
||||||
|
|
||||||
for c := uint8(0); c < numBytes; c++ {
|
|
||||||
if next, err = p.r.ReadByte(); err != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
res = res | (uint64(next) << (8 * c))
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseString parses a string from the AML bytestream.
|
|
||||||
func (p *Parser) parseString() (string, bool) {
|
|
||||||
// Read ASCII chars till we reach a null byte
|
|
||||||
var (
|
|
||||||
next byte
|
|
||||||
err error
|
|
||||||
str []byte
|
|
||||||
)
|
|
||||||
|
|
||||||
for {
|
|
||||||
next, err = p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
if next == 0x00 {
|
|
||||||
break
|
|
||||||
} else if next >= 0x01 && next <= 0x7f { // AsciiChar
|
|
||||||
str = append(str, next)
|
|
||||||
} else {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(str), true
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseSuperName attempts to pass a SuperName from the AML bytestream.
|
|
||||||
//
|
|
||||||
// Grammar:
|
|
||||||
// SuperName := SimpleName | DebugObj | Type6Opcode
|
|
||||||
// SimpleName := NameString | ArgObj | LocalObj
|
|
||||||
func (p *Parser) parseSuperName() (interface{}, bool) {
|
|
||||||
// Try parsing as SimpleName
|
|
||||||
curOffset := p.r.Offset()
|
|
||||||
if obj, ok := p.parseSimpleName(); ok {
|
|
||||||
return obj, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rewind and try parsing as object
|
|
||||||
p.r.SetOffset(curOffset)
|
|
||||||
return p.parseArgObj()
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseSimpleName attempts to pass a SimpleName from the AML bytestream.
|
|
||||||
//
|
|
||||||
// Grammar:
|
|
||||||
// SimpleName := NameString | ArgObj | LocalObj
|
|
||||||
func (p *Parser) parseSimpleName() (interface{}, bool) {
|
|
||||||
// Peek next opcode
|
|
||||||
curOffset := p.r.Offset()
|
|
||||||
nextOpcode, ok := p.nextOpcode()
|
|
||||||
|
|
||||||
var obj interface{}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
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)
|
|
||||||
obj, ok = p.parseNameString()
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseTarget attempts to pass a Target from the AML bytestream.
|
|
||||||
//
|
|
||||||
// Grammar:
|
|
||||||
// Target := SuperName | NullName
|
|
||||||
// NullName := 0x00
|
|
||||||
// SuperName := SimpleName | DebugObj | Type6Opcode
|
|
||||||
// Type6Opcode := DefRefOf | DefDerefOf | DefIndex | UserTermObj
|
|
||||||
// SimpleName := NameString | ArgObj | LocalObj
|
|
||||||
//
|
|
||||||
// UserTermObj is a control method invocation.
|
|
||||||
func (p *Parser) parseTarget() (interface{}, bool) {
|
|
||||||
// Peek next opcode
|
|
||||||
curOffset := p.r.Offset()
|
|
||||||
nextOpcode, ok := p.nextOpcode()
|
|
||||||
p.r.SetOffset(curOffset)
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
switch {
|
|
||||||
case nextOpcode.op == entity.OpZero: // this is actually a NullName
|
|
||||||
p.r.SetOffset(curOffset + 1)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can use parseObj for parsing
|
|
||||||
return p.parseArgObj()
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this case, this is either a NameString or a control method invocation.
|
|
||||||
if ok := p.parseNamedRef(); ok {
|
|
||||||
obj := p.scopeCurrent().Last()
|
|
||||||
p.scopeCurrent().Remove(obj)
|
|
||||||
return obj, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseNameString parses a NameString from the AML bytestream.
|
|
||||||
//
|
|
||||||
// Grammar:
|
|
||||||
// NameString := RootChar NamePath | PrefixPath NamePath
|
|
||||||
// PrefixPath := Nothing | '^' PrefixPath
|
|
||||||
// NamePath := NameSeg | DualNamePath | MultiNamePath | NullName
|
|
||||||
func (p *Parser) parseNameString() (string, bool) {
|
|
||||||
var str []byte
|
|
||||||
|
|
||||||
// NameString := RootChar NamePath | PrefixPath NamePath
|
|
||||||
next, err := p.r.PeekByte()
|
|
||||||
if err != nil {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch next {
|
|
||||||
case '\\': // RootChar
|
|
||||||
str = append(str, next)
|
|
||||||
_, _ = p.r.ReadByte()
|
|
||||||
case '^': // PrefixPath := Nothing | '^' PrefixPath
|
|
||||||
str = append(str, next)
|
|
||||||
_, _ = p.r.ReadByte()
|
|
||||||
for {
|
|
||||||
next, err = p.r.PeekByte()
|
|
||||||
if err != nil {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
if next != '^' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
str = append(str, next)
|
|
||||||
_, _ = p.r.ReadByte()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NamePath := NameSeg | DualNamePath | MultiNamePath | NullName
|
|
||||||
next, err = p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
var readCount int
|
|
||||||
switch next {
|
|
||||||
case 0x00: // NullName
|
|
||||||
case 0x2e: // DualNamePath := DualNamePrefix NameSeg NameSeg
|
|
||||||
readCount = 8 // NameSeg x 2
|
|
||||||
case 0x2f: // MultiNamePath := MultiNamePrefix SegCount NameSeg(SegCount)
|
|
||||||
segCount, err := p.r.ReadByte()
|
|
||||||
if segCount == 0 || err != nil {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
readCount = int(segCount) * 4
|
|
||||||
default: // NameSeg := LeadNameChar NameChar NameChar NameChar
|
|
||||||
// LeadNameChar := 'A' - 'Z' | '_'
|
|
||||||
if (next < 'A' || next > 'Z') && next != '_' {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
str = append(str, next) // LeadNameChar
|
|
||||||
readCount = 3 // NameChar x 3
|
|
||||||
}
|
|
||||||
|
|
||||||
for index := 0; readCount > 0; readCount, index = readCount-1, index+1 {
|
|
||||||
next, err := p.r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inject a '.' every 4 chars except for the last segment so
|
|
||||||
// scoped lookups can work properly.
|
|
||||||
if index > 0 && index%4 == 0 && readCount > 1 {
|
|
||||||
str = append(str, '.')
|
|
||||||
}
|
|
||||||
|
|
||||||
str = append(str, next)
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(str), true
|
|
||||||
}
|
|
||||||
|
|
||||||
// scopeCurrent returns the currently active scope.
|
|
||||||
func (p *Parser) scopeCurrent() entity.Container {
|
|
||||||
return p.scopeStack[len(p.scopeStack)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// scopeEnter enters the given scope.
|
|
||||||
func (p *Parser) scopeEnter(s entity.Container) {
|
|
||||||
p.scopeStack = append(p.scopeStack, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// scopeExit exits the current scope.
|
|
||||||
func (p *Parser) scopeExit() {
|
|
||||||
p.scopeStack = p.scopeStack[:len(p.scopeStack)-1]
|
|
||||||
}
|
|
@ -1,738 +0,0 @@
|
|||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gopheros/device/acpi/aml/entity"
|
|
||||||
"gopheros/device/acpi/table"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParser(t *testing.T) {
|
|
||||||
specs := [][]string{
|
|
||||||
[]string{"DSDT.aml", "SSDT.aml"},
|
|
||||||
[]string{"parser-testsuite-DSDT.aml"},
|
|
||||||
[]string{"parser-testsuite-fwd-decls-DSDT.aml"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for specIndex, spec := range specs {
|
|
||||||
var resolver = mockResolver{
|
|
||||||
tableFiles: spec,
|
|
||||||
}
|
|
||||||
|
|
||||||
p := NewParser(os.Stderr, genDefaultScopes())
|
|
||||||
|
|
||||||
for _, tableName := range spec {
|
|
||||||
tableName = strings.Replace(tableName, ".aml", "", -1)
|
|
||||||
if err := p.ParseAML(0, tableName, resolver.LookupTable(tableName)); err != nil {
|
|
||||||
t.Errorf("[spec %d] [%s]: %v", specIndex, tableName, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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"}}
|
|
||||||
|
|
||||||
rootNS := genDefaultScopes()
|
|
||||||
p := NewParser(ioutil.Discard, rootNS)
|
|
||||||
|
|
||||||
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.Entity
|
|
||||||
entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool {
|
|
||||||
if ent.TableHandle() == expHandle {
|
|
||||||
unloadList = append(unloadList, ent)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, ent := range unloadList {
|
|
||||||
if p := ent.Parent(); p != nil {
|
|
||||||
p.Remove(ent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We should end up with the original tree
|
|
||||||
var visitedNodes int
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
if exp := len(rootNS.Children()) + 1; visitedNodes != exp {
|
|
||||||
t.Errorf("expected to visit %d nodes; visited %d", exp, visitedNodes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserForwardDeclParsing(t *testing.T) {
|
|
||||||
var resolver = mockResolver{
|
|
||||||
tableFiles: []string{"parser-testsuite-fwd-decls-DSDT.aml"},
|
|
||||||
}
|
|
||||||
|
|
||||||
p := NewParser(ioutil.Discard, genDefaultScopes())
|
|
||||||
|
|
||||||
for _, tableName := range resolver.tableFiles {
|
|
||||||
tableName = strings.Replace(tableName, ".aml", "", -1)
|
|
||||||
if err := p.ParseAML(0, tableName, resolver.LookupTable(tableName)); err != nil {
|
|
||||||
t.Errorf("[%s]: %v", tableName, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParsePkgLength(t *testing.T) {
|
|
||||||
specs := []struct {
|
|
||||||
payload []byte
|
|
||||||
exp uint32
|
|
||||||
}{
|
|
||||||
// lead byte bits (6:7) indicate 1 extra byte for the len. The
|
|
||||||
// parsed length will use bits 0:3 from the lead byte plus
|
|
||||||
// the full 8 bits of the following byte.
|
|
||||||
{
|
|
||||||
[]byte{1<<6 | 7, 255},
|
|
||||||
4087,
|
|
||||||
},
|
|
||||||
// lead byte bits (6:7) indicate 2 extra bytes for the len. The
|
|
||||||
// parsed length will use bits 0:3 from the lead byte plus
|
|
||||||
// the full 8 bits of the following bytes.
|
|
||||||
{
|
|
||||||
[]byte{2<<6 | 8, 255, 128},
|
|
||||||
528376,
|
|
||||||
},
|
|
||||||
// lead byte bits (6:7) indicate 3 extra bytes for the len. The
|
|
||||||
// parsed length will use bits 0:3 from the lead byte plus
|
|
||||||
// the full 8 bits of the following bytes.
|
|
||||||
{
|
|
||||||
[]byte{3<<6 | 6, 255, 128, 42},
|
|
||||||
44568566,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
p := &Parser{errWriter: ioutil.Discard}
|
|
||||||
|
|
||||||
for specIndex, spec := range specs {
|
|
||||||
mockParserPayload(p, spec.payload)
|
|
||||||
got, ok := p.parsePkgLength()
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("[spec %d] parsePkgLength returned false", specIndex)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if got != spec.exp {
|
|
||||||
t.Errorf("[spec %d] expected parsePkgLength to return %d; got %d", specIndex, spec.exp, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserErrorHandling(t *testing.T) {
|
|
||||||
p := &Parser{
|
|
||||||
errWriter: ioutil.Discard,
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("ParseAML errors", func(t *testing.T) {
|
|
||||||
t.Run("parseObjList error", func(t *testing.T) {
|
|
||||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
|
||||||
|
|
||||||
// Setup resolver to serve an AML stream containing an invalid opcode
|
|
||||||
header := mockParserPayload(p, []byte{0x5b, 0x00})
|
|
||||||
|
|
||||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
|
||||||
t.Fatal("expected ParseAML to return an error")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup resolver to serve an AML stream containing an incomplete extended opcode
|
|
||||||
header = mockParserPayload(p, []byte{0x5b})
|
|
||||||
|
|
||||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
|
||||||
t.Fatal("expected ParseAML to return an error")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("unresolved entities", func(t *testing.T) {
|
|
||||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
|
||||||
|
|
||||||
// Inject a reference entity to the tree
|
|
||||||
p.root.Append(entity.NewReference(42, "UNKNOWN"))
|
|
||||||
|
|
||||||
// Setup resolver to serve an empty AML stream
|
|
||||||
header := mockParserPayload(p, nil)
|
|
||||||
|
|
||||||
if err := p.ParseAML(uint8(42), "DSDT", header); err != errResolvingEntities {
|
|
||||||
t.Fatalf("expected ParseAML to return errResolvingEntities; got %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("parseObj errors", func(t *testing.T) {
|
|
||||||
t.Run("parsePkgLength error", func(t *testing.T) {
|
|
||||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
|
||||||
|
|
||||||
// Setup resolver to serve an AML stream containing an incomplete
|
|
||||||
// buffer specification
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("incomplete object list", func(t *testing.T) {
|
|
||||||
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(entity.OpBuffer), 0x10})
|
|
||||||
|
|
||||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
|
||||||
t.Fatal("expected parsePkgLength to return an error")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("finalizeObj errors", func(t *testing.T) {
|
|
||||||
t.Run("else without matching if", func(t *testing.T) {
|
|
||||||
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(entity.OpElse), 0x0})
|
|
||||||
|
|
||||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
|
||||||
t.Fatal("expected finalizeObj to return an error")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("parseScope errors", func(t *testing.T) {
|
|
||||||
t.Run("parseNameString error", func(t *testing.T) {
|
|
||||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
|
||||||
|
|
||||||
header := mockParserPayload(p, []byte{
|
|
||||||
byte(entity.OpScope),
|
|
||||||
0x10, // pkglen
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
|
||||||
t.Fatal("expected parseScope to return an error")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("unknown scope", func(t *testing.T) {
|
|
||||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
|
||||||
|
|
||||||
header := mockParserPayload(p, []byte{
|
|
||||||
byte(entity.OpScope),
|
|
||||||
0x10, // pkglen
|
|
||||||
'F', 'O', 'O', 'F',
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
|
||||||
t.Fatal("expected parseScope to return an error")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("nameless scope", func(t *testing.T) {
|
|
||||||
p.root = entity.NewScope(entity.OpScope, 42, ``)
|
|
||||||
|
|
||||||
header := mockParserPayload(p, []byte{
|
|
||||||
byte(entity.OpScope),
|
|
||||||
0x02, // pkglen
|
|
||||||
'\\', // scope name: "\" (root scope)
|
|
||||||
0x00, // null string
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
|
||||||
t.Fatal("expected parseScope to return an error")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("parseNamespacedObj errors", func(t *testing.T) {
|
|
||||||
t.Run("parseNameString error", func(t *testing.T) {
|
|
||||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
|
||||||
|
|
||||||
mockParserPayload(p, nil)
|
|
||||||
|
|
||||||
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 = entity.NewScope(entity.OpScope, 42, `\`)
|
|
||||||
|
|
||||||
header := mockParserPayload(p, []byte{'^', 'F', 'A', 'B', 'C'})
|
|
||||||
|
|
||||||
p.scopeEnter(p.root)
|
|
||||||
devInfo := &opcodeTable[0x6a]
|
|
||||||
if p.parseNamespacedObj(devInfo, header.Length) {
|
|
||||||
t.Fatal("expected parseNamespacedObj to return false")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
// 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 = entity.NewScope(entity.OpScope, 42, `\`)
|
|
||||||
|
|
||||||
mockParserPayload(p, nil)
|
|
||||||
|
|
||||||
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("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(entity.OpIf), // Incomplete type2 opcode
|
|
||||||
})
|
|
||||||
|
|
||||||
p.scopeEnter(p.root)
|
|
||||||
if p.parseNamedRef() {
|
|
||||||
t.Fatal("expected parseNamedRef to return false")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("parseFieldList errors", func(t *testing.T) {
|
|
||||||
specs := []struct {
|
|
||||||
op entity.AMLOpcode
|
|
||||||
args []interface{}
|
|
||||||
maxReadOffset uint32
|
|
||||||
payload []byte
|
|
||||||
}{
|
|
||||||
// Invalid arg count for entity.OpField
|
|
||||||
{
|
|
||||||
entity.OpField,
|
|
||||||
nil,
|
|
||||||
0,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
// Wrong arg type for entity.OpField
|
|
||||||
{
|
|
||||||
entity.OpField,
|
|
||||||
[]interface{}{0, uint64(42)},
|
|
||||||
0,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
entity.OpField,
|
|
||||||
[]interface{}{"FLD0", uint32(42)},
|
|
||||||
0,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
// Invalid arg count for entity.OpIndexField
|
|
||||||
{
|
|
||||||
entity.OpIndexField,
|
|
||||||
nil,
|
|
||||||
0,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
// Wrong arg type for entity.OpIndexField
|
|
||||||
{
|
|
||||||
entity.OpIndexField,
|
|
||||||
[]interface{}{0, "FLD1", "FLD2"},
|
|
||||||
0,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
entity.OpIndexField,
|
|
||||||
[]interface{}{"FLD0", 0, "FLD2"},
|
|
||||||
0,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
|
||||||
entity.OpField,
|
|
||||||
[]interface{}{"FLD0", uint64(42)},
|
|
||||||
128,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
// reserved field (0x00) with missing pkgLen
|
|
||||||
{
|
|
||||||
entity.OpField,
|
|
||||||
[]interface{}{"FLD0", uint64(42)},
|
|
||||||
128,
|
|
||||||
[]byte{0x00},
|
|
||||||
},
|
|
||||||
// access field (0x01) with missing accessType
|
|
||||||
{
|
|
||||||
entity.OpField,
|
|
||||||
[]interface{}{"FLD0", uint64(42)},
|
|
||||||
128,
|
|
||||||
[]byte{0x01},
|
|
||||||
},
|
|
||||||
// access field (0x01) with missing attribute byte
|
|
||||||
{
|
|
||||||
entity.OpField,
|
|
||||||
[]interface{}{"FLD0", uint64(42)},
|
|
||||||
128,
|
|
||||||
[]byte{0x01, 0x01},
|
|
||||||
},
|
|
||||||
// connect field (0x02) with incomplete TermObject => Buffer arg
|
|
||||||
{
|
|
||||||
entity.OpField,
|
|
||||||
[]interface{}{"FLD0", uint64(42)},
|
|
||||||
128,
|
|
||||||
[]byte{0x02, byte(entity.OpBuffer)},
|
|
||||||
},
|
|
||||||
// extended access field (0x03) with missing ext. accessType
|
|
||||||
{
|
|
||||||
entity.OpField,
|
|
||||||
[]interface{}{"FLD0", uint64(42)},
|
|
||||||
128,
|
|
||||||
[]byte{0x03},
|
|
||||||
},
|
|
||||||
// extended access field (0x03) with missing ext. attribute byte
|
|
||||||
{
|
|
||||||
entity.OpField,
|
|
||||||
[]interface{}{"FLD0", uint64(42)},
|
|
||||||
128,
|
|
||||||
[]byte{0x03, 0x01},
|
|
||||||
},
|
|
||||||
// extended access field (0x03) with missing access byte count value
|
|
||||||
{
|
|
||||||
entity.OpField,
|
|
||||||
[]interface{}{"FLD0", uint64(42)},
|
|
||||||
128,
|
|
||||||
[]byte{0x03, 0x01, 0x02},
|
|
||||||
},
|
|
||||||
// named field with invalid name
|
|
||||||
{
|
|
||||||
entity.OpField,
|
|
||||||
[]interface{}{"FLD0", uint64(42)},
|
|
||||||
128,
|
|
||||||
[]byte{0xff},
|
|
||||||
},
|
|
||||||
// named field with invalid pkgLen
|
|
||||||
{
|
|
||||||
entity.OpField,
|
|
||||||
[]interface{}{"FLD0", uint64(42)},
|
|
||||||
128,
|
|
||||||
[]byte{'N', 'A', 'M', 'E'},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for specIndex, spec := range specs {
|
|
||||||
mockParserPayload(p, spec.payload)
|
|
||||||
|
|
||||||
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) {
|
|
||||||
specs := [][]byte{
|
|
||||||
// lead byte bits (6:7) indicate 1 extra byte that is missing
|
|
||||||
[]byte{1 << 6},
|
|
||||||
// lead byte bits (6:7) indicate 2 extra bytes with the 1st and then 2nd missing
|
|
||||||
[]byte{2 << 6},
|
|
||||||
[]byte{2 << 6, 0x1},
|
|
||||||
// lead byte bits (6:7) indicate 3 extra bytes with the 1st and then 2nd and then 3rd missing
|
|
||||||
[]byte{3 << 6},
|
|
||||||
[]byte{3 << 6, 0x1},
|
|
||||||
[]byte{3 << 6, 0x1, 0x2},
|
|
||||||
}
|
|
||||||
|
|
||||||
for specIndex, spec := range specs {
|
|
||||||
mockParserPayload(p, spec)
|
|
||||||
|
|
||||||
if _, ok := p.parsePkgLength(); ok {
|
|
||||||
t.Errorf("[spec %d] expected parsePkgLength to return false", specIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("parseString errors", func(t *testing.T) {
|
|
||||||
specs := [][]byte{
|
|
||||||
// Unexpected EOF before terminating null byte
|
|
||||||
[]byte{'A'},
|
|
||||||
// Characters outside the allowed [0x01, 0x7f] range
|
|
||||||
[]byte{'A', 0xba, 0xdf, 0x00},
|
|
||||||
}
|
|
||||||
|
|
||||||
for specIndex, spec := range specs {
|
|
||||||
mockParserPayload(p, spec)
|
|
||||||
|
|
||||||
if _, ok := p.parseString(); ok {
|
|
||||||
t.Errorf("[spec %d] expected parseString to return false", specIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("parseTarget errors", func(t *testing.T) {
|
|
||||||
t.Run("unexpected opcode", func(t *testing.T) {
|
|
||||||
// Unexpected opcode
|
|
||||||
mockParserPayload(p, []byte{byte(entity.OpAnd)})
|
|
||||||
|
|
||||||
if _, ok := p.parseTarget(); ok {
|
|
||||||
t.Error("expected parseTarget to return false")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("corrupted data", func(t *testing.T) {
|
|
||||||
// Invalid opcode and not a method invocation nor a namestring
|
|
||||||
mockParserPayload(p, []byte{0xba, 0xad})
|
|
||||||
|
|
||||||
if _, ok := p.parseTarget(); ok {
|
|
||||||
t.Error("expected parseTarget to return false")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("parseNameString errors", func(t *testing.T) {
|
|
||||||
t.Run("EOF while parsing path prefix", func(t *testing.T) {
|
|
||||||
mockParserPayload(p, []byte{'^'})
|
|
||||||
|
|
||||||
if _, ok := p.parseNameString(); ok {
|
|
||||||
t.Error("expected parseNameString to return false")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("EOF while parsing multiname path", func(t *testing.T) {
|
|
||||||
specs := [][]byte{
|
|
||||||
// multiname path prefix but no data following
|
|
||||||
[]byte{0x2f},
|
|
||||||
[]byte{
|
|
||||||
0x2f, // multiname path prefix
|
|
||||||
0x0, // no segments (segments must be > 0)
|
|
||||||
},
|
|
||||||
[]byte{
|
|
||||||
0x2f, // multiname path prefix
|
|
||||||
0x1, // 1 expected segment but no more data available
|
|
||||||
},
|
|
||||||
[]byte{
|
|
||||||
'\\', // RootChar and no more data
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for specIndex, spec := range specs {
|
|
||||||
mockParserPayload(p, spec)
|
|
||||||
if _, ok := p.parseNameString(); ok {
|
|
||||||
t.Errorf("[spec %d] expected parseNameString to return false", specIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func mockParserPayload(p *Parser, payload []byte) *table.SDTHeader {
|
|
||||||
resolver := fixedPayloadResolver{payload}
|
|
||||||
header := resolver.LookupTable("DSDT")
|
|
||||||
p.r.Init(
|
|
||||||
uintptr(unsafe.Pointer(header)),
|
|
||||||
header.Length,
|
|
||||||
uint32(unsafe.Sizeof(table.SDTHeader{})),
|
|
||||||
)
|
|
||||||
|
|
||||||
return resolver.LookupTable("DSDT")
|
|
||||||
}
|
|
||||||
|
|
||||||
func pkgDir() string {
|
|
||||||
_, f, _, _ := runtime.Caller(1)
|
|
||||||
return filepath.Dir(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
type mockResolver struct {
|
|
||||||
tableFiles []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m mockResolver) LookupTable(name string) *table.SDTHeader {
|
|
||||||
pathToDumps := pkgDir() + "/../../table/tabletest/"
|
|
||||||
for _, f := range m.tableFiles {
|
|
||||||
if !strings.Contains(f, name) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(pathToDumps + f)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (*table.SDTHeader)(unsafe.Pointer(&data[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type fixedPayloadResolver struct {
|
|
||||||
payload []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f fixedPayloadResolver) LookupTable(name string) *table.SDTHeader {
|
|
||||||
hdrLen := int(unsafe.Sizeof(table.SDTHeader{}))
|
|
||||||
buf := make([]byte, len(f.payload)+hdrLen)
|
|
||||||
copy(buf[hdrLen:], f.payload)
|
|
||||||
|
|
||||||
hdr := (*table.SDTHeader)(unsafe.Pointer(&buf[0]))
|
|
||||||
hdr.Length = uint32(len(buf))
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errInvalidUnreadByte = errors.New("amlStreamReader: invalid use of UnreadByte")
|
|
||||||
)
|
|
||||||
|
|
||||||
type amlStreamReader struct {
|
|
||||||
offset uint32
|
|
||||||
data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init sets up the reader so it can read up to dataLen bytes from the virtual
|
|
||||||
// memory address dataAddr. If a non-zero initialOffset is specified, it will
|
|
||||||
// be used as the current offset in the stream.
|
|
||||||
func (r *amlStreamReader) Init(dataAddr uintptr, dataLen, initialOffset uint32) {
|
|
||||||
// Overlay a byte slice on top of the memory block to be accessed.
|
|
||||||
r.data = *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
|
||||||
Len: int(dataLen),
|
|
||||||
Cap: int(dataLen),
|
|
||||||
Data: dataAddr,
|
|
||||||
}))
|
|
||||||
|
|
||||||
r.SetOffset(initialOffset)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EOF returns true if the end of the stream has been reached.
|
|
||||||
func (r *amlStreamReader) EOF() bool {
|
|
||||||
return r.offset == uint32(len(r.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadByte returns the next byte from the stream.
|
|
||||||
func (r *amlStreamReader) ReadByte() (byte, error) {
|
|
||||||
if r.EOF() {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
r.offset++
|
|
||||||
return r.data[r.offset-1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PeekByte returns the next byte from the stream without advancing the read pointer.
|
|
||||||
func (r *amlStreamReader) PeekByte() (byte, error) {
|
|
||||||
if r.EOF() {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.data[r.offset], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LastByte returns the last byte read off the stream
|
|
||||||
func (r *amlStreamReader) LastByte() (byte, error) {
|
|
||||||
if r.offset == 0 {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.data[r.offset-1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnreadByte moves back the read pointer by one byte.
|
|
||||||
func (r *amlStreamReader) UnreadByte() error {
|
|
||||||
if r.offset == 0 {
|
|
||||||
return errInvalidUnreadByte
|
|
||||||
}
|
|
||||||
|
|
||||||
r.offset--
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Offset returns the current offset.
|
|
||||||
func (r *amlStreamReader) Offset() uint32 {
|
|
||||||
return r.offset
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"testing"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAMLStreamReader(t *testing.T) {
|
|
||||||
buf := make([]byte, 16)
|
|
||||||
for i := 0; i < len(buf); i++ {
|
|
||||||
buf[i] = byte(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("without offset", func(t *testing.T) {
|
|
||||||
var r amlStreamReader
|
|
||||||
r.Init(
|
|
||||||
uintptr(unsafe.Pointer(&buf[0])),
|
|
||||||
uint32(len(buf)),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
|
|
||||||
if r.EOF() {
|
|
||||||
t.Fatal("unexpected EOF")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := r.UnreadByte(); err != errInvalidUnreadByte {
|
|
||||||
t.Fatalf("expected errInvalidUnreadByte; got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := r.LastByte(); err != io.EOF {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(buf); i++ {
|
|
||||||
exp := byte(i)
|
|
||||||
|
|
||||||
next, err := r.PeekByte()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if next != exp {
|
|
||||||
t.Fatalf("expected PeekByte to return %d; got %d", exp, next)
|
|
||||||
}
|
|
||||||
|
|
||||||
next, err = r.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if next != exp {
|
|
||||||
t.Fatalf("expected ReadByte to return %d; got %d", exp, next)
|
|
||||||
}
|
|
||||||
|
|
||||||
last, err := r.LastByte()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if last != exp {
|
|
||||||
t.Fatalf("expected LastByte to return %d; got %d", exp, last)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := r.PeekByte(); err != io.EOF {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if _, err := r.ReadByte(); err != io.EOF {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
exp := byte(len(buf) - 1)
|
|
||||||
if last, _ := r.LastByte(); last != exp {
|
|
||||||
t.Fatalf("expected LastByte to return %d; got %d", exp, last)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("with offset", func(t *testing.T) {
|
|
||||||
var r amlStreamReader
|
|
||||||
r.Init(
|
|
||||||
uintptr(unsafe.Pointer(&buf[0])),
|
|
||||||
uint32(len(buf)),
|
|
||||||
8,
|
|
||||||
)
|
|
||||||
|
|
||||||
if r.EOF() {
|
|
||||||
t.Fatal("unexpected EOF")
|
|
||||||
}
|
|
||||||
|
|
||||||
if exp, got := uint32(8), r.Offset(); got != exp {
|
|
||||||
t.Fatalf("expected Offset() to return %d; got %d", exp, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
exp := byte(8)
|
|
||||||
if next, _ := r.ReadByte(); next != exp {
|
|
||||||
t.Fatalf("expected ReadByte to return %d; got %d", exp, next)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
Binary file not shown.
@ -1,152 +0,0 @@
|
|||||||
// DSDT-parser-testsuite
|
|
||||||
//
|
|
||||||
// This file contains various ASL constructs to ensure that the AML parser
|
|
||||||
// properly handles all possible ASL opcodes it may encounter. This test file
|
|
||||||
// is used in addition to the DSDT.aml file obtained by running acpidump inside
|
|
||||||
// virtualbox.
|
|
||||||
DefinitionBlock ("parser-testsuite-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0x00000002)
|
|
||||||
{
|
|
||||||
OperationRegion (DBG0, SystemIO, 0x3000, 0x04)
|
|
||||||
Field (DBG0, ByteAcc, NoLock, Preserve)
|
|
||||||
{
|
|
||||||
DHE1, 8
|
|
||||||
}
|
|
||||||
|
|
||||||
Device (DRV0)
|
|
||||||
{
|
|
||||||
Name (_ADR, Ones)
|
|
||||||
|
|
||||||
// named entity containing qword const
|
|
||||||
Name (H15F, 0xBADC0FEEDEADC0DE)
|
|
||||||
Method (_GTF, 0, NotSerialized) // _GTF: Get Task File
|
|
||||||
{
|
|
||||||
Return (H15F)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// example from p. 268 of ACPI 6.2 spec
|
|
||||||
Scope(\_SB){
|
|
||||||
OperationRegion(TOP1, GenericSerialBus, 0x00, 0x100) // GenericSerialBus device at command offset 0x00
|
|
||||||
|
|
||||||
Name (SDB0, ResourceTemplate() {})
|
|
||||||
Field(TOP1, BufferAcc, NoLock, Preserve){
|
|
||||||
Connection(SDB0), // Use the Resource Descriptor defined above
|
|
||||||
AccessAs(BufferAcc, AttribWord),
|
|
||||||
FLD0, 8,
|
|
||||||
FLD1, 8
|
|
||||||
}
|
|
||||||
|
|
||||||
Field(TOP1, BufferAcc, NoLock, Preserve){
|
|
||||||
Connection(I2cSerialBus(0x5b,,100000,, "\\_SB",,,,RawDataBuffer(){3,9})),
|
|
||||||
AccessAs(BufferAcc, AttribBytes(4)),
|
|
||||||
FLD2, 8,
|
|
||||||
AccessAs(BufferAcc, AttribRawBytes(3)),
|
|
||||||
FLD3, 8,
|
|
||||||
AccessAs(BufferAcc, AttribRawProcessBytes(2)),
|
|
||||||
FLD4, 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other entity types
|
|
||||||
Event(HLO0)
|
|
||||||
Mutex(MUT0,1)
|
|
||||||
Signal(HLO0)
|
|
||||||
|
|
||||||
// Other executable bits
|
|
||||||
Method (EXE0, 1, Serialized)
|
|
||||||
{
|
|
||||||
Local0 = Revision
|
|
||||||
|
|
||||||
// NameString target
|
|
||||||
Local1 = SizeOf(GLB1)
|
|
||||||
|
|
||||||
Local0 = "my-handle"
|
|
||||||
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",
|
|
||||||
Package () {0,"\\_SB.PCI0"}
|
|
||||||
), Local0
|
|
||||||
)
|
|
||||||
|
|
||||||
FromBCD(9, Arg0)
|
|
||||||
ToBCD(Arg0, Local1)
|
|
||||||
|
|
||||||
Breakpoint
|
|
||||||
Debug = "test"
|
|
||||||
Fatal(0xf0, 0xdeadc0de, 1)
|
|
||||||
|
|
||||||
Reset(HLO0)
|
|
||||||
|
|
||||||
// Mutex support
|
|
||||||
Acquire(MUT0, 0xffff) // no timeout
|
|
||||||
Release(MUT0)
|
|
||||||
|
|
||||||
// Signal/Wait
|
|
||||||
Wait(HLO0, 0xffff)
|
|
||||||
|
|
||||||
// Get monotonic timer value
|
|
||||||
Local0 = Timer
|
|
||||||
|
|
||||||
CopyObject(Local0, Local1)
|
|
||||||
Return(ObjectType(Local1))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Misc regions
|
|
||||||
|
|
||||||
// BankField example from p. 899 of the spec
|
|
||||||
// Define a 256-byte operational region in SystemIO space and name it GIO0
|
|
||||||
OperationRegion (GIO0, SystemIO, 0x125, 0x100)
|
|
||||||
Field (GIO0, ByteAcc, NoLock, Preserve) {
|
|
||||||
GLB1, 1,
|
|
||||||
GLB2, 1,
|
|
||||||
Offset (1), // Move to offset for byte 1
|
|
||||||
BNK1, 4
|
|
||||||
}
|
|
||||||
|
|
||||||
BankField (GIO0, BNK1, 0, ByteAcc, NoLock, Preserve) {
|
|
||||||
Offset (0x30),
|
|
||||||
FET0, 1,
|
|
||||||
FET1, 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data Region
|
|
||||||
DataTableRegion (REG0, "FOOF", "BAR", "BAZ")
|
|
||||||
|
|
||||||
// Other resources
|
|
||||||
Processor(CPU0, 1, 0x120, 6){}
|
|
||||||
PowerResource(PWR0, 0, 0){}
|
|
||||||
ThermalZone(TZ0){}
|
|
||||||
}
|
|
Binary file not shown.
@ -1,27 +0,0 @@
|
|||||||
DefinitionBlock ("parser-testsuite-fwd-decls-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0x00000002)
|
|
||||||
{
|
|
||||||
Scope(\_SB){
|
|
||||||
Method (NST1, 2, NotSerialized)
|
|
||||||
{
|
|
||||||
Return ("something")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Method(NST0, 1, NotSerialized)
|
|
||||||
{
|
|
||||||
// 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
|
|
||||||
// of NST0 method above.
|
|
||||||
Method(NST1, 1, NotSerialized)
|
|
||||||
{
|
|
||||||
Return(Arg0+42)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user