From 64f3dae485bf96dee436b93300a49209fa8fef3e Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Wed, 11 Oct 2017 19:58:00 +0100 Subject: [PATCH] acpi: define VM type and structs to maintain execution state --- src/gopheros/device/acpi/aml/vm.go | 119 ++++++++++++++++++++++++ src/gopheros/device/acpi/aml/vm_test.go | 33 +++++++ 2 files changed, 152 insertions(+) create mode 100644 src/gopheros/device/acpi/aml/vm.go create mode 100644 src/gopheros/device/acpi/aml/vm_test.go diff --git a/src/gopheros/device/acpi/aml/vm.go b/src/gopheros/device/acpi/aml/vm.go new file mode 100644 index 0000000..3d482d4 --- /dev/null +++ b/src/gopheros/device/acpi/aml/vm.go @@ -0,0 +1,119 @@ +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 encapsulates +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 +} + +// 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 + } + } + } + + 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 +} diff --git a/src/gopheros/device/acpi/aml/vm_test.go b/src/gopheros/device/acpi/aml/vm_test.go new file mode 100644 index 0000000..d71db1b --- /dev/null +++ b/src/gopheros/device/acpi/aml/vm_test.go @@ -0,0 +1,33 @@ +package aml + +import ( + "os" + "reflect" + "testing" +) + +func TestVMInit(t *testing.T) { + t.Run("success", func(t *testing.T) { + resolver := &mockResolver{ + tableFiles: []string{"DSDT.aml"}, + } + + vm := NewVM(os.Stderr, resolver) + if err := vm.Init(); err != nil { + t.Fatal(err) + } + }) + + t.Run("parse error", func(t *testing.T) { + resolver := &fixedPayloadResolver{ + // invalid payload (incomplete opcode) + payload: []byte{extOpPrefix}, + } + + expErr := &Error{message: errParsingAML.Module + ": " + errParsingAML.Error()} + vm := NewVM(os.Stderr, resolver) + if err := vm.Init(); !reflect.DeepEqual(err, expErr) { + t.Fatalf("expected Init() to return errParsingAML; got %v", err) + } + }) +}