mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
acpi: add scaffolding code for the AML method to OS VM bytecode compiler
This commit includes the required code to process all methods defined by an AML entity hierarchy and invoke a function for converting each method statement into a sequence of opcodes that can be executed by our internal VM. The mapping of the AML opcodes to the VM opcodes is facilitated via the opcodeMap which allows us to apply a particular compiler function when converting each AML opcode. Currently, this map is empty so all AML opcodes are mapped to "nop" instructions.
This commit is contained in:
parent
3cdc10be24
commit
6f39b2af55
141
src/gopheros/device/acpi/aml/vm/compiler.go
Normal file
141
src/gopheros/device/acpi/aml/vm/compiler.go
Normal file
@ -0,0 +1,141 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"gopheros/device/acpi/aml/entity"
|
||||
"gopheros/kernel"
|
||||
"gopheros/kernel/kfmt"
|
||||
)
|
||||
|
||||
type opMapping struct {
|
||||
vmOp uint8
|
||||
compilerFn func(*compilerContext, uint8, entity.Entity) *kernel.Error
|
||||
}
|
||||
|
||||
type compilerContext struct {
|
||||
rootNS entity.Container
|
||||
|
||||
// opcodeMap contains the mappings of AML opcodes into a VM opcode plus
|
||||
// a compiler function to handle the opcode conversion.
|
||||
opcodeMap map[entity.AMLOpcode]opMapping
|
||||
|
||||
// methodCallMap maps an entity definition to an index in VM.methodCalls.
|
||||
methodCallMap map[*entity.Method]uint16
|
||||
|
||||
vmCtx *Context
|
||||
lastErr *kernel.Error
|
||||
}
|
||||
|
||||
func newCompilerContext(rootNS entity.Container) *compilerContext {
|
||||
compCtx := &compilerContext{
|
||||
rootNS: rootNS,
|
||||
methodCallMap: make(map[*entity.Method]uint16),
|
||||
vmCtx: new(Context),
|
||||
}
|
||||
|
||||
// the opcodeMap needs to be dynamically populated here instead of
|
||||
// having a shared, globally initialized map to prevent the Go compiler
|
||||
// from complaining about potential initialization loops (e.g.
|
||||
// compileFn -> compileStatement -> compileFn...)
|
||||
compCtx.opcodeMap = map[entity.AMLOpcode]opMapping{}
|
||||
|
||||
return compCtx
|
||||
}
|
||||
|
||||
// Compile returns a bytecode representation of the AML tree starting at
|
||||
// rootNS that can be consumed by the AML VM.
|
||||
func Compile(rootNS entity.Container) (*Context, *kernel.Error) {
|
||||
compCtx := newCompilerContext(rootNS)
|
||||
compileAMLTree(compCtx)
|
||||
|
||||
if compCtx.lastErr != nil {
|
||||
return nil, compCtx.lastErr
|
||||
}
|
||||
|
||||
return compCtx.vmCtx, nil
|
||||
}
|
||||
|
||||
// compileAMLTree converts the AML entity tree inside the supplied
|
||||
// compilerContext into a bytecode representation that can be consumed by the
|
||||
// host VM. This function is intentionally separate from the above Compile
|
||||
// function so tests can easily stub the opcodeMap field of compCtx.
|
||||
func compileAMLTree(compCtx *compilerContext) {
|
||||
populateMethodMap(compCtx, compCtx.rootNS)
|
||||
entity.Visit(0, compCtx.rootNS, entity.TypeMethod, func(_ int, ent entity.Entity) bool {
|
||||
if compCtx.lastErr != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
compCtx.lastErr = compileMethod(compCtx, ent.(*entity.Method))
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
// populateMethodMap visits all method entities under root and populates the
|
||||
// VM.methodCalls list as well as compCtx.methodCallMap which is used by the
|
||||
// asmContext to efficiently encode method calls to the correct index in the
|
||||
// methodCalls list.
|
||||
func populateMethodMap(compCtx *compilerContext, root entity.Container) {
|
||||
entity.Visit(0, root, entity.TypeMethod, func(_ int, ent entity.Entity) bool {
|
||||
m := &methodCall{method: ent.(*entity.Method), entrypoint: 0xffffffff}
|
||||
compCtx.vmCtx.methodCalls = append(compCtx.vmCtx.methodCalls, m)
|
||||
compCtx.methodCallMap[m.method] = uint16(len(compCtx.vmCtx.methodCalls) - 1)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
// compileMethod receives an AML method entity and emits the appropriate
|
||||
// bytecode for the statements it contains. Calls to compileMethod will also
|
||||
// populate the method's entrypoint address which is used by the VM to
|
||||
// implement the "call" opcode.
|
||||
func compileMethod(compCtx *compilerContext, method *entity.Method) *kernel.Error {
|
||||
var errBuf bytes.Buffer
|
||||
|
||||
// Setup the entrypoint address for this method.
|
||||
compCtx.vmCtx.methodCalls[compCtx.methodCallMap[method]].entrypoint = uint32(len(compCtx.vmCtx.bytecode))
|
||||
for stmtIndex, stmt := range method.Children() {
|
||||
if err := compileStatement(compCtx, stmt); err != nil {
|
||||
kfmt.Fprintf(&errBuf, "[%s:%d] %s", method.Name(), stmtIndex, err.Message)
|
||||
err.Message = errBuf.String()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// compileStatement receives as input an AML entity that represents a method
|
||||
// statement and emits the appropriate bytecode for executing it.
|
||||
func compileStatement(compCtx *compilerContext, ent entity.Entity) *kernel.Error {
|
||||
mapping, hasMapping := compCtx.opcodeMap[ent.Opcode()]
|
||||
if !hasMapping {
|
||||
emit8(compCtx, opNop)
|
||||
return nil
|
||||
}
|
||||
|
||||
return mapping.compilerFn(compCtx, mapping.vmOp, ent)
|
||||
}
|
||||
|
||||
// emit8 appends an opcode that does not require any operands to the generated
|
||||
// bytecode stream.
|
||||
func emit8(compCtx *compilerContext, op uint8) {
|
||||
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, op)
|
||||
}
|
||||
|
||||
// emit16 appends an opcode followed by an word operand to the generated
|
||||
// bytecode stream. The word operand will be encoded in big-endian format.
|
||||
func emit16(compCtx *compilerContext, op uint8, operand uint16) {
|
||||
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, op)
|
||||
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, uint8((operand>>8)&0xff))
|
||||
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, uint8(operand&0xff))
|
||||
}
|
||||
|
||||
// emit32 appends an opcode followed by an dword operand to the generated
|
||||
// bytecode stream. The word operand will be encoded in big-endian format.
|
||||
func emit32(compCtx *compilerContext, op uint8, operand uint32) {
|
||||
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, op)
|
||||
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, uint8((operand>>24)&0xff))
|
||||
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, uint8((operand>>16)&0xff))
|
||||
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, uint8((operand>>8)&0xff))
|
||||
compCtx.vmCtx.bytecode = append(compCtx.vmCtx.bytecode, uint8(operand&0xff))
|
||||
}
|
26
src/gopheros/device/acpi/aml/vm/opcodes.go
Normal file
26
src/gopheros/device/acpi/aml/vm/opcodes.go
Normal file
@ -0,0 +1,26 @@
|
||||
package vm
|
||||
|
||||
// This is the list of opcodes used by the AML virtual machine. The 2 MSB of
|
||||
// each opcode indicates the number of operands that need to be decoded by the
|
||||
// VM:
|
||||
// - 00 : no operands
|
||||
// - 01 : 2 byte operands (word)
|
||||
// - 10 : 4 byte operands (dword)
|
||||
//
|
||||
// The length of a particular instruction in bytes (including its opcode)
|
||||
// can be found via the instrEncodingLen function.
|
||||
//
|
||||
// | mnemonic | opcode | operands [count]: [operand labels] | stack before | stack after | description |
|
||||
// |------------|--------|-------------------------------------------------------|------------------|----------------------------------|--------------------------------------------------------------------------------------------------------------|
|
||||
// | nop | 0x00 | | | | nop |
|
||||
const (
|
||||
opNop uint8 = 0x00
|
||||
)
|
||||
|
||||
// instrEncodingLen returns the number of bytes that encode the instruction
|
||||
// specified by opcode op.
|
||||
func instrEncodingLen(op uint8) int {
|
||||
// Instead of extracting the 2 MSB and then multiplying by 2 we extract the 3
|
||||
// MSB and clear the right-most bit which yields the same result.
|
||||
return 1 + int((op>>5)&0x6)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user