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 {
|
type fieldUnitEntity struct {
|
||||||
fieldEntity
|
fieldEntity
|
||||||
|
|
||||||
|
// The connection which this field references.
|
||||||
|
connectionName string
|
||||||
|
resolvedConnection Entity
|
||||||
|
|
||||||
// The region which this field references.
|
// The region which this field references.
|
||||||
regionName string
|
regionName string
|
||||||
resolvedRegion *regionEntity
|
resolvedRegion *regionEntity
|
||||||
@ -309,6 +313,13 @@ type fieldUnitEntity struct {
|
|||||||
|
|
||||||
func (ent *fieldUnitEntity) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
|
func (ent *fieldUnitEntity) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
|
||||||
var ok 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 == nil {
|
||||||
if ent.resolvedRegion, ok = scopeFind(ent.parent, rootNs, ent.regionName).(*regionEntity); !ok {
|
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)
|
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 {
|
type indexFieldEntity struct {
|
||||||
fieldEntity
|
fieldEntity
|
||||||
|
|
||||||
|
// The connection which this field references.
|
||||||
|
connectionName string
|
||||||
|
resolvedConnection Entity
|
||||||
|
|
||||||
indexRegName string
|
indexRegName string
|
||||||
indexReg *fieldUnitEntity
|
indexReg *fieldUnitEntity
|
||||||
|
|
||||||
@ -335,6 +350,13 @@ type indexFieldEntity struct {
|
|||||||
|
|
||||||
func (ent *indexFieldEntity) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
|
func (ent *indexFieldEntity) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
|
||||||
var ok 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 == nil {
|
||||||
if ent.indexReg, ok = scopeFind(ent.parent, rootNs, ent.indexRegName).(*fieldUnitEntity); !ok {
|
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)
|
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 {
|
func (ref *namedReference) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
|
||||||
if ref.target = scopeFind(ref.parent, rootNs, ref.targetName); ref.target == nil {
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,3 +419,39 @@ type Device struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) getOpcode() opcode { return opDevice }
|
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*/
|
/* 0 1 2 3 4 5 6 7*/
|
||||||
/*0x00 - 0x07*/ 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x02, 0xff,
|
/*0x00 - 0x07*/ 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x02, 0xff,
|
||||||
/*0x08 - 0x0f*/ 0x03, 0xff, 0x04, 0x05, 0x06, 0x07, 0x08, 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,
|
/*0x18 - 0x1f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
/*0x20 - 0x27*/ 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,
|
/*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,
|
/*0x70 - 0x77*/ 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
|
||||||
/*0x78 - 0x7f*/ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
/*0x78 - 0x7f*/ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||||
/*0x80 - 0x87*/ 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
|
/*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,
|
/*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,
|
/*0xa0 - 0xa7*/ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0xff, 0xff,
|
||||||
/*0xa8 - 0xaf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
/*0xa8 - 0xaf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
/*0xb0 - 0xb7*/ 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,
|
/*0xb8 - 0xbf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
/*0xc0 - 0xc7*/ 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,
|
/*0xd0 - 0xd7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
/*0xd8 - 0xdf*/ 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,
|
/*0xe0 - 0xe7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
@ -282,13 +282,13 @@ var opcodeMap = [256]uint8{
|
|||||||
// invalid/unsupported opcode.
|
// invalid/unsupported opcode.
|
||||||
var extendedOpcodeMap = [256]uint8{
|
var extendedOpcodeMap = [256]uint8{
|
||||||
/* 0 1 2 3 4 5 6 7*/
|
/* 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,
|
/*0x08 - 0x0f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
/*0x10 - 0x17*/ 0xff, 0xff, 0x56, 0xff, 0xff, 0xff, 0xff, 0xff,
|
/*0x10 - 0x17*/ 0xff, 0xff, 0x56, 0x57, 0xff, 0xff, 0xff, 0xff,
|
||||||
/*0x18 - 0x1f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
/*0x18 - 0x1f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x58,
|
||||||
/*0x20 - 0x27*/ 0xff, 0x5a, 0x5b, 0xff, 0xff, 0xff, 0xff, 0xff,
|
/*0x20 - 0x27*/ 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
/*0x28 - 0x2f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
/*0x28 - 0x2f*/ 0x61, 0x62, 0x63, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
/*0x30 - 0x37*/ 0xff, 0xff, 0xff, 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,
|
/*0x38 - 0x3f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
/*0x40 - 0x47*/ 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,
|
/*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,
|
/*0x68 - 0x6f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
/*0x70 - 0x77*/ 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,
|
/*0x78 - 0x7f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
/*0x80 - 0x87*/ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0xff,
|
/*0x80 - 0x87*/ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
||||||
/*0x88 - 0x8f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
/*0x88 - 0x8f*/ 0x70, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
/*0x90 - 0x97*/ 0xff, 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,
|
/*0x98 - 0x9f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
/*0xa0 - 0xa7*/ 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
|
// not yet been mapped via an opcode table. This test will be removed once all
|
||||||
// opcodes are supported.
|
// opcodes are supported.
|
||||||
func TestFindUnmappedOpcodes(t *testing.T) {
|
func TestFindUnmappedOpcodes(t *testing.T) {
|
||||||
|
//t.SkipNow()
|
||||||
for opIndex, opRef := range opcodeMap {
|
for opIndex, opRef := range opcodeMap {
|
||||||
if opRef != badOpcode {
|
if opRef != badOpcode {
|
||||||
continue
|
continue
|
||||||
@ -170,14 +171,15 @@ func TestFindUnmappedOpcodes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for opIndex, opRef := range extendedOpcodeMap {
|
for opIndex, opRef := range extendedOpcodeMap {
|
||||||
if opRef != badOpcode {
|
// 0xff (opOnes) is defined in opcodeTable
|
||||||
|
if opRef != badOpcode || opIndex == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
opIndex += 0xff
|
opIndex += 0xff
|
||||||
for tabIndex, info := range opcodeTable {
|
for tabIndex, info := range opcodeTable {
|
||||||
if uint16(info.op) == uint16(opIndex) {
|
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
|
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