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

acpi: refactor scope lookup code and move it into the entity pkg

This commit is contained in:
Achilleas Anagnostopoulos 2017-12-29 09:10:16 +00:00
parent 17842763e9
commit 38143ab510
3 changed files with 230 additions and 368 deletions

View File

@ -1,68 +1,14 @@
package aml
package entity
import "strings"
// Visitor is a function invoked by the VM for each AML tree entity that matches
// a particular type. The return value controls whether the children of this
// entity should also be visited.
type Visitor func(depth int, obj Entity) (keepRecursing bool)
// EntityType defines the type of entity that visitors should inspect.
type EntityType uint8
// The list of supported EntityType values. EntityTypeAny works as a wildcard
// allowing the visitor to inspect all entities in the AML tree.
const (
EntityTypeAny EntityType = iota
EntityTypeDevice
EntityTypeProcessor
EntityTypePowerResource
EntityTypeThermalZone
EntityTypeMethod
)
// scopeVisit descends a scope hierarchy and invokes visitorFn for each entity
// that matches entType.
func scopeVisit(depth int, ent Entity, entType EntityType, visitorFn Visitor) bool {
op := ent.getOpcode()
switch {
case (entType == EntityTypeAny) ||
(entType == EntityTypeDevice && op == opDevice) ||
(entType == EntityTypeProcessor && op == opProcessor) ||
(entType == EntityTypePowerResource && op == opPowerRes) ||
(entType == EntityTypeThermalZone && op == opThermalZone) ||
(entType == EntityTypeMethod && op == opMethod):
// If the visitor returned false we should not visit the children
if !visitorFn(depth, ent) {
return false
}
// Visit any args that are also entities
for _, arg := range ent.getArgs() {
if argEnt, isEnt := arg.(Entity); isEnt && !scopeVisit(depth+1, argEnt, entType, visitorFn) {
return false
}
}
}
switch typ := ent.(type) {
case ScopeEntity:
// If the entity defines a scope we need to visit the child entities.
for _, child := range typ.Children() {
_ = scopeVisit(depth+1, child, entType, visitorFn)
}
}
return true
}
// scopeResolvePath examines a path expression and attempts to break it down
// ResolveScopedPath examines a path expression and attempts to break it down
// into a parent and child segment. The parent segment is looked up via the
// regular scope rules specified in page 252 of the ACPI 6.2 spec. If the
// parent scope is found then the function returns back the parent entity and
// the name of the child that should be appended to it. If the expression
// lookup fails then the function returns nil, "".
func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent ScopeEntity, name string) {
func ResolveScopedPath(curScope, rootScope Container, expr string) (parent Container, name string) {
if len(expr) <= 1 {
return nil, ""
}
@ -75,8 +21,8 @@ func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent Scop
return rootScope, expr[1:]
case '^':
lastHatIndex := strings.LastIndexByte(expr, '^')
if target := scopeFind(curScope, rootScope, expr[:lastHatIndex+1]); target != nil {
return target.(ScopeEntity), expr[lastHatIndex+1:]
if target := FindInScope(curScope, rootScope, expr[:lastHatIndex+1]); target != nil {
return target.(Container), expr[lastHatIndex+1:]
}
return nil, ""
@ -86,14 +32,14 @@ func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent Scop
}
// Pattern looks like: \FOO.BAR.BAZ or ^+FOO.BAR.BAZ or FOO.BAR.BAZ
if target := scopeFind(curScope, rootScope, expr[:lastDotIndex]); target != nil {
return target.(ScopeEntity), expr[lastDotIndex+1:]
if target := FindInScope(curScope, rootScope, expr[:lastDotIndex]); target != nil {
return target.(Container), expr[lastDotIndex+1:]
}
return nil, ""
}
// scopeFind attempts to find an object with the given name using the rules
// FindInScope attempts to find an object with the given name using the rules
// specified in page 252 of the ACPI 6.2 spec:
//
// There are two types of namespace paths: an absolute namespace path (that is,
@ -104,7 +50,7 @@ func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent Scop
// or Parent Prefixes, ^, the search rules do not apply. If the search rules
// do not apply to a relative namespace path, the namespace object is looked up
// relative to the current namespace
func scopeFind(curScope, rootScope ScopeEntity, name string) Entity {
func FindInScope(curScope, rootScope Container, name string) Entity {
nameLen := len(name)
if nameLen == 0 {
return nil
@ -113,7 +59,7 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity {
switch {
case name[0] == '\\': // relative to the root scope
if nameLen > 1 {
return scopeFindRelative(rootScope, name[1:])
return findRelativeToScope(rootScope, name[1:])
}
// Name was just `\`; this matches the root namespace
@ -130,7 +76,7 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity {
}
default:
// Found the start of the name. Look it up relative to curNs
return scopeFindRelative(curScope, name[startIndex:])
return findRelativeToScope(curScope, name[startIndex:])
}
}
@ -139,7 +85,7 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity {
case strings.ContainsRune(name, '.'):
// If the name contains any '.' then we still need to look it
// up relative to the current scope
return scopeFindRelative(curScope, name)
return findRelativeToScope(curScope, name)
default:
// We can apply the search rules described by the spec
for s := curScope; s != nil; s = s.Parent() {
@ -155,11 +101,11 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity {
return nil
}
// scopeFindRelative returns the Entity referenced by path relative
// findRelativeToScope returns the Entity referenced by path relative
// to the provided Namespace. If the name contains dots, each segment
// is used to access a nested namespace. If the path does not point
// to a NamedObject then lookupRelativeTo returns back nil.
func scopeFindRelative(ns ScopeEntity, path string) Entity {
func findRelativeToScope(ns Container, path string) Entity {
var matchName string
matchNextPathSegment:
for {
@ -170,12 +116,7 @@ matchNextPathSegment:
// Search for a scoped child named "matchName"
for _, child := range ns.Children() {
childNs, ok := child.(ScopeEntity)
if !ok {
continue
}
if childNs.Name() == matchName {
if childNs, ok := child.(Container); ok && childNs.Name() == matchName {
ns = childNs
continue matchNextPathSegment
}

View File

@ -0,0 +1,215 @@
package entity
import (
"reflect"
"testing"
)
func TestResolveScopedPath(t *testing.T) {
scopeMap := genTestScopes()
specs := []struct {
curScope Container
pathExpr string
wantParent Entity
wantName string
}{
{
scopeMap["IDE0"].(Container),
`\_SB_`,
scopeMap[`\`],
"_SB_",
},
{
scopeMap["IDE0"].(Container),
`^FOO`,
scopeMap[`PCI0`],
"FOO",
},
{
scopeMap["IDE0"].(Container),
`^^FOO`,
scopeMap[`_SB_`],
"FOO",
},
{
scopeMap["IDE0"].(Container),
`_ADR`,
scopeMap[`IDE0`],
"_ADR",
},
// Paths with dots
{
scopeMap["IDE0"].(Container),
`\_SB_.PCI0.IDE0._ADR`,
scopeMap[`IDE0`],
"_ADR",
},
{
scopeMap["PCI0"].(Container),
`IDE0._ADR`,
scopeMap[`IDE0`],
"_ADR",
},
{
scopeMap["PCI0"].(Container),
`_CRS`,
scopeMap[`PCI0`],
"_CRS",
},
// Bad queries
{
scopeMap["PCI0"].(Container),
`FOO.BAR.BAZ`,
nil,
"",
},
{
scopeMap["PCI0"].(Container),
``,
nil,
"",
},
{
scopeMap["PCI0"].(Container),
`\`,
nil,
"",
},
{
scopeMap["PCI0"].(Container),
`^^^^^^^^^BADPATH`,
nil,
"",
},
}
root := scopeMap[`\`].(Container)
for specIndex, spec := range specs {
gotParent, gotName := ResolveScopedPath(spec.curScope, root, spec.pathExpr)
if !reflect.DeepEqual(gotParent, spec.wantParent) {
t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.wantParent, gotParent)
continue
}
if gotName != spec.wantName {
t.Errorf("[spec %d] expected lookup to return node name %q; got %q", specIndex, spec.wantName, gotName)
}
}
}
func TestFindInScope(t *testing.T) {
scopeMap := genTestScopes()
specs := []struct {
curScope Container
lookup string
want Entity
}{
// Search rules do not apply for these cases
{
scopeMap["PCI0"].(Container),
`\`,
scopeMap[`\`],
},
{
scopeMap["PCI0"].(Container),
"IDE0._ADR",
scopeMap["_ADR"],
},
{
scopeMap["IDE0"].(Container),
"^^PCI0.IDE0._ADR",
scopeMap["_ADR"],
},
{
scopeMap["IDE0"].(Container),
`\_SB_.PCI0.IDE0._ADR`,
scopeMap["_ADR"],
},
{
scopeMap["IDE0"].(Container),
`\_SB_.PCI0`,
scopeMap["PCI0"],
},
{
scopeMap["IDE0"].(Container),
`^`,
scopeMap["PCI0"],
},
// Bad queries
{
scopeMap["_SB_"].(Container),
"PCI0.USB._CRS",
nil,
},
{
scopeMap["IDE0"].(Container),
"^^^^^^^^^^^^^^^^^^^",
nil,
},
{
scopeMap["IDE0"].(Container),
`^^^^^^^^^^^FOO`,
nil,
},
{
scopeMap["IDE0"].(Container),
"FOO",
nil,
},
{
scopeMap["IDE0"].(Container),
"",
nil,
},
// Search rules apply for these cases
{
scopeMap["IDE0"].(Container),
"_CRS",
scopeMap["_CRS"],
},
}
root := scopeMap[`\`].(Container)
for specIndex, spec := range specs {
if got := FindInScope(spec.curScope, root, spec.lookup); !reflect.DeepEqual(got, spec.want) {
t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.want, got)
}
}
}
func genTestScopes() map[string]Entity {
// Setup the example tree from page 252 of the acpi 6.2 spec
// \
// SB
// \
// PCI0
// | _CRS
// \
// IDE0
// | _ADR
ideScope := NewScope(OpScope, 42, `IDE0`)
pciScope := NewScope(OpScope, 42, `PCI0`)
sbScope := NewScope(OpScope, 42, `_SB_`)
rootScope := NewScope(OpScope, 42, `\`)
adr := NewMethod(42, `_ADR`)
crs := NewMethod(42, `_CRS`)
// Setup tree
ideScope.Append(adr)
pciScope.Append(crs)
pciScope.Append(ideScope)
sbScope.Append(pciScope)
rootScope.Append(sbScope)
return map[string]Entity{
"IDE0": ideScope,
"PCI0": pciScope,
"_SB_": sbScope,
"\\": rootScope,
"_ADR": adr,
"_CRS": crs,
}
}

View File

@ -1,294 +0,0 @@
package aml
import (
"reflect"
"testing"
)
func TestScopeVisit(t *testing.T) {
scopeMap := genTestScopes()
root := scopeMap[`\`].(*scopeEntity)
keepRecursing := func(Entity) bool { return true }
stopRecursing := func(Entity) bool { return false }
// Append special entities under IDE0
ide := scopeMap["IDE0"].(*scopeEntity)
ide.Append(&Device{})
ide.Append(&namedEntity{op: opProcessor})
ide.Append(&namedEntity{op: opProcessor})
ide.Append(&namedEntity{op: opPowerRes})
ide.Append(&namedEntity{op: opPowerRes})
ide.Append(&namedEntity{op: opPowerRes})
ide.Append(&namedEntity{op: opThermalZone})
ide.Append(&namedEntity{op: opThermalZone})
ide.Append(&namedEntity{op: opThermalZone})
ide.Append(&namedEntity{op: opThermalZone})
ide.Append(&Method{})
ide.Append(&Method{})
ide.Append(&Method{})
ide.Append(&Method{})
ide.Append(&Method{})
ide.Append(&methodInvocationEntity{
unnamedEntity: unnamedEntity{
args: []interface{}{
&constEntity{val: uint64(1)},
&constEntity{val: uint64(2)},
},
},
})
specs := []struct {
searchType EntityType
keepRecursingFn func(Entity) bool
wantHits int
}{
{EntityTypeAny, keepRecursing, 24},
{EntityTypeAny, stopRecursing, 1},
{
EntityTypeAny,
func(ent Entity) bool {
// Stop recursing after visiting the methodInvocationEntity
_, isInv := ent.(*methodInvocationEntity)
return !isInv
},
22,
},
{
EntityTypeAny,
func(ent Entity) bool {
// Stop recursing after visiting the first constEntity
_, isConst := ent.(*constEntity)
return !isConst
},
23,
},
{EntityTypeDevice, keepRecursing, 1},
{EntityTypeProcessor, keepRecursing, 2},
{EntityTypePowerResource, keepRecursing, 3},
{EntityTypeThermalZone, keepRecursing, 4},
{EntityTypeMethod, keepRecursing, 5},
}
for specIndex, spec := range specs {
var hits int
scopeVisit(0, root, spec.searchType, func(_ int, obj Entity) bool {
hits++
return spec.keepRecursingFn(obj)
})
if hits != spec.wantHits {
t.Errorf("[spec %d] expected visitor to be called %d times; got %d", specIndex, spec.wantHits, hits)
}
}
}
func TestScopeResolvePath(t *testing.T) {
scopeMap := genTestScopes()
specs := []struct {
curScope ScopeEntity
pathExpr string
wantParent Entity
wantName string
}{
{
scopeMap["IDE0"].(*scopeEntity),
`\_SB_`,
scopeMap[`\`],
"_SB_",
},
{
scopeMap["IDE0"].(*scopeEntity),
`^FOO`,
scopeMap[`PCI0`],
"FOO",
},
{
scopeMap["IDE0"].(*scopeEntity),
`^^FOO`,
scopeMap[`_SB_`],
"FOO",
},
{
scopeMap["IDE0"].(*scopeEntity),
`_ADR`,
scopeMap[`IDE0`],
"_ADR",
},
// Paths with dots
{
scopeMap["IDE0"].(*scopeEntity),
`\_SB_.PCI0.IDE0._ADR`,
scopeMap[`IDE0`],
"_ADR",
},
{
scopeMap["PCI0"].(*scopeEntity),
`IDE0._ADR`,
scopeMap[`IDE0`],
"_ADR",
},
{
scopeMap["PCI0"].(*scopeEntity),
`_CRS`,
scopeMap[`PCI0`],
"_CRS",
},
// Bad queries
{
scopeMap["PCI0"].(*scopeEntity),
`FOO.BAR.BAZ`,
nil,
"",
},
{
scopeMap["PCI0"].(*scopeEntity),
``,
nil,
"",
},
{
scopeMap["PCI0"].(*scopeEntity),
`\`,
nil,
"",
},
{
scopeMap["PCI0"].(*scopeEntity),
`^^^^^^^^^BADPATH`,
nil,
"",
},
}
root := scopeMap[`\`].(*scopeEntity)
for specIndex, spec := range specs {
gotParent, gotName := scopeResolvePath(spec.curScope, root, spec.pathExpr)
if !reflect.DeepEqual(gotParent, spec.wantParent) {
t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.wantParent, gotParent)
continue
}
if gotName != spec.wantName {
t.Errorf("[spec %d] expected lookup to return node name %q; got %q", specIndex, spec.wantName, gotName)
}
}
}
func TestScopeFind(t *testing.T) {
scopeMap := genTestScopes()
specs := []struct {
curScope ScopeEntity
lookup string
want Entity
}{
// Search rules do not apply for these cases
{
scopeMap["PCI0"].(*scopeEntity),
`\`,
scopeMap[`\`],
},
{
scopeMap["PCI0"].(*scopeEntity),
"IDE0._ADR",
scopeMap["_ADR"],
},
{
scopeMap["IDE0"].(*scopeEntity),
"^^PCI0.IDE0._ADR",
scopeMap["_ADR"],
},
{
scopeMap["IDE0"].(*scopeEntity),
`\_SB_.PCI0.IDE0._ADR`,
scopeMap["_ADR"],
},
{
scopeMap["IDE0"].(*scopeEntity),
`\_SB_.PCI0`,
scopeMap["PCI0"],
},
{
scopeMap["IDE0"].(*scopeEntity),
`^`,
scopeMap["PCI0"],
},
// Bad queries
{
scopeMap["_SB_"].(*scopeEntity),
"PCI0.USB._CRS",
nil,
},
{
scopeMap["IDE0"].(*scopeEntity),
"^^^^^^^^^^^^^^^^^^^",
nil,
},
{
scopeMap["IDE0"].(*scopeEntity),
`^^^^^^^^^^^FOO`,
nil,
},
{
scopeMap["IDE0"].(*scopeEntity),
"FOO",
nil,
},
{
scopeMap["IDE0"].(*scopeEntity),
"",
nil,
},
// Search rules apply for these cases
{
scopeMap["IDE0"].(*scopeEntity),
"_CRS",
scopeMap["_CRS"],
},
}
root := scopeMap[`\`].(*scopeEntity)
for specIndex, spec := range specs {
if got := scopeFind(spec.curScope, root, spec.lookup); !reflect.DeepEqual(got, spec.want) {
t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.want, got)
}
}
}
func genTestScopes() map[string]Entity {
// Setup the example tree from page 252 of the acpi 6.2 spec
// \
// SB
// \
// PCI0
// | _CRS
// \
// IDE0
// | _ADR
ideScope := &scopeEntity{name: `IDE0`}
pciScope := &scopeEntity{name: `PCI0`}
sbScope := &scopeEntity{name: `_SB_`}
rootScope := &scopeEntity{name: `\`}
adr := &namedEntity{name: `_ADR`}
crs := &namedEntity{name: `_CRS`}
// Setup tree
ideScope.Append(adr)
pciScope.Append(crs)
pciScope.Append(ideScope)
sbScope.Append(pciScope)
rootScope.Append(sbScope)
return map[string]Entity{
"IDE0": ideScope,
"PCI0": pciScope,
"_SB_": sbScope,
"\\": rootScope,
"_ADR": adr,
"_CRS": crs,
}
}