From 89923eb4818481c664fef215b8b31d23303d33af Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Sat, 16 Dec 2017 08:15:43 +0000 Subject: [PATCH 1/9] acpi: move AML opcode definitions into the entity pkg --- src/gopheros/device/acpi/aml/entity/opcode.go | 202 ++++++++++++++++++ .../device/acpi/aml/entity/opcode_test.go | 116 ++++++++++ 2 files changed, 318 insertions(+) create mode 100644 src/gopheros/device/acpi/aml/entity/opcode.go create mode 100644 src/gopheros/device/acpi/aml/entity/opcode_test.go diff --git a/src/gopheros/device/acpi/aml/entity/opcode.go b/src/gopheros/device/acpi/aml/entity/opcode.go new file mode 100644 index 0000000..7c0bf67 --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/opcode.go @@ -0,0 +1,202 @@ +package entity + +// AMLOpcode describes an AML opcode. While AML supports 256 opcodes, some of +// them are specified using a combination of an extension prefix and a code. To +// map each opcode into a single unique value the parser uses an uint16 +// representation of the opcode values. +type AMLOpcode uint16 + +const ( + // Regular opcode list + OpZero = AMLOpcode(0x00) + OpOne = AMLOpcode(0x01) + OpAlias = AMLOpcode(0x06) + OpName = AMLOpcode(0x08) + OpBytePrefix = AMLOpcode(0x0a) + OpWordPrefix = AMLOpcode(0x0b) + OpDwordPrefix = AMLOpcode(0x0c) + OpStringPrefix = AMLOpcode(0x0d) + OpQwordPrefix = AMLOpcode(0x0e) + OpScope = AMLOpcode(0x10) + OpBuffer = AMLOpcode(0x11) + OpPackage = AMLOpcode(0x12) + OpVarPackage = AMLOpcode(0x13) + OpMethod = AMLOpcode(0x14) + OpExternal = AMLOpcode(0x15) + OpLocal0 = AMLOpcode(0x60) + OpLocal1 = AMLOpcode(0x61) + OpLocal2 = AMLOpcode(0x62) + OpLocal3 = AMLOpcode(0x63) + OpLocal4 = AMLOpcode(0x64) + OpLocal5 = AMLOpcode(0x65) + OpLocal6 = AMLOpcode(0x66) + OpLocal7 = AMLOpcode(0x67) + OpArg0 = AMLOpcode(0x68) + OpArg1 = AMLOpcode(0x69) + OpArg2 = AMLOpcode(0x6a) + OpArg3 = AMLOpcode(0x6b) + OpArg4 = AMLOpcode(0x6c) + OpArg5 = AMLOpcode(0x6d) + OpArg6 = AMLOpcode(0x6e) + OpStore = AMLOpcode(0x70) + OpRefOf = AMLOpcode(0x71) + OpAdd = AMLOpcode(0x72) + OpConcat = AMLOpcode(0x73) + OpSubtract = AMLOpcode(0x74) + OpIncrement = AMLOpcode(0x75) + OpDecrement = AMLOpcode(0x76) + OpMultiply = AMLOpcode(0x77) + OpDivide = AMLOpcode(0x78) + OpShiftLeft = AMLOpcode(0x79) + OpShiftRight = AMLOpcode(0x7a) + OpAnd = AMLOpcode(0x7b) + OpNand = AMLOpcode(0x7c) + OpOr = AMLOpcode(0x7d) + OpNor = AMLOpcode(0x7e) + OpXor = AMLOpcode(0x7f) + OpNot = AMLOpcode(0x80) + OpFindSetLeftBit = AMLOpcode(0x81) + OpFindSetRightBit = AMLOpcode(0x82) + OpDerefOf = AMLOpcode(0x83) + OpConcatRes = AMLOpcode(0x84) + OpMod = AMLOpcode(0x85) + OpNotify = AMLOpcode(0x86) + OpSizeOf = AMLOpcode(0x87) + OpIndex = AMLOpcode(0x88) + OpMatch = AMLOpcode(0x89) + OpCreateDWordField = AMLOpcode(0x8a) + OpCreateWordField = AMLOpcode(0x8b) + OpCreateByteField = AMLOpcode(0x8c) + OpCreateBitField = AMLOpcode(0x8d) + OpObjectType = AMLOpcode(0x8e) + OpCreateQWordField = AMLOpcode(0x8f) + OpLand = AMLOpcode(0x90) + OpLor = AMLOpcode(0x91) + OpLnot = AMLOpcode(0x92) + OpLEqual = AMLOpcode(0x93) + OpLGreater = AMLOpcode(0x94) + OpLLess = AMLOpcode(0x95) + OpToBuffer = AMLOpcode(0x96) + OpToDecimalString = AMLOpcode(0x97) + OpToHexString = AMLOpcode(0x98) + OpToInteger = AMLOpcode(0x99) + OpToString = AMLOpcode(0x9c) + OpCopyObject = AMLOpcode(0x9d) + OpMid = AMLOpcode(0x9e) + OpContinue = AMLOpcode(0x9f) + OpIf = AMLOpcode(0xa0) + OpElse = AMLOpcode(0xa1) + OpWhile = AMLOpcode(0xa2) + OpNoop = AMLOpcode(0xa3) + OpReturn = AMLOpcode(0xa4) + OpBreak = AMLOpcode(0xa5) + OpBreakPoint = AMLOpcode(0xcc) + OpOnes = AMLOpcode(0xff) + // Extended opcodes + OpMutex = AMLOpcode(0xff + 0x01) + OpEvent = AMLOpcode(0xff + 0x02) + OpCondRefOf = AMLOpcode(0xff + 0x12) + OpCreateField = AMLOpcode(0xff + 0x13) + OpLoadTable = AMLOpcode(0xff + 0x1f) + OpLoad = AMLOpcode(0xff + 0x20) + OpStall = AMLOpcode(0xff + 0x21) + OpSleep = AMLOpcode(0xff + 0x22) + OpAcquire = AMLOpcode(0xff + 0x23) + OpSignal = AMLOpcode(0xff + 0x24) + OpWait = AMLOpcode(0xff + 0x25) + OpReset = AMLOpcode(0xff + 0x26) + OpRelease = AMLOpcode(0xff + 0x27) + OpFromBCD = AMLOpcode(0xff + 0x28) + OpToBCD = AMLOpcode(0xff + 0x29) + OpUnload = AMLOpcode(0xff + 0x2a) + OpRevision = AMLOpcode(0xff + 0x30) + OpDebug = AMLOpcode(0xff + 0x31) + OpFatal = AMLOpcode(0xff + 0x32) + OpTimer = AMLOpcode(0xff + 0x33) + OpOpRegion = AMLOpcode(0xff + 0x80) + OpField = AMLOpcode(0xff + 0x81) + OpDevice = AMLOpcode(0xff + 0x82) + OpProcessor = AMLOpcode(0xff + 0x83) + OpPowerRes = AMLOpcode(0xff + 0x84) + OpThermalZone = AMLOpcode(0xff + 0x85) + OpIndexField = AMLOpcode(0xff + 0x86) + OpBankField = AMLOpcode(0xff + 0x87) + OpDataRegion = AMLOpcode(0xff + 0x88) +) + +// OpIsLocalArg returns true if this opcode represents any of the supported local +// function args 0 to 7. +func OpIsLocalArg(op AMLOpcode) bool { + return op >= OpLocal0 && op <= OpLocal7 +} + +// OpIsMethodArg returns true if this opcode represents any of the supported +// input function args 0 to 6. +func OpIsMethodArg(op AMLOpcode) bool { + return op >= OpArg0 && op <= OpArg6 +} + +// OpIsArg returns true if this opcode is either a local or a method arg. +func OpIsArg(op AMLOpcode) bool { + return OpIsLocalArg(op) || OpIsMethodArg(op) +} + +// OpIsDataObject returns true if this opcode is part of a DataObject definition +// +// Grammar: +// DataObject := ComputationalData | DefPackage | DefVarPackage +// ComputationalData := ByteConst | WordConst | DWordConst | QWordConst | String | ConstObj | RevisionOp | DefBuffer +// ConstObj := ZeroOp | OneOp | OnesOp +func OpIsDataObject(op AMLOpcode) bool { + switch op { + case OpBytePrefix, OpWordPrefix, OpDwordPrefix, OpQwordPrefix, OpStringPrefix, + OpZero, OpOne, OpOnes, OpRevision, OpBuffer, OpPackage, OpVarPackage: + return true + default: + return false + } +} + +// OpIsType2 returns true if this is a Type2Opcode. +// +// Grammar: +// Type2Opcode := DefAcquire | DefAdd | DefAnd | DefBuffer | DefConcat | +// DefConcatRes | DefCondRefOf | DefCopyObject | DefDecrement | +// DefDerefOf | DefDivide | DefFindSetLeftBit | DefFindSetRightBit | +// DefFromBCD | DefIncrement | DefIndex | DefLAnd | DefLEqual | +// DefLGreater | DefLGreaterEqual | DefLLess | DefLLessEqual | DefMid | +// DefLNot | DefLNotEqual | DefLoadTable | DefLOr | DefMatch | DefMod | +// DefMultiply | DefNAnd | DefNOr | DefNot | DefObjectType | DefOr | +// DefPackage | DefVarPackage | DefRefOf | DefShiftLeft | DefShiftRight | +// DefSizeOf | DefStore | DefSubtract | DefTimer | DefToBCD | DefToBuffer | +// DefToDecimalString | DefToHexString | DefToInteger | DefToString | +// DefWait | DefXOr +func OpIsType2(op AMLOpcode) bool { + switch op { + case OpAcquire, OpAdd, OpAnd, OpBuffer, OpConcat, + OpConcatRes, OpCondRefOf, OpCopyObject, OpDecrement, + OpDerefOf, OpDivide, OpFindSetLeftBit, OpFindSetRightBit, + OpFromBCD, OpIncrement, OpIndex, OpLand, OpLEqual, + OpLGreater, OpLLess, OpMid, + OpLnot, OpLoadTable, OpLor, OpMatch, OpMod, + OpMultiply, OpNand, OpNor, OpNot, OpObjectType, OpOr, + OpPackage, OpVarPackage, OpRefOf, OpShiftLeft, OpShiftRight, + OpSizeOf, OpStore, OpSubtract, OpTimer, OpToBCD, OpToBuffer, + OpToDecimalString, OpToHexString, OpToInteger, OpToString, + OpWait, OpXor: + return true + default: + return false + } +} + +// OpIsBufferField returens true if this opcode describes a +// buffer field creation operation. +func OpIsBufferField(op AMLOpcode) bool { + switch op { + case OpCreateField, OpCreateBitField, OpCreateByteField, OpCreateWordField, OpCreateDWordField, OpCreateQWordField: + return true + default: + return false + } +} diff --git a/src/gopheros/device/acpi/aml/entity/opcode_test.go b/src/gopheros/device/acpi/aml/entity/opcode_test.go new file mode 100644 index 0000000..3cf0e4c --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/opcode_test.go @@ -0,0 +1,116 @@ +package entity + +import "testing" + +func TestOpcodeIsX(t *testing.T) { + specs := []struct { + op AMLOpcode + testFn func(AMLOpcode) bool + want bool + }{ + // OpIsLocalArg + {OpLocal0, OpIsLocalArg, true}, + {OpLocal1, OpIsLocalArg, true}, + {OpLocal2, OpIsLocalArg, true}, + {OpLocal3, OpIsLocalArg, true}, + {OpLocal4, OpIsLocalArg, true}, + {OpLocal5, OpIsLocalArg, true}, + {OpLocal6, OpIsLocalArg, true}, + {OpLocal7, OpIsLocalArg, true}, + {OpArg0, OpIsLocalArg, false}, + {OpDivide, OpIsLocalArg, false}, + // OpIsMethodArg + {OpArg0, OpIsMethodArg, true}, + {OpArg1, OpIsMethodArg, true}, + {OpArg2, OpIsMethodArg, true}, + {OpArg3, OpIsMethodArg, true}, + {OpArg4, OpIsMethodArg, true}, + {OpArg5, OpIsMethodArg, true}, + {OpArg6, OpIsMethodArg, true}, + {OpLocal7, OpIsMethodArg, false}, + {OpIf, OpIsMethodArg, false}, + // OpIsArg + {OpLocal5, OpIsArg, true}, + {OpArg1, OpIsArg, true}, + {OpDivide, OpIsArg, false}, + // OpIsType2 + {OpAcquire, OpIsType2, true}, + {OpAdd, OpIsType2, true}, + {OpAnd, OpIsType2, true}, + {OpBuffer, OpIsType2, true}, + {OpConcat, OpIsType2, true}, + {OpConcatRes, OpIsType2, true}, + {OpCondRefOf, OpIsType2, true}, + {OpCopyObject, OpIsType2, true}, + {OpDecrement, OpIsType2, true}, + {OpDerefOf, OpIsType2, true}, + {OpDivide, OpIsType2, true}, + {OpFindSetLeftBit, OpIsType2, true}, + {OpFindSetRightBit, OpIsType2, true}, + {OpFromBCD, OpIsType2, true}, + {OpIncrement, OpIsType2, true}, + {OpIndex, OpIsType2, true}, + {OpLand, OpIsType2, true}, + {OpLEqual, OpIsType2, true}, + {OpLGreater, OpIsType2, true}, + {OpLLess, OpIsType2, true}, + {OpMid, OpIsType2, true}, + {OpLnot, OpIsType2, true}, + {OpLoadTable, OpIsType2, true}, + {OpLor, OpIsType2, true}, + {OpMatch, OpIsType2, true}, + {OpMod, OpIsType2, true}, + {OpMultiply, OpIsType2, true}, + {OpNand, OpIsType2, true}, + {OpNor, OpIsType2, true}, + {OpNot, OpIsType2, true}, + {OpObjectType, OpIsType2, true}, + {OpOr, OpIsType2, true}, + {OpPackage, OpIsType2, true}, + {OpVarPackage, OpIsType2, true}, + {OpRefOf, OpIsType2, true}, + {OpShiftLeft, OpIsType2, true}, + {OpShiftRight, OpIsType2, true}, + {OpSizeOf, OpIsType2, true}, + {OpStore, OpIsType2, true}, + {OpSubtract, OpIsType2, true}, + {OpTimer, OpIsType2, true}, + {OpToBCD, OpIsType2, true}, + {OpToBuffer, OpIsType2, true}, + {OpToDecimalString, OpIsType2, true}, + {OpToHexString, OpIsType2, true}, + {OpToInteger, OpIsType2, true}, + {OpToString, OpIsType2, true}, + {OpWait, OpIsType2, true}, + {OpXor, OpIsType2, true}, + {OpBytePrefix, OpIsType2, false}, + // OpIsDataObject + {OpBytePrefix, OpIsDataObject, true}, + {OpWordPrefix, OpIsDataObject, true}, + {OpDwordPrefix, OpIsDataObject, true}, + {OpQwordPrefix, OpIsDataObject, true}, + {OpStringPrefix, OpIsDataObject, true}, + {OpZero, OpIsDataObject, true}, + {OpOne, OpIsDataObject, true}, + {OpOnes, OpIsDataObject, true}, + {OpRevision, OpIsDataObject, true}, + {OpBuffer, OpIsDataObject, true}, + {OpPackage, OpIsDataObject, true}, + {OpVarPackage, OpIsDataObject, true}, + {OpLor, OpIsDataObject, false}, + // OpIsBufferField + {OpCreateField, OpIsBufferField, true}, + {OpCreateBitField, OpIsBufferField, true}, + {OpCreateByteField, OpIsBufferField, true}, + {OpCreateWordField, OpIsBufferField, true}, + {OpCreateDWordField, OpIsBufferField, true}, + {OpCreateQWordField, OpIsBufferField, true}, + {OpRevision, OpIsBufferField, false}, + } + + for specIndex, spec := range specs { + if got := spec.testFn(spec.op); got != spec.want { + t.Errorf("[spec %d] opcode %q: expected to get %t; got %t", specIndex, spec.op, spec.want, got) + } + } +} From 17842763e9147eaa3dd3f51eb248cad5291e875e Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Sat, 16 Dec 2017 07:45:37 +0000 Subject: [PATCH 2/9] acpi: move entities to the entity pkg, export them and add missing entities This commit moves the AML entity definitions into the entity package and makes them exportable so we can reference them from other packages. In addition, the commit adds some missing entity structs that were previously treated as generic entities (e.g. Processor, PowerResource and ThermalZone). Finally, this commit cleans the definitions and adds missing struct attributes for the various field types (Field, IndexField, BankField) --- src/gopheros/device/acpi/aml/entity.go | 506 ---------- src/gopheros/device/acpi/aml/entity/entity.go | 946 ++++++++++++++++++ .../device/acpi/aml/entity/entity_test.go | 185 ++++ src/gopheros/device/acpi/aml/entity/opcode.go | 238 +++++ .../device/acpi/aml/entity/opcode_test.go | 129 +++ src/gopheros/device/acpi/aml/entity_test.go | 173 ---- 6 files changed, 1498 insertions(+), 679 deletions(-) delete mode 100644 src/gopheros/device/acpi/aml/entity.go create mode 100644 src/gopheros/device/acpi/aml/entity/entity.go create mode 100644 src/gopheros/device/acpi/aml/entity/entity_test.go delete mode 100644 src/gopheros/device/acpi/aml/entity_test.go diff --git a/src/gopheros/device/acpi/aml/entity.go b/src/gopheros/device/acpi/aml/entity.go deleted file mode 100644 index 6a70839..0000000 --- a/src/gopheros/device/acpi/aml/entity.go +++ /dev/null @@ -1,506 +0,0 @@ -package aml - -import ( - "gopheros/kernel/kfmt" - "io" -) - -type resolver interface { - Resolve(io.Writer, ScopeEntity) bool -} - -// Entity is an interface implemented by all AML entities. -type Entity interface { - Name() string - Parent() ScopeEntity - TableHandle() uint8 - - setTableHandle(uint8) - getOpcode() opcode - setOpcode(opcode) - setParent(ScopeEntity) - getArgs() []interface{} - setArg(uint8, interface{}) bool -} - -// ScopeEntity is an interface that is implemented by entities that define an -// AML scope. -type ScopeEntity interface { - Entity - - Children() []Entity - Append(Entity) bool - - removeChild(Entity) - lastChild() Entity -} - -// unnamedEntity defines an unnamed entity that can be attached to a parent scope. -type unnamedEntity struct { - tableHandle uint8 - op opcode - args []interface{} - parent ScopeEntity -} - -func (ent *unnamedEntity) getOpcode() opcode { return ent.op } -func (ent *unnamedEntity) setOpcode(op opcode) { ent.op = op } -func (ent *unnamedEntity) Name() string { return "" } -func (ent *unnamedEntity) Parent() ScopeEntity { return ent.parent } -func (ent *unnamedEntity) setParent(parent ScopeEntity) { ent.parent = parent } -func (ent *unnamedEntity) getArgs() []interface{} { return ent.args } -func (ent *unnamedEntity) setArg(_ uint8, arg interface{}) bool { - ent.args = append(ent.args, arg) - return true -} -func (ent *unnamedEntity) TableHandle() uint8 { return ent.tableHandle } -func (ent *unnamedEntity) setTableHandle(h uint8) { ent.tableHandle = h } - -// namedEntity is a named entity that can be attached to the parent scope. The -// setArg() implementation for this type expects arg at index 0 to contain the -// entity name. -type namedEntity struct { - tableHandle uint8 - op opcode - args []interface{} - parent ScopeEntity - - name string -} - -func (ent *namedEntity) getOpcode() opcode { return ent.op } -func (ent *namedEntity) setOpcode(op opcode) { ent.op = op } -func (ent *namedEntity) Name() string { return ent.name } -func (ent *namedEntity) Parent() ScopeEntity { return ent.parent } -func (ent *namedEntity) setParent(parent ScopeEntity) { ent.parent = parent } -func (ent *namedEntity) getArgs() []interface{} { return ent.args } -func (ent *namedEntity) setArg(argIndex uint8, arg interface{}) bool { - // arg 0 is the entity name - if argIndex == 0 { - var ok bool - ent.name, ok = arg.(string) - return ok - } - - ent.args = append(ent.args, arg) - return true -} -func (ent *namedEntity) TableHandle() uint8 { return ent.tableHandle } -func (ent *namedEntity) setTableHandle(h uint8) { ent.tableHandle = h } - -// constEntity is an optionally-named entity which always -// evaluates to a constant value. Calls to setArg for -// argument index 0 will memoize the argument value that is -// stored inside this entity. -type constEntity struct { - name string - tableHandle uint8 - op opcode - args []interface{} - parent ScopeEntity - - val interface{} -} - -func (ent *constEntity) getOpcode() opcode { return ent.op } -func (ent *constEntity) setOpcode(op opcode) { - ent.op = op - - // special const opcode cases that have an implicit value - switch ent.op { - case opZero: - ent.val = uint64(0) - case opOne: - ent.val = uint64(1) - case opOnes: - ent.val = uint64(1<<64 - 1) - } -} -func (ent *constEntity) Name() string { return ent.name } -func (ent *constEntity) Parent() ScopeEntity { return ent.parent } -func (ent *constEntity) setParent(parent ScopeEntity) { ent.parent = parent } -func (ent *constEntity) getArgs() []interface{} { return ent.args } -func (ent *constEntity) setArg(argIndex uint8, arg interface{}) bool { - ent.val = arg - return argIndex == 0 -} -func (ent *constEntity) TableHandle() uint8 { return ent.tableHandle } -func (ent *constEntity) setTableHandle(h uint8) { ent.tableHandle = h } - -// scopeEntity is an optionally named entity that defines a scope. -type scopeEntity struct { - tableHandle uint8 - op opcode - args []interface{} - parent ScopeEntity - - name string - children []Entity -} - -func (ent *scopeEntity) getOpcode() opcode { return ent.op } -func (ent *scopeEntity) setOpcode(op opcode) { ent.op = op } -func (ent *scopeEntity) Name() string { return ent.name } -func (ent *scopeEntity) Parent() ScopeEntity { return ent.parent } -func (ent *scopeEntity) setParent(parent ScopeEntity) { ent.parent = parent } -func (ent *scopeEntity) getArgs() []interface{} { return ent.args } -func (ent *scopeEntity) setArg(argIndex uint8, arg interface{}) bool { - // arg 0 *may* be the entity name. If it's not a string just add it to - // the arg list. - if argIndex == 0 { - var ok bool - if ent.name, ok = arg.(string); ok { - return true - } - } - - ent.args = append(ent.args, arg) - return true -} -func (ent *scopeEntity) Children() []Entity { return ent.children } -func (ent *scopeEntity) Append(child Entity) bool { - child.setParent(ent) - ent.children = append(ent.children, child) - return true -} -func (ent *scopeEntity) lastChild() Entity { return ent.children[len(ent.children)-1] } -func (ent *scopeEntity) removeChild(child Entity) { - for index := 0; index < len(ent.children); index++ { - if ent.children[index] == child { - ent.children = append(ent.children[:index], ent.children[index+1:]...) - return - } - } -} -func (ent *scopeEntity) TableHandle() uint8 { return ent.tableHandle } -func (ent *scopeEntity) setTableHandle(h uint8) { ent.tableHandle = h } - -// bufferEntity defines a buffer object. -type bufferEntity struct { - unnamedEntity - - size interface{} - data []byte -} - -func (ent *bufferEntity) setArg(argIndex uint8, arg interface{}) bool { - switch argIndex { - case 0: // size - ent.size = arg - return true - case 1: // data - if byteSlice, ok := arg.([]byte); ok { - ent.data = byteSlice - return true - } - } - - return false -} - -// bufferFieldEntity describes a bit/byte/word/dword/qword or arbitrary length -// buffer field. -type bufferFieldEntity struct { - namedEntity -} - -func (ent *bufferFieldEntity) setArg(argIndex uint8, arg interface{}) bool { - // opCreateField specifies the name using the arg at index 3 while - // opCreateXXXField (byte, word e.t.c) specifies the name using the - // arg at index 2 - if (ent.op == opCreateField && argIndex == 3) || argIndex == 2 { - var ok bool - ent.name, ok = arg.(string) - return ok - } - ent.args = append(ent.args, arg) - return true -} - -// RegionSpace describes the memory space where a region is located. -type RegionSpace uint8 - -// The list of supported RegionSpace values. -const ( - RegionSpaceSystemMemory RegionSpace = iota - RegionSpaceSystemIO - RegionSpacePCIConfig - RegionSpaceEmbeddedControl - RegionSpaceSMBus - RegionSpacePCIBarTarget - RegionSpaceIPMI -) - -// regionEntity defines a region located at a particular space (e.g in memory, -// an embedded controller, the SMBus e.t.c). -type regionEntity struct { - namedEntity - - space RegionSpace -} - -func (ent *regionEntity) setArg(argIndex uint8, arg interface{}) bool { - var ok bool - switch argIndex { - case 0: - ok = ent.namedEntity.setArg(argIndex, arg) - case 1: - // the parser will convert ByteData types to uint64 - var space uint64 - space, ok = arg.(uint64) - ent.space = RegionSpace(space) - case 2, 3: - ent.args = append(ent.args, arg) - ok = true - } - - return ok -} - -// FieldAccessType specifies the type of access (byte, word, e.t.c) used to -// read/write to a field. -type FieldAccessType uint8 - -// The list of supported FieldAccessType values. -const ( - FieldAccessTypeAny FieldAccessType = iota - FieldAccessTypeByte - FieldAccessTypeWord - FieldAccessTypeDword - FieldAccessTypeQword - FieldAccessTypeBuffer -) - -// FieldUpdateRule specifies how a field value is updated when a write uses -// a value with a smaller width than the field. -type FieldUpdateRule uint8 - -// The list of supported FieldUpdateRule values. -const ( - FieldUpdateRulePreserve FieldUpdateRule = iota - FieldUpdateRuleWriteAsOnes - FieldUpdateRuleWriteAsZeros -) - -// FieldAccessAttrib specifies additional information about a particular field -// access. -type FieldAccessAttrib uint8 - -// The list of supported FieldAccessAttrib values. -const ( - FieldAccessAttribQuick FieldAccessAttrib = 0x02 - FieldAccessAttribSendReceive = 0x04 - FieldAccessAttribByte = 0x06 - FieldAccessAttribWord = 0x08 - FieldAccessAttribBlock = 0x0a - FieldAccessAttribBytes = 0x0b // byteCount contains the number of bytes - FieldAccessAttribProcessCall = 0x0c - FieldAccessAttribBlockProcessCall = 0x0d - FieldAccessAttribRawBytes = 0x0e // byteCount contains the number of bytes - FieldAccessAttribRawProcessBytes = 0x0f // byteCount contains the number of bytes -) - -// fieldEntity is a named object that encapsulates the data shared between regular -// fields and index fields. -type fieldEntity struct { - namedEntity - - bitOffset uint32 - bitWidth uint32 - - lock bool - updateRule FieldUpdateRule - - // accessAttrib is valid if accessType is BufferAcc - // for the SMB or GPIO OpRegions. - accessAttrib FieldAccessAttrib - accessType FieldAccessType - - // byteCount is valid when accessAttrib is one of: - // Bytes, RawBytes or RawProcessBytes - byteCount uint8 -} - -// fieldUnitEntity is a field defined inside an operating region. -type fieldUnitEntity struct { - fieldEntity - - // The connection which this field references. - connectionName string - resolvedConnection Entity - - // The region which this field references. - regionName string - resolvedRegion *regionEntity -} - -func (ent *fieldUnitEntity) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool { - var ok bool - if ent.connectionName != "" && ent.resolvedConnection == nil { - if ent.resolvedConnection = scopeFind(ent.parent, rootNs, ent.connectionName); ent.resolvedConnection == nil { - kfmt.Fprintf(errWriter, "[field %s] could not resolve connection reference: %s\n", ent.name, ent.connectionName) - return false - } - } - - if ent.resolvedRegion == nil { - if ent.resolvedRegion, ok = scopeFind(ent.parent, rootNs, ent.regionName).(*regionEntity); !ok { - kfmt.Fprintf(errWriter, "[field %s] could not resolve referenced region: %s\n", ent.name, ent.regionName) - } - } - - return ent.resolvedRegion != nil -} - -// indexFieldEntity is a special field that groups together two field units so a -// index/data register pattern can be implemented. To write a value to an -// indexField, the interpreter must first write the appropriate offset to -// the indexRegister (using the alignment specifid by accessType) and then -// write the actual value to the dataRegister. -type indexFieldEntity struct { - fieldEntity - - // The connection which this field references. - connectionName string - resolvedConnection Entity - - indexRegName string - indexReg *fieldUnitEntity - - dataRegName string - dataReg *fieldUnitEntity -} - -func (ent *indexFieldEntity) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool { - var ok bool - if ent.connectionName != "" && ent.resolvedConnection == nil { - if ent.resolvedConnection = scopeFind(ent.parent, rootNs, ent.connectionName); ent.resolvedConnection == nil { - kfmt.Fprintf(errWriter, "[field %s] could not resolve connection reference: %s\n", ent.name, ent.connectionName) - return false - } - } - - if ent.indexReg == nil { - if ent.indexReg, ok = scopeFind(ent.parent, rootNs, ent.indexRegName).(*fieldUnitEntity); !ok { - kfmt.Fprintf(errWriter, "[indexField %s] could not resolve referenced index register: %s\n", ent.name, ent.indexRegName) - } - } - - if ent.dataReg == nil { - if ent.dataReg, ok = scopeFind(ent.parent, rootNs, ent.dataRegName).(*fieldUnitEntity); !ok { - kfmt.Fprintf(errWriter, "[dataField %s] could not resolve referenced data register: %s\n", ent.name, ent.dataRegName) - } - } - - return ent.indexReg != nil && ent.dataReg != nil -} - -// namedReference holds a named reference to an AML symbol. The spec allows -// the symbol not to be defined at the time when the reference is parsed. In -// such a case (forward reference) it will be resolved after the entire AML -// stream has successfully been parsed. -type namedReference struct { - unnamedEntity - - targetName string - target Entity -} - -func (ref *namedReference) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool { - if ref.target == nil { - if ref.target = scopeFind(ref.parent, rootNs, ref.targetName); ref.target == nil { - kfmt.Fprintf(errWriter, "could not resolve referenced symbol: %s (parent: %s)\n", ref.targetName, ref.parent.Name()) - return false - } - } - - return true -} - -// methodInvocationEntity describes an AML method invocation. -type methodInvocationEntity struct { - unnamedEntity - - methodName string - method *Method -} - -func (m *methodInvocationEntity) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool { - if m.method == nil { - var isMethod bool - if m.method, isMethod = scopeFind(m.parent, rootNs, m.methodName).(*Method); !isMethod { - kfmt.Fprintf(errWriter, "could not resolve merenced method: %s (parent: %s)\n", m.methodName, m.parent.Name()) - return false - } - } - - return true -} - -// Method defines an invocable AML method. -type Method struct { - scopeEntity - - tableHandle uint8 - argCount uint8 - serialized bool - syncLevel uint8 -} - -func (m *Method) getOpcode() opcode { return opMethod } - -// Device defines a device. -type Device struct { - scopeEntity - - tableHandle uint8 - - // The methodMap keeps track of all methods exposed by this device. - methodMap map[string]*Method -} - -func (d *Device) getOpcode() opcode { return opDevice } -func (d *Device) setTableHandle(h uint8) { d.tableHandle = h } - -// TableHandle returns the handle of the ACPI table that defines this device. -func (d *Device) TableHandle() uint8 { return d.tableHandle } - -// mutexEntity represents a named mutex object -type mutexEntity struct { - parent ScopeEntity - - // isGlobal is set to true for the pre-defined global mutex (\_GL object) - isGlobal bool - - name string - syncLevel uint8 - tableHandle uint8 -} - -func (ent *mutexEntity) getOpcode() opcode { return opMutex } -func (ent *mutexEntity) setOpcode(op opcode) {} -func (ent *mutexEntity) Name() string { return ent.name } -func (ent *mutexEntity) Parent() ScopeEntity { return ent.parent } -func (ent *mutexEntity) setParent(parent ScopeEntity) { ent.parent = parent } -func (ent *mutexEntity) getArgs() []interface{} { return nil } -func (ent *mutexEntity) setArg(argIndex uint8, arg interface{}) bool { - var ok bool - switch argIndex { - case 0: - // arg 0 is the mutex name - ent.name, ok = arg.(string) - case 1: - // arg1 is the sync level (bits 0:3) - var syncLevel uint64 - syncLevel, ok = arg.(uint64) - ent.syncLevel = uint8(syncLevel) & 0xf - } - return ok -} -func (ent *mutexEntity) TableHandle() uint8 { return ent.tableHandle } -func (ent *mutexEntity) setTableHandle(h uint8) { ent.tableHandle = h } - -// eventEntity represents a named ACPI sync event. -type eventEntity struct { - namedEntity -} diff --git a/src/gopheros/device/acpi/aml/entity/entity.go b/src/gopheros/device/acpi/aml/entity/entity.go new file mode 100644 index 0000000..6333c89 --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/entity.go @@ -0,0 +1,946 @@ +package entity + +import "gopheros/kernel" + +// Entity is an interface implemented by all AML entities. +type Entity interface { + // Opcode returns the AML op associated with this entity. + Opcode() AMLOpcode + + // Name returns the entity's name or an empty string if no name is + // associated with the entity. + Name() string + + // Parent returns the Container of this entity. + Parent() Container + + // SetParent updates the parent container reference. + SetParent(Container) + + // TableHandle returns the handle of the ACPI table where this entity + // was defined. + TableHandle() uint8 + + // Args returns the argument list for this entity. + Args() []interface{} + + // SetArg adds an argument value at the specified argument index. + SetArg(uint8, interface{}) bool +} + +// Container is an interface that is implemented by entities contain a +// collection of other Entities and define an AML scope. +type Container interface { + Entity + + // Children returns the list of entities that are children of this + // container. + Children() []Entity + + // Append adds an entity to a container. + Append(Entity) bool + + // Remove searches the child list for an entity and removes it if found. + Remove(Entity) + + // Last returns the last entity that was added to this container. + Last() Entity +} + +type FieldAccessTypeProvider interface { + // DefaultAccessType returns the default FieldAccessType for any field unit + // defined by this field. + DefaultAccessType() FieldAccessType +} + +// LazyRefResolver is an interface implemented by entities that contain symbol +// references that are lazily resolved after the full AML entity tree has been +// parsed. +type LazyRefResolver interface { + // ResolveSymbolRefs receives as input the root of the AML entity tree and + // attempts to resolve any symbol references using the scope searching rules + // defined by the ACPI spec. + ResolveSymbolRefs(Container) *kernel.Error +} + +// Generic describes an entity without a name. +type Generic struct { + _ uint8 + tableHandle uint8 + op AMLOpcode + args []interface{} + parent Container +} + +// NewGeneric returns a new generic AML entity. +func NewGeneric(op AMLOpcode, tableHandle uint8) *Generic { + return &Generic{ + op: op, + tableHandle: tableHandle, + } +} + +// Opcode returns the AML op associated with this entity. +func (ent *Generic) Opcode() AMLOpcode { return ent.op } + +// Name returns the entity's name. For this type of entity it always returns +// an empty string. +func (ent *Generic) Name() string { return "" } + +// Parent returns the Container of this entity. +func (ent *Generic) Parent() Container { return ent.parent } + +// SetParent updates the parent container reference. +func (ent *Generic) SetParent(parent Container) { ent.parent = parent } + +// TableHandle returns the handle of the ACPI table where this entity was +// defined. +func (ent *Generic) TableHandle() uint8 { return ent.tableHandle } + +// Args returns the argument list for this entity. +func (ent *Generic) Args() []interface{} { return ent.args } + +// SetArg adds an argument value at the specified argument index. +func (ent *Generic) SetArg(_ uint8, arg interface{}) bool { + ent.args = append(ent.args, arg) + return true +} + +// GenericNamed describes an entity whose name is specified as the argument at +// index zero. +type GenericNamed struct { + Generic + name string +} + +// NewGenericNamed returns a new generic named AML entity. +func NewGenericNamed(op AMLOpcode, tableHandle uint8) *GenericNamed { + return &GenericNamed{ + Generic: Generic{ + op: op, + tableHandle: tableHandle, + }, + } +} + +// Name returns the entity's name. +func (ent *GenericNamed) Name() string { return ent.name } + +// SetArg adds an argument value at the specified argument index. +func (ent *GenericNamed) SetArg(argIndex uint8, arg interface{}) bool { + // arg 0 is the entity name + if argIndex == 0 { + var ok bool + ent.name, ok = arg.(string) + return ok + } + + ent.args = append(ent.args, arg) + return true +} + +// Const is an optionally named entity that contains a constant uint64 or +// string value. +type Const struct { + GenericNamed + Value interface{} +} + +// NewConst creates a new AML constant entity. +func NewConst(op AMLOpcode, tableHandle uint8, initialValue interface{}) *Const { + return &Const{ + GenericNamed: GenericNamed{ + Generic: Generic{ + op: op, + tableHandle: tableHandle, + }, + }, + Value: initialValue, + } +} + +// SetName allows the caller to override the name for a Const entity. +func (ent *Const) SetName(name string) { ent.name = name } + +// SetArg adds an argument value at the specified argument index. +func (ent *Const) SetArg(argIndex uint8, arg interface{}) bool { + // Const entities accept at most one arg + ent.Value = arg + return argIndex == 0 +} + +// Scope is an optionally named entity that groups together multiple entities. +type Scope struct { + GenericNamed + children []Entity +} + +// NewScope creates a new AML named scope entity. +func NewScope(op AMLOpcode, tableHandle uint8, name string) *Scope { + return &Scope{ + GenericNamed: GenericNamed{ + Generic: Generic{ + op: op, + tableHandle: tableHandle, + }, + name: name, + }, + } +} + +// Children returns the list of entities that are children of this container. +func (ent *Scope) Children() []Entity { return ent.children } + +// Append adds an entity to a container. +func (ent *Scope) Append(child Entity) bool { + child.SetParent(ent) + ent.children = append(ent.children, child) + return true +} + +// Remove searches the child list for an entity and removes it if found. +func (ent *Scope) Remove(child Entity) { + for index := 0; index < len(ent.children); index++ { + if ent.children[index] == child { + ent.children = append(ent.children[:index], ent.children[index+1:]...) + return + } + } +} + +// Last returns the last entity that was added to this container. +func (ent *Scope) Last() Entity { return ent.children[len(ent.children)-1] } + +// Buffer defines an AML buffer entity. The entity fields specify a size (arg +// 0) and an optional initializer. +type Buffer struct { + Generic + + size interface{} + data []byte +} + +// NewBuffer creates a new AML buffer entity. +func NewBuffer(tableHandle uint8) *Buffer { + return &Buffer{ + Generic: Generic{ + op: OpBuffer, + tableHandle: tableHandle, + }, + } +} + +// SetArg adds an argument value at the specified argument index. +func (ent *Buffer) SetArg(argIndex uint8, arg interface{}) bool { + switch argIndex { + case 0: // size + ent.size = arg + return true + case 1: // data + if byteSlice, ok := arg.([]byte); ok { + ent.data = byteSlice + return true + } + } + + return false +} + +// BufferField describes a bit/byte/word/dword/qword or arbitrary length +// region within a Buffer. +type BufferField struct { + GenericNamed + + SourceBuf interface{} + Index interface{} + NumBits interface{} +} + +// NewBufferField creates a new AML buffer field entity. +func NewBufferField(op AMLOpcode, tableHandle uint8, bits uint64) *BufferField { + return &BufferField{ + GenericNamed: GenericNamed{ + Generic: Generic{ + op: op, + tableHandle: tableHandle, + }, + }, + NumBits: bits, + } +} + +// SetArg adds an argument value at the specified argument index. +func (ent *BufferField) SetArg(argIndex uint8, arg interface{}) bool { + switch argIndex { + case 0: + ent.SourceBuf = arg + case 1: + ent.Index = arg + case 2, 3: + // opCreateField specifies the name using the arg at index 3 + // while opCreateXXXField (byte, word e.t.c) specifies the name + // using the arg at index 2 + var ok bool + if ent.name, ok = arg.(string); !ok { + ent.NumBits = arg + } + } + return argIndex <= 3 +} + +// RegionSpace describes the memory space where a region is located. +type RegionSpace uint8 + +// The list of supported RegionSpace values. +const ( + RegionSpaceSystemMemory RegionSpace = iota + RegionSpaceSystemIO + RegionSpacePCIConfig + RegionSpaceEmbeddedControl + RegionSpaceSMBus + RegionSpacePCIBarTarget + RegionSpaceIPMI +) + +// Region defines a region located at a particular space (e.g in memory, an +// embedded controller, the SMBus e.t.c). +type Region struct { + GenericNamed + + Space RegionSpace + Offset interface{} + Len interface{} +} + +// NewRegion creates a new AML region entity. +func NewRegion(tableHandle uint8) *Region { + return &Region{ + GenericNamed: GenericNamed{ + Generic: Generic{ + op: OpOpRegion, + tableHandle: tableHandle, + }, + }, + } +} + +// SetArg adds an argument value at the specified argument index. +func (ent *Region) SetArg(argIndex uint8, arg interface{}) bool { + var ok bool + switch argIndex { + case 0: + ok = ent.GenericNamed.SetArg(argIndex, arg) + case 1: + // the parser will convert ByteData types to uint64 + var space uint64 + space, ok = arg.(uint64) + ent.Space = RegionSpace(space) + case 2: + ent.Offset = arg + ok = true + case 3: + ent.Len = arg + ok = true + } + + return ok +} + +// FieldAccessType specifies the type of access (byte, word, e.t.c) used to +// read/write to a field. +type FieldAccessType uint8 + +// The list of supported FieldAccessType values. +const ( + FieldAccessTypeAny FieldAccessType = iota + FieldAccessTypeByte + FieldAccessTypeWord + FieldAccessTypeDword + FieldAccessTypeQword + FieldAccessTypeBuffer +) + +// FieldLockRule specifies what type of locking is required when accesing field. +type FieldLockRule uint8 + +// The list of supported FieldLockRule values. +const ( + FieldLockRuleNoLock FieldLockRule = iota + FieldLockRuleLock +) + +// FieldUpdateRule specifies how a field value is updated when a write uses +// a value with a smaller width than the field. +type FieldUpdateRule uint8 + +// The list of supported FieldUpdateRule values. +const ( + FieldUpdateRulePreserve FieldUpdateRule = iota + FieldUpdateRuleWriteAsOnes + FieldUpdateRuleWriteAsZeros +) + +// FieldAccessAttrib specifies additional information about a particular field +// access. +type FieldAccessAttrib uint8 + +// The list of supported FieldAccessAttrib values. +const ( + FieldAccessAttribQuick FieldAccessAttrib = 0x02 + FieldAccessAttribSendReceive = 0x04 + FieldAccessAttribByte = 0x06 + FieldAccessAttribWord = 0x08 + FieldAccessAttribBlock = 0x0a + FieldAccessAttribBytes = 0x0b // byteCount contains the number of bytes + FieldAccessAttribProcessCall = 0x0c + FieldAccessAttribBlockProcessCall = 0x0d + FieldAccessAttribRawBytes = 0x0e // byteCount contains the number of bytes + FieldAccessAttribRawProcessBytes = 0x0f // byteCount contains the number of bytes +) + +// Field is anobject that controls access to a host operating region. It is +// referenced by a list of FieldUnit objects that appear as siblings of a Field +// in the same scope. +type Field struct { + Generic + + // The region which this field references. + RegionName string + Region *Region + + AccessType FieldAccessType + LockRule FieldLockRule + UpdateRule FieldUpdateRule +} + +// NewField creates a new AML field entity. +func NewField(tableHandle uint8) *Field { + return &Field{ + Generic: Generic{ + op: OpField, + tableHandle: tableHandle, + }, + } +} + +// DefaultAccessType returns the default FieldAccessType for any field unit +// defined by this field. +func (ent *Field) DefaultAccessType() FieldAccessType { + return ent.AccessType +} + +// SetArg adds an argument value at the specified argument index. +func (ent *Field) SetArg(argIndex uint8, arg interface{}) bool { + var ( + ok bool + uintVal uint64 + ) + + switch argIndex { + case 0: + ent.RegionName, ok = arg.(string) + case 1: + uintVal, ok = arg.(uint64) + + ent.AccessType = FieldAccessType(uintVal & 0xf) // access type; bits[0:3] + ent.LockRule = FieldLockRule((uintVal >> 4) & 0x1) // lock; bit 4 + ent.UpdateRule = FieldUpdateRule((uintVal >> 5) & 0x3) // update rule; bits[5:6] + } + + return ok +} + +// IndexField is a special field that groups together two field units so a +// index/data register pattern can be implemented. To write a value to an +// IndexField, the interpreter must first write the appropriate offset to +// the IndexRegister (using the alignment specifid by AccessType) and then +// write the actual value to the DataRegister. +type IndexField struct { + Generic + + IndexRegName string + IndexReg *FieldUnit + + DataRegName string + DataReg *FieldUnit + + AccessType FieldAccessType + LockRule FieldLockRule + UpdateRule FieldUpdateRule +} + +// NewIndexField creates a new AML index field entity. +func NewIndexField(tableHandle uint8) *IndexField { + return &IndexField{ + Generic: Generic{ + op: OpIndexField, + tableHandle: tableHandle, + }, + } +} + +// DefaultAccessType returns the default FieldAccessType for any field unit +// defined by this field. +func (ent *IndexField) DefaultAccessType() FieldAccessType { + return ent.AccessType +} + +// SetArg adds an argument value at the specified argument index. +func (ent *IndexField) SetArg(argIndex uint8, arg interface{}) bool { + var ( + ok bool + uintVal uint64 + ) + + switch argIndex { + case 0: + ent.IndexRegName, ok = arg.(string) + case 1: + ent.DataRegName, ok = arg.(string) + case 2: + uintVal, ok = arg.(uint64) + + ent.AccessType = FieldAccessType(uintVal & 0xf) // access type; bits[0:3] + ent.LockRule = FieldLockRule((uintVal >> 4) & 0x1) // lock; bit 4 + ent.UpdateRule = FieldUpdateRule((uintVal >> 5) & 0x3) // update rule; bits[5:6] + } + return ok +} + +// BankField is a special field where a bank register must be used to select +// the appropriate bank region before accessing its contents. +type BankField struct { + Generic + + // The region which this field references. + RegionName string + Region *Region + + // The bank name which controls access to field units defined within this field. + BankFieldUnitName string + BankFieldUnit *FieldUnit + + // The value that needs to be written to the bank field before accessing any field unit. + BankFieldUnitValue interface{} + + AccessType FieldAccessType + LockRule FieldLockRule + UpdateRule FieldUpdateRule +} + +// NewBankField creates a new AML bank field entity. +func NewBankField(tableHandle uint8) *BankField { + return &BankField{ + Generic: Generic{ + op: OpBankField, + tableHandle: tableHandle, + }, + } +} + +// DefaultAccessType returns the default FieldAccessType for any field unit +// defined by this field. +func (ent *BankField) DefaultAccessType() FieldAccessType { + return ent.AccessType +} + +// SetArg adds an argument value at the specified argument index. +func (ent *BankField) SetArg(argIndex uint8, arg interface{}) bool { + var ( + ok bool + uintVal uint64 + ) + + switch argIndex { + case 0: + ent.RegionName, ok = arg.(string) + case 1: + ent.BankFieldUnitName, ok = arg.(string) + case 2: + ent.BankFieldUnitValue, ok = arg, true + case 3: + uintVal, ok = arg.(uint64) + + ent.AccessType = FieldAccessType(uintVal & 0xf) // access type; bits[0:3] + ent.LockRule = FieldLockRule((uintVal >> 4) & 0x1) // lock; bit 4 + ent.UpdateRule = FieldUpdateRule((uintVal >> 5) & 0x3) // update rule; bits[5:6] + } + return ok +} + +// FieldUnit describes a sub-region inside a parent field. +type FieldUnit struct { + GenericNamed + + // Depending on what field this unit belongs to this will be a pointer + // to one of: Field, BankField, IndexField + Field interface{} + + // The access type to use. Inherited by parent field unless explicitly + // changed via a directive in the field unit definition list. + AccessType FieldAccessType + + // AccessAttrib is valid if AccessType is BufferAcc for the SMB or GPIO OpRegions. + AccessAttrib FieldAccessAttrib + + // ByteCount is valid when AccessAttrib is one of: Bytes, RawBytes or RawProcessBytes + ByteCount uint8 + + // Field offset in parent region and its width. + BitOffset uint32 + BitWidth uint32 + + // The connection resource for field access references (serial bus or GPIO). + ConnectionName string + Connection Entity +} + +// NewFieldUnit creates a new field unit entity. +func NewFieldUnit(tableHandle uint8, name string) *FieldUnit { + return &FieldUnit{ + GenericNamed: GenericNamed{ + Generic: Generic{ + op: OpFieldUnit, + tableHandle: tableHandle, + }, + name: name, + }, + } +} + +// Reference holds a named reference to an AML symbol. The spec allows the +// symbol not to be defined at the time when the reference is parsed. In such a +// case (forward reference) it will be resolved after the entire AML stream has +// successfully been parsed. +type Reference struct { + Generic + + TargetName string + Target Entity +} + +// NewReference creates a new reference to a named entity. +func NewReference(tableHandle uint8, target string) *Reference { + return &Reference{ + Generic: Generic{ + op: OpName, + tableHandle: tableHandle, + }, + TargetName: target, + } +} + +// Method describes an invocable AML method. +type Method struct { + Scope + + ArgCount uint8 + Serialized bool + SyncLevel uint8 +} + +// NewMethod creats a new AML method entity. +func NewMethod(tableHandle uint8, name string) *Method { + return &Method{ + Scope: Scope{ + GenericNamed: GenericNamed{ + Generic: Generic{ + op: OpMethod, + tableHandle: tableHandle, + }, + name: name, + }, + }, + } +} + +// SetArg adds an argument value at the specified argument index. +func (ent *Method) SetArg(argIndex uint8, arg interface{}) bool { + var ( + ok bool + uintVal uint64 + ) + + switch argIndex { + case 0: + // Arg0 is the name but it is actually defined when creating the entity + ok = true + case 1: + // arg1 is the method flags + uintVal, ok = arg.(uint64) + + ent.ArgCount = (uint8(uintVal) & 0x7) // bits[0:2] + ent.Serialized = (uint8(uintVal)>>3)&0x1 == 0x1 // bit 3 + ent.SyncLevel = (uint8(uintVal) >> 4) & 0xf // bits[4:7] + + } + return ok +} + +// Invocation describes an AML method invocation. +type Invocation struct { + Generic + + MethodName string + MethodDef *Method +} + +// NewInvocation creates a new method invocation object. +func NewInvocation(tableHandle uint8, name string, args []interface{}) *Invocation { + return &Invocation{ + Generic: Generic{ + op: OpMethodInvocation, + tableHandle: tableHandle, + args: args, + }, + MethodName: name, + } +} + +// Device defines an AML device entity. +type Device struct { + Scope +} + +// NewDevice creates a new device object. +func NewDevice(tableHandle uint8, name string) *Device { + return &Device{ + Scope: Scope{ + GenericNamed: GenericNamed{ + Generic: Generic{ + op: OpDevice, + tableHandle: tableHandle, + }, + name: name, + }, + }, + } +} + +// Processor describes a AML processor entity. According to the spec, the use +// of processor operators is deprecated and processors should be declared as +// Device entities instead. +type Processor struct { + Scope + + // A unique ID for this processor. + ID uint8 + + // The length of the processor register block. According to the spec, + // this field may be zero. + RegBlockLen uint8 + + // The I/O address of the process register block. + RegBlockAddr uint32 +} + +// NewProcessor creates a new processor object. +func NewProcessor(tableHandle uint8, name string) *Processor { + return &Processor{ + Scope: Scope{ + GenericNamed: GenericNamed{ + Generic: Generic{ + op: OpProcessor, + tableHandle: tableHandle, + }, + name: name, + }, + }, + } +} + +// SetArg adds an argument value at the specified argument index. +func (ent *Processor) SetArg(argIndex uint8, arg interface{}) bool { + var ( + ok bool + uintVal uint64 + ) + + switch argIndex { + case 0: + // Arg0 is the name but it is actually defined when creating the entity + ok = true + case 1: + // arg1 is the processor ID (ByteData) + uintVal, ok = arg.(uint64) + ent.ID = uint8(uintVal) + case 2: + // arg2 is the processor I/O reg block address (Dword) + uintVal, ok = arg.(uint64) + ent.RegBlockAddr = uint32(uintVal) + case 3: + // arg3 is the processor I/O reg block address len (ByteData) + uintVal, ok = arg.(uint64) + ent.RegBlockLen = uint8(uintVal) + } + return ok +} + +// PowerResource describes a AML power resource entity. +type PowerResource struct { + Scope + + // The deepest system sleep level OSPM must maintain to keep this power + // resource on (0 equates to S0, 1 equates to S1, and so on). + SystemLevel uint8 + + // ResourceOrder provides the system with the order in which Power + // Resources must be enabled or disabled. Each unique resourceorder + // value represents a level, and any number of power resources may have + // the same level. Power Resource levels are enabled from low values to + // high values and are disabled from high values to low values. + ResourceOrder uint16 +} + +// NewPowerResource creates a new power resource object. +func NewPowerResource(tableHandle uint8, name string) *PowerResource { + return &PowerResource{ + Scope: Scope{ + GenericNamed: GenericNamed{ + Generic: Generic{ + op: OpPowerRes, + tableHandle: tableHandle, + }, + name: name, + }, + }, + } +} + +// SetArg adds an argument value at the specified argument index. +func (ent *PowerResource) SetArg(argIndex uint8, arg interface{}) bool { + var ( + ok bool + uintVal uint64 + ) + + switch argIndex { + case 0: + // Arg0 is the name but it is actually defined when creating the entity + ok = true + case 1: + // arg1 is the system level (ByteData) + uintVal, ok = arg.(uint64) + ent.SystemLevel = uint8(uintVal) + case 2: + // arg2 is the resource order (WordData) + uintVal, ok = arg.(uint64) + ent.ResourceOrder = uint16(uintVal) + } + return ok +} + +// ThermalZone describes a AML thermal zone entity. +type ThermalZone struct { + Scope +} + +// NewThermalZone creates a new thermal zone object. +func NewThermalZone(tableHandle uint8, name string) *ThermalZone { + return &ThermalZone{ + Scope: Scope{ + GenericNamed: GenericNamed{ + Generic: Generic{ + op: OpThermalZone, + tableHandle: tableHandle, + }, + name: name, + }, + }, + } +} + +// Mutex describes a AML mutex entity. +type Mutex struct { + GenericNamed + + // IsGlobal is set to true for the pre-defined global mutex (\_GL object) + IsGlobal bool + + SyncLevel uint8 +} + +// NewMutex creates a new mutex object. +func NewMutex(tableHandle uint8) *Mutex { + return &Mutex{ + GenericNamed: GenericNamed{ + Generic: Generic{ + op: OpMutex, + tableHandle: tableHandle, + }, + }, + } +} + +// SetArg adds an argument value at the specified argument index. +func (ent *Mutex) SetArg(argIndex uint8, arg interface{}) bool { + var ok bool + switch argIndex { + case 0: + // arg 0 is the mutex name + ent.name, ok = arg.(string) + case 1: + // arg1 is the sync level (bits 0:3) + var syncLevel uint64 + syncLevel, ok = arg.(uint64) + ent.SyncLevel = uint8(syncLevel) & 0xf + } + return ok +} + +// Event represents a named ACPI sync event. +type Event struct { + GenericNamed +} + +// NewEvent creates a new event object. +func NewEvent(tableHandle uint8) *Event { + return &Event{ + GenericNamed: GenericNamed{ + Generic: Generic{ + op: OpEvent, + tableHandle: tableHandle, + }, + }, + } +} + +// Package is an entity that contains one of the following entity types: +// - constant data objects (int, string, buffer or package) +// - named references to data objects (int, string, buffer, buffer field, +// field unit or package) +// - named references to non-data objects (device, event, method, mutex, region +// power resource, processor or thermal zone) +type Package struct { + Generic + + // The number of elements in the package. In most cases, the package + // length is known at compile-time and will be emitted as a const + // value. However, the standard also allows dynamic definition of + // package elements (e.g. inside a method). In the latter case (or if + // the package contains more that 255 elements) this will be a + // expression that the VM needs to evaluate as an integer value. + NumElements interface{} +} + +// NewPackage creates a new package entity with the OpPackage or the +// OpVarPackage opcodes. +func NewPackage(op AMLOpcode, tableHandle uint8) *Package { + return &Package{ + Generic: Generic{ + op: op, + tableHandle: tableHandle, + }, + } +} + +// SetArg adds an argument value at the specified argument index. +func (ent *Package) SetArg(argIndex uint8, arg interface{}) bool { + // Package entities define the number of elements as the first arg. + if argIndex == 0 { + ent.NumElements = arg + return true + } + + return ent.Generic.SetArg(argIndex, arg) +} diff --git a/src/gopheros/device/acpi/aml/entity/entity_test.go b/src/gopheros/device/acpi/aml/entity/entity_test.go new file mode 100644 index 0000000..692f419 --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/entity_test.go @@ -0,0 +1,185 @@ +package entity + +import ( + "reflect" + "testing" +) + +func TestEntityMethods(t *testing.T) { + namedConst := NewConst(OpDwordPrefix, 42, "foo") + namedConst.SetName("TAG0") + + specs := []struct { + ent Entity + expOp AMLOpcode + expName string + }{ + {NewGeneric(OpNoop, 42), OpNoop, ""}, + {NewGenericNamed(OpAcquire, 42), OpAcquire, ""}, + {namedConst, OpDwordPrefix, "TAG0"}, + {NewScope(OpScope, 42, "_SB_"), OpScope, "_SB_"}, + {NewBuffer(42), OpBuffer, ""}, + {NewBufferField(OpCreateByteField, 42, 8), OpCreateByteField, ""}, + {NewField(42), OpField, ""}, + {NewIndexField(42), OpIndexField, ""}, + {NewBankField(42), OpBankField, ""}, + {NewReference(42, "TRG0"), OpName, ""}, + {NewMethod(42, "FOO0"), OpMethod, "FOO0"}, + {NewInvocation(42, "MTH0", nil), OpMethodInvocation, ""}, + {NewMutex(42), OpMutex, ""}, + {NewDevice(42, "DEV0"), OpDevice, "DEV0"}, + {NewProcessor(42, "CPU0"), OpProcessor, "CPU0"}, + {NewPowerResource(42, "POW0"), OpPowerRes, "POW0"}, + {NewThermalZone(42, "THE0"), OpThermalZone, "THE0"}, + {NewEvent(42), OpEvent, ""}, + {NewRegion(42), OpOpRegion, ""}, + {NewFieldUnit(42, "FOO0"), OpFieldUnit, "FOO0"}, + {NewPackage(OpPackage, 42), OpPackage, ""}, + } + + t.Run("opcode and name getter", func(t *testing.T) { + for specIndex, spec := range specs { + if got := spec.ent.Opcode(); got != spec.expOp { + t.Errorf("[spec %d] expected to get back opcode %d; got %d", specIndex, spec.expOp, got) + } + + if got := spec.ent.Name(); got != spec.expName { + t.Errorf("[spec %d] expected to get name: %q; got %q", specIndex, spec.expName, got) + } + } + }) + + t.Run("table handle getter", func(t *testing.T) { + exp := uint8(42) + for specIndex, spec := range specs { + if got := spec.ent.TableHandle(); got != exp { + t.Errorf("[spec %d] expected to get back handle %d; got %d", specIndex, exp, got) + } + } + }) + + t.Run("append/remove/get parent methods", func(t *testing.T) { + parent := NewScope(OpScope, 2, "_SB_") + parent.name = `\` + + for specIndex, spec := range specs { + parent.Append(spec.ent) + if got := spec.ent.Parent(); got != parent { + t.Errorf("[spec %d] expected to get back parent %v; got %v", specIndex, parent, got) + } + + if got := parent.Last(); got != spec.ent { + t.Errorf("[spec %d] expected parent's last entity to be the one just appended", specIndex) + } + + parent.Remove(spec.ent) + } + + if got := len(parent.Children()); got != 0 { + t.Fatalf("expected parent not to have any child nodes; got %d", got) + } + }) +} + +func TestEntityArgAssignment(t *testing.T) { + specs := []struct { + ent Entity + argList []interface{} + expArgList []interface{} + limitedArgs bool + }{ + { + NewGeneric(1, 2), + []interface{}{"foo", 1, "bar"}, + []interface{}{"foo", 1, "bar"}, + false, + }, + { + NewGenericNamed(1, 2), + []interface{}{"foo", 1, "bar"}, + []interface{}{1, "bar"}, // GenericNamed uses arg0 as the name + false, + }, + { + NewConst(1, 2, 3), + []interface{}{"foo"}, + nil, // Const populates its internal state using the arg 0 + true, + }, + { + NewBuffer(2), + []interface{}{1, []byte{}}, + nil, // Buffer populates its internal state using the first 2 args + true, + }, + { + NewBufferField(OpCreateDWordField, 2, 32), + []interface{}{"a", "b", "c"}, + nil, // Buffer populates its internal state using the first 3 args (opCreateDwordField) + false, + }, + { + NewBufferField(1, 2, 0), + []interface{}{"a", "b", 10, "c"}, + nil, // Buffer populates its internal state using the first 4 args (opCreateField) + true, + }, + { + NewRegion(2), + []interface{}{"REG0", uint64(0x4), 0, 10}, + nil, // Region populates its internal state using the first 4 args + true, + }, + { + NewMutex(2), + []interface{}{"MUT0", uint64(1)}, + nil, // Mutex populates its internal state using the first 2 args + true, + }, + { + NewProcessor(2, "CPU0"), + []interface{}{uint64(1), uint64(0xdeadc0de), uint64(0)}, + nil, // Processor populates its internal state using the first 3 args + true, + }, + { + NewPowerResource(2, "POW0"), + []interface{}{uint64(2), uint64(1)}, + nil, // PowerResource populates its internal state using the first 2 args + true, + }, + { + NewMethod(2, "MTH0"), + []interface{}{"arg0 ignored", uint64(0x42)}, + nil, // Method populates its internal state using the first 2 args + true, + }, + { + NewPackage(OpPackage, 2), + []interface{}{uint64(1), NewConst(OpDwordPrefix, 2, uint64(42))}, + []interface{}{NewConst(OpDwordPrefix, 2, uint64(42))}, + false, + }, + } + +nextSpec: + for specIndex, spec := range specs { + for i, arg := range spec.argList { + if !spec.ent.SetArg(uint8(i), arg) { + t.Errorf("[spec %d] error setting arg %d", specIndex, i) + continue nextSpec + } + } + + if spec.limitedArgs { + if spec.ent.SetArg(uint8(len(spec.argList)), nil) { + t.Errorf("[spec %d] expected additional calls to setArg to return false", specIndex) + continue nextSpec + } + } + + if got := spec.ent.Args(); !reflect.DeepEqual(got, spec.expArgList) { + t.Errorf("[spec %d] expected to get back arg list %v; got %v", specIndex, spec.expArgList, got) + } + } +} diff --git a/src/gopheros/device/acpi/aml/entity/opcode.go b/src/gopheros/device/acpi/aml/entity/opcode.go index 7c0bf67..a9c608c 100644 --- a/src/gopheros/device/acpi/aml/entity/opcode.go +++ b/src/gopheros/device/acpi/aml/entity/opcode.go @@ -122,8 +122,246 @@ const ( OpIndexField = AMLOpcode(0xff + 0x86) OpBankField = AMLOpcode(0xff + 0x87) OpDataRegion = AMLOpcode(0xff + 0x88) + // Special internal opcodes which are not part of the spec; these are + // for internal use by the AML interpreter. + OpFieldUnit = AMLOpcode(0xff + 0xfd) + OpMethodInvocation = AMLOpcode(0xff + 0xfe) ) +// String implements fmt.Stringer for the AMLOpcode type. +func (op AMLOpcode) String() string { + switch op { + case OpZero: + return "Zero" + case OpOne: + return "One" + case OpAlias: + return "Alias" + case OpName: + return "Name" + case OpBytePrefix: + return "Byte" + case OpWordPrefix: + return "Word" + case OpDwordPrefix: + return "Dword" + case OpStringPrefix: + return "String" + case OpQwordPrefix: + return "Qword" + case OpScope: + return "Scope" + case OpBuffer: + return "Buffer" + case OpPackage: + return "Package" + case OpVarPackage: + return "VarPackage" + case OpMethod: + return "Method" + case OpExternal: + return "External" + case OpLocal0: + return "Local0" + case OpLocal1: + return "Local1" + case OpLocal2: + return "Local2" + case OpLocal3: + return "Local3" + case OpLocal4: + return "Local4" + case OpLocal5: + return "Local5" + case OpLocal6: + return "Local6" + case OpLocal7: + return "Local7" + case OpArg0: + return "Arg0" + case OpArg1: + return "Arg1" + case OpArg2: + return "Arg2" + case OpArg3: + return "Arg3" + case OpArg4: + return "Arg4" + case OpArg5: + return "Arg5" + case OpArg6: + return "Arg6" + case OpStore: + return "Store" + case OpRefOf: + return "RefOf" + case OpAdd: + return "Add" + case OpConcat: + return "Concat" + case OpSubtract: + return "Subtract" + case OpIncrement: + return "Increment" + case OpDecrement: + return "Decrement" + case OpMultiply: + return "Multiply" + case OpDivide: + return "Divide" + case OpShiftLeft: + return "ShiftLeft" + case OpShiftRight: + return "ShiftRight" + case OpAnd: + return "And" + case OpNand: + return "Nand" + case OpOr: + return "Or" + case OpNor: + return "Nor" + case OpXor: + return "Xor" + case OpNot: + return "Not" + case OpFindSetLeftBit: + return "FindSetLeftBit" + case OpFindSetRightBit: + return "FindSetRightBit" + case OpDerefOf: + return "DerefOf" + case OpConcatRes: + return "ConcatRes" + case OpMod: + return "Mod" + case OpNotify: + return "Notify" + case OpSizeOf: + return "SizeOf" + case OpIndex: + return "Index" + case OpMatch: + return "Match" + case OpCreateDWordField: + return "CreateDWordField" + case OpCreateWordField: + return "CreateWordField" + case OpCreateByteField: + return "CreateByteField" + case OpCreateBitField: + return "CreateBitField" + case OpObjectType: + return "ObjectType" + case OpCreateQWordField: + return "CreateQWordField" + case OpLand: + return "Land" + case OpLor: + return "Lor" + case OpLnot: + return "Lnot" + case OpLEqual: + return "LEqual" + case OpLGreater: + return "LGreater" + case OpLLess: + return "LLess" + case OpToBuffer: + return "ToBuffer" + case OpToDecimalString: + return "ToDecimalString" + case OpToHexString: + return "ToHexString" + case OpToInteger: + return "ToInteger" + case OpToString: + return "ToString" + case OpCopyObject: + return "CopyObject" + case OpMid: + return "Mid" + case OpContinue: + return "Continue" + case OpIf: + return "If" + case OpElse: + return "Else" + case OpWhile: + return "While" + case OpNoop: + return "Noop" + case OpReturn: + return "Return" + case OpBreak: + return "Break" + case OpBreakPoint: + return "BreakPoint" + case OpOnes: + return "Ones" + case OpMutex: + return "Mutex" + case OpEvent: + return "Event" + case OpCondRefOf: + return "CondRefOf" + case OpCreateField: + return "CreateField" + case OpLoadTable: + return "LoadTable" + case OpLoad: + return "Load" + case OpStall: + return "Stall" + case OpSleep: + return "Sleep" + case OpAcquire: + return "Acquire" + case OpSignal: + return "Signal" + case OpWait: + return "Wait" + case OpReset: + return "Reset" + case OpRelease: + return "Release" + case OpFromBCD: + return "FromBCD" + case OpToBCD: + return "ToBCD" + case OpUnload: + return "Unload" + case OpRevision: + return "Revision" + case OpDebug: + return "Debug" + case OpFatal: + return "Fatal" + case OpTimer: + return "Timer" + case OpOpRegion: + return "OpRegion" + case OpField: + return "Field" + case OpDevice: + return "Device" + case OpProcessor: + return "Processor" + case OpPowerRes: + return "PowerRes" + case OpThermalZone: + return "ThermalZone" + case OpIndexField: + return "IndexField" + case OpBankField: + return "BankField" + case OpDataRegion: + return "DataRegion" + default: + return "unknown" + } +} + // OpIsLocalArg returns true if this opcode represents any of the supported local // function args 0 to 7. func OpIsLocalArg(op AMLOpcode) bool { diff --git a/src/gopheros/device/acpi/aml/entity/opcode_test.go b/src/gopheros/device/acpi/aml/entity/opcode_test.go index 3cf0e4c..6f89051 100644 --- a/src/gopheros/device/acpi/aml/entity/opcode_test.go +++ b/src/gopheros/device/acpi/aml/entity/opcode_test.go @@ -2,6 +2,135 @@ package entity import "testing" +func TestOpcodeToString(t *testing.T) { + opcodeList := []AMLOpcode{ + OpZero, + OpOne, + OpAlias, + OpName, + OpBytePrefix, + OpWordPrefix, + OpDwordPrefix, + OpStringPrefix, + OpQwordPrefix, + OpScope, + OpBuffer, + OpPackage, + OpVarPackage, + OpMethod, + OpExternal, + OpLocal0, + OpLocal1, + OpLocal2, + OpLocal3, + OpLocal4, + OpLocal5, + OpLocal6, + OpLocal7, + OpArg0, + OpArg1, + OpArg2, + OpArg3, + OpArg4, + OpArg5, + OpArg6, + OpStore, + OpRefOf, + OpAdd, + OpConcat, + OpSubtract, + OpIncrement, + OpDecrement, + OpMultiply, + OpDivide, + OpShiftLeft, + OpShiftRight, + OpAnd, + OpNand, + OpOr, + OpNor, + OpXor, + OpNot, + OpFindSetLeftBit, + OpFindSetRightBit, + OpDerefOf, + OpConcatRes, + OpMod, + OpNotify, + OpSizeOf, + OpIndex, + OpMatch, + OpCreateDWordField, + OpCreateWordField, + OpCreateByteField, + OpCreateBitField, + OpObjectType, + OpCreateQWordField, + OpLand, + OpLor, + OpLnot, + OpLEqual, + OpLGreater, + OpLLess, + OpToBuffer, + OpToDecimalString, + OpToHexString, + OpToInteger, + OpToString, + OpCopyObject, + OpMid, + OpContinue, + OpIf, + OpElse, + OpWhile, + OpNoop, + OpReturn, + OpBreak, + OpBreakPoint, + OpOnes, + OpMutex, + OpEvent, + OpCondRefOf, + OpCreateField, + OpLoadTable, + OpLoad, + OpStall, + OpSleep, + OpAcquire, + OpSignal, + OpWait, + OpReset, + OpRelease, + OpFromBCD, + OpToBCD, + OpUnload, + OpRevision, + OpDebug, + OpFatal, + OpTimer, + OpOpRegion, + OpField, + OpDevice, + OpProcessor, + OpPowerRes, + OpThermalZone, + OpIndexField, + OpBankField, + OpDataRegion, + } + + for specIndex, op := range opcodeList { + if op.String() == "unknown" { + t.Errorf("[spec %d] op 0x%x String() returned \"unknown\"", specIndex, op) + } + } + + // Also test invalid opcode + if got := AMLOpcode(0xffff).String(); got != "unknown" { + t.Fatalf("expected String() for invalid opcode to return \"unknown\"; got: %q", got) + } +} + func TestOpcodeIsX(t *testing.T) { specs := []struct { op AMLOpcode diff --git a/src/gopheros/device/acpi/aml/entity_test.go b/src/gopheros/device/acpi/aml/entity_test.go deleted file mode 100644 index 97ea402..0000000 --- a/src/gopheros/device/acpi/aml/entity_test.go +++ /dev/null @@ -1,173 +0,0 @@ -package aml - -import ( - "io/ioutil" - "reflect" - "testing" -) - -func TestEntityMethods(t *testing.T) { - specs := []Entity{ - &unnamedEntity{}, - &constEntity{}, - &scopeEntity{}, - &bufferEntity{}, - &fieldUnitEntity{}, - &indexFieldEntity{}, - &namedReference{}, - &methodInvocationEntity{}, - &Method{}, - &Device{}, - &mutexEntity{}, - &eventEntity{}, - } - - t.Run("table handle methods", func(t *testing.T) { - exp := uint8(42) - for specIndex, spec := range specs { - spec.setTableHandle(exp) - if got := spec.TableHandle(); got != exp { - t.Errorf("[spec %d] expected to get back handle %d; got %d", specIndex, exp, got) - } - } - }) - - t.Run("append/remove/get parent methods", func(t *testing.T) { - parent := &scopeEntity{name: `\`} - - for specIndex, spec := range specs { - parent.Append(spec) - if got := spec.Parent(); got != parent { - t.Errorf("[spec %d] expected to get back parent %v; got %v", specIndex, parent, got) - } - - parent.removeChild(spec) - } - - if got := len(parent.Children()); got != 0 { - t.Fatalf("expected parent not to have any child nodes; got %d", got) - } - }) -} - -func TestEntityArgAssignment(t *testing.T) { - specs := []struct { - ent Entity - argList []interface{} - expArgList []interface{} - limitedArgs bool - }{ - { - &unnamedEntity{}, - []interface{}{"foo", 1, "bar"}, - []interface{}{"foo", 1, "bar"}, - false, - }, - { - &constEntity{}, - []interface{}{"foo"}, - nil, // constEntity populates its internal state using the 1st arg - true, - }, - { - &scopeEntity{}, - []interface{}{"foo", 1, 2, 3}, - []interface{}{1, 2, 3}, // scopeEntity will treat arg0 as the scope name if it is a string - false, - }, - { - &bufferEntity{}, - []interface{}{1, []byte{}}, - nil, // bufferEntity populates its internal state using the first 2 args - true, - }, - { - ®ionEntity{}, - []interface{}{"REG0", uint64(0x4), 0, 10}, - []interface{}{0, 10}, // region populates its internal state using the first 2 args - true, - }, - { - &mutexEntity{}, - []interface{}{"MUT0", uint64(1)}, - nil, // mutexEntity populates its internal state using the first 2 args - true, - }, - } - -nextSpec: - for specIndex, spec := range specs { - for i, arg := range spec.argList { - if !spec.ent.setArg(uint8(i), arg) { - t.Errorf("[spec %d] error setting arg %d", specIndex, i) - continue nextSpec - } - } - - if spec.limitedArgs { - if spec.ent.setArg(uint8(len(spec.argList)), nil) { - t.Errorf("[spec %d] expected additional calls to setArg to return false", specIndex) - continue nextSpec - } - } - - if got := spec.ent.getArgs(); !reflect.DeepEqual(got, spec.expArgList) { - t.Errorf("[spec %d] expected to get back arg list %v; got %v", specIndex, spec.expArgList, got) - } - } -} - -func TestEntityResolveErrors(t *testing.T) { - scope := &scopeEntity{name: `\`} - - specs := []resolver{ - // Unknown connection entity - &fieldUnitEntity{connectionName: "CON0"}, - // Unknown region - &fieldUnitEntity{connectionName: `\`, regionName: "REG0"}, - // Unknown connection entity - &indexFieldEntity{connectionName: "CON0"}, - // Unknown index register - &indexFieldEntity{connectionName: `\`, indexRegName: "IND0"}, - // Unknown data register - &indexFieldEntity{connectionName: `\`, indexRegName: `\`, dataRegName: "DAT0"}, - // Unknown reference - &namedReference{unnamedEntity: unnamedEntity{parent: scope}, targetName: "TRG0"}, - // Unknown method name - &methodInvocationEntity{unnamedEntity: unnamedEntity{parent: scope}, methodName: "MTH0"}, - } - - for specIndex, spec := range specs { - if spec.Resolve(ioutil.Discard, scope) { - t.Errorf("[spec %d] expected Resolve() to fail", specIndex) - } - } -} - -func TestMethodInvocationResolver(t *testing.T) { - scope := &scopeEntity{name: `\`} - scope.Append(&Method{ - scopeEntity: scopeEntity{ - name: "MTH0", - }, - }) - - validInv := &methodInvocationEntity{ - methodName: "MTH0", - } - - invalidInv := &methodInvocationEntity{ - methodName: "FOO0", - } - - scope.Append(validInv) - scope.Append(invalidInv) - - if !validInv.Resolve(ioutil.Discard, scope) { - t.Fatal("expected method invocation to resolve method", validInv.methodName) - } - - if invalidInv.Resolve(ioutil.Discard, scope) { - t.Fatal("expected method invocation NOT to resolve method", invalidInv.methodName) - } -} From 38143ab51047730cf200c0c8da35d3f5c8e15490 Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Fri, 29 Dec 2017 09:10:16 +0000 Subject: [PATCH 3/9] acpi: refactor scope lookup code and move it into the entity pkg --- .../device/acpi/aml/{ => entity}/scope.go | 89 +----- .../device/acpi/aml/entity/scope_test.go | 215 +++++++++++++ src/gopheros/device/acpi/aml/scope_test.go | 294 ------------------ 3 files changed, 230 insertions(+), 368 deletions(-) rename src/gopheros/device/acpi/aml/{ => entity}/scope.go (54%) create mode 100644 src/gopheros/device/acpi/aml/entity/scope_test.go delete mode 100644 src/gopheros/device/acpi/aml/scope_test.go diff --git a/src/gopheros/device/acpi/aml/scope.go b/src/gopheros/device/acpi/aml/entity/scope.go similarity index 54% rename from src/gopheros/device/acpi/aml/scope.go rename to src/gopheros/device/acpi/aml/entity/scope.go index 4057fea..8537950 100644 --- a/src/gopheros/device/acpi/aml/scope.go +++ b/src/gopheros/device/acpi/aml/entity/scope.go @@ -1,68 +1,14 @@ -package aml +package entity import "strings" -// Visitor is a function invoked by the VM for each AML tree entity that matches -// a particular type. The return value controls whether the children of this -// entity should also be visited. -type Visitor func(depth int, obj Entity) (keepRecursing bool) - -// EntityType defines the type of entity that visitors should inspect. -type EntityType uint8 - -// The list of supported EntityType values. EntityTypeAny works as a wildcard -// allowing the visitor to inspect all entities in the AML tree. -const ( - EntityTypeAny EntityType = iota - EntityTypeDevice - EntityTypeProcessor - EntityTypePowerResource - EntityTypeThermalZone - EntityTypeMethod -) - -// scopeVisit descends a scope hierarchy and invokes visitorFn for each entity -// that matches entType. -func scopeVisit(depth int, ent Entity, entType EntityType, visitorFn Visitor) bool { - op := ent.getOpcode() - switch { - case (entType == EntityTypeAny) || - (entType == EntityTypeDevice && op == opDevice) || - (entType == EntityTypeProcessor && op == opProcessor) || - (entType == EntityTypePowerResource && op == opPowerRes) || - (entType == EntityTypeThermalZone && op == opThermalZone) || - (entType == EntityTypeMethod && op == opMethod): - // If the visitor returned false we should not visit the children - if !visitorFn(depth, ent) { - return false - } - - // Visit any args that are also entities - for _, arg := range ent.getArgs() { - if argEnt, isEnt := arg.(Entity); isEnt && !scopeVisit(depth+1, argEnt, entType, visitorFn) { - return false - } - } - } - - switch typ := ent.(type) { - case ScopeEntity: - // If the entity defines a scope we need to visit the child entities. - for _, child := range typ.Children() { - _ = scopeVisit(depth+1, child, entType, visitorFn) - } - } - - return true -} - -// scopeResolvePath examines a path expression and attempts to break it down +// ResolveScopedPath examines a path expression and attempts to break it down // into a parent and child segment. The parent segment is looked up via the // regular scope rules specified in page 252 of the ACPI 6.2 spec. If the // parent scope is found then the function returns back the parent entity and // the name of the child that should be appended to it. If the expression // lookup fails then the function returns nil, "". -func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent ScopeEntity, name string) { +func ResolveScopedPath(curScope, rootScope Container, expr string) (parent Container, name string) { if len(expr) <= 1 { return nil, "" } @@ -75,8 +21,8 @@ func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent Scop return rootScope, expr[1:] case '^': lastHatIndex := strings.LastIndexByte(expr, '^') - if target := scopeFind(curScope, rootScope, expr[:lastHatIndex+1]); target != nil { - return target.(ScopeEntity), expr[lastHatIndex+1:] + if target := FindInScope(curScope, rootScope, expr[:lastHatIndex+1]); target != nil { + return target.(Container), expr[lastHatIndex+1:] } return nil, "" @@ -86,14 +32,14 @@ func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent Scop } // Pattern looks like: \FOO.BAR.BAZ or ^+FOO.BAR.BAZ or FOO.BAR.BAZ - if target := scopeFind(curScope, rootScope, expr[:lastDotIndex]); target != nil { - return target.(ScopeEntity), expr[lastDotIndex+1:] + if target := FindInScope(curScope, rootScope, expr[:lastDotIndex]); target != nil { + return target.(Container), expr[lastDotIndex+1:] } return nil, "" } -// scopeFind attempts to find an object with the given name using the rules +// FindInScope attempts to find an object with the given name using the rules // specified in page 252 of the ACPI 6.2 spec: // // There are two types of namespace paths: an absolute namespace path (that is, @@ -104,7 +50,7 @@ func scopeResolvePath(curScope, rootScope ScopeEntity, expr string) (parent Scop // or Parent Prefixes, ‘^’, the search rules do not apply. If the search rules // do not apply to a relative namespace path, the namespace object is looked up // relative to the current namespace -func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { +func FindInScope(curScope, rootScope Container, name string) Entity { nameLen := len(name) if nameLen == 0 { return nil @@ -113,7 +59,7 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { switch { case name[0] == '\\': // relative to the root scope if nameLen > 1 { - return scopeFindRelative(rootScope, name[1:]) + return findRelativeToScope(rootScope, name[1:]) } // Name was just `\`; this matches the root namespace @@ -130,7 +76,7 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { } default: // Found the start of the name. Look it up relative to curNs - return scopeFindRelative(curScope, name[startIndex:]) + return findRelativeToScope(curScope, name[startIndex:]) } } @@ -139,7 +85,7 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { case strings.ContainsRune(name, '.'): // If the name contains any '.' then we still need to look it // up relative to the current scope - return scopeFindRelative(curScope, name) + return findRelativeToScope(curScope, name) default: // We can apply the search rules described by the spec for s := curScope; s != nil; s = s.Parent() { @@ -155,11 +101,11 @@ func scopeFind(curScope, rootScope ScopeEntity, name string) Entity { return nil } -// scopeFindRelative returns the Entity referenced by path relative +// findRelativeToScope returns the Entity referenced by path relative // to the provided Namespace. If the name contains dots, each segment // is used to access a nested namespace. If the path does not point // to a NamedObject then lookupRelativeTo returns back nil. -func scopeFindRelative(ns ScopeEntity, path string) Entity { +func findRelativeToScope(ns Container, path string) Entity { var matchName string matchNextPathSegment: for { @@ -170,12 +116,7 @@ matchNextPathSegment: // Search for a scoped child named "matchName" for _, child := range ns.Children() { - childNs, ok := child.(ScopeEntity) - if !ok { - continue - } - - if childNs.Name() == matchName { + if childNs, ok := child.(Container); ok && childNs.Name() == matchName { ns = childNs continue matchNextPathSegment } diff --git a/src/gopheros/device/acpi/aml/entity/scope_test.go b/src/gopheros/device/acpi/aml/entity/scope_test.go new file mode 100644 index 0000000..5d22117 --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/scope_test.go @@ -0,0 +1,215 @@ +package entity + +import ( + "reflect" + "testing" +) + +func TestResolveScopedPath(t *testing.T) { + scopeMap := genTestScopes() + + specs := []struct { + curScope Container + pathExpr string + wantParent Entity + wantName string + }{ + { + scopeMap["IDE0"].(Container), + `\_SB_`, + scopeMap[`\`], + "_SB_", + }, + { + scopeMap["IDE0"].(Container), + `^FOO`, + scopeMap[`PCI0`], + "FOO", + }, + { + scopeMap["IDE0"].(Container), + `^^FOO`, + scopeMap[`_SB_`], + "FOO", + }, + { + scopeMap["IDE0"].(Container), + `_ADR`, + scopeMap[`IDE0`], + "_ADR", + }, + // Paths with dots + { + scopeMap["IDE0"].(Container), + `\_SB_.PCI0.IDE0._ADR`, + scopeMap[`IDE0`], + "_ADR", + }, + { + scopeMap["PCI0"].(Container), + `IDE0._ADR`, + scopeMap[`IDE0`], + "_ADR", + }, + { + scopeMap["PCI0"].(Container), + `_CRS`, + scopeMap[`PCI0`], + "_CRS", + }, + // Bad queries + { + scopeMap["PCI0"].(Container), + `FOO.BAR.BAZ`, + nil, + "", + }, + { + scopeMap["PCI0"].(Container), + ``, + nil, + "", + }, + { + scopeMap["PCI0"].(Container), + `\`, + nil, + "", + }, + { + scopeMap["PCI0"].(Container), + `^^^^^^^^^BADPATH`, + nil, + "", + }, + } + + root := scopeMap[`\`].(Container) + for specIndex, spec := range specs { + gotParent, gotName := ResolveScopedPath(spec.curScope, root, spec.pathExpr) + if !reflect.DeepEqual(gotParent, spec.wantParent) { + t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.wantParent, gotParent) + continue + } + + if gotName != spec.wantName { + t.Errorf("[spec %d] expected lookup to return node name %q; got %q", specIndex, spec.wantName, gotName) + } + } +} + +func TestFindInScope(t *testing.T) { + scopeMap := genTestScopes() + + specs := []struct { + curScope Container + lookup string + want Entity + }{ + // Search rules do not apply for these cases + { + scopeMap["PCI0"].(Container), + `\`, + scopeMap[`\`], + }, + { + scopeMap["PCI0"].(Container), + "IDE0._ADR", + scopeMap["_ADR"], + }, + { + scopeMap["IDE0"].(Container), + "^^PCI0.IDE0._ADR", + scopeMap["_ADR"], + }, + { + scopeMap["IDE0"].(Container), + `\_SB_.PCI0.IDE0._ADR`, + scopeMap["_ADR"], + }, + { + scopeMap["IDE0"].(Container), + `\_SB_.PCI0`, + scopeMap["PCI0"], + }, + { + scopeMap["IDE0"].(Container), + `^`, + scopeMap["PCI0"], + }, + // Bad queries + { + scopeMap["_SB_"].(Container), + "PCI0.USB._CRS", + nil, + }, + { + scopeMap["IDE0"].(Container), + "^^^^^^^^^^^^^^^^^^^", + nil, + }, + { + scopeMap["IDE0"].(Container), + `^^^^^^^^^^^FOO`, + nil, + }, + { + scopeMap["IDE0"].(Container), + "FOO", + nil, + }, + { + scopeMap["IDE0"].(Container), + "", + nil, + }, + // Search rules apply for these cases + { + scopeMap["IDE0"].(Container), + "_CRS", + scopeMap["_CRS"], + }, + } + + root := scopeMap[`\`].(Container) + for specIndex, spec := range specs { + if got := FindInScope(spec.curScope, root, spec.lookup); !reflect.DeepEqual(got, spec.want) { + t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.want, got) + } + } +} + +func genTestScopes() map[string]Entity { + // Setup the example tree from page 252 of the acpi 6.2 spec + // \ + // SB + // \ + // PCI0 + // | _CRS + // \ + // IDE0 + // | _ADR + ideScope := NewScope(OpScope, 42, `IDE0`) + pciScope := NewScope(OpScope, 42, `PCI0`) + sbScope := NewScope(OpScope, 42, `_SB_`) + rootScope := NewScope(OpScope, 42, `\`) + + adr := NewMethod(42, `_ADR`) + crs := NewMethod(42, `_CRS`) + + // Setup tree + ideScope.Append(adr) + pciScope.Append(crs) + pciScope.Append(ideScope) + sbScope.Append(pciScope) + rootScope.Append(sbScope) + + return map[string]Entity{ + "IDE0": ideScope, + "PCI0": pciScope, + "_SB_": sbScope, + "\\": rootScope, + "_ADR": adr, + "_CRS": crs, + } +} diff --git a/src/gopheros/device/acpi/aml/scope_test.go b/src/gopheros/device/acpi/aml/scope_test.go deleted file mode 100644 index fd2dc85..0000000 --- a/src/gopheros/device/acpi/aml/scope_test.go +++ /dev/null @@ -1,294 +0,0 @@ -package aml - -import ( - "reflect" - "testing" -) - -func TestScopeVisit(t *testing.T) { - scopeMap := genTestScopes() - root := scopeMap[`\`].(*scopeEntity) - - keepRecursing := func(Entity) bool { return true } - stopRecursing := func(Entity) bool { return false } - - // Append special entities under IDE0 - ide := scopeMap["IDE0"].(*scopeEntity) - ide.Append(&Device{}) - ide.Append(&namedEntity{op: opProcessor}) - ide.Append(&namedEntity{op: opProcessor}) - ide.Append(&namedEntity{op: opPowerRes}) - ide.Append(&namedEntity{op: opPowerRes}) - ide.Append(&namedEntity{op: opPowerRes}) - ide.Append(&namedEntity{op: opThermalZone}) - ide.Append(&namedEntity{op: opThermalZone}) - ide.Append(&namedEntity{op: opThermalZone}) - ide.Append(&namedEntity{op: opThermalZone}) - ide.Append(&Method{}) - ide.Append(&Method{}) - ide.Append(&Method{}) - ide.Append(&Method{}) - ide.Append(&Method{}) - ide.Append(&methodInvocationEntity{ - unnamedEntity: unnamedEntity{ - args: []interface{}{ - &constEntity{val: uint64(1)}, - &constEntity{val: uint64(2)}, - }, - }, - }) - - specs := []struct { - searchType EntityType - keepRecursingFn func(Entity) bool - wantHits int - }{ - {EntityTypeAny, keepRecursing, 24}, - {EntityTypeAny, stopRecursing, 1}, - { - EntityTypeAny, - func(ent Entity) bool { - // Stop recursing after visiting the methodInvocationEntity - _, isInv := ent.(*methodInvocationEntity) - return !isInv - }, - 22, - }, - - { - EntityTypeAny, - func(ent Entity) bool { - // Stop recursing after visiting the first constEntity - _, isConst := ent.(*constEntity) - return !isConst - }, - 23, - }, - {EntityTypeDevice, keepRecursing, 1}, - {EntityTypeProcessor, keepRecursing, 2}, - {EntityTypePowerResource, keepRecursing, 3}, - {EntityTypeThermalZone, keepRecursing, 4}, - {EntityTypeMethod, keepRecursing, 5}, - } - - for specIndex, spec := range specs { - var hits int - scopeVisit(0, root, spec.searchType, func(_ int, obj Entity) bool { - hits++ - return spec.keepRecursingFn(obj) - }) - - if hits != spec.wantHits { - t.Errorf("[spec %d] expected visitor to be called %d times; got %d", specIndex, spec.wantHits, hits) - } - } -} - -func TestScopeResolvePath(t *testing.T) { - scopeMap := genTestScopes() - - specs := []struct { - curScope ScopeEntity - pathExpr string - wantParent Entity - wantName string - }{ - { - scopeMap["IDE0"].(*scopeEntity), - `\_SB_`, - scopeMap[`\`], - "_SB_", - }, - { - scopeMap["IDE0"].(*scopeEntity), - `^FOO`, - scopeMap[`PCI0`], - "FOO", - }, - { - scopeMap["IDE0"].(*scopeEntity), - `^^FOO`, - scopeMap[`_SB_`], - "FOO", - }, - { - scopeMap["IDE0"].(*scopeEntity), - `_ADR`, - scopeMap[`IDE0`], - "_ADR", - }, - // Paths with dots - { - scopeMap["IDE0"].(*scopeEntity), - `\_SB_.PCI0.IDE0._ADR`, - scopeMap[`IDE0`], - "_ADR", - }, - { - scopeMap["PCI0"].(*scopeEntity), - `IDE0._ADR`, - scopeMap[`IDE0`], - "_ADR", - }, - { - scopeMap["PCI0"].(*scopeEntity), - `_CRS`, - scopeMap[`PCI0`], - "_CRS", - }, - // Bad queries - { - scopeMap["PCI0"].(*scopeEntity), - `FOO.BAR.BAZ`, - nil, - "", - }, - { - scopeMap["PCI0"].(*scopeEntity), - ``, - nil, - "", - }, - { - scopeMap["PCI0"].(*scopeEntity), - `\`, - nil, - "", - }, - { - scopeMap["PCI0"].(*scopeEntity), - `^^^^^^^^^BADPATH`, - nil, - "", - }, - } - - root := scopeMap[`\`].(*scopeEntity) - for specIndex, spec := range specs { - gotParent, gotName := scopeResolvePath(spec.curScope, root, spec.pathExpr) - if !reflect.DeepEqual(gotParent, spec.wantParent) { - t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.wantParent, gotParent) - continue - } - - if gotName != spec.wantName { - t.Errorf("[spec %d] expected lookup to return node name %q; got %q", specIndex, spec.wantName, gotName) - } - } -} - -func TestScopeFind(t *testing.T) { - scopeMap := genTestScopes() - - specs := []struct { - curScope ScopeEntity - lookup string - want Entity - }{ - // Search rules do not apply for these cases - { - scopeMap["PCI0"].(*scopeEntity), - `\`, - scopeMap[`\`], - }, - { - scopeMap["PCI0"].(*scopeEntity), - "IDE0._ADR", - scopeMap["_ADR"], - }, - { - scopeMap["IDE0"].(*scopeEntity), - "^^PCI0.IDE0._ADR", - scopeMap["_ADR"], - }, - { - scopeMap["IDE0"].(*scopeEntity), - `\_SB_.PCI0.IDE0._ADR`, - scopeMap["_ADR"], - }, - { - scopeMap["IDE0"].(*scopeEntity), - `\_SB_.PCI0`, - scopeMap["PCI0"], - }, - { - scopeMap["IDE0"].(*scopeEntity), - `^`, - scopeMap["PCI0"], - }, - // Bad queries - { - scopeMap["_SB_"].(*scopeEntity), - "PCI0.USB._CRS", - nil, - }, - { - scopeMap["IDE0"].(*scopeEntity), - "^^^^^^^^^^^^^^^^^^^", - nil, - }, - { - scopeMap["IDE0"].(*scopeEntity), - `^^^^^^^^^^^FOO`, - nil, - }, - { - scopeMap["IDE0"].(*scopeEntity), - "FOO", - nil, - }, - { - scopeMap["IDE0"].(*scopeEntity), - "", - nil, - }, - // Search rules apply for these cases - { - scopeMap["IDE0"].(*scopeEntity), - "_CRS", - scopeMap["_CRS"], - }, - } - - root := scopeMap[`\`].(*scopeEntity) - for specIndex, spec := range specs { - if got := scopeFind(spec.curScope, root, spec.lookup); !reflect.DeepEqual(got, spec.want) { - t.Errorf("[spec %d] expected lookup to return %#v; got %#v", specIndex, spec.want, got) - } - } -} - -func genTestScopes() map[string]Entity { - // Setup the example tree from page 252 of the acpi 6.2 spec - // \ - // SB - // \ - // PCI0 - // | _CRS - // \ - // IDE0 - // | _ADR - ideScope := &scopeEntity{name: `IDE0`} - pciScope := &scopeEntity{name: `PCI0`} - sbScope := &scopeEntity{name: `_SB_`} - rootScope := &scopeEntity{name: `\`} - - adr := &namedEntity{name: `_ADR`} - crs := &namedEntity{name: `_CRS`} - - // Setup tree - ideScope.Append(adr) - pciScope.Append(crs) - pciScope.Append(ideScope) - sbScope.Append(pciScope) - rootScope.Append(sbScope) - - return map[string]Entity{ - "IDE0": ideScope, - "PCI0": pciScope, - "_SB_": sbScope, - "\\": rootScope, - "_ADR": adr, - "_CRS": crs, - } -} From 7e419ae20ea309b5dbde196d30fd6bde946c03e0 Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Sun, 17 Dec 2017 16:59:08 +0000 Subject: [PATCH 4/9] acpi: move entity visitor code to the entity pkg --- .../device/acpi/aml/entity/visitor.go | 64 +++++++++++++ .../device/acpi/aml/entity/visitor_test.go | 93 +++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 src/gopheros/device/acpi/aml/entity/visitor.go create mode 100644 src/gopheros/device/acpi/aml/entity/visitor_test.go diff --git a/src/gopheros/device/acpi/aml/entity/visitor.go b/src/gopheros/device/acpi/aml/entity/visitor.go new file mode 100644 index 0000000..0b82550 --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/visitor.go @@ -0,0 +1,64 @@ +package entity + +// Visitor is a function invoked by the VM for each AML tree entity that matches +// a particular type. The return value controls whether the children of this +// entity should also be visited. +type Visitor func(depth int, obj Entity) (keepRecursing bool) + +// Type defines the type of entity that visitors should inspect. +type Type uint8 + +// The list of supported Type values. TypeAny works as a wildcard +// allowing the visitor to inspect all entities in the AML tree. +const ( + TypeAny Type = iota + TypeDevice + TypeProcessor + TypePowerResource + TypeThermalZone + TypeMethod + TypeMutex + TypeEvent + TypeField + TypeIndexField + TypeBankField +) + +// Visit descends a scope hierarchy and invokes visitorFn for each entity +// that matches entType. +func Visit(depth int, ent Entity, entType Type, visitorFn Visitor) bool { + op := ent.Opcode() + switch { + case (entType == TypeAny) || + (entType == TypeDevice && op == OpDevice) || + (entType == TypeProcessor && op == OpProcessor) || + (entType == TypePowerResource && op == OpPowerRes) || + (entType == TypeThermalZone && op == OpThermalZone) || + (entType == TypeMethod && op == OpMethod) || + (entType == TypeMutex && op == OpMutex) || + (entType == TypeEvent && op == OpEvent) || + (entType == TypeField && op == OpField) || + (entType == TypeIndexField && op == OpIndexField) || + (entType == TypeBankField && op == OpBankField): + // If the visitor returned false we should not visit the children + if !visitorFn(depth, ent) { + return false + } + + // Visit any args that are also entities + for _, arg := range ent.Args() { + if argEnt, isEnt := arg.(Entity); isEnt && !Visit(depth+1, argEnt, entType, visitorFn) { + return false + } + } + } + + // If the entity defines a scope we need to visit the child entities. + if container, isContainer := ent.(Container); isContainer { + for _, child := range container.Children() { + _ = Visit(depth+1, child, entType, visitorFn) + } + } + + return true +} diff --git a/src/gopheros/device/acpi/aml/entity/visitor_test.go b/src/gopheros/device/acpi/aml/entity/visitor_test.go new file mode 100644 index 0000000..3c7aa4e --- /dev/null +++ b/src/gopheros/device/acpi/aml/entity/visitor_test.go @@ -0,0 +1,93 @@ +package entity + +import "testing" + +func TestScopeVisit(t *testing.T) { + tableHandle := uint8(42) + keepRecursing := func(Entity) bool { return true } + stopRecursing := func(Entity) bool { return false } + + // Append special entities under IDE0 + root := NewScope(tableHandle, "IDE0") + root.Append(NewDevice(tableHandle, "DEV0")) + root.Append(NewProcessor(tableHandle, "FOO0")) + root.Append(NewProcessor(tableHandle, "FOO0")) + root.Append(NewPowerResource(tableHandle, "FOO0")) + root.Append(NewPowerResource(tableHandle, "FOO0")) + root.Append(NewPowerResource(tableHandle, "FOO0")) + root.Append(NewThermalZone(tableHandle, "FOO0")) + root.Append(NewThermalZone(tableHandle, "FOO0")) + root.Append(NewThermalZone(tableHandle, "FOO0")) + root.Append(NewThermalZone(tableHandle, "FOO0")) + root.Append(NewMethod(tableHandle, "MTH0")) + root.Append(NewMethod(tableHandle, "MTH1")) + root.Append(NewMethod(tableHandle, "MTH2")) + root.Append(NewMethod(tableHandle, "MTH3")) + root.Append(NewMethod(tableHandle, "MTH4")) + root.Append(NewMutex(tableHandle)) + root.Append(NewMutex(tableHandle)) + root.Append(NewEvent(tableHandle)) + root.Append(NewEvent(tableHandle)) + root.Append(NewEvent(tableHandle)) + root.Append(NewField(tableHandle)) + root.Append(NewIndexField(tableHandle)) + root.Append(NewBankField(tableHandle)) + root.Append(&Invocation{ + Generic: Generic{ + op: OpMethodInvocation, + args: []interface{}{ + NewConst(OpOne, tableHandle, uint64(1)), + NewConst(OpDwordPrefix, tableHandle, uint64(2)), + }, + }, + }) + + specs := []struct { + searchType Type + keepRecursingFn func(Entity) bool + wantHits int + }{ + {TypeAny, keepRecursing, 27}, + {TypeAny, stopRecursing, 1}, + { + TypeAny, + func(ent Entity) bool { + // Stop recursing after visiting the Invocation entity + _, isInv := ent.(*Invocation) + return !isInv + }, + 25, + }, + { + TypeAny, + func(ent Entity) bool { + // Stop recursing after visiting the first Const entity + _, isConst := ent.(*Const) + return !isConst + }, + 26, + }, + {TypeDevice, keepRecursing, 1}, + {TypeProcessor, keepRecursing, 2}, + {TypePowerResource, keepRecursing, 3}, + {TypeThermalZone, keepRecursing, 4}, + {TypeMethod, keepRecursing, 5}, + {TypeMutex, keepRecursing, 2}, + {TypeEvent, keepRecursing, 3}, + {TypeField, keepRecursing, 1}, + {TypeIndexField, keepRecursing, 1}, + {TypeBankField, keepRecursing, 1}, + } + + for specIndex, spec := range specs { + var hits int + Visit(0, root, spec.searchType, func(_ int, obj Entity) bool { + hits++ + return spec.keepRecursingFn(obj) + }) + + if hits != spec.wantHits { + t.Errorf("[spec %d] expected visitor to be called %d times; got %d", specIndex, spec.wantHits, hits) + } + } +} From d1eb0b6d4d6a65ca2942e251728cb39d595aeaf5 Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Sat, 16 Dec 2017 08:19:22 +0000 Subject: [PATCH 5/9] acpi: move parser opcode table into the parser pkg --- src/gopheros/device/acpi/aml/opcode.go | 269 -------------- src/gopheros/device/acpi/aml/opcode_table.go | 317 ---------------- src/gopheros/device/acpi/aml/opcode_test.go | 187 ---------- .../device/acpi/aml/parser/opcode_table.go | 339 ++++++++++++++++++ .../device/acpi/aml/parser/opcode_test.go | 35 ++ 5 files changed, 374 insertions(+), 773 deletions(-) delete mode 100644 src/gopheros/device/acpi/aml/opcode.go delete mode 100644 src/gopheros/device/acpi/aml/opcode_table.go delete mode 100644 src/gopheros/device/acpi/aml/opcode_test.go create mode 100644 src/gopheros/device/acpi/aml/parser/opcode_table.go create mode 100644 src/gopheros/device/acpi/aml/parser/opcode_test.go diff --git a/src/gopheros/device/acpi/aml/opcode.go b/src/gopheros/device/acpi/aml/opcode.go deleted file mode 100644 index 617fc7a..0000000 --- a/src/gopheros/device/acpi/aml/opcode.go +++ /dev/null @@ -1,269 +0,0 @@ -package aml - -// opcode describes an AML opcode. While AML supports 256 opcodes, some of them -// are specified using a combination of an extension prefix and a code. To map -// each opcode into a single unique value the parser uses an uint16 -// representation of the opcode values. -type opcode uint16 - -// String implements fmt.Stringer for opcode. -func (op opcode) String() string { - for _, entry := range opcodeTable { - if entry.op == op { - return entry.name - } - } - - return "unknown" -} - -// opIsLocalArg returns true if this opcode represents any of the supported local -// function args 0 to 7. -func opIsLocalArg(op opcode) bool { - return op >= opLocal0 && op <= opLocal7 -} - -// opIsMethodArg returns true if this opcode represents any of the supported -// input function args 0 to 6. -func opIsMethodArg(op opcode) bool { - return op >= opArg0 && op <= opArg6 -} - -// opIsArg returns true if this opcode is either a local or a method arg. -func opIsArg(op opcode) bool { - return opIsLocalArg(op) || opIsMethodArg(op) -} - -// opIsDataObject returns true if this opcode is part of a DataObject definition -// -// Grammar: -// DataObject := ComputationalData | DefPackage | DefVarPackage -// ComputationalData := ByteConst | WordConst | DWordConst | QWordConst | String | ConstObj | RevisionOp | DefBuffer -// ConstObj := ZeroOp | OneOp | OnesOp -func opIsDataObject(op opcode) bool { - switch op { - case opBytePrefix, opWordPrefix, opDwordPrefix, opQwordPrefix, opStringPrefix, - opZero, opOne, opOnes, opRevision, opBuffer, opPackage, opVarPackage: - return true - default: - return false - } -} - -// opIsType2 returns true if this is a Type2Opcode. -// -// Grammar: -// Type2Opcode := DefAcquire | DefAdd | DefAnd | DefBuffer | DefConcat | -// DefConcatRes | DefCondRefOf | DefCopyObject | DefDecrement | -// DefDerefOf | DefDivide | DefFindSetLeftBit | DefFindSetRightBit | -// DefFromBCD | DefIncrement | DefIndex | DefLAnd | DefLEqual | -// DefLGreater | DefLGreaterEqual | DefLLess | DefLLessEqual | DefMid | -// DefLNot | DefLNotEqual | DefLoadTable | DefLOr | DefMatch | DefMod | -// DefMultiply | DefNAnd | DefNOr | DefNot | DefObjectType | DefOr | -// DefPackage | DefVarPackage | DefRefOf | DefShiftLeft | DefShiftRight | -// DefSizeOf | DefStore | DefSubtract | DefTimer | DefToBCD | DefToBuffer | -// DefToDecimalString | DefToHexString | DefToInteger | DefToString | -// DefWait | DefXOr -func opIsType2(op opcode) bool { - switch op { - case opAcquire, opAdd, opAnd, opBuffer, opConcat, - opConcatRes, opCondRefOf, opCopyObject, opDecrement, - opDerefOf, opDivide, opFindSetLeftBit, opFindSetRightBit, - opFromBCD, opIncrement, opIndex, opLand, opLEqual, - opLGreater, opLLess, opMid, - opLnot, opLoadTable, opLor, opMatch, opMod, - opMultiply, opNand, opNor, opNot, opObjectType, opOr, - opPackage, opVarPackage, opRefOf, opShiftLeft, opShiftRight, - opSizeOf, opStore, opSubtract, opTimer, opToBCD, opToBuffer, - opToDecimalString, opToHexString, opToInteger, opToString, - opWait, opXor: - return true - default: - return false - } -} - -// opIsBufferField returens true if this opcode describes a -// buffer field creation operation. -func opIsBufferField(op opcode) bool { - switch op { - case opCreateField, opCreateBitField, opCreateByteField, opCreateWordField, opCreateDWordField, opCreateQWordField: - return true - default: - return false - } -} - -// objType represents the object types that are supported by the AML parser. -type objType uint8 - -// The list of AML object types. -const ( - objTypeAny objType = iota - objTypeInteger - objTypeString - objTypeBuffer - objTypePackage - objTypeDevice - objTypeEvent - objTypeMethod - objTypeMutex - objTypeRegion - objTypePower - objTypeProcessor - objTypeThermal - objTypeBufferField - objTypeLocalRegionField - objTypeLocalBankField - objTypeLocalReference - objTypeLocalAlias - objTypeLocalScope - objTypeLocalVariable - objTypeMethodArgument -) - -// opFlag specifies a list of OR-able flags that describe the object -// type/attributes generated by a particular opcode. -type opFlag uint16 - -const ( - opFlagNone opFlag = 1 << iota - opFlagHasPkgLen - opFlagNamed - opFlagConstant - opFlagReference - opFlagArithmetic - opFlagCreate - opFlagReturn - opFlagExecutable - opFlagNoOp - opFlagScoped -) - -// is returns true if f is set in this opFlag. -func (fl opFlag) is(f opFlag) bool { - return (fl & f) != 0 -} - -// opArgFlags encodes up to 7 opArgFlag values in a uint64 value. -type opArgFlags uint64 - -// argCount returns the number of encoded args in the given flag. -func (fl opArgFlags) argCount() (count uint8) { - // Each argument is specified using 8 bits with 0x0 indicating the end of the - // argument list - for ; fl&0xf != 0; fl, count = fl>>8, count+1 { - } - - return count -} - -// arg returns the arg flags for argument "num" where num is the 0-based index -// of the argument to return. The allowed values for num are 0-6. -func (fl opArgFlags) arg(num uint8) opArgFlag { - return opArgFlag((fl >> (num * 8)) & 0xf) -} - -// contains returns true if the arg flags contain any argument with type x. -func (fl opArgFlags) contains(x opArgFlag) bool { - // Each argument is specified using 8 bits with 0x0 indicating the end of the - // argument list - for ; fl&0xf != 0; fl >>= 8 { - if opArgFlag(fl&0xf) == x { - return true - } - } - - return false -} - -// opArgFlag represents the type of an argument expected by a particular opcode. -type opArgFlag uint8 - -// The list of supported opArgFlag values. -const ( - _ opArgFlag = iota - opArgTermList - opArgTermObj - opArgByteList - opArgPackage - opArgString - opArgByteData - opArgWord - opArgDword - opArgQword - opArgNameString - opArgSuperName - opArgSimpleName - opArgDataRefObj - opArgTarget - opArgFieldList -) - -// String implements fmt.Stringer for opArgFlag. -func (fl opArgFlag) String() string { - switch fl { - case opArgTermList: - return "opArgTermList" - case opArgTermObj: - return "opArgTermObj" - case opArgByteList: - return "opArgByteList" - case opArgPackage: - return "opArgPackage" - case opArgString: - return "opArgString" - case opArgByteData: - return "opArgByteData" - case opArgWord: - return "opArgWord" - case opArgDword: - return "opArgDword" - case opArgQword: - return "opArgQword" - case opArgNameString: - return "opArgNameString" - case opArgSuperName: - return "opArgSuperName" - case opArgSimpleName: - return "opArgSimpleName" - case opArgDataRefObj: - return "opArgDataRefObj" - case opArgTarget: - return "opArgTarget" - case opArgFieldList: - return "opArgFieldList" - } - return "" -} - -func makeArg0() opArgFlags { return 0 } -func makeArg1(arg0 opArgFlag) opArgFlags { return opArgFlags(arg0) } -func makeArg2(arg0, arg1 opArgFlag) opArgFlags { return opArgFlags(arg1)<<8 | opArgFlags(arg0) } -func makeArg3(arg0, arg1, arg2 opArgFlag) opArgFlags { - return opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) -} -func makeArg4(arg0, arg1, arg2, arg3 opArgFlag) opArgFlags { - return opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) -} -func makeArg5(arg0, arg1, arg2, arg3, arg4 opArgFlag) opArgFlags { - return opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) -} -func makeArg6(arg0, arg1, arg2, arg3, arg4, arg5 opArgFlag) opArgFlags { - return opArgFlags(arg5)<<40 | opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) -} -func makeArg7(arg0, arg1, arg2, arg3, arg4, arg5, arg6 opArgFlag) opArgFlags { - return opArgFlags(arg6)<<48 | opArgFlags(arg5)<<40 | opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) -} - -// opcodeInfo contains all known information about an opcode, -// its argument count and types as well as the type of object -// represented by it. -type opcodeInfo struct { - op opcode - name string - objType objType - - flags opFlag - argFlags opArgFlags -} diff --git a/src/gopheros/device/acpi/aml/opcode_table.go b/src/gopheros/device/acpi/aml/opcode_table.go deleted file mode 100644 index ba8e500..0000000 --- a/src/gopheros/device/acpi/aml/opcode_table.go +++ /dev/null @@ -1,317 +0,0 @@ -package aml - -const ( - badOpcode = 0xff - extOpPrefix = 0x5b - - // Regular opcode list - opZero = opcode(0x00) - opOne = opcode(0x01) - opAlias = opcode(0x06) - opName = opcode(0x08) - opBytePrefix = opcode(0x0a) - opWordPrefix = opcode(0x0b) - opDwordPrefix = opcode(0x0c) - opStringPrefix = opcode(0x0d) - opQwordPrefix = opcode(0x0e) - opScope = opcode(0x10) - opBuffer = opcode(0x11) - opPackage = opcode(0x12) - opVarPackage = opcode(0x13) - opMethod = opcode(0x14) - opExternal = opcode(0x15) - opLocal0 = opcode(0x60) - opLocal1 = opcode(0x61) - opLocal2 = opcode(0x62) - opLocal3 = opcode(0x63) - opLocal4 = opcode(0x64) - opLocal5 = opcode(0x65) - opLocal6 = opcode(0x66) - opLocal7 = opcode(0x67) - opArg0 = opcode(0x68) - opArg1 = opcode(0x69) - opArg2 = opcode(0x6a) - opArg3 = opcode(0x6b) - opArg4 = opcode(0x6c) - opArg5 = opcode(0x6d) - opArg6 = opcode(0x6e) - opStore = opcode(0x70) - opRefOf = opcode(0x71) - opAdd = opcode(0x72) - opConcat = opcode(0x73) - opSubtract = opcode(0x74) - opIncrement = opcode(0x75) - opDecrement = opcode(0x76) - opMultiply = opcode(0x77) - opDivide = opcode(0x78) - opShiftLeft = opcode(0x79) - opShiftRight = opcode(0x7a) - opAnd = opcode(0x7b) - opNand = opcode(0x7c) - opOr = opcode(0x7d) - opNor = opcode(0x7e) - opXor = opcode(0x7f) - opNot = opcode(0x80) - opFindSetLeftBit = opcode(0x81) - opFindSetRightBit = opcode(0x82) - opDerefOf = opcode(0x83) - opConcatRes = opcode(0x84) - opMod = opcode(0x85) - opNotify = opcode(0x86) - opSizeOf = opcode(0x87) - opIndex = opcode(0x88) - opMatch = opcode(0x89) - opCreateDWordField = opcode(0x8a) - opCreateWordField = opcode(0x8b) - opCreateByteField = opcode(0x8c) - opCreateBitField = opcode(0x8d) - opObjectType = opcode(0x8e) - opCreateQWordField = opcode(0x8f) - opLand = opcode(0x90) - opLor = opcode(0x91) - opLnot = opcode(0x92) - opLEqual = opcode(0x93) - opLGreater = opcode(0x94) - opLLess = opcode(0x95) - opToBuffer = opcode(0x96) - opToDecimalString = opcode(0x97) - opToHexString = opcode(0x98) - opToInteger = opcode(0x99) - opToString = opcode(0x9c) - opCopyObject = opcode(0x9d) - opMid = opcode(0x9e) - opContinue = opcode(0x9f) - opIf = opcode(0xa0) - opElse = opcode(0xa1) - opWhile = opcode(0xa2) - opNoop = opcode(0xa3) - opReturn = opcode(0xa4) - opBreak = opcode(0xa5) - opBreakPoint = opcode(0xcc) - opOnes = opcode(0xff) - // Extended opcodes - opMutex = opcode(0xff + 0x01) - opEvent = opcode(0xff + 0x02) - opCondRefOf = opcode(0xff + 0x12) - opCreateField = opcode(0xff + 0x13) - opLoadTable = opcode(0xff + 0x1f) - opLoad = opcode(0xff + 0x20) - opStall = opcode(0xff + 0x21) - opSleep = opcode(0xff + 0x22) - opAcquire = opcode(0xff + 0x23) - opSignal = opcode(0xff + 0x24) - opWait = opcode(0xff + 0x25) - opReset = opcode(0xff + 0x26) - opRelease = opcode(0xff + 0x27) - opFromBCD = opcode(0xff + 0x28) - opToBCD = opcode(0xff + 0x29) - opUnload = opcode(0xff + 0x2a) - opRevision = opcode(0xff + 0x30) - opDebug = opcode(0xff + 0x31) - opFatal = opcode(0xff + 0x32) - opTimer = opcode(0xff + 0x33) - opOpRegion = opcode(0xff + 0x80) - opField = opcode(0xff + 0x81) - opDevice = opcode(0xff + 0x82) - opProcessor = opcode(0xff + 0x83) - opPowerRes = opcode(0xff + 0x84) - opThermalZone = opcode(0xff + 0x85) - opIndexField = opcode(0xff + 0x86) - opBankField = opcode(0xff + 0x87) - opDataRegion = opcode(0xff + 0x88) -) - -// The opcode table contains all opcode-related information that the parser knows. -// This table is modeled after a similar table used in the acpica implementation. -var opcodeTable = []opcodeInfo{ - /*0x00*/ {opZero, "Zero", objTypeInteger, opFlagConstant, makeArg0()}, - /*0x01*/ {opOne, "One", objTypeInteger, opFlagConstant, makeArg0()}, - /*0x02*/ {opAlias, "Alias", objTypeLocalAlias, opFlagNamed, makeArg2(opArgNameString, opArgNameString)}, - /*0x03*/ {opName, "Name", objTypeAny, opFlagNamed, makeArg2(opArgNameString, opArgDataRefObj)}, - /*0x04*/ {opBytePrefix, "Byte", objTypeInteger, opFlagConstant, makeArg1(opArgByteData)}, - /*0x05*/ {opWordPrefix, "Word", objTypeInteger, opFlagConstant, makeArg1(opArgWord)}, - /*0x06*/ {opDwordPrefix, "Dword", objTypeInteger, opFlagConstant, makeArg1(opArgDword)}, - /*0x07*/ {opStringPrefix, "String", objTypeString, opFlagConstant, makeArg1(opArgString)}, - /*0x08*/ {opQwordPrefix, "Qword", objTypeInteger, opFlagConstant, makeArg1(opArgQword)}, - /*0x09*/ {opScope, "Scope", objTypeLocalScope, opFlagNamed, makeArg2(opArgNameString, opArgTermList)}, - /*0x0a*/ {opBuffer, "Buffer", objTypeBuffer, opFlagHasPkgLen, makeArg2(opArgTermObj, opArgByteList)}, - /*0x0b*/ {opPackage, "Package", objTypePackage, opFlagNone, makeArg2(opArgByteData, opArgTermList)}, - /*0x0c*/ {opVarPackage, "VarPackage", objTypePackage, opFlagNone, makeArg2(opArgByteData, opArgTermList)}, - /*0x0d*/ {opMethod, "Method", objTypeMethod, opFlagNamed | opFlagScoped, makeArg3(opArgNameString, opArgByteData, opArgTermList)}, - /*0x0e*/ {opExternal, "External", objTypeAny, opFlagNamed, makeArg3(opArgNameString, opArgByteData, opArgByteData)}, - /*0x0f*/ {opLocal0, "Local0", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0x10*/ {opLocal1, "Local1", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0x11*/ {opLocal2, "Local2", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0x12*/ {opLocal3, "Local3", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0x13*/ {opLocal4, "Local4", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0120*/ {opLocal5, "Local5", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0x15*/ {opLocal6, "Local6", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0x16*/ {opLocal7, "Local7", objTypeLocalVariable, opFlagExecutable, makeArg0()}, - /*0x17*/ {opArg0, "Arg0", objTypeMethodArgument, opFlagExecutable, makeArg0()}, - /*0x18*/ {opArg1, "Arg1", objTypeMethodArgument, opFlagExecutable, makeArg0()}, - /*0x19*/ {opArg2, "Arg2", objTypeMethodArgument, opFlagExecutable, makeArg0()}, - /*0x1a*/ {opArg3, "Arg3", objTypeMethodArgument, opFlagExecutable, makeArg0()}, - /*0x1b*/ {opArg4, "Arg4", objTypeMethodArgument, opFlagExecutable, makeArg0()}, - /*0x1c*/ {opArg5, "Arg5", objTypeMethodArgument, opFlagExecutable, makeArg0()}, - /*0x1d*/ {opArg6, "Arg6", objTypeMethodArgument, opFlagExecutable, makeArg0()}, - /*0x1e*/ {opStore, "Store", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgSuperName)}, - /*0x1f*/ {opRefOf, "RefOf", objTypeAny, opFlagReference | opFlagExecutable, makeArg1(opArgSuperName)}, - /*0x20*/ {opAdd, "Add", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x21*/ {opConcat, "Concat", objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x22*/ {opSubtract, "Subtract", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x23*/ {opIncrement, "Increment", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgSuperName)}, - /*0x24*/ {opDecrement, "Decrement", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgSuperName)}, - /*0x25*/ {opMultiply, "Multiply", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x26*/ {opDivide, "Divide", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTarget, opArgTarget)}, - /*0x27*/ {opShiftLeft, "ShiftLeft", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x28*/ {opShiftRight, "ShiftRight", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x29*/ {opAnd, "And", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x2a*/ {opNand, "Nand", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x2b*/ {opOr, "Or", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x2c*/ {opNor, "Nor", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x2d*/ {opXor, "Xor", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x2e*/ {opNot, "Not", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x2f*/ {opFindSetLeftBit, "FindSetLeftBit", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x30*/ {opFindSetRightBit, "FindSetRightBit", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x31*/ {opDerefOf, "DerefOf", objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, - /*0x32*/ {opConcatRes, "ConcatRes", objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x33*/ {opMod, "Mod", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x34*/ {opNotify, "Notify", objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgTermObj)}, - /*0x35*/ {opSizeOf, "SizeOf", objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, - /*0x36*/ {opIndex, "Index", objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x37*/ {opMatch, "Match", objTypeAny, opFlagExecutable, makeArg6(opArgTermObj, opArgByteData, opArgTermObj, opArgByteData, opArgTermObj, opArgTermObj)}, - /*0x38*/ {opCreateDWordField, "CreateDWordField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, - /*0x39*/ {opCreateWordField, "CreateWordField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, - /*0x3a*/ {opCreateByteField, "CreateByteField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, - /*0x3b*/ {opCreateBitField, "CreateBitField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, - /*0x3c*/ {opObjectType, "ObjectType", objTypeAny, opFlagNone, makeArg1(opArgSuperName)}, - /*0x3d*/ {opCreateQWordField, "CreateQWordField", objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, - /*0x3e*/ {opLand, "Land", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, - /*0x3f*/ {opLor, "Lor", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, - /*0x40*/ {opLnot, "Lnot", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgTermObj)}, - /*0x41*/ {opLEqual, "LEqual", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, - /*0x42*/ {opLGreater, "LGreater", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, - /*0x43*/ {opLLess, "LLess", objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, - /*0x44*/ {opToBuffer, "ToBuffer", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x45*/ {opToDecimalString, "ToDecimalString", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x46*/ {opToHexString, "ToHexString", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x47*/ {opToInteger, "ToInteger", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x48*/ {opToString, "ToString", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x49*/ {opCopyObject, "CopyObject", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgSimpleName)}, - /*0x4a*/ {opMid, "Mid", objTypeAny, opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTermObj, opArgTarget)}, - /*0x4b*/ {opContinue, "Continue", objTypeAny, opFlagExecutable, makeArg0()}, - /*0x4c*/ {opIf, "If", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTermList)}, - /*0x4d*/ {opElse, "Else", objTypeAny, opFlagExecutable | opFlagScoped, makeArg1(opArgTermList)}, - /*0x4e*/ {opWhile, "While", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTermList)}, - /*0x4f*/ {opNoop, "Noop", objTypeAny, opFlagNoOp, makeArg0()}, - /*0x50*/ {opReturn, "Return", objTypeAny, opFlagReturn, makeArg1(opArgTermObj)}, - /*0x51*/ {opBreak, "Break", objTypeAny, opFlagExecutable, makeArg0()}, - /*0x52*/ {opBreakPoint, "BreakPoint", objTypeAny, opFlagNoOp, makeArg0()}, - /*0x53*/ {opOnes, "Ones", objTypeInteger, opFlagConstant, makeArg0()}, - /*0x54*/ {opMutex, "Mutex", objTypeMutex, opFlagNamed, makeArg2(opArgNameString, opArgByteData)}, - /*0x55*/ {opEvent, "Event", objTypeEvent, opFlagNamed, makeArg1(opArgNameString)}, - /*0x56*/ {opCondRefOf, "CondRefOf", objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgSuperName)}, - /*0x57*/ {opCreateField, "CreateField", objTypeBufferField, opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTermObj, opArgNameString)}, - /*0x58*/ {opLoadTable, "LoadTable", objTypeAny, opFlagExecutable, makeArg7(opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj)}, - /*0x59*/ {opLoad, "Load", objTypeAny, opFlagExecutable, makeArg2(opArgNameString, opArgSuperName)}, - /*0x5a*/ {opStall, "Stall", objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, - /*0x5b*/ {opSleep, "Sleep", objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, - /*0x5c*/ {opAcquire, "Acquire", objTypeAny, opFlagExecutable, makeArg2(opArgNameString, opArgSuperName)}, - /*0x5d*/ {opSignal, "Signal", objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, - /*0x5e*/ {opWait, "Wait", objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgTermObj)}, - /*0x5f*/ {opReset, "Reset", objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, - /*0x60*/ {opRelease, "Release", objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, - /*0x61*/ {opFromBCD, "FromBCD", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x62*/ {opToBCD, "ToBCD", objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, - /*0x63*/ {opUnload, "Unload", objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, - /*0x64*/ {opRevision, "Revision", objTypeInteger, opFlagConstant | opFlagExecutable, makeArg0()}, - /*0x65*/ {opDebug, "Debug", objTypeLocalReference, opFlagExecutable, makeArg0()}, - /*0x66*/ {opFatal, "Fatal", objTypeAny, opFlagExecutable, makeArg3(opArgByteData, opArgDword, opArgTermObj)}, - /*0x67*/ {opTimer, "Timer", objTypeAny, opFlagNone, makeArg0()}, - /*0x68*/ {opOpRegion, "OpRegion", objTypeRegion, opFlagNamed, makeArg4(opArgNameString, opArgByteData, opArgTermObj, opArgTermObj)}, - /*0x69*/ {opField, "Field", objTypeAny, opFlagNone, makeArg3(opArgNameString, opArgByteData, opArgFieldList)}, - /*0x6a*/ {opDevice, "Device", objTypeDevice, opFlagNamed | opFlagScoped, makeArg2(opArgNameString, opArgTermList)}, - /*0x6b*/ {opProcessor, "Processor", objTypeProcessor, opFlagNamed | opFlagScoped, makeArg5(opArgNameString, opArgByteData, opArgDword, opArgByteData, opArgTermList)}, - /*0x6c*/ {opPowerRes, "PowerRes", objTypePower, opFlagNamed | opFlagScoped, makeArg4(opArgNameString, opArgByteData, opArgWord, opArgTermList)}, - /*0x6d*/ {opThermalZone, "ThermalZone", objTypeThermal, opFlagNamed | opFlagScoped, makeArg2(opArgNameString, opArgTermList)}, - /*0x6e*/ {opIndexField, "IndexField", objTypeAny, opFlagNone, makeArg4(opArgNameString, opArgNameString, opArgByteData, opArgFieldList)}, - /*0x6f*/ {opBankField, "BankField", objTypeLocalBankField, opFlagNamed, makeArg5(opArgNameString, opArgNameString, opArgTermObj, opArgByteData, opArgFieldList)}, - /*0x70*/ {opDataRegion, "DataRegion", objTypeLocalRegionField, opFlagNamed, makeArg4(opArgNameString, opArgTermObj, opArgTermObj, opArgTermObj)}, -} - -// opcodeMap maps an AML opcode to an entry in the opcode table. Entries with -// the value 0xff indicate an invalid/unsupported opcode. -var opcodeMap = [256]uint8{ - /* 0 1 2 3 4 5 6 7*/ - /*0x00 - 0x07*/ 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x02, 0xff, - /*0x08 - 0x0f*/ 0x03, 0xff, 0x04, 0x05, 0x06, 0x07, 0x08, 0xff, - /*0x10 - 0x17*/ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xff, 0xff, - /*0x18 - 0x1f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x20 - 0x27*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x28 - 0x2f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x30 - 0x37*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x38 - 0x3f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x40 - 0x47*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x48 - 0x4f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x50 - 0x57*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x58 - 0x5f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x60 - 0x67*/ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, - /*0x68 - 0x6f*/ 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0xff, - /*0x70 - 0x77*/ 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, - /*0x78 - 0x7f*/ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, - /*0x80 - 0x87*/ 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, - /*0x88 - 0x8f*/ 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, - /*0x90 - 0x97*/ 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, - /*0x98 - 0x9f*/ 0x46, 0x47, 0x48, 0x49, 0x4a, 0x49, 0x4a, 0x4b, - /*0xa0 - 0xa7*/ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0xff, 0xff, - /*0xa8 - 0xaf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xb0 - 0xb7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xb8 - 0xbf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xc0 - 0xc7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xc8 - 0xcf*/ 0xff, 0xff, 0xff, 0xff, 0x52, 0xff, 0xff, 0xff, - /*0xd0 - 0xd7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xd8 - 0xdf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xe0 - 0xe7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xe8 - 0xef*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xf0 - 0xf7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xf8 - 0xff*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x53, -} - -// extendedOpcodeMap maps an AML extended opcode (extOpPrefix + code) to an -// entry in the opcode table. Entries with the value 0xff indicate an -// invalid/unsupported opcode. -var extendedOpcodeMap = [256]uint8{ - /* 0 1 2 3 4 5 6 7*/ - /*0x00 - 0x07*/ 0xff, 0x54, 0x55, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x08 - 0x0f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x10 - 0x17*/ 0xff, 0xff, 0x56, 0x57, 0xff, 0xff, 0xff, 0xff, - /*0x18 - 0x1f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x58, - /*0x20 - 0x27*/ 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, - /*0x28 - 0x2f*/ 0x61, 0x62, 0x63, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x30 - 0x37*/ 0x64, 0x65, 0x66, 0x67, 0xff, 0xff, 0xff, 0xff, - /*0x38 - 0x3f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x40 - 0x47*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x48 - 0x4f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x50 - 0x57*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x58 - 0x5f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x60 - 0x67*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x68 - 0x6f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x70 - 0x77*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x78 - 0x7f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x80 - 0x87*/ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - /*0x88 - 0x8f*/ 0x70, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x90 - 0x97*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0x98 - 0x9f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xa0 - 0xa7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xa8 - 0xaf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xb0 - 0xb7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xb8 - 0xbf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xc0 - 0xc7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xc8 - 0xcf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xd0 - 0xd7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xd8 - 0xdf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xe0 - 0xe7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xe8 - 0xef*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xf0 - 0xf7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - /*0xf8 - 0xff*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x53, -} diff --git a/src/gopheros/device/acpi/aml/opcode_test.go b/src/gopheros/device/acpi/aml/opcode_test.go deleted file mode 100644 index 7258759..0000000 --- a/src/gopheros/device/acpi/aml/opcode_test.go +++ /dev/null @@ -1,187 +0,0 @@ -package aml - -import "testing" - -func TestOpcodeToString(t *testing.T) { - if exp, got := "Acquire", opAcquire.String(); got != exp { - t.Fatalf("expected opAcquire.toString() to return %q; got %q", exp, got) - } - - if exp, got := "unknown", opcode(0xffff).String(); got != exp { - t.Fatalf("expected opcode.String() to return %q; got %q", exp, got) - } -} - -func TestOpcodeIsX(t *testing.T) { - specs := []struct { - op opcode - testFn func(opcode) bool - want bool - }{ - // opIsLocalArg - {opLocal0, opIsLocalArg, true}, - {opLocal1, opIsLocalArg, true}, - {opLocal2, opIsLocalArg, true}, - {opLocal3, opIsLocalArg, true}, - {opLocal4, opIsLocalArg, true}, - {opLocal5, opIsLocalArg, true}, - {opLocal6, opIsLocalArg, true}, - {opLocal7, opIsLocalArg, true}, - {opArg0, opIsLocalArg, false}, - {opDivide, opIsLocalArg, false}, - // opIsMethodArg - {opArg0, opIsMethodArg, true}, - {opArg1, opIsMethodArg, true}, - {opArg2, opIsMethodArg, true}, - {opArg3, opIsMethodArg, true}, - {opArg4, opIsMethodArg, true}, - {opArg5, opIsMethodArg, true}, - {opArg6, opIsMethodArg, true}, - {opLocal7, opIsMethodArg, false}, - {opIf, opIsMethodArg, false}, - // opIsArg - {opLocal5, opIsArg, true}, - {opArg1, opIsArg, true}, - {opDivide, opIsArg, false}, - // opIsType2 - {opAcquire, opIsType2, true}, - {opAdd, opIsType2, true}, - {opAnd, opIsType2, true}, - {opBuffer, opIsType2, true}, - {opConcat, opIsType2, true}, - {opConcatRes, opIsType2, true}, - {opCondRefOf, opIsType2, true}, - {opCopyObject, opIsType2, true}, - {opDecrement, opIsType2, true}, - {opDerefOf, opIsType2, true}, - {opDivide, opIsType2, true}, - {opFindSetLeftBit, opIsType2, true}, - {opFindSetRightBit, opIsType2, true}, - {opFromBCD, opIsType2, true}, - {opIncrement, opIsType2, true}, - {opIndex, opIsType2, true}, - {opLand, opIsType2, true}, - {opLEqual, opIsType2, true}, - {opLGreater, opIsType2, true}, - {opLLess, opIsType2, true}, - {opMid, opIsType2, true}, - {opLnot, opIsType2, true}, - {opLoadTable, opIsType2, true}, - {opLor, opIsType2, true}, - {opMatch, opIsType2, true}, - {opMod, opIsType2, true}, - {opMultiply, opIsType2, true}, - {opNand, opIsType2, true}, - {opNor, opIsType2, true}, - {opNot, opIsType2, true}, - {opObjectType, opIsType2, true}, - {opOr, opIsType2, true}, - {opPackage, opIsType2, true}, - {opVarPackage, opIsType2, true}, - {opRefOf, opIsType2, true}, - {opShiftLeft, opIsType2, true}, - {opShiftRight, opIsType2, true}, - {opSizeOf, opIsType2, true}, - {opStore, opIsType2, true}, - {opSubtract, opIsType2, true}, - {opTimer, opIsType2, true}, - {opToBCD, opIsType2, true}, - {opToBuffer, opIsType2, true}, - {opToDecimalString, opIsType2, true}, - {opToHexString, opIsType2, true}, - {opToInteger, opIsType2, true}, - {opToString, opIsType2, true}, - {opWait, opIsType2, true}, - {opXor, opIsType2, true}, - {opBytePrefix, opIsType2, false}, - // opIsDataObject - {opBytePrefix, opIsDataObject, true}, - {opWordPrefix, opIsDataObject, true}, - {opDwordPrefix, opIsDataObject, true}, - {opQwordPrefix, opIsDataObject, true}, - {opStringPrefix, opIsDataObject, true}, - {opZero, opIsDataObject, true}, - {opOne, opIsDataObject, true}, - {opOnes, opIsDataObject, true}, - {opRevision, opIsDataObject, true}, - {opBuffer, opIsDataObject, true}, - {opPackage, opIsDataObject, true}, - {opVarPackage, opIsDataObject, true}, - {opLor, opIsDataObject, false}, - // opIsBufferField - {opCreateField, opIsBufferField, true}, - {opCreateBitField, opIsBufferField, true}, - {opCreateByteField, opIsBufferField, true}, - {opCreateWordField, opIsBufferField, true}, - {opCreateDWordField, opIsBufferField, true}, - {opCreateQWordField, opIsBufferField, true}, - {opRevision, opIsBufferField, false}, - } - - for specIndex, spec := range specs { - if got := spec.testFn(spec.op); got != spec.want { - t.Errorf("[spec %d] opcode %q: expected to get %t; got %t", specIndex, spec.op, spec.want, got) - } - } -} - -func TestOpArgFlagToString(t *testing.T) { - specs := map[opArgFlag]string{ - opArgTermList: "opArgTermList", - opArgTermObj: "opArgTermObj", - opArgByteList: "opArgByteList", - opArgPackage: "opArgPackage", - opArgString: "opArgString", - opArgByteData: "opArgByteData", - opArgWord: "opArgWord", - opArgDword: "opArgDword", - opArgQword: "opArgQword", - opArgNameString: "opArgNameString", - opArgSuperName: "opArgSuperName", - opArgSimpleName: "opArgSimpleName", - opArgDataRefObj: "opArgDataRefObj", - opArgTarget: "opArgTarget", - opArgFieldList: "opArgFieldList", - opArgFlag(0xff): "", - } - - for flag, want := range specs { - if got := flag.String(); got != want { - t.Errorf("expected %q; got %q", want, got) - } - } -} - -// TestFindUnmappedOpcodes is a helper test that pinpoints opcodes that have -// not yet been mapped via an opcode table. This test will be removed once all -// opcodes are supported. -func TestFindUnmappedOpcodes(t *testing.T) { - //t.SkipNow() - for opIndex, opRef := range opcodeMap { - if opRef != badOpcode { - continue - } - - for tabIndex, info := range opcodeTable { - if uint16(info.op) == uint16(opIndex) { - t.Errorf("set opcodeMap[0x%02x] = 0x%02x // %s\n", opIndex, tabIndex, info.op.String()) - break - } - } - } - - for opIndex, opRef := range extendedOpcodeMap { - // 0xff (opOnes) is defined in opcodeTable - if opRef != badOpcode || opIndex == 0 { - continue - } - - opIndex += 0xff - for tabIndex, info := range opcodeTable { - if uint16(info.op) == uint16(opIndex) { - t.Errorf("set extendedOpcodeMap[0x%02x] = 0x%02x // %s\n", opIndex-0xff, tabIndex, info.op.String()) - break - } - } - } -} diff --git a/src/gopheros/device/acpi/aml/parser/opcode_table.go b/src/gopheros/device/acpi/aml/parser/opcode_table.go new file mode 100644 index 0000000..df983dc --- /dev/null +++ b/src/gopheros/device/acpi/aml/parser/opcode_table.go @@ -0,0 +1,339 @@ +package parser + +import "gopheros/device/acpi/aml/entity" + +const ( + badOpcode = 0xff + extOpPrefix = 0x5b +) + +// objType represents the object types that are supported by the AML parser. +type objType uint8 + +// The list of AML object types. +const ( + objTypeAny objType = iota + objTypeInteger + objTypeString + objTypeBuffer + objTypePackage + objTypeDevice + objTypeEvent + objTypeMethod + objTypeMutex + objTypeRegion + objTypePower + objTypeProcessor + objTypeThermal + objTypeBufferField + objTypeLocalRegionField + objTypeLocalBankField + objTypeLocalReference + objTypeLocalAlias + objTypeLocalScope + objTypeLocalVariable + objTypeMethodArgument +) + +// opFlag specifies a list of OR-able flags that describe the object +// type/attributes generated by a particular opcode. +type opFlag uint16 + +const ( + opFlagNone opFlag = 1 << iota + opFlagHasPkgLen + opFlagNamed + opFlagConstant + opFlagReference + opFlagArithmetic + opFlagCreate + opFlagReturn + opFlagExecutable + opFlagNoOp + opFlagScoped +) + +// is returns true if f is set in this opFlag. +func (fl opFlag) is(f opFlag) bool { + return (fl & f) != 0 +} + +// opArgFlags encodes up to 7 opArgFlag values in a uint64 value. +type opArgFlags uint64 + +// argCount returns the number of encoded args in the given flag. +func (fl opArgFlags) argCount() (count uint8) { + // Each argument is specified using 8 bits with 0x0 indicating the end of the + // argument list + for ; fl&0xf != 0; fl, count = fl>>8, count+1 { + } + + return count +} + +// arg returns the arg flags for argument "num" where num is the 0-based index +// of the argument to return. The allowed values for num are 0-6. +func (fl opArgFlags) arg(num uint8) opArgFlag { + return opArgFlag((fl >> (num * 8)) & 0xf) +} + +// contains returns true if the arg flags contain any argument with type x. +func (fl opArgFlags) contains(x opArgFlag) bool { + // Each argument is specified using 8 bits with 0x0 indicating the end of the + // argument list + for ; fl&0xf != 0; fl >>= 8 { + if opArgFlag(fl&0xf) == x { + return true + } + } + + return false +} + +// opArgFlag represents the type of an argument expected by a particular opcode. +type opArgFlag uint8 + +// The list of supported opArgFlag values. +const ( + _ opArgFlag = iota + opArgTermList + opArgTermObj + opArgByteList + opArgPackage + opArgString + opArgByteData + opArgWord + opArgDword + opArgQword + opArgNameString + opArgSuperName + opArgSimpleName + opArgDataRefObj + opArgTarget + opArgFieldList +) + +func makeArg0() opArgFlags { return 0 } +func makeArg1(arg0 opArgFlag) opArgFlags { return opArgFlags(arg0) } +func makeArg2(arg0, arg1 opArgFlag) opArgFlags { return opArgFlags(arg1)<<8 | opArgFlags(arg0) } +func makeArg3(arg0, arg1, arg2 opArgFlag) opArgFlags { + return opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) +} +func makeArg4(arg0, arg1, arg2, arg3 opArgFlag) opArgFlags { + return opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) +} +func makeArg5(arg0, arg1, arg2, arg3, arg4 opArgFlag) opArgFlags { + return opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) +} +func makeArg6(arg0, arg1, arg2, arg3, arg4, arg5 opArgFlag) opArgFlags { + return opArgFlags(arg5)<<40 | opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) +} +func makeArg7(arg0, arg1, arg2, arg3, arg4, arg5, arg6 opArgFlag) opArgFlags { + return opArgFlags(arg6)<<48 | opArgFlags(arg5)<<40 | opArgFlags(arg4)<<32 | opArgFlags(arg3)<<24 | opArgFlags(arg2)<<16 | opArgFlags(arg1)<<8 | opArgFlags(arg0) +} + +// opcodeInfo contains all known information about an opcode, +// its argument count and types as well as the type of object +// represented by it. +type opcodeInfo struct { + op entity.AMLOpcode + objType objType + + flags opFlag + argFlags opArgFlags +} + +// The opcode table contains all opcode-related information that the parser knows. +// This table is modeled after a similar table used in the acpica implementation. +var opcodeTable = []opcodeInfo{ + /*0x00*/ {entity.OpZero, objTypeInteger, opFlagConstant, makeArg0()}, + /*0x01*/ {entity.OpOne, objTypeInteger, opFlagConstant, makeArg0()}, + /*0x02*/ {entity.OpAlias, objTypeLocalAlias, opFlagNamed, makeArg2(opArgNameString, opArgNameString)}, + /*0x03*/ {entity.OpName, objTypeAny, opFlagNamed, makeArg2(opArgNameString, opArgDataRefObj)}, + /*0x04*/ {entity.OpBytePrefix, objTypeInteger, opFlagConstant, makeArg1(opArgByteData)}, + /*0x05*/ {entity.OpWordPrefix, objTypeInteger, opFlagConstant, makeArg1(opArgWord)}, + /*0x06*/ {entity.OpDwordPrefix, objTypeInteger, opFlagConstant, makeArg1(opArgDword)}, + /*0x07*/ {entity.OpStringPrefix, objTypeString, opFlagConstant, makeArg1(opArgString)}, + /*0x08*/ {entity.OpQwordPrefix, objTypeInteger, opFlagConstant, makeArg1(opArgQword)}, + /*0x09*/ {entity.OpScope, objTypeLocalScope, opFlagNamed, makeArg2(opArgNameString, opArgTermList)}, + /*0x0a*/ {entity.OpBuffer, objTypeBuffer, opFlagHasPkgLen, makeArg2(opArgTermObj, opArgByteList)}, + /*0x0b*/ {entity.OpPackage, objTypePackage, opFlagNone, makeArg2(opArgByteData, opArgTermList)}, + /*0x0c*/ {entity.OpVarPackage, objTypePackage, opFlagNone, makeArg2(opArgByteData, opArgTermList)}, + /*0x0d*/ {entity.OpMethod, objTypeMethod, opFlagNamed | opFlagScoped, makeArg3(opArgNameString, opArgByteData, opArgTermList)}, + /*0x0e*/ {entity.OpExternal, objTypeAny, opFlagNamed, makeArg3(opArgNameString, opArgByteData, opArgByteData)}, + /*0x0f*/ {entity.OpLocal0, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0x10*/ {entity.OpLocal1, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0x11*/ {entity.OpLocal2, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0x12*/ {entity.OpLocal3, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0x13*/ {entity.OpLocal4, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0120*/ {entity.OpLocal5, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0x15*/ {entity.OpLocal6, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0x16*/ {entity.OpLocal7, objTypeLocalVariable, opFlagExecutable, makeArg0()}, + /*0x17*/ {entity.OpArg0, objTypeMethodArgument, opFlagExecutable, makeArg0()}, + /*0x18*/ {entity.OpArg1, objTypeMethodArgument, opFlagExecutable, makeArg0()}, + /*0x19*/ {entity.OpArg2, objTypeMethodArgument, opFlagExecutable, makeArg0()}, + /*0x1a*/ {entity.OpArg3, objTypeMethodArgument, opFlagExecutable, makeArg0()}, + /*0x1b*/ {entity.OpArg4, objTypeMethodArgument, opFlagExecutable, makeArg0()}, + /*0x1c*/ {entity.OpArg5, objTypeMethodArgument, opFlagExecutable, makeArg0()}, + /*0x1d*/ {entity.OpArg6, objTypeMethodArgument, opFlagExecutable, makeArg0()}, + /*0x1e*/ {entity.OpStore, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgSuperName)}, + /*0x1f*/ {entity.OpRefOf, objTypeAny, opFlagReference | opFlagExecutable, makeArg1(opArgSuperName)}, + /*0x20*/ {entity.OpAdd, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x21*/ {entity.OpConcat, objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x22*/ {entity.OpSubtract, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x23*/ {entity.OpIncrement, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgSuperName)}, + /*0x24*/ {entity.OpDecrement, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgSuperName)}, + /*0x25*/ {entity.OpMultiply, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x26*/ {entity.OpDivide, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTarget, opArgTarget)}, + /*0x27*/ {entity.OpShiftLeft, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x28*/ {entity.OpShiftRight, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x29*/ {entity.OpAnd, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x2a*/ {entity.OpNand, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x2b*/ {entity.OpOr, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x2c*/ {entity.OpNor, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x2d*/ {entity.OpXor, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x2e*/ {entity.OpNot, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x2f*/ {entity.OpFindSetLeftBit, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x30*/ {entity.OpFindSetRightBit, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x31*/ {entity.OpDerefOf, objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, + /*0x32*/ {entity.OpConcatRes, objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x33*/ {entity.OpMod, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x34*/ {entity.OpNotify, objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgTermObj)}, + /*0x35*/ {entity.OpSizeOf, objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, + /*0x36*/ {entity.OpIndex, objTypeAny, opFlagExecutable, makeArg3(opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x37*/ {entity.OpMatch, objTypeAny, opFlagExecutable, makeArg6(opArgTermObj, opArgByteData, opArgTermObj, opArgByteData, opArgTermObj, opArgTermObj)}, + /*0x38*/ {entity.OpCreateDWordField, objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, + /*0x39*/ {entity.OpCreateWordField, objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, + /*0x3a*/ {entity.OpCreateByteField, objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, + /*0x3b*/ {entity.OpCreateBitField, objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, + /*0x3c*/ {entity.OpObjectType, objTypeAny, opFlagNone, makeArg1(opArgSuperName)}, + /*0x3d*/ {entity.OpCreateQWordField, objTypeBufferField, opFlagNamed | opFlagCreate, makeArg3(opArgTermObj, opArgTermObj, opArgNameString)}, + /*0x3e*/ {entity.OpLand, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, + /*0x3f*/ {entity.OpLor, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, + /*0x40*/ {entity.OpLnot, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg1(opArgTermObj)}, + /*0x41*/ {entity.OpLEqual, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, + /*0x42*/ {entity.OpLGreater, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, + /*0x43*/ {entity.OpLLess, objTypeAny, opFlagArithmetic | opFlagExecutable, makeArg2(opArgTermObj, opArgTermObj)}, + /*0x44*/ {entity.OpToBuffer, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x45*/ {entity.OpToDecimalString, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x46*/ {entity.OpToHexString, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x47*/ {entity.OpToInteger, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x48*/ {entity.OpToString, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x49*/ {entity.OpCopyObject, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgSimpleName)}, + /*0x4a*/ {entity.OpMid, objTypeAny, opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTermObj, opArgTarget)}, + /*0x4b*/ {entity.OpContinue, objTypeAny, opFlagExecutable, makeArg0()}, + /*0x4c*/ {entity.OpIf, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTermList)}, + /*0x4d*/ {entity.OpElse, objTypeAny, opFlagExecutable | opFlagScoped, makeArg1(opArgTermList)}, + /*0x4e*/ {entity.OpWhile, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTermList)}, + /*0x4f*/ {entity.OpNoop, objTypeAny, opFlagNoOp, makeArg0()}, + /*0x50*/ {entity.OpReturn, objTypeAny, opFlagReturn, makeArg1(opArgTermObj)}, + /*0x51*/ {entity.OpBreak, objTypeAny, opFlagExecutable, makeArg0()}, + /*0x52*/ {entity.OpBreakPoint, objTypeAny, opFlagNoOp, makeArg0()}, + /*0x53*/ {entity.OpOnes, objTypeInteger, opFlagConstant, makeArg0()}, + /*0x54*/ {entity.OpMutex, objTypeMutex, opFlagNamed, makeArg2(opArgNameString, opArgByteData)}, + /*0x55*/ {entity.OpEvent, objTypeEvent, opFlagNamed, makeArg1(opArgNameString)}, + /*0x56*/ {entity.OpCondRefOf, objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgSuperName)}, + /*0x57*/ {entity.OpCreateField, objTypeBufferField, opFlagExecutable, makeArg4(opArgTermObj, opArgTermObj, opArgTermObj, opArgNameString)}, + /*0x58*/ {entity.OpLoadTable, objTypeAny, opFlagExecutable, makeArg7(opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj, opArgTermObj)}, + /*0x59*/ {entity.OpLoad, objTypeAny, opFlagExecutable, makeArg2(opArgNameString, opArgSuperName)}, + /*0x5a*/ {entity.OpStall, objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, + /*0x5b*/ {entity.OpSleep, objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, + /*0x5c*/ {entity.OpAcquire, objTypeAny, opFlagExecutable, makeArg2(opArgNameString, opArgSuperName)}, + /*0x5d*/ {entity.OpSignal, objTypeAny, opFlagExecutable, makeArg1(opArgTermObj)}, + /*0x5e*/ {entity.OpWait, objTypeAny, opFlagExecutable, makeArg2(opArgSuperName, opArgTermObj)}, + /*0x5f*/ {entity.OpReset, objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, + /*0x60*/ {entity.OpRelease, objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, + /*0x61*/ {entity.OpFromBCD, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x62*/ {entity.OpToBCD, objTypeAny, opFlagExecutable, makeArg2(opArgTermObj, opArgTarget)}, + /*0x63*/ {entity.OpUnload, objTypeAny, opFlagExecutable, makeArg1(opArgSuperName)}, + /*0x64*/ {entity.OpRevision, objTypeInteger, opFlagConstant | opFlagExecutable, makeArg0()}, + /*0x65*/ {entity.OpDebug, objTypeLocalReference, opFlagExecutable, makeArg0()}, + /*0x66*/ {entity.OpFatal, objTypeAny, opFlagExecutable, makeArg3(opArgByteData, opArgDword, opArgTermObj)}, + /*0x67*/ {entity.OpTimer, objTypeAny, opFlagNone, makeArg0()}, + /*0x68*/ {entity.OpOpRegion, objTypeRegion, opFlagNamed, makeArg4(opArgNameString, opArgByteData, opArgTermObj, opArgTermObj)}, + /*0x69*/ {entity.OpField, objTypeAny, opFlagNone, makeArg3(opArgNameString, opArgByteData, opArgFieldList)}, + /*0x6a*/ {entity.OpDevice, objTypeDevice, opFlagNamed | opFlagScoped, makeArg2(opArgNameString, opArgTermList)}, + /*0x6b*/ {entity.OpProcessor, objTypeProcessor, opFlagNamed | opFlagScoped, makeArg5(opArgNameString, opArgByteData, opArgDword, opArgByteData, opArgTermList)}, + /*0x6c*/ {entity.OpPowerRes, objTypePower, opFlagNamed | opFlagScoped, makeArg4(opArgNameString, opArgByteData, opArgWord, opArgTermList)}, + /*0x6d*/ {entity.OpThermalZone, objTypeThermal, opFlagNamed | opFlagScoped, makeArg2(opArgNameString, opArgTermList)}, + /*0x6e*/ {entity.OpIndexField, objTypeAny, opFlagNone, makeArg4(opArgNameString, opArgNameString, opArgByteData, opArgFieldList)}, + /*0x6f*/ {entity.OpBankField, objTypeLocalBankField, opFlagNamed, makeArg5(opArgNameString, opArgNameString, opArgTermObj, opArgByteData, opArgFieldList)}, + /*0x70*/ {entity.OpDataRegion, objTypeLocalRegionField, opFlagNamed, makeArg4(opArgNameString, opArgTermObj, opArgTermObj, opArgTermObj)}, +} + +// opcodeMap maps an AML opcode to an entry in the opcode table. Entries with +// the value 0xff indicate an invalid/unsupported opcode. +var opcodeMap = [256]uint8{ + /* 0 1 2 3 4 5 6 7*/ + /*0x00 - 0x07*/ 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x02, 0xff, + /*0x08 - 0x0f*/ 0x03, 0xff, 0x04, 0x05, 0x06, 0x07, 0x08, 0xff, + /*0x10 - 0x17*/ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xff, 0xff, + /*0x18 - 0x1f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x20 - 0x27*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x28 - 0x2f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x30 - 0x37*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x38 - 0x3f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x40 - 0x47*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x48 - 0x4f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x50 - 0x57*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x58 - 0x5f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x60 - 0x67*/ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + /*0x68 - 0x6f*/ 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0xff, + /*0x70 - 0x77*/ 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, + /*0x78 - 0x7f*/ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, + /*0x80 - 0x87*/ 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + /*0x88 - 0x8f*/ 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, + /*0x90 - 0x97*/ 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, + /*0x98 - 0x9f*/ 0x46, 0x47, 0x48, 0x49, 0x4a, 0x49, 0x4a, 0x4b, + /*0xa0 - 0xa7*/ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0xff, 0xff, + /*0xa8 - 0xaf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xb0 - 0xb7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xb8 - 0xbf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xc0 - 0xc7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xc8 - 0xcf*/ 0xff, 0xff, 0xff, 0xff, 0x52, 0xff, 0xff, 0xff, + /*0xd0 - 0xd7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xd8 - 0xdf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xe0 - 0xe7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xe8 - 0xef*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xf0 - 0xf7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xf8 - 0xff*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x53, +} + +// extendedOpcodeMap maps an AML extended opcode (extOpPrefix + code) to an +// entry in the opcode table. Entries with the value 0xff indicate an +// invalid/unsupported opcode. +var extendedOpcodeMap = [256]uint8{ + /* 0 1 2 3 4 5 6 7*/ + /*0x00 - 0x07*/ 0xff, 0x54, 0x55, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x08 - 0x0f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x10 - 0x17*/ 0xff, 0xff, 0x56, 0x57, 0xff, 0xff, 0xff, 0xff, + /*0x18 - 0x1f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x58, + /*0x20 - 0x27*/ 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, + /*0x28 - 0x2f*/ 0x61, 0x62, 0x63, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x30 - 0x37*/ 0x64, 0x65, 0x66, 0x67, 0xff, 0xff, 0xff, 0xff, + /*0x38 - 0x3f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x40 - 0x47*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x48 - 0x4f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x50 - 0x57*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x58 - 0x5f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x60 - 0x67*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x68 - 0x6f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x70 - 0x77*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x78 - 0x7f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x80 - 0x87*/ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + /*0x88 - 0x8f*/ 0x70, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x90 - 0x97*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0x98 - 0x9f*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xa0 - 0xa7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xa8 - 0xaf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xb0 - 0xb7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xb8 - 0xbf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xc0 - 0xc7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xc8 - 0xcf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xd0 - 0xd7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xd8 - 0xdf*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xe0 - 0xe7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xe8 - 0xef*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xf0 - 0xf7*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /*0xf8 - 0xff*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x53, +} diff --git a/src/gopheros/device/acpi/aml/parser/opcode_test.go b/src/gopheros/device/acpi/aml/parser/opcode_test.go new file mode 100644 index 0000000..ee5bdd5 --- /dev/null +++ b/src/gopheros/device/acpi/aml/parser/opcode_test.go @@ -0,0 +1,35 @@ +package parser + +import "testing" + +// TestFindUnmappedOpcodes is a helper test that pinpoints opcodes that have +// not yet been mapped via an opcode table. +func TestFindUnmappedOpcodes(t *testing.T) { + for opIndex, opRef := range opcodeMap { + if opRef != badOpcode { + continue + } + + for tabIndex, info := range opcodeTable { + if uint16(info.op) == uint16(opIndex) { + t.Errorf("set opcodeMap[0x%02x] = 0x%02x // %s\n", opIndex, tabIndex, info.op.String()) + break + } + } + } + + for opIndex, opRef := range extendedOpcodeMap { + // 0xff (opOnes) is defined in opcodeTable + if opRef != badOpcode || opIndex == 0 { + continue + } + + opIndex += 0xff + for tabIndex, info := range opcodeTable { + if uint16(info.op) == uint16(opIndex) { + t.Errorf("set extendedOpcodeMap[0x%02x] = 0x%02x // %s\n", opIndex-0xff, tabIndex, info.op.String()) + break + } + } + } +} From e7f203f06ac689dccbb5b22014a4371c1a8588fb Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Sun, 17 Dec 2017 16:12:01 +0000 Subject: [PATCH 6/9] acpi: move stream reader implementation into parser pkg --- src/gopheros/device/acpi/aml/{ => parser}/stream_reader.go | 2 +- src/gopheros/device/acpi/aml/{ => parser}/stream_reader_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/gopheros/device/acpi/aml/{ => parser}/stream_reader.go (99%) rename src/gopheros/device/acpi/aml/{ => parser}/stream_reader_test.go (99%) diff --git a/src/gopheros/device/acpi/aml/stream_reader.go b/src/gopheros/device/acpi/aml/parser/stream_reader.go similarity index 99% rename from src/gopheros/device/acpi/aml/stream_reader.go rename to src/gopheros/device/acpi/aml/parser/stream_reader.go index 31e6e5b..4b29d67 100644 --- a/src/gopheros/device/acpi/aml/stream_reader.go +++ b/src/gopheros/device/acpi/aml/parser/stream_reader.go @@ -1,4 +1,4 @@ -package aml +package parser import ( "errors" diff --git a/src/gopheros/device/acpi/aml/stream_reader_test.go b/src/gopheros/device/acpi/aml/parser/stream_reader_test.go similarity index 99% rename from src/gopheros/device/acpi/aml/stream_reader_test.go rename to src/gopheros/device/acpi/aml/parser/stream_reader_test.go index 560a7ca..8c6c701 100644 --- a/src/gopheros/device/acpi/aml/stream_reader_test.go +++ b/src/gopheros/device/acpi/aml/parser/stream_reader_test.go @@ -1,4 +1,4 @@ -package aml +package parser import ( "io" From 41eae61c9f5bd754acc083bab5742349c99fef59 Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Fri, 29 Dec 2017 09:12:08 +0000 Subject: [PATCH 7/9] acpi: update AML parser to use the new entities --- src/gopheros/device/acpi/aml/entity/entity.go | 1 + .../device/acpi/aml/entity/entity_test.go | 31 ++ src/gopheros/device/acpi/aml/entity/opcode.go | 12 +- .../device/acpi/aml/entity/opcode_test.go | 8 - .../device/acpi/aml/entity/visitor_test.go | 2 +- .../device/acpi/aml/parser/opcode_table.go | 3 +- .../device/acpi/aml/{ => parser}/parser.go | 379 ++++++++---------- .../acpi/aml/{ => parser}/parser_test.go | 232 ++++++----- .../table/tabletest/parser-testsuite-DSDT.aml | Bin 488 -> 627 bytes .../table/tabletest/parser-testsuite-DSDT.dsl | 185 +++++---- 10 files changed, 441 insertions(+), 412 deletions(-) rename src/gopheros/device/acpi/aml/{ => parser}/parser.go (71%) rename src/gopheros/device/acpi/aml/{ => parser}/parser_test.go (74%) diff --git a/src/gopheros/device/acpi/aml/entity/entity.go b/src/gopheros/device/acpi/aml/entity/entity.go index 6333c89..03a81ad 100644 --- a/src/gopheros/device/acpi/aml/entity/entity.go +++ b/src/gopheros/device/acpi/aml/entity/entity.go @@ -47,6 +47,7 @@ type Container interface { Last() Entity } +// FieldAccessTypeProvider is an interface implemented by all field entities. type FieldAccessTypeProvider interface { // DefaultAccessType returns the default FieldAccessType for any field unit // defined by this field. diff --git a/src/gopheros/device/acpi/aml/entity/entity_test.go b/src/gopheros/device/acpi/aml/entity/entity_test.go index 692f419..97d26f3 100644 --- a/src/gopheros/device/acpi/aml/entity/entity_test.go +++ b/src/gopheros/device/acpi/aml/entity/entity_test.go @@ -79,6 +79,19 @@ func TestEntityMethods(t *testing.T) { t.Fatalf("expected parent not to have any child nodes; got %d", got) } }) + + t.Run("FieldAccessTypeProvider implementers", func(t *testing.T) { + for specIndex, spec := range specs { + provider, ok := spec.ent.(FieldAccessTypeProvider) + if !ok { + continue + } + + if exp, got := FieldAccessTypeAny, provider.DefaultAccessType(); got != exp { + t.Errorf("[spec %d] expected provider to return access type: %d; got %d", specIndex, exp, got) + } + } + }) } func TestEntityArgAssignment(t *testing.T) { @@ -160,6 +173,24 @@ func TestEntityArgAssignment(t *testing.T) { []interface{}{NewConst(OpDwordPrefix, 2, uint64(42))}, false, }, + { + NewField(2), + []interface{}{"REG0", uint64(128)}, + nil, // Field populates its internal state using the first 2 args + true, + }, + { + NewIndexField(2), + []interface{}{"REG0", "DAT0", uint64(128)}, + nil, // IndexField populates its internal state using the first 3 args + true, + }, + { + NewBankField(2), + []interface{}{"REG0", "BNK0", uint64(0xf00f), uint64(128)}, + nil, // BankField populates its internal state using the first 4 args + true, + }, } nextSpec: diff --git a/src/gopheros/device/acpi/aml/entity/opcode.go b/src/gopheros/device/acpi/aml/entity/opcode.go index a9c608c..0a3abf2 100644 --- a/src/gopheros/device/acpi/aml/entity/opcode.go +++ b/src/gopheros/device/acpi/aml/entity/opcode.go @@ -6,6 +6,7 @@ package entity // representation of the opcode values. type AMLOpcode uint16 +// List of AML opcodes const ( // Regular opcode list OpZero = AMLOpcode(0x00) @@ -427,14 +428,3 @@ func OpIsType2(op AMLOpcode) bool { return false } } - -// OpIsBufferField returens true if this opcode describes a -// buffer field creation operation. -func OpIsBufferField(op AMLOpcode) bool { - switch op { - case OpCreateField, OpCreateBitField, OpCreateByteField, OpCreateWordField, OpCreateDWordField, OpCreateQWordField: - return true - default: - return false - } -} diff --git a/src/gopheros/device/acpi/aml/entity/opcode_test.go b/src/gopheros/device/acpi/aml/entity/opcode_test.go index 6f89051..23c0bdd 100644 --- a/src/gopheros/device/acpi/aml/entity/opcode_test.go +++ b/src/gopheros/device/acpi/aml/entity/opcode_test.go @@ -227,14 +227,6 @@ func TestOpcodeIsX(t *testing.T) { {OpPackage, OpIsDataObject, true}, {OpVarPackage, OpIsDataObject, true}, {OpLor, OpIsDataObject, false}, - // OpIsBufferField - {OpCreateField, OpIsBufferField, true}, - {OpCreateBitField, OpIsBufferField, true}, - {OpCreateByteField, OpIsBufferField, true}, - {OpCreateWordField, OpIsBufferField, true}, - {OpCreateDWordField, OpIsBufferField, true}, - {OpCreateQWordField, OpIsBufferField, true}, - {OpRevision, OpIsBufferField, false}, } for specIndex, spec := range specs { diff --git a/src/gopheros/device/acpi/aml/entity/visitor_test.go b/src/gopheros/device/acpi/aml/entity/visitor_test.go index 3c7aa4e..d4368b7 100644 --- a/src/gopheros/device/acpi/aml/entity/visitor_test.go +++ b/src/gopheros/device/acpi/aml/entity/visitor_test.go @@ -8,7 +8,7 @@ func TestScopeVisit(t *testing.T) { stopRecursing := func(Entity) bool { return false } // Append special entities under IDE0 - root := NewScope(tableHandle, "IDE0") + root := NewScope(OpScope, tableHandle, "IDE0") root.Append(NewDevice(tableHandle, "DEV0")) root.Append(NewProcessor(tableHandle, "FOO0")) root.Append(NewProcessor(tableHandle, "FOO0")) diff --git a/src/gopheros/device/acpi/aml/parser/opcode_table.go b/src/gopheros/device/acpi/aml/parser/opcode_table.go index df983dc..404592f 100644 --- a/src/gopheros/device/acpi/aml/parser/opcode_table.go +++ b/src/gopheros/device/acpi/aml/parser/opcode_table.go @@ -55,7 +55,7 @@ const ( // is returns true if f is set in this opFlag. func (fl opFlag) is(f opFlag) bool { - return (fl & f) != 0 + return (fl & f) == f } // opArgFlags encodes up to 7 opArgFlag values in a uint64 value. @@ -99,7 +99,6 @@ const ( opArgTermList opArgTermObj opArgByteList - opArgPackage opArgString opArgByteData opArgWord diff --git a/src/gopheros/device/acpi/aml/parser.go b/src/gopheros/device/acpi/aml/parser/parser.go similarity index 71% rename from src/gopheros/device/acpi/aml/parser.go rename to src/gopheros/device/acpi/aml/parser/parser.go index 2b25bdf..7daa950 100644 --- a/src/gopheros/device/acpi/aml/parser.go +++ b/src/gopheros/device/acpi/aml/parser/parser.go @@ -1,6 +1,7 @@ -package aml +package parser import ( + "gopheros/device/acpi/aml/entity" "gopheros/device/acpi/table" "gopheros/kernel" "gopheros/kernel/kfmt" @@ -17,8 +18,8 @@ var ( type Parser struct { r amlStreamReader errWriter io.Writer - root ScopeEntity - scopeStack []ScopeEntity + root entity.Container + scopeStack []entity.Container tableName string tableHandle uint8 @@ -30,7 +31,7 @@ type Parser struct { } // NewParser returns a new AML parser instance. -func NewParser(errWriter io.Writer, rootEntity ScopeEntity) *Parser { +func NewParser(errWriter io.Writer, rootEntity entity.Container) *Parser { return &Parser{ errWriter: errWriter, root: rootEntity, @@ -65,25 +66,29 @@ func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDT } p.scopeExit() - // Pass 3: check parents and resolve forward references + // Pass 3: check parents and resolve symbol references var resolveFailed bool - scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool { + entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool { // Skip method bodies; their contents will be lazily resolved by the interpreter - if _, isMethod := ent.(*Method); isMethod { + if _, isMethod := ent.(*entity.Method); isMethod { return false } // Populate parents for any entity args that are also entities but are not // linked to a parent (e.g. a package inside a named entity). - for _, arg := range ent.getArgs() { - if argEnt, isArgEnt := arg.(Entity); isArgEnt && argEnt.Parent() == nil { - argEnt.setParent(ent.Parent()) + for _, arg := range ent.Args() { + if argEnt, isArgEnt := arg.(entity.Entity); isArgEnt && argEnt.Parent() == nil { + argEnt.SetParent(ent.Parent()) } } - if res, ok := ent.(resolver); ok && !res.Resolve(p.errWriter, p.root) { - resolveFailed = true - return false + // Resolve any symbol references + if lazyRef, ok := ent.(entity.LazyRefResolver); ok { + if err := lazyRef.ResolveSymbolRefs(p.root); err != nil { + kfmt.Fprintf(p.errWriter, "%s\n", err.Message) + resolveFailed = true + return false + } } return true @@ -117,7 +122,7 @@ func (p *Parser) detectMethodDeclarations() { continue } - if next.op != opMethod { + if next.op != entity.OpMethod { continue } @@ -187,11 +192,11 @@ func (p *Parser) parseObj() bool { } // If we encounter a named scope we need to look it up and parse the arg list relative to it - switch info.op { - case opScope: + switch { + case info.op == entity.OpScope: return p.parseScope(curOffset + pkgLen) - case opDevice, opMethod: - return p.parseNamespacedObj(info.op, curOffset+pkgLen) + case info.flags.is(opFlagNamed | opFlagScoped): + return p.parseNamespacedObj(info, curOffset+pkgLen) } // Create appropriate object for opcode type and attach it to current scope unless it is @@ -217,33 +222,22 @@ func (p *Parser) parseObj() bool { } // finalizeObj applies post-parse logic for special object types. -func (p *Parser) finalizeObj(op opcode, obj Entity) bool { - obj.setTableHandle(p.tableHandle) - +func (p *Parser) finalizeObj(op entity.AMLOpcode, obj entity.Entity) bool { switch op { - case opElse: + case entity.OpElse: // If this is an else block we need to append it as an argument to the // If block // Pop Else block of the current scope curScope := p.scopeCurrent() - curScope.removeChild(curScope.lastChild()) - prevObj := curScope.lastChild() - if prevObj.getOpcode() != opIf { + curScope.Remove(curScope.Last()) + prevObj := curScope.Last() + if prevObj.Opcode() != entity.OpIf { kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] encountered else block without a matching if block\n", p.tableName, p.r.Offset()) return false } // If predicate(0) then(1) else(2) - prevObj.setArg(2, obj) - case opDevice: - // Build method map - dev := obj.(*Device) - dev.methodMap = make(map[string]*Method) - scopeVisit(0, dev, EntityTypeMethod, func(_ int, ent Entity) bool { - method := ent.(*Method) - dev.methodMap[method.name] = method - return false - }) + prevObj.SetArg(2, obj) } return true @@ -262,14 +256,14 @@ func (p *Parser) parseScope(maxReadOffset uint32) bool { return false } - target := scopeFind(p.scopeCurrent(), p.root, name) + target := entity.FindInScope(p.scopeCurrent(), p.root, name) if target == nil { kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] undefined scope: %s\n", p.tableName, p.r.Offset(), name) return false } - switch target.getOpcode() { - case opDevice, opProcessor, opThermalZone, opPowerRes: + switch target.Opcode() { + case entity.OpDevice, entity.OpProcessor, entity.OpThermalZone, entity.OpPowerRes: // ok default: // Only allow if this is a named scope @@ -279,7 +273,7 @@ func (p *Parser) parseScope(maxReadOffset uint32) bool { } } - p.scopeEnter(target.(ScopeEntity)) + p.scopeEnter(target.(entity.Container)) ok = p.parseObjList(maxReadOffset) p.scopeExit() @@ -287,48 +281,50 @@ func (p *Parser) parseScope(maxReadOffset uint32) bool { } // parseNamespacedObj reads a scope target name from the AML bytestream, -// attaches the device or method (depending on the opcode) object to the -// correct parent scope, enters the device scope and parses the object list -// contained in the device definition. -func (p *Parser) parseNamespacedObj(op opcode, maxReadOffset uint32) bool { +// attaches the appropriate object depending on the opcode to the correct +// parent scope and then parses any contained objects. The contained objects +// will be appended inside the newly constructed scope. +func (p *Parser) parseNamespacedObj(info *opcodeInfo, maxReadOffset uint32) bool { scopeExpr, ok := p.parseNameString() if !ok { return false } - parent, name := scopeResolvePath(p.scopeCurrent(), p.root, scopeExpr) + parent, name := entity.ResolveScopedPath(p.scopeCurrent(), p.root, scopeExpr) if parent == nil { kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] undefined scope target: %s (current scope: %s)\n", p.tableName, p.r.Offset(), scopeExpr, p.scopeCurrent().Name()) return false } - var obj ScopeEntity - switch op { - case opDevice: - obj = &Device{scopeEntity: scopeEntity{name: name}} - case opMethod: - m := &Method{scopeEntity: scopeEntity{name: name}} - - flags, flagOk := p.parseNumConstant(1) - if !flagOk { - return false - } - m.argCount = (uint8(flags) & 0x7) // bits[0:2] - m.serialized = (uint8(flags)>>3)&0x1 == 0x1 // bit 3 - m.syncLevel = (uint8(flags) >> 4) & 0xf // bits[4:7] - - obj = m + var obj entity.Container + switch info.op { + case entity.OpDevice: + obj = entity.NewDevice(p.tableHandle, name) + case entity.OpProcessor: + obj = entity.NewProcessor(p.tableHandle, name) + case entity.OpPowerRes: + obj = entity.NewPowerResource(p.tableHandle, name) + case entity.OpThermalZone: + obj = entity.NewThermalZone(p.tableHandle, name) + case entity.OpMethod: + obj = entity.NewMethod(p.tableHandle, name) + default: + kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] unsupported namespaced op: %s (current scope: %s)\n", p.tableName, p.r.Offset(), info.op.String(), p.scopeCurrent().Name()) + return false } + // Parse any args that follow the name. The last arg is always an ArgTermList parent.Append(obj) - p.scopeEnter(obj) - ok = p.parseObjList(maxReadOffset) - p.scopeExit() + for argIndex := uint8(1); argIndex < info.argFlags.argCount(); argIndex++ { + if !p.parseArg(info, obj, argIndex, info.argFlags.arg(argIndex), maxReadOffset) { + return false + } + } - return ok && p.finalizeObj(op, obj) + return ok && p.finalizeObj(info.op, obj) } -func (p *Parser) parseArg(info *opcodeInfo, obj Entity, argIndex uint8, argType opArgFlag, maxReadOffset uint32) bool { +func (p *Parser) parseArg(info *opcodeInfo, obj entity.Entity, argIndex uint8, argType opArgFlag, maxReadOffset uint32) bool { var ( arg interface{} ok bool @@ -359,19 +355,20 @@ func (p *Parser) parseArg(info *opcodeInfo, obj Entity, argIndex uint8, argType // If object is a scoped entity enter it's scope before parsing // the term list. Otherwise, create an unnamed scope, attach it // as the next argument to obj and enter that. - if s, isScopeEnt := obj.(ScopeEntity); isScopeEnt { + if s, isScopeEnt := obj.(entity.Container); isScopeEnt { p.scopeEnter(s) } else { - ns := &scopeEntity{op: opScope} + // Create an unnamed scope (e.g if, else, while scope) + ns := entity.NewScope(info.op, p.tableHandle, "") p.scopeEnter(ns) - obj.setArg(argIndex, ns) + obj.SetArg(argIndex, ns) } ok = p.parseObjList(maxReadOffset) p.scopeExit() return ok case opArgFieldList: - return p.parseFieldList(info.op, obj.getArgs(), maxReadOffset) + return p.parseFieldList(obj, maxReadOffset) case opArgByteList: var bl []byte for p.r.Offset() < maxReadOffset { @@ -388,45 +385,68 @@ func (p *Parser) parseArg(info *opcodeInfo, obj Entity, argIndex uint8, argType return false } - return obj.setArg(argIndex, arg) + return obj.SetArg(argIndex, arg) } -func (p *Parser) parseArgObj() (Entity, bool) { +func (p *Parser) parseArgObj() (entity.Entity, bool) { if ok := p.parseObj(); !ok { return nil, false } curScope := p.scopeCurrent() - obj := curScope.lastChild() - curScope.removeChild(obj) + obj := curScope.Last() + curScope.Remove(obj) return obj, true } -func (p *Parser) makeObjForOpcode(info *opcodeInfo) Entity { - var obj Entity +func (p *Parser) makeObjForOpcode(info *opcodeInfo) entity.Entity { + var obj entity.Entity switch { - case info.op == opOpRegion: - obj = new(regionEntity) - case info.op == opBuffer: - obj = new(bufferEntity) - case info.op == opMutex: - obj = new(mutexEntity) - case info.op == opEvent: - obj = new(eventEntity) - case opIsBufferField(info.op): - obj = new(bufferFieldEntity) + case info.op == entity.OpOpRegion: + obj = entity.NewRegion(p.tableHandle) + case info.op == entity.OpBuffer: + obj = entity.NewBuffer(p.tableHandle) + case info.op == entity.OpMutex: + obj = entity.NewMutex(p.tableHandle) + case info.op == entity.OpEvent: + obj = entity.NewEvent(p.tableHandle) + case info.op == entity.OpField: + obj = entity.NewField(p.tableHandle) + case info.op == entity.OpIndexField: + obj = entity.NewIndexField(p.tableHandle) + case info.op == entity.OpBankField: + obj = entity.NewBankField(p.tableHandle) + case info.op == entity.OpCreateField: + obj = entity.NewBufferField(info.op, p.tableHandle, 0) + case info.op == entity.OpCreateBitField: + obj = entity.NewBufferField(info.op, p.tableHandle, 1) + case info.op == entity.OpCreateByteField: + obj = entity.NewBufferField(info.op, p.tableHandle, 8) + case info.op == entity.OpCreateWordField: + obj = entity.NewBufferField(info.op, p.tableHandle, 16) + case info.op == entity.OpCreateDWordField: + obj = entity.NewBufferField(info.op, p.tableHandle, 32) + case info.op == entity.OpCreateQWordField: + obj = entity.NewBufferField(info.op, p.tableHandle, 64) + case info.op == entity.OpZero: + obj = entity.NewConst(info.op, p.tableHandle, uint64(0)) + case info.op == entity.OpOne: + obj = entity.NewConst(info.op, p.tableHandle, uint64(1)) + case info.op == entity.OpOnes: + obj = entity.NewConst(info.op, p.tableHandle, uint64((1<<64)-1)) case info.flags.is(opFlagConstant): - obj = new(constEntity) + obj = entity.NewConst(info.op, p.tableHandle, nil) // will be parsed as an arg + case info.op == entity.OpPackage || info.op == entity.OpVarPackage: + obj = entity.NewPackage(info.op, p.tableHandle) case info.flags.is(opFlagScoped): - obj = new(scopeEntity) + obj = entity.NewScope(info.op, p.tableHandle, "") case info.flags.is(opFlagNamed): - obj = new(namedEntity) + obj = entity.NewGenericNamed(info.op, p.tableHandle) default: - obj = new(unnamedEntity) + obj = entity.NewGeneric(info.op, p.tableHandle) } - obj.setOpcode(info.op) return obj } @@ -447,7 +467,7 @@ func (p *Parser) parseNamedRef() bool { var ( curOffset uint32 argIndex uint8 - arg Entity + arg entity.Entity argList []interface{} ) @@ -459,14 +479,14 @@ func (p *Parser) parseNamedRef() bool { p.r.SetOffset(curOffset) switch { - case ok && (opIsType2(nextOpcode.op) || opIsArg(nextOpcode.op) || opIsDataObject(nextOpcode.op)): + case ok && (entity.OpIsType2(nextOpcode.op) || entity.OpIsArg(nextOpcode.op) || entity.OpIsDataObject(nextOpcode.op)): arg, ok = p.parseArgObj() default: // It may be a nested invocation or named ref ok = p.parseNamedRef() if ok { - arg = p.scopeCurrent().lastChild() - p.scopeCurrent().removeChild(arg) + arg = p.scopeCurrent().Last() + p.scopeCurrent().Remove(arg) } } @@ -486,14 +506,11 @@ func (p *Parser) parseNamedRef() bool { return false } - return p.scopeCurrent().Append(&methodInvocationEntity{ - unnamedEntity: unnamedEntity{args: argList}, - methodName: name, - }) + return p.scopeCurrent().Append(entity.NewInvocation(p.tableHandle, name, argList)) } // Otherwise this is a reference to a named entity - return p.scopeCurrent().Append(&namedReference{targetName: name}) + return p.scopeCurrent().Append(entity.NewReference(p.tableHandle, name)) } func (p *Parser) nextOpcode() (*opcodeInfo, bool) { @@ -533,69 +550,30 @@ func (p *Parser) nextOpcode() (*opcodeInfo, bool) { // AccessField := 0x1 AccessType AccessAttrib // ConnectField := 0x02 NameString | 0x02 BufferData // ExtendedAccessField := 0x3 AccessType ExtendedAccessType AccessLength -func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uint32) bool { +func (p *Parser) parseFieldList(fieldEnt entity.Entity, maxReadOffset uint32) bool { var ( - // for fieldUnit, name0 is the region name and name1 is not used; - // for indexField, - name0, name1 string - flags uint64 + ok bool - ok bool - bitWidth uint32 - curBitOffset uint32 - accessAttrib FieldAccessAttrib - accessByteCount uint8 - unitName string - ) + accessType entity.FieldAccessType - switch op { - case opField: // Field := PkgLength Region AccessFlags FieldList - if len(args) != 2 { - kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d, opcode 0x%2x] invalid arg count: %d\n", p.tableName, p.r.Offset(), uint32(op), len(args)) - return false - } - - name0, ok = args[0].(string) - if !ok { - return false - } - - flags, ok = args[1].(uint64) - if !ok { - return false - } - case opIndexField: // Field := PkgLength IndexFieldName DataFieldName AccessFlags FieldList - if len(args) != 3 { - kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d, opcode 0x%2x] invalid arg count: %d\n", p.tableName, p.r.Offset(), uint32(op), len(args)) - return false - } - - name0, ok = args[0].(string) - if !ok { - return false - } - - name1, ok = args[1].(string) - if !ok { - return false - } - - flags, ok = args[2].(uint64) - if !ok { - return false - } - } - - // Decode flags - accessType := FieldAccessType(flags & 0xf) // access type; bits[0:3] - lock := (flags>>4)&0x1 == 0x1 // lock; bit 4 - updateRule := FieldUpdateRule((flags >> 5) & 0x3) // update rule; bits[5:6] - - var ( + bitWidth uint32 + curBitOffset uint32 connectionName string - resolvedConnection Entity + unitName string + resolvedConnection entity.Entity + accessAttrib entity.FieldAccessAttrib + accessByteCount uint8 ) + // Load default field access rule; it applies to all field units unless + // overridden via a directive in the field unit list + if accessProvider, isProvider := fieldEnt.(entity.FieldAccessTypeProvider); isProvider { + accessType = accessProvider.DefaultAccessType() + } else { + // not a field entity + return false + } + for p.r.Offset() < maxReadOffset { next, err := p.r.ReadByte() if err != nil { @@ -616,7 +594,7 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin if err != nil { return false } - accessType = FieldAccessType(next & 0xf) // access type; bits[0:3] + accessType = entity.FieldAccessType(next & 0xf) // access type; bits[0:3] attrib, err := p.r.ReadByte() if err != nil { @@ -626,7 +604,7 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin // To specify AccessAttribBytes, RawBytes and RawProcessBytes // the ASL compiler will emit an ExtendedAccessField opcode. accessByteCount = 0 - accessAttrib = FieldAccessAttrib(attrib) + accessAttrib = entity.FieldAccessAttrib(attrib) continue case 0x2: // ConnectField => <0x2> NameString> | <0x02> TermObj => Buffer @@ -643,7 +621,7 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin if err != nil { return false } - accessType = FieldAccessType(next & 0xf) // access type; bits[0:3] + accessType = entity.FieldAccessType(next & 0xf) // access type; bits[0:3] extAccessAttrib, err := p.r.ReadByte() if err != nil { @@ -657,11 +635,11 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin switch extAccessAttrib { case 0x0b: - accessAttrib = FieldAccessAttribBytes + accessAttrib = entity.FieldAccessAttribBytes case 0xe: - accessAttrib = FieldAccessAttribRawBytes + accessAttrib = entity.FieldAccessAttribRawBytes case 0x0f: - accessAttrib = FieldAccessAttribRawProcessBytes + accessAttrib = entity.FieldAccessAttribRawProcessBytes } default: // NamedField _ = p.r.UnreadByte() @@ -675,55 +653,20 @@ func (p *Parser) parseFieldList(op opcode, args []interface{}, maxReadOffset uin } // According to the spec, the field elements are should - // be visible at the same scope as the Field/IndexField - switch op { - case opField: - p.scopeCurrent().Append(&fieldUnitEntity{ - fieldEntity: fieldEntity{ - namedEntity: namedEntity{ - tableHandle: p.tableHandle, - op: op, - name: unitName, - }, - bitOffset: curBitOffset, - bitWidth: bitWidth, - lock: lock, - updateRule: updateRule, - accessType: accessType, - accessAttrib: accessAttrib, - byteCount: accessByteCount, - }, - connectionName: connectionName, - resolvedConnection: resolvedConnection, - regionName: name0, - }) - case opIndexField: - p.scopeCurrent().Append(&indexFieldEntity{ - fieldEntity: fieldEntity{ - namedEntity: namedEntity{ - tableHandle: p.tableHandle, - op: op, - name: unitName, - }, - bitOffset: curBitOffset, - bitWidth: bitWidth, - lock: lock, - updateRule: updateRule, - accessType: accessType, - accessAttrib: accessAttrib, - byteCount: accessByteCount, - }, - connectionName: connectionName, - resolvedConnection: resolvedConnection, - indexRegName: name0, - dataRegName: name1, - }) - } + // be visible at the same scope as the Field that declares them + unit := entity.NewFieldUnit(p.tableHandle, unitName) + unit.Field = fieldEnt + unit.AccessType = accessType + unit.AccessAttrib = accessAttrib + unit.ByteCount = accessByteCount + unit.BitOffset = curBitOffset + unit.BitWidth = bitWidth + unit.ConnectionName = connectionName + unit.Connection = resolvedConnection + p.scopeCurrent().Append(unit) curBitOffset += bitWidth - } - } return ok && p.r.Offset() == maxReadOffset @@ -859,10 +802,8 @@ func (p *Parser) parseSimpleName() (interface{}, bool) { var obj interface{} switch { - case ok && nextOpcode.op >= opLocal0 && nextOpcode.op <= opLocal7: - obj, ok = &unnamedEntity{op: nextOpcode.op}, true - case ok && nextOpcode.op >= opArg0 && nextOpcode.op <= opArg6: - obj, ok = &unnamedEntity{op: nextOpcode.op}, true + case ok && entity.OpIsArg(nextOpcode.op): + obj, ok = entity.NewGeneric(nextOpcode.op, p.tableHandle), true default: // Rewind and try parsing as NameString p.r.SetOffset(curOffset) @@ -890,10 +831,10 @@ func (p *Parser) parseTarget() (interface{}, bool) { if ok { switch { - case nextOpcode.op == opZero: // this is actually a NullName + case nextOpcode.op == entity.OpZero: // this is actually a NullName p.r.SetOffset(curOffset + 1) - return &constEntity{op: opStringPrefix, val: ""}, true - case opIsArg(nextOpcode.op) || nextOpcode.op == opRefOf || nextOpcode.op == opDerefOf || nextOpcode.op == opIndex || nextOpcode.op == opDebug: // LocalObj | ArgObj | Type6 | DebugObj + return entity.NewConst(entity.OpStringPrefix, p.tableHandle, ""), true + case entity.OpIsArg(nextOpcode.op) || nextOpcode.op == entity.OpRefOf || nextOpcode.op == entity.OpDerefOf || nextOpcode.op == entity.OpIndex || nextOpcode.op == entity.OpDebug: // LocalObj | ArgObj | Type6 | DebugObj default: // Unexpected opcode return nil, false @@ -905,8 +846,8 @@ func (p *Parser) parseTarget() (interface{}, bool) { // In this case, this is either a NameString or a control method invocation. if ok := p.parseNamedRef(); ok { - obj := p.scopeCurrent().lastChild() - p.scopeCurrent().removeChild(obj) + obj := p.scopeCurrent().Last() + p.scopeCurrent().Remove(obj) return obj, ok } @@ -996,12 +937,12 @@ func (p *Parser) parseNameString() (string, bool) { } // scopeCurrent returns the currently active scope. -func (p *Parser) scopeCurrent() ScopeEntity { +func (p *Parser) scopeCurrent() entity.Container { return p.scopeStack[len(p.scopeStack)-1] } // scopeEnter enters the given scope. -func (p *Parser) scopeEnter(s ScopeEntity) { +func (p *Parser) scopeEnter(s entity.Container) { p.scopeStack = append(p.scopeStack, s) } diff --git a/src/gopheros/device/acpi/aml/parser_test.go b/src/gopheros/device/acpi/aml/parser/parser_test.go similarity index 74% rename from src/gopheros/device/acpi/aml/parser_test.go rename to src/gopheros/device/acpi/aml/parser/parser_test.go index 654df99..fa223a1 100644 --- a/src/gopheros/device/acpi/aml/parser_test.go +++ b/src/gopheros/device/acpi/aml/parser/parser_test.go @@ -1,6 +1,7 @@ -package aml +package parser import ( + "gopheros/device/acpi/aml/entity" "gopheros/device/acpi/table" "io/ioutil" "os" @@ -23,19 +24,7 @@ func TestParser(t *testing.T) { tableFiles: spec, } - // Create default scopes - 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 - - // Inject pre-defined OSPM objects - rootNS.Append(&constEntity{name: "_OS_", val: "gopheros"}) - rootNS.Append(&constEntity{name: "_REV", val: uint64(2)}) - - p := NewParser(os.Stderr, rootNS) + p := NewParser(os.Stderr, genDefaultScopes()) for _, tableName := range spec { tableName = strings.Replace(tableName, ".aml", "", -1) @@ -50,25 +39,18 @@ func TestParser(t *testing.T) { func TestTableHandleAssignment(t *testing.T) { var resolver = mockResolver{tableFiles: []string{"parser-testsuite-DSDT.aml"}} - // Create default scopes - 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 - + rootNS := genDefaultScopes() p := NewParser(ioutil.Discard, rootNS) - expHandle := uint8(42) + expHandle := uint8(0x0f) tableName := "parser-testsuite-DSDT" if err := p.ParseAML(expHandle, tableName, resolver.LookupTable(tableName)); err != nil { t.Error(err) } // Drop all entities that were assigned the handle value - var unloadList []Entity - scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool { + var unloadList []entity.Entity + entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool { if ent.TableHandle() == expHandle { unloadList = append(unloadList, ent) return false @@ -78,13 +60,13 @@ func TestTableHandleAssignment(t *testing.T) { for _, ent := range unloadList { if p := ent.Parent(); p != nil { - p.removeChild(ent) + p.Remove(ent) } } // We should end up with the original tree var visitedNodes int - scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool { + entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool { visitedNodes++ if ent.TableHandle() == expHandle { t.Errorf("encountered entity that should have been pruned: %#+v", ent) @@ -102,15 +84,7 @@ func TestParserForwardDeclParsing(t *testing.T) { tableFiles: []string{"parser-testsuite-fwd-decls-DSDT.aml"}, } - // Create default scopes - 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 - - p := NewParser(ioutil.Discard, rootNS) + p := NewParser(ioutil.Discard, genDefaultScopes()) for _, tableName := range resolver.tableFiles { tableName = strings.Replace(tableName, ".aml", "", -1) @@ -172,7 +146,7 @@ func TestParserErrorHandling(t *testing.T) { t.Run("ParseAML errors", func(t *testing.T) { t.Run("parseObjList error", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) // Setup resolver to serve an AML stream containing an invalid opcode header := mockParserPayload(p, []byte{0x5b, 0x00}) @@ -190,12 +164,10 @@ func TestParserErrorHandling(t *testing.T) { }) t.Run("unresolved entities", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) // Inject a reference entity to the tree - p.root.Append(&namedReference{ - targetName: "UNKNOWN", - }) + p.root.Append(entity.NewReference(42, "UNKNOWN")) // Setup resolver to serve an empty AML stream header := mockParserPayload(p, nil) @@ -208,11 +180,11 @@ func TestParserErrorHandling(t *testing.T) { t.Run("parseObj errors", func(t *testing.T) { t.Run("parsePkgLength error", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) // Setup resolver to serve an AML stream containing an incomplete // buffer specification - header := mockParserPayload(p, []byte{byte(opBuffer)}) + header := mockParserPayload(p, []byte{byte(entity.OpBuffer)}) if err := p.ParseAML(uint8(42), "DSDT", header); err == nil { t.Fatal("expected parsePkgLength to return an error") @@ -220,11 +192,11 @@ func TestParserErrorHandling(t *testing.T) { }) t.Run("incomplete object list", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) // Setup resolver to serve an AML stream containing an incomplete // buffer arglist specification - header := mockParserPayload(p, []byte{byte(opBuffer), 0x10}) + header := mockParserPayload(p, []byte{byte(entity.OpBuffer), 0x10}) if err := p.ParseAML(uint8(42), "DSDT", header); err == nil { t.Fatal("expected parsePkgLength to return an error") @@ -234,13 +206,12 @@ func TestParserErrorHandling(t *testing.T) { t.Run("finalizeObj errors", func(t *testing.T) { t.Run("else without matching if", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} - p.root.Append(&constEntity{val: 0x42}) - p.root.Append(&scopeEntity{op: opElse}) + p.root = entity.NewScope(entity.OpScope, 42, `\`) + p.root.Append(entity.NewConst(entity.OpDwordPrefix, 42, uint64(0x42))) // Setup resolver to serve an AML stream containing an // empty else statement without a matching if - header := mockParserPayload(p, []byte{byte(opElse), 0x0}) + header := mockParserPayload(p, []byte{byte(entity.OpElse), 0x0}) if err := p.ParseAML(uint8(42), "DSDT", header); err == nil { t.Fatal("expected finalizeObj to return an error") @@ -251,10 +222,10 @@ func TestParserErrorHandling(t *testing.T) { t.Run("parseScope errors", func(t *testing.T) { t.Run("parseNameString error", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) header := mockParserPayload(p, []byte{ - byte(opScope), + byte(entity.OpScope), 0x10, // pkglen }) @@ -264,10 +235,10 @@ func TestParserErrorHandling(t *testing.T) { }) t.Run("unknown scope", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) header := mockParserPayload(p, []byte{ - byte(opScope), + byte(entity.OpScope), 0x10, // pkglen 'F', 'O', 'O', 'F', }) @@ -278,10 +249,10 @@ func TestParserErrorHandling(t *testing.T) { }) t.Run("nameless scope", func(t *testing.T) { - p.root = &scopeEntity{} + p.root = entity.NewScope(entity.OpScope, 42, ``) header := mockParserPayload(p, []byte{ - byte(opScope), + byte(entity.OpScope), 0x02, // pkglen '\\', // scope name: "\" (root scope) 0x00, // null string @@ -295,58 +266,75 @@ func TestParserErrorHandling(t *testing.T) { t.Run("parseNamespacedObj errors", func(t *testing.T) { t.Run("parseNameString error", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) mockParserPayload(p, nil) - if p.parseNamespacedObj(opDevice, 10) { + devInfo := &opcodeTable[0x6a] + if p.parseNamespacedObj(devInfo, 10) { t.Fatal("expected parseNamespacedObj to return false") } }) t.Run("scope lookup error", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) header := mockParserPayload(p, []byte{'^', 'F', 'A', 'B', 'C'}) p.scopeEnter(p.root) - if p.parseNamespacedObj(opDevice, header.Length) { + devInfo := &opcodeTable[0x6a] + if p.parseNamespacedObj(devInfo, header.Length) { t.Fatal("expected parseNamespacedObj to return false") } }) - t.Run("error parsing method arg count", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + t.Run("unsupported namespaced entity", func(t *testing.T) { + p.root = entity.NewScope(entity.OpScope, 42, `\`) header := mockParserPayload(p, []byte{'F', 'A', 'B', 'C'}) p.scopeEnter(p.root) - if p.parseNamespacedObj(opMethod, header.Length) { + + // We just pass a random non-namespaced opcode table entry to parseNamespacedObj + zeroInfo := &opcodeTable[0x00] + if p.parseNamespacedObj(zeroInfo, header.Length) { + t.Fatal("expected parseNamespacedObj to return false") + } + }) + + t.Run("error parsing args after name", func(t *testing.T) { + p.root = entity.NewScope(entity.OpScope, 42, `\`) + + header := mockParserPayload(p, []byte{'F', 'A', 'B', 'C'}) + + p.scopeEnter(p.root) + methodInfo := &opcodeTable[0x0d] + if p.parseNamespacedObj(methodInfo, header.Length) { t.Fatal("expected parseNamespacedObj to return false") } }) }) t.Run("parseArg bytelist errors", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) mockParserPayload(p, nil) - if p.parseArg(new(opcodeInfo), new(unnamedEntity), 0, opArgByteList, 42) { + if p.parseArg(new(opcodeInfo), entity.NewGeneric(0, 0), 0, opArgByteList, 42) { t.Fatal("expected parseNamespacedObj to return false") } }) t.Run("parseNamedRef errors", func(t *testing.T) { t.Run("missing args", func(t *testing.T) { - p.root = &scopeEntity{op: opScope, name: `\`} + p.root = entity.NewScope(entity.OpScope, 42, `\`) p.methodArgCount = map[string]uint8{ "MTHD": 10, } mockParserPayload(p, []byte{ 'M', 'T', 'H', 'D', - byte(opIf), // Incomplete type2 opcode + byte(entity.OpIf), // Incomplete type2 opcode }) p.scopeEnter(p.root) @@ -358,123 +346,149 @@ func TestParserErrorHandling(t *testing.T) { t.Run("parseFieldList errors", func(t *testing.T) { specs := []struct { - op opcode + op entity.AMLOpcode args []interface{} maxReadOffset uint32 payload []byte }{ - // Invalid arg count for opField + // Invalid arg count for entity.OpField { - opField, + entity.OpField, nil, 0, nil, }, - // Wrong arg type for opField + // Wrong arg type for entity.OpField { - opField, + entity.OpField, []interface{}{0, uint64(42)}, 0, nil, }, { - opField, + entity.OpField, []interface{}{"FLD0", uint32(42)}, 0, nil, }, - // Invalid arg count for opIndexField + // Invalid arg count for entity.OpIndexField { - opIndexField, + entity.OpIndexField, nil, 0, nil, }, - // Wrong arg type for opIndexField + // Wrong arg type for entity.OpIndexField { - opIndexField, + entity.OpIndexField, []interface{}{0, "FLD1", "FLD2"}, 0, nil, }, { - opIndexField, + entity.OpIndexField, []interface{}{"FLD0", 0, "FLD2"}, 0, nil, }, { - opIndexField, + entity.OpIndexField, + []interface{}{"FLD0", "FLD1", 0}, + 0, + nil, + }, + // Invalid arg count for entity.OpBankField + { + entity.OpBankField, + nil, + 0, + nil, + }, + // Wrong arg type for entity.OpBankField + { + entity.OpBankField, + []interface{}{0, "FLD1", "FLD2"}, + 0, + nil, + }, + { + entity.OpBankField, + []interface{}{"FLD0", 0, "FLD2"}, + 0, + nil, + }, + { + entity.OpBankField, []interface{}{"FLD0", "FLD1", 0}, 0, nil, }, // unexpected EOF parsing fields { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, nil, }, // reserved field (0x00) with missing pkgLen { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{0x00}, }, // access field (0x01) with missing accessType { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{0x01}, }, // access field (0x01) with missing attribute byte { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{0x01, 0x01}, }, // connect field (0x02) with incomplete TermObject => Buffer arg { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, - []byte{0x02, byte(opBuffer)}, + []byte{0x02, byte(entity.OpBuffer)}, }, // extended access field (0x03) with missing ext. accessType { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{0x03}, }, // extended access field (0x03) with missing ext. attribute byte { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{0x03, 0x01}, }, // extended access field (0x03) with missing access byte count value { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{0x03, 0x01, 0x02}, }, // named field with invalid name { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{0xff}, }, // named field with invalid pkgLen { - opField, + entity.OpField, []interface{}{"FLD0", uint64(42)}, 128, []byte{'N', 'A', 'M', 'E'}, @@ -484,10 +498,16 @@ func TestParserErrorHandling(t *testing.T) { for specIndex, spec := range specs { mockParserPayload(p, spec.payload) - if p.parseFieldList(spec.op, spec.args, spec.maxReadOffset) { + if p.parseFieldList(entity.NewField(42), spec.maxReadOffset) { t.Errorf("[spec %d] expected parseFieldLis to return false", specIndex) } } + + t.Run("non-field entity argument", func(t *testing.T) { + if p.parseFieldList(entity.NewDevice(42, "DEV0"), 128) { + t.Fatal("expected parseFieldList to return false when a non-field argument is passed to it") + } + }) }) t.Run("parsePkgLength errors", func(t *testing.T) { @@ -532,7 +552,7 @@ func TestParserErrorHandling(t *testing.T) { t.Run("parseTarget errors", func(t *testing.T) { t.Run("unexpected opcode", func(t *testing.T) { // Unexpected opcode - mockParserPayload(p, []byte{byte(opAnd)}) + mockParserPayload(p, []byte{byte(entity.OpAnd)}) if _, ok := p.parseTarget(); ok { t.Error("expected parseTarget to return false") @@ -591,7 +611,7 @@ func TestDetectMethodDeclarations(t *testing.T) { } validMethod := []byte{ - byte(opMethod), + byte(entity.OpMethod), 5, // pkgLen 'M', 'T', 'H', 'D', 2, // flags (2 args) @@ -614,7 +634,7 @@ func TestDetectMethodDeclarations(t *testing.T) { t.Run("bad pkgLen", func(t *testing.T) { mockParserPayload(p, []byte{ - byte(opMethod), + byte(entity.OpMethod), // lead byte bits (6:7) indicate 1 extra byte that is missing byte(1 << 6), }) @@ -625,7 +645,7 @@ func TestDetectMethodDeclarations(t *testing.T) { t.Run("error parsing namestring", func(t *testing.T) { mockParserPayload(p, append([]byte{ - byte(opMethod), + byte(entity.OpMethod), byte(5), // pkgLen 10, // bogus char, not part of namestring }, validMethod...)) @@ -645,7 +665,7 @@ func TestDetectMethodDeclarations(t *testing.T) { t.Run("error parsing method flags", func(t *testing.T) { mockParserPayload(p, []byte{ - byte(opMethod), + byte(entity.OpMethod), byte(5), // pkgLen 'F', 'O', 'O', 'F', // Missing flag byte @@ -678,7 +698,7 @@ type mockResolver struct { } func (m mockResolver) LookupTable(name string) *table.SDTHeader { - pathToDumps := pkgDir() + "/../table/tabletest/" + pathToDumps := pkgDir() + "/../../table/tabletest/" for _, f := range m.tableFiles { if !strings.Contains(f, name) { continue @@ -709,3 +729,23 @@ func (f fixedPayloadResolver) LookupTable(name string) *table.SDTHeader { return hdr } + +func genDefaultScopes() entity.Container { + rootNS := entity.NewScope(entity.OpScope, 42, `\`) + rootNS.Append(entity.NewScope(entity.OpScope, 42, `_GPE`)) // General events in GPE register block + rootNS.Append(entity.NewScope(entity.OpScope, 42, `_PR_`)) // ACPI 1.0 processor namespace + rootNS.Append(entity.NewScope(entity.OpScope, 42, `_SB_`)) // System bus with all device objects + rootNS.Append(entity.NewScope(entity.OpScope, 42, `_SI_`)) // System indicators + rootNS.Append(entity.NewScope(entity.OpScope, 42, `_TZ_`)) // ACPI 1.0 thermal zone namespace + + // Inject pre-defined OSPM objects + rootNS.Append(namedConst(entity.NewConst(entity.OpStringPrefix, 42, "gopheros"), "_OS_")) + rootNS.Append(namedConst(entity.NewConst(entity.OpStringPrefix, 42, uint64(2)), "_REV")) + + return rootNS +} + +func namedConst(ent *entity.Const, name string) *entity.Const { + ent.SetName(name) + return ent +} diff --git a/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.aml b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.aml index 5d278a1ccf09445ba8bc3a0d49c02509eb819b12..5786a1894fb9e026dd40f5ffa583f13bbfc62e00 100644 GIT binary patch delta 187 zcmaFC{F#NzCD2i?D!1OrnJ|7`S+WEK{%`D@f3^AX=W+ z-__TUf!8+@NHg$;Ox9-HD;M3&?Hmwlz^K5;z`z#W!VwT2WWd0{5Z%fe5@iq%0H267 Ae*gdg delta 28 kcmey&@`9PmCD Date: Fri, 29 Dec 2017 09:11:09 +0000 Subject: [PATCH 8/9] acpi: implement LazyRefResolver for entities that use lazy symbol references --- src/gopheros/device/acpi/aml/entity/entity.go | 108 ++++++++++++++++++ .../device/acpi/aml/entity/entity_test.go | 87 ++++++++++++++ 2 files changed, 195 insertions(+) diff --git a/src/gopheros/device/acpi/aml/entity/entity.go b/src/gopheros/device/acpi/aml/entity/entity.go index 03a81ad..285382c 100644 --- a/src/gopheros/device/acpi/aml/entity/entity.go +++ b/src/gopheros/device/acpi/aml/entity/entity.go @@ -451,6 +451,21 @@ func (ent *Field) SetArg(argIndex uint8, arg interface{}) bool { return ok } +// ResolveSymbolRefs receives as input the root of the AML entity tree and +// attempts to resolve any symbol references using the scope searching rules +// defined by the ACPI spec. +func (ent *Field) ResolveSymbolRefs(rootNS Container) *kernel.Error { + var ok bool + if ent.Region, ok = FindInScope(ent.Parent(), rootNS, ent.RegionName).(*Region); !ok { + return &kernel.Error{ + Module: "acpi_aml_resolver", + Message: "could not resolve referenced field region: " + ent.RegionName, + } + } + + return nil +} + // IndexField is a special field that groups together two field units so a // index/data register pattern can be implemented. To write a value to an // IndexField, the interpreter must first write the appropriate offset to @@ -508,6 +523,29 @@ func (ent *IndexField) SetArg(argIndex uint8, arg interface{}) bool { return ok } +// ResolveSymbolRefs receives as input the root of the AML entity tree and +// attempts to resolve any symbol references using the scope searching rules +// defined by the ACPI spec. +func (ent *IndexField) ResolveSymbolRefs(rootNS Container) *kernel.Error { + var ok bool + + if ent.IndexReg, ok = FindInScope(ent.Parent(), rootNS, ent.IndexRegName).(*FieldUnit); !ok { + return &kernel.Error{ + Module: "acpi_aml_resolver", + Message: "could not resolve referenced index register: " + ent.IndexRegName, + } + } + + if ent.DataReg, ok = FindInScope(ent.Parent(), rootNS, ent.DataRegName).(*FieldUnit); !ok { + return &kernel.Error{ + Module: "acpi_aml_resolver", + Message: "could not resolve referenced data register: " + ent.DataRegName, + } + } + + return nil +} + // BankField is a special field where a bank register must be used to select // the appropriate bank region before accessing its contents. type BankField struct { @@ -569,6 +607,29 @@ func (ent *BankField) SetArg(argIndex uint8, arg interface{}) bool { return ok } +// ResolveSymbolRefs receives as input the root of the AML entity tree and +// attempts to resolve any symbol references using the scope searching rules +// defined by the ACPI spec. +func (ent *BankField) ResolveSymbolRefs(rootNS Container) *kernel.Error { + var ok bool + + if ent.Region, ok = FindInScope(ent.Parent(), rootNS, ent.RegionName).(*Region); !ok { + return &kernel.Error{ + Module: "acpi_aml_resolver", + Message: "could not resolve referenced field region: " + ent.RegionName, + } + } + + if ent.BankFieldUnit, ok = FindInScope(ent.Parent(), rootNS, ent.BankFieldUnitName).(*FieldUnit); !ok { + return &kernel.Error{ + Module: "acpi_aml_resolver", + Message: "could not resolve referenced bank register field: " + ent.BankFieldUnitName, + } + } + + return nil +} + // FieldUnit describes a sub-region inside a parent field. type FieldUnit struct { GenericNamed @@ -609,6 +670,24 @@ func NewFieldUnit(tableHandle uint8, name string) *FieldUnit { } } +// ResolveSymbolRefs receives as input the root of the AML entity tree and +// attempts to resolve any symbol references using the scope searching rules +// defined by the ACPI spec. +func (ent *FieldUnit) ResolveSymbolRefs(rootNS Container) *kernel.Error { + if ent.ConnectionName == "" { + return nil + } + + if ent.Connection = FindInScope(ent.Parent(), rootNS, ent.ConnectionName); ent.Connection == nil { + return &kernel.Error{ + Module: "acpi_aml_resolver", + Message: "[field unit: " + ent.Name() + "] could not resolve connection reference: " + ent.ConnectionName, + } + } + + return nil +} + // Reference holds a named reference to an AML symbol. The spec allows the // symbol not to be defined at the time when the reference is parsed. In such a // case (forward reference) it will be resolved after the entire AML stream has @@ -631,6 +710,20 @@ func NewReference(tableHandle uint8, target string) *Reference { } } +// ResolveSymbolRefs receives as input the root of the AML entity tree and +// attempts to resolve any symbol references using the scope searching rules +// defined by the ACPI spec. +func (ent *Reference) ResolveSymbolRefs(rootNS Container) *kernel.Error { + if ent.Target = FindInScope(ent.Parent(), rootNS, ent.TargetName); ent.Target == nil { + return &kernel.Error{ + Module: "acpi_aml_vm", + Message: "could not resolve referenced symbol: " + ent.TargetName + "; parent: " + ent.Parent().Name(), + } + } + + return nil +} + // Method describes an invocable AML method. type Method struct { Scope @@ -698,6 +791,21 @@ func NewInvocation(tableHandle uint8, name string, args []interface{}) *Invocati } } +// ResolveSymbolRefs receives as input the root of the AML entity tree and +// attempts to resolve any symbol references using the scope searching rules +// defined by the ACPI spec. +func (ent *Invocation) ResolveSymbolRefs(rootNS Container) *kernel.Error { + var ok bool + if ent.MethodDef, ok = FindInScope(ent.Parent(), rootNS, ent.MethodName).(*Method); !ok { + return &kernel.Error{ + Module: "acpi_aml_vm", + Message: "could not resolve method invocation to: " + ent.MethodName + "; parent: " + ent.Parent().Name(), + } + } + + return nil +} + // Device defines an AML device entity. type Device struct { Scope diff --git a/src/gopheros/device/acpi/aml/entity/entity_test.go b/src/gopheros/device/acpi/aml/entity/entity_test.go index 97d26f3..a4efcd7 100644 --- a/src/gopheros/device/acpi/aml/entity/entity_test.go +++ b/src/gopheros/device/acpi/aml/entity/entity_test.go @@ -214,3 +214,90 @@ nextSpec: } } } + +func TestLazySymbolResolver(t *testing.T) { + root := NewScope(OpScope, 42, `\`) + reg0 := NewRegion(42) + reg0.SetArg(0, "REG0") + root.Append(reg0) + root.Append(NewFieldUnit(42, "FLD0")) + root.Append(NewMethod(42, "MTH0")) + + specs := []struct { + ent Entity + expErr string + }{ + { + &Field{RegionName: "MISSING"}, + "could not resolve referenced field region: MISSING", + }, + { + &Field{RegionName: "REG0"}, + "", + }, + { + &IndexField{IndexRegName: "UNKNOWN"}, + "could not resolve referenced index register: UNKNOWN", + }, + { + &IndexField{IndexRegName: "FLD0", DataRegName: "UNKNOWN"}, + "could not resolve referenced data register: UNKNOWN", + }, + { + &IndexField{IndexRegName: "FLD0", DataRegName: "FLD0"}, + "", + }, + { + &BankField{RegionName: "MISSING"}, + "could not resolve referenced field region: MISSING", + }, + { + &BankField{RegionName: "REG0", BankFieldUnitName: "UNKNOWN"}, + "could not resolve referenced bank register field: UNKNOWN", + }, + { + &BankField{RegionName: "REG0", BankFieldUnitName: "FLD0"}, + "", + }, + { + &FieldUnit{ + GenericNamed: GenericNamed{name: "FLD0"}, + ConnectionName: "MISSING", + }, + "[field unit: FLD0] could not resolve connection reference: MISSING", + }, + { + // No connection reference + &FieldUnit{}, + "", + }, + { + &FieldUnit{ConnectionName: "FLD0"}, + "", + }, + { + &Reference{TargetName: "MISSING"}, + `could not resolve referenced symbol: MISSING; parent: \`, + }, + { + &Reference{TargetName: "FLD0"}, + "", + }, + { + &Invocation{MethodName: "UNKNOWN"}, + `could not resolve method invocation to: UNKNOWN; parent: \`, + }, + { + &Invocation{MethodName: "MTH0"}, + "", + }, + } + + for specIndex, spec := range specs { + root.Append(spec.ent) + err := spec.ent.(LazyRefResolver).ResolveSymbolRefs(root) + if spec.expErr != "" && (err == nil || err.Message != spec.expErr) { + t.Errorf("[spec %d] expected ResolveReferences to return error %q; got: %v", specIndex, spec.expErr, err) + } + } +} From c09798622bb725d93b9d0c09a9456481e906483e Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Sat, 6 Jan 2018 10:35:26 +0000 Subject: [PATCH 9/9] acpi: provide more robust implementation for parsing AML method bodies The previous implementation used brute-force approach where the parser made an initial pass scanning the AML bytestream and looking for method declaration opcodes. It then parsed out the method name and arg count and populated a map which was used to detect the number of arguments to be parsed upon encountering a method invocation. This approach proved to be error-prone and would lead to an incorrect parse tree in the following ASL example: Method (FOOF, 1, NotSerialized) { Return ("bar") } Method (TST0, 0, NotSerialized) { FOOF(0) \_SB.FOOF(2, 3) } Scope(\_SB){ // Another FOOF method in \_SB which takes a different arg count Method (FOOF, 2, NotSerialized) { Return ("something") } } In the above example the parser would correctly parse the first FOOF call in TST0 but fail to parse the second invocation since the method name contains a scope. The second invocation would actually yield the following incorrect entity list (arguments appear as sibling entities): Ref(\_SB.FOOF), Const(2), Const(3) The new approach gets rid of the brute-force method and instead modifies the initial parse of the tree not to parse the entities in the AML method bodies but to instead track the start and end offset in the AML stream for the body contents. In the second pass (where the parser normally resolves symbol references), the parser can properly parse the contents of method bodies since the entire AML tree is now known and the parser can use the regular scope lookup rules to find the correct method declaration for the invocation and figure out the argument count it needs to parse. --- src/gopheros/device/acpi/aml/entity/entity.go | 29 ++-- .../device/acpi/aml/entity/entity_test.go | 10 +- .../device/acpi/aml/parser/opcode_table.go | 3 + src/gopheros/device/acpi/aml/parser/parser.go | 144 ++++++++---------- .../device/acpi/aml/parser/parser_test.go | 137 ++++++++--------- .../device/acpi/aml/parser/stream_reader.go | 3 + .../parser-testsuite-fwd-decls-DSDT.aml | Bin 62 -> 108 bytes .../parser-testsuite-fwd-decls-DSDT.dsl | 17 ++- 8 files changed, 155 insertions(+), 188 deletions(-) diff --git a/src/gopheros/device/acpi/aml/entity/entity.go b/src/gopheros/device/acpi/aml/entity/entity.go index 285382c..5e2b85e 100644 --- a/src/gopheros/device/acpi/aml/entity/entity.go +++ b/src/gopheros/device/acpi/aml/entity/entity.go @@ -1,6 +1,8 @@ package entity -import "gopheros/kernel" +import ( + "gopheros/kernel" +) // Entity is an interface implemented by all AML entities. type Entity interface { @@ -731,6 +733,9 @@ type Method struct { ArgCount uint8 Serialized bool SyncLevel uint8 + + BodyStartOffset uint32 + BodyEndOffset uint32 } // NewMethod creats a new AML method entity. @@ -775,37 +780,21 @@ func (ent *Method) SetArg(argIndex uint8, arg interface{}) bool { type Invocation struct { Generic - MethodName string - MethodDef *Method + Method *Method } // NewInvocation creates a new method invocation object. -func NewInvocation(tableHandle uint8, name string, args []interface{}) *Invocation { +func NewInvocation(tableHandle uint8, method *Method, args []interface{}) *Invocation { return &Invocation{ Generic: Generic{ op: OpMethodInvocation, tableHandle: tableHandle, args: args, }, - MethodName: name, + Method: method, } } -// ResolveSymbolRefs receives as input the root of the AML entity tree and -// attempts to resolve any symbol references using the scope searching rules -// defined by the ACPI spec. -func (ent *Invocation) ResolveSymbolRefs(rootNS Container) *kernel.Error { - var ok bool - if ent.MethodDef, ok = FindInScope(ent.Parent(), rootNS, ent.MethodName).(*Method); !ok { - return &kernel.Error{ - Module: "acpi_aml_vm", - Message: "could not resolve method invocation to: " + ent.MethodName + "; parent: " + ent.Parent().Name(), - } - } - - return nil -} - // Device defines an AML device entity. type Device struct { Scope diff --git a/src/gopheros/device/acpi/aml/entity/entity_test.go b/src/gopheros/device/acpi/aml/entity/entity_test.go index a4efcd7..9df2eed 100644 --- a/src/gopheros/device/acpi/aml/entity/entity_test.go +++ b/src/gopheros/device/acpi/aml/entity/entity_test.go @@ -25,7 +25,7 @@ func TestEntityMethods(t *testing.T) { {NewBankField(42), OpBankField, ""}, {NewReference(42, "TRG0"), OpName, ""}, {NewMethod(42, "FOO0"), OpMethod, "FOO0"}, - {NewInvocation(42, "MTH0", nil), OpMethodInvocation, ""}, + {NewInvocation(42, NewMethod(42, "MTH0"), nil), OpMethodInvocation, ""}, {NewMutex(42), OpMutex, ""}, {NewDevice(42, "DEV0"), OpDevice, "DEV0"}, {NewProcessor(42, "CPU0"), OpProcessor, "CPU0"}, @@ -283,14 +283,6 @@ func TestLazySymbolResolver(t *testing.T) { &Reference{TargetName: "FLD0"}, "", }, - { - &Invocation{MethodName: "UNKNOWN"}, - `could not resolve method invocation to: UNKNOWN; parent: \`, - }, - { - &Invocation{MethodName: "MTH0"}, - "", - }, } for specIndex, spec := range specs { diff --git a/src/gopheros/device/acpi/aml/parser/opcode_table.go b/src/gopheros/device/acpi/aml/parser/opcode_table.go index 404592f..e1333d8 100644 --- a/src/gopheros/device/acpi/aml/parser/opcode_table.go +++ b/src/gopheros/device/acpi/aml/parser/opcode_table.go @@ -142,6 +142,9 @@ type opcodeInfo struct { argFlags opArgFlags } +// Used by Parser.parseMethodBody for deferred parsing of method bodies. +const methodOpInfoIndex = 0x0d + // The opcode table contains all opcode-related information that the parser knows. // This table is modeled after a similar table used in the acpica implementation. var opcodeTable = []opcodeInfo{ diff --git a/src/gopheros/device/acpi/aml/parser/parser.go b/src/gopheros/device/acpi/aml/parser/parser.go index 7daa950..8f4d495 100644 --- a/src/gopheros/device/acpi/aml/parser/parser.go +++ b/src/gopheros/device/acpi/aml/parser/parser.go @@ -14,6 +14,13 @@ var ( errResolvingEntities = &kernel.Error{Module: "acpi_aml_parser", Message: "AML bytecode contains unresolvable entities"} ) +type parseOpt uint8 + +const ( + parseOptSkipMethodBodies parseOpt = iota + parseOptParseMethodBodies +) + // Parser implements an AML parser. type Parser struct { r amlStreamReader @@ -23,19 +30,14 @@ type Parser struct { tableName string tableHandle uint8 - // methodArgCount is initialized in a pre-parse step with the names and expected - // number of args for each function declaration. This is required as function - // invocations do not employ any mechanism to indicate the number of args that - // need to be parsed. Moreover, the spec allows for forward function declarations. - methodArgCount map[string]uint8 + parseOptions parseOpt } // NewParser returns a new AML parser instance. func NewParser(errWriter io.Writer, rootEntity entity.Container) *Parser { return &Parser{ - errWriter: errWriter, - root: rootEntity, - methodArgCount: make(map[string]uint8), + errWriter: errWriter, + root: rootEntity, } } @@ -51,12 +53,9 @@ func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDT uint32(unsafe.Sizeof(table.SDTHeader{})), ) - // Pass 1: scan bytecode and locate all method declarations. This allows us to - // properly parse the arguments to method invocations at pass 2 even if the - // the name of the invoked method is a forward reference. - p.detectMethodDeclarations() - - // Pass 2: decode bytecode and build entitites + // Pass 1: decode bytecode and build entitites without recursing into + // function bodies. + p.parseOptions = parseOptSkipMethodBodies p.scopeStack = nil p.scopeEnter(p.root) if !p.parseObjList(header.Length) { @@ -66,11 +65,15 @@ func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDT } p.scopeExit() - // Pass 3: check parents and resolve symbol references + // Pass 2: parse method bodies, check entity parents and resolve all + // symbol references var resolveFailed bool entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool { - // Skip method bodies; their contents will be lazily resolved by the interpreter - if _, isMethod := ent.(*entity.Method); isMethod { + if method, isMethod := ent.(*entity.Method); isMethod { + resolveFailed = resolveFailed || !p.parseMethodBody(method) + + // Don't recurse into method bodies; their contents + // will be lazilly resolved by the VM return false } @@ -101,57 +104,6 @@ func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDT return nil } -// detectMethodDeclarations scans the AML byte-stream looking for function -// declarations. For each discovered function, the method will parse its flags -// and update the methodArgCount map with the number of required arguments. -func (p *Parser) detectMethodDeclarations() { - var ( - next *opcodeInfo - method string - startOffset = p.r.Offset() - curOffset, pkgLen uint32 - flags uint64 - ok bool - ) - - for !p.r.EOF() { - if next, ok = p.nextOpcode(); !ok { - // Skip one byte to the right and try again. Maybe we are stuck inside - // the contents of a string or buffer - _, _ = p.r.ReadByte() - continue - } - - if next.op != entity.OpMethod { - continue - } - - // Parse pkg len; if this fails then this is not a method declaration - curOffset = p.r.Offset() - if pkgLen, ok = p.parsePkgLength(); !ok { - continue - } - - // Parse method name - if method, ok = p.parseNameString(); !ok { - continue - } - - // The next byte encodes the method flags which also contains the arg count - // at bits 0:2 - if flags, ok = p.parseNumConstant(1); !ok { - continue - } - - p.methodArgCount[method] = uint8(flags) & 0x7 - - // At this point we can use the pkg length to skip over the term list - p.r.SetOffset(curOffset + pkgLen) - } - - p.r.SetOffset(startOffset) -} - // parseObjList tries to parse an AML object list. Object lists are usually // specified together with a pkgLen block which is used to calculate the max // read offset that the parser may reach. @@ -174,8 +126,7 @@ func (p *Parser) parseObj() bool { ) // If we cannot decode the next opcode then this may be a method - // invocation or a name reference. If neither is the case, we need to - // rewind the stream and parse a method invocation before giving up. + // invocation or a name reference. curOffset = p.r.Offset() if info, ok = p.nextOpcode(); !ok { p.r.SetOffset(curOffset) @@ -352,6 +303,16 @@ func (p *Parser) parseArg(info *opcodeInfo, obj entity.Entity, argIndex uint8, a case opArgTarget: arg, ok = p.parseTarget() case opArgTermList: + // If this is a method and the SkipMethodBodies option is set + // then record the body start and end offset so we can parse + // it at a later stage. + if method, isMethod := obj.(*entity.Method); isMethod && p.parseOptions == parseOptSkipMethodBodies { + method.BodyStartOffset = p.r.Offset() + method.BodyEndOffset = maxReadOffset + p.r.SetOffset(maxReadOffset) + return true + } + // If object is a scoped entity enter it's scope before parsing // the term list. Otherwise, create an unnamed scope, attach it // as the next argument to obj and enter that. @@ -450,6 +411,25 @@ func (p *Parser) makeObjForOpcode(info *opcodeInfo) entity.Entity { return obj } +// parseMethodBody parses the entities that make up a method's body. After the +// entire AML tree has been parsed, the parser makes a second pass and calls +// parseMethodBody for each Method entity. +// +// By deferring the parsing of the method body, we ensure that the parser can +// lookup the method declarations (even if forward declarations are used) for +// each method invocation. As method declarations contain information about the +// expected argument count, the parser can use this information to properly +// parse the invocation arguments. For more details see: parseNamedRef +func (p *Parser) parseMethodBody(method *entity.Method) bool { + p.parseOptions = parseOptParseMethodBodies + p.scopeEnter(method) + p.r.SetOffset(method.BodyStartOffset) + ok := p.parseArg(&opcodeTable[methodOpInfoIndex], method, 2, opArgTermList, method.BodyEndOffset) + p.scopeExit() + + return ok +} + // parseNamedRef attempts to parse either a method invocation or a named // reference. As AML allows for forward references, the actual contents for // this entity will not be known until the entire AML stream has been parsed. @@ -464,15 +444,17 @@ func (p *Parser) parseNamedRef() bool { return false } - var ( - curOffset uint32 - argIndex uint8 - arg entity.Entity - argList []interface{} - ) + // Check if this is a method invocation + ent := entity.FindInScope(p.scopeCurrent(), p.root, name) + if methodDef, isMethod := ent.(*entity.Method); isMethod { + var ( + curOffset uint32 + argIndex uint8 + arg entity.Entity + argList []interface{} + ) - if argCount, isMethod := p.methodArgCount[name]; isMethod { - for argIndex < argCount && !p.r.EOF() { + for argIndex < methodDef.ArgCount && !p.r.EOF() { // Peek next opcode curOffset = p.r.Offset() nextOpcode, ok := p.nextOpcode() @@ -501,12 +483,12 @@ func (p *Parser) parseNamedRef() bool { } // Check whether all expected arguments have been parsed - if argIndex != argCount { - kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] unexpected arglist end for method %s invocation: expected %d; got %d\n", p.tableName, p.r.Offset(), name, argCount, argIndex) + if argIndex != methodDef.ArgCount { + kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] unexpected arglist end for method %s invocation: expected %d; got %d\n", p.tableName, p.r.Offset(), name, methodDef.ArgCount, argIndex) return false } - return p.scopeCurrent().Append(entity.NewInvocation(p.tableHandle, name, argList)) + return p.scopeCurrent().Append(entity.NewInvocation(p.tableHandle, methodDef, argList)) } // Otherwise this is a reference to a named entity diff --git a/src/gopheros/device/acpi/aml/parser/parser_test.go b/src/gopheros/device/acpi/aml/parser/parser_test.go index fa223a1..78d3bbf 100644 --- a/src/gopheros/device/acpi/aml/parser/parser_test.go +++ b/src/gopheros/device/acpi/aml/parser/parser_test.go @@ -36,6 +36,63 @@ func TestParser(t *testing.T) { } } +func TestParsingOfMethodBodies(t *testing.T) { + var resolver = mockResolver{ + tableFiles: []string{"parser-testsuite-fwd-decls-DSDT.aml"}, + } + + p := NewParser(os.Stderr, genDefaultScopes()) + tableName := strings.Replace(resolver.tableFiles[0], ".aml", "", -1) + if err := p.ParseAML(0, tableName, resolver.LookupTable(tableName)); err != nil { + t.Fatalf("[%s]: %v", tableName, err) + } + + // Collect invocations + var invocations []*entity.Invocation + entity.Visit(0, p.root, entity.TypeAny, func(_ int, ent entity.Entity) bool { + if inv, isInv := ent.(*entity.Invocation); isInv { + invocations = append(invocations, inv) + } + return true + }) + + specs := []struct { + expParentName string + expArgCount int + }{ + // Call to `\NST1` + {`\`, 1}, + // Call to `\_SB.NST1` + {`_SB_`, 2}, + // Call to `\NST1` (first argument of above invocation) + {`\`, 1}, + } + + if exp, got := len(specs), len(invocations); exp != got { + t.Fatalf("expected parser to produce %d method invocations; got %d", exp, got) + } + + for specIndex, spec := range specs { + if got := invocations[specIndex].Method.Parent().Name(); got != spec.expParentName { + t.Errorf( + "[spec %d] expected invocation to target %s.%s; got %s.%s", + specIndex, + spec.expParentName, invocations[specIndex].Method.Name(), + got, invocations[specIndex].Method.Name(), + ) + } + + if got := len(invocations[specIndex].Args()); got != spec.expArgCount { + t.Errorf( + "[spec %d] expected invocation to target %s.%s to receive %d args; got %d", + specIndex, + spec.expParentName, invocations[specIndex].Method.Name(), spec.expArgCount, + got, + ) + } + } +} + func TestTableHandleAssignment(t *testing.T) { var resolver = mockResolver{tableFiles: []string{"parser-testsuite-DSDT.aml"}} @@ -326,11 +383,12 @@ func TestParserErrorHandling(t *testing.T) { }) t.Run("parseNamedRef errors", func(t *testing.T) { - t.Run("missing args", func(t *testing.T) { + t.Run("incorrect args for method", func(t *testing.T) { p.root = entity.NewScope(entity.OpScope, 42, `\`) - p.methodArgCount = map[string]uint8{ - "MTHD": 10, - } + + methodDecl := entity.NewMethod(42, "MTHD") + methodDecl.ArgCount = 5 + p.root.Append(methodDecl) mockParserPayload(p, []byte{ 'M', 'T', 'H', 'D', @@ -605,77 +663,6 @@ func TestParserErrorHandling(t *testing.T) { }) } -func TestDetectMethodDeclarations(t *testing.T) { - p := &Parser{ - errWriter: ioutil.Discard, - } - - validMethod := []byte{ - byte(entity.OpMethod), - 5, // pkgLen - 'M', 'T', 'H', 'D', - 2, // flags (2 args) - } - - t.Run("success", func(t *testing.T) { - mockParserPayload(p, validMethod) - p.methodArgCount = make(map[string]uint8) - p.detectMethodDeclarations() - - argCount, inMap := p.methodArgCount["MTHD"] - if !inMap { - t.Error(`detectMethodDeclarations failed to parse method "MTHD"`) - } - - if exp := uint8(2); argCount != exp { - t.Errorf(`expected arg count for "MTHD" to be %d; got %d`, exp, argCount) - } - }) - - t.Run("bad pkgLen", func(t *testing.T) { - mockParserPayload(p, []byte{ - byte(entity.OpMethod), - // lead byte bits (6:7) indicate 1 extra byte that is missing - byte(1 << 6), - }) - - p.methodArgCount = make(map[string]uint8) - p.detectMethodDeclarations() - }) - - t.Run("error parsing namestring", func(t *testing.T) { - mockParserPayload(p, append([]byte{ - byte(entity.OpMethod), - byte(5), // pkgLen - 10, // bogus char, not part of namestring - }, validMethod...)) - - p.methodArgCount = make(map[string]uint8) - p.detectMethodDeclarations() - - argCount, inMap := p.methodArgCount["MTHD"] - if !inMap { - t.Error(`detectMethodDeclarations failed to parse method "MTHD"`) - } - - if exp := uint8(2); argCount != exp { - t.Errorf(`expected arg count for "MTHD" to be %d; got %d`, exp, argCount) - } - }) - - t.Run("error parsing method flags", func(t *testing.T) { - mockParserPayload(p, []byte{ - byte(entity.OpMethod), - byte(5), // pkgLen - 'F', 'O', 'O', 'F', - // Missing flag byte - }) - - p.methodArgCount = make(map[string]uint8) - p.detectMethodDeclarations() - }) -} - func mockParserPayload(p *Parser, payload []byte) *table.SDTHeader { resolver := fixedPayloadResolver{payload} header := resolver.LookupTable("DSDT") diff --git a/src/gopheros/device/acpi/aml/parser/stream_reader.go b/src/gopheros/device/acpi/aml/parser/stream_reader.go index 4b29d67..fc151e3 100644 --- a/src/gopheros/device/acpi/aml/parser/stream_reader.go +++ b/src/gopheros/device/acpi/aml/parser/stream_reader.go @@ -80,5 +80,8 @@ func (r *amlStreamReader) Offset() uint32 { // SetOffset sets the reader offset to the supplied value. func (r *amlStreamReader) SetOffset(off uint32) { + if max := uint32(len(r.data)); off > max { + off = max + } r.offset = off } diff --git a/src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.aml b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.aml index 00d0f4d23bb18bdcc8ed07d53ee7c2129ff9bb4f..68fbecc66021e36dc20ff68187819d850a13a963 100644 GIT binary patch delta 89 zcmcD^;c^Lf3CUq#U|{+)kxR-$Kq5ZaDPBa#FF3@IX$fy}er{?>MrK|*gNPzf$bb