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

acpi: implement AML multi-pass parser

This commit is contained in:
Achilleas Anagnostopoulos 2018-03-06 08:19:43 +00:00
parent 98fe98bc83
commit fe6c8f7293
2 changed files with 2342 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,905 @@
package aml
import (
"bytes"
"gopheros/device/acpi/table"
"io/ioutil"
"os"
"reflect"
"testing"
"unsafe"
)
func TestParseAMLErrors(t *testing.T) {
t.Run("parseObjectList failed", func(t *testing.T) {
p, resolver := parserForMockPayload(t, []byte{uint8(pOpBuffer)})
if err := p.ParseAML(0, "DSDT", resolver.LookupTable("DSDT")); err != errParsingAML {
t.Fatalf("expected to get errParsingAML; got: %v", err)
}
})
t.Run("connectNamedObjArgs failed", func(t *testing.T) {
p, resolver := parserForMockPayload(t, []byte{})
namedObj := p.objTree.newNamedObject(pOpName, 0, [amlNameLen]byte{'F', 'O', 'O', 'F'})
p.objTree.append(namedObj, p.objTree.newObject(pOpDwordPrefix, 0))
p.objTree.append(p.objTree.ObjectAt(1), namedObj) // Attach to first child of root scope
if err := p.ParseAML(0, "DSDT", resolver.LookupTable("DSDT")); err != errParsingAML {
t.Fatalf("expected to get errParsingAML; got: %v", err)
}
})
t.Run("mergeScopeDirectives failed", func(t *testing.T) {
p, resolver := parserForMockPayload(t, []byte{})
scopeDirective := p.objTree.newObject(pOpScope, 0)
p.objTree.append(p.objTree.ObjectAt(1), scopeDirective) // Attach to first child of root scope
if err := p.ParseAML(0, "DSDT", resolver.LookupTable("DSDT")); err != errParsingAML {
t.Fatalf("expected to get errParsingAML; got: %v", err)
}
})
t.Run("relocateNamedObjects failed", func(t *testing.T) {
p, resolver := parserForMockPayload(t, []byte{})
namedObj := p.objTree.newNamedObject(pOpName, 0, [amlNameLen]byte{'F', 'O', 'O', 'F'})
namepath := p.objTree.newObject(pOpIntNamePath, 0)
namepath.value = []byte{'^', '^', 'F', 'O', 'O', 'F'}
target := p.objTree.newObject(pOpOnes, 0)
p.objTree.append(namedObj, namepath)
p.objTree.append(namedObj, target)
p.objTree.append(p.objTree.ObjectAt(0), namedObj)
if err := p.ParseAML(0, "DSDT", resolver.LookupTable("DSDT")); err != errParsingAML {
t.Fatalf("expected to get errParsingAML; got: %v", err)
}
})
t.Run("parseDeferredBlocks failed", func(t *testing.T) {
p, resolver := parserForMockPayload(t, []byte{})
// Attach a deferred block to the first child of the root scope
def := p.objTree.newObject(pOpBankField, 0)
def.pkgEnd = 1
p.objTree.append(p.objTree.ObjectAt(1), def)
if err := p.ParseAML(0, "DSDT", resolver.LookupTable("DSDT")); err != errParsingAML {
t.Fatalf("expected to get errParsingAML; got: %v", err)
}
})
t.Run("resolveMethodCalls failed", func(t *testing.T) {
p, resolver := parserForMockPayload(t, []byte{})
method := p.objTree.newNamedObject(pOpMethod, 0, [amlNameLen]byte{'M', 'T', 'H', 'D'})
namepath := p.objTree.newObject(pOpIntNamePath, 0)
namepath.value = []byte{'M', 'T', 'H', 'D'}
p.objTree.append(method, namepath)
p.objTree.append(p.objTree.ObjectAt(0), method)
inv := p.objTree.newObject(pOpIntNamePathOrMethodCall, 0)
inv.value = []byte{'M', 'T', 'H', 'D'}
p.objTree.append(p.objTree.ObjectAt(0), inv)
if err := p.ParseAML(0, "DSDT", resolver.LookupTable("DSDT")); err != errParsingAML {
t.Fatalf("expected to get errParsingAML; got: %v", err)
}
})
t.Run("connectNonNamedObjArgs failed", func(t *testing.T) {
p, resolver := parserForMockPayload(t, []byte{})
// Use a named object whose args contain a TermArg
obj := p.objTree.newObject(pOpMatch, 0)
p.objTree.append(p.objTree.ObjectAt(0), obj)
if err := p.ParseAML(0, "DSDT", resolver.LookupTable("DSDT")); err != errParsingAML {
t.Fatalf("expected to get errParsingAML; got: %v", err)
}
})
}
func TestParseObjectListErrors(t *testing.T) {
p, _ := parserForMockPayload(t, []byte{uint8(pOpBuffer)})
p.scopeEnter(0)
if res := p.parseObjectList(); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
}
func TestParseArgErrors(t *testing.T) {
info := new(pOpcodeInfo)
obj := new(Object)
t.Run("parsePkgLength error", func(t *testing.T) {
// Incomplete pkg length
p, _ := parserForMockPayload(t, []byte{0xff})
if _, res := p.parseArg(info, obj, pArgTypePkgLen); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
t.Run("invalid pkgLen", func(t *testing.T) {
p, _ := parserForMockPayload(t, []byte{0x4})
if _, res := p.parseArg(info, obj, pArgTypePkgLen); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
t.Run("parseTermList error", func(t *testing.T) {
p, _ := parserForMockPayload(t, []byte{extOpPrefix})
p.mode = parseModeAllBlocks
if _, res := p.parseArg(info, obj, pArgTypeTermList); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
}
func TestParseNameOrMethodCallErrors(t *testing.T) {
payload := []byte{
// Namestring
'F', 'O', 'O', 'F',
}
t.Run("path not resolving to entity", func(t *testing.T) {
p, _ := parserForMockPayload(t, payload)
p.scopeEnter(0)
p.mode = parseModeAllBlocks
if res := p.parseNamePathOrMethodCall(); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
t.Run("incomplete method call", func(t *testing.T) {
p, _ := parserForMockPayload(t, payload)
p.mode = parseModeAllBlocks
// Add a method FOOF object as a child of the root scope and set it up
// so that it requires 1 arg.
method := p.objTree.newNamedObject(pOpMethod, 0, [amlNameLen]byte{'F', 'O', 'O', 'F'})
p.objTree.append(p.objTree.ObjectAt(0), method)
p.objTree.append(method, p.objTree.newObject(pOpIntNamePath, 0))
argCountObj := p.objTree.newObject(pOpBytePrefix, 0)
argCountObj.value = uint64(1)
p.objTree.append(method, argCountObj)
// Enter one of the default child scopes of the root scope
// so the lookup for FOOF yields the object we just appended.
p.scopeEnter(1)
p.mode = parseModeAllBlocks
if res := p.parseNamePathOrMethodCall(); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
}
func TestParseStrictTermArgErrors(t *testing.T) {
// Set up the stream to include a non-Type2/arg opcode
p, _ := parserForMockPayload(t, []byte{uint8(pOpMethod)})
p.scopeEnter(0)
p.mode = parseModeAllBlocks
if _, res := p.parseStrictTermArg(new(Object)); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
}
func TestParseSimpleArg(t *testing.T) {
specs := []struct {
payload []byte
argType pArgType
expRes parseResult
expVal interface{}
}{
{
[]byte{0x32},
pArgTypeByteData,
parseResultOk,
uint64(0x32),
},
{
[]byte{0x32, 0x33},
pArgTypeWordData,
parseResultOk,
uint64(0x3332),
},
{
[]byte{0x32, 0x33, 0x34, 0x35},
pArgTypeDwordData,
parseResultOk,
uint64(0x35343332),
},
{
[]byte{0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39},
pArgTypeQwordData,
parseResultOk,
uint64(0x3938373635343332),
},
{
[]byte{'F', 'O', 'O', 0x00},
pArgTypeString,
parseResultOk,
[]byte{'F', 'O', 'O'},
},
{
[]byte{'^', '^', 'F', 'O', 'O', 'F'},
pArgTypeNameString,
parseResultOk,
[]byte{'^', '^', 'F', 'O', 'O', 'F'},
},
// unsupported arg type
{
[]byte{},
pArgTypeFieldList,
parseResultFailed,
nil,
},
}
for specIndex, spec := range specs {
p, _ := parserForMockPayload(t, spec.payload)
obj, res := p.parseSimpleArg(spec.argType)
if res != spec.expRes {
t.Errorf("[spec %d] expected to get parse result %d; got %d", specIndex, spec.expRes, res)
continue
}
if obj != nil && !reflect.DeepEqual(obj.value, spec.expVal) {
t.Errorf("[spec %d] expected to get value \"%v\"; got \"%v\"", specIndex, spec.expVal, obj.value)
}
}
}
func TestParseTargetErrors(t *testing.T) {
// Set up the stream to include an un-expected opcode
p, _ := parserForMockPayload(t, []byte{uint8(pOpMethod)})
p.scopeEnter(0)
if _, res := p.parseTarget(); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
}
func TestParseFieldElementsErrors(t *testing.T) {
genFieldObj := func(tree *ObjectTree) *Object {
field := tree.newObject(pOpField, 0)
flags := tree.newObject(pOpBytePrefix, 0)
flags.value = uint64(0xfe)
tree.append(field, flags)
tree.append(tree.ObjectAt(0), field)
return field
}
specs := []struct {
descr string
payload []byte
}{
{
"reserved field parsePkgLength error",
[]byte{
0x00, // ReservedField
},
},
{
"access field access type parse error",
[]byte{
0x01, // AccessField
},
},
{
"access field access attrib parse error",
[]byte{
0x01, // AccessField
0x01, // AccessType
},
},
{
"ext. access field access type parse error",
[]byte{
0x03, // ExtendedAccessField
},
},
{
"ext. access field access attrib parse error",
[]byte{
0x03, // ExtendedAccessField
0x01, // AccessType
},
},
{
"ext. access field access length parse error",
[]byte{
0x03, // ExtendedAccessField
0x01, // AccessType
0x02, // AccessAttrib
},
},
{
"EOF parsing connection",
[]byte{
0x02, // Connection
},
},
{
"connection buffer data parsePkgLength error",
[]byte{
0x02, // Connection
uint8(pOpBuffer),
},
},
{
"connection buffer length exceeds stream length",
[]byte{
0x02, // Connection
uint8(pOpBuffer),
0x5, // pkgLen
},
},
{
"connection bad opcode type for buffer size",
[]byte{
0x02, // Connection
uint8(pOpBuffer),
0x1, // pkgLen
extOpPrefix,
},
},
{
"connection bad word buffer size",
[]byte{
0x02, // Connection
uint8(pOpBuffer),
0x2, // pkgLen
uint8(pOpWordPrefix),
0x1, // missing next byte
},
},
{
"connection bad dword buffer size",
[]byte{
0x02, // Connection
uint8(pOpBuffer),
0x3, // pkgLen
uint8(pOpDwordPrefix),
0x1, 0x2, 0x3, // missing next byte
},
},
{
"connection parse namestring error",
[]byte{
0x02, // Connection
'^',
},
},
{
"incomplete named field name",
[]byte{
'F', 'O', 'O', // missing last character
},
},
{
"error parsing named field pkgLength",
[]byte{
'F', 'O', 'O', 'F',
0xff, // incomplete pkg length
},
},
}
for _, spec := range specs {
t.Run(spec.descr, func(t *testing.T) {
p, _ := parserForMockPayload(t, spec.payload)
if res := p.parseFieldElements(genFieldObj(p.objTree)); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
}
}
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,
},
}
for specIndex, spec := range specs {
p, _ := parserForMockPayload(t, spec.payload)
got, res := p.parsePkgLength()
if res != parseResultOk {
t.Errorf("[spec %d] expected to get parseResultOk(%d); got %d", specIndex, parseResultOk, res)
continue
}
if got != spec.exp {
t.Errorf("[spec %d] expected parsePkgLength to return %d; got %d", specIndex, spec.exp, got)
}
}
}
func TestParsePkgLengthErrors(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 {
p, _ := parserForMockPayload(t, spec)
if _, res := p.parsePkgLength(); res != parseResultFailed {
t.Errorf("[spec %d] expected to get parseResultFailed(%d); got %d", specIndex, parseResultFailed, res)
}
}
}
func TestParseStringErrors(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 {
p, _ := parserForMockPayload(t, spec)
if _, res := p.parseString(); res != parseResultFailed {
t.Errorf("[spec %d] expected to get parseResultFailed(%d); got %d", specIndex, parseResultFailed, res)
}
}
}
func TestParseNamestring(t *testing.T) {
specs := []struct {
payload []byte
expRes parseResult
expVal []byte
}{
{
[]byte{0x00},
parseResultOk,
[]byte{},
},
{
// NameSeg with root prefix
[]byte{'\\', '_', 'F', 'O', 'O'},
parseResultOk,
[]byte{'\\', '_', 'F', 'O', 'O'},
},
{
// DualNamePath
[]byte{0x2e, 'F', 'O', 'O', 'F', 'B', 'A', 'R', 'B'},
parseResultOk,
[]byte{0x2e, 'F', 'O', 'O', 'F', 'B', 'A', 'R', 'B'},
},
{
// MultiNamePath with caret prefix
[]byte{'^', '^',
0x2f,
0x3, // 3 segments
'F', 'O', 'O', 'F',
'F', 'O', 'O', 'F',
'F', 'O', 'O', 'F',
},
parseResultOk,
[]byte{'^', '^',
0x2f,
0x3, // 3 segments
'F', 'O', 'O', 'F',
'F', 'O', 'O', 'F',
'F', 'O', 'O', 'F',
},
},
{
// Unexpected EOF after prefix
[]byte{'^'},
parseResultFailed,
nil,
},
{
// Unexpected EOF in DualNamePath
[]byte{0x2e, 'F', 'O', 'O', 'F', 'B', 'A', 'R'}, // missing last char
parseResultFailed,
nil,
},
{
// Unexpected EOF reading SegCount in MultiNamePath
[]byte{'^', '^',
0x2f,
// missing segment count
},
parseResultFailed,
nil,
},
{
// Unexpected EOF in MultiNamePath
[]byte{'^', '^',
0x2f,
0x3, // 3 segments
'F', 'O', 'O', 'F',
'F', 'O', 'O', 'F',
// missing third segment
},
parseResultFailed,
nil,
},
{
// Unexpected EOF in NameSeg
[]byte{'F', 'O', 'O'}, // missing last char
parseResultFailed,
nil,
},
{
// Invalid lead char for NameSeg
[]byte{'0', 'F', 'O', 'O'},
parseResultFailed,
nil,
},
}
for specIndex, spec := range specs {
p, _ := parserForMockPayload(t, spec.payload)
obj, res := p.parseNameString()
if res != spec.expRes {
t.Errorf("[spec %d] expected to get parse result %d; got %d", specIndex, spec.expRes, res)
continue
}
if obj != nil && !reflect.DeepEqual(obj, spec.expVal) {
t.Errorf("[spec %d] expected to get value \"%v\"; got \"%v\"", specIndex, spec.expVal, obj)
}
}
}
func TestConnectNamedObjectsErrors(t *testing.T) {
t.Run("first arg is not a namepath", func(t *testing.T) {
tree := NewObjectTree()
tree.CreateDefaultScopes(0)
namedObj := tree.newNamedObject(pOpName, 0, [amlNameLen]byte{'F', 'O', 'O', 'F'})
tree.append(namedObj, tree.newObject(pOpDwordPrefix, 0))
tree.append(tree.ObjectAt(1), namedObj) // Attach to first child of root scope
p := NewParser(ioutil.Discard, tree)
if res := p.connectNamedObjArgs(0); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
t.Run("named object arg count mismatch", func(t *testing.T) {
tree := NewObjectTree()
tree.CreateDefaultScopes(0)
// Use a named object whose args contain a TermArg
namedObj := tree.newNamedObject(pOpBankField, 0, [amlNameLen]byte{'F', 'O', 'O', 'F'})
namepathObj := tree.newObject(pOpIntNamePath, 0)
namepathObj.value = []byte{'F', 'O', 'O', 'F'}
tree.append(namedObj, namepathObj)
tree.append(tree.ObjectAt(0), namedObj)
p := NewParser(ioutil.Discard, tree)
if res := p.connectNamedObjArgs(0); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
}
func TestMergeScopeDirectivesErrors(t *testing.T) {
t.Run("malformed scope object", func(t *testing.T) {
tree := NewObjectTree()
tree.CreateDefaultScopes(0)
scopeDirective := tree.newObject(pOpScope, 0)
tree.append(tree.ObjectAt(1), scopeDirective) // Attach to first child of root scope
p := NewParser(ioutil.Discard, tree)
if res := p.mergeScopeDirectives(0); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
t.Run("unable to resolve scope reference", func(t *testing.T) {
tree := NewObjectTree()
tree.CreateDefaultScopes(0)
scopeDirective := tree.newObject(pOpScope, 0)
namepathObj := tree.newObject(pOpIntNamePath, 0)
namepathObj.value = []byte{'F', 'O', 'O', 'F'}
tree.append(scopeDirective, namepathObj)
tree.append(tree.ObjectAt(1), scopeDirective) // Attach to first child of root scope
// Simulate second mergeScopes attempt
p := NewParser(ioutil.Discard, tree)
p.resolvePasses = 2
if res := p.mergeScopeDirectives(0); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
t.Run("scope target resolves to obj without IntScopeBlock child", func(t *testing.T) {
tree := NewObjectTree()
tree.CreateDefaultScopes(0)
scopeDirective := tree.newObject(pOpScope, 0)
namepathObj := tree.newObject(pOpIntNamePath, 0)
namepathObj.value = []byte{'D', 'E', 'V', '0'}
tree.append(scopeDirective, namepathObj)
tree.append(tree.ObjectAt(1), scopeDirective) // Attach to first child of root scope
tree.append(tree.ObjectAt(0), tree.newNamedObject(pOpDevice, 0, [amlNameLen]byte{'D', 'E', 'V', '0'}))
// Simulate second mergeScopes attempt
p := NewParser(ioutil.Discard, tree)
p.resolvePasses = 2
if res := p.mergeScopeDirectives(0); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
}
func TestRelocateNamedObjectsErrors(t *testing.T) {
t.Run("first arg is not a namepath", func(t *testing.T) {
tree := NewObjectTree()
tree.CreateDefaultScopes(0)
namedObj := tree.newNamedObject(pOpName, 0, [amlNameLen]byte{'F', 'O', 'O', 'F'})
tree.append(namedObj, tree.newObject(pOpDwordPrefix, 0))
tree.append(tree.ObjectAt(1), namedObj) // Attach to first child of root scope
p := NewParser(ioutil.Discard, tree)
if res := p.relocateNamedObjects(0); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
t.Run("unresolved relocation target", func(t *testing.T) {
tree := NewObjectTree()
tree.CreateDefaultScopes(0)
scope := tree.newNamedObject(pOpIntScopeBlock, 0, [amlNameLen]byte{'S', 'C', 'O', 'P'})
tree.append(tree.ObjectAt(1), scope)
namedObj := tree.newNamedObject(pOpName, 0, [amlNameLen]byte{'F', 'O', 'O', 'F'})
namepathObj := tree.newObject(pOpIntNamePath, 0)
namepathObj.value = []byte{'^', '^', '^', '^', 'F', 'O', 'O', 'F'}
tree.append(namedObj, namepathObj)
tree.append(scope, namedObj)
p := NewParser(ioutil.Discard, tree)
if res := p.relocateNamedObjects(0); res != parseResultRequireExtraPass {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultRequireExtraPass, res)
}
})
t.Run("unresolved relocation target after maxResolvePasses", func(t *testing.T) {
tree := NewObjectTree()
tree.CreateDefaultScopes(0)
namedObj := tree.newNamedObject(pOpName, 0, [amlNameLen]byte{'F', 'O', 'O', 'F'})
namepathObj := tree.newObject(pOpIntNamePath, 0)
namepathObj.value = []byte{'^', '^', 'F', 'O', 'O', 'F'}
tree.append(namedObj, namepathObj)
// call relocateNamedObjects on detached nameObj and simulate maxResolvePasses relocateNamedObjects calls
p := NewParser(ioutil.Discard, tree)
p.resolvePasses = maxResolvePasses + 1
if res := p.relocateNamedObjects(namedObj.index); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
t.Run("resolve target resolves to obj without IntScopeBlock child", func(t *testing.T) {
tree := NewObjectTree()
tree.CreateDefaultScopes(0)
dev0 := tree.newNamedObject(pOpDevice, 0, [amlNameLen]byte{'D', 'E', 'V', '0'})
namepathObj := tree.newObject(pOpIntNamePath, 0)
namepathObj.value = []byte{'D', 'E', 'V', '0'}
tree.append(dev0, namepathObj)
tree.append(tree.ObjectAt(0), dev0)
dev1 := tree.newNamedObject(pOpDevice, 0, [amlNameLen]byte{'D', 'E', 'V', '1'})
namepathObj = tree.newObject(pOpIntNamePath, 0)
namepathObj.value = []byte{'^', 'D', 'E', 'V', '1'}
tree.append(dev1, namepathObj)
// place another named obj between dev0 and dev1 so that the caret in
// dev1 namepath resolves to dev0
cpu0 := tree.newNamedObject(pOpProcessor, 0, [amlNameLen]byte{'C', 'P', 'U', '0'})
namepathObj = tree.newObject(pOpIntNamePath, 0)
namepathObj.value = []byte{'C', 'P', 'U', '0'}
tree.append(cpu0, namepathObj)
tree.append(dev0, cpu0)
tree.append(cpu0, dev1)
p := NewParser(ioutil.Discard, tree)
if res := p.relocateNamedObjects(0); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
}
func TestParseDeferredBlocksErrors(t *testing.T) {
p, _ := parserForMockPayload(t, []byte{extOpPrefix})
// Attach a deferred block to the first child of the root scope
def := p.objTree.newObject(pOpBankField, 0)
def.pkgEnd = 1
p.objTree.append(p.objTree.ObjectAt(1), def)
if res := p.parseDeferredBlocks(0); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
}
func TestConnectNonNamedObjectsErrors(t *testing.T) {
// Allocate a tree but don't create default scopes to make sure that we lack
// the number of args (siblings OR parent's siblings) required for the pOpAdd opcode
tree := NewObjectTree()
root := tree.newObject(pOpIntScopeBlock, 0)
scope := tree.newObject(pOpIntScopeBlock, 0)
tree.append(root, scope)
// Use a named object whose args contain a TermArg
obj := tree.newObject(pOpAdd, 0)
tree.append(scope, obj)
p := NewParser(os.Stdout, tree)
if res := p.connectNonNamedObjArgs(0); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
}
func TestResolveMethodCallsErrors(t *testing.T) {
t.Run("method missing flag object", func(t *testing.T) {
tree := NewObjectTree()
tree.CreateDefaultScopes(0)
method := tree.newNamedObject(pOpMethod, 0, [amlNameLen]byte{'M', 'T', 'H', 'D'})
namepath := tree.newObject(pOpIntNamePath, 0)
namepath.value = []byte{'M', 'T', 'H', 'D'}
tree.append(method, namepath)
tree.append(tree.ObjectAt(0), method)
inv := tree.newObject(pOpIntNamePathOrMethodCall, 0)
inv.value = []byte{'M', 'T', 'H', 'D'}
tree.append(tree.ObjectAt(0), inv)
p := NewParser(os.Stdout, tree)
if res := p.resolveMethodCalls(0); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
t.Run("method contains malformed flag object", func(t *testing.T) {
tree := NewObjectTree()
tree.CreateDefaultScopes(0)
method := tree.newNamedObject(pOpMethod, 0, [amlNameLen]byte{'M', 'T', 'H', 'D'})
namepath := tree.newObject(pOpIntNamePath, 0)
namepath.value = []byte{'M', 'T', 'H', 'D'}
flags := tree.newObject(pOpStringPrefix, 0)
flags.value = []byte{'F', 'O', 'O'} // malformed flags: this should be a uint64
tree.append(method, namepath)
tree.append(method, flags)
tree.append(tree.ObjectAt(0), method)
inv := tree.newObject(pOpIntNamePathOrMethodCall, 0)
inv.value = []byte{'M', 'T', 'H', 'D'}
tree.append(tree.ObjectAt(0), inv)
p := NewParser(os.Stdout, tree)
if res := p.resolveMethodCalls(0); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
t.Run("method call arg count mismatch", func(t *testing.T) {
// Allocate a tree but don't create default scopes to make sure that we lack
// the number of args (siblings OR parent's siblings) required for the method
// invocation
tree := NewObjectTree()
root := tree.newObject(pOpIntScopeBlock, 0)
scope := tree.newObject(pOpIntScopeBlock, 0)
tree.append(root, scope)
// MTHD calls expect 6 args
method := tree.newNamedObject(pOpMethod, 0, [amlNameLen]byte{'M', 'T', 'H', 'D'})
namepath := tree.newObject(pOpIntNamePath, 0)
namepath.value = []byte{'M', 'T', 'H', 'D'}
flags := tree.newObject(pOpBytePrefix, 0)
flags.value = uint64(0x6)
tree.append(method, namepath)
tree.append(method, flags)
tree.append(scope, method)
inv := tree.newObject(pOpIntNamePathOrMethodCall, 0)
inv.value = []byte{'M', 'T', 'H', 'D'}
tree.append(scope, inv)
p := NewParser(os.Stdout, tree)
if res := p.resolveMethodCalls(0); res != parseResultFailed {
t.Fatalf("expected to get parseResultFailed(%d); got %d", parseResultFailed, res)
}
})
}
func parserForMockPayload(t *testing.T, payload []byte) (*Parser, table.Resolver) {
tree := NewObjectTree()
tree.CreateDefaultScopes(42)
p := NewParser(&testWriter{t: t}, tree)
resolver := mockByteDataResolver(payload)
p.init(0, "DSDT", resolver.LookupTable("DSDT"))
return p, resolver
}
type testWriter struct {
t *testing.T
buf bytes.Buffer
}
func (t *testWriter) Write(data []byte) (int, error) {
for _, b := range data {
if b == '\n' {
t.t.Log(t.buf.String())
t.buf.Reset()
continue
}
_ = t.buf.WriteByte(b)
}
return len(data), nil
}
type mockByteDataResolver []byte
func (m mockByteDataResolver) LookupTable(string) *table.SDTHeader {
headerLen := unsafe.Sizeof(table.SDTHeader{})
stream := make([]byte, int(headerLen)+len(m))
copy(stream[headerLen:], m)
header := (*table.SDTHeader)(unsafe.Pointer(&stream[0]))
header.Signature = [4]byte{'D', 'S', 'D', 'T'}
header.Length = uint32(len(stream))
header.Revision = 2
return header
}