mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
Since the ACPI standard allows forward function declarations this step is required so we can properly parse the argument list for function invocations. Contrary to other AML entities, method invocations do not include any sort of pkgLength information so unless we track the expected argument count for each function, our parser will not be able to figure out where the argument list ends.
1001 lines
25 KiB
Go
1001 lines
25 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 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
|
|
}
|
|
|
|
// 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.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]
|
|
}
|