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:
parent
851011f957
commit
b00fff0e39
398
src/gopheros/device/acpi/aml/obj_tree.go
Normal file
398
src/gopheros/device/acpi/aml/obj_tree.go
Normal 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
|
||||||
|
}
|
434
src/gopheros/device/acpi/aml/obj_tree_test.go
Normal file
434
src/gopheros/device/acpi/aml/obj_tree_test.go
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user