diff --git a/src/gopheros/device/acpi/aml/entity.go b/src/gopheros/device/acpi/aml/entity.go index 1c34c18..27dc22c 100644 --- a/src/gopheros/device/acpi/aml/entity.go +++ b/src/gopheros/device/acpi/aml/entity.go @@ -9,30 +9,38 @@ type resolver interface { Resolve(io.Writer, ScopeEntity) bool } +// Entity is an interface implemented by all AML entities. type Entity interface { - getOpcode() opcode - setOpcode(opcode) Name() string Parent() ScopeEntity + TableHandle() uint8 + + setTableHandle(uint8) + getOpcode() opcode + setOpcode(opcode) setParent(ScopeEntity) getArgs() []interface{} setArg(uint8, interface{}) bool } +// ScopeEntity is an interface that is implemented by entities that define an +// AML scope. type ScopeEntity interface { Entity Children() []Entity Append(Entity) bool - removeLastChild() Entity + + removeChild(Entity) lastChild() Entity } // unnamedEntity defines an unnamed entity that can be attached to a parent scope. type unnamedEntity struct { - op opcode - args []interface{} - parent ScopeEntity + tableHandle uint8 + op opcode + args []interface{} + parent ScopeEntity } func (ent *unnamedEntity) getOpcode() opcode { return ent.op } @@ -45,14 +53,17 @@ func (ent *unnamedEntity) setArg(_ uint8, arg interface{}) bool { ent.args = append(ent.args, arg) return true } +func (ent *unnamedEntity) TableHandle() uint8 { return ent.tableHandle } +func (ent *unnamedEntity) setTableHandle(h uint8) { ent.tableHandle = h } // namedEntity is a named entity that can be attached to the parent scope. The // setArg() implementation for this type expects arg at index 0 to contain the // entity name. type namedEntity struct { - op opcode - args []interface{} - parent ScopeEntity + tableHandle uint8 + op opcode + args []interface{} + parent ScopeEntity name string } @@ -74,14 +85,17 @@ func (ent *namedEntity) setArg(argIndex uint8, arg interface{}) bool { ent.args = append(ent.args, arg) return true } +func (ent *namedEntity) TableHandle() uint8 { return ent.tableHandle } +func (ent *namedEntity) setTableHandle(h uint8) { ent.tableHandle = h } // constEntity is an unnamedEntity which always evaluates to a constant value. // Calls to setArg for argument index 0 will memoize the argument value that is // stored inside this entity. type constEntity struct { - op opcode - args []interface{} - parent ScopeEntity + tableHandle uint8 + op opcode + args []interface{} + parent ScopeEntity val interface{} } @@ -108,12 +122,15 @@ func (ent *constEntity) setArg(argIndex uint8, arg interface{}) bool { ent.val = arg return argIndex == 0 } +func (ent *constEntity) TableHandle() uint8 { return ent.tableHandle } +func (ent *constEntity) setTableHandle(h uint8) { ent.tableHandle = h } // scopeEntity is an optionally named entity that defines a scope. type scopeEntity struct { - op opcode - args []interface{} - parent ScopeEntity + tableHandle uint8 + op opcode + args []interface{} + parent ScopeEntity name string children []Entity @@ -145,12 +162,16 @@ func (ent *scopeEntity) Append(child Entity) bool { return true } func (ent *scopeEntity) lastChild() Entity { return ent.children[len(ent.children)-1] } -func (ent *scopeEntity) removeLastChild() Entity { - lastIndex := len(ent.children) - 1 - child := ent.children[lastIndex] - ent.children = ent.children[:lastIndex] - return child +func (ent *scopeEntity) removeChild(child Entity) { + for index := 0; index < len(ent.children); index++ { + if ent.children[index] == child { + ent.children = append(ent.children[:index], ent.children[index+1:]...) + return + } + } } +func (ent *scopeEntity) TableHandle() uint8 { return ent.tableHandle } +func (ent *scopeEntity) setTableHandle(h uint8) { ent.tableHandle = h } // bufferEntity defines a buffer object. type bufferEntity struct { @@ -403,9 +424,10 @@ type methodInvocationEntity struct { type Method struct { scopeEntity - argCount uint8 - serialized bool - syncLevel uint8 + tableHandle uint8 + argCount uint8 + serialized bool + syncLevel uint8 } func (m *Method) getOpcode() opcode { return opMethod } @@ -414,11 +436,17 @@ func (m *Method) getOpcode() opcode { return opMethod } type Device struct { scopeEntity + tableHandle uint8 + // The methodMap keeps track of all methods exposed by this device. methodMap map[string]*Method } -func (d *Device) getOpcode() opcode { return opDevice } +func (d *Device) getOpcode() opcode { return opDevice } +func (d *Device) setTableHandle(h uint8) { d.tableHandle = h } + +// TableHandle returns the handle of the ACPI table that defines this device. +func (d *Device) TableHandle() uint8 { return d.tableHandle } // mutexEntity represents a named mutex object type mutexEntity struct { @@ -427,8 +455,9 @@ type mutexEntity struct { // isGlobal is set to true for the pre-defined global mutex (\_GL object) isGlobal bool - name string - syncLevel uint8 + name string + syncLevel uint8 + tableHandle uint8 } func (ent *mutexEntity) getOpcode() opcode { return opMutex } @@ -450,6 +479,8 @@ func (ent *mutexEntity) setArg(argIndex uint8, arg interface{}) bool { ent.syncLevel = uint8(syncLevel) & 0xf return ok } +func (ent *mutexEntity) TableHandle() uint8 { return ent.tableHandle } +func (ent *mutexEntity) setTableHandle(h uint8) { ent.tableHandle = h } // eventEntity represents a named ACPI sync event. type eventEntity struct { diff --git a/src/gopheros/device/acpi/aml/parser.go b/src/gopheros/device/acpi/aml/parser.go index f679ea6..75339c2 100644 --- a/src/gopheros/device/acpi/aml/parser.go +++ b/src/gopheros/device/acpi/aml/parser.go @@ -15,11 +15,12 @@ var ( // Parser implements an AML parser. type Parser struct { - r amlStreamReader - errWriter io.Writer - root ScopeEntity - scopeStack []ScopeEntity - tableName string + r amlStreamReader + errWriter io.Writer + root ScopeEntity + scopeStack []ScopeEntity + tableName string + tableHandle uint8 } // NewParser returns a new AML parser instance. @@ -31,8 +32,10 @@ func NewParser(errWriter io.Writer, rootEntity ScopeEntity) *Parser { } // ParseAML attempts to parse the AML byte-code contained in the supplied ACPI -// table. The parser emits any encountered errors to the specified errWriter. -func (p *Parser) ParseAML(tableName string, header *table.SDTHeader) *kernel.Error { +// table tagging each scoped entity with the supplied table handle. The parser +// emits any encountered errors to the specified errWriter. +func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDTHeader) *kernel.Error { + p.tableHandle = tableHandle p.tableName = tableName p.r.Init( uintptr(unsafe.Pointer(header)), @@ -137,13 +140,16 @@ func (p *Parser) parseObj() bool { // finalizeObj applies post-parse logic for special object types. func (p *Parser) finalizeObj(op opcode, obj Entity) bool { + obj.setTableHandle(p.tableHandle) + switch op { case opElse: // If this is an else block we need to append it as an argument to the // If block // Pop Else block of the current scope - p.scopeCurrent().removeLastChild() - prevObj := p.scopeCurrent().lastChild() + curScope := p.scopeCurrent() + curScope.removeChild(curScope.lastChild()) + prevObj := curScope.lastChild() if prevObj.getOpcode() != opIf { kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] encountered else block without a matching if block\n", p.tableName, p.r.Offset()) return false @@ -203,7 +209,7 @@ func (p *Parser) parseScope(maxReadOffset uint32) bool { } // parseNamespacedObj reads a scope target name from the AML bytestream, -// attaches the a device or method (depending on the opcode) object to the +// attaches the device or method (depending on the opcode) object to the // correct parent scope, enters the device scope and parses the object list // contained in the device definition. func (p *Parser) parseNamespacedObj(op opcode, maxReadOffset uint32) bool { @@ -312,7 +318,10 @@ func (p *Parser) parseArgObj() (Entity, bool) { return nil, false } - return p.scopeCurrent().removeLastChild(), true + curScope := p.scopeCurrent() + obj := curScope.lastChild() + curScope.removeChild(obj) + return obj, true } func (p *Parser) makeObjForOpcode(info *opcodeInfo) Entity { @@ -385,7 +394,8 @@ func (p *Parser) parseMethodInvocationOrNameRef() bool { // It may be a nested invocation or named ref ok = p.parseMethodInvocationOrNameRef() if ok { - arg = p.scopeCurrent().removeLastChild() + arg = p.scopeCurrent().lastChild() + p.scopeCurrent().removeChild(arg) } } @@ -599,8 +609,9 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin p.scopeCurrent().Append(&fieldUnitEntity{ fieldEntity: fieldEntity{ namedEntity: namedEntity{ - op: op, - name: unitName, + tableHandle: p.tableHandle, + op: op, + name: unitName, }, bitOffset: curBitOffset, bitWidth: bitWidth, @@ -618,8 +629,9 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin p.scopeCurrent().Append(&indexFieldEntity{ fieldEntity: fieldEntity{ namedEntity: namedEntity{ - op: op, - name: unitName, + tableHandle: p.tableHandle, + op: op, + name: unitName, }, bitOffset: curBitOffset, bitWidth: bitWidth, @@ -822,7 +834,9 @@ func (p *Parser) parseTarget() (interface{}, bool) { // In this case, this is either a NameString or a control method invocation. if ok := p.parseMethodInvocationOrNameRef(); ok { - return p.scopeCurrent().removeLastChild(), ok + obj := p.scopeCurrent().lastChild() + p.scopeCurrent().removeChild(obj) + return obj, ok } return nil, false diff --git a/src/gopheros/device/acpi/aml/parser_test.go b/src/gopheros/device/acpi/aml/parser_test.go index 4f4d6bf..6c117fe 100644 --- a/src/gopheros/device/acpi/aml/parser_test.go +++ b/src/gopheros/device/acpi/aml/parser_test.go @@ -33,7 +33,7 @@ func TestParser(t *testing.T) { for _, tableName := range spec { tableName := strings.Replace(tableName, ".aml", "", -1) - if err := p.ParseAML(tableName, resolver.LookupTable(tableName)); err != nil { + if err := p.ParseAML(0, tableName, resolver.LookupTable(tableName)); err != nil { t.Errorf("[spec %d] [%s]: %v", specIndex, tableName, err) break } @@ -41,6 +41,56 @@ func TestParser(t *testing.T) { } } +func TestTableHandleAssignment(t *testing.T) { + var resolver = mockResolver{tableFiles: []string{"parser-testsuite-DSDT.aml"}} + + // Create default scopes + rootNS := &scopeEntity{op: opScope, name: `\`} + rootNS.Append(&scopeEntity{op: opScope, name: `_GPE`}) // General events in GPE register block + rootNS.Append(&scopeEntity{op: opScope, name: `_PR_`}) // ACPI 1.0 processor namespace + rootNS.Append(&scopeEntity{op: opScope, name: `_SB_`}) // System bus with all device objects + rootNS.Append(&scopeEntity{op: opScope, name: `_SI_`}) // System indicators + rootNS.Append(&scopeEntity{op: opScope, name: `_TZ_`}) // ACPI 1.0 thermal zone namespace + + p := NewParser(ioutil.Discard, rootNS) + + expHandle := uint8(42) + tableName := "parser-testsuite-DSDT" + if err := p.ParseAML(expHandle, tableName, resolver.LookupTable(tableName)); err != nil { + t.Error(err) + } + + // Drop all entities that were assigned the handle value + var unloadList []Entity + scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool { + if ent.TableHandle() == expHandle { + unloadList = append(unloadList, ent) + return false + } + return true + }) + + for _, ent := range unloadList { + if p := ent.Parent(); p != nil { + p.removeChild(ent) + } + } + + // We should end up with the original tree + var visitedNodes int + scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool { + visitedNodes++ + if ent.TableHandle() == expHandle { + t.Errorf("encounted entity that should have been pruned: %#+v", ent) + } + return true + }) + + if exp := len(rootNS.Children()) + 1; visitedNodes != exp { + t.Errorf("expected to visit %d nodes; visited %d", exp, visitedNodes) + } +} + func pkgDir() string { _, f, _, _ := runtime.Caller(1) return filepath.Dir(f)