mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
This commit updates the post-parse step so that: - the visitor not longer recurses into method bodies. Since code inside methods may potentially generate dynamic/scoped entities or even use conditional invocations (if CondRefOf(X) { X(...) }), symbol resolution will be deferred to the AML interpreter. - parent-child relationships between entities are checked and updated if not properly specified
1012 lines
26 KiB
Go
1012 lines
26 KiB
Go
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
|
|
|
|
// methodArgCount is initialized in a pre-parse step with the names and expected
|
|
// number of args for each function declaration. This is required as function
|
|
// invocations do not employ any mechanism to indicate the number of args that
|
|
// need to be parsed. Moreover, the spec allows for forward function declarations.
|
|
methodArgCount map[string]uint8
|
|
}
|
|
|
|
// NewParser returns a new AML parser instance.
|
|
func NewParser(errWriter io.Writer, rootEntity ScopeEntity) *Parser {
|
|
return &Parser{
|
|
errWriter: errWriter,
|
|
root: rootEntity,
|
|
methodArgCount: make(map[string]uint8),
|
|
}
|
|
}
|
|
|
|
// 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: scan bytecode and locate all method declarations. This allows us to
|
|
// properly parse the arguments to method invocations at pass 2 even if the
|
|
// the name of the invoked method is a forward reference.
|
|
p.detectMethodDeclarations()
|
|
|
|
// Pass 2: 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 3: check parents and resolve forward references
|
|
var resolveFailed bool
|
|
scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool {
|
|
// Skip method bodies; their contents will be lazily resolved by the interpreter
|
|
if _, isMethod := ent.(*Method); isMethod {
|
|
return false
|
|
}
|
|
|
|
// Populate parents for any entity args that are also entities but are not
|
|
// linked to a parent (e.g. a package inside a named entity).
|
|
for _, arg := range ent.getArgs() {
|
|
if argEnt, isArgEnt := arg.(Entity); isArgEnt && argEnt.Parent() == nil {
|
|
argEnt.setParent(ent.Parent())
|
|
}
|
|
}
|
|
|
|
if res, ok := ent.(resolver); ok && !res.Resolve(p.errWriter, p.root) {
|
|
resolveFailed = true
|
|
return false
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
if resolveFailed {
|
|
return errResolvingEntities
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// detectMethodDeclarations scans the AML byte-stream looking for function
|
|
// declarations. For each discovered function, the method will parse its flags
|
|
// and update the methodArgCount map with the number of required arguments.
|
|
func (p *Parser) detectMethodDeclarations() {
|
|
var (
|
|
next *opcodeInfo
|
|
method string
|
|
startOffset = p.r.Offset()
|
|
curOffset, pkgLen uint32
|
|
flags uint64
|
|
ok bool
|
|
)
|
|
|
|
for !p.r.EOF() {
|
|
if next, ok = p.nextOpcode(); !ok {
|
|
// Skip one byte to the right and try again. Maybe we are stuck inside
|
|
// the contents of a string or buffer
|
|
_, _ = p.r.ReadByte()
|
|
continue
|
|
}
|
|
|
|
if next.op != opMethod {
|
|
continue
|
|
}
|
|
|
|
// Parse pkg len; if this fails then this is not a method declaration
|
|
curOffset = p.r.Offset()
|
|
if pkgLen, ok = p.parsePkgLength(); !ok {
|
|
continue
|
|
}
|
|
|
|
// Parse method name
|
|
if method, ok = p.parseNameString(); !ok {
|
|
continue
|
|
}
|
|
|
|
// The next byte encodes the method flags which also contains the arg count
|
|
// at bits 0:2
|
|
if flags, ok = p.parseNumConstant(1); !ok {
|
|
continue
|
|
}
|
|
|
|
p.methodArgCount[method] = uint8(flags) & 0x7
|
|
|
|
// At this point we can use the pkg length to skip over the term list
|
|
p.r.SetOffset(curOffset + pkgLen)
|
|
}
|
|
|
|
p.r.SetOffset(startOffset)
|
|
}
|
|
|
|
// 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.parseNamedRef()
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// parseNamedRef attempts to parse either a method invocation or a named
|
|
// reference. As AML allows for forward references, the actual contents for
|
|
// this entity will not be known until the entire AML stream has been parsed.
|
|
//
|
|
// Grammar:
|
|
// MethodInvocation := NameString TermArgList
|
|
// TermArgList = Nothing | TermArg TermArgList
|
|
// TermArg = Type2Opcode | DataObject | ArgObj | LocalObj | MethodInvocation
|
|
func (p *Parser) parseNamedRef() bool {
|
|
name, ok := p.parseNameString()
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
var (
|
|
curOffset uint32
|
|
argIndex uint8
|
|
arg Entity
|
|
argList []interface{}
|
|
)
|
|
|
|
if argCount, isMethod := p.methodArgCount[name]; isMethod {
|
|
for argIndex < 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.parseNamedRef()
|
|
if ok {
|
|
arg = p.scopeCurrent().lastChild()
|
|
p.scopeCurrent().removeChild(arg)
|
|
}
|
|
}
|
|
|
|
// No more TermArgs to parse
|
|
if !ok {
|
|
p.r.SetOffset(curOffset)
|
|
break
|
|
}
|
|
|
|
argList = append(argList, arg)
|
|
argIndex++
|
|
}
|
|
|
|
// Check whether all expected arguments have been parsed
|
|
if argIndex != argCount {
|
|
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] unexpected arglist end for method %s invocation: expected %d; got %d\n", p.tableName, p.r.Offset(), name, argCount, argIndex)
|
|
return false
|
|
}
|
|
|
|
return p.scopeCurrent().Append(&methodInvocationEntity{
|
|
unnamedEntity: unnamedEntity{args: argList},
|
|
methodName: name,
|
|
})
|
|
}
|
|
|
|
// Otherwise this is a reference to a named entity
|
|
return p.scopeCurrent().Append(&namedReference{targetName: name})
|
|
}
|
|
|
|
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.parseNamedRef(); 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]
|
|
}
|