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

acpi: tag entities with the handle of the table that defines them

This allows us to implement the Unload opcode which given a handle,
removes all entities that are tagged by it.
This commit is contained in:
Achilleas Anagnostopoulos 2017-09-27 17:24:47 +01:00
parent 2a84c75d8e
commit d020045887
3 changed files with 139 additions and 44 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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)