mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
acpi: update AML parser to use the new entities
This commit is contained in:
parent
e7f203f06a
commit
41eae61c9f
@ -47,6 +47,7 @@ type Container interface {
|
||||
Last() Entity
|
||||
}
|
||||
|
||||
// FieldAccessTypeProvider is an interface implemented by all field entities.
|
||||
type FieldAccessTypeProvider interface {
|
||||
// DefaultAccessType returns the default FieldAccessType for any field unit
|
||||
// defined by this field.
|
||||
|
@ -79,6 +79,19 @@ func TestEntityMethods(t *testing.T) {
|
||||
t.Fatalf("expected parent not to have any child nodes; got %d", got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("FieldAccessTypeProvider implementers", func(t *testing.T) {
|
||||
for specIndex, spec := range specs {
|
||||
provider, ok := spec.ent.(FieldAccessTypeProvider)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if exp, got := FieldAccessTypeAny, provider.DefaultAccessType(); got != exp {
|
||||
t.Errorf("[spec %d] expected provider to return access type: %d; got %d", specIndex, exp, got)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestEntityArgAssignment(t *testing.T) {
|
||||
@ -160,6 +173,24 @@ func TestEntityArgAssignment(t *testing.T) {
|
||||
[]interface{}{NewConst(OpDwordPrefix, 2, uint64(42))},
|
||||
false,
|
||||
},
|
||||
{
|
||||
NewField(2),
|
||||
[]interface{}{"REG0", uint64(128)},
|
||||
nil, // Field populates its internal state using the first 2 args
|
||||
true,
|
||||
},
|
||||
{
|
||||
NewIndexField(2),
|
||||
[]interface{}{"REG0", "DAT0", uint64(128)},
|
||||
nil, // IndexField populates its internal state using the first 3 args
|
||||
true,
|
||||
},
|
||||
{
|
||||
NewBankField(2),
|
||||
[]interface{}{"REG0", "BNK0", uint64(0xf00f), uint64(128)},
|
||||
nil, // BankField populates its internal state using the first 4 args
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
nextSpec:
|
||||
|
@ -6,6 +6,7 @@ package entity
|
||||
// representation of the opcode values.
|
||||
type AMLOpcode uint16
|
||||
|
||||
// List of AML opcodes
|
||||
const (
|
||||
// Regular opcode list
|
||||
OpZero = AMLOpcode(0x00)
|
||||
@ -427,14 +428,3 @@ func OpIsType2(op AMLOpcode) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// OpIsBufferField returens true if this opcode describes a
|
||||
// buffer field creation operation.
|
||||
func OpIsBufferField(op AMLOpcode) bool {
|
||||
switch op {
|
||||
case OpCreateField, OpCreateBitField, OpCreateByteField, OpCreateWordField, OpCreateDWordField, OpCreateQWordField:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -227,14 +227,6 @@ func TestOpcodeIsX(t *testing.T) {
|
||||
{OpPackage, OpIsDataObject, true},
|
||||
{OpVarPackage, OpIsDataObject, true},
|
||||
{OpLor, OpIsDataObject, false},
|
||||
// OpIsBufferField
|
||||
{OpCreateField, OpIsBufferField, true},
|
||||
{OpCreateBitField, OpIsBufferField, true},
|
||||
{OpCreateByteField, OpIsBufferField, true},
|
||||
{OpCreateWordField, OpIsBufferField, true},
|
||||
{OpCreateDWordField, OpIsBufferField, true},
|
||||
{OpCreateQWordField, OpIsBufferField, true},
|
||||
{OpRevision, OpIsBufferField, false},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
|
@ -8,7 +8,7 @@ func TestScopeVisit(t *testing.T) {
|
||||
stopRecursing := func(Entity) bool { return false }
|
||||
|
||||
// Append special entities under IDE0
|
||||
root := NewScope(tableHandle, "IDE0")
|
||||
root := NewScope(OpScope, tableHandle, "IDE0")
|
||||
root.Append(NewDevice(tableHandle, "DEV0"))
|
||||
root.Append(NewProcessor(tableHandle, "FOO0"))
|
||||
root.Append(NewProcessor(tableHandle, "FOO0"))
|
||||
|
@ -55,7 +55,7 @@ const (
|
||||
|
||||
// is returns true if f is set in this opFlag.
|
||||
func (fl opFlag) is(f opFlag) bool {
|
||||
return (fl & f) != 0
|
||||
return (fl & f) == f
|
||||
}
|
||||
|
||||
// opArgFlags encodes up to 7 opArgFlag values in a uint64 value.
|
||||
@ -99,7 +99,6 @@ const (
|
||||
opArgTermList
|
||||
opArgTermObj
|
||||
opArgByteList
|
||||
opArgPackage
|
||||
opArgString
|
||||
opArgByteData
|
||||
opArgWord
|
||||
|
@ -1,6 +1,7 @@
|
||||
package aml
|
||||
package parser
|
||||
|
||||
import (
|
||||
"gopheros/device/acpi/aml/entity"
|
||||
"gopheros/device/acpi/table"
|
||||
"gopheros/kernel"
|
||||
"gopheros/kernel/kfmt"
|
||||
@ -17,8 +18,8 @@ var (
|
||||
type Parser struct {
|
||||
r amlStreamReader
|
||||
errWriter io.Writer
|
||||
root ScopeEntity
|
||||
scopeStack []ScopeEntity
|
||||
root entity.Container
|
||||
scopeStack []entity.Container
|
||||
tableName string
|
||||
tableHandle uint8
|
||||
|
||||
@ -30,7 +31,7 @@ type Parser struct {
|
||||
}
|
||||
|
||||
// NewParser returns a new AML parser instance.
|
||||
func NewParser(errWriter io.Writer, rootEntity ScopeEntity) *Parser {
|
||||
func NewParser(errWriter io.Writer, rootEntity entity.Container) *Parser {
|
||||
return &Parser{
|
||||
errWriter: errWriter,
|
||||
root: rootEntity,
|
||||
@ -65,25 +66,29 @@ func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDT
|
||||
}
|
||||
p.scopeExit()
|
||||
|
||||
// Pass 3: check parents and resolve forward references
|
||||
// Pass 3: check parents and resolve symbol references
|
||||
var resolveFailed bool
|
||||
scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool {
|
||||
entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool {
|
||||
// Skip method bodies; their contents will be lazily resolved by the interpreter
|
||||
if _, isMethod := ent.(*Method); isMethod {
|
||||
if _, isMethod := ent.(*entity.Method); isMethod {
|
||||
return false
|
||||
}
|
||||
|
||||
// Populate parents for any entity args that are also entities but are not
|
||||
// linked to a parent (e.g. a package inside a named entity).
|
||||
for _, arg := range ent.getArgs() {
|
||||
if argEnt, isArgEnt := arg.(Entity); isArgEnt && argEnt.Parent() == nil {
|
||||
argEnt.setParent(ent.Parent())
|
||||
for _, arg := range ent.Args() {
|
||||
if argEnt, isArgEnt := arg.(entity.Entity); isArgEnt && argEnt.Parent() == nil {
|
||||
argEnt.SetParent(ent.Parent())
|
||||
}
|
||||
}
|
||||
|
||||
if res, ok := ent.(resolver); ok && !res.Resolve(p.errWriter, p.root) {
|
||||
resolveFailed = true
|
||||
return false
|
||||
// Resolve any symbol references
|
||||
if lazyRef, ok := ent.(entity.LazyRefResolver); ok {
|
||||
if err := lazyRef.ResolveSymbolRefs(p.root); err != nil {
|
||||
kfmt.Fprintf(p.errWriter, "%s\n", err.Message)
|
||||
resolveFailed = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
@ -117,7 +122,7 @@ func (p *Parser) detectMethodDeclarations() {
|
||||
continue
|
||||
}
|
||||
|
||||
if next.op != opMethod {
|
||||
if next.op != entity.OpMethod {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -187,11 +192,11 @@ func (p *Parser) parseObj() bool {
|
||||
}
|
||||
|
||||
// If we encounter a named scope we need to look it up and parse the arg list relative to it
|
||||
switch info.op {
|
||||
case opScope:
|
||||
switch {
|
||||
case info.op == entity.OpScope:
|
||||
return p.parseScope(curOffset + pkgLen)
|
||||
case opDevice, opMethod:
|
||||
return p.parseNamespacedObj(info.op, curOffset+pkgLen)
|
||||
case info.flags.is(opFlagNamed | opFlagScoped):
|
||||
return p.parseNamespacedObj(info, curOffset+pkgLen)
|
||||
}
|
||||
|
||||
// Create appropriate object for opcode type and attach it to current scope unless it is
|
||||
@ -217,33 +222,22 @@ 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)
|
||||
|
||||
func (p *Parser) finalizeObj(op entity.AMLOpcode, obj entity.Entity) bool {
|
||||
switch op {
|
||||
case opElse:
|
||||
case entity.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
|
||||
curScope := p.scopeCurrent()
|
||||
curScope.removeChild(curScope.lastChild())
|
||||
prevObj := curScope.lastChild()
|
||||
if prevObj.getOpcode() != opIf {
|
||||
curScope.Remove(curScope.Last())
|
||||
prevObj := curScope.Last()
|
||||
if prevObj.Opcode() != entity.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
|
||||
}
|
||||
|
||||
// If predicate(0) then(1) else(2)
|
||||
prevObj.setArg(2, obj)
|
||||
case opDevice:
|
||||
// Build method map
|
||||
dev := obj.(*Device)
|
||||
dev.methodMap = make(map[string]*Method)
|
||||
scopeVisit(0, dev, EntityTypeMethod, func(_ int, ent Entity) bool {
|
||||
method := ent.(*Method)
|
||||
dev.methodMap[method.name] = method
|
||||
return false
|
||||
})
|
||||
prevObj.SetArg(2, obj)
|
||||
}
|
||||
|
||||
return true
|
||||
@ -262,14 +256,14 @@ func (p *Parser) parseScope(maxReadOffset uint32) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
target := scopeFind(p.scopeCurrent(), p.root, name)
|
||||
target := entity.FindInScope(p.scopeCurrent(), p.root, name)
|
||||
if target == nil {
|
||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] undefined scope: %s\n", p.tableName, p.r.Offset(), name)
|
||||
return false
|
||||
}
|
||||
|
||||
switch target.getOpcode() {
|
||||
case opDevice, opProcessor, opThermalZone, opPowerRes:
|
||||
switch target.Opcode() {
|
||||
case entity.OpDevice, entity.OpProcessor, entity.OpThermalZone, entity.OpPowerRes:
|
||||
// ok
|
||||
default:
|
||||
// Only allow if this is a named scope
|
||||
@ -279,7 +273,7 @@ func (p *Parser) parseScope(maxReadOffset uint32) bool {
|
||||
}
|
||||
}
|
||||
|
||||
p.scopeEnter(target.(ScopeEntity))
|
||||
p.scopeEnter(target.(entity.Container))
|
||||
ok = p.parseObjList(maxReadOffset)
|
||||
p.scopeExit()
|
||||
|
||||
@ -287,48 +281,50 @@ func (p *Parser) parseScope(maxReadOffset uint32) bool {
|
||||
}
|
||||
|
||||
// parseNamespacedObj reads a scope target name from the AML bytestream,
|
||||
// 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 {
|
||||
// attaches the appropriate object depending on the opcode to the correct
|
||||
// parent scope and then parses any contained objects. The contained objects
|
||||
// will be appended inside the newly constructed scope.
|
||||
func (p *Parser) parseNamespacedObj(info *opcodeInfo, maxReadOffset uint32) bool {
|
||||
scopeExpr, ok := p.parseNameString()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
parent, name := scopeResolvePath(p.scopeCurrent(), p.root, scopeExpr)
|
||||
parent, name := entity.ResolveScopedPath(p.scopeCurrent(), p.root, scopeExpr)
|
||||
if parent == nil {
|
||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] undefined scope target: %s (current scope: %s)\n", p.tableName, p.r.Offset(), scopeExpr, p.scopeCurrent().Name())
|
||||
return false
|
||||
}
|
||||
|
||||
var obj ScopeEntity
|
||||
switch op {
|
||||
case opDevice:
|
||||
obj = &Device{scopeEntity: scopeEntity{name: name}}
|
||||
case opMethod:
|
||||
m := &Method{scopeEntity: scopeEntity{name: name}}
|
||||
|
||||
flags, flagOk := p.parseNumConstant(1)
|
||||
if !flagOk {
|
||||
return false
|
||||
}
|
||||
m.argCount = (uint8(flags) & 0x7) // bits[0:2]
|
||||
m.serialized = (uint8(flags)>>3)&0x1 == 0x1 // bit 3
|
||||
m.syncLevel = (uint8(flags) >> 4) & 0xf // bits[4:7]
|
||||
|
||||
obj = m
|
||||
var obj entity.Container
|
||||
switch info.op {
|
||||
case entity.OpDevice:
|
||||
obj = entity.NewDevice(p.tableHandle, name)
|
||||
case entity.OpProcessor:
|
||||
obj = entity.NewProcessor(p.tableHandle, name)
|
||||
case entity.OpPowerRes:
|
||||
obj = entity.NewPowerResource(p.tableHandle, name)
|
||||
case entity.OpThermalZone:
|
||||
obj = entity.NewThermalZone(p.tableHandle, name)
|
||||
case entity.OpMethod:
|
||||
obj = entity.NewMethod(p.tableHandle, name)
|
||||
default:
|
||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] unsupported namespaced op: %s (current scope: %s)\n", p.tableName, p.r.Offset(), info.op.String(), p.scopeCurrent().Name())
|
||||
return false
|
||||
}
|
||||
|
||||
// Parse any args that follow the name. The last arg is always an ArgTermList
|
||||
parent.Append(obj)
|
||||
p.scopeEnter(obj)
|
||||
ok = p.parseObjList(maxReadOffset)
|
||||
p.scopeExit()
|
||||
for argIndex := uint8(1); argIndex < info.argFlags.argCount(); argIndex++ {
|
||||
if !p.parseArg(info, obj, argIndex, info.argFlags.arg(argIndex), maxReadOffset) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return ok && p.finalizeObj(op, obj)
|
||||
return ok && p.finalizeObj(info.op, obj)
|
||||
}
|
||||
|
||||
func (p *Parser) parseArg(info *opcodeInfo, obj Entity, argIndex uint8, argType opArgFlag, maxReadOffset uint32) bool {
|
||||
func (p *Parser) parseArg(info *opcodeInfo, obj entity.Entity, argIndex uint8, argType opArgFlag, maxReadOffset uint32) bool {
|
||||
var (
|
||||
arg interface{}
|
||||
ok bool
|
||||
@ -359,19 +355,20 @@ func (p *Parser) parseArg(info *opcodeInfo, obj Entity, argIndex uint8, argType
|
||||
// If object is a scoped entity enter it's scope before parsing
|
||||
// the term list. Otherwise, create an unnamed scope, attach it
|
||||
// as the next argument to obj and enter that.
|
||||
if s, isScopeEnt := obj.(ScopeEntity); isScopeEnt {
|
||||
if s, isScopeEnt := obj.(entity.Container); isScopeEnt {
|
||||
p.scopeEnter(s)
|
||||
} else {
|
||||
ns := &scopeEntity{op: opScope}
|
||||
// Create an unnamed scope (e.g if, else, while scope)
|
||||
ns := entity.NewScope(info.op, p.tableHandle, "")
|
||||
p.scopeEnter(ns)
|
||||
obj.setArg(argIndex, ns)
|
||||
obj.SetArg(argIndex, ns)
|
||||
}
|
||||
|
||||
ok = p.parseObjList(maxReadOffset)
|
||||
p.scopeExit()
|
||||
return ok
|
||||
case opArgFieldList:
|
||||
return p.parseFieldList(info.op, obj.getArgs(), maxReadOffset)
|
||||
return p.parseFieldList(obj, maxReadOffset)
|
||||
case opArgByteList:
|
||||
var bl []byte
|
||||
for p.r.Offset() < maxReadOffset {
|
||||
@ -388,45 +385,68 @@ func (p *Parser) parseArg(info *opcodeInfo, obj Entity, argIndex uint8, argType
|
||||
return false
|
||||
}
|
||||
|
||||
return obj.setArg(argIndex, arg)
|
||||
return obj.SetArg(argIndex, arg)
|
||||
}
|
||||
|
||||
func (p *Parser) parseArgObj() (Entity, bool) {
|
||||
func (p *Parser) parseArgObj() (entity.Entity, bool) {
|
||||
if ok := p.parseObj(); !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
curScope := p.scopeCurrent()
|
||||
obj := curScope.lastChild()
|
||||
curScope.removeChild(obj)
|
||||
obj := curScope.Last()
|
||||
curScope.Remove(obj)
|
||||
return obj, true
|
||||
}
|
||||
|
||||
func (p *Parser) makeObjForOpcode(info *opcodeInfo) Entity {
|
||||
var obj Entity
|
||||
func (p *Parser) makeObjForOpcode(info *opcodeInfo) entity.Entity {
|
||||
var obj entity.Entity
|
||||
|
||||
switch {
|
||||
case info.op == opOpRegion:
|
||||
obj = new(regionEntity)
|
||||
case info.op == opBuffer:
|
||||
obj = new(bufferEntity)
|
||||
case info.op == opMutex:
|
||||
obj = new(mutexEntity)
|
||||
case info.op == opEvent:
|
||||
obj = new(eventEntity)
|
||||
case opIsBufferField(info.op):
|
||||
obj = new(bufferFieldEntity)
|
||||
case info.op == entity.OpOpRegion:
|
||||
obj = entity.NewRegion(p.tableHandle)
|
||||
case info.op == entity.OpBuffer:
|
||||
obj = entity.NewBuffer(p.tableHandle)
|
||||
case info.op == entity.OpMutex:
|
||||
obj = entity.NewMutex(p.tableHandle)
|
||||
case info.op == entity.OpEvent:
|
||||
obj = entity.NewEvent(p.tableHandle)
|
||||
case info.op == entity.OpField:
|
||||
obj = entity.NewField(p.tableHandle)
|
||||
case info.op == entity.OpIndexField:
|
||||
obj = entity.NewIndexField(p.tableHandle)
|
||||
case info.op == entity.OpBankField:
|
||||
obj = entity.NewBankField(p.tableHandle)
|
||||
case info.op == entity.OpCreateField:
|
||||
obj = entity.NewBufferField(info.op, p.tableHandle, 0)
|
||||
case info.op == entity.OpCreateBitField:
|
||||
obj = entity.NewBufferField(info.op, p.tableHandle, 1)
|
||||
case info.op == entity.OpCreateByteField:
|
||||
obj = entity.NewBufferField(info.op, p.tableHandle, 8)
|
||||
case info.op == entity.OpCreateWordField:
|
||||
obj = entity.NewBufferField(info.op, p.tableHandle, 16)
|
||||
case info.op == entity.OpCreateDWordField:
|
||||
obj = entity.NewBufferField(info.op, p.tableHandle, 32)
|
||||
case info.op == entity.OpCreateQWordField:
|
||||
obj = entity.NewBufferField(info.op, p.tableHandle, 64)
|
||||
case info.op == entity.OpZero:
|
||||
obj = entity.NewConst(info.op, p.tableHandle, uint64(0))
|
||||
case info.op == entity.OpOne:
|
||||
obj = entity.NewConst(info.op, p.tableHandle, uint64(1))
|
||||
case info.op == entity.OpOnes:
|
||||
obj = entity.NewConst(info.op, p.tableHandle, uint64((1<<64)-1))
|
||||
case info.flags.is(opFlagConstant):
|
||||
obj = new(constEntity)
|
||||
obj = entity.NewConst(info.op, p.tableHandle, nil) // will be parsed as an arg
|
||||
case info.op == entity.OpPackage || info.op == entity.OpVarPackage:
|
||||
obj = entity.NewPackage(info.op, p.tableHandle)
|
||||
case info.flags.is(opFlagScoped):
|
||||
obj = new(scopeEntity)
|
||||
obj = entity.NewScope(info.op, p.tableHandle, "")
|
||||
case info.flags.is(opFlagNamed):
|
||||
obj = new(namedEntity)
|
||||
obj = entity.NewGenericNamed(info.op, p.tableHandle)
|
||||
default:
|
||||
obj = new(unnamedEntity)
|
||||
obj = entity.NewGeneric(info.op, p.tableHandle)
|
||||
}
|
||||
|
||||
obj.setOpcode(info.op)
|
||||
return obj
|
||||
}
|
||||
|
||||
@ -447,7 +467,7 @@ func (p *Parser) parseNamedRef() bool {
|
||||
var (
|
||||
curOffset uint32
|
||||
argIndex uint8
|
||||
arg Entity
|
||||
arg entity.Entity
|
||||
argList []interface{}
|
||||
)
|
||||
|
||||
@ -459,14 +479,14 @@ func (p *Parser) parseNamedRef() bool {
|
||||
p.r.SetOffset(curOffset)
|
||||
|
||||
switch {
|
||||
case ok && (opIsType2(nextOpcode.op) || opIsArg(nextOpcode.op) || opIsDataObject(nextOpcode.op)):
|
||||
case ok && (entity.OpIsType2(nextOpcode.op) || entity.OpIsArg(nextOpcode.op) || entity.OpIsDataObject(nextOpcode.op)):
|
||||
arg, ok = p.parseArgObj()
|
||||
default:
|
||||
// It may be a nested invocation or named ref
|
||||
ok = p.parseNamedRef()
|
||||
if ok {
|
||||
arg = p.scopeCurrent().lastChild()
|
||||
p.scopeCurrent().removeChild(arg)
|
||||
arg = p.scopeCurrent().Last()
|
||||
p.scopeCurrent().Remove(arg)
|
||||
}
|
||||
}
|
||||
|
||||
@ -486,14 +506,11 @@ func (p *Parser) parseNamedRef() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
return p.scopeCurrent().Append(&methodInvocationEntity{
|
||||
unnamedEntity: unnamedEntity{args: argList},
|
||||
methodName: name,
|
||||
})
|
||||
return p.scopeCurrent().Append(entity.NewInvocation(p.tableHandle, name, argList))
|
||||
}
|
||||
|
||||
// Otherwise this is a reference to a named entity
|
||||
return p.scopeCurrent().Append(&namedReference{targetName: name})
|
||||
return p.scopeCurrent().Append(entity.NewReference(p.tableHandle, name))
|
||||
}
|
||||
|
||||
func (p *Parser) nextOpcode() (*opcodeInfo, bool) {
|
||||
@ -533,69 +550,30 @@ func (p *Parser) nextOpcode() (*opcodeInfo, bool) {
|
||||
// AccessField := 0x1 AccessType AccessAttrib
|
||||
// ConnectField := 0x02 NameString | 0x02 BufferData
|
||||
// ExtendedAccessField := 0x3 AccessType ExtendedAccessType AccessLength
|
||||
func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uint32) bool {
|
||||
func (p *Parser) parseFieldList(fieldEnt entity.Entity, maxReadOffset uint32) bool {
|
||||
var (
|
||||
// for fieldUnit, name0 is the region name and name1 is not used;
|
||||
// for indexField,
|
||||
name0, name1 string
|
||||
flags uint64
|
||||
ok bool
|
||||
|
||||
ok bool
|
||||
bitWidth uint32
|
||||
curBitOffset uint32
|
||||
accessAttrib FieldAccessAttrib
|
||||
accessByteCount uint8
|
||||
unitName string
|
||||
)
|
||||
accessType entity.FieldAccessType
|
||||
|
||||
switch op {
|
||||
case opField: // Field := PkgLength Region AccessFlags FieldList
|
||||
if len(args) != 2 {
|
||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d, opcode 0x%2x] invalid arg count: %d\n", p.tableName, p.r.Offset(), uint32(op), len(args))
|
||||
return false
|
||||
}
|
||||
|
||||
name0, ok = args[0].(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
flags, ok = args[1].(uint64)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
case opIndexField: // Field := PkgLength IndexFieldName DataFieldName AccessFlags FieldList
|
||||
if len(args) != 3 {
|
||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d, opcode 0x%2x] invalid arg count: %d\n", p.tableName, p.r.Offset(), uint32(op), len(args))
|
||||
return false
|
||||
}
|
||||
|
||||
name0, ok = args[0].(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
name1, ok = args[1].(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
flags, ok = args[2].(uint64)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Decode flags
|
||||
accessType := FieldAccessType(flags & 0xf) // access type; bits[0:3]
|
||||
lock := (flags>>4)&0x1 == 0x1 // lock; bit 4
|
||||
updateRule := FieldUpdateRule((flags >> 5) & 0x3) // update rule; bits[5:6]
|
||||
|
||||
var (
|
||||
bitWidth uint32
|
||||
curBitOffset uint32
|
||||
connectionName string
|
||||
resolvedConnection Entity
|
||||
unitName string
|
||||
resolvedConnection entity.Entity
|
||||
accessAttrib entity.FieldAccessAttrib
|
||||
accessByteCount uint8
|
||||
)
|
||||
|
||||
// Load default field access rule; it applies to all field units unless
|
||||
// overridden via a directive in the field unit list
|
||||
if accessProvider, isProvider := fieldEnt.(entity.FieldAccessTypeProvider); isProvider {
|
||||
accessType = accessProvider.DefaultAccessType()
|
||||
} else {
|
||||
// not a field entity
|
||||
return false
|
||||
}
|
||||
|
||||
for p.r.Offset() < maxReadOffset {
|
||||
next, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
@ -616,7 +594,7 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
accessType = FieldAccessType(next & 0xf) // access type; bits[0:3]
|
||||
accessType = entity.FieldAccessType(next & 0xf) // access type; bits[0:3]
|
||||
|
||||
attrib, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
@ -626,7 +604,7 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin
|
||||
// To specify AccessAttribBytes, RawBytes and RawProcessBytes
|
||||
// the ASL compiler will emit an ExtendedAccessField opcode.
|
||||
accessByteCount = 0
|
||||
accessAttrib = FieldAccessAttrib(attrib)
|
||||
accessAttrib = entity.FieldAccessAttrib(attrib)
|
||||
|
||||
continue
|
||||
case 0x2: // ConnectField => <0x2> NameString> | <0x02> TermObj => Buffer
|
||||
@ -643,7 +621,7 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
accessType = FieldAccessType(next & 0xf) // access type; bits[0:3]
|
||||
accessType = entity.FieldAccessType(next & 0xf) // access type; bits[0:3]
|
||||
|
||||
extAccessAttrib, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
@ -657,11 +635,11 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin
|
||||
|
||||
switch extAccessAttrib {
|
||||
case 0x0b:
|
||||
accessAttrib = FieldAccessAttribBytes
|
||||
accessAttrib = entity.FieldAccessAttribBytes
|
||||
case 0xe:
|
||||
accessAttrib = FieldAccessAttribRawBytes
|
||||
accessAttrib = entity.FieldAccessAttribRawBytes
|
||||
case 0x0f:
|
||||
accessAttrib = FieldAccessAttribRawProcessBytes
|
||||
accessAttrib = entity.FieldAccessAttribRawProcessBytes
|
||||
}
|
||||
default: // NamedField
|
||||
_ = p.r.UnreadByte()
|
||||
@ -675,55 +653,20 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin
|
||||
}
|
||||
|
||||
// According to the spec, the field elements are should
|
||||
// be visible at the same scope as the Field/IndexField
|
||||
switch op {
|
||||
case opField:
|
||||
p.scopeCurrent().Append(&fieldUnitEntity{
|
||||
fieldEntity: fieldEntity{
|
||||
namedEntity: namedEntity{
|
||||
tableHandle: p.tableHandle,
|
||||
op: op,
|
||||
name: unitName,
|
||||
},
|
||||
bitOffset: curBitOffset,
|
||||
bitWidth: bitWidth,
|
||||
lock: lock,
|
||||
updateRule: updateRule,
|
||||
accessType: accessType,
|
||||
accessAttrib: accessAttrib,
|
||||
byteCount: accessByteCount,
|
||||
},
|
||||
connectionName: connectionName,
|
||||
resolvedConnection: resolvedConnection,
|
||||
regionName: name0,
|
||||
})
|
||||
case opIndexField:
|
||||
p.scopeCurrent().Append(&indexFieldEntity{
|
||||
fieldEntity: fieldEntity{
|
||||
namedEntity: namedEntity{
|
||||
tableHandle: p.tableHandle,
|
||||
op: op,
|
||||
name: unitName,
|
||||
},
|
||||
bitOffset: curBitOffset,
|
||||
bitWidth: bitWidth,
|
||||
lock: lock,
|
||||
updateRule: updateRule,
|
||||
accessType: accessType,
|
||||
accessAttrib: accessAttrib,
|
||||
byteCount: accessByteCount,
|
||||
},
|
||||
connectionName: connectionName,
|
||||
resolvedConnection: resolvedConnection,
|
||||
indexRegName: name0,
|
||||
dataRegName: name1,
|
||||
})
|
||||
}
|
||||
// be visible at the same scope as the Field that declares them
|
||||
unit := entity.NewFieldUnit(p.tableHandle, unitName)
|
||||
unit.Field = fieldEnt
|
||||
unit.AccessType = accessType
|
||||
unit.AccessAttrib = accessAttrib
|
||||
unit.ByteCount = accessByteCount
|
||||
unit.BitOffset = curBitOffset
|
||||
unit.BitWidth = bitWidth
|
||||
unit.ConnectionName = connectionName
|
||||
unit.Connection = resolvedConnection
|
||||
|
||||
p.scopeCurrent().Append(unit)
|
||||
curBitOffset += bitWidth
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ok && p.r.Offset() == maxReadOffset
|
||||
@ -859,10 +802,8 @@ func (p *Parser) parseSimpleName() (interface{}, bool) {
|
||||
var obj interface{}
|
||||
|
||||
switch {
|
||||
case ok && nextOpcode.op >= opLocal0 && nextOpcode.op <= opLocal7:
|
||||
obj, ok = &unnamedEntity{op: nextOpcode.op}, true
|
||||
case ok && nextOpcode.op >= opArg0 && nextOpcode.op <= opArg6:
|
||||
obj, ok = &unnamedEntity{op: nextOpcode.op}, true
|
||||
case ok && entity.OpIsArg(nextOpcode.op):
|
||||
obj, ok = entity.NewGeneric(nextOpcode.op, p.tableHandle), true
|
||||
default:
|
||||
// Rewind and try parsing as NameString
|
||||
p.r.SetOffset(curOffset)
|
||||
@ -890,10 +831,10 @@ func (p *Parser) parseTarget() (interface{}, bool) {
|
||||
|
||||
if ok {
|
||||
switch {
|
||||
case nextOpcode.op == opZero: // this is actually a NullName
|
||||
case nextOpcode.op == entity.OpZero: // this is actually a NullName
|
||||
p.r.SetOffset(curOffset + 1)
|
||||
return &constEntity{op: opStringPrefix, val: ""}, true
|
||||
case opIsArg(nextOpcode.op) || nextOpcode.op == opRefOf || nextOpcode.op == opDerefOf || nextOpcode.op == opIndex || nextOpcode.op == opDebug: // LocalObj | ArgObj | Type6 | DebugObj
|
||||
return entity.NewConst(entity.OpStringPrefix, p.tableHandle, ""), true
|
||||
case entity.OpIsArg(nextOpcode.op) || nextOpcode.op == entity.OpRefOf || nextOpcode.op == entity.OpDerefOf || nextOpcode.op == entity.OpIndex || nextOpcode.op == entity.OpDebug: // LocalObj | ArgObj | Type6 | DebugObj
|
||||
default:
|
||||
// Unexpected opcode
|
||||
return nil, false
|
||||
@ -905,8 +846,8 @@ func (p *Parser) parseTarget() (interface{}, bool) {
|
||||
|
||||
// In this case, this is either a NameString or a control method invocation.
|
||||
if ok := p.parseNamedRef(); ok {
|
||||
obj := p.scopeCurrent().lastChild()
|
||||
p.scopeCurrent().removeChild(obj)
|
||||
obj := p.scopeCurrent().Last()
|
||||
p.scopeCurrent().Remove(obj)
|
||||
return obj, ok
|
||||
}
|
||||
|
||||
@ -996,12 +937,12 @@ func (p *Parser) parseNameString() (string, bool) {
|
||||
}
|
||||
|
||||
// scopeCurrent returns the currently active scope.
|
||||
func (p *Parser) scopeCurrent() ScopeEntity {
|
||||
func (p *Parser) scopeCurrent() entity.Container {
|
||||
return p.scopeStack[len(p.scopeStack)-1]
|
||||
}
|
||||
|
||||
// scopeEnter enters the given scope.
|
||||
func (p *Parser) scopeEnter(s ScopeEntity) {
|
||||
func (p *Parser) scopeEnter(s entity.Container) {
|
||||
p.scopeStack = append(p.scopeStack, s)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package aml
|
||||
package parser
|
||||
|
||||
import (
|
||||
"gopheros/device/acpi/aml/entity"
|
||||
"gopheros/device/acpi/table"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@ -23,19 +24,7 @@ func TestParser(t *testing.T) {
|
||||
tableFiles: spec,
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// Inject pre-defined OSPM objects
|
||||
rootNS.Append(&constEntity{name: "_OS_", val: "gopheros"})
|
||||
rootNS.Append(&constEntity{name: "_REV", val: uint64(2)})
|
||||
|
||||
p := NewParser(os.Stderr, rootNS)
|
||||
p := NewParser(os.Stderr, genDefaultScopes())
|
||||
|
||||
for _, tableName := range spec {
|
||||
tableName = strings.Replace(tableName, ".aml", "", -1)
|
||||
@ -50,25 +39,18 @@ 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
|
||||
|
||||
rootNS := genDefaultScopes()
|
||||
p := NewParser(ioutil.Discard, rootNS)
|
||||
|
||||
expHandle := uint8(42)
|
||||
expHandle := uint8(0x0f)
|
||||
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 {
|
||||
var unloadList []entity.Entity
|
||||
entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool {
|
||||
if ent.TableHandle() == expHandle {
|
||||
unloadList = append(unloadList, ent)
|
||||
return false
|
||||
@ -78,13 +60,13 @@ func TestTableHandleAssignment(t *testing.T) {
|
||||
|
||||
for _, ent := range unloadList {
|
||||
if p := ent.Parent(); p != nil {
|
||||
p.removeChild(ent)
|
||||
p.Remove(ent)
|
||||
}
|
||||
}
|
||||
|
||||
// We should end up with the original tree
|
||||
var visitedNodes int
|
||||
scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool {
|
||||
entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool {
|
||||
visitedNodes++
|
||||
if ent.TableHandle() == expHandle {
|
||||
t.Errorf("encountered entity that should have been pruned: %#+v", ent)
|
||||
@ -102,15 +84,7 @@ func TestParserForwardDeclParsing(t *testing.T) {
|
||||
tableFiles: []string{"parser-testsuite-fwd-decls-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)
|
||||
p := NewParser(ioutil.Discard, genDefaultScopes())
|
||||
|
||||
for _, tableName := range resolver.tableFiles {
|
||||
tableName = strings.Replace(tableName, ".aml", "", -1)
|
||||
@ -172,7 +146,7 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
|
||||
t.Run("ParseAML errors", func(t *testing.T) {
|
||||
t.Run("parseObjList error", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
||||
|
||||
// Setup resolver to serve an AML stream containing an invalid opcode
|
||||
header := mockParserPayload(p, []byte{0x5b, 0x00})
|
||||
@ -190,12 +164,10 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("unresolved entities", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
||||
|
||||
// Inject a reference entity to the tree
|
||||
p.root.Append(&namedReference{
|
||||
targetName: "UNKNOWN",
|
||||
})
|
||||
p.root.Append(entity.NewReference(42, "UNKNOWN"))
|
||||
|
||||
// Setup resolver to serve an empty AML stream
|
||||
header := mockParserPayload(p, nil)
|
||||
@ -208,11 +180,11 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
|
||||
t.Run("parseObj errors", func(t *testing.T) {
|
||||
t.Run("parsePkgLength error", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
||||
|
||||
// Setup resolver to serve an AML stream containing an incomplete
|
||||
// buffer specification
|
||||
header := mockParserPayload(p, []byte{byte(opBuffer)})
|
||||
header := mockParserPayload(p, []byte{byte(entity.OpBuffer)})
|
||||
|
||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
||||
t.Fatal("expected parsePkgLength to return an error")
|
||||
@ -220,11 +192,11 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("incomplete object list", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
||||
|
||||
// Setup resolver to serve an AML stream containing an incomplete
|
||||
// buffer arglist specification
|
||||
header := mockParserPayload(p, []byte{byte(opBuffer), 0x10})
|
||||
header := mockParserPayload(p, []byte{byte(entity.OpBuffer), 0x10})
|
||||
|
||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
||||
t.Fatal("expected parsePkgLength to return an error")
|
||||
@ -234,13 +206,12 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
|
||||
t.Run("finalizeObj errors", func(t *testing.T) {
|
||||
t.Run("else without matching if", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
p.root.Append(&constEntity{val: 0x42})
|
||||
p.root.Append(&scopeEntity{op: opElse})
|
||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
||||
p.root.Append(entity.NewConst(entity.OpDwordPrefix, 42, uint64(0x42)))
|
||||
|
||||
// Setup resolver to serve an AML stream containing an
|
||||
// empty else statement without a matching if
|
||||
header := mockParserPayload(p, []byte{byte(opElse), 0x0})
|
||||
header := mockParserPayload(p, []byte{byte(entity.OpElse), 0x0})
|
||||
|
||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
||||
t.Fatal("expected finalizeObj to return an error")
|
||||
@ -251,10 +222,10 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
|
||||
t.Run("parseScope errors", func(t *testing.T) {
|
||||
t.Run("parseNameString error", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
||||
|
||||
header := mockParserPayload(p, []byte{
|
||||
byte(opScope),
|
||||
byte(entity.OpScope),
|
||||
0x10, // pkglen
|
||||
})
|
||||
|
||||
@ -264,10 +235,10 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("unknown scope", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
||||
|
||||
header := mockParserPayload(p, []byte{
|
||||
byte(opScope),
|
||||
byte(entity.OpScope),
|
||||
0x10, // pkglen
|
||||
'F', 'O', 'O', 'F',
|
||||
})
|
||||
@ -278,10 +249,10 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("nameless scope", func(t *testing.T) {
|
||||
p.root = &scopeEntity{}
|
||||
p.root = entity.NewScope(entity.OpScope, 42, ``)
|
||||
|
||||
header := mockParserPayload(p, []byte{
|
||||
byte(opScope),
|
||||
byte(entity.OpScope),
|
||||
0x02, // pkglen
|
||||
'\\', // scope name: "\" (root scope)
|
||||
0x00, // null string
|
||||
@ -295,58 +266,75 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
|
||||
t.Run("parseNamespacedObj errors", func(t *testing.T) {
|
||||
t.Run("parseNameString error", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
||||
|
||||
mockParserPayload(p, nil)
|
||||
|
||||
if p.parseNamespacedObj(opDevice, 10) {
|
||||
devInfo := &opcodeTable[0x6a]
|
||||
if p.parseNamespacedObj(devInfo, 10) {
|
||||
t.Fatal("expected parseNamespacedObj to return false")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("scope lookup error", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
||||
|
||||
header := mockParserPayload(p, []byte{'^', 'F', 'A', 'B', 'C'})
|
||||
|
||||
p.scopeEnter(p.root)
|
||||
if p.parseNamespacedObj(opDevice, header.Length) {
|
||||
devInfo := &opcodeTable[0x6a]
|
||||
if p.parseNamespacedObj(devInfo, header.Length) {
|
||||
t.Fatal("expected parseNamespacedObj to return false")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("error parsing method arg count", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
t.Run("unsupported namespaced entity", func(t *testing.T) {
|
||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
||||
|
||||
header := mockParserPayload(p, []byte{'F', 'A', 'B', 'C'})
|
||||
|
||||
p.scopeEnter(p.root)
|
||||
if p.parseNamespacedObj(opMethod, header.Length) {
|
||||
|
||||
// We just pass a random non-namespaced opcode table entry to parseNamespacedObj
|
||||
zeroInfo := &opcodeTable[0x00]
|
||||
if p.parseNamespacedObj(zeroInfo, header.Length) {
|
||||
t.Fatal("expected parseNamespacedObj to return false")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("error parsing args after name", func(t *testing.T) {
|
||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
||||
|
||||
header := mockParserPayload(p, []byte{'F', 'A', 'B', 'C'})
|
||||
|
||||
p.scopeEnter(p.root)
|
||||
methodInfo := &opcodeTable[0x0d]
|
||||
if p.parseNamespacedObj(methodInfo, header.Length) {
|
||||
t.Fatal("expected parseNamespacedObj to return false")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("parseArg bytelist errors", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
||||
|
||||
mockParserPayload(p, nil)
|
||||
|
||||
if p.parseArg(new(opcodeInfo), new(unnamedEntity), 0, opArgByteList, 42) {
|
||||
if p.parseArg(new(opcodeInfo), entity.NewGeneric(0, 0), 0, opArgByteList, 42) {
|
||||
t.Fatal("expected parseNamespacedObj to return false")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("parseNamedRef errors", func(t *testing.T) {
|
||||
t.Run("missing args", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
p.root = entity.NewScope(entity.OpScope, 42, `\`)
|
||||
p.methodArgCount = map[string]uint8{
|
||||
"MTHD": 10,
|
||||
}
|
||||
|
||||
mockParserPayload(p, []byte{
|
||||
'M', 'T', 'H', 'D',
|
||||
byte(opIf), // Incomplete type2 opcode
|
||||
byte(entity.OpIf), // Incomplete type2 opcode
|
||||
})
|
||||
|
||||
p.scopeEnter(p.root)
|
||||
@ -358,123 +346,149 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
|
||||
t.Run("parseFieldList errors", func(t *testing.T) {
|
||||
specs := []struct {
|
||||
op opcode
|
||||
op entity.AMLOpcode
|
||||
args []interface{}
|
||||
maxReadOffset uint32
|
||||
payload []byte
|
||||
}{
|
||||
// Invalid arg count for opField
|
||||
// Invalid arg count for entity.OpField
|
||||
{
|
||||
opField,
|
||||
entity.OpField,
|
||||
nil,
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
// Wrong arg type for opField
|
||||
// Wrong arg type for entity.OpField
|
||||
{
|
||||
opField,
|
||||
entity.OpField,
|
||||
[]interface{}{0, uint64(42)},
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
opField,
|
||||
entity.OpField,
|
||||
[]interface{}{"FLD0", uint32(42)},
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
// Invalid arg count for opIndexField
|
||||
// Invalid arg count for entity.OpIndexField
|
||||
{
|
||||
opIndexField,
|
||||
entity.OpIndexField,
|
||||
nil,
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
// Wrong arg type for opIndexField
|
||||
// Wrong arg type for entity.OpIndexField
|
||||
{
|
||||
opIndexField,
|
||||
entity.OpIndexField,
|
||||
[]interface{}{0, "FLD1", "FLD2"},
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
opIndexField,
|
||||
entity.OpIndexField,
|
||||
[]interface{}{"FLD0", 0, "FLD2"},
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
opIndexField,
|
||||
entity.OpIndexField,
|
||||
[]interface{}{"FLD0", "FLD1", 0},
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
// Invalid arg count for entity.OpBankField
|
||||
{
|
||||
entity.OpBankField,
|
||||
nil,
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
// Wrong arg type for entity.OpBankField
|
||||
{
|
||||
entity.OpBankField,
|
||||
[]interface{}{0, "FLD1", "FLD2"},
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
entity.OpBankField,
|
||||
[]interface{}{"FLD0", 0, "FLD2"},
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
entity.OpBankField,
|
||||
[]interface{}{"FLD0", "FLD1", 0},
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
// unexpected EOF parsing fields
|
||||
{
|
||||
opField,
|
||||
entity.OpField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
nil,
|
||||
},
|
||||
// reserved field (0x00) with missing pkgLen
|
||||
{
|
||||
opField,
|
||||
entity.OpField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0x00},
|
||||
},
|
||||
// access field (0x01) with missing accessType
|
||||
{
|
||||
opField,
|
||||
entity.OpField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0x01},
|
||||
},
|
||||
// access field (0x01) with missing attribute byte
|
||||
{
|
||||
opField,
|
||||
entity.OpField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0x01, 0x01},
|
||||
},
|
||||
// connect field (0x02) with incomplete TermObject => Buffer arg
|
||||
{
|
||||
opField,
|
||||
entity.OpField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0x02, byte(opBuffer)},
|
||||
[]byte{0x02, byte(entity.OpBuffer)},
|
||||
},
|
||||
// extended access field (0x03) with missing ext. accessType
|
||||
{
|
||||
opField,
|
||||
entity.OpField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0x03},
|
||||
},
|
||||
// extended access field (0x03) with missing ext. attribute byte
|
||||
{
|
||||
opField,
|
||||
entity.OpField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0x03, 0x01},
|
||||
},
|
||||
// extended access field (0x03) with missing access byte count value
|
||||
{
|
||||
opField,
|
||||
entity.OpField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0x03, 0x01, 0x02},
|
||||
},
|
||||
// named field with invalid name
|
||||
{
|
||||
opField,
|
||||
entity.OpField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0xff},
|
||||
},
|
||||
// named field with invalid pkgLen
|
||||
{
|
||||
opField,
|
||||
entity.OpField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{'N', 'A', 'M', 'E'},
|
||||
@ -484,10 +498,16 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
for specIndex, spec := range specs {
|
||||
mockParserPayload(p, spec.payload)
|
||||
|
||||
if p.parseFieldList(spec.op, spec.args, spec.maxReadOffset) {
|
||||
if p.parseFieldList(entity.NewField(42), spec.maxReadOffset) {
|
||||
t.Errorf("[spec %d] expected parseFieldLis to return false", specIndex)
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("non-field entity argument", func(t *testing.T) {
|
||||
if p.parseFieldList(entity.NewDevice(42, "DEV0"), 128) {
|
||||
t.Fatal("expected parseFieldList to return false when a non-field argument is passed to it")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("parsePkgLength errors", func(t *testing.T) {
|
||||
@ -532,7 +552,7 @@ func TestParserErrorHandling(t *testing.T) {
|
||||
t.Run("parseTarget errors", func(t *testing.T) {
|
||||
t.Run("unexpected opcode", func(t *testing.T) {
|
||||
// Unexpected opcode
|
||||
mockParserPayload(p, []byte{byte(opAnd)})
|
||||
mockParserPayload(p, []byte{byte(entity.OpAnd)})
|
||||
|
||||
if _, ok := p.parseTarget(); ok {
|
||||
t.Error("expected parseTarget to return false")
|
||||
@ -591,7 +611,7 @@ func TestDetectMethodDeclarations(t *testing.T) {
|
||||
}
|
||||
|
||||
validMethod := []byte{
|
||||
byte(opMethod),
|
||||
byte(entity.OpMethod),
|
||||
5, // pkgLen
|
||||
'M', 'T', 'H', 'D',
|
||||
2, // flags (2 args)
|
||||
@ -614,7 +634,7 @@ func TestDetectMethodDeclarations(t *testing.T) {
|
||||
|
||||
t.Run("bad pkgLen", func(t *testing.T) {
|
||||
mockParserPayload(p, []byte{
|
||||
byte(opMethod),
|
||||
byte(entity.OpMethod),
|
||||
// lead byte bits (6:7) indicate 1 extra byte that is missing
|
||||
byte(1 << 6),
|
||||
})
|
||||
@ -625,7 +645,7 @@ func TestDetectMethodDeclarations(t *testing.T) {
|
||||
|
||||
t.Run("error parsing namestring", func(t *testing.T) {
|
||||
mockParserPayload(p, append([]byte{
|
||||
byte(opMethod),
|
||||
byte(entity.OpMethod),
|
||||
byte(5), // pkgLen
|
||||
10, // bogus char, not part of namestring
|
||||
}, validMethod...))
|
||||
@ -645,7 +665,7 @@ func TestDetectMethodDeclarations(t *testing.T) {
|
||||
|
||||
t.Run("error parsing method flags", func(t *testing.T) {
|
||||
mockParserPayload(p, []byte{
|
||||
byte(opMethod),
|
||||
byte(entity.OpMethod),
|
||||
byte(5), // pkgLen
|
||||
'F', 'O', 'O', 'F',
|
||||
// Missing flag byte
|
||||
@ -678,7 +698,7 @@ type mockResolver struct {
|
||||
}
|
||||
|
||||
func (m mockResolver) LookupTable(name string) *table.SDTHeader {
|
||||
pathToDumps := pkgDir() + "/../table/tabletest/"
|
||||
pathToDumps := pkgDir() + "/../../table/tabletest/"
|
||||
for _, f := range m.tableFiles {
|
||||
if !strings.Contains(f, name) {
|
||||
continue
|
||||
@ -709,3 +729,23 @@ func (f fixedPayloadResolver) LookupTable(name string) *table.SDTHeader {
|
||||
|
||||
return hdr
|
||||
}
|
||||
|
||||
func genDefaultScopes() entity.Container {
|
||||
rootNS := entity.NewScope(entity.OpScope, 42, `\`)
|
||||
rootNS.Append(entity.NewScope(entity.OpScope, 42, `_GPE`)) // General events in GPE register block
|
||||
rootNS.Append(entity.NewScope(entity.OpScope, 42, `_PR_`)) // ACPI 1.0 processor namespace
|
||||
rootNS.Append(entity.NewScope(entity.OpScope, 42, `_SB_`)) // System bus with all device objects
|
||||
rootNS.Append(entity.NewScope(entity.OpScope, 42, `_SI_`)) // System indicators
|
||||
rootNS.Append(entity.NewScope(entity.OpScope, 42, `_TZ_`)) // ACPI 1.0 thermal zone namespace
|
||||
|
||||
// Inject pre-defined OSPM objects
|
||||
rootNS.Append(namedConst(entity.NewConst(entity.OpStringPrefix, 42, "gopheros"), "_OS_"))
|
||||
rootNS.Append(namedConst(entity.NewConst(entity.OpStringPrefix, 42, uint64(2)), "_REV"))
|
||||
|
||||
return rootNS
|
||||
}
|
||||
|
||||
func namedConst(ent *entity.Const, name string) *entity.Const {
|
||||
ent.SetName(name)
|
||||
return ent
|
||||
}
|
Binary file not shown.
@ -6,112 +6,147 @@
|
||||
// virtualbox.
|
||||
DefinitionBlock ("parser-testsuite-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0x00000002)
|
||||
{
|
||||
OperationRegion (DBG0, SystemIO, 0x3000, 0x04)
|
||||
Field (DBG0, ByteAcc, NoLock, Preserve)
|
||||
{
|
||||
DHE1, 8
|
||||
}
|
||||
|
||||
Device (DRV0)
|
||||
OperationRegion (DBG0, SystemIO, 0x3000, 0x04)
|
||||
Field (DBG0, ByteAcc, NoLock, Preserve)
|
||||
{
|
||||
Name (_ADR, Ones)
|
||||
|
||||
// named entity containing qword const
|
||||
Name (H15F, 0xBADC0FEEDEADC0DE)
|
||||
Method (_GTF, 0, NotSerialized) // _GTF: Get Task File
|
||||
{
|
||||
Return (H15F)
|
||||
}
|
||||
DHE1, 8
|
||||
}
|
||||
|
||||
// example from p. 268 of ACPI 6.2 spec
|
||||
Scope(\_SB){
|
||||
OperationRegion(TOP1, GenericSerialBus, 0x00, 0x100) // GenericSerialBus device at command offset 0x00
|
||||
Device (DRV0)
|
||||
{
|
||||
Name (_ADR, Ones)
|
||||
|
||||
Name (SDB0, ResourceTemplate() {})
|
||||
Field(TOP1, BufferAcc, NoLock, Preserve){
|
||||
Connection(SDB0), // Use the Resource Descriptor defined above
|
||||
AccessAs(BufferAcc, AttribWord),
|
||||
FLD0, 8,
|
||||
FLD1, 8
|
||||
}
|
||||
// named entity containing qword const
|
||||
Name (H15F, 0xBADC0FEEDEADC0DE)
|
||||
Method (_GTF, 0, NotSerialized) // _GTF: Get Task File
|
||||
{
|
||||
Return (H15F)
|
||||
}
|
||||
}
|
||||
|
||||
Field(TOP1, BufferAcc, NoLock, Preserve){
|
||||
Connection(I2cSerialBus(0x5b,,100000,, "\\_SB",,,,RawDataBuffer(){3,9})),
|
||||
AccessAs(BufferAcc, AttribBytes(4)),
|
||||
FLD2, 8,
|
||||
AccessAs(BufferAcc, AttribRawBytes(3)),
|
||||
FLD3, 8,
|
||||
AccessAs(BufferAcc, AttribRawProcessBytes(2)),
|
||||
FLD4, 8
|
||||
}
|
||||
// example from p. 268 of ACPI 6.2 spec
|
||||
Scope(\_SB){
|
||||
OperationRegion(TOP1, GenericSerialBus, 0x00, 0x100) // GenericSerialBus device at command offset 0x00
|
||||
|
||||
Name (SDB0, ResourceTemplate() {})
|
||||
Field(TOP1, BufferAcc, NoLock, Preserve){
|
||||
Connection(SDB0), // Use the Resource Descriptor defined above
|
||||
AccessAs(BufferAcc, AttribWord),
|
||||
FLD0, 8,
|
||||
FLD1, 8
|
||||
}
|
||||
|
||||
Field(TOP1, BufferAcc, NoLock, Preserve){
|
||||
Connection(I2cSerialBus(0x5b,,100000,, "\\_SB",,,,RawDataBuffer(){3,9})),
|
||||
AccessAs(BufferAcc, AttribBytes(4)),
|
||||
FLD2, 8,
|
||||
AccessAs(BufferAcc, AttribRawBytes(3)),
|
||||
FLD3, 8,
|
||||
AccessAs(BufferAcc, AttribRawProcessBytes(2)),
|
||||
FLD4, 8
|
||||
}
|
||||
}
|
||||
|
||||
// Other entity types
|
||||
Event(HLO0)
|
||||
// Other entity types
|
||||
Event(HLO0)
|
||||
Mutex(MUT0,1)
|
||||
Signal(HLO0)
|
||||
|
||||
// Other executable bits
|
||||
Method (EXE0, 1, Serialized)
|
||||
{
|
||||
Local0 = Revision
|
||||
Local0 = Revision
|
||||
|
||||
// NameString target
|
||||
Local1 = SizeOf(GLB1)
|
||||
// NameString target
|
||||
Local1 = SizeOf(GLB1)
|
||||
|
||||
Local0 = "my-handle"
|
||||
Load(DBG0, Local0)
|
||||
Unload(Local0)
|
||||
Local0 = "my-handle"
|
||||
Load(DBG0, Local0)
|
||||
Unload(Local0)
|
||||
|
||||
// Example from p. 951 of the spec
|
||||
Store (
|
||||
LoadTable ("OEM1", "MYOEM", "TABLE1", "\\_SB.PCI0","MYD",
|
||||
Package () {0,"\\_SB.PCI0"}
|
||||
), Local0
|
||||
)
|
||||
CreateBitField(Arg0, 0, WFL0)
|
||||
if(Arg0==0){
|
||||
Return(WFL0)
|
||||
}
|
||||
|
||||
FromBCD(9, Arg0)
|
||||
ToBCD(Arg0, Local1)
|
||||
CreateByteField(Arg0, 0, WFL1)
|
||||
if(Arg0==1){
|
||||
Return(WFL1)
|
||||
}
|
||||
|
||||
Breakpoint
|
||||
Debug = "test"
|
||||
Fatal(0xf0, 0xdeadc0de, 1)
|
||||
CreateWordField(Arg0, 0, WFL2)
|
||||
if(Arg0==2){
|
||||
Return(WFL2)
|
||||
}
|
||||
|
||||
Reset(HLO0)
|
||||
CreateDwordField(Arg0, 0, WFL3)
|
||||
if(Arg0==3){
|
||||
Return(WFL3)
|
||||
}
|
||||
|
||||
// Mutex support
|
||||
Acquire(MUT0, 0xffff) // no timeout
|
||||
Release(MUT0)
|
||||
CreateQwordField(Arg0, 0, WFL4)
|
||||
if(Arg0==4){
|
||||
Return(WFL4)
|
||||
}
|
||||
|
||||
// Signal/Wait
|
||||
Wait(HLO0, 0xffff)
|
||||
CreateField(Arg0, 0, 13, WFL5)
|
||||
if(Arg0==5){
|
||||
Return(WFL5)
|
||||
}
|
||||
|
||||
// Get monotonic timer value
|
||||
Local0 = Timer
|
||||
// Example from p. 951 of the spec
|
||||
Store (
|
||||
LoadTable ("OEM1", "MYOEM", "TABLE1", "\\_SB.PCI0","MYD",
|
||||
Package () {0,"\\_SB.PCI0"}
|
||||
), Local0
|
||||
)
|
||||
|
||||
CopyObject(Local0, Local1)
|
||||
Return(ObjectType(Local1))
|
||||
FromBCD(9, Arg0)
|
||||
ToBCD(Arg0, Local1)
|
||||
|
||||
Breakpoint
|
||||
Debug = "test"
|
||||
Fatal(0xf0, 0xdeadc0de, 1)
|
||||
|
||||
Reset(HLO0)
|
||||
|
||||
// Mutex support
|
||||
Acquire(MUT0, 0xffff) // no timeout
|
||||
Release(MUT0)
|
||||
|
||||
// Signal/Wait
|
||||
Wait(HLO0, 0xffff)
|
||||
|
||||
// Get monotonic timer value
|
||||
Local0 = Timer
|
||||
|
||||
CopyObject(Local0, Local1)
|
||||
Return(ObjectType(Local1))
|
||||
}
|
||||
|
||||
// Misc regions
|
||||
// Misc regions
|
||||
|
||||
// BankField example from p. 899 of the spec
|
||||
// Define a 256-byte operational region in SystemIO space and name it GIO0
|
||||
OperationRegion (GIO0, SystemIO, 0x125, 0x100)
|
||||
// BankField example from p. 899 of the spec
|
||||
// Define a 256-byte operational region in SystemIO space and name it GIO0
|
||||
OperationRegion (GIO0, SystemIO, 0x125, 0x100)
|
||||
Field (GIO0, ByteAcc, NoLock, Preserve) {
|
||||
GLB1, 1,
|
||||
GLB1, 1,
|
||||
GLB2, 1,
|
||||
Offset (1), // Move to offset for byte 1
|
||||
BNK1, 4
|
||||
}
|
||||
|
||||
BankField (GIO0, BNK1, 0, ByteAcc, NoLock, Preserve) {
|
||||
Offset (0x30),
|
||||
FET0, 1,
|
||||
FET1, 1
|
||||
}
|
||||
BankField (GIO0, BNK1, 0, ByteAcc, NoLock, Preserve) {
|
||||
Offset (0x30),
|
||||
FET0, 1,
|
||||
FET1, 1
|
||||
}
|
||||
|
||||
// Data Region
|
||||
DataTableRegion (REG0, "FOOF", "BAR", "BAZ")
|
||||
// Data Region
|
||||
DataTableRegion (REG0, "FOOF", "BAR", "BAZ")
|
||||
|
||||
// Other resources
|
||||
Processor(CPU0, 1, 0x120, 6){}
|
||||
PowerResource(PWR0, 0, 0){}
|
||||
ThermalZone(TZ0){}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user