From 38143ab51047730cf200c0c8da35d3f5c8e15490 Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Fri, 29 Dec 2017 09:10:16 +0000 Subject: [PATCH] acpi: refactor scope lookup code and move it into the entity pkg --- .../device/acpi/aml/{ => entity}/scope.go | 89 +----- .../device/acpi/aml/entity/scope_test.go | 215 +++++++++++++ src/gopheros/device/acpi/aml/scope_test.go | 294 ------------------ 3 files changed, 230 insertions(+), 368 deletions(-) rename src/gopheros/device/acpi/aml/{ => entity}/scope.go (54%) create mode 100644 src/gopheros/device/acpi/aml/entity/scope_test.go delete mode 100644 src/gopheros/device/acpi/aml/scope_test.go diff --git a/src/gopheros/device/acpi/aml/scope.go b/src/gopheros/device/acpi/aml/entity/scope.go similarity index 54% rename from src/gopheros/device/acpi/aml/scope.go rename to src/gopheros/device/acpi/aml/entity/scope.go index 4057fea..8537950 100644 --- a/src/gopheros/device/acpi/aml/scope.go +++ b/src/gopheros/device/acpi/aml/entity/scope.go @@ -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 } diff --git a/src/gopheros/device/acpi/aml/entity/scope_test.go b/src/gopheros/device/acpi/aml/entity/scope_test.go new file mode 100644 index 0000000..5d22117 --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/scope_test.go @@ -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, + } +} diff --git a/src/gopheros/device/acpi/aml/scope_test.go b/src/gopheros/device/acpi/aml/scope_test.go deleted file mode 100644 index fd2dc85..0000000 --- a/src/gopheros/device/acpi/aml/scope_test.go +++ /dev/null @@ -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, - } -}