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

acpi: implement string <=> int converter

The converter is pretty basic at the moment and it only supports
converting to/from Integer and String types. This commit also includes
some argument => uint64 conversion helpers that will serve as the basis
for implementing the ALU opcodes.
This commit is contained in:
Achilleas Anagnostopoulos 2017-11-27 07:25:33 +00:00
parent d6a825fc02
commit a172621af7
3 changed files with 309 additions and 0 deletions

View File

@ -16,6 +16,8 @@ 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"}
)
// objRef is a pointer to an argument (local or global) or a named AML object.

View File

@ -1,5 +1,7 @@
package aml
import "strconv"
// valueType represents the data types that the AML interpreter can process.
type valueType uint8
@ -70,6 +72,105 @@ func (vt valueType) String() string {
}
}
// vmToIntArg attempts to convert the entity argument at position argIndex to
// a uint64 value.
func vmToIntArg(ctx *execContext, ent Entity, argIndex int) (uint64, *Error) {
args := ent.getArgs()
if len(args) <= argIndex {
return 0, errArgIndexOutOfBounds
}
argVal, err := vmConvert(ctx, args[argIndex], valueTypeInteger)
if err != nil {
return 0, err
}
return argVal.(uint64), nil
}
// vmToIntArgs2 attempts to convert the entity arguments at positions argIndex1
// and argIndex2 to uint64 values.
func vmToIntArgs2(ctx *execContext, ent Entity, argIndex1, argIndex2 int) (uint64, uint64, *Error) {
args := ent.getArgs()
if len(args) <= argIndex1 || len(args) <= argIndex2 {
return 0, 0, errArgIndexOutOfBounds
}
argVal1, err := vmConvert(ctx, args[argIndex1], valueTypeInteger)
if err != nil {
return 0, 0, err
}
argVal2, err := vmConvert(ctx, args[argIndex2], valueTypeInteger)
if err != nil {
return 0, 0, err
}
return argVal1.(uint64), argVal2.(uint64), nil
}
// vmConvert attempts to convert the input argument to the specified type. If
// the conversion is not possible then vmConvert returns back an error.
func vmConvert(ctx *execContext, arg interface{}, toType valueType) (interface{}, *Error) {
argVal, err := vmLoad(ctx, arg)
if err != nil {
return nil, err
}
// Conversion not required; we can just read the value directly
argType := vmTypeOf(ctx, argVal)
if argType == toType {
return argVal, nil
}
switch argType {
case valueTypeString:
argAsStr := argVal.(string)
switch toType {
case valueTypeInteger:
// According to the spec: If no integer object exists, a new integer is created. The
// integer is initialized to the value zero and the ASCII string is interpreted as a
// hexadecimal constant. Each string character is interpreted as a hexadecimal value
// (0- 9, A-F, a-f), starting with the first character as the most significant
// digit, and ending with the first non-hexadecimal character, end-of-string, or
// when the size of an integer is reached (8 characters for 32-bit integers and 16
// characters for 64-bit integers). Note: the first non-hex character terminates the
// 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
}
var res = uint64(0)
for i := 0; i < len(argAsStr) && i < ctx.vm.sizeOfIntInBits>>2; i++ {
ch := argAsStr[i]
if ch >= '0' && ch <= '9' {
res = res<<4 | uint64(ch-'0')
} else if ch >= 'a' && ch <= 'f' {
res = res<<4 | uint64(ch-'a'+10)
} else if ch >= 'A' && ch <= 'F' {
res = res<<4 | uint64(ch-'A'+10)
} else {
// non-hex character; we should stop and return without an error
break
}
}
return res, nil
}
case valueTypeInteger:
argAsInt := argVal.(uint64)
switch toType {
case valueTypeString:
// Integers are formatted as hex strings without a 0x prefix
return strconv.FormatUint(argAsInt, 16), nil
}
}
return nil, errConversionFailed
}
// vmTypeOf returns the type of data stored inside the supplied argument.
func vmTypeOf(ctx *execContext, arg interface{}) valueType {
// Some objects (e.g args, constEntity contents) may require to perform

View File

@ -1,6 +1,7 @@
package aml
import (
"reflect"
"testing"
)
@ -172,3 +173,208 @@ func TestVMTypeOf(t *testing.T) {
}
}
}
func TestVMToIntArg(t *testing.T) {
ctx := &execContext{
vm: &VM{sizeOfIntInBits: 64},
}
specs := []struct {
ent Entity
argIndex int
expVal uint64
expErr *Error
}{
{
&unnamedEntity{
args: []interface{}{uint64(42)},
},
0,
42,
nil,
},
{
&unnamedEntity{
args: []interface{}{""},
},
0,
0,
errConversionFailed,
},
{
&unnamedEntity{},
0,
0,
errArgIndexOutOfBounds,
},
}
for specIndex, spec := range specs {
got, err := vmToIntArg(ctx, spec.ent, spec.argIndex)
switch {
case !reflect.DeepEqual(spec.expErr, err):
t.Errorf("[spec %d] expected error: %v; got: %v", specIndex, spec.expErr, err)
case got != spec.expVal:
t.Errorf("[spec %d] expected to get value %v; got %v", specIndex, spec.expVal, got)
}
}
}
func TestVMToIntArgs2(t *testing.T) {
ctx := &execContext{
vm: &VM{sizeOfIntInBits: 64},
}
specs := []struct {
ent Entity
argIndex [2]int
expVal [2]uint64
expErr *Error
}{
{
&unnamedEntity{
args: []interface{}{uint64(42), uint64(999)},
},
[2]int{0, 1},
[2]uint64{42, 999},
nil,
},
{
&unnamedEntity{
args: []interface{}{"", uint64(999)},
},
[2]int{0, 1},
[2]uint64{0, 0},
errConversionFailed,
},
{
&unnamedEntity{
args: []interface{}{uint64(123), ""},
},
[2]int{0, 1},
[2]uint64{0, 0},
errConversionFailed,
},
{
&unnamedEntity{},
[2]int{128, 0},
[2]uint64{0, 0},
errArgIndexOutOfBounds,
},
{
&unnamedEntity{args: []interface{}{uint64(42)}},
[2]int{0, 128},
[2]uint64{0, 0},
errArgIndexOutOfBounds,
},
}
for specIndex, spec := range specs {
got1, got2, err := vmToIntArgs2(ctx, spec.ent, 0, 1)
switch {
case !reflect.DeepEqual(spec.expErr, err):
t.Errorf("[spec %d] expected error: %v; got: %v", specIndex, spec.expErr, err)
case got1 != spec.expVal[0] || got2 != spec.expVal[1]:
t.Errorf("[spec %d] expected to get values [%v, %v] ; got [%v, %v]", specIndex,
spec.expVal[0], spec.expVal[1],
got1, got2,
)
}
}
}
func TestVMConvert(t *testing.T) {
specs := []struct {
ctx *execContext
in interface{}
toType valueType
expVal interface{}
expErr *Error
}{
// No conversion required
{
nil,
"foo",
valueTypeString,
"foo",
nil,
},
// string -> int (32-bit mode)
{
&execContext{
vm: &VM{sizeOfIntInBits: 32},
},
"bAdF00D9",
valueTypeInteger,
uint64(0xbadf00d9),
nil,
},
// string -> int (64-bit mode)
{
&execContext{
vm: &VM{sizeOfIntInBits: 64},
},
"feedfaceDEADC0DE-ignored-data",
valueTypeInteger,
uint64(0xfeedfacedeadc0de),
nil,
},
// string -> int (64-bit mode) ; stop at first non-hex char
{
&execContext{
vm: &VM{sizeOfIntInBits: 64},
},
"feedGARBAGE",
valueTypeInteger,
uint64(0xfeed),
nil,
},
// string -> int; empty string should trigger an error
{
&execContext{
vm: &VM{sizeOfIntInBits: 64},
},
"",
valueTypeInteger,
nil,
errConversionFailed,
},
// int -> string
{
nil,
uint64(0xfeedfacedeadc0de),
valueTypeString,
"feedfacedeadc0de",
nil,
},
// vmLoad returns an error
{
nil,
&unnamedEntity{op: opAdd},
valueTypeInteger,
nil,
&Error{message: "readArg: unsupported entity type: " + opAdd.String()},
},
// unsupported conversion
{
nil,
uint64(42),
valueTypeDevice,
nil,
errConversionFailed,
},
}
for specIndex, spec := range specs {
got, err := vmConvert(spec.ctx, spec.in, spec.toType)
switch {
case !reflect.DeepEqual(spec.expErr, err):
t.Errorf("[spec %d] expected error: %v; got: %v", specIndex, spec.expErr, err)
case got != spec.expVal:
t.Errorf("[spec %d] expected to get value %v (type: %v); got %v (type %v)", specIndex,
spec.expVal, reflect.TypeOf(spec.expVal),
got, reflect.TypeOf(got),
)
}
}
}