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

acpi: define tree-based structure for storing parsed AML entities

ACPI entity definitions form a tree whose roots are a sequence of
pre-defined namespace objects. The parser stores all AML entities using
a space-optimized structure (Object). These objects are organized into a
tree via the ObjectTree structure.

Instead of storing pointers to other objects (i.e. siblings, children or
parent), objects use uint32 indices to objects managed by the
ObjectTree. This has the nice advantage of reducing the memory
requirements for our tree in half when running on 64-bits (4-bytes per
index vs 8-bytes per pointer) while also allowing us to recycle objects
that are explicitly freed by the parser.
This commit is contained in:
Achilleas Anagnostopoulos 2018-02-27 08:49:32 +00:00
parent 851011f957
commit b00fff0e39
2 changed files with 832 additions and 0 deletions

View File

@ -0,0 +1,398 @@
package aml
const (
// InvalidIndex is a sentinel value used by ObjectTree to indicate that
// a returned object index is not valid.
InvalidIndex uint32 = (1 << 32) - 1
// The size of AML name identifiers in bytes.
amlNameLen = 4
)
// fieldElement groups together information about a field element. This
// information can also be obtained by scanning a field element's siblings but
// it is summarized in this structure for convenience.
type fieldElement struct {
// The offset in the address space defined by parent field.
offset uint32
// The width of this field element in bits.
width uint32
accessLength uint8
_pad [3]uint8
accessType uint8
accessAttrib uint8
lockType uint8
updateType uint8
// The index of an Object to use as a connection target. Used only for
// GenericSerialBus and GeneralPurposeIO operation regions.
connectionIndex uint32
// The index of the Field (pOpField, pOpIndexField or pOpBankField) that this
// field element belongs to. According to the spec, named fields appear
// at the same scope as their parent.
fieldIndex uint32
}
// Object describes an entity encoded in the AML bytestream.
type Object struct {
// The AML ocode that describes this oBject.
opcode uint16
// The Index in the opcodeTable for this opcode
infoIndex uint8
// The table handle which contains this entity.
tableHandle uint8
// Named AML entities provide a fixed-width name which is padded by '_' chars.
name [amlNameLen]byte
// The following indices refer to other Objects in the ObjectTree
// that allocated this Object instance. Uninitialized indices are
// represented via a math.MaxUint32 value.
index uint32
parentIndex uint32
prevSiblingIndex uint32
nextSiblingIndex uint32
firstArgIndex uint32
lastArgIndex uint32
// The byte offset in the AML stream where this opcode is defined.
amlOffset uint32
// A non-zero value for pkgEnd indicates that this opcode requires deferred
// parsing due to its potentially ambiguous contents.
pkgEnd uint32
// A value placeholder for entites that contain values (e.g. int
// or string constants byte slices e.t.c)
value interface{}
}
// ObjectTree is a structure that contains a tree of AML entities where each
// entity is allocated from a contiguous Object pool. Index #0 of the pool
// contains the root scope ('\') of the AML tree.
type ObjectTree struct {
objPool []*Object
freeListHeadIndex uint32
}
// NewObjectTree returns a new ObjectTree instance.
func NewObjectTree() *ObjectTree {
return &ObjectTree{
freeListHeadIndex: InvalidIndex,
}
}
// CreateDefaultScopes populates the Object pool with the default scopes
// specified by the ACPI standard:
//
// +-[\] (Root scope)
// +- [_GPE] (General events in GPE register block)
// +- [_PR_] (ACPI 1.0 processor namespace)
// +- [_SB_] (System bus with all device objects)
// +- [_SI_] (System indicators)
// +- [_TZ_] (ACPI 1.0 thermal zone namespace)
func (tree *ObjectTree) CreateDefaultScopes(tableHandle uint8) {
root := tree.newNamedObject(pOpIntScopeBlock, tableHandle, [amlNameLen]byte{'\\'})
tree.append(root, tree.newNamedObject(pOpIntScopeBlock, tableHandle, [amlNameLen]byte{'_', 'G', 'P', 'E'})) // General events in GPE register block
tree.append(root, tree.newNamedObject(pOpIntScopeBlock, tableHandle, [amlNameLen]byte{'_', 'P', 'R', '_'})) // ACPI 1.0 processor namespace
tree.append(root, tree.newNamedObject(pOpIntScopeBlock, tableHandle, [amlNameLen]byte{'_', 'S', 'B', '_'})) // System bus with all device objects
tree.append(root, tree.newNamedObject(pOpIntScopeBlock, tableHandle, [amlNameLen]byte{'_', 'S', 'I', '_'})) // System indicators
tree.append(root, tree.newNamedObject(pOpIntScopeBlock, tableHandle, [amlNameLen]byte{'_', 'T', 'Z', '_'})) // ACPI 1.0 thermal zone namespace
}
// newObject allocates a new Object from the Object pool, populates its
// contents and returns back a pointer to it.
func (tree *ObjectTree) newObject(opcode uint16, tableHandle uint8) *Object {
var obj *Object
// Check the free list first
if tree.freeListHeadIndex != InvalidIndex {
obj = tree.objPool[tree.freeListHeadIndex]
tree.freeListHeadIndex = obj.nextSiblingIndex
} else {
// Allocate new object and attach it to the pool
obj = new(Object)
obj.index = uint32(len(tree.objPool))
tree.objPool = append(tree.objPool, obj)
}
obj.opcode = opcode
obj.infoIndex = pOpcodeTableIndex(opcode, true)
obj.tableHandle = tableHandle
obj.parentIndex = InvalidIndex
obj.prevSiblingIndex = InvalidIndex
obj.nextSiblingIndex = InvalidIndex
obj.firstArgIndex = InvalidIndex
obj.lastArgIndex = InvalidIndex
obj.value = nil
return obj
}
// newNamedObject allocates a new Object from the Object pool, populates its
// name and returns back a pointer to it.
func (tree *ObjectTree) newNamedObject(opcode uint16, tableHandle uint8, name [amlNameLen]byte) *Object {
obj := tree.newObject(opcode, tableHandle)
obj.name = name
return obj
}
// append appends arg to obj's argument list.
func (tree *ObjectTree) append(obj, arg *Object) {
arg.parentIndex = obj.index
if obj.lastArgIndex == InvalidIndex {
obj.firstArgIndex = arg.index
obj.lastArgIndex = arg.index
return
}
LastArg := tree.ObjectAt(obj.lastArgIndex)
LastArg.nextSiblingIndex = arg.index
arg.prevSiblingIndex = LastArg.index
arg.nextSiblingIndex = InvalidIndex
obj.lastArgIndex = arg.index
}
// appendAfter appends arg to obj's argument list after nextTo.
func (tree *ObjectTree) appendAfter(obj, arg, nextTo *Object) {
// nextTo is the last arg of obj; this is equivalent to a regular append
if nextTo.nextSiblingIndex == InvalidIndex {
tree.append(obj, arg)
return
}
arg.parentIndex = obj.index
arg.prevSiblingIndex = nextTo.index
arg.nextSiblingIndex = nextTo.nextSiblingIndex
tree.ObjectAt(arg.nextSiblingIndex).prevSiblingIndex = arg.index
nextTo.nextSiblingIndex = arg.index
}
// free appends obj to the tree's free object list allowing it to be re-used by
// future calls to NewObject and NewNamedObject. Callers must ensure that any
// held pointers to the object are not used after calling free.
func (tree *ObjectTree) free(obj *Object) {
if obj.parentIndex != InvalidIndex {
tree.detach(tree.ObjectAt(obj.parentIndex), obj)
}
if obj.firstArgIndex != InvalidIndex || obj.lastArgIndex != InvalidIndex {
panic("aml.ObjectTree: attempted to free object that still contains argument references")
}
// Push the object to the top of the free list and change its opcode to
// indicate this is a freed node
obj.opcode = pOpIntFreedObject
obj.nextSiblingIndex = tree.freeListHeadIndex
tree.freeListHeadIndex = obj.index
}
// detach detaches arg from obj's argument list.
func (tree *ObjectTree) detach(obj, arg *Object) {
if obj.firstArgIndex == arg.index {
obj.firstArgIndex = arg.nextSiblingIndex
}
if obj.lastArgIndex == arg.index {
obj.lastArgIndex = arg.prevSiblingIndex
}
if arg.nextSiblingIndex != InvalidIndex {
tree.ObjectAt(arg.nextSiblingIndex).prevSiblingIndex = arg.prevSiblingIndex
}
if arg.prevSiblingIndex != InvalidIndex {
tree.ObjectAt(arg.prevSiblingIndex).nextSiblingIndex = arg.nextSiblingIndex
}
arg.prevSiblingIndex = InvalidIndex
arg.nextSiblingIndex = InvalidIndex
arg.parentIndex = InvalidIndex
}
// ObjectAt returns a pointer to the Object at the specified index or nil if
// no object with this index exists inside the object tree.
func (tree *ObjectTree) ObjectAt(index uint32) *Object {
if index >= uint32(len(tree.objPool)) {
return nil
}
obj := tree.objPool[index]
if obj.opcode == pOpIntFreedObject {
return nil
}
return obj
}
// Find attempts to resolve the given expression into an Object 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 (tree *ObjectTree) Find(scopeIndex uint32, expr []byte) uint32 {
exprLen := len(expr)
if exprLen == 0 || scopeIndex == InvalidIndex {
return InvalidIndex
}
switch {
case expr[0] == '\\': // relative to the root scope
// Name was just `\`; this matches the root namespace
if exprLen == 1 {
return 0
}
return tree.findRelative(0, expr[1:])
case expr[0] == '^': // relative to the parent scope(s)
for startIndex := 0; startIndex < exprLen; startIndex++ {
switch expr[startIndex] {
case '^':
// Mpve up one parent. If we were at the root scope
// then the lookup failed.
if scopeIndex = tree.ObjectAt(scopeIndex).parentIndex; scopeIndex == InvalidIndex {
return InvalidIndex
}
default:
// Found the start of the name. Look it up relative to current scope
return tree.findRelative(scopeIndex, expr[startIndex:])
}
}
// Name was just a sequence of '^'; this matches the last scopeIndex value
return scopeIndex
case exprLen > amlNameLen:
// The expression consists of multiple name segments joined together (e.g. FOOFBAR0)
// In this case we need to apply relative lookup rules for FOOF.BAR0
return tree.findRelative(scopeIndex, expr)
default:
// expr is a simple name. According to the spec, we need to
// search for it in this scope and all its parent scopes till
// we reach the root.
for nextScopeIndex := scopeIndex; nextScopeIndex != InvalidIndex; nextScopeIndex = tree.ObjectAt(nextScopeIndex).parentIndex {
scopeObj := tree.ObjectAt(nextScopeIndex)
checkNextSibling:
for nextIndex := scopeObj.firstArgIndex; nextIndex != InvalidIndex; nextIndex = tree.ObjectAt(nextIndex).nextSiblingIndex {
obj := tree.ObjectAt(nextIndex)
for byteIndex := 0; byteIndex < amlNameLen; byteIndex++ {
if expr[byteIndex] != obj.name[byteIndex] {
continue checkNextSibling
}
}
// Found match
return obj.index
}
}
}
// Not found
return InvalidIndex
}
// findRelative attempts to resolve an object using relative scope lookup rules.
func (tree *ObjectTree) findRelative(scopeIndex uint32, expr []byte) uint32 {
exprLen := len(expr)
nextSegment:
for segIndex := 0; segIndex < exprLen; segIndex += amlNameLen {
// If expr contains a dual or multinamed path then we may encounter special
// prefix chars in the stream (the parser extracts the raw data). In this
// case skip over them.
for ; segIndex < exprLen && expr[segIndex] != '_' && (expr[segIndex] < 'A' || expr[segIndex] > 'Z'); segIndex++ {
}
if segIndex >= exprLen {
return InvalidIndex
}
// Search current scope for an entity matching the next name segment
scopeObj := tree.ObjectAt(scopeIndex)
checkNextSibling:
for nextIndex := scopeObj.firstArgIndex; nextIndex != InvalidIndex; nextIndex = tree.ObjectAt(nextIndex).nextSiblingIndex {
obj := tree.ObjectAt(nextIndex)
for byteIndex := 0; byteIndex < amlNameLen; byteIndex++ {
if expr[segIndex+byteIndex] != obj.name[byteIndex] {
continue checkNextSibling
}
}
// Found match; set match as the next scope index and
// try to match the next segment
scopeIndex = nextIndex
continue nextSegment
}
// Failed to match next segment. Lookup failed
return InvalidIndex
}
// scopeIndex contains the index of the last matched name in the expression
// which is the target we were looking for.
return scopeIndex
}
// ClosestNamedAncestor returns the index of the first named object that is an
// ancestor of obj. If any of obj's parents are unresolved scope directives
// then the call will return InvalidIndex.
func (tree *ObjectTree) ClosestNamedAncestor(obj *Object) uint32 {
if obj == nil {
return InvalidIndex
}
for ancestorIndex := obj.parentIndex; ancestorIndex != InvalidIndex; {
ancestor := tree.ObjectAt(ancestorIndex)
if ancestor.opcode == pOpScope {
break
}
if pOpcodeTable[ancestor.infoIndex].flags&pOpFlagNamed != 0 {
return ancestorIndex
}
ancestorIndex = ancestor.parentIndex
}
return InvalidIndex
}
// NumArgs returns the number of arguments contained in obj.
func (tree *ObjectTree) NumArgs(obj *Object) uint32 {
if obj == nil {
return 0
}
var argCount uint32
for siblingIndex := obj.firstArgIndex; siblingIndex != InvalidIndex; siblingIndex = tree.ObjectAt(siblingIndex).nextSiblingIndex {
argCount++
}
return argCount
}
// ArgAt returns a pointer to obj's arg located at index.
func (tree *ObjectTree) ArgAt(obj *Object, index uint32) *Object {
if obj == nil {
return nil
}
for argIndex, siblingIndex := uint32(0), obj.firstArgIndex; siblingIndex != InvalidIndex; argIndex, siblingIndex = argIndex+1, tree.ObjectAt(siblingIndex).nextSiblingIndex {
if argIndex == index {
return tree.ObjectAt(siblingIndex)
}
}
return nil
}

View File

@ -0,0 +1,434 @@
package aml
import (
"fmt"
"testing"
)
func TestTreeObjectAt(t *testing.T) {
tree := NewObjectTree()
root := tree.newObject(pOpIntScopeBlock, 0)
obj1 := tree.newObject(pOpIntScopeBlock, 1)
tree.append(root, obj1)
if got := tree.ObjectAt(obj1.index); got != obj1 {
t.Fatalf("expected to get back obj1; got %#+v", got)
}
if got := tree.ObjectAt(obj1.index + 1); got != nil {
t.Fatalf("expected to get nil; got %#+v", got)
}
tree.free(obj1)
if got := tree.ObjectAt(obj1.index); got != nil {
t.Fatalf("expected to get back nil after freeing obj1; got %#+v", got)
}
}
func TestTreeFreelist(t *testing.T) {
tree := NewObjectTree()
root := tree.newObject(pOpIntScopeBlock, 0)
obj1 := tree.newObject(pOpIntScopeBlock, 1)
obj2 := tree.newObject(pOpIntScopeBlock, 2)
obj3 := tree.newObject(pOpIntScopeBlock, 3)
tree.append(root, obj1)
tree.append(root, obj2)
tree.append(root, obj3)
// By freeing these objects they will be re-used by the following NewObject
// calls in LIFO order.
obj2Index := obj2.index
obj3Index := obj3.index
tree.free(obj3)
tree.free(obj2)
newObj := tree.newObject(pOpIntScopeBlock, 4)
if newObj.index != obj2Index {
t.Errorf("expected object index to be %d; got %d", obj2Index, newObj.index)
}
newObj = tree.newObject(pOpIntScopeBlock, 4)
if newObj.index != obj3Index {
t.Errorf("expected object index to be %d; got %d", obj3Index, newObj.index)
}
if tree.freeListHeadIndex != InvalidIndex {
t.Errorf("expected free list head index to be InvalidIndex; got %d", tree.freeListHeadIndex)
}
}
func TestTreeFreelistPanic(t *testing.T) {
tree := NewObjectTree()
tree.CreateDefaultScopes(42)
defer func() {
expErr := "aml.ObjectTree: attempted to free object that still contains argument references"
if err := recover(); err != expErr {
t.Fatalf("expected call to Free to panic with: %s; got: %v", expErr, err)
}
}()
// Call should panic as root contains argument references
tree.free(tree.ObjectAt(0))
}
func TestTreeAppend(t *testing.T) {
tree := NewObjectTree()
root := tree.newObject(pOpIntScopeBlock, 0)
obj1 := tree.newObject(pOpIntScopeBlock, 1)
obj2 := tree.newObject(pOpIntScopeBlock, 2)
obj3 := tree.newObject(pOpIntScopeBlock, 3)
if root.firstArgIndex != InvalidIndex || root.lastArgIndex != InvalidIndex {
t.Fatal("expected root First/Last arg indices to be InvalidIndex")
}
tree.append(root, obj1)
if root.firstArgIndex != obj1.index || root.lastArgIndex != obj1.index {
t.Fatal("expected root First/Last arg indices to point to obj1")
}
if obj1.parentIndex != root.index {
t.Fatal("expected obj1 parent index to point to root")
}
if obj1.firstArgIndex != InvalidIndex || obj1.lastArgIndex != InvalidIndex {
t.Fatal("expected obj1 First/Last arg indices to be InvalidIndex")
}
// Attach the remaining pointers and follow the links both ways
tree.append(root, obj2)
tree.append(root, obj3)
visitedObjects := 0
for i := root.firstArgIndex; i != InvalidIndex; i = tree.ObjectAt(i).nextSiblingIndex {
visitedObjects++
}
if want := 3; visitedObjects != want {
t.Fatalf("expected to visit %d objects in left -> right traversal; visited %d", want, visitedObjects)
}
visitedObjects = 0
for i := root.lastArgIndex; i != InvalidIndex; i = tree.ObjectAt(i).prevSiblingIndex {
visitedObjects++
}
if want := 3; visitedObjects != want {
t.Fatalf("expected to visit %d objects in right -> left traversal; visited %d", want, visitedObjects)
}
}
func TestTreeAppendAfter(t *testing.T) {
tree := NewObjectTree()
root := tree.newObject(pOpIntScopeBlock, 0)
obj1 := tree.newObject(pOpIntScopeBlock, 1)
obj2 := tree.newObject(pOpIntScopeBlock, 2)
obj3 := tree.newObject(pOpIntScopeBlock, 3)
if root.firstArgIndex != InvalidIndex || root.lastArgIndex != InvalidIndex {
t.Fatal("expected root First/Last arg indices to be InvalidIndex")
}
tree.append(root, obj1)
tree.appendAfter(root, obj2, obj1)
tree.appendAfter(root, obj3, obj1)
expIndexList := []uint32{obj1.index, obj3.index, obj2.index}
visitedObjects := 0
for i := root.firstArgIndex; i != InvalidIndex; i = tree.ObjectAt(i).nextSiblingIndex {
if i != expIndexList[visitedObjects] {
t.Fatalf("expected arg %d to have index %d; got %d", visitedObjects, expIndexList[visitedObjects], i)
}
visitedObjects++
}
if want := 3; visitedObjects != want {
t.Fatalf("expected to visit %d objects in left -> right traversal; visited %d", want, visitedObjects)
}
}
func TestTreeDetach(t *testing.T) {
tree := NewObjectTree()
root := tree.newObject(pOpIntScopeBlock, 0)
obj1 := tree.newObject(pOpIntScopeBlock, 1)
obj2 := tree.newObject(pOpIntScopeBlock, 2)
obj3 := tree.newObject(pOpIntScopeBlock, 3)
t.Run("detach in reverse order", func(t *testing.T) {
tree.append(root, obj1)
tree.append(root, obj2)
tree.append(root, obj3)
tree.detach(root, obj3)
if root.firstArgIndex != obj1.index || root.lastArgIndex != obj2.index {
t.Fatalf("unexpected first/last indices: want (%d, %d); got (%d, %d)", obj1.index, obj2.index, root.firstArgIndex, root.lastArgIndex)
}
tree.detach(root, obj2)
if root.firstArgIndex != obj1.index || root.lastArgIndex != obj1.index {
t.Fatalf("unexpected first/last indices: want (%d, %d); got (%d, %d)", obj1.index, obj1.index, root.firstArgIndex, root.lastArgIndex)
}
tree.detach(root, obj1)
if root.firstArgIndex != InvalidIndex || root.lastArgIndex != InvalidIndex {
t.Fatalf("unexpected first/last indices: want (%d, %d); got (%d, %d)", InvalidIndex, InvalidIndex, root.firstArgIndex, root.lastArgIndex)
}
})
t.Run("detach in insertion order", func(t *testing.T) {
tree.append(root, obj1)
tree.append(root, obj2)
tree.append(root, obj3)
tree.detach(root, obj1)
if root.firstArgIndex != obj2.index || root.lastArgIndex != obj3.index {
t.Fatalf("unexpected first/last indices: want (%d, %d); got (%d, %d)", obj2.index, obj3.index, root.firstArgIndex, root.lastArgIndex)
}
tree.detach(root, obj2)
if root.firstArgIndex != obj3.index || root.lastArgIndex != obj3.index {
t.Fatalf("unexpected first/last indices: want (%d, %d); got (%d, %d)", obj3.index, obj3.index, root.firstArgIndex, root.lastArgIndex)
}
tree.detach(root, obj3)
if root.firstArgIndex != InvalidIndex || root.lastArgIndex != InvalidIndex {
t.Fatalf("unexpected first/last indices: want (%d, %d); got (%d, %d)", InvalidIndex, InvalidIndex, root.firstArgIndex, root.lastArgIndex)
}
})
t.Run("detach middle node and then edges", func(t *testing.T) {
tree.append(root, obj1)
tree.append(root, obj2)
tree.append(root, obj3)
tree.detach(root, obj2)
if root.firstArgIndex != obj1.index || root.lastArgIndex != obj3.index {
t.Fatalf("unexpected first/last indices: want (%d, %d); got (%d, %d)", obj1.index, obj3.index, root.firstArgIndex, root.lastArgIndex)
}
if obj1.nextSiblingIndex != obj3.index {
t.Fatalf("expected obj1 NextSiblingIndex to be %d; got %d", obj3.index, obj1.nextSiblingIndex)
}
if obj3.prevSiblingIndex != obj1.index {
t.Fatalf("expected obj3 PrevSiblingIndex to be %d; got %d", obj1.index, obj3.prevSiblingIndex)
}
tree.detach(root, obj1)
if root.firstArgIndex != obj3.index || root.lastArgIndex != obj3.index {
t.Fatalf("unexpected first/last indices: want (%d, %d); got (%d, %d)", obj3.index, obj3.index, root.firstArgIndex, root.lastArgIndex)
}
tree.detach(root, obj3)
if root.firstArgIndex != InvalidIndex || root.lastArgIndex != InvalidIndex {
t.Fatalf("unexpected first/last indices: want (%d, %d); got (%d, %d)", InvalidIndex, InvalidIndex, root.firstArgIndex, root.lastArgIndex)
}
})
}
func TestFind(t *testing.T) {
tree, scopeMap := genTestScopes()
specs := []struct {
curScope uint32
expr string
want uint32
}{
// Search rules do not apply for these cases
{
scopeMap["PCI0"],
`\`,
scopeMap[`\`],
},
{
scopeMap["PCI0"],
"IDE0_ADR",
scopeMap["_ADR"],
},
{
scopeMap["IDE0"],
"^^PCI0IDE0_ADR",
scopeMap["_ADR"],
},
{
scopeMap["IDE0"],
`\_SB_PCI0IDE0_ADR`,
scopeMap["_ADR"],
},
// Raw multi-segment path (prefix 0x2f, segCount: 3)
{
scopeMap["_ADR"],
fmt.Sprintf("\\%c%c_SB_PCI0IDE0", 0x2f, 0x03),
scopeMap["IDE0"],
},
{
scopeMap["IDE0"],
`\_SB_PCI0`,
scopeMap["PCI0"],
},
{
scopeMap["IDE0"],
`^`,
scopeMap["PCI0"],
},
// Bad queries
{
scopeMap["_SB_"],
"PCI0USB0_CRS",
InvalidIndex,
},
{
scopeMap["IDE0"],
"^^^^^^^^^^^^^^^^^^^",
InvalidIndex,
},
{
scopeMap["IDE0"],
`^^^^^^^^^^^FOO`,
InvalidIndex,
},
{
scopeMap["IDE0"],
"FOO",
InvalidIndex,
},
{
scopeMap["IDE0"],
"",
InvalidIndex,
},
// Incomplete multi-segment path (prefix 0x2f, segCount: 3)
{
scopeMap["_ADR"],
fmt.Sprintf("\\%c%c?", 0x2f, 0x03),
InvalidIndex,
},
// Search rules apply for these cases
{
scopeMap["IDE0"],
"_CRS",
scopeMap["_CRS"],
},
}
for specIndex, spec := range specs {
if got := tree.Find(spec.curScope, []byte(spec.expr)); got != spec.want {
t.Errorf("[spec %d] expected lookup to return index %d; got %d", specIndex, spec.want, got)
}
}
}
func TestNumArgs(t *testing.T) {
tree := NewObjectTree()
tree.CreateDefaultScopes(42)
if exp, got := uint32(5), tree.NumArgs(tree.ObjectAt(0)); got != exp {
t.Fatalf("expected NumArgs(root) to return %d; got %d", exp, got)
}
if got := tree.NumArgs(nil); got != 0 {
t.Fatalf("expected NumArgs(nil) to return 0; got %d", got)
}
}
func TestArgAt(t *testing.T) {
tree := NewObjectTree()
tree.CreateDefaultScopes(42)
root := tree.ObjectAt(0)
arg0 := tree.ArgAt(root, 0)
expName := [amlNameLen]byte{'_', 'G', 'P', 'E'}
if arg0.name != expName {
t.Errorf("expected ArgAt(root, 0) to return object with name: %s; got: %s", string(expName[:]), string(arg0.name[:]))
}
if got := tree.ArgAt(root, InvalidIndex); got != nil {
t.Errorf("expected ArgAt(root, InvalidIndex) to return nil; got: %v", got)
}
if got := tree.ArgAt(nil, 1); got != nil {
t.Fatalf("expected ArgAt(nil, x) to return nil; got %#+v", got)
}
}
func TestClosestNamedAncestor(t *testing.T) {
tree := NewObjectTree()
root := tree.newObject(pOpIntScopeBlock, 0)
obj1 := tree.newObject(pOpMethod, 1)
obj2 := tree.newObject(pOpIf, 2)
scope := tree.newObject(pOpScope, 3)
obj3 := tree.newObject(pOpIf, 4)
tree.append(root, obj1)
tree.append(obj1, obj2)
tree.append(obj2, scope)
tree.append(scope, obj3)
if got := tree.ClosestNamedAncestor(obj3); got != InvalidIndex {
t.Errorf("expected ClosestNamedAncestor to return InvalidIndex; got %d", got)
}
// No parent exists
if got := tree.ClosestNamedAncestor(root); got != InvalidIndex {
t.Errorf("expected ClosestNamedAncestor to return InvalidIndex; got %d", got)
}
// Make the root a non-named object and call ClosestNamedAncestor on its child
root.opcode = pOpAdd
root.infoIndex = pOpcodeTableIndex(root.opcode, false)
if got := tree.ClosestNamedAncestor(obj1); got != InvalidIndex {
t.Errorf("expected ClosestNamedAncestor to return InvalidIndex; got %d", got)
}
if got := tree.ClosestNamedAncestor(nil); got != InvalidIndex {
t.Fatalf("expected ClosestNamedAncestor(nil) to return InvalidIndex; got %d", got)
}
}
func genTestScopes() (*ObjectTree, map[string]uint32) {
// Setup the example tree from page 252 of the acpi 6.2 spec
// \
// SB
// \
// PCI0
// | _CRS
// \
// IDE0
// | _ADR
tree := NewObjectTree()
root := tree.newNamedObject(pOpIntScopeBlock, 0, [4]byte{'\\'})
pci := tree.newNamedObject(pOpIntScopeBlock, 0, [4]byte{'P', 'C', 'I', '0'})
ide := tree.newNamedObject(pOpIntScopeBlock, 0, [4]byte{'I', 'D', 'E', '0'})
sb := tree.newNamedObject(pOpIntScopeBlock, 0, [4]byte{'_', 'S', 'B', '_'})
crs := tree.newNamedObject(pOpIntScopeBlock, 0, [4]byte{'_', 'C', 'R', 'S'})
adr := tree.newNamedObject(pOpIntScopeBlock, 0, [4]byte{'_', 'A', 'D', 'R'})
// Setup tree
tree.append(root, sb)
tree.append(sb, pci)
tree.append(pci, crs)
tree.append(pci, ide)
tree.append(ide, adr)
return tree, map[string]uint32{
"IDE0": ide.index,
"PCI0": pci.index,
"_SB_": sb.index,
"\\": root.index,
"_ADR": adr.index,
"_CRS": crs.index,
}
}