mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
acpi: implement AML parser for all AML opcodes in the ACPI 6.2 spec
This commit is contained in:
parent
4dd7c0b077
commit
2a84c75d8e
@ -302,6 +302,10 @@ type fieldEntity struct {
|
||||
type fieldUnitEntity struct {
|
||||
fieldEntity
|
||||
|
||||
// The connection which this field references.
|
||||
connectionName string
|
||||
resolvedConnection Entity
|
||||
|
||||
// The region which this field references.
|
||||
regionName string
|
||||
resolvedRegion *regionEntity
|
||||
@ -309,6 +313,13 @@ type fieldUnitEntity struct {
|
||||
|
||||
func (ent *fieldUnitEntity) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
|
||||
var ok bool
|
||||
if ent.connectionName != "" && ent.resolvedConnection == nil {
|
||||
if ent.resolvedConnection = scopeFind(ent.parent, rootNs, ent.connectionName); ent.resolvedConnection == nil {
|
||||
kfmt.Fprintf(errWriter, "[field %s] could not resolve connection reference: %s\n", ent.name, ent.connectionName)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if ent.resolvedRegion == nil {
|
||||
if ent.resolvedRegion, ok = scopeFind(ent.parent, rootNs, ent.regionName).(*regionEntity); !ok {
|
||||
kfmt.Fprintf(errWriter, "[field %s] could not resolve referenced region: %s\n", ent.name, ent.regionName)
|
||||
@ -326,6 +337,10 @@ func (ent *fieldUnitEntity) Resolve(errWriter io.Writer, rootNs ScopeEntity) boo
|
||||
type indexFieldEntity struct {
|
||||
fieldEntity
|
||||
|
||||
// The connection which this field references.
|
||||
connectionName string
|
||||
resolvedConnection Entity
|
||||
|
||||
indexRegName string
|
||||
indexReg *fieldUnitEntity
|
||||
|
||||
@ -335,6 +350,13 @@ type indexFieldEntity struct {
|
||||
|
||||
func (ent *indexFieldEntity) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
|
||||
var ok bool
|
||||
if ent.connectionName != "" && ent.resolvedConnection == nil {
|
||||
if ent.resolvedConnection = scopeFind(ent.parent, rootNs, ent.connectionName); ent.resolvedConnection == nil {
|
||||
kfmt.Fprintf(errWriter, "[field %s] could not resolve connection reference: %s\n", ent.name, ent.connectionName)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if ent.indexReg == nil {
|
||||
if ent.indexReg, ok = scopeFind(ent.parent, rootNs, ent.indexRegName).(*fieldUnitEntity); !ok {
|
||||
kfmt.Fprintf(errWriter, "[indexField %s] could not resolve referenced index register: %s\n", ent.name, ent.indexRegName)
|
||||
@ -363,7 +385,7 @@ type namedReference struct {
|
||||
|
||||
func (ref *namedReference) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
|
||||
if ref.target = scopeFind(ref.parent, rootNs, ref.targetName); ref.target == nil {
|
||||
kfmt.Fprintf(errWriter, "could not resolve referenced symbol: %s\n", ref.targetName)
|
||||
kfmt.Fprintf(errWriter, "could not resolve referenced symbol: %s (parent: %s)\n", ref.targetName, ref.parent.Name())
|
||||
return false
|
||||
}
|
||||
|
||||
@ -397,3 +419,39 @@ type Device struct {
|
||||
}
|
||||
|
||||
func (d *Device) getOpcode() opcode { return opDevice }
|
||||
|
||||
// mutexEntity represents a named mutex object
|
||||
type mutexEntity struct {
|
||||
parent ScopeEntity
|
||||
|
||||
// isGlobal is set to true for the pre-defined global mutex (\_GL object)
|
||||
isGlobal bool
|
||||
|
||||
name string
|
||||
syncLevel uint8
|
||||
}
|
||||
|
||||
func (ent *mutexEntity) getOpcode() opcode { return opMutex }
|
||||
func (ent *mutexEntity) setOpcode(op opcode) {}
|
||||
func (ent *mutexEntity) Name() string { return ent.name }
|
||||
func (ent *mutexEntity) Parent() ScopeEntity { return ent.parent }
|
||||
func (ent *mutexEntity) setParent(parent ScopeEntity) { ent.parent = parent }
|
||||
func (ent *mutexEntity) getArgs() []interface{} { return nil }
|
||||
func (ent *mutexEntity) setArg(argIndex uint8, arg interface{}) bool {
|
||||
// arg 0 is the mutex name
|
||||
if argIndex == 0 {
|
||||
var ok bool
|
||||
ent.name, ok = arg.(string)
|
||||
return ok
|
||||
}
|
||||
|
||||
// arg1 is the sync level (bits 0:3)
|
||||
syncLevel, ok := arg.(uint64)
|
||||
ent.syncLevel = uint8(syncLevel) & 0xf
|
||||
return ok
|
||||
}
|
||||
|
||||
// eventEntity represents a named ACPI sync event.
|
||||
type eventEntity struct {
|
||||
namedEntity
|
||||
}
|
||||
|
@ -245,7 +245,7 @@ var opcodeMap = [256]uint8{
|
||||
/* 0 1 2 3 4 5 6 7*/
|
||||
/*0x00 - 0x07*/ 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x02, 0xff,
|
||||
/*0x08 - 0x0f*/ 0x03, 0xff, 0x04, 0x05, 0x06, 0x07, 0x08, 0xff,
|
||||
/*0x10 - 0x17*/ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0xff, 0xff, 0xff,
|
||||
/*0x10 - 0x17*/ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xff, 0xff,
|
||||
/*0x18 - 0x1f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x20 - 0x27*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x28 - 0x2f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
@ -260,15 +260,15 @@ var opcodeMap = [256]uint8{
|
||||
/*0x70 - 0x77*/ 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
|
||||
/*0x78 - 0x7f*/ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
/*0x80 - 0x87*/ 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
|
||||
/*0x88 - 0x8f*/ 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0xff, 0x3d,
|
||||
/*0x88 - 0x8f*/ 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
|
||||
/*0x90 - 0x97*/ 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
|
||||
/*0x98 - 0x9f*/ 0x46, 0x47, 0x48, 0x49, 0x4a, 0xff, 0x4a, 0x4b,
|
||||
/*0x98 - 0x9f*/ 0x46, 0x47, 0x48, 0x49, 0x4a, 0x49, 0x4a, 0x4b,
|
||||
/*0xa0 - 0xa7*/ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0xff, 0xff,
|
||||
/*0xa8 - 0xaf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0xb0 - 0xb7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0xb8 - 0xbf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0xc0 - 0xc7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0xc8 - 0xcf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0xc8 - 0xcf*/ 0xff, 0xff, 0xff, 0xff, 0x52, 0xff, 0xff, 0xff,
|
||||
/*0xd0 - 0xd7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0xd8 - 0xdf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0xe0 - 0xe7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
@ -282,13 +282,13 @@ var opcodeMap = [256]uint8{
|
||||
// invalid/unsupported opcode.
|
||||
var extendedOpcodeMap = [256]uint8{
|
||||
/* 0 1 2 3 4 5 6 7*/
|
||||
/*0x00 - 0x07*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x00 - 0x07*/ 0xff, 0x54, 0x55, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x08 - 0x0f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x10 - 0x17*/ 0xff, 0xff, 0x56, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x18 - 0x1f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x20 - 0x27*/ 0xff, 0x5a, 0x5b, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x28 - 0x2f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x30 - 0x37*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x10 - 0x17*/ 0xff, 0xff, 0x56, 0x57, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x18 - 0x1f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x58,
|
||||
/*0x20 - 0x27*/ 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||
/*0x28 - 0x2f*/ 0x61, 0x62, 0x63, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x30 - 0x37*/ 0x64, 0x65, 0x66, 0x67, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x38 - 0x3f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x40 - 0x47*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x48 - 0x4f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
@ -298,8 +298,8 @@ var extendedOpcodeMap = [256]uint8{
|
||||
/*0x68 - 0x6f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x70 - 0x77*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x78 - 0x7f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x80 - 0x87*/ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0xff,
|
||||
/*0x88 - 0x8f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x80 - 0x87*/ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
||||
/*0x88 - 0x8f*/ 0x70, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x90 - 0x97*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x98 - 0x9f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0xa0 - 0xa7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
|
@ -156,6 +156,7 @@ func TestOpArgFlagToString(t *testing.T) {
|
||||
// not yet been mapped via an opcode table. This test will be removed once all
|
||||
// opcodes are supported.
|
||||
func TestFindUnmappedOpcodes(t *testing.T) {
|
||||
//t.SkipNow()
|
||||
for opIndex, opRef := range opcodeMap {
|
||||
if opRef != badOpcode {
|
||||
continue
|
||||
@ -170,14 +171,15 @@ func TestFindUnmappedOpcodes(t *testing.T) {
|
||||
}
|
||||
|
||||
for opIndex, opRef := range extendedOpcodeMap {
|
||||
if opRef != badOpcode {
|
||||
// 0xff (opOnes) is defined in opcodeTable
|
||||
if opRef != badOpcode || opIndex == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
opIndex += 0xff
|
||||
for tabIndex, info := range opcodeTable {
|
||||
if uint16(info.op) == uint16(opIndex) {
|
||||
t.Errorf("set extendedOpcodeMap[0x%02x] = 0x%02x // %s\n", opIndex, tabIndex, info.op.String())
|
||||
t.Errorf("set extendedOpcodeMap[0x%02x] = 0x%02x // %s\n", opIndex-0xff, tabIndex, info.op.String())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
922
src/gopheros/device/acpi/aml/parser.go
Normal file
922
src/gopheros/device/acpi/aml/parser.go
Normal file
@ -0,0 +1,922 @@
|
||||
package aml
|
||||
|
||||
import (
|
||||
"gopheros/device/acpi/table"
|
||||
"gopheros/kernel"
|
||||
"gopheros/kernel/kfmt"
|
||||
"io"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
errParsingAML = &kernel.Error{Module: "acpi_aml_parser", Message: "could not parse AML bytecode"}
|
||||
errResolvingEntities = &kernel.Error{Module: "acpi_aml_parser", Message: "AML bytecode contains unresolvable entities"}
|
||||
)
|
||||
|
||||
// Parser implements an AML parser.
|
||||
type Parser struct {
|
||||
r amlStreamReader
|
||||
errWriter io.Writer
|
||||
root ScopeEntity
|
||||
scopeStack []ScopeEntity
|
||||
tableName string
|
||||
}
|
||||
|
||||
// NewParser returns a new AML parser instance.
|
||||
func NewParser(errWriter io.Writer, rootEntity ScopeEntity) *Parser {
|
||||
return &Parser{
|
||||
errWriter: errWriter,
|
||||
root: rootEntity,
|
||||
}
|
||||
}
|
||||
|
||||
// ParseAML attempts to parse the AML byte-code contained in the supplied ACPI
|
||||
// table. The parser emits any encountered errors to the specified errWriter.
|
||||
func (p *Parser) ParseAML(tableName string, header *table.SDTHeader) *kernel.Error {
|
||||
p.tableName = tableName
|
||||
p.r.Init(
|
||||
uintptr(unsafe.Pointer(header)),
|
||||
header.Length,
|
||||
uint32(unsafe.Sizeof(table.SDTHeader{})),
|
||||
)
|
||||
|
||||
// Pass 1: decode bytecode and build entitites
|
||||
p.scopeStack = nil
|
||||
p.scopeEnter(p.root)
|
||||
if !p.parseObjList(header.Length) {
|
||||
lastOp, _ := p.r.LastByte()
|
||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] error parsing AML bytecode (last op 0x%x)\n", p.tableName, p.r.Offset()-1, lastOp)
|
||||
return errParsingAML
|
||||
}
|
||||
p.scopeExit()
|
||||
|
||||
// Pass 2: resolve forward references
|
||||
var resolveFailed bool
|
||||
scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool {
|
||||
if res, ok := ent.(resolver); ok && !res.Resolve(p.errWriter, p.root) {
|
||||
resolveFailed = true
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if resolveFailed {
|
||||
return errResolvingEntities
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseObjList tries to parse an AML object list. Object lists are usually
|
||||
// specified together with a pkgLen block which is used to calculate the max
|
||||
// read offset that the parser may reach.
|
||||
func (p *Parser) parseObjList(maxOffset uint32) bool {
|
||||
for !p.r.EOF() && p.r.Offset() < maxOffset {
|
||||
if !p.parseObj() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *Parser) parseObj() bool {
|
||||
var (
|
||||
curOffset uint32
|
||||
pkgLen uint32
|
||||
info *opcodeInfo
|
||||
ok bool
|
||||
)
|
||||
|
||||
// If we cannot decode the next opcode then this may be a method
|
||||
// invocation or a name reference. If neither is the case, we need to
|
||||
// rewind the stream and parse a method invocation before giving up.
|
||||
curOffset = p.r.Offset()
|
||||
if info, ok = p.nextOpcode(); !ok {
|
||||
p.r.SetOffset(curOffset)
|
||||
return p.parseMethodInvocationOrNameRef()
|
||||
}
|
||||
|
||||
hasPkgLen := info.flags.is(opFlagHasPkgLen) || info.argFlags.contains(opArgTermList) || info.argFlags.contains(opArgFieldList)
|
||||
|
||||
if hasPkgLen {
|
||||
curOffset = p.r.Offset()
|
||||
if pkgLen, ok = p.parsePkgLength(); !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 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:
|
||||
return p.parseScope(curOffset + pkgLen)
|
||||
case opDevice, opMethod:
|
||||
return p.parseNamespacedObj(info.op, curOffset+pkgLen)
|
||||
}
|
||||
|
||||
// Create appropriate object for opcode type and attach it to current scope unless it is
|
||||
// a device named scope in which case it may define a relative scope name
|
||||
obj := p.makeObjForOpcode(info)
|
||||
p.scopeCurrent().Append(obj)
|
||||
|
||||
if argCount := info.argFlags.argCount(); argCount > 0 {
|
||||
for argIndex := uint8(0); argIndex < argCount; argIndex++ {
|
||||
if !p.parseArg(
|
||||
info,
|
||||
obj,
|
||||
argIndex,
|
||||
info.argFlags.arg(argIndex),
|
||||
curOffset+pkgLen,
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return p.finalizeObj(info.op, obj)
|
||||
}
|
||||
|
||||
// finalizeObj applies post-parse logic for special object types.
|
||||
func (p *Parser) finalizeObj(op opcode, obj Entity) bool {
|
||||
switch op {
|
||||
case opElse:
|
||||
// If this is an else block we need to append it as an argument to the
|
||||
// If block
|
||||
// Pop Else block of the current scope
|
||||
p.scopeCurrent().removeLastChild()
|
||||
prevObj := p.scopeCurrent().lastChild()
|
||||
if prevObj.getOpcode() != opIf {
|
||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] encountered else block without a matching if block\n", p.tableName, p.r.Offset())
|
||||
return false
|
||||
}
|
||||
|
||||
// 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
|
||||
})
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// parseScope reads a scope name from the AML bytestream, enters it and parses
|
||||
// an objlist relative to it. The referenced scope must be one of:
|
||||
// - one of the pre-defined scopes
|
||||
// - device
|
||||
// - processor
|
||||
// - thermal zone
|
||||
// - power resource
|
||||
func (p *Parser) parseScope(maxReadOffset uint32) bool {
|
||||
name, ok := p.parseNameString()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
target := scopeFind(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:
|
||||
// ok
|
||||
default:
|
||||
// Only allow if this is a named scope
|
||||
if target.Name() == "" {
|
||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] %s does not refer to a scoped object\n", p.tableName, p.r.Offset(), name)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
p.scopeEnter(target.(ScopeEntity))
|
||||
ok = p.parseObjList(maxReadOffset)
|
||||
p.scopeExit()
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// parseNamespacedObj reads a scope target name from the AML bytestream,
|
||||
// attaches the a 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 {
|
||||
scopeExpr, ok := p.parseNameString()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
parent, name := scopeResolvePath(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, ok := p.parseNumConstant(1)
|
||||
if !ok {
|
||||
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
|
||||
}
|
||||
|
||||
parent.Append(obj)
|
||||
p.scopeEnter(obj)
|
||||
ok = p.parseObjList(maxReadOffset)
|
||||
p.scopeExit()
|
||||
|
||||
return ok && p.finalizeObj(op, obj)
|
||||
}
|
||||
|
||||
func (p *Parser) parseArg(info *opcodeInfo, obj Entity, argIndex uint8, argType opArgFlag, maxReadOffset uint32) bool {
|
||||
var (
|
||||
arg interface{}
|
||||
ok bool
|
||||
)
|
||||
|
||||
switch argType {
|
||||
case opArgNameString:
|
||||
arg, ok = p.parseNameString()
|
||||
case opArgByteData:
|
||||
arg, ok = p.parseNumConstant(1)
|
||||
case opArgWord:
|
||||
arg, ok = p.parseNumConstant(2)
|
||||
case opArgDword:
|
||||
arg, ok = p.parseNumConstant(4)
|
||||
case opArgQword:
|
||||
arg, ok = p.parseNumConstant(8)
|
||||
case opArgString:
|
||||
arg, ok = p.parseString()
|
||||
case opArgTermObj, opArgDataRefObj:
|
||||
arg, ok = p.parseArgObj()
|
||||
case opArgSimpleName:
|
||||
arg, ok = p.parseSimpleName()
|
||||
case opArgSuperName:
|
||||
arg, ok = p.parseSuperName()
|
||||
case opArgTarget:
|
||||
arg, ok = p.parseTarget()
|
||||
case opArgTermList:
|
||||
// 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, ok := obj.(ScopeEntity); ok {
|
||||
p.scopeEnter(s)
|
||||
} else {
|
||||
ns := &scopeEntity{op: opScope}
|
||||
p.scopeEnter(ns)
|
||||
obj.setArg(argIndex, ns)
|
||||
}
|
||||
|
||||
ok = p.parseObjList(maxReadOffset)
|
||||
p.scopeExit()
|
||||
return ok
|
||||
case opArgFieldList:
|
||||
return p.parseFieldList(info.op, obj.getArgs(), maxReadOffset)
|
||||
case opArgByteList:
|
||||
var bl []byte
|
||||
for p.r.Offset() < maxReadOffset {
|
||||
b, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
bl = append(bl, b)
|
||||
}
|
||||
arg, ok = bl, true
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return obj.setArg(argIndex, arg)
|
||||
}
|
||||
|
||||
func (p *Parser) parseArgObj() (Entity, bool) {
|
||||
if ok := p.parseObj(); !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return p.scopeCurrent().removeLastChild(), true
|
||||
}
|
||||
|
||||
func (p *Parser) makeObjForOpcode(info *opcodeInfo) Entity {
|
||||
var obj Entity
|
||||
|
||||
switch {
|
||||
case info.objType == objTypeLocalScope:
|
||||
obj = new(scopeEntity)
|
||||
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.flags.is(opFlagConstant):
|
||||
obj = new(constEntity)
|
||||
case info.flags.is(opFlagScoped):
|
||||
obj = new(scopeEntity)
|
||||
case info.flags.is(opFlagNamed):
|
||||
obj = new(namedEntity)
|
||||
default:
|
||||
obj = new(unnamedEntity)
|
||||
}
|
||||
|
||||
obj.setOpcode(info.op)
|
||||
return obj
|
||||
}
|
||||
|
||||
// parseMethodInvocationOrNameRef attempts to parse a method invocation and its term
|
||||
// args. This method first scans the NameString and performs a lookup. If the
|
||||
// lookup returns a method definition then we consult it to figure out how many
|
||||
// arguments we need to parse.
|
||||
//
|
||||
// Grammar:
|
||||
// MethodInvocation := NameString TermArgList
|
||||
// TermArgList = Nothing | TermArg TermArgList
|
||||
// TermArg = Type2Opcode | DataObject | ArgObj | LocalObj | MethodInvocation
|
||||
func (p *Parser) parseMethodInvocationOrNameRef() bool {
|
||||
invocationStartOffset := p.r.Offset()
|
||||
name, ok := p.parseNameString()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// Lookup Name and try matching it to a function definition
|
||||
if methodDef, ok := scopeFind(p.scopeCurrent(), p.root, name).(*Method); ok {
|
||||
var (
|
||||
invocation = &methodInvocationEntity{
|
||||
methodDef: methodDef,
|
||||
}
|
||||
curOffset uint32
|
||||
argIndex uint8
|
||||
arg Entity
|
||||
)
|
||||
|
||||
for argIndex < methodDef.argCount && !p.r.EOF() {
|
||||
// Peek next opcode
|
||||
curOffset = p.r.Offset()
|
||||
nextOpcode, ok := p.nextOpcode()
|
||||
p.r.SetOffset(curOffset)
|
||||
|
||||
switch {
|
||||
case ok && (opIsType2(nextOpcode.op) || opIsArg(nextOpcode.op) || opIsDataObject(nextOpcode.op)):
|
||||
arg, ok = p.parseArgObj()
|
||||
default:
|
||||
// It may be a nested invocation or named ref
|
||||
ok = p.parseMethodInvocationOrNameRef()
|
||||
if ok {
|
||||
arg = p.scopeCurrent().removeLastChild()
|
||||
}
|
||||
}
|
||||
|
||||
// No more TermArgs to parse
|
||||
if !ok {
|
||||
p.r.SetOffset(curOffset)
|
||||
break
|
||||
}
|
||||
|
||||
invocation.setArg(argIndex, arg)
|
||||
argIndex++
|
||||
}
|
||||
|
||||
if argIndex != methodDef.argCount {
|
||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] argument mismatch (exp: %d, got %d) for invocation of method: %s\n", p.tableName, invocationStartOffset, methodDef.argCount, argIndex, name)
|
||||
return false
|
||||
}
|
||||
|
||||
p.scopeCurrent().Append(invocation)
|
||||
return true
|
||||
}
|
||||
|
||||
// This is a name reference; assume it's a forward reference for now
|
||||
// and delegate its resolution to a post-parse step.
|
||||
p.scopeCurrent().Append(&namedReference{targetName: name})
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *Parser) nextOpcode() (*opcodeInfo, bool) {
|
||||
next, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if next != extOpPrefix {
|
||||
index := opcodeMap[next]
|
||||
if index == badOpcode {
|
||||
return nil, false
|
||||
}
|
||||
return &opcodeTable[index], true
|
||||
}
|
||||
|
||||
// Scan next byte to figure out the opcode
|
||||
if next, err = p.r.ReadByte(); err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
index := extendedOpcodeMap[next]
|
||||
if index == badOpcode {
|
||||
return nil, false
|
||||
}
|
||||
return &opcodeTable[index], true
|
||||
}
|
||||
|
||||
// parseFieldList parses a list of FieldElements until the reader reaches
|
||||
// maxReadOffset and appends them to the current scope. Depending on the opcode
|
||||
// this method will emit either fieldUnit objects or indexField objects
|
||||
//
|
||||
// Grammar:
|
||||
// FieldElement := NamedField | ReservedField | AccessField | ExtendedAccessField | ConnectField
|
||||
// NamedField := NameSeg PkgLength
|
||||
// ReservedField := 0x00 PkgLength
|
||||
// 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 {
|
||||
var (
|
||||
// for fieldUnit, name0 is the region name and name1 is not used;
|
||||
// for indexField,
|
||||
name0, name1 string
|
||||
flags uint64
|
||||
|
||||
ok bool
|
||||
bitWidth uint32
|
||||
curBitOffset uint32
|
||||
accessAttrib FieldAccessAttrib
|
||||
accessByteCount uint8
|
||||
unitName string
|
||||
)
|
||||
|
||||
switch op {
|
||||
case opField: // Field := PkgLength Region AccessFlags FieldList
|
||||
if len(args) != 2 {
|
||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] unsupported 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] unsupported 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
|
||||
resolvedConnection Entity
|
||||
)
|
||||
|
||||
for p.r.Offset() < maxReadOffset && !p.r.EOF() {
|
||||
next, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch next {
|
||||
case 0x00: // ReservedField; generated by the Offset() command
|
||||
bitWidth, ok = p.parsePkgLength()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
curBitOffset += bitWidth
|
||||
continue
|
||||
case 0x1: // AccessField; set access attributes for following fields
|
||||
next, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
accessType = FieldAccessType(next & 0xf) // access type; bits[0:3]
|
||||
|
||||
attrib, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// To specify AccessAttribBytes, RawBytes and RawProcessBytes
|
||||
// the ASL compiler will emit an ExtendedAccessField opcode.
|
||||
accessByteCount = 0
|
||||
accessAttrib = FieldAccessAttrib(attrib)
|
||||
|
||||
continue
|
||||
case 0x2: // ConnectField => <0x2> NameString> | <0x02> TermObj => Buffer
|
||||
curOffset := p.r.Offset()
|
||||
if connectionName, ok = p.parseNameString(); !ok {
|
||||
// Rewind and try parsing it as an object
|
||||
p.r.SetOffset(curOffset)
|
||||
if resolvedConnection, ok = p.parseArgObj(); !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
case 0x3: // ExtendedAccessField => <0x03> AccessType ExtendedAccessAttrib AccessLength
|
||||
next, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
accessType = FieldAccessType(next & 0xf) // access type; bits[0:3]
|
||||
|
||||
extAccessAttrib, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
accessByteCount, err = p.r.ReadByte()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch extAccessAttrib {
|
||||
case 0x0b:
|
||||
accessAttrib = FieldAccessAttribBytes
|
||||
case 0xe:
|
||||
accessAttrib = FieldAccessAttribRawBytes
|
||||
case 0x0f:
|
||||
accessAttrib = FieldAccessAttribRawProcessBytes
|
||||
}
|
||||
default: // NamedField
|
||||
p.r.UnreadByte()
|
||||
if unitName, ok = p.parseNameString(); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
bitWidth, ok = p.parsePkgLength()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// 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{
|
||||
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{
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
curBitOffset += bitWidth
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ok && p.r.Offset() == maxReadOffset
|
||||
}
|
||||
|
||||
// parsePkgLength parses a PkgLength value from the AML bytestream.
|
||||
func (p *Parser) parsePkgLength() (uint32, bool) {
|
||||
lead, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// The high 2 bits of the lead byte indicate how many bytes follow.
|
||||
var pkgLen uint32
|
||||
switch lead >> 6 {
|
||||
case 0:
|
||||
pkgLen = uint32(lead)
|
||||
case 1:
|
||||
b1, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// lead bits 0-3 are the lsb of the length nybble
|
||||
pkgLen = uint32(b1)<<4 | uint32(lead&0xf)
|
||||
case 2:
|
||||
b1, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
b2, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// lead bits 0-3 are the lsb of the length nybble
|
||||
pkgLen = uint32(b2)<<12 | uint32(b1)<<4 | uint32(lead&0xf)
|
||||
case 3:
|
||||
b1, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
b2, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
b3, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// lead bits 0-3 are the lsb of the length nybble
|
||||
pkgLen = uint32(b3)<<20 | uint32(b2)<<12 | uint32(b1)<<4 | uint32(lead&0xf)
|
||||
}
|
||||
|
||||
return pkgLen, true
|
||||
}
|
||||
|
||||
// parseNumConstant parses a byte/word/dword or qword value from the AML bytestream.
|
||||
func (p *Parser) parseNumConstant(numBytes uint8) (uint64, bool) {
|
||||
var (
|
||||
next byte
|
||||
err error
|
||||
res uint64
|
||||
)
|
||||
|
||||
for c := uint8(0); c < numBytes; c++ {
|
||||
if next, err = p.r.ReadByte(); err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
res = res | (uint64(next) << (8 * c))
|
||||
}
|
||||
|
||||
return res, true
|
||||
}
|
||||
|
||||
// parseString parses a string from the AML bytestream.
|
||||
func (p *Parser) parseString() (string, bool) {
|
||||
// Read ASCII chars till we reach a null byte
|
||||
var (
|
||||
next byte
|
||||
err error
|
||||
str []byte
|
||||
)
|
||||
|
||||
for {
|
||||
next, err = p.r.ReadByte()
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if next == 0x00 {
|
||||
break
|
||||
} else if next >= 0x01 && next <= 0x7f { // AsciiChar
|
||||
str = append(str, next)
|
||||
} else {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
return string(str), true
|
||||
}
|
||||
|
||||
// parseSuperName attempts to pass a SuperName from the AML bytestream.
|
||||
//
|
||||
// Grammar:
|
||||
// SuperName := SimpleName | DebugObj | Type6Opcode
|
||||
// SimpleName := NameString | ArgObj | LocalObj
|
||||
func (p *Parser) parseSuperName() (interface{}, bool) {
|
||||
// Try parsing as SimpleName
|
||||
curOffset := p.r.Offset()
|
||||
if obj, ok := p.parseSimpleName(); ok {
|
||||
return obj, ok
|
||||
}
|
||||
|
||||
// Rewind and try parsing as object
|
||||
p.r.SetOffset(curOffset)
|
||||
return p.parseArgObj()
|
||||
}
|
||||
|
||||
// parseSimpleName attempts to pass a SimpleName from the AML bytestream.
|
||||
//
|
||||
// Grammar:
|
||||
// SimpleName := NameString | ArgObj | LocalObj
|
||||
func (p *Parser) parseSimpleName() (interface{}, bool) {
|
||||
// Peek next opcode
|
||||
curOffset := p.r.Offset()
|
||||
nextOpcode, ok := p.nextOpcode()
|
||||
|
||||
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
|
||||
default:
|
||||
// Rewind and try parsing as NameString
|
||||
p.r.SetOffset(curOffset)
|
||||
obj, ok = p.parseNameString()
|
||||
}
|
||||
|
||||
return obj, ok
|
||||
}
|
||||
|
||||
// parseTarget attempts to pass a Target from the AML bytestream.
|
||||
//
|
||||
// Grammar:
|
||||
// Target := SuperName | NullName
|
||||
// NullName := 0x00
|
||||
// SuperName := SimpleName | DebugObj | Type6Opcode
|
||||
// Type6Opcode := DefRefOf | DefDerefOf | DefIndex | UserTermObj
|
||||
// SimpleName := NameString | ArgObj | LocalObj
|
||||
//
|
||||
// UserTermObj is a control method invocation.
|
||||
func (p *Parser) parseTarget() (interface{}, bool) {
|
||||
// Peek next opcode
|
||||
curOffset := p.r.Offset()
|
||||
nextOpcode, ok := p.nextOpcode()
|
||||
p.r.SetOffset(curOffset)
|
||||
|
||||
if ok {
|
||||
switch {
|
||||
case nextOpcode.op == opZero: // this is actuall a NullName
|
||||
p.r.SetOffset(curOffset + 1)
|
||||
return &constEntity{op: opStringPrefix, val: ""}, true
|
||||
case opIsArg(nextOpcode.op): // ArgObj | LocalObj
|
||||
case nextOpcode.op == opRefOf || nextOpcode.op == opDerefOf || nextOpcode.op == opIndex || nextOpcode.op == opDebug: // Type6 | DebugObj
|
||||
default:
|
||||
// Unexpected opcode
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// We can use parseObj for parsing
|
||||
return p.parseArgObj()
|
||||
}
|
||||
|
||||
// In this case, this is either a NameString or a control method invocation.
|
||||
if ok := p.parseMethodInvocationOrNameRef(); ok {
|
||||
return p.scopeCurrent().removeLastChild(), ok
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// parseNameString parses a NameString from the AML bytestream.
|
||||
//
|
||||
// Grammar:
|
||||
// NameString := RootChar NamePath | PrefixPath NamePath
|
||||
// PrefixPath := Nothing | '^' PrefixPath
|
||||
// NamePath := NameSeg | DualNamePath | MultiNamePath | NullName
|
||||
func (p *Parser) parseNameString() (string, bool) {
|
||||
var str []byte
|
||||
|
||||
// NameString := RootChar NamePath | PrefixPath NamePath
|
||||
next, err := p.r.PeekByte()
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
switch next {
|
||||
case '\\': // RootChar
|
||||
str = append(str, next)
|
||||
p.r.ReadByte()
|
||||
case '^': // PrefixPath := Nothing | '^' PrefixPath
|
||||
str = append(str, next)
|
||||
for {
|
||||
next, err = p.r.PeekByte()
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if next != '^' {
|
||||
break
|
||||
}
|
||||
|
||||
str = append(str, next)
|
||||
p.r.ReadByte()
|
||||
}
|
||||
}
|
||||
|
||||
// NamePath := NameSeg | DualNamePath | MultiNamePath | NullName
|
||||
next, err = p.r.ReadByte()
|
||||
var readCount int
|
||||
switch next {
|
||||
case 0x00: // NullName
|
||||
case 0x2e: // DualNamePath := DualNamePrefix NameSeg NameSeg
|
||||
readCount = 8 // NameSeg x 2
|
||||
case 0x2f: // MultiNamePath := MultiNamePrefix SegCount NameSeg(SegCount)
|
||||
segCount, err := p.r.ReadByte()
|
||||
if segCount == 0 || err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
readCount = int(segCount) * 4
|
||||
default: // NameSeg := LeadNameChar NameChar NameChar NameChar
|
||||
// LeadNameChar := 'A' - 'Z' | '_'
|
||||
if (next < 'A' || next > 'Z') && next != '_' {
|
||||
return "", false
|
||||
}
|
||||
|
||||
str = append(str, next) // LeadNameChar
|
||||
readCount = 3 // NameChar x 3
|
||||
}
|
||||
|
||||
for index := 0; readCount > 0; readCount, index = readCount-1, index+1 {
|
||||
next, err := p.r.ReadByte()
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Inject a '.' every 4 chars except for the last segment so
|
||||
// scoped lookups can work properly.
|
||||
if index > 0 && index%4 == 0 && readCount > 1 {
|
||||
str = append(str, '.')
|
||||
}
|
||||
|
||||
str = append(str, next)
|
||||
}
|
||||
|
||||
return string(str), true
|
||||
}
|
||||
|
||||
// scopeCurrent returns the currently active scope.
|
||||
func (p *Parser) scopeCurrent() ScopeEntity {
|
||||
return p.scopeStack[len(p.scopeStack)-1]
|
||||
}
|
||||
|
||||
// scopeEnter enters the given scope.
|
||||
func (p *Parser) scopeEnter(s ScopeEntity) {
|
||||
p.scopeStack = append(p.scopeStack, s)
|
||||
}
|
||||
|
||||
// scopeExit exits the current scope.
|
||||
func (p *Parser) scopeExit() {
|
||||
p.scopeStack = p.scopeStack[:len(p.scopeStack)-1]
|
||||
}
|
69
src/gopheros/device/acpi/aml/parser_test.go
Normal file
69
src/gopheros/device/acpi/aml/parser_test.go
Normal file
@ -0,0 +1,69 @@
|
||||
package aml
|
||||
|
||||
import (
|
||||
"gopheros/device/acpi/table"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func TestParser(t *testing.T) {
|
||||
specs := [][]string{
|
||||
[]string{"DSDT.aml", "SSDT.aml"},
|
||||
[]string{"parser-testsuite-DSDT.aml"},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
var resolver = mockResolver{
|
||||
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
|
||||
|
||||
p := NewParser(ioutil.Discard, rootNS)
|
||||
|
||||
for _, tableName := range spec {
|
||||
tableName := strings.Replace(tableName, ".aml", "", -1)
|
||||
if err := p.ParseAML(tableName, resolver.LookupTable(tableName)); err != nil {
|
||||
t.Errorf("[spec %d] [%s]: %v", specIndex, tableName, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func pkgDir() string {
|
||||
_, f, _, _ := runtime.Caller(1)
|
||||
return filepath.Dir(f)
|
||||
}
|
||||
|
||||
type mockResolver struct {
|
||||
tableFiles []string
|
||||
}
|
||||
|
||||
func (m mockResolver) LookupTable(name string) *table.SDTHeader {
|
||||
pathToDumps := pkgDir() + "/../table/tabletest/"
|
||||
for _, f := range m.tableFiles {
|
||||
if !strings.Contains(f, name) {
|
||||
continue
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(pathToDumps + f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return (*table.SDTHeader)(unsafe.Pointer(&data[0]))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Binary file not shown.
@ -0,0 +1,117 @@
|
||||
// DSDT-parser-testsuite
|
||||
//
|
||||
// This file contains various ASL constructs to ensure that the AML parser
|
||||
// properly handles all possible ASL opcodes it may encounter. This test file
|
||||
// is used in addition to the DSDT.aml file obtained by running acpidump inside
|
||||
// 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)
|
||||
{
|
||||
Name (_ADR, Ones)
|
||||
|
||||
// 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
|
||||
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 executable bits
|
||||
Method (EXE0, 1, Serialized)
|
||||
{
|
||||
Local0 = Revision
|
||||
|
||||
// NameString target
|
||||
Local1 = SizeOf(GLB1)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
FromBCD(9, Arg0)
|
||||
ToBCD(Arg0, Local1)
|
||||
|
||||
Breakpoint
|
||||
Debug = "test"
|
||||
Fatal(0xf0, 0xdeadc0de, 1)
|
||||
|
||||
Reset(HLO0)
|
||||
|
||||
// Mutex support
|
||||
Mutex(MUT0, 1)
|
||||
Acquire(MUT0, 0xffff) // no timeout
|
||||
Release(MUT0)
|
||||
|
||||
// Signal/Wait
|
||||
Signal(HLO0)
|
||||
Wait(HLO0, 0xffff)
|
||||
|
||||
// Get monotonic timer value
|
||||
Local0 = Timer
|
||||
|
||||
CopyObject(Local0, Local1)
|
||||
Return(ObjectType(Local1))
|
||||
}
|
||||
|
||||
// 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)
|
||||
Field (GIO0, ByteAcc, NoLock, Preserve) {
|
||||
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
|
||||
}
|
||||
|
||||
// Data Region
|
||||
DataTableRegion (REG0, "FOOF", "BAR", "BAZ")
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user