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:
parent
2a84c75d8e
commit
d020045887
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user