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
|
Resolve(io.Writer, ScopeEntity) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Entity is an interface implemented by all AML entities.
|
||||||
type Entity interface {
|
type Entity interface {
|
||||||
getOpcode() opcode
|
|
||||||
setOpcode(opcode)
|
|
||||||
Name() string
|
Name() string
|
||||||
Parent() ScopeEntity
|
Parent() ScopeEntity
|
||||||
|
TableHandle() uint8
|
||||||
|
|
||||||
|
setTableHandle(uint8)
|
||||||
|
getOpcode() opcode
|
||||||
|
setOpcode(opcode)
|
||||||
setParent(ScopeEntity)
|
setParent(ScopeEntity)
|
||||||
getArgs() []interface{}
|
getArgs() []interface{}
|
||||||
setArg(uint8, interface{}) bool
|
setArg(uint8, interface{}) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ScopeEntity is an interface that is implemented by entities that define an
|
||||||
|
// AML scope.
|
||||||
type ScopeEntity interface {
|
type ScopeEntity interface {
|
||||||
Entity
|
Entity
|
||||||
|
|
||||||
Children() []Entity
|
Children() []Entity
|
||||||
Append(Entity) bool
|
Append(Entity) bool
|
||||||
removeLastChild() Entity
|
|
||||||
|
removeChild(Entity)
|
||||||
lastChild() Entity
|
lastChild() Entity
|
||||||
}
|
}
|
||||||
|
|
||||||
// unnamedEntity defines an unnamed entity that can be attached to a parent scope.
|
// unnamedEntity defines an unnamed entity that can be attached to a parent scope.
|
||||||
type unnamedEntity struct {
|
type unnamedEntity struct {
|
||||||
op opcode
|
tableHandle uint8
|
||||||
args []interface{}
|
op opcode
|
||||||
parent ScopeEntity
|
args []interface{}
|
||||||
|
parent ScopeEntity
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ent *unnamedEntity) getOpcode() opcode { return ent.op }
|
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)
|
ent.args = append(ent.args, arg)
|
||||||
return true
|
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
|
// 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
|
// setArg() implementation for this type expects arg at index 0 to contain the
|
||||||
// entity name.
|
// entity name.
|
||||||
type namedEntity struct {
|
type namedEntity struct {
|
||||||
op opcode
|
tableHandle uint8
|
||||||
args []interface{}
|
op opcode
|
||||||
parent ScopeEntity
|
args []interface{}
|
||||||
|
parent ScopeEntity
|
||||||
|
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
@ -74,14 +85,17 @@ func (ent *namedEntity) setArg(argIndex uint8, arg interface{}) bool {
|
|||||||
ent.args = append(ent.args, arg)
|
ent.args = append(ent.args, arg)
|
||||||
return true
|
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.
|
// 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
|
// Calls to setArg for argument index 0 will memoize the argument value that is
|
||||||
// stored inside this entity.
|
// stored inside this entity.
|
||||||
type constEntity struct {
|
type constEntity struct {
|
||||||
op opcode
|
tableHandle uint8
|
||||||
args []interface{}
|
op opcode
|
||||||
parent ScopeEntity
|
args []interface{}
|
||||||
|
parent ScopeEntity
|
||||||
|
|
||||||
val interface{}
|
val interface{}
|
||||||
}
|
}
|
||||||
@ -108,12 +122,15 @@ func (ent *constEntity) setArg(argIndex uint8, arg interface{}) bool {
|
|||||||
ent.val = arg
|
ent.val = arg
|
||||||
return argIndex == 0
|
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.
|
// scopeEntity is an optionally named entity that defines a scope.
|
||||||
type scopeEntity struct {
|
type scopeEntity struct {
|
||||||
op opcode
|
tableHandle uint8
|
||||||
args []interface{}
|
op opcode
|
||||||
parent ScopeEntity
|
args []interface{}
|
||||||
|
parent ScopeEntity
|
||||||
|
|
||||||
name string
|
name string
|
||||||
children []Entity
|
children []Entity
|
||||||
@ -145,12 +162,16 @@ func (ent *scopeEntity) Append(child Entity) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
func (ent *scopeEntity) lastChild() Entity { return ent.children[len(ent.children)-1] }
|
func (ent *scopeEntity) lastChild() Entity { return ent.children[len(ent.children)-1] }
|
||||||
func (ent *scopeEntity) removeLastChild() Entity {
|
func (ent *scopeEntity) removeChild(child Entity) {
|
||||||
lastIndex := len(ent.children) - 1
|
for index := 0; index < len(ent.children); index++ {
|
||||||
child := ent.children[lastIndex]
|
if ent.children[index] == child {
|
||||||
ent.children = ent.children[:lastIndex]
|
ent.children = append(ent.children[:index], ent.children[index+1:]...)
|
||||||
return child
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
func (ent *scopeEntity) TableHandle() uint8 { return ent.tableHandle }
|
||||||
|
func (ent *scopeEntity) setTableHandle(h uint8) { ent.tableHandle = h }
|
||||||
|
|
||||||
// bufferEntity defines a buffer object.
|
// bufferEntity defines a buffer object.
|
||||||
type bufferEntity struct {
|
type bufferEntity struct {
|
||||||
@ -403,9 +424,10 @@ type methodInvocationEntity struct {
|
|||||||
type Method struct {
|
type Method struct {
|
||||||
scopeEntity
|
scopeEntity
|
||||||
|
|
||||||
argCount uint8
|
tableHandle uint8
|
||||||
serialized bool
|
argCount uint8
|
||||||
syncLevel uint8
|
serialized bool
|
||||||
|
syncLevel uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Method) getOpcode() opcode { return opMethod }
|
func (m *Method) getOpcode() opcode { return opMethod }
|
||||||
@ -414,11 +436,17 @@ func (m *Method) getOpcode() opcode { return opMethod }
|
|||||||
type Device struct {
|
type Device struct {
|
||||||
scopeEntity
|
scopeEntity
|
||||||
|
|
||||||
|
tableHandle uint8
|
||||||
|
|
||||||
// The methodMap keeps track of all methods exposed by this device.
|
// The methodMap keeps track of all methods exposed by this device.
|
||||||
methodMap map[string]*Method
|
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
|
// mutexEntity represents a named mutex object
|
||||||
type mutexEntity struct {
|
type mutexEntity struct {
|
||||||
@ -427,8 +455,9 @@ type mutexEntity struct {
|
|||||||
// isGlobal is set to true for the pre-defined global mutex (\_GL object)
|
// isGlobal is set to true for the pre-defined global mutex (\_GL object)
|
||||||
isGlobal bool
|
isGlobal bool
|
||||||
|
|
||||||
name string
|
name string
|
||||||
syncLevel uint8
|
syncLevel uint8
|
||||||
|
tableHandle uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ent *mutexEntity) getOpcode() opcode { return opMutex }
|
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
|
ent.syncLevel = uint8(syncLevel) & 0xf
|
||||||
return ok
|
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.
|
// eventEntity represents a named ACPI sync event.
|
||||||
type eventEntity struct {
|
type eventEntity struct {
|
||||||
|
@ -15,11 +15,12 @@ var (
|
|||||||
|
|
||||||
// Parser implements an AML parser.
|
// Parser implements an AML parser.
|
||||||
type Parser struct {
|
type Parser struct {
|
||||||
r amlStreamReader
|
r amlStreamReader
|
||||||
errWriter io.Writer
|
errWriter io.Writer
|
||||||
root ScopeEntity
|
root ScopeEntity
|
||||||
scopeStack []ScopeEntity
|
scopeStack []ScopeEntity
|
||||||
tableName string
|
tableName string
|
||||||
|
tableHandle uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewParser returns a new AML parser instance.
|
// 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
|
// ParseAML attempts to parse the AML byte-code contained in the supplied ACPI
|
||||||
// table. The parser emits any encountered errors to the specified errWriter.
|
// table tagging each scoped entity with the supplied table handle. The parser
|
||||||
func (p *Parser) ParseAML(tableName string, header *table.SDTHeader) *kernel.Error {
|
// 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.tableName = tableName
|
||||||
p.r.Init(
|
p.r.Init(
|
||||||
uintptr(unsafe.Pointer(header)),
|
uintptr(unsafe.Pointer(header)),
|
||||||
@ -137,13 +140,16 @@ func (p *Parser) parseObj() bool {
|
|||||||
|
|
||||||
// finalizeObj applies post-parse logic for special object types.
|
// finalizeObj applies post-parse logic for special object types.
|
||||||
func (p *Parser) finalizeObj(op opcode, obj Entity) bool {
|
func (p *Parser) finalizeObj(op opcode, obj Entity) bool {
|
||||||
|
obj.setTableHandle(p.tableHandle)
|
||||||
|
|
||||||
switch op {
|
switch op {
|
||||||
case opElse:
|
case opElse:
|
||||||
// If this is an else block we need to append it as an argument to the
|
// If this is an else block we need to append it as an argument to the
|
||||||
// If block
|
// If block
|
||||||
// Pop Else block of the current scope
|
// Pop Else block of the current scope
|
||||||
p.scopeCurrent().removeLastChild()
|
curScope := p.scopeCurrent()
|
||||||
prevObj := p.scopeCurrent().lastChild()
|
curScope.removeChild(curScope.lastChild())
|
||||||
|
prevObj := curScope.lastChild()
|
||||||
if prevObj.getOpcode() != opIf {
|
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())
|
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] encountered else block without a matching if block\n", p.tableName, p.r.Offset())
|
||||||
return false
|
return false
|
||||||
@ -203,7 +209,7 @@ func (p *Parser) parseScope(maxReadOffset uint32) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseNamespacedObj reads a scope target name from the AML bytestream,
|
// 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
|
// correct parent scope, enters the device scope and parses the object list
|
||||||
// contained in the device definition.
|
// contained in the device definition.
|
||||||
func (p *Parser) parseNamespacedObj(op opcode, maxReadOffset uint32) bool {
|
func (p *Parser) parseNamespacedObj(op opcode, maxReadOffset uint32) bool {
|
||||||
@ -312,7 +318,10 @@ func (p *Parser) parseArgObj() (Entity, bool) {
|
|||||||
return nil, false
|
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 {
|
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
|
// It may be a nested invocation or named ref
|
||||||
ok = p.parseMethodInvocationOrNameRef()
|
ok = p.parseMethodInvocationOrNameRef()
|
||||||
if ok {
|
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{
|
p.scopeCurrent().Append(&fieldUnitEntity{
|
||||||
fieldEntity: fieldEntity{
|
fieldEntity: fieldEntity{
|
||||||
namedEntity: namedEntity{
|
namedEntity: namedEntity{
|
||||||
op: op,
|
tableHandle: p.tableHandle,
|
||||||
name: unitName,
|
op: op,
|
||||||
|
name: unitName,
|
||||||
},
|
},
|
||||||
bitOffset: curBitOffset,
|
bitOffset: curBitOffset,
|
||||||
bitWidth: bitWidth,
|
bitWidth: bitWidth,
|
||||||
@ -618,8 +629,9 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin
|
|||||||
p.scopeCurrent().Append(&indexFieldEntity{
|
p.scopeCurrent().Append(&indexFieldEntity{
|
||||||
fieldEntity: fieldEntity{
|
fieldEntity: fieldEntity{
|
||||||
namedEntity: namedEntity{
|
namedEntity: namedEntity{
|
||||||
op: op,
|
tableHandle: p.tableHandle,
|
||||||
name: unitName,
|
op: op,
|
||||||
|
name: unitName,
|
||||||
},
|
},
|
||||||
bitOffset: curBitOffset,
|
bitOffset: curBitOffset,
|
||||||
bitWidth: bitWidth,
|
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.
|
// In this case, this is either a NameString or a control method invocation.
|
||||||
if ok := p.parseMethodInvocationOrNameRef(); ok {
|
if ok := p.parseMethodInvocationOrNameRef(); ok {
|
||||||
return p.scopeCurrent().removeLastChild(), ok
|
obj := p.scopeCurrent().lastChild()
|
||||||
|
p.scopeCurrent().removeChild(obj)
|
||||||
|
return obj, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, false
|
return nil, false
|
||||||
|
@ -33,7 +33,7 @@ func TestParser(t *testing.T) {
|
|||||||
|
|
||||||
for _, tableName := range spec {
|
for _, tableName := range spec {
|
||||||
tableName := strings.Replace(tableName, ".aml", "", -1)
|
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)
|
t.Errorf("[spec %d] [%s]: %v", specIndex, tableName, err)
|
||||||
break
|
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 {
|
func pkgDir() string {
|
||||||
_, f, _, _ := runtime.Caller(1)
|
_, f, _, _ := runtime.Caller(1)
|
||||||
return filepath.Dir(f)
|
return filepath.Dir(f)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user