1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00

acpi: add VM-support for AML bitwise opcodes

This commit is contained in:
Achilleas Anagnostopoulos 2017-11-22 08:46:36 +00:00
parent 38b2a3e4e2
commit 2be5b9d224
10 changed files with 932 additions and 20 deletions

View File

@ -13,12 +13,13 @@ const (
)
var (
errNilStoreOperands = &Error{message: "vmStore: src and/or dst operands are nil"}
errInvalidStoreDestination = &Error{message: "vmStore: destination operand is not an AML entity"}
errCopyFailed = &Error{message: "vmCopyObject: copy failed"}
errConversionFailed = &Error{message: "vmConvert: conversion failed"}
errArgIndexOutOfBounds = &Error{message: "vm: arg index out of bounds"}
errDivideByZero = &Error{message: "vm: division by zero"}
errNilStoreOperands = &Error{message: "vmStore: src and/or dst operands are nil"}
errInvalidStoreDestination = &Error{message: "vmStore: destination operand is not an AML entity"}
errCopyFailed = &Error{message: "vmCopyObject: copy failed"}
errConversionFromEmptyString = &Error{message: "vmConvert: conversion from String requires a non-empty value"}
errArgIndexOutOfBounds = &Error{message: "vm: arg index out of bounds"}
errDivideByZero = &Error{message: "vm: division by zero"}
errInvalidComparisonType = &Error{message: "vm: logic opcodes can only be applied to Integer, String or Buffer arguments"}
)
// objRef is a pointer to an argument (local or global) or a named AML object.
@ -122,7 +123,7 @@ func (vm *VM) Init() *Error {
}
vm.populateJumpTable()
return nil
return vm.checkEntities()
}
// Lookup traverses a potentially nested absolute AML path and returns the
@ -141,6 +142,62 @@ func (vm *VM) Lookup(absPath string) Entity {
return scopeFindRelative(vm.rootNS, absPath[1:])
}
// checkEntities performs a DFS on the entity tree and initializes
// entities that defer their initialization until an AML interpreter
// is available.
func (vm *VM) checkEntities() *Error {
var (
err *Error
ctx = &execContext{vm: vm}
)
vm.Visit(EntityTypeAny, func(_ int, ent Entity) bool {
// Stop recursing after the first detected error
if err != nil {
return false
}
// Peek into named entities that wrap other entities
if namedEnt, ok := ent.(*namedEntity); ok {
if nestedEnt, ok := namedEnt.args[0].(Entity); ok {
ent = nestedEnt
}
}
switch typ := ent.(type) {
case *Method:
// Do not recurse into methods; ath this stage we are only interested in
// initializing static entities.
return false
case *bufferEntity:
// According to p.911-912 of the spec:
// - if a size is specified but no initializer the VM should allocate
// a buffer of the requested size
// - if both a size and initializer are specified but size > len(data)
// then the data needs to be padded with zeroes
// Evaluate size arg as an integer
var size interface{}
if size, err = vmConvert(ctx, typ.size, valueTypeInteger); err != nil {
return false
}
sizeAsInt := size.(uint64)
if typ.data == nil {
typ.data = make([]byte, size.(uint64))
}
if dataLen := uint64(len(typ.data)); dataLen < sizeAsInt {
typ.data = append(typ.data, make([]byte, sizeAsInt-dataLen)...)
}
}
return true
})
return err
}
// 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

View File

@ -24,7 +24,6 @@ const (
valueTypeString
valueTypePowerResource
valueTypeProcessor
valueTypeRawDataBuffer
valueTypeThermalZone
)
@ -63,8 +62,6 @@ func (vt valueType) String() string {
return "PowerResource"
case valueTypeProcessor:
return "Processor"
case valueTypeRawDataBuffer:
return "RawDataBuffer"
case valueTypeThermalZone:
return "ThermalZone"
default:
@ -139,7 +136,7 @@ func vmConvert(ctx *execContext, arg interface{}, toType valueType) (interface{}
// conversion without error, and a “0x” prefix is not allowed. Conversion of a null
// (zero-length) string to an integer is not allowed.
if len(argAsStr) == 0 {
return nil, errConversionFailed
return nil, errConversionFromEmptyString
}
var res = uint64(0)
@ -167,7 +164,14 @@ func vmConvert(ctx *execContext, arg interface{}, toType valueType) (interface{}
return strconv.FormatUint(argAsInt, 16), nil
}
}
return nil, errConversionFailed
return nil, conversionError(argType, toType)
}
func conversionError(from, to valueType) *Error {
return &Error{
message: "vmConvert: conversion from " + from.String() + " to " + to.String() + " is not supported",
}
}
// vmTypeOf returns the type of data stored inside the supplied argument.
@ -225,7 +229,7 @@ func vmTypeOf(ctx *execContext, arg interface{}) valueType {
case uint64, bool:
return valueTypeInteger
case []byte:
return valueTypeRawDataBuffer
return valueTypeBuffer
default:
return valueTypeUninitialized
}

View File

@ -23,7 +23,6 @@ func TestValueTypeToString(t *testing.T) {
valueTypeString: "String",
valueTypePowerResource: "PowerResource",
valueTypeProcessor: "Processor",
valueTypeRawDataBuffer: "RawDataBuffer",
valueTypeThermalZone: "ThermalZone",
valueTypeUninitialized: "Uninitialized",
}
@ -153,7 +152,7 @@ func TestVMTypeOf(t *testing.T) {
{
nil,
[]byte("some data"),
valueTypeRawDataBuffer,
valueTypeBuffer,
},
{
nil,
@ -199,7 +198,7 @@ func TestVMToIntArg(t *testing.T) {
},
0,
0,
errConversionFailed,
errConversionFromEmptyString,
},
{
&unnamedEntity{},
@ -245,7 +244,7 @@ func TestVMToIntArgs2(t *testing.T) {
},
[2]int{0, 1},
[2]uint64{0, 0},
errConversionFailed,
errConversionFromEmptyString,
},
{
&unnamedEntity{
@ -253,7 +252,7 @@ func TestVMToIntArgs2(t *testing.T) {
},
[2]int{0, 1},
[2]uint64{0, 0},
errConversionFailed,
errConversionFromEmptyString,
},
{
&unnamedEntity{},
@ -344,7 +343,7 @@ func TestVMConvert(t *testing.T) {
"",
valueTypeInteger,
nil,
errConversionFailed,
errConversionFromEmptyString,
},
// int -> string
{
@ -360,7 +359,7 @@ func TestVMConvert(t *testing.T) {
uint64(42),
valueTypeDevice,
nil,
errConversionFailed,
conversionError(valueTypeInteger, valueTypeDevice),
},
{
&execContext{vm: vm},

View File

@ -22,6 +22,24 @@ func (vm *VM) populateJumpTable() {
vm.jumpTable[opDivide] = vmOpDivide
vm.jumpTable[opMod] = vmOpMod
vm.jumpTable[opShiftLeft] = vmOpShiftLeft
vm.jumpTable[opShiftRight] = vmOpShiftRight
vm.jumpTable[opAnd] = vmOpBitwiseAnd
vm.jumpTable[opOr] = vmOpBitwiseOr
vm.jumpTable[opNand] = vmOpBitwiseNand
vm.jumpTable[opNor] = vmOpBitwiseNor
vm.jumpTable[opXor] = vmOpBitwiseXor
vm.jumpTable[opNot] = vmOpBitwiseNot
vm.jumpTable[opFindSetLeftBit] = vmOpFindSetLeftBit
vm.jumpTable[opFindSetRightBit] = vmOpFindSetRightBit
vm.jumpTable[opLnot] = vmOpLogicalNot
vm.jumpTable[opLand] = vmOpLogicalAnd
vm.jumpTable[opLor] = vmOpLogicalOr
vm.jumpTable[opLEqual] = vmOpLogicalEqual
vm.jumpTable[opLLess] = vmOpLogicalLess
vm.jumpTable[opLGreater] = vmOpLogicalGreater
// Store-related opcodes
vm.jumpTable[opStore] = vmOpStore
}

View File

@ -18,6 +18,8 @@ func vmLoad(ctx *execContext, arg interface{}) (interface{}, *Error) {
arg = ctx.localArg[op-opLocal0]
case opIsMethodArg(op):
arg = ctx.methodArg[op-opArg0]
case op == opBuffer:
return typ.(*bufferEntity).data, nil
default:
// Val may be a nested opcode (e.g Add(Add(1,1), 2))
// In this case, try evaluating the opcode and replace arg with the

View File

@ -1,5 +1,7 @@
package aml
import "bytes"
// Args: left, right, store?
// Returns: left + right
func vmOpAdd(ctx *execContext, ent Entity) *Error {
@ -134,3 +136,377 @@ func vmOpMod(ctx *execContext, ent Entity) *Error {
ctx.retVal = left % right
return vmCondStore(ctx, ctx.retVal, ent, 2)
}
// Args; left, right, store?
// Returns left << right
func vmOpShiftLeft(ctx *execContext, ent Entity) *Error {
var (
left, right uint64
err *Error
)
if left, right, err = vmToIntArgs2(ctx, ent, 0, 1); err != nil {
return err
}
ctx.retVal = left << right
return vmCondStore(ctx, ctx.retVal, ent, 2)
}
// Args; left, right, store?
// Returns left >> right
func vmOpShiftRight(ctx *execContext, ent Entity) *Error {
var (
left, right uint64
err *Error
)
if left, right, err = vmToIntArgs2(ctx, ent, 0, 1); err != nil {
return err
}
ctx.retVal = left >> right
return vmCondStore(ctx, ctx.retVal, ent, 2)
}
// Args; left, right, store?
// Returns left & right
func vmOpBitwiseAnd(ctx *execContext, ent Entity) *Error {
var (
left, right uint64
err *Error
)
if left, right, err = vmToIntArgs2(ctx, ent, 0, 1); err != nil {
return err
}
ctx.retVal = left & right
return vmCondStore(ctx, ctx.retVal, ent, 2)
}
// Args; left, right, store?
// Returns left | right
func vmOpBitwiseOr(ctx *execContext, ent Entity) *Error {
var (
left, right uint64
err *Error
)
if left, right, err = vmToIntArgs2(ctx, ent, 0, 1); err != nil {
return err
}
ctx.retVal = left | right
return vmCondStore(ctx, ctx.retVal, ent, 2)
}
// Args; left, right, store?
// Returns left &^ right
func vmOpBitwiseNand(ctx *execContext, ent Entity) *Error {
var (
left, right uint64
err *Error
)
if left, right, err = vmToIntArgs2(ctx, ent, 0, 1); err != nil {
return err
}
ctx.retVal = left &^ right
return vmCondStore(ctx, ctx.retVal, ent, 2)
}
// Args; left, right, store?
// Returns ^(left | right)
func vmOpBitwiseNor(ctx *execContext, ent Entity) *Error {
var (
left, right uint64
err *Error
)
if left, right, err = vmToIntArgs2(ctx, ent, 0, 1); err != nil {
return err
}
ctx.retVal = ^(left | right)
return vmCondStore(ctx, ctx.retVal, ent, 2)
}
// Args; left, right, store?
// Returns left ^ right
func vmOpBitwiseXor(ctx *execContext, ent Entity) *Error {
var (
left, right uint64
err *Error
)
if left, right, err = vmToIntArgs2(ctx, ent, 0, 1); err != nil {
return err
}
ctx.retVal = left ^ right
return vmCondStore(ctx, ctx.retVal, ent, 2)
}
// Args; left, store?
// Returns ^left
func vmOpBitwiseNot(ctx *execContext, ent Entity) *Error {
var (
left uint64
err *Error
)
if left, err = vmToIntArg(ctx, ent, 0); err != nil {
return err
}
ctx.retVal = ^left
return vmCondStore(ctx, ctx.retVal, ent, 1)
}
// Args; left, store?
// Returns the one-based bit location of the first MSb (most
// significant set bit). The result of 0 means no bit was
// set, 1 means the left-most bit set is the first bit, 2
// means the left-most bit set is the second bit, and so on.
func vmOpFindSetLeftBit(ctx *execContext, ent Entity) *Error {
var (
left uint64
err *Error
)
if left, err = vmToIntArg(ctx, ent, 0); err != nil {
return err
}
ctx.retVal = uint64(0)
for off, mask := uint8(1), uint64(1<<63); mask > 0; off, mask = off+1, mask>>1 {
if left&mask != 0 {
ctx.retVal = uint64(off)
break
}
}
return vmCondStore(ctx, ctx.retVal, ent, 1)
}
// Args; left, store?
// Returns the one-based bit location of the first LSb (least significant set
// bit). The result of 0 means no bit was set, 32 means the first bit set is
// the thirty-second bit, 31 means the first bit set is the thirty-first bit,
// and so on.
func vmOpFindSetRightBit(ctx *execContext, ent Entity) *Error {
var (
left uint64
err *Error
)
if left, err = vmToIntArg(ctx, ent, 0); err != nil {
return err
}
ctx.retVal = uint64(0)
for off, mask := uint8(1), uint64(1); off <= 64; off, mask = off+1, mask<<1 {
if left&mask != 0 {
ctx.retVal = uint64(off)
break
}
}
return vmCondStore(ctx, ctx.retVal, ent, 1)
}
// Args: left
// Returns !left
func vmOpLogicalNot(ctx *execContext, ent Entity) *Error {
var (
left uint64
err *Error
)
if left, err = vmToIntArg(ctx, ent, 0); err != nil {
return err
}
ctx.retVal = left == 0
return nil
}
// Args: left, right
// Returns left || right
func vmOpLogicalOr(ctx *execContext, ent Entity) *Error {
var (
left, right uint64
err *Error
)
if left, right, err = vmToIntArgs2(ctx, ent, 0, 1); err != nil {
return err
}
ctx.retVal = left != 0 || right != 0
return nil
}
// Args: left, right
// Returns left && right
func vmOpLogicalAnd(ctx *execContext, ent Entity) *Error {
var (
left, right uint64
err *Error
)
if left, right, err = vmToIntArgs2(ctx, ent, 0, 1); err != nil {
return err
}
ctx.retVal = left != 0 && right != 0
return nil
}
// Args: left, right
// Returns left == right
// Operands must evaluate to either a Number, a String or a Buffer. The type
// of the first operand dictates the type of the second
func vmOpLogicalEqual(ctx *execContext, ent Entity) *Error {
var (
left, right interface{}
argType valueType
err *Error
args = ent.getArgs()
)
if len(args) != 2 {
return errArgIndexOutOfBounds
}
if left, err = vmLoad(ctx, args[0]); err != nil {
return err
}
if right, err = vmLoad(ctx, args[1]); err != nil {
return err
}
argType = vmTypeOf(ctx, args[0])
// Right operand must be coerced to the same type as left
if right, err = vmConvert(ctx, right, argType); err != nil {
return err
}
switch argType {
case valueTypeInteger, valueTypeString:
ctx.retVal = left == right
case valueTypeBuffer:
ctx.retVal = cmpBuffers(left.([]byte), right.([]byte)) == 0
default:
return errInvalidComparisonType
}
return nil
}
// Args: left, right
// Returns left < right
// Operands must evaluate to either a Number, a String or a Buffer. The type
// of the first operand dictates the type of the second
func vmOpLogicalLess(ctx *execContext, ent Entity) *Error {
var (
left, right interface{}
argType valueType
err *Error
args = ent.getArgs()
)
if len(args) != 2 {
return errArgIndexOutOfBounds
}
if left, err = vmLoad(ctx, args[0]); err != nil {
return err
}
if right, err = vmLoad(ctx, args[1]); err != nil {
return err
}
argType = vmTypeOf(ctx, args[0])
// Right operand must be coerced to the same type as left
if right, err = vmConvert(ctx, right, argType); err != nil {
return err
}
switch argType {
case valueTypeInteger:
ctx.retVal = left.(uint64) < right.(uint64)
case valueTypeString:
ctx.retVal = left.(string) < right.(string)
case valueTypeBuffer:
ctx.retVal = cmpBuffers(left.([]byte), right.([]byte)) == -1
default:
return errInvalidComparisonType
}
return err
}
// Args: left, right
// Returns left > right
// Operands must evaluate to either a Number, a String or a Buffer. The type
// of the first operand dictates the type of the second
func vmOpLogicalGreater(ctx *execContext, ent Entity) *Error {
var (
left, right interface{}
argType valueType
err *Error
args = ent.getArgs()
)
if len(args) != 2 {
return errArgIndexOutOfBounds
}
if left, err = vmLoad(ctx, args[0]); err != nil {
return err
}
if right, err = vmLoad(ctx, args[1]); err != nil {
return err
}
argType = vmTypeOf(ctx, args[0])
// Right operand must be coerced to the same type as left
if right, err = vmConvert(ctx, right, argType); err != nil {
return err
}
switch argType {
case valueTypeInteger:
ctx.retVal = left.(uint64) > right.(uint64)
case valueTypeString:
ctx.retVal = left.(string) > right.(string)
case valueTypeBuffer:
ctx.retVal = cmpBuffers(left.([]byte), right.([]byte)) == 1
default:
return errInvalidComparisonType
}
return err
}
// cmpBuffers compares left and right and returns 0 if they are equal, -1 if
// left < right and 1 if left > right. According to the ACPI spec, cmpBuffers
// will first compare the lengths of the slices before delegating a
// lexicographical comparison to bytes.Compare.
func cmpBuffers(left, right []byte) int {
llen, rlen := len(left), len(right)
if llen < rlen {
return -1
} else if llen > rlen {
return 1
}
return bytes.Compare(left, right)
}

View File

@ -126,3 +126,305 @@ func TestArithmeticExpressionErrors(t *testing.T) {
}
})
}
func TestBitwiseExpressions(t *testing.T) {
specs := []struct {
method string
input interface{}
exp uint64
}{
{`\BI00`, uint64(100), uint64(800)},
{`\BI01`, uint64(100), uint64(25)},
{`\BI02`, uint64(7), uint64(5)},
{`\BI03`, uint64(32), uint64(40)},
{`\BI04`, uint64(8), uint64(0)},
{`\BI04`, uint64(9), uint64(1)},
{`\BI05`, uint64(7), uint64(0xfffffffffffffff8)},
{`\BI05`, uint64(12), uint64(0xfffffffffffffff0)},
{`\BI06`, uint64(32), uint64(48)},
{`\BI07`, uint64(0xffffffff), uint64(0xffffffff00000000)},
{`\BI08`, uint64(1 << 63), uint64(1)},
{`\BI08`, uint64(1), uint64(64)},
{`\BI08`, uint64(0), uint64(0)},
{`\BI09`, uint64(1 << 2), uint64(3)},
{`\BI09`, uint64(1 << 63), uint64(64)},
{`\BI09`, uint64(0), uint64(0)},
}
resolver := &mockResolver{
tableFiles: []string{"vm-testsuite-DSDT.aml"},
}
vm := NewVM(os.Stderr, resolver)
if err := vm.Init(); err != nil {
t.Fatal(err)
}
for specIndex, spec := range specs {
m := vm.Lookup(spec.method)
if m == nil {
t.Errorf("error looking up method: %q", spec.method)
continue
}
method := m.(*Method)
ctx := &execContext{
methodArg: [maxMethodArgs]interface{}{spec.input},
vm: vm,
}
if err := vm.execBlock(ctx, method); err != nil {
t.Errorf("[spec %02d] %s: invocation failed: %v\n", specIndex, spec.method, err)
continue
}
if !reflect.DeepEqual(ctx.retVal, spec.exp) {
t.Errorf("[spec %02d] %s: expected %d; got %v\n", specIndex, spec.method, spec.exp, ctx.retVal)
}
}
}
func TestBitwiseExpressionErrors(t *testing.T) {
t.Run("arg handling errors", func(t *testing.T) {
specs := []opHandler{
vmOpShiftLeft,
vmOpShiftRight,
vmOpBitwiseAnd,
vmOpBitwiseOr,
vmOpBitwiseNand,
vmOpBitwiseNor,
vmOpBitwiseXor,
vmOpBitwiseNot,
vmOpFindSetLeftBit,
vmOpFindSetRightBit,
}
for specIndex, handler := range specs {
if err := handler(nil, new(unnamedEntity)); err == nil {
t.Errorf("[spec %d] expected opHandler to return an error", specIndex)
}
}
})
}
func TestLogicExpressions(t *testing.T) {
bufOp := unnamedEntity{op: opBuffer}
specs := []struct {
method string
inputA, inputB interface{}
exp interface{}
}{
{`\LO00`, uint64(10), uint64(10), uint64(1)},
{`\LO00`, uint64(5), uint64(0), uint64(0)},
{`\LO00`, "lorem", "lorem", uint64(1)},
{`\LO00`, "ipsum", "DOLOR", uint64(0)},
{
`\LO00`,
&bufferEntity{unnamedEntity: bufOp, data: []byte{'!'}},
&bufferEntity{unnamedEntity: bufOp, data: []byte{'!'}},
uint64(1),
},
{
`\LO00`,
&bufferEntity{unnamedEntity: bufOp, data: []byte("LOREM")},
&bufferEntity{unnamedEntity: bufOp, data: []byte("lorem")},
uint64(0),
},
//
{`\LO01`, uint64(10), uint64(10), uint64(0)},
{`\LO01`, uint64(5), uint64(0), uint64(1)},
{`\LO01`, uint64(30), uint64(150), uint64(0)},
{`\LO01`, "lorem", "lore", uint64(1)},
{`\LO01`, "abc", "abd", uint64(0)},
{
`\LO01`,
&bufferEntity{unnamedEntity: bufOp, data: []byte("lore0")},
&bufferEntity{unnamedEntity: bufOp, data: []byte("lore1")},
uint64(0),
},
{
`\LO01`,
&bufferEntity{unnamedEntity: bufOp, data: []byte("1000")},
&bufferEntity{unnamedEntity: bufOp, data: []byte("0111")},
uint64(1),
},
//
{`\LO02`, uint64(10), uint64(10), uint64(1)},
{`\LO02`, uint64(50), uint64(49), uint64(1)},
{`\LO02`, uint64(49), uint64(50), uint64(0)},
//
{`\LO03`, uint64(10), uint64(10), uint64(0)},
{`\LO03`, uint64(0), uint64(10), uint64(1)},
//
{`\LO04`, uint64(10), uint64(10), uint64(0)},
{`\LO04`, uint64(5), uint64(0), uint64(0)},
{`\LO04`, uint64(30), uint64(150), uint64(1)},
{`\LO04`, "123", "321", uint64(1)},
{`\LO04`, "ab", "abc", uint64(1)},
{
`\LO04`,
&bufferEntity{unnamedEntity: bufOp, data: []byte("lore000")},
&bufferEntity{unnamedEntity: bufOp, data: []byte("lore1")},
uint64(0),
},
{
`\LO04`,
&bufferEntity{unnamedEntity: bufOp, data: []byte("1000")},
&bufferEntity{unnamedEntity: bufOp, data: []byte("0111+1")},
uint64(1),
},
//
{`\LO05`, uint64(10), uint64(10), uint64(1)},
{`\LO05`, uint64(50), uint64(49), uint64(0)},
{`\LO05`, uint64(49), uint64(50), uint64(1)},
//
{`\LO06`, true, false, uint64(0)},
{`\LO06`, false, true, uint64(0)},
{`\LO06`, true, true, uint64(1)},
{`\LO06`, false, false, uint64(0)},
{`\LO06`, "AA", "0", uint64(0)},
{`\LO06`, "0", "F00", uint64(0)},
//
{`\LO07`, true, false, uint64(1)},
{`\LO07`, false, true, uint64(1)},
{`\LO07`, true, true, uint64(1)},
{`\LO07`, false, false, uint64(0)},
{`\LO07`, "AA", "0", uint64(1)},
{`\LO07`, "0", "F00", uint64(1)},
{`\LO07`, "f00", "c0ffee", uint64(1)},
{`\LO07`, "0", "0", uint64(0)},
//
{`\LO08`, true, nil, uint64(0)},
{`\LO08`, false, nil, uint64(1)},
}
resolver := &mockResolver{
tableFiles: []string{"vm-testsuite-DSDT.aml"},
}
vm := NewVM(os.Stderr, resolver)
if err := vm.Init(); err != nil {
t.Fatal(err)
}
for specIndex, spec := range specs {
m := vm.Lookup(spec.method)
if m == nil {
t.Errorf("error looking up method: %q", spec.method)
continue
}
method := m.(*Method)
ctx := &execContext{
methodArg: [maxMethodArgs]interface{}{spec.inputA, spec.inputB},
vm: vm,
}
if err := vm.execBlock(ctx, method); err != nil {
t.Errorf("[spec %02d] %s: invocation failed: %v\n", specIndex, spec.method, err)
continue
}
if !reflect.DeepEqual(ctx.retVal, spec.exp) {
t.Errorf("[spec %02d] %s: expected %d; got %v\n", specIndex, spec.method, spec.exp, ctx.retVal)
}
}
}
func TestLogicExpressionErrors(t *testing.T) {
specs := []struct {
handler opHandler
argCount int
}{
{vmOpLogicalNot, 1},
{vmOpLogicalAnd, 2},
{vmOpLogicalOr, 2},
{vmOpLogicalEqual, 2},
{vmOpLogicalLess, 2},
{vmOpLogicalGreater, 2},
}
t.Run("arg handling errors", func(t *testing.T) {
for specIndex, spec := range specs {
if err := spec.handler(nil, new(unnamedEntity)); err == nil {
t.Errorf("[spec %d] expected opHandler to return an error", specIndex)
}
}
})
t.Run("arg loading errors", func(t *testing.T) {
ent := &unnamedEntity{
args: make([]interface{}, 2),
}
vm := &VM{sizeOfIntInBits: 64}
vm.populateJumpTable()
ctx := &execContext{vm: vm}
for specIndex, spec := range specs {
ent.args[0] = &Device{}
ent.args[1] = uint64(9)
if err := spec.handler(ctx, ent); err == nil {
t.Errorf("[spec %d] expected opHandler to return an error", specIndex)
}
if spec.argCount < 2 {
continue
}
ent.args[0] = uint64(123)
ent.args[1] = &Device{}
if err := spec.handler(ctx, ent); err == nil {
t.Errorf("[spec %d] expected opHandler to return an error", specIndex)
}
}
})
t.Run("2nd arg conversion error", func(t *testing.T) {
ent := &unnamedEntity{
args: make([]interface{}, 2),
}
vm := &VM{sizeOfIntInBits: 64}
vm.populateJumpTable()
ctx := &execContext{vm: vm}
for specIndex, spec := range specs {
if spec.argCount < 2 {
continue
}
ent.args[0] = uint64(30)
ent.args[1] = int64(9)
if err := spec.handler(ctx, ent); err == nil {
t.Errorf("[spec %d] expected opHandler to return an error", specIndex)
}
}
})
t.Run("unsupported comparison error", func(t *testing.T) {
specs := []opHandler{
vmOpLogicalEqual,
vmOpLogicalLess,
vmOpLogicalGreater,
}
ent := &unnamedEntity{
args: make([]interface{}, 2),
}
vm := &VM{sizeOfIntInBits: 64}
vm.populateJumpTable()
ctx := &execContext{vm: vm}
for specIndex, handler := range specs {
ent.args[0] = int64(1)
ent.args[1] = int64(9)
if err := handler(ctx, ent); err != errInvalidComparisonType {
t.Errorf("[spec %d] expected opHandler to return errInvalidComparisonType; got %v", specIndex, err)
}
}
})
}

View File

@ -25,6 +25,7 @@ func TestVMInit(t *testing.T) {
}
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)
@ -268,3 +269,33 @@ func TestVMExecBlockControlFlows(t *testing.T) {
}
})
}
func TestVMCheckEntitiesErrors(t *testing.T) {
vm := NewVM(os.Stderr, nil)
// Add a bogus named buffer entity to the root scope
vm.rootNS.Append(
&namedEntity{
name: "F000",
args: []interface{}{
&bufferEntity{
size: "", // this will cause a errConversionFromEmptyString error
},
},
},
)
// The previous error should prevent this entity from being processed
bufEnt := &bufferEntity{
size: uint64(16),
}
vm.rootNS.Append(bufEnt)
if err := vm.checkEntities(); err != errConversionFromEmptyString {
t.Fatalf("expected to get errConversionFromEmptyString; got %v", err)
}
if len(bufEnt.data) != 0 {
t.Fatal("expected error to short-circuit the entity check")
}
}

View File

@ -1,5 +1,22 @@
DefinitionBlock ("vm-testsuite-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0x00000002)
{
// This example tests automatic allocation for buffers that specify a size
// but not a data initializer.
Scope(\_SB){
// Buffer gets allocated with a length of 16 bytes
Name(BUF0, Buffer(16){})
// Buffer data gets padded with zeroes up to the declared length
Name(BUF1, Buffer(16){0xde, 0xad, 0xc0, 0xde})
OperationRegion(TOP1, GenericSerialBus, 0x00, 0x100) // GenericSerialBus device at command offset 0x00
Field(TOP1, BufferAcc, NoLock, Preserve){
Connection(I2cSerialBus(0x5b,,100000,, "\\_SB",,,,RawDataBuffer(16){})),
AccessAs(BufferAcc, AttribBytes(4)),
FLD2, 8,
}
}
// Arithmetic ops
Method (AR00, 1, NotSerialized)
{
@ -81,4 +98,110 @@ DefinitionBlock ("vm-testsuite-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0x000
{
Return(Arg0 / 10)
}
// Bit ops
Method (BI00, 1, NotSerialized)
{
ShiftLeft(Arg0, 3, Local0)
Return(Local0)
}
Method (BI01, 1, NotSerialized)
{
ShiftRight(Arg0, 2, Local0)
Return(Local0)
}
Method (BI02, 1, NotSerialized)
{
And(Arg0, 0xbadf00d, Local0)
Return(Local0)
}
Method (BI03, 1, NotSerialized)
{
Or(Arg0, 8, Local0)
Return(Local0)
}
Method (BI04, 1, NotSerialized)
{
Nand(Arg0, 8, Local0)
Return(Local0)
}
Method (BI05, 1, NotSerialized)
{
Nor(Arg0, 0x7, Local0)
Return(Local0)
}
Method (BI06, 1, NotSerialized)
{
Xor(Arg0, 16, Local0)
Return(Local0)
}
Method (BI07, 1, NotSerialized)
{
Not(Arg0, Local0)
Return(Local0)
}
Method (BI08, 1, NotSerialized)
{
Return(FindSetLeftBit(Arg0))
}
Method (BI09, 1, NotSerialized)
{
Return(FindSetRightBit(Arg0))
}
// Logic ops
Method (LO00, 2, NotSerialized)
{
Return(Arg0 == Arg1)
}
Method (LO01, 2, NotSerialized)
{
Return(Arg0 > Arg1)
}
Method (LO02, 2, NotSerialized)
{
Return(Arg0 >= Arg1)
}
Method (LO03, 2, NotSerialized)
{
Return(Arg0 != Arg1)
}
Method (LO04, 2, NotSerialized)
{
Return(Arg0 < Arg1)
}
Method (LO05, 2, NotSerialized)
{
Return(Arg0 <= Arg1)
}
Method (LO06, 2, NotSerialized)
{
Return(Arg0 && Arg1)
}
Method (LO07, 2, NotSerialized)
{
Return(Arg0 || Arg1)
}
Method (LO08, 1, NotSerialized)
{
Return(!Arg0)
}
}