mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
The previous implementation used brute-force approach where the parser made an initial pass scanning the AML bytestream and looking for method declaration opcodes. It then parsed out the method name and arg count and populated a map which was used to detect the number of arguments to be parsed upon encountering a method invocation. This approach proved to be error-prone and would lead to an incorrect parse tree in the following ASL example: Method (FOOF, 1, NotSerialized) { Return ("bar") } Method (TST0, 0, NotSerialized) { FOOF(0) \_SB.FOOF(2, 3) } Scope(\_SB){ // Another FOOF method in \_SB which takes a different arg count Method (FOOF, 2, NotSerialized) { Return ("something") } } In the above example the parser would correctly parse the first FOOF call in TST0 but fail to parse the second invocation since the method name contains a scope. The second invocation would actually yield the following incorrect entity list (arguments appear as sibling entities): Ref(\_SB.FOOF), Const(2), Const(3) The new approach gets rid of the brute-force method and instead modifies the initial parse of the tree not to parse the entities in the AML method bodies but to instead track the start and end offset in the AML stream for the body contents. In the second pass (where the parser normally resolves symbol references), the parser can properly parse the contents of method bodies since the entire AML tree is now known and the parser can use the regular scope lookup rules to find the correct method declaration for the invocation and figure out the argument count it needs to parse.
935 lines
25 KiB
Go
935 lines
25 KiB
Go
package parser
|
|
|
|
import (
|
|
"gopheros/device/acpi/aml/entity"
|
|
"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"}
|
|
)
|
|
|
|
type parseOpt uint8
|
|
|
|
const (
|
|
parseOptSkipMethodBodies parseOpt = iota
|
|
parseOptParseMethodBodies
|
|
)
|
|
|
|
// Parser implements an AML parser.
|
|
type Parser struct {
|
|
r amlStreamReader
|
|
errWriter io.Writer
|
|
root entity.Container
|
|
scopeStack []entity.Container
|
|
tableName string
|
|
tableHandle uint8
|
|
|
|
parseOptions parseOpt
|
|
}
|
|
|
|
// NewParser returns a new AML parser instance.
|
|
func NewParser(errWriter io.Writer, rootEntity entity.Container) *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 without recursing into
|
|
// function bodies.
|
|
p.parseOptions = parseOptSkipMethodBodies
|
|
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: parse method bodies, check entity parents and resolve all
|
|
// symbol references
|
|
var resolveFailed bool
|
|
entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool {
|
|
if method, isMethod := ent.(*entity.Method); isMethod {
|
|
resolveFailed = resolveFailed || !p.parseMethodBody(method)
|
|
|
|
// Don't recurse into method bodies; their contents
|
|
// will be lazilly resolved by the VM
|
|
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.Args() {
|
|
if argEnt, isArgEnt := arg.(entity.Entity); isArgEnt && argEnt.Parent() == nil {
|
|
argEnt.SetParent(ent.Parent())
|
|
}
|
|
}
|
|
|
|
// Resolve any symbol references
|
|
if lazyRef, ok := ent.(entity.LazyRefResolver); ok {
|
|
if err := lazyRef.ResolveSymbolRefs(p.root); err != nil {
|
|
kfmt.Fprintf(p.errWriter, "%s\n", err.Message)
|
|
resolveFailed = true
|
|
return false
|
|
}
|
|
}
|
|
|
|
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.
|
|
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 {
|
|
case info.op == entity.OpScope:
|
|
return p.parseScope(curOffset + pkgLen)
|
|
case info.flags.is(opFlagNamed | opFlagScoped):
|
|
return p.parseNamespacedObj(info, 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 entity.AMLOpcode, obj entity.Entity) bool {
|
|
switch op {
|
|
case entity.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.Remove(curScope.Last())
|
|
prevObj := curScope.Last()
|
|
if prevObj.Opcode() != entity.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)
|
|
}
|
|
|
|
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 := entity.FindInScope(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.Opcode() {
|
|
case entity.OpDevice, entity.OpProcessor, entity.OpThermalZone, entity.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.(entity.Container))
|
|
ok = p.parseObjList(maxReadOffset)
|
|
p.scopeExit()
|
|
|
|
return ok
|
|
}
|
|
|
|
// parseNamespacedObj reads a scope target name from the AML bytestream,
|
|
// attaches the appropriate object depending on the opcode to the correct
|
|
// parent scope and then parses any contained objects. The contained objects
|
|
// will be appended inside the newly constructed scope.
|
|
func (p *Parser) parseNamespacedObj(info *opcodeInfo, maxReadOffset uint32) bool {
|
|
scopeExpr, ok := p.parseNameString()
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
parent, name := entity.ResolveScopedPath(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 entity.Container
|
|
switch info.op {
|
|
case entity.OpDevice:
|
|
obj = entity.NewDevice(p.tableHandle, name)
|
|
case entity.OpProcessor:
|
|
obj = entity.NewProcessor(p.tableHandle, name)
|
|
case entity.OpPowerRes:
|
|
obj = entity.NewPowerResource(p.tableHandle, name)
|
|
case entity.OpThermalZone:
|
|
obj = entity.NewThermalZone(p.tableHandle, name)
|
|
case entity.OpMethod:
|
|
obj = entity.NewMethod(p.tableHandle, name)
|
|
default:
|
|
kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] unsupported namespaced op: %s (current scope: %s)\n", p.tableName, p.r.Offset(), info.op.String(), p.scopeCurrent().Name())
|
|
return false
|
|
}
|
|
|
|
// Parse any args that follow the name. The last arg is always an ArgTermList
|
|
parent.Append(obj)
|
|
for argIndex := uint8(1); argIndex < info.argFlags.argCount(); argIndex++ {
|
|
if !p.parseArg(info, obj, argIndex, info.argFlags.arg(argIndex), maxReadOffset) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return ok && p.finalizeObj(info.op, obj)
|
|
}
|
|
|
|
func (p *Parser) parseArg(info *opcodeInfo, obj entity.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 this is a method and the SkipMethodBodies option is set
|
|
// then record the body start and end offset so we can parse
|
|
// it at a later stage.
|
|
if method, isMethod := obj.(*entity.Method); isMethod && p.parseOptions == parseOptSkipMethodBodies {
|
|
method.BodyStartOffset = p.r.Offset()
|
|
method.BodyEndOffset = maxReadOffset
|
|
p.r.SetOffset(maxReadOffset)
|
|
return true
|
|
}
|
|
|
|
// 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.(entity.Container); isScopeEnt {
|
|
p.scopeEnter(s)
|
|
} else {
|
|
// Create an unnamed scope (e.g if, else, while scope)
|
|
ns := entity.NewScope(info.op, p.tableHandle, "")
|
|
p.scopeEnter(ns)
|
|
obj.SetArg(argIndex, ns)
|
|
}
|
|
|
|
ok = p.parseObjList(maxReadOffset)
|
|
p.scopeExit()
|
|
return ok
|
|
case opArgFieldList:
|
|
return p.parseFieldList(obj, 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.Entity, bool) {
|
|
if ok := p.parseObj(); !ok {
|
|
return nil, false
|
|
}
|
|
|
|
curScope := p.scopeCurrent()
|
|
obj := curScope.Last()
|
|
curScope.Remove(obj)
|
|
return obj, true
|
|
}
|
|
|
|
func (p *Parser) makeObjForOpcode(info *opcodeInfo) entity.Entity {
|
|
var obj entity.Entity
|
|
|
|
switch {
|
|
case info.op == entity.OpOpRegion:
|
|
obj = entity.NewRegion(p.tableHandle)
|
|
case info.op == entity.OpBuffer:
|
|
obj = entity.NewBuffer(p.tableHandle)
|
|
case info.op == entity.OpMutex:
|
|
obj = entity.NewMutex(p.tableHandle)
|
|
case info.op == entity.OpEvent:
|
|
obj = entity.NewEvent(p.tableHandle)
|
|
case info.op == entity.OpField:
|
|
obj = entity.NewField(p.tableHandle)
|
|
case info.op == entity.OpIndexField:
|
|
obj = entity.NewIndexField(p.tableHandle)
|
|
case info.op == entity.OpBankField:
|
|
obj = entity.NewBankField(p.tableHandle)
|
|
case info.op == entity.OpCreateField:
|
|
obj = entity.NewBufferField(info.op, p.tableHandle, 0)
|
|
case info.op == entity.OpCreateBitField:
|
|
obj = entity.NewBufferField(info.op, p.tableHandle, 1)
|
|
case info.op == entity.OpCreateByteField:
|
|
obj = entity.NewBufferField(info.op, p.tableHandle, 8)
|
|
case info.op == entity.OpCreateWordField:
|
|
obj = entity.NewBufferField(info.op, p.tableHandle, 16)
|
|
case info.op == entity.OpCreateDWordField:
|
|
obj = entity.NewBufferField(info.op, p.tableHandle, 32)
|
|
case info.op == entity.OpCreateQWordField:
|
|
obj = entity.NewBufferField(info.op, p.tableHandle, 64)
|
|
case info.op == entity.OpZero:
|
|
obj = entity.NewConst(info.op, p.tableHandle, uint64(0))
|
|
case info.op == entity.OpOne:
|
|
obj = entity.NewConst(info.op, p.tableHandle, uint64(1))
|
|
case info.op == entity.OpOnes:
|
|
obj = entity.NewConst(info.op, p.tableHandle, uint64((1<<64)-1))
|
|
case info.flags.is(opFlagConstant):
|
|
obj = entity.NewConst(info.op, p.tableHandle, nil) // will be parsed as an arg
|
|
case info.op == entity.OpPackage || info.op == entity.OpVarPackage:
|
|
obj = entity.NewPackage(info.op, p.tableHandle)
|
|
case info.flags.is(opFlagScoped):
|
|
obj = entity.NewScope(info.op, p.tableHandle, "")
|
|
case info.flags.is(opFlagNamed):
|
|
obj = entity.NewGenericNamed(info.op, p.tableHandle)
|
|
default:
|
|
obj = entity.NewGeneric(info.op, p.tableHandle)
|
|
}
|
|
|
|
return obj
|
|
}
|
|
|
|
// parseMethodBody parses the entities that make up a method's body. After the
|
|
// entire AML tree has been parsed, the parser makes a second pass and calls
|
|
// parseMethodBody for each Method entity.
|
|
//
|
|
// By deferring the parsing of the method body, we ensure that the parser can
|
|
// lookup the method declarations (even if forward declarations are used) for
|
|
// each method invocation. As method declarations contain information about the
|
|
// expected argument count, the parser can use this information to properly
|
|
// parse the invocation arguments. For more details see: parseNamedRef
|
|
func (p *Parser) parseMethodBody(method *entity.Method) bool {
|
|
p.parseOptions = parseOptParseMethodBodies
|
|
p.scopeEnter(method)
|
|
p.r.SetOffset(method.BodyStartOffset)
|
|
ok := p.parseArg(&opcodeTable[methodOpInfoIndex], method, 2, opArgTermList, method.BodyEndOffset)
|
|
p.scopeExit()
|
|
|
|
return ok
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// Check if this is a method invocation
|
|
ent := entity.FindInScope(p.scopeCurrent(), p.root, name)
|
|
if methodDef, isMethod := ent.(*entity.Method); isMethod {
|
|
var (
|
|
curOffset uint32
|
|
argIndex uint8
|
|
arg entity.Entity
|
|
argList []interface{}
|
|
)
|
|
|
|
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 && (entity.OpIsType2(nextOpcode.op) || entity.OpIsArg(nextOpcode.op) || entity.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().Last()
|
|
p.scopeCurrent().Remove(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 != methodDef.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, methodDef.ArgCount, argIndex)
|
|
return false
|
|
}
|
|
|
|
return p.scopeCurrent().Append(entity.NewInvocation(p.tableHandle, methodDef, argList))
|
|
}
|
|
|
|
// Otherwise this is a reference to a named entity
|
|
return p.scopeCurrent().Append(entity.NewReference(p.tableHandle, 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(fieldEnt entity.Entity, maxReadOffset uint32) bool {
|
|
var (
|
|
ok bool
|
|
|
|
accessType entity.FieldAccessType
|
|
|
|
bitWidth uint32
|
|
curBitOffset uint32
|
|
connectionName string
|
|
unitName string
|
|
resolvedConnection entity.Entity
|
|
accessAttrib entity.FieldAccessAttrib
|
|
accessByteCount uint8
|
|
)
|
|
|
|
// Load default field access rule; it applies to all field units unless
|
|
// overridden via a directive in the field unit list
|
|
if accessProvider, isProvider := fieldEnt.(entity.FieldAccessTypeProvider); isProvider {
|
|
accessType = accessProvider.DefaultAccessType()
|
|
} else {
|
|
// not a field entity
|
|
return false
|
|
}
|
|
|
|
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 = entity.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 = entity.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 = entity.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 = entity.FieldAccessAttribBytes
|
|
case 0xe:
|
|
accessAttrib = entity.FieldAccessAttribRawBytes
|
|
case 0x0f:
|
|
accessAttrib = entity.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 that declares them
|
|
unit := entity.NewFieldUnit(p.tableHandle, unitName)
|
|
unit.Field = fieldEnt
|
|
unit.AccessType = accessType
|
|
unit.AccessAttrib = accessAttrib
|
|
unit.ByteCount = accessByteCount
|
|
unit.BitOffset = curBitOffset
|
|
unit.BitWidth = bitWidth
|
|
unit.ConnectionName = connectionName
|
|
unit.Connection = resolvedConnection
|
|
|
|
p.scopeCurrent().Append(unit)
|
|
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 && entity.OpIsArg(nextOpcode.op):
|
|
obj, ok = entity.NewGeneric(nextOpcode.op, p.tableHandle), 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 == entity.OpZero: // this is actually a NullName
|
|
p.r.SetOffset(curOffset + 1)
|
|
return entity.NewConst(entity.OpStringPrefix, p.tableHandle, ""), true
|
|
case entity.OpIsArg(nextOpcode.op) || nextOpcode.op == entity.OpRefOf || nextOpcode.op == entity.OpDerefOf || nextOpcode.op == entity.OpIndex || nextOpcode.op == entity.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().Last()
|
|
p.scopeCurrent().Remove(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() entity.Container {
|
|
return p.scopeStack[len(p.scopeStack)-1]
|
|
}
|
|
|
|
// scopeEnter enters the given scope.
|
|
func (p *Parser) scopeEnter(s entity.Container) {
|
|
p.scopeStack = append(p.scopeStack, s)
|
|
}
|
|
|
|
// scopeExit exits the current scope.
|
|
func (p *Parser) scopeExit() {
|
|
p.scopeStack = p.scopeStack[:len(p.scopeStack)-1]
|
|
}
|