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:
parent
38b2a3e4e2
commit
2be5b9d224
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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},
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user