1
0
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:
Achilleas Anagnostopoulos 2017-12-29 09:12:08 +00:00
parent 12ad177381
commit b278ce626b
10 changed files with 441 additions and 412 deletions

View File

@ -47,6 +47,7 @@ type Container interface {
Last() Entity Last() Entity
} }
// FieldAccessTypeProvider is an interface implemented by all field entities.
type FieldAccessTypeProvider interface { type FieldAccessTypeProvider interface {
// DefaultAccessType returns the default FieldAccessType for any field unit // DefaultAccessType returns the default FieldAccessType for any field unit
// defined by this field. // defined by this field.

View File

@ -79,6 +79,19 @@ func TestEntityMethods(t *testing.T) {
t.Fatalf("expected parent not to have any child nodes; got %d", got) 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) { func TestEntityArgAssignment(t *testing.T) {
@ -160,6 +173,24 @@ func TestEntityArgAssignment(t *testing.T) {
[]interface{}{NewConst(OpDwordPrefix, 2, uint64(42))}, []interface{}{NewConst(OpDwordPrefix, 2, uint64(42))},
false, 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: nextSpec:

View File

@ -6,6 +6,7 @@ package entity
// representation of the opcode values. // representation of the opcode values.
type AMLOpcode uint16 type AMLOpcode uint16
// List of AML opcodes
const ( const (
// Regular opcode list // Regular opcode list
OpZero = AMLOpcode(0x00) OpZero = AMLOpcode(0x00)
@ -427,14 +428,3 @@ func OpIsType2(op AMLOpcode) bool {
return false 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
}
}

View File

@ -227,14 +227,6 @@ func TestOpcodeIsX(t *testing.T) {
{OpPackage, OpIsDataObject, true}, {OpPackage, OpIsDataObject, true},
{OpVarPackage, OpIsDataObject, true}, {OpVarPackage, OpIsDataObject, true},
{OpLor, OpIsDataObject, false}, {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 { for specIndex, spec := range specs {

View File

@ -8,7 +8,7 @@ func TestScopeVisit(t *testing.T) {
stopRecursing := func(Entity) bool { return false } stopRecursing := func(Entity) bool { return false }
// Append special entities under IDE0 // Append special entities under IDE0
root := NewScope(tableHandle, "IDE0") root := NewScope(OpScope, tableHandle, "IDE0")
root.Append(NewDevice(tableHandle, "DEV0")) root.Append(NewDevice(tableHandle, "DEV0"))
root.Append(NewProcessor(tableHandle, "FOO0")) root.Append(NewProcessor(tableHandle, "FOO0"))
root.Append(NewProcessor(tableHandle, "FOO0")) root.Append(NewProcessor(tableHandle, "FOO0"))

View File

@ -55,7 +55,7 @@ const (
// is returns true if f is set in this opFlag. // is returns true if f is set in this opFlag.
func (fl opFlag) is(f opFlag) bool { 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. // opArgFlags encodes up to 7 opArgFlag values in a uint64 value.
@ -99,7 +99,6 @@ const (
opArgTermList opArgTermList
opArgTermObj opArgTermObj
opArgByteList opArgByteList
opArgPackage
opArgString opArgString
opArgByteData opArgByteData
opArgWord opArgWord

View File

@ -1,6 +1,7 @@
package aml package parser
import ( import (
"gopheros/device/acpi/aml/entity"
"gopheros/device/acpi/table" "gopheros/device/acpi/table"
"gopheros/kernel" "gopheros/kernel"
"gopheros/kernel/kfmt" "gopheros/kernel/kfmt"
@ -17,8 +18,8 @@ var (
type Parser struct { type Parser struct {
r amlStreamReader r amlStreamReader
errWriter io.Writer errWriter io.Writer
root ScopeEntity root entity.Container
scopeStack []ScopeEntity scopeStack []entity.Container
tableName string tableName string
tableHandle uint8 tableHandle uint8
@ -30,7 +31,7 @@ type Parser struct {
} }
// NewParser returns a new AML parser instance. // 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{ return &Parser{
errWriter: errWriter, errWriter: errWriter,
root: rootEntity, root: rootEntity,
@ -65,25 +66,29 @@ func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDT
} }
p.scopeExit() p.scopeExit()
// Pass 3: check parents and resolve forward references // Pass 3: check parents and resolve symbol references
var resolveFailed bool 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 // 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 return false
} }
// Populate parents for any entity args that are also entities but are not // 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). // linked to a parent (e.g. a package inside a named entity).
for _, arg := range ent.getArgs() { for _, arg := range ent.Args() {
if argEnt, isArgEnt := arg.(Entity); isArgEnt && argEnt.Parent() == nil { if argEnt, isArgEnt := arg.(entity.Entity); isArgEnt && argEnt.Parent() == nil {
argEnt.setParent(ent.Parent()) argEnt.SetParent(ent.Parent())
} }
} }
if res, ok := ent.(resolver); ok && !res.Resolve(p.errWriter, p.root) { // Resolve any symbol references
resolveFailed = true if lazyRef, ok := ent.(entity.LazyRefResolver); ok {
return false if err := lazyRef.ResolveSymbolRefs(p.root); err != nil {
kfmt.Fprintf(p.errWriter, "%s\n", err.Message)
resolveFailed = true
return false
}
} }
return true return true
@ -117,7 +122,7 @@ func (p *Parser) detectMethodDeclarations() {
continue continue
} }
if next.op != opMethod { if next.op != entity.OpMethod {
continue 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 // If we encounter a named scope we need to look it up and parse the arg list relative to it
switch info.op { switch {
case opScope: case info.op == entity.OpScope:
return p.parseScope(curOffset + pkgLen) return p.parseScope(curOffset + pkgLen)
case opDevice, opMethod: case info.flags.is(opFlagNamed | opFlagScoped):
return p.parseNamespacedObj(info.op, curOffset+pkgLen) return p.parseNamespacedObj(info, curOffset+pkgLen)
} }
// Create appropriate object for opcode type and attach it to current scope unless it is // 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. // finalizeObj applies post-parse logic for special object types.
func (p *Parser) finalizeObj(op opcode, obj Entity) bool { func (p *Parser) finalizeObj(op entity.AMLOpcode, obj entity.Entity) bool {
obj.setTableHandle(p.tableHandle)
switch op { switch op {
case opElse: case entity.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
curScope := p.scopeCurrent() curScope := p.scopeCurrent()
curScope.removeChild(curScope.lastChild()) curScope.Remove(curScope.Last())
prevObj := curScope.lastChild() prevObj := curScope.Last()
if prevObj.getOpcode() != opIf { 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()) 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
} }
// If predicate(0) then(1) else(2) // If predicate(0) then(1) else(2)
prevObj.setArg(2, obj) 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
})
} }
return true return true
@ -262,14 +256,14 @@ func (p *Parser) parseScope(maxReadOffset uint32) bool {
return false return false
} }
target := scopeFind(p.scopeCurrent(), p.root, name) target := entity.FindInScope(p.scopeCurrent(), p.root, name)
if target == nil { if target == nil {
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] undefined scope: %s\n", p.tableName, p.r.Offset(), name) kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] undefined scope: %s\n", p.tableName, p.r.Offset(), name)
return false return false
} }
switch target.getOpcode() { switch target.Opcode() {
case opDevice, opProcessor, opThermalZone, opPowerRes: case entity.OpDevice, entity.OpProcessor, entity.OpThermalZone, entity.OpPowerRes:
// ok // ok
default: default:
// Only allow if this is a named scope // 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) ok = p.parseObjList(maxReadOffset)
p.scopeExit() p.scopeExit()
@ -287,48 +281,50 @@ 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 device or method (depending on the opcode) object to the // attaches the appropriate object depending on the opcode to the correct
// correct parent scope, enters the device scope and parses the object list // parent scope and then parses any contained objects. The contained objects
// contained in the device definition. // will be appended inside the newly constructed scope.
func (p *Parser) parseNamespacedObj(op opcode, maxReadOffset uint32) bool { func (p *Parser) parseNamespacedObj(info *opcodeInfo, maxReadOffset uint32) bool {
scopeExpr, ok := p.parseNameString() scopeExpr, ok := p.parseNameString()
if !ok { if !ok {
return false return false
} }
parent, name := scopeResolvePath(p.scopeCurrent(), p.root, scopeExpr) parent, name := entity.ResolveScopedPath(p.scopeCurrent(), p.root, scopeExpr)
if parent == nil { 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()) 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 return false
} }
var obj ScopeEntity var obj entity.Container
switch op { switch info.op {
case opDevice: case entity.OpDevice:
obj = &Device{scopeEntity: scopeEntity{name: name}} obj = entity.NewDevice(p.tableHandle, name)
case opMethod: case entity.OpProcessor:
m := &Method{scopeEntity: scopeEntity{name: name}} obj = entity.NewProcessor(p.tableHandle, name)
case entity.OpPowerRes:
flags, flagOk := p.parseNumConstant(1) obj = entity.NewPowerResource(p.tableHandle, name)
if !flagOk { case entity.OpThermalZone:
return false obj = entity.NewThermalZone(p.tableHandle, name)
} case entity.OpMethod:
m.argCount = (uint8(flags) & 0x7) // bits[0:2] obj = entity.NewMethod(p.tableHandle, name)
m.serialized = (uint8(flags)>>3)&0x1 == 0x1 // bit 3 default:
m.syncLevel = (uint8(flags) >> 4) & 0xf // bits[4:7] 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
obj = m
} }
// Parse any args that follow the name. The last arg is always an ArgTermList
parent.Append(obj) parent.Append(obj)
p.scopeEnter(obj) for argIndex := uint8(1); argIndex < info.argFlags.argCount(); argIndex++ {
ok = p.parseObjList(maxReadOffset) if !p.parseArg(info, obj, argIndex, info.argFlags.arg(argIndex), maxReadOffset) {
p.scopeExit() 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 ( var (
arg interface{} arg interface{}
ok bool 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 // If object is a scoped entity enter it's scope before parsing
// the term list. Otherwise, create an unnamed scope, attach it // the term list. Otherwise, create an unnamed scope, attach it
// as the next argument to obj and enter that. // 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) p.scopeEnter(s)
} else { } 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) p.scopeEnter(ns)
obj.setArg(argIndex, ns) obj.SetArg(argIndex, ns)
} }
ok = p.parseObjList(maxReadOffset) ok = p.parseObjList(maxReadOffset)
p.scopeExit() p.scopeExit()
return ok return ok
case opArgFieldList: case opArgFieldList:
return p.parseFieldList(info.op, obj.getArgs(), maxReadOffset) return p.parseFieldList(obj, maxReadOffset)
case opArgByteList: case opArgByteList:
var bl []byte var bl []byte
for p.r.Offset() < maxReadOffset { for p.r.Offset() < maxReadOffset {
@ -388,45 +385,68 @@ func (p *Parser) parseArg(info *opcodeInfo, obj Entity, argIndex uint8, argType
return false 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 { if ok := p.parseObj(); !ok {
return nil, false return nil, false
} }
curScope := p.scopeCurrent() curScope := p.scopeCurrent()
obj := curScope.lastChild() obj := curScope.Last()
curScope.removeChild(obj) curScope.Remove(obj)
return obj, true return obj, true
} }
func (p *Parser) makeObjForOpcode(info *opcodeInfo) Entity { func (p *Parser) makeObjForOpcode(info *opcodeInfo) entity.Entity {
var obj Entity var obj entity.Entity
switch { switch {
case info.op == opOpRegion: case info.op == entity.OpOpRegion:
obj = new(regionEntity) obj = entity.NewRegion(p.tableHandle)
case info.op == opBuffer: case info.op == entity.OpBuffer:
obj = new(bufferEntity) obj = entity.NewBuffer(p.tableHandle)
case info.op == opMutex: case info.op == entity.OpMutex:
obj = new(mutexEntity) obj = entity.NewMutex(p.tableHandle)
case info.op == opEvent: case info.op == entity.OpEvent:
obj = new(eventEntity) obj = entity.NewEvent(p.tableHandle)
case opIsBufferField(info.op): case info.op == entity.OpField:
obj = new(bufferFieldEntity) 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): 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): case info.flags.is(opFlagScoped):
obj = new(scopeEntity) obj = entity.NewScope(info.op, p.tableHandle, "")
case info.flags.is(opFlagNamed): case info.flags.is(opFlagNamed):
obj = new(namedEntity) obj = entity.NewGenericNamed(info.op, p.tableHandle)
default: default:
obj = new(unnamedEntity) obj = entity.NewGeneric(info.op, p.tableHandle)
} }
obj.setOpcode(info.op)
return obj return obj
} }
@ -447,7 +467,7 @@ func (p *Parser) parseNamedRef() bool {
var ( var (
curOffset uint32 curOffset uint32
argIndex uint8 argIndex uint8
arg Entity arg entity.Entity
argList []interface{} argList []interface{}
) )
@ -459,14 +479,14 @@ func (p *Parser) parseNamedRef() bool {
p.r.SetOffset(curOffset) p.r.SetOffset(curOffset)
switch { 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() arg, ok = p.parseArgObj()
default: default:
// It may be a nested invocation or named ref // It may be a nested invocation or named ref
ok = p.parseNamedRef() ok = p.parseNamedRef()
if ok { if ok {
arg = p.scopeCurrent().lastChild() arg = p.scopeCurrent().Last()
p.scopeCurrent().removeChild(arg) p.scopeCurrent().Remove(arg)
} }
} }
@ -486,14 +506,11 @@ func (p *Parser) parseNamedRef() bool {
return false return false
} }
return p.scopeCurrent().Append(&methodInvocationEntity{ return p.scopeCurrent().Append(entity.NewInvocation(p.tableHandle, name))
unnamedEntity: unnamedEntity{args: argList},
methodName: name,
})
} }
// Otherwise this is a reference to a named entity // 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) { func (p *Parser) nextOpcode() (*opcodeInfo, bool) {
@ -533,69 +550,30 @@ func (p *Parser) nextOpcode() (*opcodeInfo, bool) {
// AccessField := 0x1 AccessType AccessAttrib // AccessField := 0x1 AccessType AccessAttrib
// ConnectField := 0x02 NameString | 0x02 BufferData // ConnectField := 0x02 NameString | 0x02 BufferData
// ExtendedAccessField := 0x3 AccessType ExtendedAccessType AccessLength // 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 ( var (
// for fieldUnit, name0 is the region name and name1 is not used; ok bool
// for indexField,
name0, name1 string
flags uint64
ok bool accessType entity.FieldAccessType
bitWidth uint32
curBitOffset uint32
accessAttrib FieldAccessAttrib
accessByteCount uint8
unitName string
)
switch op { bitWidth uint32
case opField: // Field := PkgLength Region AccessFlags FieldList curBitOffset uint32
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 (
connectionName string 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 { for p.r.Offset() < maxReadOffset {
next, err := p.r.ReadByte() next, err := p.r.ReadByte()
if err != nil { if err != nil {
@ -616,7 +594,7 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin
if err != nil { if err != nil {
return false 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() attrib, err := p.r.ReadByte()
if err != nil { if err != nil {
@ -626,7 +604,7 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin
// To specify AccessAttribBytes, RawBytes and RawProcessBytes // To specify AccessAttribBytes, RawBytes and RawProcessBytes
// the ASL compiler will emit an ExtendedAccessField opcode. // the ASL compiler will emit an ExtendedAccessField opcode.
accessByteCount = 0 accessByteCount = 0
accessAttrib = FieldAccessAttrib(attrib) accessAttrib = entity.FieldAccessAttrib(attrib)
continue continue
case 0x2: // ConnectField => <0x2> NameString> | <0x02> TermObj => Buffer 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 { if err != nil {
return false 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() extAccessAttrib, err := p.r.ReadByte()
if err != nil { if err != nil {
@ -657,11 +635,11 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin
switch extAccessAttrib { switch extAccessAttrib {
case 0x0b: case 0x0b:
accessAttrib = FieldAccessAttribBytes accessAttrib = entity.FieldAccessAttribBytes
case 0xe: case 0xe:
accessAttrib = FieldAccessAttribRawBytes accessAttrib = entity.FieldAccessAttribRawBytes
case 0x0f: case 0x0f:
accessAttrib = FieldAccessAttribRawProcessBytes accessAttrib = entity.FieldAccessAttribRawProcessBytes
} }
default: // NamedField default: // NamedField
_ = p.r.UnreadByte() _ = 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 // According to the spec, the field elements are should
// be visible at the same scope as the Field/IndexField // be visible at the same scope as the Field that declares them
switch op { unit := entity.NewFieldUnit(p.tableHandle, unitName)
case opField: unit.Field = fieldEnt
p.scopeCurrent().Append(&fieldUnitEntity{ unit.AccessType = accessType
fieldEntity: fieldEntity{ unit.AccessAttrib = accessAttrib
namedEntity: namedEntity{ unit.ByteCount = accessByteCount
tableHandle: p.tableHandle, unit.BitOffset = curBitOffset
op: op, unit.BitWidth = bitWidth
name: unitName, unit.ConnectionName = connectionName
}, unit.Connection = resolvedConnection
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,
})
}
p.scopeCurrent().Append(unit)
curBitOffset += bitWidth curBitOffset += bitWidth
} }
} }
return ok && p.r.Offset() == maxReadOffset return ok && p.r.Offset() == maxReadOffset
@ -859,10 +802,8 @@ func (p *Parser) parseSimpleName() (interface{}, bool) {
var obj interface{} var obj interface{}
switch { switch {
case ok && nextOpcode.op >= opLocal0 && nextOpcode.op <= opLocal7: case ok && entity.OpIsArg(nextOpcode.op):
obj, ok = &unnamedEntity{op: nextOpcode.op}, true obj, ok = entity.NewGeneric(nextOpcode.op, p.tableHandle), true
case ok && nextOpcode.op >= opArg0 && nextOpcode.op <= opArg6:
obj, ok = &unnamedEntity{op: nextOpcode.op}, true
default: default:
// Rewind and try parsing as NameString // Rewind and try parsing as NameString
p.r.SetOffset(curOffset) p.r.SetOffset(curOffset)
@ -890,10 +831,10 @@ func (p *Parser) parseTarget() (interface{}, bool) {
if ok { if ok {
switch { 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) p.r.SetOffset(curOffset + 1)
return &constEntity{op: opStringPrefix, val: ""}, true return entity.NewConst(entity.OpStringPrefix, p.tableHandle, ""), true
case opIsArg(nextOpcode.op) || nextOpcode.op == opRefOf || nextOpcode.op == opDerefOf || nextOpcode.op == opIndex || nextOpcode.op == opDebug: // LocalObj | ArgObj | Type6 | DebugObj 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: default:
// Unexpected opcode // Unexpected opcode
return nil, false 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. // In this case, this is either a NameString or a control method invocation.
if ok := p.parseNamedRef(); ok { if ok := p.parseNamedRef(); ok {
obj := p.scopeCurrent().lastChild() obj := p.scopeCurrent().Last()
p.scopeCurrent().removeChild(obj) p.scopeCurrent().Remove(obj)
return obj, ok return obj, ok
} }
@ -996,12 +937,12 @@ func (p *Parser) parseNameString() (string, bool) {
} }
// scopeCurrent returns the currently active scope. // scopeCurrent returns the currently active scope.
func (p *Parser) scopeCurrent() ScopeEntity { func (p *Parser) scopeCurrent() entity.Container {
return p.scopeStack[len(p.scopeStack)-1] return p.scopeStack[len(p.scopeStack)-1]
} }
// scopeEnter enters the given scope. // scopeEnter enters the given scope.
func (p *Parser) scopeEnter(s ScopeEntity) { func (p *Parser) scopeEnter(s entity.Container) {
p.scopeStack = append(p.scopeStack, s) p.scopeStack = append(p.scopeStack, s)
} }

View File

@ -1,6 +1,7 @@
package aml package parser
import ( import (
"gopheros/device/acpi/aml/entity"
"gopheros/device/acpi/table" "gopheros/device/acpi/table"
"io/ioutil" "io/ioutil"
"os" "os"
@ -23,19 +24,7 @@ func TestParser(t *testing.T) {
tableFiles: spec, tableFiles: spec,
} }
// Create default scopes p := NewParser(os.Stderr, genDefaultScopes())
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)
for _, tableName := range spec { for _, tableName := range spec {
tableName = strings.Replace(tableName, ".aml", "", -1) tableName = strings.Replace(tableName, ".aml", "", -1)
@ -50,25 +39,18 @@ func TestParser(t *testing.T) {
func TestTableHandleAssignment(t *testing.T) { func TestTableHandleAssignment(t *testing.T) {
var resolver = mockResolver{tableFiles: []string{"parser-testsuite-DSDT.aml"}} var resolver = mockResolver{tableFiles: []string{"parser-testsuite-DSDT.aml"}}
// Create default scopes rootNS := genDefaultScopes()
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, rootNS)
expHandle := uint8(42) expHandle := uint8(0x0f)
tableName := "parser-testsuite-DSDT" tableName := "parser-testsuite-DSDT"
if err := p.ParseAML(expHandle, tableName, resolver.LookupTable(tableName)); err != nil { if err := p.ParseAML(expHandle, tableName, resolver.LookupTable(tableName)); err != nil {
t.Error(err) t.Error(err)
} }
// Drop all entities that were assigned the handle value // Drop all entities that were assigned the handle value
var unloadList []Entity var unloadList []entity.Entity
scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool { entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool {
if ent.TableHandle() == expHandle { if ent.TableHandle() == expHandle {
unloadList = append(unloadList, ent) unloadList = append(unloadList, ent)
return false return false
@ -78,13 +60,13 @@ func TestTableHandleAssignment(t *testing.T) {
for _, ent := range unloadList { for _, ent := range unloadList {
if p := ent.Parent(); p != nil { if p := ent.Parent(); p != nil {
p.removeChild(ent) p.Remove(ent)
} }
} }
// We should end up with the original tree // We should end up with the original tree
var visitedNodes int 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++ visitedNodes++
if ent.TableHandle() == expHandle { if ent.TableHandle() == expHandle {
t.Errorf("encountered entity that should have been pruned: %#+v", ent) 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"}, tableFiles: []string{"parser-testsuite-fwd-decls-DSDT.aml"},
} }
// Create default scopes p := NewParser(ioutil.Discard, genDefaultScopes())
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)
for _, tableName := range resolver.tableFiles { for _, tableName := range resolver.tableFiles {
tableName = strings.Replace(tableName, ".aml", "", -1) 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("ParseAML errors", func(t *testing.T) {
t.Run("parseObjList error", 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 // Setup resolver to serve an AML stream containing an invalid opcode
header := mockParserPayload(p, []byte{0x5b, 0x00}) header := mockParserPayload(p, []byte{0x5b, 0x00})
@ -190,12 +164,10 @@ func TestParserErrorHandling(t *testing.T) {
}) })
t.Run("unresolved entities", func(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 // Inject a reference entity to the tree
p.root.Append(&namedReference{ p.root.Append(entity.NewReference(42, "UNKNOWN"))
targetName: "UNKNOWN",
})
// Setup resolver to serve an empty AML stream // Setup resolver to serve an empty AML stream
header := mockParserPayload(p, nil) header := mockParserPayload(p, nil)
@ -208,11 +180,11 @@ func TestParserErrorHandling(t *testing.T) {
t.Run("parseObj errors", func(t *testing.T) { t.Run("parseObj errors", func(t *testing.T) {
t.Run("parsePkgLength error", 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 // Setup resolver to serve an AML stream containing an incomplete
// buffer specification // 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 { if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
t.Fatal("expected parsePkgLength to return an error") 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) { 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 // Setup resolver to serve an AML stream containing an incomplete
// buffer arglist specification // 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 { if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
t.Fatal("expected parsePkgLength to return an error") 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("finalizeObj errors", func(t *testing.T) {
t.Run("else without matching if", func(t *testing.T) { t.Run("else without matching if", func(t *testing.T) {
p.root = &scopeEntity{op: opScope, name: `\`} p.root = entity.NewScope(entity.OpScope, 42, `\`)
p.root.Append(&constEntity{val: 0x42}) p.root.Append(entity.NewConst(entity.OpDwordPrefix, 42, uint64(0x42)))
p.root.Append(&scopeEntity{op: opElse})
// Setup resolver to serve an AML stream containing an // Setup resolver to serve an AML stream containing an
// empty else statement without a matching if // 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 { if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
t.Fatal("expected finalizeObj to return an error") 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("parseScope errors", func(t *testing.T) {
t.Run("parseNameString error", 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{ header := mockParserPayload(p, []byte{
byte(opScope), byte(entity.OpScope),
0x10, // pkglen 0x10, // pkglen
}) })
@ -264,10 +235,10 @@ func TestParserErrorHandling(t *testing.T) {
}) })
t.Run("unknown scope", func(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{ header := mockParserPayload(p, []byte{
byte(opScope), byte(entity.OpScope),
0x10, // pkglen 0x10, // pkglen
'F', 'O', 'O', 'F', 'F', 'O', 'O', 'F',
}) })
@ -278,10 +249,10 @@ func TestParserErrorHandling(t *testing.T) {
}) })
t.Run("nameless scope", func(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{ header := mockParserPayload(p, []byte{
byte(opScope), byte(entity.OpScope),
0x02, // pkglen 0x02, // pkglen
'\\', // scope name: "\" (root scope) '\\', // scope name: "\" (root scope)
0x00, // null string 0x00, // null string
@ -295,58 +266,75 @@ func TestParserErrorHandling(t *testing.T) {
t.Run("parseNamespacedObj errors", func(t *testing.T) { t.Run("parseNamespacedObj errors", func(t *testing.T) {
t.Run("parseNameString error", 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) mockParserPayload(p, nil)
if p.parseNamespacedObj(opDevice, 10) { devInfo := &opcodeTable[0x6a]
if p.parseNamespacedObj(devInfo, 10) {
t.Fatal("expected parseNamespacedObj to return false") t.Fatal("expected parseNamespacedObj to return false")
} }
}) })
t.Run("scope lookup error", func(t *testing.T) { 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'}) header := mockParserPayload(p, []byte{'^', 'F', 'A', 'B', 'C'})
p.scopeEnter(p.root) 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.Fatal("expected parseNamespacedObj to return false")
} }
}) })
t.Run("error parsing method arg count", func(t *testing.T) { t.Run("unsupported namespaced entity", 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'}) header := mockParserPayload(p, []byte{'F', 'A', 'B', 'C'})
p.scopeEnter(p.root) 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.Fatal("expected parseNamespacedObj to return false")
} }
}) })
}) })
t.Run("parseArg bytelist errors", func(t *testing.T) { 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) 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.Fatal("expected parseNamespacedObj to return false")
} }
}) })
t.Run("parseNamedRef errors", func(t *testing.T) { t.Run("parseNamedRef errors", func(t *testing.T) {
t.Run("missing args", 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{ p.methodArgCount = map[string]uint8{
"MTHD": 10, "MTHD": 10,
} }
mockParserPayload(p, []byte{ mockParserPayload(p, []byte{
'M', 'T', 'H', 'D', 'M', 'T', 'H', 'D',
byte(opIf), // Incomplete type2 opcode byte(entity.OpIf), // Incomplete type2 opcode
}) })
p.scopeEnter(p.root) p.scopeEnter(p.root)
@ -358,123 +346,149 @@ func TestParserErrorHandling(t *testing.T) {
t.Run("parseFieldList errors", func(t *testing.T) { t.Run("parseFieldList errors", func(t *testing.T) {
specs := []struct { specs := []struct {
op opcode op entity.AMLOpcode
args []interface{} args []interface{}
maxReadOffset uint32 maxReadOffset uint32
payload []byte payload []byte
}{ }{
// Invalid arg count for opField // Invalid arg count for entity.OpField
{ {
opField, entity.OpField,
nil, nil,
0, 0,
nil, nil,
}, },
// Wrong arg type for opField // Wrong arg type for entity.OpField
{ {
opField, entity.OpField,
[]interface{}{0, uint64(42)}, []interface{}{0, uint64(42)},
0, 0,
nil, nil,
}, },
{ {
opField, entity.OpField,
[]interface{}{"FLD0", uint32(42)}, []interface{}{"FLD0", uint32(42)},
0, 0,
nil, nil,
}, },
// Invalid arg count for opIndexField // Invalid arg count for entity.OpIndexField
{ {
opIndexField, entity.OpIndexField,
nil, nil,
0, 0,
nil, nil,
}, },
// Wrong arg type for opIndexField // Wrong arg type for entity.OpIndexField
{ {
opIndexField, entity.OpIndexField,
[]interface{}{0, "FLD1", "FLD2"}, []interface{}{0, "FLD1", "FLD2"},
0, 0,
nil, nil,
}, },
{ {
opIndexField, entity.OpIndexField,
[]interface{}{"FLD0", 0, "FLD2"}, []interface{}{"FLD0", 0, "FLD2"},
0, 0,
nil, 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}, []interface{}{"FLD0", "FLD1", 0},
0, 0,
nil, nil,
}, },
// unexpected EOF parsing fields // unexpected EOF parsing fields
{ {
opField, entity.OpField,
[]interface{}{"FLD0", uint64(42)}, []interface{}{"FLD0", uint64(42)},
128, 128,
nil, nil,
}, },
// reserved field (0x00) with missing pkgLen // reserved field (0x00) with missing pkgLen
{ {
opField, entity.OpField,
[]interface{}{"FLD0", uint64(42)}, []interface{}{"FLD0", uint64(42)},
128, 128,
[]byte{0x00}, []byte{0x00},
}, },
// access field (0x01) with missing accessType // access field (0x01) with missing accessType
{ {
opField, entity.OpField,
[]interface{}{"FLD0", uint64(42)}, []interface{}{"FLD0", uint64(42)},
128, 128,
[]byte{0x01}, []byte{0x01},
}, },
// access field (0x01) with missing attribute byte // access field (0x01) with missing attribute byte
{ {
opField, entity.OpField,
[]interface{}{"FLD0", uint64(42)}, []interface{}{"FLD0", uint64(42)},
128, 128,
[]byte{0x01, 0x01}, []byte{0x01, 0x01},
}, },
// connect field (0x02) with incomplete TermObject => Buffer arg // connect field (0x02) with incomplete TermObject => Buffer arg
{ {
opField, entity.OpField,
[]interface{}{"FLD0", uint64(42)}, []interface{}{"FLD0", uint64(42)},
128, 128,
[]byte{0x02, byte(opBuffer)}, []byte{0x02, byte(entity.OpBuffer)},
}, },
// extended access field (0x03) with missing ext. accessType // extended access field (0x03) with missing ext. accessType
{ {
opField, entity.OpField,
[]interface{}{"FLD0", uint64(42)}, []interface{}{"FLD0", uint64(42)},
128, 128,
[]byte{0x03}, []byte{0x03},
}, },
// extended access field (0x03) with missing ext. attribute byte // extended access field (0x03) with missing ext. attribute byte
{ {
opField, entity.OpField,
[]interface{}{"FLD0", uint64(42)}, []interface{}{"FLD0", uint64(42)},
128, 128,
[]byte{0x03, 0x01}, []byte{0x03, 0x01},
}, },
// extended access field (0x03) with missing access byte count value // extended access field (0x03) with missing access byte count value
{ {
opField, entity.OpField,
[]interface{}{"FLD0", uint64(42)}, []interface{}{"FLD0", uint64(42)},
128, 128,
[]byte{0x03, 0x01, 0x02}, []byte{0x03, 0x01, 0x02},
}, },
// named field with invalid name // named field with invalid name
{ {
opField, entity.OpField,
[]interface{}{"FLD0", uint64(42)}, []interface{}{"FLD0", uint64(42)},
128, 128,
[]byte{0xff}, []byte{0xff},
}, },
// named field with invalid pkgLen // named field with invalid pkgLen
{ {
opField, entity.OpField,
[]interface{}{"FLD0", uint64(42)}, []interface{}{"FLD0", uint64(42)},
128, 128,
[]byte{'N', 'A', 'M', 'E'}, []byte{'N', 'A', 'M', 'E'},
@ -484,10 +498,16 @@ func TestParserErrorHandling(t *testing.T) {
for specIndex, spec := range specs { for specIndex, spec := range specs {
mockParserPayload(p, spec.payload) 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.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) { 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("parseTarget errors", func(t *testing.T) {
t.Run("unexpected opcode", func(t *testing.T) { t.Run("unexpected opcode", func(t *testing.T) {
// Unexpected opcode // Unexpected opcode
mockParserPayload(p, []byte{byte(opAnd)}) mockParserPayload(p, []byte{byte(entity.OpAnd)})
if _, ok := p.parseTarget(); ok { if _, ok := p.parseTarget(); ok {
t.Error("expected parseTarget to return false") t.Error("expected parseTarget to return false")
@ -591,7 +611,7 @@ func TestDetectMethodDeclarations(t *testing.T) {
} }
validMethod := []byte{ validMethod := []byte{
byte(opMethod), byte(entity.OpMethod),
5, // pkgLen 5, // pkgLen
'M', 'T', 'H', 'D', 'M', 'T', 'H', 'D',
2, // flags (2 args) 2, // flags (2 args)
@ -614,7 +634,7 @@ func TestDetectMethodDeclarations(t *testing.T) {
t.Run("bad pkgLen", func(t *testing.T) { t.Run("bad pkgLen", func(t *testing.T) {
mockParserPayload(p, []byte{ mockParserPayload(p, []byte{
byte(opMethod), byte(entity.OpMethod),
// lead byte bits (6:7) indicate 1 extra byte that is missing // lead byte bits (6:7) indicate 1 extra byte that is missing
byte(1 << 6), byte(1 << 6),
}) })
@ -625,7 +645,7 @@ func TestDetectMethodDeclarations(t *testing.T) {
t.Run("error parsing namestring", func(t *testing.T) { t.Run("error parsing namestring", func(t *testing.T) {
mockParserPayload(p, append([]byte{ mockParserPayload(p, append([]byte{
byte(opMethod), byte(entity.OpMethod),
byte(5), // pkgLen byte(5), // pkgLen
10, // bogus char, not part of namestring 10, // bogus char, not part of namestring
}, validMethod...)) }, validMethod...))
@ -645,7 +665,7 @@ func TestDetectMethodDeclarations(t *testing.T) {
t.Run("error parsing method flags", func(t *testing.T) { t.Run("error parsing method flags", func(t *testing.T) {
mockParserPayload(p, []byte{ mockParserPayload(p, []byte{
byte(opMethod), byte(entity.OpMethod),
byte(5), // pkgLen byte(5), // pkgLen
'F', 'O', 'O', 'F', 'F', 'O', 'O', 'F',
// Missing flag byte // Missing flag byte
@ -678,7 +698,7 @@ type mockResolver struct {
} }
func (m mockResolver) LookupTable(name string) *table.SDTHeader { func (m mockResolver) LookupTable(name string) *table.SDTHeader {
pathToDumps := pkgDir() + "/../table/tabletest/" pathToDumps := pkgDir() + "/../../table/tabletest/"
for _, f := range m.tableFiles { for _, f := range m.tableFiles {
if !strings.Contains(f, name) { if !strings.Contains(f, name) {
continue continue
@ -709,3 +729,23 @@ func (f fixedPayloadResolver) LookupTable(name string) *table.SDTHeader {
return hdr 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
}

View File

@ -6,112 +6,147 @@
// virtualbox. // virtualbox.
DefinitionBlock ("parser-testsuite-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0x00000002) DefinitionBlock ("parser-testsuite-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0x00000002)
{ {
OperationRegion (DBG0, SystemIO, 0x3000, 0x04) OperationRegion (DBG0, SystemIO, 0x3000, 0x04)
Field (DBG0, ByteAcc, NoLock, Preserve) Field (DBG0, ByteAcc, NoLock, Preserve)
{
DHE1, 8
}
Device (DRV0)
{ {
Name (_ADR, Ones) DHE1, 8
// named entity containing qword const
Name (H15F, 0xBADC0FEEDEADC0DE)
Method (_GTF, 0, NotSerialized) // _GTF: Get Task File
{
Return (H15F)
}
} }
// example from p. 268 of ACPI 6.2 spec Device (DRV0)
Scope(\_SB){ {
OperationRegion(TOP1, GenericSerialBus, 0x00, 0x100) // GenericSerialBus device at command offset 0x00 Name (_ADR, Ones)
Name (SDB0, ResourceTemplate() {}) // named entity containing qword const
Field(TOP1, BufferAcc, NoLock, Preserve){ Name (H15F, 0xBADC0FEEDEADC0DE)
Connection(SDB0), // Use the Resource Descriptor defined above Method (_GTF, 0, NotSerialized) // _GTF: Get Task File
AccessAs(BufferAcc, AttribWord), {
FLD0, 8, Return (H15F)
FLD1, 8 }
} }
Field(TOP1, BufferAcc, NoLock, Preserve){ // example from p. 268 of ACPI 6.2 spec
Connection(I2cSerialBus(0x5b,,100000,, "\\_SB",,,,RawDataBuffer(){3,9})), Scope(\_SB){
AccessAs(BufferAcc, AttribBytes(4)), OperationRegion(TOP1, GenericSerialBus, 0x00, 0x100) // GenericSerialBus device at command offset 0x00
FLD2, 8,
AccessAs(BufferAcc, AttribRawBytes(3)), Name (SDB0, ResourceTemplate() {})
FLD3, 8, Field(TOP1, BufferAcc, NoLock, Preserve){
AccessAs(BufferAcc, AttribRawProcessBytes(2)), Connection(SDB0), // Use the Resource Descriptor defined above
FLD4, 8 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 // Other entity types
Event(HLO0) Event(HLO0)
Mutex(MUT0,1) Mutex(MUT0,1)
Signal(HLO0) Signal(HLO0)
// Other executable bits // Other executable bits
Method (EXE0, 1, Serialized) Method (EXE0, 1, Serialized)
{ {
Local0 = Revision Local0 = Revision
// NameString target // NameString target
Local1 = SizeOf(GLB1) Local1 = SizeOf(GLB1)
Local0 = "my-handle" Local0 = "my-handle"
Load(DBG0, Local0) Load(DBG0, Local0)
Unload(Local0) Unload(Local0)
// Example from p. 951 of the spec CreateBitField(Arg0, 0, WFL0)
Store ( if(Arg0==0){
LoadTable ("OEM1", "MYOEM", "TABLE1", "\\_SB.PCI0","MYD", Return(WFL0)
Package () {0,"\\_SB.PCI0"} }
), Local0
)
FromBCD(9, Arg0) CreateByteField(Arg0, 0, WFL1)
ToBCD(Arg0, Local1) if(Arg0==1){
Return(WFL1)
}
Breakpoint CreateWordField(Arg0, 0, WFL2)
Debug = "test" if(Arg0==2){
Fatal(0xf0, 0xdeadc0de, 1) Return(WFL2)
}
Reset(HLO0) CreateDwordField(Arg0, 0, WFL3)
if(Arg0==3){
Return(WFL3)
}
// Mutex support CreateQwordField(Arg0, 0, WFL4)
Acquire(MUT0, 0xffff) // no timeout if(Arg0==4){
Release(MUT0) Return(WFL4)
}
// Signal/Wait CreateField(Arg0, 0, 13, WFL5)
Wait(HLO0, 0xffff) if(Arg0==5){
Return(WFL5)
}
// Get monotonic timer value // Example from p. 951 of the spec
Local0 = Timer Store (
LoadTable ("OEM1", "MYOEM", "TABLE1", "\\_SB.PCI0","MYD",
Package () {0,"\\_SB.PCI0"}
), Local0
)
CopyObject(Local0, Local1) FromBCD(9, Arg0)
Return(ObjectType(Local1)) 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 // BankField example from p. 899 of the spec
// Define a 256-byte operational region in SystemIO space and name it GIO0 // Define a 256-byte operational region in SystemIO space and name it GIO0
OperationRegion (GIO0, SystemIO, 0x125, 0x100) OperationRegion (GIO0, SystemIO, 0x125, 0x100)
Field (GIO0, ByteAcc, NoLock, Preserve) { Field (GIO0, ByteAcc, NoLock, Preserve) {
GLB1, 1, GLB1, 1,
GLB2, 1, GLB2, 1,
Offset (1), // Move to offset for byte 1 Offset (1), // Move to offset for byte 1
BNK1, 4 BNK1, 4
} }
BankField (GIO0, BNK1, 0, ByteAcc, NoLock, Preserve) { BankField (GIO0, BNK1, 0, ByteAcc, NoLock, Preserve) {
Offset (0x30), Offset (0x30),
FET0, 1, FET0, 1,
FET1, 1 FET1, 1
} }
// Data Region // Data Region
DataTableRegion (REG0, "FOOF", "BAR", "BAZ") DataTableRegion (REG0, "FOOF", "BAR", "BAZ")
// Other resources
Processor(CPU0, 1, 0x120, 6){}
PowerResource(PWR0, 0, 0){}
ThermalZone(TZ0){}
} }