1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00
Achilleas Anagnostopoulos 63c69fe8d3 acpi: implement internal VM method for executing AML blocks
The execBlock method supports the various control flows defined by the
ACPI standard (next instr, continue, break and return). It is designed
so that it can be recursively invoked both AML methods and various
flow-altering opcodes (e.g. opIf, opWhile e.t.c.)
2017-12-04 06:35:26 +00:00

165 lines
4.8 KiB
Go

package aml
import (
"gopheros/device/acpi/table"
"io"
)
const (
// According to the ACPI spec, methods can use up to 8 local args and
// can receive up to 7 method args.
maxLocalArgs = 8
maxMethodArgs = 7
)
// ctrlFlowType describes the different ways that the control flow can be altered
// while executing a set of AML opcodes.
type ctrlFlowType uint8
// The list of supported control flows.
const (
ctrlFlowTypeNextOpcode ctrlFlowType = iota
ctrlFlowTypeBreak
ctrlFlowTypeContinue
ctrlFlowTypeFnReturn
)
// execContext holds the AML interpreter state while an AML method executes.
type execContext struct {
localArg [maxLocalArgs]interface{}
methodArg [maxMethodArgs]interface{}
// ctrlFlow specifies how the VM should select the next instruction to
// execute.
ctrlFlow ctrlFlowType
// retVal holds the return value from a method if ctrlFlow is set to
// the value ctrlFlowTypeFnReturn.
retVal interface{}
vm *VM
}
// Error describes errors that occur while executing AML code.
type Error struct {
message string
}
// Error implements the error interface.
func (e *Error) Error() string {
return e.message
}
// VM is a structure that stores the output of the AML bytecode parser and
// provides methods for interpreting any executable opcode.
type VM struct {
errWriter io.Writer
tableResolver table.Resolver
tableParser *Parser
// rootNS holds a pointer to the root of the ACPI tree.
rootNS ScopeEntity
// According to the ACPI spec, the Revision field in the DSDT specifies
// whether integers are treated as 32 or 64-bits. The VM memoizes this
// value so that it can be used by the data conversion helpers.
sizeOfIntInBits int
jumpTable [numOpcodes]opHandler
}
// NewVM creates a new AML VM and initializes it with the default scope
// hierarchy and pre-defined objects contained in the ACPI specification.
func NewVM(errWriter io.Writer, resolver table.Resolver) *VM {
root := defaultACPIScopes()
return &VM{
rootNS: root,
errWriter: errWriter,
tableResolver: resolver,
tableParser: NewParser(errWriter, root),
}
}
// Init attempts to locate and parse the AML byte-code contained in the
// system's DSDT and SSDT tables.
func (vm *VM) Init() *Error {
for tableHandle, tableName := range []string{"DSDT", "SSDT"} {
header := vm.tableResolver.LookupTable(tableName)
if header == nil {
continue
}
if err := vm.tableParser.ParseAML(uint8(tableHandle+1), tableName, header); err != nil {
return &Error{message: err.Module + ": " + err.Error()}
}
if tableName == "DSDT" {
vm.sizeOfIntInBits = 32
if header.Revision >= 2 {
vm.sizeOfIntInBits = 64
}
}
}
vm.populateJumpTable()
return nil
}
// Lookup traverses a potentially nested absolute AML path and returns the
// Entity reachable via that path or nil if the path does not point to a
// defined Entity.
func (vm *VM) Lookup(absPath string) Entity {
if absPath == "" || absPath[0] != '\\' {
return nil
}
// If we just search for `\` return the root namespace
if len(absPath) == 1 {
return vm.rootNS
}
return scopeFindRelative(vm.rootNS, absPath[1:])
}
// Visit performs a DFS on the AML namespace tree invoking the visitor for each
// encountered entity whose type matches entType. Namespace nodes are visited
// in parent to child order a property which allows the supplied visitor
// function to signal that it's children should not be visited.
func (vm *VM) Visit(entType EntityType, visitorFn Visitor) {
scopeVisit(0, vm.rootNS, entType, visitorFn)
}
// execBlock attempts to execute all AML opcodes in the supplied scoped entity.
// If all opcodes are successfully executed, the provided execContext will be
// updated to reflect the current VM state. Otherwise, an error will be
// returned.
func (vm *VM) execBlock(ctx *execContext, block ScopeEntity) *Error {
instrList := block.Children()
numInstr := len(instrList)
for instrIndex := 0; instrIndex < numInstr && ctx.ctrlFlow == ctrlFlowTypeNextOpcode; instrIndex++ {
instr := instrList[instrIndex]
if err := vm.jumpTable[instr.getOpcode()](ctx, instr); err != nil {
return err
}
}
return nil
}
// defaultACPIScopes constructs a tree of scoped entities that correspond to
// the predefined scopes contained in the ACPI specification and returns back
// its root node.
func defaultACPIScopes() ScopeEntity {
rootNS := &scopeEntity{op: opScope, name: `\`}
rootNS.Append(&scopeEntity{op: opScope, name: `_GPE`}) // General events in GPE register block
rootNS.Append(&scopeEntity{op: opScope, name: `_PR_`}) // ACPI 1.0 processor namespace
rootNS.Append(&scopeEntity{op: opScope, name: `_SB_`}) // System bus with all device objects
rootNS.Append(&scopeEntity{op: opScope, name: `_SI_`}) // System indicators
rootNS.Append(&scopeEntity{op: opScope, name: `_TZ_`}) // ACPI 1.0 thermal zone namespace
return rootNS
}