mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
Merge pull request #53 from achilleasa/implement-aml-parser
Implement AML parser
This commit is contained in:
commit
0f85d4be53
492
src/gopheros/device/acpi/aml/entity.go
Normal file
492
src/gopheros/device/acpi/aml/entity.go
Normal file
@ -0,0 +1,492 @@
|
||||
package aml
|
||||
|
||||
import (
|
||||
"gopheros/kernel/kfmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type resolver interface {
|
||||
Resolve(io.Writer, ScopeEntity) bool
|
||||
}
|
||||
|
||||
// Entity is an interface implemented by all AML entities.
|
||||
type Entity interface {
|
||||
Name() string
|
||||
Parent() ScopeEntity
|
||||
TableHandle() uint8
|
||||
|
||||
setTableHandle(uint8)
|
||||
getOpcode() opcode
|
||||
setOpcode(opcode)
|
||||
setParent(ScopeEntity)
|
||||
getArgs() []interface{}
|
||||
setArg(uint8, interface{}) bool
|
||||
}
|
||||
|
||||
// ScopeEntity is an interface that is implemented by entities that define an
|
||||
// AML scope.
|
||||
type ScopeEntity interface {
|
||||
Entity
|
||||
|
||||
Children() []Entity
|
||||
Append(Entity) bool
|
||||
|
||||
removeChild(Entity)
|
||||
lastChild() Entity
|
||||
}
|
||||
|
||||
// unnamedEntity defines an unnamed entity that can be attached to a parent scope.
|
||||
type unnamedEntity struct {
|
||||
tableHandle uint8
|
||||
op opcode
|
||||
args []interface{}
|
||||
parent ScopeEntity
|
||||
}
|
||||
|
||||
func (ent *unnamedEntity) getOpcode() opcode { return ent.op }
|
||||
func (ent *unnamedEntity) setOpcode(op opcode) { ent.op = op }
|
||||
func (ent *unnamedEntity) Name() string { return "" }
|
||||
func (ent *unnamedEntity) Parent() ScopeEntity { return ent.parent }
|
||||
func (ent *unnamedEntity) setParent(parent ScopeEntity) { ent.parent = parent }
|
||||
func (ent *unnamedEntity) getArgs() []interface{} { return ent.args }
|
||||
func (ent *unnamedEntity) setArg(_ uint8, arg interface{}) bool {
|
||||
ent.args = append(ent.args, arg)
|
||||
return true
|
||||
}
|
||||
func (ent *unnamedEntity) TableHandle() uint8 { return ent.tableHandle }
|
||||
func (ent *unnamedEntity) setTableHandle(h uint8) { ent.tableHandle = h }
|
||||
|
||||
// namedEntity is a named entity that can be attached to the parent scope. The
|
||||
// setArg() implementation for this type expects arg at index 0 to contain the
|
||||
// entity name.
|
||||
type namedEntity struct {
|
||||
tableHandle uint8
|
||||
op opcode
|
||||
args []interface{}
|
||||
parent ScopeEntity
|
||||
|
||||
name string
|
||||
}
|
||||
|
||||
func (ent *namedEntity) getOpcode() opcode { return ent.op }
|
||||
func (ent *namedEntity) setOpcode(op opcode) { ent.op = op }
|
||||
func (ent *namedEntity) Name() string { return ent.name }
|
||||
func (ent *namedEntity) Parent() ScopeEntity { return ent.parent }
|
||||
func (ent *namedEntity) setParent(parent ScopeEntity) { ent.parent = parent }
|
||||
func (ent *namedEntity) getArgs() []interface{} { return ent.args }
|
||||
func (ent *namedEntity) setArg(argIndex uint8, arg interface{}) bool {
|
||||
// arg 0 is the entity name
|
||||
if argIndex == 0 {
|
||||
var ok bool
|
||||
ent.name, ok = arg.(string)
|
||||
return ok
|
||||
}
|
||||
|
||||
ent.args = append(ent.args, arg)
|
||||
return true
|
||||
}
|
||||
func (ent *namedEntity) TableHandle() uint8 { return ent.tableHandle }
|
||||
func (ent *namedEntity) setTableHandle(h uint8) { ent.tableHandle = h }
|
||||
|
||||
// constEntity is an unnamedEntity which always evaluates to a constant value.
|
||||
// Calls to setArg for argument index 0 will memoize the argument value that is
|
||||
// stored inside this entity.
|
||||
type constEntity struct {
|
||||
tableHandle uint8
|
||||
op opcode
|
||||
args []interface{}
|
||||
parent ScopeEntity
|
||||
|
||||
val interface{}
|
||||
}
|
||||
|
||||
func (ent *constEntity) getOpcode() opcode { return ent.op }
|
||||
func (ent *constEntity) setOpcode(op opcode) {
|
||||
ent.op = op
|
||||
|
||||
// special const opcode cases that have an implicit value
|
||||
switch ent.op {
|
||||
case opZero:
|
||||
ent.val = uint64(0)
|
||||
case opOne:
|
||||
ent.val = uint64(1)
|
||||
case opOnes:
|
||||
ent.val = uint64(1<<64 - 1)
|
||||
}
|
||||
}
|
||||
func (ent *constEntity) Name() string { return "" }
|
||||
func (ent *constEntity) Parent() ScopeEntity { return ent.parent }
|
||||
func (ent *constEntity) setParent(parent ScopeEntity) { ent.parent = parent }
|
||||
func (ent *constEntity) getArgs() []interface{} { return ent.args }
|
||||
func (ent *constEntity) setArg(argIndex uint8, arg interface{}) bool {
|
||||
ent.val = arg
|
||||
return argIndex == 0
|
||||
}
|
||||
func (ent *constEntity) TableHandle() uint8 { return ent.tableHandle }
|
||||
func (ent *constEntity) setTableHandle(h uint8) { ent.tableHandle = h }
|
||||
|
||||
// scopeEntity is an optionally named entity that defines a scope.
|
||||
type scopeEntity struct {
|
||||
tableHandle uint8
|
||||
op opcode
|
||||
args []interface{}
|
||||
parent ScopeEntity
|
||||
|
||||
name string
|
||||
children []Entity
|
||||
}
|
||||
|
||||
func (ent *scopeEntity) getOpcode() opcode { return ent.op }
|
||||
func (ent *scopeEntity) setOpcode(op opcode) { ent.op = op }
|
||||
func (ent *scopeEntity) Name() string { return ent.name }
|
||||
func (ent *scopeEntity) Parent() ScopeEntity { return ent.parent }
|
||||
func (ent *scopeEntity) setParent(parent ScopeEntity) { ent.parent = parent }
|
||||
func (ent *scopeEntity) getArgs() []interface{} { return ent.args }
|
||||
func (ent *scopeEntity) setArg(argIndex uint8, arg interface{}) bool {
|
||||
// arg 0 *may* be the entity name. If it's not a string just add it to
|
||||
// the arg list.
|
||||
if argIndex == 0 {
|
||||
var ok bool
|
||||
if ent.name, ok = arg.(string); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
ent.args = append(ent.args, arg)
|
||||
return true
|
||||
}
|
||||
func (ent *scopeEntity) Children() []Entity { return ent.children }
|
||||
func (ent *scopeEntity) Append(child Entity) bool {
|
||||
child.setParent(ent)
|
||||
ent.children = append(ent.children, child)
|
||||
return true
|
||||
}
|
||||
func (ent *scopeEntity) lastChild() Entity { return ent.children[len(ent.children)-1] }
|
||||
func (ent *scopeEntity) removeChild(child Entity) {
|
||||
for index := 0; index < len(ent.children); index++ {
|
||||
if ent.children[index] == child {
|
||||
ent.children = append(ent.children[:index], ent.children[index+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
func (ent *scopeEntity) TableHandle() uint8 { return ent.tableHandle }
|
||||
func (ent *scopeEntity) setTableHandle(h uint8) { ent.tableHandle = h }
|
||||
|
||||
// bufferEntity defines a buffer object.
|
||||
type bufferEntity struct {
|
||||
unnamedEntity
|
||||
|
||||
size interface{}
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (ent *bufferEntity) setArg(argIndex uint8, arg interface{}) bool {
|
||||
switch argIndex {
|
||||
case 0: // size
|
||||
ent.size = arg
|
||||
return true
|
||||
case 1: // data
|
||||
if byteSlice, ok := arg.([]byte); ok {
|
||||
ent.data = byteSlice
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// bufferFieldEntity describes a bit/byte/word/dword/qword or arbitrary length
|
||||
// buffer field.
|
||||
type bufferFieldEntity struct {
|
||||
namedEntity
|
||||
}
|
||||
|
||||
func (ent *bufferFieldEntity) setArg(argIndex uint8, arg interface{}) bool {
|
||||
// opCreateField specifies the name using the arg at index 3 while
|
||||
// opCreateXXXField (byte, word e.t.c) specifies the name using the
|
||||
// arg at index 2
|
||||
if (ent.op == opCreateField && argIndex == 3) || argIndex == 2 {
|
||||
var ok bool
|
||||
ent.name, ok = arg.(string)
|
||||
return ok
|
||||
}
|
||||
ent.args = append(ent.args, arg)
|
||||
return true
|
||||
}
|
||||
|
||||
// RegionSpace describes the memory space where a region is located.
|
||||
type RegionSpace uint8
|
||||
|
||||
// The list of supported RegionSpace values.
|
||||
const (
|
||||
RegionSpaceSystemMemory RegionSpace = iota
|
||||
RegionSpaceSystemIO
|
||||
RegionSpacePCIConfig
|
||||
RegionSpaceEmbeddedControl
|
||||
RegionSpaceSMBus
|
||||
RegionSpacePCIBarTarget
|
||||
RegionSpaceIPMI
|
||||
)
|
||||
|
||||
// regionEntity defines a region located at a particular space (e.g in memory,
|
||||
// an embedded controller, the SMBus e.t.c).
|
||||
type regionEntity struct {
|
||||
namedEntity
|
||||
|
||||
space RegionSpace
|
||||
}
|
||||
|
||||
func (ent *regionEntity) setArg(argIndex uint8, arg interface{}) bool {
|
||||
var ok bool
|
||||
switch argIndex {
|
||||
case 0:
|
||||
ok = ent.namedEntity.setArg(argIndex, arg)
|
||||
case 1:
|
||||
// the parser will convert ByteData types to uint64
|
||||
var space uint64
|
||||
space, ok = arg.(uint64)
|
||||
ent.space = RegionSpace(space)
|
||||
case 2, 3:
|
||||
ent.args = append(ent.args, arg)
|
||||
ok = true
|
||||
}
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// FieldAccessType specifies the type of access (byte, word, e.t.c) used to
|
||||
// read/write to a field.
|
||||
type FieldAccessType uint8
|
||||
|
||||
// The list of supported FieldAccessType values.
|
||||
const (
|
||||
FieldAccessTypeAny FieldAccessType = iota
|
||||
FieldAccessTypeByte
|
||||
FieldAccessTypeWord
|
||||
FieldAccessTypeDword
|
||||
FieldAccessTypeQword
|
||||
FieldAccessTypeBuffer
|
||||
)
|
||||
|
||||
// FieldUpdateRule specifies how a field value is updated when a write uses
|
||||
// a value with a smaller width than the field.
|
||||
type FieldUpdateRule uint8
|
||||
|
||||
// The list of supported FieldUpdateRule values.
|
||||
const (
|
||||
FieldUpdateRulePreserve FieldUpdateRule = iota
|
||||
FieldUpdateRuleWriteAsOnes
|
||||
FieldUpdateRuleWriteAsZeros
|
||||
)
|
||||
|
||||
// FieldAccessAttrib specifies additional information about a particular field
|
||||
// access.
|
||||
type FieldAccessAttrib uint8
|
||||
|
||||
// The list of supported FieldAccessAttrib values.
|
||||
const (
|
||||
FieldAccessAttribQuick FieldAccessAttrib = 0x02
|
||||
FieldAccessAttribSendReceive = 0x04
|
||||
FieldAccessAttribByte = 0x06
|
||||
FieldAccessAttribWord = 0x08
|
||||
FieldAccessAttribBlock = 0x0a
|
||||
FieldAccessAttribBytes = 0x0b // byteCount contains the number of bytes
|
||||
FieldAccessAttribProcessCall = 0x0c
|
||||
FieldAccessAttribBlockProcessCall = 0x0d
|
||||
FieldAccessAttribRawBytes = 0x0e // byteCount contains the number of bytes
|
||||
FieldAccessAttribRawProcessBytes = 0x0f // byteCount contains the number of bytes
|
||||
)
|
||||
|
||||
// fieldEntity is a named object that encapsulates the data shared between regular
|
||||
// fields and index fields.
|
||||
type fieldEntity struct {
|
||||
namedEntity
|
||||
|
||||
bitOffset uint32
|
||||
bitWidth uint32
|
||||
|
||||
lock bool
|
||||
updateRule FieldUpdateRule
|
||||
|
||||
// accessAttrib is valid if accessType is BufferAcc
|
||||
// for the SMB or GPIO OpRegions.
|
||||
accessAttrib FieldAccessAttrib
|
||||
accessType FieldAccessType
|
||||
|
||||
// byteCount is valid when accessAttrib is one of:
|
||||
// Bytes, RawBytes or RawProcessBytes
|
||||
byteCount uint8
|
||||
}
|
||||
|
||||
// fieldUnitEntity is a field defined inside an operating region.
|
||||
type fieldUnitEntity struct {
|
||||
fieldEntity
|
||||
|
||||
// The connection which this field references.
|
||||
connectionName string
|
||||
resolvedConnection Entity
|
||||
|
||||
// The region which this field references.
|
||||
regionName string
|
||||
resolvedRegion *regionEntity
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
return ent.resolvedRegion != nil
|
||||
}
|
||||
|
||||
// indexFieldEntity is a special field that groups together two field units so a
|
||||
// index/data register pattern can be implemented. To write a value to an
|
||||
// indexField, the interpreter must first write the appropriate offset to
|
||||
// the indexRegister (using the alignment specifid by accessType) and then
|
||||
// write the actual value to the dataRegister.
|
||||
type indexFieldEntity struct {
|
||||
fieldEntity
|
||||
|
||||
// The connection which this field references.
|
||||
connectionName string
|
||||
resolvedConnection Entity
|
||||
|
||||
indexRegName string
|
||||
indexReg *fieldUnitEntity
|
||||
|
||||
dataRegName string
|
||||
dataReg *fieldUnitEntity
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
if ent.dataReg == nil {
|
||||
if ent.dataReg, ok = scopeFind(ent.parent, rootNs, ent.dataRegName).(*fieldUnitEntity); !ok {
|
||||
kfmt.Fprintf(errWriter, "[dataField %s] could not resolve referenced data register: %s\n", ent.name, ent.dataRegName)
|
||||
}
|
||||
}
|
||||
|
||||
return ent.indexReg != nil && ent.dataReg != nil
|
||||
}
|
||||
|
||||
// namedReference holds a named reference to an AML symbol. The spec allows
|
||||
// the symbol not to be defined at the time when the reference is parsed. In
|
||||
// such a case (forward reference) it will be resolved after the entire AML
|
||||
// stream has successfully been parsed.
|
||||
type namedReference struct {
|
||||
unnamedEntity
|
||||
|
||||
targetName string
|
||||
target Entity
|
||||
}
|
||||
|
||||
func (ref *namedReference) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool {
|
||||
if ref.target == nil {
|
||||
ref.target = scopeFind(ref.parent, rootNs, ref.targetName)
|
||||
}
|
||||
|
||||
if ref.target == nil {
|
||||
kfmt.Fprintf(errWriter, "could not resolve referenced symbol: %s (parent: %s)\n", ref.targetName, ref.parent.Name())
|
||||
}
|
||||
|
||||
return ref.target != nil
|
||||
}
|
||||
|
||||
// methodInvocationEntity describes an AML method invocation.
|
||||
type methodInvocationEntity struct {
|
||||
unnamedEntity
|
||||
|
||||
methodDef *Method
|
||||
}
|
||||
|
||||
// Method defines an invocable AML method.
|
||||
type Method struct {
|
||||
scopeEntity
|
||||
|
||||
tableHandle uint8
|
||||
argCount uint8
|
||||
serialized bool
|
||||
syncLevel uint8
|
||||
}
|
||||
|
||||
func (m *Method) getOpcode() opcode { return opMethod }
|
||||
|
||||
// Device defines a device.
|
||||
type Device struct {
|
||||
scopeEntity
|
||||
|
||||
tableHandle uint8
|
||||
|
||||
// The methodMap keeps track of all methods exposed by this device.
|
||||
methodMap map[string]*Method
|
||||
}
|
||||
|
||||
func (d *Device) getOpcode() opcode { return opDevice }
|
||||
func (d *Device) setTableHandle(h uint8) { d.tableHandle = h }
|
||||
|
||||
// TableHandle returns the handle of the ACPI table that defines this device.
|
||||
func (d *Device) TableHandle() uint8 { return d.tableHandle }
|
||||
|
||||
// mutexEntity represents a named mutex object
|
||||
type mutexEntity struct {
|
||||
parent ScopeEntity
|
||||
|
||||
// isGlobal is set to true for the pre-defined global mutex (\_GL object)
|
||||
isGlobal bool
|
||||
|
||||
name string
|
||||
syncLevel uint8
|
||||
tableHandle 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 {
|
||||
var ok bool
|
||||
switch argIndex {
|
||||
case 0:
|
||||
// arg 0 is the mutex name
|
||||
ent.name, ok = arg.(string)
|
||||
case 1:
|
||||
// arg1 is the sync level (bits 0:3)
|
||||
var syncLevel uint64
|
||||
syncLevel, ok = arg.(uint64)
|
||||
ent.syncLevel = uint8(syncLevel) & 0xf
|
||||
}
|
||||
return ok
|
||||
}
|
||||
func (ent *mutexEntity) TableHandle() uint8 { return ent.tableHandle }
|
||||
func (ent *mutexEntity) setTableHandle(h uint8) { ent.tableHandle = h }
|
||||
|
||||
// eventEntity represents a named ACPI sync event.
|
||||
type eventEntity struct {
|
||||
namedEntity
|
||||
}
|
143
src/gopheros/device/acpi/aml/entity_test.go
Normal file
143
src/gopheros/device/acpi/aml/entity_test.go
Normal file
@ -0,0 +1,143 @@
|
||||
package aml
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEntityMethods(t *testing.T) {
|
||||
specs := []Entity{
|
||||
&unnamedEntity{},
|
||||
&constEntity{},
|
||||
&scopeEntity{},
|
||||
&bufferEntity{},
|
||||
&fieldUnitEntity{},
|
||||
&indexFieldEntity{},
|
||||
&namedReference{},
|
||||
&methodInvocationEntity{},
|
||||
&Method{},
|
||||
&Device{},
|
||||
&mutexEntity{},
|
||||
&eventEntity{},
|
||||
}
|
||||
|
||||
t.Run("table handle methods", func(t *testing.T) {
|
||||
exp := uint8(42)
|
||||
for specIndex, spec := range specs {
|
||||
spec.setTableHandle(exp)
|
||||
if got := spec.TableHandle(); got != exp {
|
||||
t.Errorf("[spec %d] expected to get back handle %d; got %d", specIndex, exp, got)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("append/remove/get parent methods", func(t *testing.T) {
|
||||
parent := &scopeEntity{name: `\`}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
parent.Append(spec)
|
||||
if got := spec.Parent(); got != parent {
|
||||
t.Errorf("[spec %d] expected to get back parent %v; got %v", specIndex, parent, got)
|
||||
}
|
||||
|
||||
parent.removeChild(spec)
|
||||
}
|
||||
|
||||
if got := len(parent.Children()); got != 0 {
|
||||
t.Fatalf("expected parent not to have any child nodes; got %d", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestEntityArgAssignment(t *testing.T) {
|
||||
specs := []struct {
|
||||
ent Entity
|
||||
argList []interface{}
|
||||
expArgList []interface{}
|
||||
limitedArgs bool
|
||||
}{
|
||||
{
|
||||
&unnamedEntity{},
|
||||
[]interface{}{"foo", 1, "bar"},
|
||||
[]interface{}{"foo", 1, "bar"},
|
||||
false,
|
||||
},
|
||||
{
|
||||
&constEntity{},
|
||||
[]interface{}{"foo"},
|
||||
nil, // constEntity populates its internal state using the 1st arg
|
||||
true,
|
||||
},
|
||||
{
|
||||
&scopeEntity{},
|
||||
[]interface{}{"foo", 1, 2, 3},
|
||||
[]interface{}{1, 2, 3}, // scopeEntity will treat arg0 as the scope name if it is a string
|
||||
false,
|
||||
},
|
||||
{
|
||||
&bufferEntity{},
|
||||
[]interface{}{1, []byte{}},
|
||||
nil, // bufferEntity populates its internal state using the first 2 args
|
||||
true,
|
||||
},
|
||||
{
|
||||
®ionEntity{},
|
||||
[]interface{}{"REG0", uint64(0x4), 0, 10},
|
||||
[]interface{}{0, 10}, // region populates its internal state using the first 2 args
|
||||
true,
|
||||
},
|
||||
{
|
||||
&mutexEntity{},
|
||||
[]interface{}{"MUT0", uint64(1)},
|
||||
nil, // mutexEntity populates its internal state using the first 2 args
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
nextSpec:
|
||||
for specIndex, spec := range specs {
|
||||
for i, arg := range spec.argList {
|
||||
if !spec.ent.setArg(uint8(i), arg) {
|
||||
t.Errorf("[spec %d] error setting arg %d", specIndex, i)
|
||||
continue nextSpec
|
||||
}
|
||||
}
|
||||
|
||||
if spec.limitedArgs {
|
||||
if spec.ent.setArg(uint8(len(spec.argList)), nil) {
|
||||
t.Errorf("[spec %d] expected additional calls to setArg to return false", specIndex)
|
||||
continue nextSpec
|
||||
}
|
||||
}
|
||||
|
||||
if got := spec.ent.getArgs(); !reflect.DeepEqual(got, spec.expArgList) {
|
||||
t.Errorf("[spec %d] expected to get back arg list %v; got %v", specIndex, spec.expArgList, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEntityResolveErrors(t *testing.T) {
|
||||
scope := &scopeEntity{name: `\`}
|
||||
|
||||
specs := []resolver{
|
||||
// Unknown connection entity
|
||||
&fieldUnitEntity{connectionName: "CON0"},
|
||||
// Unknown region
|
||||
&fieldUnitEntity{connectionName: `\`, regionName: "REG0"},
|
||||
// Unknown connection entity
|
||||
&indexFieldEntity{connectionName: "CON0"},
|
||||
// Unknown index register
|
||||
&indexFieldEntity{connectionName: `\`, indexRegName: "IND0"},
|
||||
// Unknown data register
|
||||
&indexFieldEntity{connectionName: `\`, indexRegName: `\`, dataRegName: "DAT0"},
|
||||
// Unknown reference
|
||||
&namedReference{unnamedEntity: unnamedEntity{parent: scope}, targetName: "TRG0"},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
if spec.Resolve(ioutil.Discard, scope) {
|
||||
t.Errorf("[spec %d] expected Resolve() to fail", specIndex)
|
||||
}
|
||||
}
|
||||
}
|
269
src/gopheros/device/acpi/aml/opcode.go
Normal file
269
src/gopheros/device/acpi/aml/opcode.go
Normal file
@ -0,0 +1,269 @@
|
||||
package aml
|
||||
|
||||
// opcode describes an AML opcode. While AML supports 256 opcodes, some of them
|
||||
// are specified using a combination of an extension prefix and a code. To map
|
||||
// each opcode into a single unique value the parser uses an uint16
|
||||
// representation of the opcode values.
|
||||
type opcode uint16
|
||||
|
||||
// String implements fmt.Stringer for opcode.
|
||||
func (op opcode) String() string {
|
||||
for _, entry := range opcodeTable {
|
||||
if entry.op == op {
|
||||
return entry.name
|
||||
}
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// opIsLocalArg returns true if this opcode represents any of the supported local
|
||||
// function args 0 to 7.
|
||||
func opIsLocalArg(op opcode) bool {
|
||||
return op >= opLocal0 && op <= opLocal7
|
||||
}
|
||||
|
||||
// opIsMethodArg returns true if this opcode represents any of the supported
|
||||
// input function args 0 to 6.
|
||||
func opIsMethodArg(op opcode) bool {
|
||||
return op >= opArg0 && op <= opArg6
|
||||
}
|
||||
|
||||
// opIsArg returns true if this opcode is either a local or a method arg.
|
||||
func opIsArg(op opcode) bool {
|
||||
return opIsLocalArg(op) || opIsMethodArg(op)
|
||||
}
|
||||
|
||||
// opIsDataObject returns true if this opcode is part of a DataObject definition
|
||||
//
|
||||
// Grammar:
|
||||
// DataObject := ComputationalData | DefPackage | DefVarPackage
|
||||
// ComputationalData := ByteConst | WordConst | DWordConst | QWordConst | String | ConstObj | RevisionOp | DefBuffer
|
||||
// ConstObj := ZeroOp | OneOp | OnesOp
|
||||
func opIsDataObject(op opcode) bool {
|
||||
switch op {
|
||||
case opBytePrefix, opWordPrefix, opDwordPrefix, opQwordPrefix, opStringPrefix,
|
||||
opZero, opOne, opOnes, opRevision, opBuffer, opPackage, opVarPackage:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// opIsType2 returns true if this is a Type2Opcode.
|
||||
//
|
||||
// Grammar:
|
||||
// Type2Opcode := DefAcquire | DefAdd | DefAnd | DefBuffer | DefConcat |
|
||||
// DefConcatRes | DefCondRefOf | DefCopyObject | DefDecrement |
|
||||
// DefDerefOf | DefDivide | DefFindSetLeftBit | DefFindSetRightBit |
|
||||
// DefFromBCD | DefIncrement | DefIndex | DefLAnd | DefLEqual |
|
||||
// DefLGreater | DefLGreaterEqual | DefLLess | DefLLessEqual | DefMid |
|
||||
// DefLNot | DefLNotEqual | DefLoadTable | DefLOr | DefMatch | DefMod |
|
||||
// DefMultiply | DefNAnd | DefNOr | DefNot | DefObjectType | DefOr |
|
||||
// DefPackage | DefVarPackage | DefRefOf | DefShiftLeft | DefShiftRight |
|
||||
// DefSizeOf | DefStore | DefSubtract | DefTimer | DefToBCD | DefToBuffer |
|
||||
// DefToDecimalString | DefToHexString | DefToInteger | DefToString |
|
||||
// DefWait | DefXOr
|
||||
func opIsType2(op opcode) bool {
|
||||
switch op {
|
||||
case opAcquire, opAdd, opAnd, opBuffer, opConcat,
|
||||
opConcatRes, opCondRefOf, opCopyObject, opDecrement,
|
||||
opDerefOf, opDivide, opFindSetLeftBit, opFindSetRightBit,
|
||||
opFromBCD, opIncrement, opIndex, opLand, opLEqual,
|
||||
opLGreater, opLLess, opMid,
|
||||
opLnot, opLoadTable, opLor, opMatch, opMod,
|
||||
opMultiply, opNand, opNor, opNot, opObjectType, opOr,
|
||||
opPackage, opVarPackage, opRefOf, opShiftLeft, opShiftRight,
|
||||
opSizeOf, opStore, opSubtract, opTimer, opToBCD, opToBuffer,
|
||||
opToDecimalString, opToHexString, opToInteger, opToString,
|
||||
opWait, opXor:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// opIsBufferField returens true if this opcode describes a
|
||||
// buffer field creation operation.
|
||||
func opIsBufferField(op opcode) bool {
|
||||
switch op {
|
||||
case opCreateField, opCreateBitField, opCreateByteField, opCreateWordField, opCreateDWordField, opCreateQWordField:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// objType represents the object types that are supported by the AML parser.
|
||||
type objType uint8
|
||||
|
||||
// The list of AML object types.
|
||||
const (
|
||||
objTypeAny objType = iota
|
||||
objTypeInteger
|
||||
objTypeString
|
||||
objTypeBuffer
|
||||
objTypePackage
|
||||
objTypeDevice
|
||||
objTypeEvent
|
||||
objTypeMethod
|
||||
objTypeMutex
|
||||
objTypeRegion
|
||||
objTypePower
|
||||
objTypeProcessor
|
||||
objTypeThermal
|
||||
objTypeBufferField
|
||||
objTypeLocalRegionField
|
||||
objTypeLocalBankField
|
||||
objTypeLocalReference
|
||||
objTypeLocalAlias
|
||||
objTypeLocalScope
|
||||
objTypeLocalVariable
|
||||
objTypeMethodArgument
|
||||
)
|
||||
|
||||
// opFlag specifies a list of OR-able flags that describe the object
|
||||
// type/attributes generated by a particular opcode.
|
||||
type opFlag uint16
|
||||
|
||||
const (
|
||||
opFlagNone opFlag = 1 << iota
|
||||
opFlagHasPkgLen
|
||||
opFlagNamed
|
||||
opFlagConstant
|
||||
opFlagReference
|
||||
opFlagArithmetic
|
||||
opFlagCreate
|
||||
opFlagReturn
|
||||
opFlagExecutable
|
||||
opFlagNoOp
|
||||
opFlagScoped
|
||||
)
|
||||
|
||||
// is returns true if f is set in this opFlag.
|
||||
func (fl opFlag) is(f opFlag) bool {
|
||||
return (fl & f) != 0
|
||||
}
|
||||
|
||||
// opArgFlags encodes up to 7 opArgFlag values in a uint64 value.
|
||||
type opArgFlags uint64
|
||||
|
||||
// argCount returns the number of encoded args in the given flag.
|
||||
func (fl opArgFlags) argCount() (count uint8) {
|
||||
// Each argument is specified using 8 bits with 0x0 indicating the end of the
|
||||
// argument list
|
||||
for ; fl&0xf != 0; fl, count = fl>>8, count+1 {
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
// arg returns the arg flags for argument "num" where num is the 0-based index
|
||||
// of the argument to return. The allowed values for num are 0-6.
|
||||
func (fl opArgFlags) arg(num uint8) opArgFlag {
|
||||
return opArgFlag((fl >> (num * 8)) & 0xf)
|
||||
}
|
||||
|
||||
// contains returns true if the arg flags contain any argument with type x.
|
||||
func (fl opArgFlags) contains(x opArgFlag) bool {
|
||||
// Each argument is specified using 8 bits with 0x0 indicating the end of the
|
||||
// argument list
|
||||
for ; fl&0xf != 0; fl >>= 8 {
|
||||
if opArgFlag(fl&0xf) == x {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// opArgFlag represents the type of an argument expected by a particular opcode.
|
||||
type opArgFlag uint8
|
||||
|
||||
// The list of supported opArgFlag values.
|
||||
const (
|
||||
_ opArgFlag = iota
|
||||
opArgTermList
|
||||
opArgTermObj
|
||||
opArgByteList
|
||||
opArgPackage
|
||||
opArgString
|
||||
opArgByteData
|
||||
opArgWord
|
||||
opArgDword
|
||||
opArgQword
|
||||
opArgNameString
|
||||
opArgSuperName
|
||||
opArgSimpleName
|
||||
opArgDataRefObj
|
||||
opArgTarget
|
||||
opArgFieldList
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer for opArgFlag.
|
||||
func (fl opArgFlag) String() string {
|
||||
switch fl {
|
||||
case opArgTermList:
|
||||
return "opArgTermList"
|
||||
case opArgTermObj:
|
||||
return "opArgTermObj"
|
||||
case opArgByteList:
|
||||
return "opArgByteList"
|
||||
case opArgPackage:
|
||||
return "opArgPackage"
|
||||
case opArgString:
|
||||
return "opArgString"
|
||||
case opArgByteData:
|
||||
return "opArgByteData"
|
||||
case opArgWord:
|
||||
return "opArgWord"
|
||||
case opArgDword:
|
||||
return "opArgDword"
|
||||
case opArgQword:
|
||||
return "opArgQword"
|
||||
case opArgNameString:
|
||||
return "opArgNameString"
|
||||
case opArgSuperName:
|
||||
return "opArgSuperName"
|
||||
case opArgSimpleName:
|
||||
return "opArgSimpleName"
|
||||
case opArgDataRefObj:
|
||||
return "opArgDataRefObj"
|
||||
case opArgTarget:
|
||||
return "opArgTarget"
|
||||
case opArgFieldList:
|
||||
return "opArgFieldList"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func makeArg0() opArgFlags { return 0 }
|
||||
func makeArg1(arg0 opArgFlag) opArgFlags { return opArgFlags(arg0) }
|
||||
func makeArg2(arg0, arg1 opArgFlag) opArgFlags { return opArgFlags(arg1)<<8 | opArgFlags(arg0) }
|
||||
func makeArg3(arg0, arg1, arg2 opArgFlag) opArgFlags {
|
||||
return opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0)
|
||||
}
|
||||
func makeArg4(arg0, arg1, arg2, arg3 opArgFlag) opArgFlags {
|
||||
return opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0)
|
||||
}
|
||||
func makeArg5(arg0, arg1, arg2, arg3, arg4 opArgFlag) opArgFlags {
|
||||
return opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0)
|
||||
}
|
||||
func makeArg6(arg0, arg1, arg2, arg3, arg4, arg5 opArgFlag) opArgFlags {
|
||||
return opArgFlags(arg5)<<40 | opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0)
|
||||
}
|
||||
func makeArg7(arg0, arg1, arg2, arg3, arg4, arg5, arg6 opArgFlag) opArgFlags {
|
||||
return opArgFlags(arg6)<<48 | opArgFlags(arg5)<<40 | opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0)
|
||||
}
|
||||
|
||||
// opcodeInfo contains all known information about an opcode,
|
||||
// its argument count and types as well as the type of object
|
||||
// represented by it.
|
||||
type opcodeInfo struct {
|
||||
op opcode
|
||||
name string
|
||||
objType objType
|
||||
|
||||
flags opFlag
|
||||
argFlags opArgFlags
|
||||
}
|
317
src/gopheros/device/acpi/aml/opcode_table.go
Normal file
317
src/gopheros/device/acpi/aml/opcode_table.go
Normal file
@ -0,0 +1,317 @@
|
||||
package aml
|
||||
|
||||
const (
|
||||
badOpcode = 0xff
|
||||
extOpPrefix = 0x5b
|
||||
|
||||
// Regular opcode list
|
||||
opZero = opcode(0x00)
|
||||
opOne = opcode(0x01)
|
||||
opAlias = opcode(0x06)
|
||||
opName = opcode(0x08)
|
||||
opBytePrefix = opcode(0x0a)
|
||||
opWordPrefix = opcode(0x0b)
|
||||
opDwordPrefix = opcode(0x0c)
|
||||
opStringPrefix = opcode(0x0d)
|
||||
opQwordPrefix = opcode(0x0e)
|
||||
opScope = opcode(0x10)
|
||||
opBuffer = opcode(0x11)
|
||||
opPackage = opcode(0x12)
|
||||
opVarPackage = opcode(0x13)
|
||||
opMethod = opcode(0x14)
|
||||
opExternal = opcode(0x15)
|
||||
opLocal0 = opcode(0x60)
|
||||
opLocal1 = opcode(0x61)
|
||||
opLocal2 = opcode(0x62)
|
||||
opLocal3 = opcode(0x63)
|
||||
opLocal4 = opcode(0x64)
|
||||
opLocal5 = opcode(0x65)
|
||||
opLocal6 = opcode(0x66)
|
||||
opLocal7 = opcode(0x67)
|
||||
opArg0 = opcode(0x68)
|
||||
opArg1 = opcode(0x69)
|
||||
opArg2 = opcode(0x6a)
|
||||
opArg3 = opcode(0x6b)
|
||||
opArg4 = opcode(0x6c)
|
||||
opArg5 = opcode(0x6d)
|
||||
opArg6 = opcode(0x6e)
|
||||
opStore = opcode(0x70)
|
||||
opRefOf = opcode(0x71)
|
||||
opAdd = opcode(0x72)
|
||||
opConcat = opcode(0x73)
|
||||
opSubtract = opcode(0x74)
|
||||
opIncrement = opcode(0x75)
|
||||
opDecrement = opcode(0x76)
|
||||
opMultiply = opcode(0x77)
|
||||
opDivide = opcode(0x78)
|
||||
opShiftLeft = opcode(0x79)
|
||||
opShiftRight = opcode(0x7a)
|
||||
opAnd = opcode(0x7b)
|
||||
opNand = opcode(0x7c)
|
||||
opOr = opcode(0x7d)
|
||||
opNor = opcode(0x7e)
|
||||
opXor = opcode(0x7f)
|
||||
opNot = opcode(0x80)
|
||||
opFindSetLeftBit = opcode(0x81)
|
||||
opFindSetRightBit = opcode(0x82)
|
||||
opDerefOf = opcode(0x83)
|
||||
opConcatRes = opcode(0x84)
|
||||
opMod = opcode(0x85)
|
||||
opNotify = opcode(0x86)
|
||||
opSizeOf = opcode(0x87)
|
||||
opIndex = opcode(0x88)
|
||||
opMatch = opcode(0x89)
|
||||
opCreateDWordField = opcode(0x8a)
|
||||
opCreateWordField = opcode(0x8b)
|
||||
opCreateByteField = opcode(0x8c)
|
||||
opCreateBitField = opcode(0x8d)
|
||||
opObjectType = opcode(0x8e)
|
||||
opCreateQWordField = opcode(0x8f)
|
||||
opLand = opcode(0x90)
|
||||
opLor = opcode(0x91)
|
||||
opLnot = opcode(0x92)
|
||||
opLEqual = opcode(0x93)
|
||||
opLGreater = opcode(0x94)
|
||||
opLLess = opcode(0x95)
|
||||
opToBuffer = opcode(0x96)
|
||||
opToDecimalString = opcode(0x97)
|
||||
opToHexString = opcode(0x98)
|
||||
opToInteger = opcode(0x99)
|
||||
opToString = opcode(0x9c)
|
||||
opCopyObject = opcode(0x9d)
|
||||
opMid = opcode(0x9e)
|
||||
opContinue = opcode(0x9f)
|
||||
opIf = opcode(0xa0)
|
||||
opElse = opcode(0xa1)
|
||||
opWhile = opcode(0xa2)
|
||||
opNoop = opcode(0xa3)
|
||||
opReturn = opcode(0xa4)
|
||||
opBreak = opcode(0xa5)
|
||||
opBreakPoint = opcode(0xcc)
|
||||
opOnes = opcode(0xff)
|
||||
// Extended opcodes
|
||||
opMutex = opcode(0xff + 0x01)
|
||||
opEvent = opcode(0xff + 0x02)
|
||||
opCondRefOf = opcode(0xff + 0x12)
|
||||
opCreateField = opcode(0xff + 0x13)
|
||||
opLoadTable = opcode(0xff + 0x1f)
|
||||
opLoad = opcode(0xff + 0x20)
|
||||
opStall = opcode(0xff + 0x21)
|
||||
opSleep = opcode(0xff + 0x22)
|
||||
opAcquire = opcode(0xff + 0x23)
|
||||
opSignal = opcode(0xff + 0x24)
|
||||
opWait = opcode(0xff + 0x25)
|
||||
opReset = opcode(0xff + 0x26)
|
||||
opRelease = opcode(0xff + 0x27)
|
||||
opFromBCD = opcode(0xff + 0x28)
|
||||
opToBCD = opcode(0xff + 0x29)
|
||||
opUnload = opcode(0xff + 0x2a)
|
||||
opRevision = opcode(0xff + 0x30)
|
||||
opDebug = opcode(0xff + 0x31)
|
||||
opFatal = opcode(0xff + 0x32)
|
||||
opTimer = opcode(0xff + 0x33)
|
||||
opOpRegion = opcode(0xff + 0x80)
|
||||
opField = opcode(0xff + 0x81)
|
||||
opDevice = opcode(0xff + 0x82)
|
||||
opProcessor = opcode(0xff + 0x83)
|
||||
opPowerRes = opcode(0xff + 0x84)
|
||||
opThermalZone = opcode(0xff + 0x85)
|
||||
opIndexField = opcode(0xff + 0x86)
|
||||
opBankField = opcode(0xff + 0x87)
|
||||
opDataRegion = opcode(0xff + 0x88)
|
||||
)
|
||||
|
||||
// The opcode table contains all opcode-related information that the parser knows.
|
||||
// This table is modeled after a similar table used in the acpica implementation.
|
||||
var opcodeTable = []opcodeInfo{
|
||||
/*0x00*/ {opZero, "Zero", objTypeInteger, opFlagConstant, makeArg0()},
|
||||
/*0x01*/ {opOne, "One", objTypeInteger, opFlagConstant, makeArg0()},
|
||||
/*0x02*/ {opAlias, "Alias", objTypeLocalAlias, opFlagNamed, makeArg2(opArgNameString, opArgNameString)},
|
||||
/*0x03*/ {opName, "Name", objTypeAny, opFlagNamed, makeArg2(opArgNameString, opArgDataRefObj)},
|
||||
/*0x04*/ {opBytePrefix, "Byte", objTypeInteger, opFlagConstant, makeArg1(opArgByteData)},
|
||||
/*0x05*/ {opWordPrefix, "Word", objTypeInteger, opFlagConstant, makeArg1(opArgWord)},
|
||||
/*0x06*/ {opDwordPrefix, "Dword", objTypeInteger, opFlagConstant, makeArg1(opArgDword)},
|
||||
/*0x07*/ {opStringPrefix, "String", objTypeString, opFlagConstant, makeArg1(opArgString)},
|
||||
/*0x08*/ {opQwordPrefix, "Qword", objTypeInteger, opFlagConstant, makeArg1(opArgQword)},
|
||||
/*0x09*/ {opScope, "Scope", objTypeLocalScope, opFlagNamed, makeArg2(opArgNameString, opArgTermList)},
|
||||
/*0x0a*/ {opBuffer, "Buffer", objTypeBuffer, opFlagHasPkgLen, makeArg2(opArgTermObj, opArgByteList)},
|
||||
/*0x0b*/ {opPackage, "Package", objTypePackage, opFlagNone, makeArg2(opArgByteData, opArgTermList)},
|
||||
/*0x0c*/ {opVarPackage, "VarPackage", objTypePackage, opFlagNone, makeArg2(opArgByteData, opArgTermList)},
|
||||
/*0x0d*/ {opMethod, "Method", objTypeMethod, opFlagNamed | opFlagScoped, makeArg3(opArgNameString, opArgByteData, opArgTermList)},
|
||||
/*0x0e*/ {opExternal, "External", objTypeAny, opFlagNamed, makeArg3(opArgNameString, opArgByteData, opArgByteData)},
|
||||
/*0x0f*/ {opLocal0, "Local0", objTypeLocalVariable, opFlagExecutable, makeArg0()},
|
||||
/*0x10*/ {opLocal1, "Local1", objTypeLocalVariable, opFlagExecutable, makeArg0()},
|
||||
/*0x11*/ {opLocal2, "Local2", objTypeLocalVariable, opFlagExecutable, makeArg0()},
|
||||
/*0x12*/ {opLocal3, "Local3", objTypeLocalVariable, opFlagExecutable, makeArg0()},
|
||||
/*0x13*/ {opLocal4, "Local4", objTypeLocalVariable, opFlagExecutable, makeArg0()},
|
||||
/*0120*/ {opLocal5, "Local5", objTypeLocalVariable, opFlagExecutable, makeArg0()},
|
||||
/*0x15*/ {opLocal6, "Local6", objTypeLocalVariable, opFlagExecutable, makeArg0()},
|
||||
/*0x16*/ {opLocal7, "Local7", objTypeLocalVariable, opFlagExecutable, makeArg0()},
|
||||
/*0x17*/ {opArg0, "Arg0", objTypeMethodArgument, opFlagExecutable, makeArg0()},
|
||||
/*0x18*/ {opArg1, "Arg1", objTypeMethodArgument, opFlagExecutable, makeArg0()},
|
||||
/*0x19*/ {opArg2, "Arg2", objTypeMethodArgument, opFlagExecutable, makeArg0()},
|
||||
/*0x1a*/ {opArg3, "Arg3", objTypeMethodArgument, opFlagExecutable, makeArg0()},
|
||||
/*0x1b*/ {opArg4, "Arg4", objTypeMethodArgument, opFlagExecutable, makeArg0()},
|
||||
/*0x1c*/ {opArg5, "Arg5", objTypeMethodArgument, opFlagExecutable, makeArg0()},
|
||||
/*0x1d*/ {opArg6, "Arg6", objTypeMethodArgument, opFlagExecutable, makeArg0()},
|
||||
/*0x1e*/ {opStore, "Store", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgSuperName)},
|
||||
/*0x1f*/ {opRefOf, "RefOf", objTypeAny, opFlagReference | opFlagExecutable, makeArg1(opArgSuperName)},
|
||||
/*0x20*/ {opAdd, "Add", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x21*/ {opConcat, "Concat", objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x22*/ {opSubtract, "Subtract", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x23*/ {opIncrement, "Increment", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgSuperName)},
|
||||
/*0x24*/ {opDecrement, "Decrement", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgSuperName)},
|
||||
/*0x25*/ {opMultiply, "Multiply", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x26*/ {opDivide, "Divide", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTarget, opArgTarget)},
|
||||
/*0x27*/ {opShiftLeft, "ShiftLeft", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x28*/ {opShiftRight, "ShiftRight", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x29*/ {opAnd, "And", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x2a*/ {opNand, "Nand", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x2b*/ {opOr, "Or", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x2c*/ {opNor, "Nor", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x2d*/ {opXor, "Xor", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x2e*/ {opNot, "Not", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)},
|
||||
/*0x2f*/ {opFindSetLeftBit, "FindSetLeftBit", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)},
|
||||
/*0x30*/ {opFindSetRightBit, "FindSetRightBit", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)},
|
||||
/*0x31*/ {opDerefOf, "DerefOf", objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)},
|
||||
/*0x32*/ {opConcatRes, "ConcatRes", objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x33*/ {opMod, "Mod", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x34*/ {opNotify, "Notify", objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgTermObj)},
|
||||
/*0x35*/ {opSizeOf, "SizeOf", objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)},
|
||||
/*0x36*/ {opIndex, "Index", objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x37*/ {opMatch, "Match", objTypeAny, opFlagExecutable, makeArg6(opArgTermObj, opArgByteData, opArgTermObj, opArgByteData, opArgTermObj, opArgTermObj)},
|
||||
/*0x38*/ {opCreateDWordField, "CreateDWordField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)},
|
||||
/*0x39*/ {opCreateWordField, "CreateWordField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)},
|
||||
/*0x3a*/ {opCreateByteField, "CreateByteField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)},
|
||||
/*0x3b*/ {opCreateBitField, "CreateBitField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)},
|
||||
/*0x3c*/ {opObjectType, "ObjectType", objTypeAny, opFlagNone, makeArg1(opArgSuperName)},
|
||||
/*0x3d*/ {opCreateQWordField, "CreateQWordField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)},
|
||||
/*0x3e*/ {opLand, "Land", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)},
|
||||
/*0x3f*/ {opLor, "Lor", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)},
|
||||
/*0x40*/ {opLnot, "Lnot", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgTermObj)},
|
||||
/*0x41*/ {opLEqual, "LEqual", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)},
|
||||
/*0x42*/ {opLGreater, "LGreater", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)},
|
||||
/*0x43*/ {opLLess, "LLess", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)},
|
||||
/*0x44*/ {opToBuffer, "ToBuffer", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)},
|
||||
/*0x45*/ {opToDecimalString, "ToDecimalString", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)},
|
||||
/*0x46*/ {opToHexString, "ToHexString", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)},
|
||||
/*0x47*/ {opToInteger, "ToInteger", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)},
|
||||
/*0x48*/ {opToString, "ToString", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)},
|
||||
/*0x49*/ {opCopyObject, "CopyObject", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgSimpleName)},
|
||||
/*0x4a*/ {opMid, "Mid", objTypeAny, opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTermObj, opArgTarget)},
|
||||
/*0x4b*/ {opContinue, "Continue", objTypeAny, opFlagExecutable, makeArg0()},
|
||||
/*0x4c*/ {opIf, "If", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTermList)},
|
||||
/*0x4d*/ {opElse, "Else", objTypeAny, opFlagExecutable | opFlagScoped, makeArg1(opArgTermList)},
|
||||
/*0x4e*/ {opWhile, "While", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTermList)},
|
||||
/*0x4f*/ {opNoop, "Noop", objTypeAny, opFlagNoOp, makeArg0()},
|
||||
/*0x50*/ {opReturn, "Return", objTypeAny, opFlagReturn, makeArg1(opArgTermObj)},
|
||||
/*0x51*/ {opBreak, "Break", objTypeAny, opFlagExecutable, makeArg0()},
|
||||
/*0x52*/ {opBreakPoint, "BreakPoint", objTypeAny, opFlagNoOp, makeArg0()},
|
||||
/*0x53*/ {opOnes, "Ones", objTypeInteger, opFlagConstant, makeArg0()},
|
||||
/*0x54*/ {opMutex, "Mutex", objTypeMutex, opFlagNamed, makeArg2(opArgNameString, opArgByteData)},
|
||||
/*0x55*/ {opEvent, "Event", objTypeEvent, opFlagNamed, makeArg1(opArgNameString)},
|
||||
/*0x56*/ {opCondRefOf, "CondRefOf", objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgSuperName)},
|
||||
/*0x57*/ {opCreateField, "CreateField", objTypeBufferField, opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTermObj, opArgNameString)},
|
||||
/*0x58*/ {opLoadTable, "LoadTable", objTypeAny, opFlagExecutable, makeArg7(opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj)},
|
||||
/*0x59*/ {opLoad, "Load", objTypeAny, opFlagExecutable, makeArg2(opArgNameString, opArgSuperName)},
|
||||
/*0x5a*/ {opStall, "Stall", objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)},
|
||||
/*0x5b*/ {opSleep, "Sleep", objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)},
|
||||
/*0x5c*/ {opAcquire, "Acquire", objTypeAny, opFlagExecutable, makeArg2(opArgNameString, opArgSuperName)},
|
||||
/*0x5d*/ {opSignal, "Signal", objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)},
|
||||
/*0x5e*/ {opWait, "Wait", objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgTermObj)},
|
||||
/*0x5f*/ {opReset, "Reset", objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)},
|
||||
/*0x60*/ {opRelease, "Release", objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)},
|
||||
/*0x61*/ {opFromBCD, "FromBCD", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)},
|
||||
/*0x62*/ {opToBCD, "ToBCD", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)},
|
||||
/*0x63*/ {opUnload, "Unload", objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)},
|
||||
/*0x64*/ {opRevision, "Revision", objTypeInteger, opFlagConstant | opFlagExecutable, makeArg0()},
|
||||
/*0x65*/ {opDebug, "Debug", objTypeLocalReference, opFlagExecutable, makeArg0()},
|
||||
/*0x66*/ {opFatal, "Fatal", objTypeAny, opFlagExecutable, makeArg3(opArgByteData, opArgDword, opArgTermObj)},
|
||||
/*0x67*/ {opTimer, "Timer", objTypeAny, opFlagNone, makeArg0()},
|
||||
/*0x68*/ {opOpRegion, "OpRegion", objTypeRegion, opFlagNamed, makeArg4(opArgNameString, opArgByteData, opArgTermObj, opArgTermObj)},
|
||||
/*0x69*/ {opField, "Field", objTypeAny, opFlagNone, makeArg3(opArgNameString, opArgByteData, opArgFieldList)},
|
||||
/*0x6a*/ {opDevice, "Device", objTypeDevice, opFlagNamed | opFlagScoped, makeArg2(opArgNameString, opArgTermList)},
|
||||
/*0x6b*/ {opProcessor, "Processor", objTypeProcessor, opFlagNamed | opFlagScoped, makeArg5(opArgNameString, opArgByteData, opArgDword, opArgByteData, opArgTermList)},
|
||||
/*0x6c*/ {opPowerRes, "PowerRes", objTypePower, opFlagNamed | opFlagScoped, makeArg4(opArgNameString, opArgByteData, opArgWord, opArgTermList)},
|
||||
/*0x6d*/ {opThermalZone, "ThermalZone", objTypeThermal, opFlagNamed | opFlagScoped, makeArg2(opArgNameString, opArgTermList)},
|
||||
/*0x6e*/ {opIndexField, "IndexField", objTypeAny, opFlagNone, makeArg4(opArgNameString, opArgNameString, opArgByteData, opArgFieldList)},
|
||||
/*0x6f*/ {opBankField, "BankField", objTypeLocalBankField, opFlagNamed, makeArg5(opArgNameString, opArgNameString, opArgTermObj, opArgByteData, opArgFieldList)},
|
||||
/*0x70*/ {opDataRegion, "DataRegion", objTypeLocalRegionField, opFlagNamed, makeArg4(opArgNameString, opArgTermObj, opArgTermObj, opArgTermObj)},
|
||||
}
|
||||
|
||||
// opcodeMap maps an AML opcode to an entry in the opcode table. Entries with
|
||||
// the value 0xff indicate an invalid/unsupported opcode.
|
||||
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, 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,
|
||||
/*0x30 - 0x37*/ 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,
|
||||
/*0x48 - 0x4f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x50 - 0x57*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x58 - 0x5f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x60 - 0x67*/ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
/*0x68 - 0x6f*/ 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0xff,
|
||||
/*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, 0x3c, 0x3d,
|
||||
/*0x90 - 0x97*/ 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
|
||||
/*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, 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,
|
||||
/*0xe8 - 0xef*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0xf0 - 0xf7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0xf8 - 0xff*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x53,
|
||||
}
|
||||
|
||||
// extendedOpcodeMap maps an AML extended opcode (extOpPrefix + code) to an
|
||||
// entry in the opcode table. Entries with the value 0xff indicate an
|
||||
// invalid/unsupported opcode.
|
||||
var extendedOpcodeMap = [256]uint8{
|
||||
/* 0 1 2 3 4 5 6 7*/
|
||||
/*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, 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,
|
||||
/*0x50 - 0x57*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x58 - 0x5f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0x60 - 0x67*/ 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,
|
||||
/*0x78 - 0x7f*/ 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,
|
||||
/*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,
|
||||
/*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,
|
||||
/*0xe8 - 0xef*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0xf0 - 0xf7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
/*0xf8 - 0xff*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x53,
|
||||
}
|
187
src/gopheros/device/acpi/aml/opcode_test.go
Normal file
187
src/gopheros/device/acpi/aml/opcode_test.go
Normal file
@ -0,0 +1,187 @@
|
||||
package aml
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestOpcodeToString(t *testing.T) {
|
||||
if exp, got := "Acquire", opAcquire.String(); got != exp {
|
||||
t.Fatalf("expected opAcquire.toString() to return %q; got %q", exp, got)
|
||||
}
|
||||
|
||||
if exp, got := "unknown", opcode(0xffff).String(); got != exp {
|
||||
t.Fatalf("expected opcode.String() to return %q; got %q", exp, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpcodeIsX(t *testing.T) {
|
||||
specs := []struct {
|
||||
op opcode
|
||||
testFn func(opcode) bool
|
||||
want bool
|
||||
}{
|
||||
// opIsLocalArg
|
||||
{opLocal0, opIsLocalArg, true},
|
||||
{opLocal1, opIsLocalArg, true},
|
||||
{opLocal2, opIsLocalArg, true},
|
||||
{opLocal3, opIsLocalArg, true},
|
||||
{opLocal4, opIsLocalArg, true},
|
||||
{opLocal5, opIsLocalArg, true},
|
||||
{opLocal6, opIsLocalArg, true},
|
||||
{opLocal7, opIsLocalArg, true},
|
||||
{opArg0, opIsLocalArg, false},
|
||||
{opDivide, opIsLocalArg, false},
|
||||
// opIsMethodArg
|
||||
{opArg0, opIsMethodArg, true},
|
||||
{opArg1, opIsMethodArg, true},
|
||||
{opArg2, opIsMethodArg, true},
|
||||
{opArg3, opIsMethodArg, true},
|
||||
{opArg4, opIsMethodArg, true},
|
||||
{opArg5, opIsMethodArg, true},
|
||||
{opArg6, opIsMethodArg, true},
|
||||
{opLocal7, opIsMethodArg, false},
|
||||
{opIf, opIsMethodArg, false},
|
||||
// opIsArg
|
||||
{opLocal5, opIsArg, true},
|
||||
{opArg1, opIsArg, true},
|
||||
{opDivide, opIsArg, false},
|
||||
// opIsType2
|
||||
{opAcquire, opIsType2, true},
|
||||
{opAdd, opIsType2, true},
|
||||
{opAnd, opIsType2, true},
|
||||
{opBuffer, opIsType2, true},
|
||||
{opConcat, opIsType2, true},
|
||||
{opConcatRes, opIsType2, true},
|
||||
{opCondRefOf, opIsType2, true},
|
||||
{opCopyObject, opIsType2, true},
|
||||
{opDecrement, opIsType2, true},
|
||||
{opDerefOf, opIsType2, true},
|
||||
{opDivide, opIsType2, true},
|
||||
{opFindSetLeftBit, opIsType2, true},
|
||||
{opFindSetRightBit, opIsType2, true},
|
||||
{opFromBCD, opIsType2, true},
|
||||
{opIncrement, opIsType2, true},
|
||||
{opIndex, opIsType2, true},
|
||||
{opLand, opIsType2, true},
|
||||
{opLEqual, opIsType2, true},
|
||||
{opLGreater, opIsType2, true},
|
||||
{opLLess, opIsType2, true},
|
||||
{opMid, opIsType2, true},
|
||||
{opLnot, opIsType2, true},
|
||||
{opLoadTable, opIsType2, true},
|
||||
{opLor, opIsType2, true},
|
||||
{opMatch, opIsType2, true},
|
||||
{opMod, opIsType2, true},
|
||||
{opMultiply, opIsType2, true},
|
||||
{opNand, opIsType2, true},
|
||||
{opNor, opIsType2, true},
|
||||
{opNot, opIsType2, true},
|
||||
{opObjectType, opIsType2, true},
|
||||
{opOr, opIsType2, true},
|
||||
{opPackage, opIsType2, true},
|
||||
{opVarPackage, opIsType2, true},
|
||||
{opRefOf, opIsType2, true},
|
||||
{opShiftLeft, opIsType2, true},
|
||||
{opShiftRight, opIsType2, true},
|
||||
{opSizeOf, opIsType2, true},
|
||||
{opStore, opIsType2, true},
|
||||
{opSubtract, opIsType2, true},
|
||||
{opTimer, opIsType2, true},
|
||||
{opToBCD, opIsType2, true},
|
||||
{opToBuffer, opIsType2, true},
|
||||
{opToDecimalString, opIsType2, true},
|
||||
{opToHexString, opIsType2, true},
|
||||
{opToInteger, opIsType2, true},
|
||||
{opToString, opIsType2, true},
|
||||
{opWait, opIsType2, true},
|
||||
{opXor, opIsType2, true},
|
||||
{opBytePrefix, opIsType2, false},
|
||||
// opIsDataObject
|
||||
{opBytePrefix, opIsDataObject, true},
|
||||
{opWordPrefix, opIsDataObject, true},
|
||||
{opDwordPrefix, opIsDataObject, true},
|
||||
{opQwordPrefix, opIsDataObject, true},
|
||||
{opStringPrefix, opIsDataObject, true},
|
||||
{opZero, opIsDataObject, true},
|
||||
{opOne, opIsDataObject, true},
|
||||
{opOnes, opIsDataObject, true},
|
||||
{opRevision, opIsDataObject, true},
|
||||
{opBuffer, opIsDataObject, true},
|
||||
{opPackage, opIsDataObject, true},
|
||||
{opVarPackage, opIsDataObject, true},
|
||||
{opLor, opIsDataObject, false},
|
||||
// opIsBufferField
|
||||
{opCreateField, opIsBufferField, true},
|
||||
{opCreateBitField, opIsBufferField, true},
|
||||
{opCreateByteField, opIsBufferField, true},
|
||||
{opCreateWordField, opIsBufferField, true},
|
||||
{opCreateDWordField, opIsBufferField, true},
|
||||
{opCreateQWordField, opIsBufferField, true},
|
||||
{opRevision, opIsBufferField, false},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
if got := spec.testFn(spec.op); got != spec.want {
|
||||
t.Errorf("[spec %d] opcode %q: expected to get %t; got %t", specIndex, spec.op, spec.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpArgFlagToString(t *testing.T) {
|
||||
specs := map[opArgFlag]string{
|
||||
opArgTermList: "opArgTermList",
|
||||
opArgTermObj: "opArgTermObj",
|
||||
opArgByteList: "opArgByteList",
|
||||
opArgPackage: "opArgPackage",
|
||||
opArgString: "opArgString",
|
||||
opArgByteData: "opArgByteData",
|
||||
opArgWord: "opArgWord",
|
||||
opArgDword: "opArgDword",
|
||||
opArgQword: "opArgQword",
|
||||
opArgNameString: "opArgNameString",
|
||||
opArgSuperName: "opArgSuperName",
|
||||
opArgSimpleName: "opArgSimpleName",
|
||||
opArgDataRefObj: "opArgDataRefObj",
|
||||
opArgTarget: "opArgTarget",
|
||||
opArgFieldList: "opArgFieldList",
|
||||
opArgFlag(0xff): "",
|
||||
}
|
||||
|
||||
for flag, want := range specs {
|
||||
if got := flag.String(); got != want {
|
||||
t.Errorf("expected %q; got %q", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestFindUnmappedOpcodes is a helper test that pinpoints opcodes that have
|
||||
// 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
|
||||
}
|
||||
|
||||
for tabIndex, info := range opcodeTable {
|
||||
if uint16(info.op) == uint16(opIndex) {
|
||||
t.Errorf("set opcodeMap[0x%02x] = 0x%02x // %s\n", opIndex, tabIndex, info.op.String())
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for opIndex, opRef := range extendedOpcodeMap {
|
||||
// 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-0xff, tabIndex, info.op.String())
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
937
src/gopheros/device/acpi/aml/parser.go
Normal file
937
src/gopheros/device/acpi/aml/parser.go
Normal file
@ -0,0 +1,937 @@
|
||||
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
|
||||
tableHandle uint8
|
||||
}
|
||||
|
||||
// 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 tagging each scoped entity with the supplied table handle. The parser
|
||||
// emits any encountered errors to the specified errWriter.
|
||||
func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDTHeader) *kernel.Error {
|
||||
p.tableHandle = tableHandle
|
||||
p.tableName = tableName
|
||||
p.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 {
|
||||
obj.setTableHandle(p.tableHandle)
|
||||
|
||||
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
|
||||
curScope := p.scopeCurrent()
|
||||
curScope.removeChild(curScope.lastChild())
|
||||
prevObj := curScope.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 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, flagOk := p.parseNumConstant(1)
|
||||
if !flagOk {
|
||||
return false
|
||||
}
|
||||
m.argCount = (uint8(flags) & 0x7) // bits[0:2]
|
||||
m.serialized = (uint8(flags)>>3)&0x1 == 0x1 // bit 3
|
||||
m.syncLevel = (uint8(flags) >> 4) & 0xf // bits[4:7]
|
||||
|
||||
obj = m
|
||||
}
|
||||
|
||||
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, isScopeEnt := obj.(ScopeEntity); isScopeEnt {
|
||||
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
|
||||
}
|
||||
|
||||
curScope := p.scopeCurrent()
|
||||
obj := curScope.lastChild()
|
||||
curScope.removeChild(obj)
|
||||
return obj, true
|
||||
}
|
||||
|
||||
func (p *Parser) makeObjForOpcode(info *opcodeInfo) Entity {
|
||||
var obj Entity
|
||||
|
||||
switch {
|
||||
case info.op == opOpRegion:
|
||||
obj = new(regionEntity)
|
||||
case info.op == opBuffer:
|
||||
obj = new(bufferEntity)
|
||||
case info.op == opMutex:
|
||||
obj = new(mutexEntity)
|
||||
case info.op == opEvent:
|
||||
obj = new(eventEntity)
|
||||
case opIsBufferField(info.op):
|
||||
obj = new(bufferFieldEntity)
|
||||
case info.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().lastChild()
|
||||
p.scopeCurrent().removeChild(arg)
|
||||
}
|
||||
}
|
||||
|
||||
// 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, opcode 0x%2x] invalid arg count: %d\n", p.tableName, p.r.Offset(), uint32(op), len(args))
|
||||
return false
|
||||
}
|
||||
|
||||
name0, ok = args[0].(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
flags, ok = args[1].(uint64)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
case opIndexField: // Field := PkgLength IndexFieldName DataFieldName AccessFlags FieldList
|
||||
if len(args) != 3 {
|
||||
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d, opcode 0x%2x] invalid arg count: %d\n", p.tableName, p.r.Offset(), uint32(op), len(args))
|
||||
return false
|
||||
}
|
||||
|
||||
name0, ok = args[0].(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
name1, ok = args[1].(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
flags, ok = args[2].(uint64)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Decode flags
|
||||
accessType := FieldAccessType(flags & 0xf) // access type; bits[0:3]
|
||||
lock := (flags>>4)&0x1 == 0x1 // lock; bit 4
|
||||
updateRule := FieldUpdateRule((flags >> 5) & 0x3) // update rule; bits[5:6]
|
||||
|
||||
var (
|
||||
connectionName string
|
||||
resolvedConnection Entity
|
||||
)
|
||||
|
||||
for p.r.Offset() < maxReadOffset {
|
||||
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{
|
||||
tableHandle: p.tableHandle,
|
||||
op: op,
|
||||
name: unitName,
|
||||
},
|
||||
bitOffset: curBitOffset,
|
||||
bitWidth: bitWidth,
|
||||
lock: lock,
|
||||
updateRule: updateRule,
|
||||
accessType: accessType,
|
||||
accessAttrib: accessAttrib,
|
||||
byteCount: accessByteCount,
|
||||
},
|
||||
connectionName: connectionName,
|
||||
resolvedConnection: resolvedConnection,
|
||||
regionName: name0,
|
||||
})
|
||||
case opIndexField:
|
||||
p.scopeCurrent().Append(&indexFieldEntity{
|
||||
fieldEntity: fieldEntity{
|
||||
namedEntity: namedEntity{
|
||||
tableHandle: p.tableHandle,
|
||||
op: op,
|
||||
name: unitName,
|
||||
},
|
||||
bitOffset: curBitOffset,
|
||||
bitWidth: bitWidth,
|
||||
lock: lock,
|
||||
updateRule: updateRule,
|
||||
accessType: accessType,
|
||||
accessAttrib: accessAttrib,
|
||||
byteCount: accessByteCount,
|
||||
},
|
||||
connectionName: connectionName,
|
||||
resolvedConnection: resolvedConnection,
|
||||
indexRegName: name0,
|
||||
dataRegName: name1,
|
||||
})
|
||||
}
|
||||
|
||||
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 actually a NullName
|
||||
p.r.SetOffset(curOffset + 1)
|
||||
return &constEntity{op: opStringPrefix, val: ""}, true
|
||||
case opIsArg(nextOpcode.op) || nextOpcode.op == opRefOf || nextOpcode.op == opDerefOf || nextOpcode.op == opIndex || nextOpcode.op == opDebug: // LocalObj | ArgObj | Type6 | DebugObj
|
||||
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 {
|
||||
obj := p.scopeCurrent().lastChild()
|
||||
p.scopeCurrent().removeChild(obj)
|
||||
return obj, 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)
|
||||
p.r.ReadByte()
|
||||
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()
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
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]
|
||||
}
|
611
src/gopheros/device/acpi/aml/parser_test.go
Normal file
611
src/gopheros/device/acpi/aml/parser_test.go
Normal file
@ -0,0 +1,611 @@
|
||||
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(0, tableName, resolver.LookupTable(tableName)); err != nil {
|
||||
t.Errorf("[spec %d] [%s]: %v", specIndex, tableName, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableHandleAssignment(t *testing.T) {
|
||||
var resolver = mockResolver{tableFiles: []string{"parser-testsuite-DSDT.aml"}}
|
||||
|
||||
// Create default scopes
|
||||
rootNS := &scopeEntity{op: opScope, name: `\`}
|
||||
rootNS.Append(&scopeEntity{op: opScope, name: `_GPE`}) // General events in GPE register block
|
||||
rootNS.Append(&scopeEntity{op: opScope, name: `_PR_`}) // ACPI 1.0 processor namespace
|
||||
rootNS.Append(&scopeEntity{op: opScope, name: `_SB_`}) // System bus with all device objects
|
||||
rootNS.Append(&scopeEntity{op: opScope, name: `_SI_`}) // System indicators
|
||||
rootNS.Append(&scopeEntity{op: opScope, name: `_TZ_`}) // ACPI 1.0 thermal zone namespace
|
||||
|
||||
p := NewParser(ioutil.Discard, rootNS)
|
||||
|
||||
expHandle := uint8(42)
|
||||
tableName := "parser-testsuite-DSDT"
|
||||
if err := p.ParseAML(expHandle, tableName, resolver.LookupTable(tableName)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Drop all entities that were assigned the handle value
|
||||
var unloadList []Entity
|
||||
scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool {
|
||||
if ent.TableHandle() == expHandle {
|
||||
unloadList = append(unloadList, ent)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
for _, ent := range unloadList {
|
||||
if p := ent.Parent(); p != nil {
|
||||
p.removeChild(ent)
|
||||
}
|
||||
}
|
||||
|
||||
// We should end up with the original tree
|
||||
var visitedNodes int
|
||||
scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool {
|
||||
visitedNodes++
|
||||
if ent.TableHandle() == expHandle {
|
||||
t.Errorf("encountered entity that should have been pruned: %#+v", ent)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if exp := len(rootNS.Children()) + 1; visitedNodes != exp {
|
||||
t.Errorf("expected to visit %d nodes; visited %d", exp, visitedNodes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePkgLength(t *testing.T) {
|
||||
specs := []struct {
|
||||
payload []byte
|
||||
exp uint32
|
||||
}{
|
||||
// lead byte bits (6:7) indicate 1 extra byte for the len. The
|
||||
// parsed length will use bits 0:3 from the lead byte plus
|
||||
// the full 8 bits of the following byte.
|
||||
{
|
||||
[]byte{1<<6 | 7, 255},
|
||||
4087,
|
||||
},
|
||||
// lead byte bits (6:7) indicate 2 extra bytes for the len. The
|
||||
// parsed length will use bits 0:3 from the lead byte plus
|
||||
// the full 8 bits of the following bytes.
|
||||
{
|
||||
[]byte{2<<6 | 8, 255, 128},
|
||||
528376,
|
||||
},
|
||||
// lead byte bits (6:7) indicate 3 extra bytes for the len. The
|
||||
// parsed length will use bits 0:3 from the lead byte plus
|
||||
// the full 8 bits of the following bytes.
|
||||
{
|
||||
[]byte{3<<6 | 6, 255, 128, 42},
|
||||
44568566,
|
||||
},
|
||||
}
|
||||
|
||||
p := &Parser{errWriter: ioutil.Discard}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
mockParserPayload(p, spec.payload)
|
||||
got, ok := p.parsePkgLength()
|
||||
if !ok {
|
||||
t.Errorf("[spec %d] parsePkgLength returned false", specIndex)
|
||||
continue
|
||||
}
|
||||
|
||||
if got != spec.exp {
|
||||
t.Errorf("[spec %d] expected parsePkgLength to return %d; got %d", specIndex, spec.exp, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParserErrorHandling(t *testing.T) {
|
||||
p := &Parser{
|
||||
errWriter: ioutil.Discard,
|
||||
}
|
||||
|
||||
t.Run("ParseAML errors", func(t *testing.T) {
|
||||
t.Run("parseObjList error", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
|
||||
// Setup resolver to serve an AML stream containing an invalid opcode
|
||||
header := mockParserPayload(p, []byte{0x5b, 0x00})
|
||||
|
||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
||||
t.Fatal("expected ParseAML to return an error")
|
||||
}
|
||||
|
||||
// Setup resolver to serve an AML stream containing an incomplete extended opcode
|
||||
header = mockParserPayload(p, []byte{0x5b})
|
||||
|
||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
||||
t.Fatal("expected ParseAML to return an error")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("unresolved entities", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
|
||||
// Inject a reference entity to the tree
|
||||
p.root.Append(&namedReference{
|
||||
targetName: "UNKNOWN",
|
||||
})
|
||||
|
||||
// Setup resolver to serve an empty AML stream
|
||||
header := mockParserPayload(p, nil)
|
||||
|
||||
if err := p.ParseAML(uint8(42), "DSDT", header); err != errResolvingEntities {
|
||||
t.Fatalf("expected ParseAML to return errResolvingEntities; got %v", err)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("parseObj errors", func(t *testing.T) {
|
||||
t.Run("parsePkgLength error", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
|
||||
// Setup resolver to serve an AML stream containing an incomplete
|
||||
// buffer specification
|
||||
header := mockParserPayload(p, []byte{byte(opBuffer)})
|
||||
|
||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
||||
t.Fatal("expected parsePkgLength to return an error")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("incomplete object list", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
|
||||
// Setup resolver to serve an AML stream containing an incomplete
|
||||
// buffer arglist specification
|
||||
header := mockParserPayload(p, []byte{byte(opBuffer), 0x10})
|
||||
|
||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
||||
t.Fatal("expected parsePkgLength to return an error")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("finalizeObj errors", func(t *testing.T) {
|
||||
t.Run("else without matching if", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
p.root.Append(&constEntity{val: 0x42})
|
||||
p.root.Append(&scopeEntity{op: opElse})
|
||||
|
||||
// Setup resolver to serve an AML stream containing an
|
||||
// empty else statement without a matching if
|
||||
header := mockParserPayload(p, []byte{byte(opElse), 0x0})
|
||||
|
||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
||||
t.Fatal("expected finalizeObj to return an error")
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
t.Run("parseScope errors", func(t *testing.T) {
|
||||
t.Run("parseNameString error", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
|
||||
header := mockParserPayload(p, []byte{
|
||||
byte(opScope),
|
||||
0x10, // pkglen
|
||||
})
|
||||
|
||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
||||
t.Fatal("expected parseScope to return an error")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("unknown scope", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
|
||||
header := mockParserPayload(p, []byte{
|
||||
byte(opScope),
|
||||
0x10, // pkglen
|
||||
'F', 'O', 'O', 'F',
|
||||
})
|
||||
|
||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
||||
t.Fatal("expected parseScope to return an error")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("nameless scope", func(t *testing.T) {
|
||||
p.root = &scopeEntity{}
|
||||
|
||||
header := mockParserPayload(p, []byte{
|
||||
byte(opScope),
|
||||
0x02, // pkglen
|
||||
'\\', // scope name: "\" (root scope)
|
||||
0x00, // null string
|
||||
})
|
||||
|
||||
if err := p.ParseAML(uint8(42), "DSDT", header); err == nil {
|
||||
t.Fatal("expected parseScope to return an error")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("parseNamespacedObj errors", func(t *testing.T) {
|
||||
t.Run("parseNameString error", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
|
||||
mockParserPayload(p, nil)
|
||||
|
||||
if p.parseNamespacedObj(opDevice, 10) {
|
||||
t.Fatal("expected parseNamespacedObj to return false")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("scope lookup error", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
|
||||
header := mockParserPayload(p, []byte{'^', 'F', 'A', 'B', 'C'})
|
||||
|
||||
p.scopeEnter(p.root)
|
||||
if p.parseNamespacedObj(opDevice, header.Length) {
|
||||
t.Fatal("expected parseNamespacedObj to return false")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("error parsing method arg count", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
|
||||
header := mockParserPayload(p, []byte{'F', 'A', 'B', 'C'})
|
||||
|
||||
p.scopeEnter(p.root)
|
||||
if p.parseNamespacedObj(opMethod, header.Length) {
|
||||
t.Fatal("expected parseNamespacedObj to return false")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("parseArg bytelist errors", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
|
||||
mockParserPayload(p, nil)
|
||||
|
||||
if p.parseArg(new(opcodeInfo), new(unnamedEntity), 0, opArgByteList, 42) {
|
||||
t.Fatal("expected parseNamespacedObj to return false")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("parseMethodInvocationOrNameRef errors", func(t *testing.T) {
|
||||
t.Run("missing args", func(t *testing.T) {
|
||||
p.root = &scopeEntity{op: opScope, name: `\`}
|
||||
p.root.Append(&Method{
|
||||
scopeEntity: scopeEntity{name: "MTHD"},
|
||||
argCount: 10,
|
||||
})
|
||||
|
||||
mockParserPayload(p, []byte{
|
||||
'M', 'T', 'H', 'D',
|
||||
byte(opIf), // Incomplete type2 opcode
|
||||
})
|
||||
|
||||
p.scopeEnter(p.root)
|
||||
if p.parseMethodInvocationOrNameRef() {
|
||||
t.Fatal("expected parseMethodInvocationOrNameRef to return false")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("parseFieldList errors", func(t *testing.T) {
|
||||
specs := []struct {
|
||||
op opcode
|
||||
args []interface{}
|
||||
maxReadOffset uint32
|
||||
payload []byte
|
||||
}{
|
||||
// Invalid arg count for opField
|
||||
{
|
||||
opField,
|
||||
nil,
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
// Wrong arg type for opField
|
||||
{
|
||||
opField,
|
||||
[]interface{}{0, uint64(42)},
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
opField,
|
||||
[]interface{}{"FLD0", uint32(42)},
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
// Invalid arg count for opIndexField
|
||||
{
|
||||
opIndexField,
|
||||
nil,
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
// Wrong arg type for opIndexField
|
||||
{
|
||||
opIndexField,
|
||||
[]interface{}{0, "FLD1", "FLD2"},
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
opIndexField,
|
||||
[]interface{}{"FLD0", 0, "FLD2"},
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
opIndexField,
|
||||
[]interface{}{"FLD0", "FLD1", 0},
|
||||
0,
|
||||
nil,
|
||||
},
|
||||
// unexpected EOF parsing fields
|
||||
{
|
||||
opField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
nil,
|
||||
},
|
||||
// reserved field (0x00) with missing pkgLen
|
||||
{
|
||||
opField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0x00},
|
||||
},
|
||||
// access field (0x01) with missing accessType
|
||||
{
|
||||
opField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0x01},
|
||||
},
|
||||
// access field (0x01) with missing attribute byte
|
||||
{
|
||||
opField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0x01, 0x01},
|
||||
},
|
||||
// connect field (0x02) with incomplete TermObject => Buffer arg
|
||||
{
|
||||
opField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0x02, byte(opBuffer)},
|
||||
},
|
||||
// extended access field (0x03) with missing ext. accessType
|
||||
{
|
||||
opField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0x03},
|
||||
},
|
||||
// extended access field (0x03) with missing ext. attribute byte
|
||||
{
|
||||
opField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0x03, 0x01},
|
||||
},
|
||||
// extended access field (0x03) with missing access byte count value
|
||||
{
|
||||
opField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0x03, 0x01, 0x02},
|
||||
},
|
||||
// named field with invalid name
|
||||
{
|
||||
opField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{0xff},
|
||||
},
|
||||
// named field with invalid pkgLen
|
||||
{
|
||||
opField,
|
||||
[]interface{}{"FLD0", uint64(42)},
|
||||
128,
|
||||
[]byte{'N', 'A', 'M', 'E'},
|
||||
},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
mockParserPayload(p, spec.payload)
|
||||
|
||||
if p.parseFieldList(spec.op, spec.args, spec.maxReadOffset) {
|
||||
t.Errorf("[spec %d] expected parseFieldLis to return false", specIndex)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("parsePkgLength errors", func(t *testing.T) {
|
||||
specs := [][]byte{
|
||||
// lead byte bits (6:7) indicate 1 extra byte that is missing
|
||||
[]byte{1 << 6},
|
||||
// lead byte bits (6:7) indicate 2 extra bytes with the 1st and then 2nd missing
|
||||
[]byte{2 << 6},
|
||||
[]byte{2 << 6, 0x1},
|
||||
// lead byte bits (6:7) indicate 3 extra bytes with the 1st and then 2nd and then 3rd missing
|
||||
[]byte{3 << 6},
|
||||
[]byte{3 << 6, 0x1},
|
||||
[]byte{3 << 6, 0x1, 0x2},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
mockParserPayload(p, spec)
|
||||
|
||||
if _, ok := p.parsePkgLength(); ok {
|
||||
t.Errorf("[spec %d] expected parsePkgLength to return false", specIndex)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("parseString errors", func(t *testing.T) {
|
||||
specs := [][]byte{
|
||||
// Unexpected EOF before terminating null byte
|
||||
[]byte{'A'},
|
||||
// Characters outside the allowed [0x01, 0x7f] range
|
||||
[]byte{'A', 0xba, 0xdf, 0x00},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
mockParserPayload(p, spec)
|
||||
|
||||
if _, ok := p.parseString(); ok {
|
||||
t.Errorf("[spec %d] expected parseString to return false", specIndex)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("parseTarget errors", func(t *testing.T) {
|
||||
t.Run("unexpected opcode", func(t *testing.T) {
|
||||
// Unexpected opcode
|
||||
mockParserPayload(p, []byte{byte(opAnd)})
|
||||
|
||||
if _, ok := p.parseTarget(); ok {
|
||||
t.Error("expected parseTarget to return false")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("corrupted data", func(t *testing.T) {
|
||||
// Invalid opcode and not a method invocation nor a namestring
|
||||
mockParserPayload(p, []byte{0xba, 0xad})
|
||||
|
||||
if _, ok := p.parseTarget(); ok {
|
||||
t.Error("expected parseTarget to return false")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("parseNameString errors", func(t *testing.T) {
|
||||
t.Run("EOF while parsing path prefix", func(t *testing.T) {
|
||||
mockParserPayload(p, []byte{'^'})
|
||||
|
||||
if _, ok := p.parseNameString(); ok {
|
||||
t.Error("expected parseNameString to return false")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("EOF while parsing multiname path", func(t *testing.T) {
|
||||
specs := [][]byte{
|
||||
// multiname path prefix but no data following
|
||||
[]byte{0x2f},
|
||||
[]byte{
|
||||
0x2f, // multiname path prefix
|
||||
0x0, // no segments (segments must be > 0)
|
||||
},
|
||||
[]byte{
|
||||
0x2f, // multiname path prefix
|
||||
0x1, // 1 expected segment but no more data available
|
||||
},
|
||||
[]byte{
|
||||
'\\', // RootChar and no more data
|
||||
},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
mockParserPayload(p, spec)
|
||||
if _, ok := p.parseNameString(); ok {
|
||||
t.Errorf("[spec %d] expected parseNameString to return false", specIndex)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func mockParserPayload(p *Parser, payload []byte) *table.SDTHeader {
|
||||
resolver := fixedPayloadResolver{payload}
|
||||
header := resolver.LookupTable("DSDT")
|
||||
p.r.Init(
|
||||
uintptr(unsafe.Pointer(header)),
|
||||
header.Length,
|
||||
uint32(unsafe.Sizeof(table.SDTHeader{})),
|
||||
)
|
||||
|
||||
return resolver.LookupTable("DSDT")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type fixedPayloadResolver struct {
|
||||
payload []byte
|
||||
}
|
||||
|
||||
func (f fixedPayloadResolver) LookupTable(name string) *table.SDTHeader {
|
||||
hdrLen := int(unsafe.Sizeof(table.SDTHeader{}))
|
||||
buf := make([]byte, len(f.payload)+hdrLen)
|
||||
copy(buf[hdrLen:], f.payload)
|
||||
|
||||
hdr := (*table.SDTHeader)(unsafe.Pointer(&buf[0]))
|
||||
hdr.Length = uint32(len(buf))
|
||||
|
||||
return hdr
|
||||
}
|
189
src/gopheros/device/acpi/aml/scope.go
Normal file
189
src/gopheros/device/acpi/aml/scope.go
Normal file
@ -0,0 +1,189 @@
|
||||
package aml
|
||||
|
||||
import "strings"
|
||||
|
||||
// Visitor is a function invoked by the VM for each AML tree entity that matches
|
||||
// a particular type. The return value controls whether the children of this
|
||||
// entity should also be visited.
|
||||
type Visitor func(depth int, obj Entity) (keepRecursing bool)
|
||||
|
||||
// EntityType defines the type of entity that visitors should inspect.
|
||||
type EntityType uint8
|
||||
|
||||
// The list of supported EntityType values. EntityTypeAny works as a wildcard
|
||||
// allowing the visitor to inspect all entities in the AML tree.
|
||||
const (
|
||||
EntityTypeAny EntityType = iota
|
||||
EntityTypeDevice
|
||||
EntityTypeProcessor
|
||||
EntityTypePowerResource
|
||||
EntityTypeThermalZone
|
||||
EntityTypeMethod
|
||||
)
|
||||
|
||||
// scopeVisit descends a scope hierarchy and invokes visitorFn for each entity
|
||||
// that matches entType.
|
||||
func scopeVisit(depth int, ent Entity, entType EntityType, visitorFn Visitor) bool {
|
||||
op := ent.getOpcode()
|
||||
switch {
|
||||
case (entType == EntityTypeAny) ||
|
||||
(entType == EntityTypeDevice && op == opDevice) ||
|
||||
(entType == EntityTypeProcessor && op == opProcessor) ||
|
||||
(entType == EntityTypePowerResource && op == opPowerRes) ||
|
||||
(entType == EntityTypeThermalZone && op == opThermalZone) ||
|
||||
(entType == EntityTypeMethod && op == opMethod):
|
||||
// If the visitor returned false we should not visit the children
|
||||
if !visitorFn(depth, ent) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// If the entity defines a scope we need to visit the child entities.
|
||||
if scopeEnt, ok := ent.(ScopeEntity); ok {
|
||||
for _, child := range scopeEnt.Children() {
|
||||
scopeVisit(depth+1, child, entType, visitorFn)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// scopeResolvePath examines a path expression and attempts to break it down
|
||||
// into a parent and child segment. The parent segment is looked up via the
|
||||
// regular scope rules specified in page 252 of the ACPI 6.2 spec. If the
|
||||
// parent scope is found then the function returns back the parent entity and
|
||||
// the name of the child that should be appended to it. If the expression
|
||||
// lookup fails then the function returns nil, "".
|
||||
func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent ScopeEntity, name string) {
|
||||
if len(expr) <= 1 {
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// Pattern looks like \FOO or ^+BAR or BAZ (relative to curScope)
|
||||
lastDotIndex := strings.LastIndexByte(expr, '.')
|
||||
if lastDotIndex == -1 {
|
||||
switch expr[0] {
|
||||
case '\\':
|
||||
return rootScope, expr[1:]
|
||||
case '^':
|
||||
lastHatIndex := strings.LastIndexByte(expr, '^')
|
||||
if target := scopeFind(curScope, rootScope, expr[:lastHatIndex+1]); target != nil {
|
||||
return target.(ScopeEntity), expr[lastHatIndex+1:]
|
||||
}
|
||||
|
||||
return nil, ""
|
||||
default:
|
||||
return curScope, expr
|
||||
}
|
||||
}
|
||||
|
||||
// Pattern looks like: \FOO.BAR.BAZ or ^+FOO.BAR.BAZ or FOO.BAR.BAZ
|
||||
if target := scopeFind(curScope, rootScope, expr[:lastDotIndex]); target != nil {
|
||||
return target.(ScopeEntity), expr[lastDotIndex+1:]
|
||||
}
|
||||
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// scopeFind attempts to find an object with the given name using the rules
|
||||
// specified in page 252 of the ACPI 6.2 spec:
|
||||
//
|
||||
// There are two types of namespace paths: an absolute namespace path (that is,
|
||||
// one that starts with a ‘\’ prefix), and a relative namespace path (that is,
|
||||
// one that is relative to the current namespace). The namespace search rules
|
||||
// discussed above, only apply to single NameSeg paths, which is a relative
|
||||
// namespace path. For those relative name paths that contain multiple NameSegs
|
||||
// or Parent Prefixes, ‘^’, the search rules do not apply. If the search rules
|
||||
// do not apply to a relative namespace path, the namespace object is looked up
|
||||
// relative to the current namespace
|
||||
func scopeFind(curScope, rootScope ScopeEntity, name string) Entity {
|
||||
nameLen := len(name)
|
||||
if nameLen == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case name[0] == '\\': // relative to the root scope
|
||||
if nameLen > 1 {
|
||||
return scopeFindRelative(rootScope, name[1:])
|
||||
}
|
||||
|
||||
// Name was just `\`; this matches the root namespace
|
||||
return rootScope
|
||||
case name[0] == '^': // relative to the parent scope(s)
|
||||
for startIndex := 0; startIndex < nameLen; startIndex++ {
|
||||
switch name[startIndex] {
|
||||
case '^':
|
||||
curScope = curScope.Parent()
|
||||
|
||||
// No parent to visit
|
||||
if curScope == nil {
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
// Found the start of the name. Look it up relative to curNs
|
||||
return scopeFindRelative(curScope, name[startIndex:])
|
||||
}
|
||||
}
|
||||
|
||||
// Name was just a sequence of '^'; this matches the last curScope value
|
||||
return curScope
|
||||
case strings.ContainsRune(name, '.'):
|
||||
// If the name contains any '.' then we still need to look it
|
||||
// up relative to the current scope
|
||||
return scopeFindRelative(curScope, name)
|
||||
default:
|
||||
// We can apply the search rules described by the spec
|
||||
for s := curScope; s != nil; s = s.Parent() {
|
||||
for _, child := range s.Children() {
|
||||
if child.Name() == name {
|
||||
return child
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not found
|
||||
return nil
|
||||
}
|
||||
|
||||
// scopeFindRelative returns the Entity referenced by path relative
|
||||
// to the provided Namespace. If the name contains dots, each segment
|
||||
// is used to access a nested namespace. If the path does not point
|
||||
// to a NamedObject then lookupRelativeTo returns back nil.
|
||||
func scopeFindRelative(ns ScopeEntity, path string) Entity {
|
||||
var matchName string
|
||||
matchNextPathSegment:
|
||||
for {
|
||||
dotSepIndex := strings.IndexRune(path, '.')
|
||||
if dotSepIndex != -1 {
|
||||
matchName = path[:dotSepIndex]
|
||||
path = path[dotSepIndex+1:]
|
||||
|
||||
// Search for a scoped child named "matchName"
|
||||
for _, child := range ns.Children() {
|
||||
childNs, ok := child.(ScopeEntity)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if childNs.Name() == matchName {
|
||||
ns = childNs
|
||||
continue matchNextPathSegment
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Search for a child named "name"
|
||||
for _, child := range ns.Children() {
|
||||
if child.Name() == path {
|
||||
return child
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next segment in the path was not found or last segment not found
|
||||
break
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
264
src/gopheros/device/acpi/aml/scope_test.go
Normal file
264
src/gopheros/device/acpi/aml/scope_test.go
Normal file
@ -0,0 +1,264 @@
|
||||
package aml
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestScopeVisit(t *testing.T) {
|
||||
scopeMap := genTestScopes()
|
||||
root := scopeMap[`\`].(*scopeEntity)
|
||||
|
||||
// Append special entities under IDE0
|
||||
ide := scopeMap["IDE0"].(*scopeEntity)
|
||||
ide.Append(&Device{})
|
||||
ide.Append(&namedEntity{op: opProcessor})
|
||||
ide.Append(&namedEntity{op: opProcessor})
|
||||
ide.Append(&namedEntity{op: opPowerRes})
|
||||
ide.Append(&namedEntity{op: opPowerRes})
|
||||
ide.Append(&namedEntity{op: opPowerRes})
|
||||
ide.Append(&namedEntity{op: opThermalZone})
|
||||
ide.Append(&namedEntity{op: opThermalZone})
|
||||
ide.Append(&namedEntity{op: opThermalZone})
|
||||
ide.Append(&namedEntity{op: opThermalZone})
|
||||
ide.Append(&Method{})
|
||||
ide.Append(&Method{})
|
||||
ide.Append(&Method{})
|
||||
ide.Append(&Method{})
|
||||
ide.Append(&Method{})
|
||||
|
||||
specs := []struct {
|
||||
searchType EntityType
|
||||
keepRecursing bool
|
||||
wantHits int
|
||||
}{
|
||||
{EntityTypeAny, true, 21},
|
||||
{EntityTypeAny, false, 1},
|
||||
{EntityTypeDevice, true, 1},
|
||||
{EntityTypeProcessor, true, 2},
|
||||
{EntityTypePowerResource, true, 3},
|
||||
{EntityTypeThermalZone, true, 4},
|
||||
{EntityTypeMethod, true, 5},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
var hits int
|
||||
scopeVisit(0, root, spec.searchType, func(_ int, obj Entity) bool {
|
||||
hits++
|
||||
return spec.keepRecursing
|
||||
})
|
||||
|
||||
if hits != spec.wantHits {
|
||||
t.Errorf("[spec %d] expected visitor to be called %d times; got %d", specIndex, spec.wantHits, hits)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestScopeResolvePath(t *testing.T) {
|
||||
scopeMap := genTestScopes()
|
||||
|
||||
specs := []struct {
|
||||
curScope ScopeEntity
|
||||
pathExpr string
|
||||
wantParent Entity
|
||||
wantName string
|
||||
}{
|
||||
{
|
||||
scopeMap["IDE0"].(*scopeEntity),
|
||||
`\_SB_`,
|
||||
scopeMap[`\`],
|
||||
"_SB_",
|
||||
},
|
||||
{
|
||||
scopeMap["IDE0"].(*scopeEntity),
|
||||
`^FOO`,
|
||||
scopeMap[`PCI0`],
|
||||
"FOO",
|
||||
},
|
||||
{
|
||||
scopeMap["IDE0"].(*scopeEntity),
|
||||
`^^FOO`,
|
||||
scopeMap[`_SB_`],
|
||||
"FOO",
|
||||
},
|
||||
{
|
||||
scopeMap["IDE0"].(*scopeEntity),
|
||||
`_ADR`,
|
||||
scopeMap[`IDE0`],
|
||||
"_ADR",
|
||||
},
|
||||
// Paths with dots
|
||||
{
|
||||
scopeMap["IDE0"].(*scopeEntity),
|
||||
`\_SB_.PCI0.IDE0._ADR`,
|
||||
scopeMap[`IDE0`],
|
||||
"_ADR",
|
||||
},
|
||||
{
|
||||
scopeMap["PCI0"].(*scopeEntity),
|
||||
`IDE0._ADR`,
|
||||
scopeMap[`IDE0`],
|
||||
"_ADR",
|
||||
},
|
||||
{
|
||||
scopeMap["PCI0"].(*scopeEntity),
|
||||
`_CRS`,
|
||||
scopeMap[`PCI0`],
|
||||
"_CRS",
|
||||
},
|
||||
// Bad queries
|
||||
{
|
||||
scopeMap["PCI0"].(*scopeEntity),
|
||||
`FOO.BAR.BAZ`,
|
||||
nil,
|
||||
"",
|
||||
},
|
||||
{
|
||||
scopeMap["PCI0"].(*scopeEntity),
|
||||
``,
|
||||
nil,
|
||||
"",
|
||||
},
|
||||
{
|
||||
scopeMap["PCI0"].(*scopeEntity),
|
||||
`\`,
|
||||
nil,
|
||||
"",
|
||||
},
|
||||
{
|
||||
scopeMap["PCI0"].(*scopeEntity),
|
||||
`^^^^^^^^^BADPATH`,
|
||||
nil,
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
root := scopeMap[`\`].(*scopeEntity)
|
||||
for specIndex, spec := range specs {
|
||||
gotParent, gotName := scopeResolvePath(spec.curScope, root, spec.pathExpr)
|
||||
if !reflect.DeepEqual(gotParent, spec.wantParent) {
|
||||
t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.wantParent, gotParent)
|
||||
continue
|
||||
}
|
||||
|
||||
if gotName != spec.wantName {
|
||||
t.Errorf("[spec %d] expected lookup to return node name %q; got %q", specIndex, spec.wantName, gotName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestScopeFind(t *testing.T) {
|
||||
scopeMap := genTestScopes()
|
||||
|
||||
specs := []struct {
|
||||
curScope ScopeEntity
|
||||
lookup string
|
||||
want Entity
|
||||
}{
|
||||
// Search rules do not apply for these cases
|
||||
{
|
||||
scopeMap["PCI0"].(*scopeEntity),
|
||||
`\`,
|
||||
scopeMap[`\`],
|
||||
},
|
||||
{
|
||||
scopeMap["PCI0"].(*scopeEntity),
|
||||
"IDE0._ADR",
|
||||
scopeMap["_ADR"],
|
||||
},
|
||||
{
|
||||
scopeMap["IDE0"].(*scopeEntity),
|
||||
"^^PCI0.IDE0._ADR",
|
||||
scopeMap["_ADR"],
|
||||
},
|
||||
{
|
||||
scopeMap["IDE0"].(*scopeEntity),
|
||||
`\_SB_.PCI0.IDE0._ADR`,
|
||||
scopeMap["_ADR"],
|
||||
},
|
||||
{
|
||||
scopeMap["IDE0"].(*scopeEntity),
|
||||
`\_SB_.PCI0`,
|
||||
scopeMap["PCI0"],
|
||||
},
|
||||
{
|
||||
scopeMap["IDE0"].(*scopeEntity),
|
||||
`^`,
|
||||
scopeMap["PCI0"],
|
||||
},
|
||||
// Bad queries
|
||||
{
|
||||
scopeMap["_SB_"].(*scopeEntity),
|
||||
"PCI0.USB._CRS",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
scopeMap["IDE0"].(*scopeEntity),
|
||||
"^^^^^^^^^^^^^^^^^^^",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
scopeMap["IDE0"].(*scopeEntity),
|
||||
`^^^^^^^^^^^FOO`,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
scopeMap["IDE0"].(*scopeEntity),
|
||||
"FOO",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
scopeMap["IDE0"].(*scopeEntity),
|
||||
"",
|
||||
nil,
|
||||
},
|
||||
// Search rules apply for these cases
|
||||
{
|
||||
scopeMap["IDE0"].(*scopeEntity),
|
||||
"_CRS",
|
||||
scopeMap["_CRS"],
|
||||
},
|
||||
}
|
||||
|
||||
root := scopeMap[`\`].(*scopeEntity)
|
||||
for specIndex, spec := range specs {
|
||||
if got := scopeFind(spec.curScope, root, spec.lookup); !reflect.DeepEqual(got, spec.want) {
|
||||
t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func genTestScopes() map[string]Entity {
|
||||
// Setup the example tree from page 252 of the acpi 6.2 spec
|
||||
// \
|
||||
// SB
|
||||
// \
|
||||
// PCI0
|
||||
// | _CRS
|
||||
// \
|
||||
// IDE0
|
||||
// | _ADR
|
||||
ideScope := &scopeEntity{name: `IDE0`}
|
||||
pciScope := &scopeEntity{name: `PCI0`}
|
||||
sbScope := &scopeEntity{name: `_SB_`}
|
||||
rootScope := &scopeEntity{name: `\`}
|
||||
|
||||
adr := &namedEntity{name: `_ADR`}
|
||||
crs := &namedEntity{name: `_CRS`}
|
||||
|
||||
// Setup tree
|
||||
ideScope.Append(adr)
|
||||
pciScope.Append(crs)
|
||||
pciScope.Append(ideScope)
|
||||
sbScope.Append(pciScope)
|
||||
rootScope.Append(sbScope)
|
||||
|
||||
return map[string]Entity{
|
||||
"IDE0": ideScope,
|
||||
"PCI0": pciScope,
|
||||
"_SB_": sbScope,
|
||||
"\\": rootScope,
|
||||
"_ADR": adr,
|
||||
"_CRS": crs,
|
||||
}
|
||||
}
|
84
src/gopheros/device/acpi/aml/stream_reader.go
Normal file
84
src/gopheros/device/acpi/aml/stream_reader.go
Normal file
@ -0,0 +1,84 @@
|
||||
package aml
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidUnreadByte = errors.New("amlStreamReader: invalid use of UnreadByte")
|
||||
)
|
||||
|
||||
type amlStreamReader struct {
|
||||
offset uint32
|
||||
data []byte
|
||||
}
|
||||
|
||||
// Init sets up the reader so it can read up to dataLen bytes from the virtual
|
||||
// memory address dataAddr. If a non-zero initialOffset is specified, it will
|
||||
// be used as the current offset in the stream.
|
||||
func (r *amlStreamReader) Init(dataAddr uintptr, dataLen, initialOffset uint32) {
|
||||
// Overlay a byte slice on top of the memory block to be accessed.
|
||||
r.data = *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Len: int(dataLen),
|
||||
Cap: int(dataLen),
|
||||
Data: dataAddr,
|
||||
}))
|
||||
|
||||
r.SetOffset(initialOffset)
|
||||
}
|
||||
|
||||
// EOF returns true if the end of the stream has been reached.
|
||||
func (r *amlStreamReader) EOF() bool {
|
||||
return r.offset == uint32(len(r.data))
|
||||
}
|
||||
|
||||
// ReadByte returns the next byte from the stream.
|
||||
func (r *amlStreamReader) ReadByte() (byte, error) {
|
||||
if r.EOF() {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
r.offset++
|
||||
return r.data[r.offset-1], nil
|
||||
}
|
||||
|
||||
// PeekByte returns the next byte from the stream without advancing the read pointer.
|
||||
func (r *amlStreamReader) PeekByte() (byte, error) {
|
||||
if r.EOF() {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
return r.data[r.offset], nil
|
||||
}
|
||||
|
||||
// LastByte returns the last byte read off the stream
|
||||
func (r *amlStreamReader) LastByte() (byte, error) {
|
||||
if r.offset == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
return r.data[r.offset-1], nil
|
||||
}
|
||||
|
||||
// UnreadByte moves back the read pointer by one byte.
|
||||
func (r *amlStreamReader) UnreadByte() error {
|
||||
if r.offset == 0 {
|
||||
return errInvalidUnreadByte
|
||||
}
|
||||
|
||||
r.offset--
|
||||
return nil
|
||||
}
|
||||
|
||||
// Offset returns the current offset.
|
||||
func (r *amlStreamReader) Offset() uint32 {
|
||||
return r.offset
|
||||
}
|
||||
|
||||
// SetOffset sets the reader offset to the supplied value.
|
||||
func (r *amlStreamReader) SetOffset(off uint32) {
|
||||
r.offset = off
|
||||
}
|
97
src/gopheros/device/acpi/aml/stream_reader_test.go
Normal file
97
src/gopheros/device/acpi/aml/stream_reader_test.go
Normal file
@ -0,0 +1,97 @@
|
||||
package aml
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func TestAMLStreamReader(t *testing.T) {
|
||||
buf := make([]byte, 16)
|
||||
for i := 0; i < len(buf); i++ {
|
||||
buf[i] = byte(i)
|
||||
}
|
||||
|
||||
t.Run("without offset", func(t *testing.T) {
|
||||
var r amlStreamReader
|
||||
r.Init(
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uint32(len(buf)),
|
||||
0,
|
||||
)
|
||||
|
||||
if r.EOF() {
|
||||
t.Fatal("unexpected EOF")
|
||||
}
|
||||
|
||||
if err := r.UnreadByte(); err != errInvalidUnreadByte {
|
||||
t.Fatalf("expected errInvalidUnreadByte; got %v", err)
|
||||
}
|
||||
|
||||
if _, err := r.LastByte(); err != io.EOF {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
for i := 0; i < len(buf); i++ {
|
||||
exp := byte(i)
|
||||
|
||||
next, err := r.PeekByte()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if next != exp {
|
||||
t.Fatalf("expected PeekByte to return %d; got %d", exp, next)
|
||||
}
|
||||
|
||||
next, err = r.ReadByte()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if next != exp {
|
||||
t.Fatalf("expected ReadByte to return %d; got %d", exp, next)
|
||||
}
|
||||
|
||||
last, err := r.LastByte()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if last != exp {
|
||||
t.Fatalf("expected LastByte to return %d; got %d", exp, last)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := r.PeekByte(); err != io.EOF {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if _, err := r.ReadByte(); err != io.EOF {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
exp := byte(len(buf) - 1)
|
||||
if last, _ := r.LastByte(); last != exp {
|
||||
t.Fatalf("expected LastByte to return %d; got %d", exp, last)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
t.Run("with offset", func(t *testing.T) {
|
||||
var r amlStreamReader
|
||||
r.Init(
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uint32(len(buf)),
|
||||
8,
|
||||
)
|
||||
|
||||
if r.EOF() {
|
||||
t.Fatal("unexpected EOF")
|
||||
}
|
||||
|
||||
if exp, got := uint32(8), r.Offset(); got != exp {
|
||||
t.Fatalf("expected Offset() to return %d; got %d", exp, got)
|
||||
}
|
||||
|
||||
exp := byte(8)
|
||||
if next, _ := r.ReadByte(); next != exp {
|
||||
t.Fatalf("expected ReadByte to return %d; got %d", exp, next)
|
||||
}
|
||||
})
|
||||
}
|
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