1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00
Achilleas Anagnostopoulos c09798622b acpi: provide more robust implementation for parsing AML method bodies
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.
2018-01-06 10:44:19 +00:00

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]
}