1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00
2018-03-09 07:14:12 +00:00

600 lines
18 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package aml
import (
"bytes"
"gopheros/kernel/kfmt"
"io"
)
const (
// InvalidIndex is a sentinel value used by ObjectTree to indicate that
// a returned object index is not valid.
InvalidIndex uint32 = (1 << 32) - 1
// The size of AML name identifiers in bytes.
amlNameLen = 4
)
// fieldElement groups together information about a field element. This
// information can also be obtained by scanning a field element's siblings but
// it is summarized in this structure for convenience.
type fieldElement struct {
// The offset in the address space defined by parent field.
offset uint32
// The width of this field element in bits.
width uint32
accessLength uint8
_pad [3]uint8
accessType uint8
accessAttrib uint8
lockType uint8
updateType uint8
// The index of an Object to use as a connection target. Used only for
// GenericSerialBus and GeneralPurposeIO operation regions.
connectionIndex uint32
// The index of the Field (pOpField, pOpIndexField or pOpBankField) that this
// field element belongs to. According to the spec, named fields appear
// at the same scope as their parent.
fieldIndex uint32
}
// Object describes an entity encoded in the AML bytestream.
type Object struct {
// The AML ocode that describes this oBject.
opcode uint16
// The Index in the opcodeTable for this opcode
infoIndex uint8
// The table handle which contains this entity.
tableHandle uint8
// Named AML entities provide a fixed-width name which is padded by '_' chars.
name [amlNameLen]byte
// The following indices refer to other Objects in the ObjectTree
// that allocated this Object instance. Uninitialized indices are
// represented via a math.MaxUint32 value.
index uint32
parentIndex uint32
prevSiblingIndex uint32
nextSiblingIndex uint32
firstArgIndex uint32
lastArgIndex uint32
// The byte offset in the AML stream where this opcode is defined.
amlOffset uint32
// A non-zero value for pkgEnd indicates that this opcode requires deferred
// parsing due to its potentially ambiguous contents.
pkgEnd uint32
// A value placeholder for entites that contain values (e.g. int
// or string constants byte slices e.t.c)
value interface{}
}
// ObjectTree is a structure that contains a tree of AML entities where each
// entity is allocated from a contiguous Object pool. Index #0 of the pool
// contains the root scope ('\') of the AML tree.
type ObjectTree struct {
objPool []*Object
freeListHeadIndex uint32
}
// NewObjectTree returns a new ObjectTree instance.
func NewObjectTree() *ObjectTree {
return &ObjectTree{
freeListHeadIndex: InvalidIndex,
}
}
// CreateDefaultScopes populates the Object pool with the default scopes
// specified by the ACPI standard:
//
// +-[\] (Root scope)
// +- [_GPE] (General events in GPE register block)
// +- [_PR_] (ACPI 1.0 processor namespace)
// +- [_SB_] (System bus with all device objects)
// +- [_SI_] (System indicators)
// +- [_TZ_] (ACPI 1.0 thermal zone namespace)
func (tree *ObjectTree) CreateDefaultScopes(tableHandle uint8) {
root := tree.newNamedObject(pOpIntScopeBlock, tableHandle, [amlNameLen]byte{'\\'})
tree.append(root, tree.newNamedObject(pOpIntScopeBlock, tableHandle, [amlNameLen]byte{'_', 'G', 'P', 'E'})) // General events in GPE register block
tree.append(root, tree.newNamedObject(pOpIntScopeBlock, tableHandle, [amlNameLen]byte{'_', 'P', 'R', '_'})) // ACPI 1.0 processor namespace
tree.append(root, tree.newNamedObject(pOpIntScopeBlock, tableHandle, [amlNameLen]byte{'_', 'S', 'B', '_'})) // System bus with all device objects
tree.append(root, tree.newNamedObject(pOpIntScopeBlock, tableHandle, [amlNameLen]byte{'_', 'S', 'I', '_'})) // System indicators
tree.append(root, tree.newNamedObject(pOpIntScopeBlock, tableHandle, [amlNameLen]byte{'_', 'T', 'Z', '_'})) // ACPI 1.0 thermal zone namespace
}
// newObject allocates a new Object from the Object pool, populates its
// contents and returns back a pointer to it.
func (tree *ObjectTree) newObject(opcode uint16, tableHandle uint8) *Object {
var obj *Object
// Check the free list first
if tree.freeListHeadIndex != InvalidIndex {
obj = tree.objPool[tree.freeListHeadIndex]
tree.freeListHeadIndex = obj.nextSiblingIndex
} else {
// Allocate new object and attach it to the pool
obj = new(Object)
obj.index = uint32(len(tree.objPool))
tree.objPool = append(tree.objPool, obj)
}
obj.opcode = opcode
obj.infoIndex = pOpcodeTableIndex(opcode, true)
obj.tableHandle = tableHandle
obj.parentIndex = InvalidIndex
obj.prevSiblingIndex = InvalidIndex
obj.nextSiblingIndex = InvalidIndex
obj.firstArgIndex = InvalidIndex
obj.lastArgIndex = InvalidIndex
obj.value = nil
return obj
}
// newNamedObject allocates a new Object from the Object pool, populates its
// name and returns back a pointer to it.
func (tree *ObjectTree) newNamedObject(opcode uint16, tableHandle uint8, name [amlNameLen]byte) *Object {
obj := tree.newObject(opcode, tableHandle)
obj.name = name
return obj
}
// append appends arg to obj's argument list.
func (tree *ObjectTree) append(obj, arg *Object) {
arg.parentIndex = obj.index
if obj.lastArgIndex == InvalidIndex {
obj.firstArgIndex = arg.index
obj.lastArgIndex = arg.index
return
}
LastArg := tree.ObjectAt(obj.lastArgIndex)
LastArg.nextSiblingIndex = arg.index
arg.prevSiblingIndex = LastArg.index
arg.nextSiblingIndex = InvalidIndex
obj.lastArgIndex = arg.index
}
// appendAfter appends arg to obj's argument list after nextTo.
func (tree *ObjectTree) appendAfter(obj, arg, nextTo *Object) {
// nextTo is the last arg of obj; this is equivalent to a regular append
if nextTo.nextSiblingIndex == InvalidIndex {
tree.append(obj, arg)
return
}
arg.parentIndex = obj.index
arg.prevSiblingIndex = nextTo.index
arg.nextSiblingIndex = nextTo.nextSiblingIndex
tree.ObjectAt(arg.nextSiblingIndex).prevSiblingIndex = arg.index
nextTo.nextSiblingIndex = arg.index
}
// free appends obj to the tree's free object list allowing it to be re-used by
// future calls to NewObject and NewNamedObject. Callers must ensure that any
// held pointers to the object are not used after calling free.
func (tree *ObjectTree) free(obj *Object) {
if obj.parentIndex != InvalidIndex {
tree.detach(tree.ObjectAt(obj.parentIndex), obj)
}
if obj.firstArgIndex != InvalidIndex || obj.lastArgIndex != InvalidIndex {
panic("aml.ObjectTree: attempted to free object that still contains argument references")
}
// Push the object to the top of the free list and change its opcode to
// indicate this is a freed node
obj.opcode = pOpIntFreedObject
obj.nextSiblingIndex = tree.freeListHeadIndex
tree.freeListHeadIndex = obj.index
}
// detach detaches arg from obj's argument list.
func (tree *ObjectTree) detach(obj, arg *Object) {
if obj.firstArgIndex == arg.index {
obj.firstArgIndex = arg.nextSiblingIndex
}
if obj.lastArgIndex == arg.index {
obj.lastArgIndex = arg.prevSiblingIndex
}
if arg.nextSiblingIndex != InvalidIndex {
tree.ObjectAt(arg.nextSiblingIndex).prevSiblingIndex = arg.prevSiblingIndex
}
if arg.prevSiblingIndex != InvalidIndex {
tree.ObjectAt(arg.prevSiblingIndex).nextSiblingIndex = arg.nextSiblingIndex
}
arg.prevSiblingIndex = InvalidIndex
arg.nextSiblingIndex = InvalidIndex
arg.parentIndex = InvalidIndex
}
// ObjectAt returns a pointer to the Object at the specified index or nil if
// no object with this index exists inside the object tree.
func (tree *ObjectTree) ObjectAt(index uint32) *Object {
if index >= uint32(len(tree.objPool)) {
return nil
}
obj := tree.objPool[index]
if obj.opcode == pOpIntFreedObject {
return nil
}
return obj
}
// Find attempts to resolve the given expression into an Object 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,
// one that starts with a ‘\’ prefix), and a relative namespace path (that is,
// one that is relative to the current namespace). The namespace search rules
// discussed above, only apply to single NameSeg paths, which is a relative
// namespace path. For those relative name paths that contain multiple NameSegs
// 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 (tree *ObjectTree) Find(scopeIndex uint32, expr []byte) uint32 {
exprLen := len(expr)
if exprLen == 0 || scopeIndex == InvalidIndex {
return InvalidIndex
}
switch {
case expr[0] == '\\': // relative to the root scope
// Name was just `\`; this matches the root namespace
if exprLen == 1 {
return 0
}
return tree.findRelative(0, expr[1:])
case expr[0] == '^': // relative to the parent scope(s)
for startIndex := 0; startIndex < exprLen; startIndex++ {
switch expr[startIndex] {
case '^':
// Mpve up one parent. If we were at the root scope
// then the lookup failed.
if scopeIndex = tree.ObjectAt(scopeIndex).parentIndex; scopeIndex == InvalidIndex {
return InvalidIndex
}
default:
// Found the start of the name. Look it up relative to current scope
return tree.findRelative(scopeIndex, expr[startIndex:])
}
}
// Name was just a sequence of '^'; this matches the last scopeIndex value
return scopeIndex
case exprLen > amlNameLen:
// The expression consists of multiple name segments joined together (e.g. FOOFBAR0)
// In this case we need to apply relative lookup rules for FOOF.BAR0
return tree.findRelative(scopeIndex, expr)
case exprLen == amlNameLen:
// expr is a simple name. According to the spec, we need to
// search for it in this scope and all its parent scopes till
// we reach the root.
for nextScopeIndex := scopeIndex; nextScopeIndex != InvalidIndex; nextScopeIndex = tree.ObjectAt(nextScopeIndex).parentIndex {
scopeObj := tree.ObjectAt(nextScopeIndex)
checkNextSibling:
for nextIndex := scopeObj.firstArgIndex; nextIndex != InvalidIndex; nextIndex = tree.ObjectAt(nextIndex).nextSiblingIndex {
obj := tree.ObjectAt(nextIndex)
for byteIndex := 0; byteIndex < amlNameLen; byteIndex++ {
if expr[byteIndex] != obj.name[byteIndex] {
continue checkNextSibling
}
}
// Found match
return obj.index
}
}
}
// Not found
return InvalidIndex
}
// findRelative attempts to resolve an object using relative scope lookup rules.
func (tree *ObjectTree) findRelative(scopeIndex uint32, expr []byte) uint32 {
exprLen := len(expr)
nextSegment:
for segIndex := 0; segIndex < exprLen; segIndex += amlNameLen {
// If expr contains a dual or multinamed path then we may encounter special
// prefix chars in the stream (the parser extracts the raw data). In this
// case skip over them.
for ; segIndex < exprLen && expr[segIndex] != '_' && (expr[segIndex] < 'A' || expr[segIndex] > 'Z'); segIndex++ {
}
if exprLen-segIndex < amlNameLen {
return InvalidIndex
}
// Search current scope for an entity matching the next name segment
scopeObj := tree.ObjectAt(scopeIndex)
checkNextSibling:
for nextIndex := scopeObj.firstArgIndex; nextIndex != InvalidIndex; nextIndex = tree.ObjectAt(nextIndex).nextSiblingIndex {
obj := tree.ObjectAt(nextIndex)
for byteIndex := 0; byteIndex < amlNameLen; byteIndex++ {
if expr[segIndex+byteIndex] != obj.name[byteIndex] {
continue checkNextSibling
}
}
// Found match; set match as the next scope index and
// try to match the next segment
scopeIndex = nextIndex
continue nextSegment
}
// Failed to match next segment. Lookup failed
return InvalidIndex
}
// scopeIndex contains the index of the last matched name in the expression
// which is the target we were looking for.
return scopeIndex
}
// ClosestNamedAncestor returns the index of the first named object that is an
// ancestor of obj. If any of obj's parents are unresolved scope directives
// then the call will return InvalidIndex.
func (tree *ObjectTree) ClosestNamedAncestor(obj *Object) uint32 {
if obj == nil {
return InvalidIndex
}
for ancestorIndex := obj.parentIndex; ancestorIndex != InvalidIndex; {
ancestor := tree.ObjectAt(ancestorIndex)
if ancestor.opcode == pOpScope {
break
}
if pOpcodeTable[ancestor.infoIndex].flags&pOpFlagNamed != 0 {
return ancestorIndex
}
ancestorIndex = ancestor.parentIndex
}
return InvalidIndex
}
// NumArgs returns the number of arguments contained in obj.
func (tree *ObjectTree) NumArgs(obj *Object) uint32 {
if obj == nil {
return 0
}
var argCount uint32
for siblingIndex := obj.firstArgIndex; siblingIndex != InvalidIndex; siblingIndex = tree.ObjectAt(siblingIndex).nextSiblingIndex {
argCount++
}
return argCount
}
// ArgAt returns a pointer to obj's arg located at index.
func (tree *ObjectTree) ArgAt(obj *Object, index uint32) *Object {
if obj == nil {
return nil
}
for argIndex, siblingIndex := uint32(0), obj.firstArgIndex; siblingIndex != InvalidIndex; argIndex, siblingIndex = argIndex+1, tree.ObjectAt(siblingIndex).nextSiblingIndex {
if argIndex == index {
return tree.ObjectAt(siblingIndex)
}
}
return nil
}
// PrettyPrint outputs a pretty-printed version of the AML tree to w.
func (tree *ObjectTree) PrettyPrint(w io.Writer) {
if len(tree.objPool) != 0 {
var padBuf bytes.Buffer
padBuf.WriteByte(' ')
tree.toString(w, &padBuf, 0)
}
}
func (tree *ObjectTree) toString(w io.Writer, padBuf *bytes.Buffer, index uint32) {
curObj := tree.ObjectAt(index)
_, _ = w.Write(padBuf.Bytes())
kfmt.Fprintf(w, "+- [%s", pOpcodeName(curObj.opcode))
if name := nameOf(curObj); len(name) != 0 {
kfmt.Fprintf(w, ", name: \"%s\"", name)
}
if curObj.opcode == pOpMethod {
kfmt.Fprintf(w, ", argCount: %d", uint8(tree.ArgAt(curObj, 1).value.(uint64)&0x7))
}
kfmt.Fprintf(w, ", table: %d, index: %d, offset: 0x%x", curObj.tableHandle, curObj.index, curObj.amlOffset)
kfmt.Fprintf(w, "]")
if curObj.opcode == pOpIntMethodCall {
methodObj := tree.ObjectAt(curObj.value.(uint32))
argCount := uint8(tree.ArgAt(methodObj, 1).value.(uint64) & 0x7)
kfmt.Fprintf(w, " -> [call to \"%s\", argCount: %d, table: %d, index: %d, offset: 0x%x]", methodObj.name[:], argCount, methodObj.tableHandle, methodObj.index, methodObj.amlOffset)
} else if curObj.opcode == pOpIntResolvedNamePath {
resolvedObj := tree.ObjectAt(curObj.value.(uint32))
kfmt.Fprintf(w, " -> [resolved to \"%s\", table: %d, index: %d, offset: 0x%x]", nameOf(resolvedObj), resolvedObj.tableHandle, resolvedObj.index, resolvedObj.amlOffset)
} else if curObj.opcode == pOpIntNamedField {
field := curObj.value.(*fieldElement)
kfmt.Fprintf(w, " -> [field index: %d, offset(bytes): 0x%x, width(bits): 0x%x, accType: ", field.fieldIndex, field.offset, field.width)
switch field.accessType {
case 0x00:
kfmt.Fprintf(w, "Any")
case 0x01:
kfmt.Fprintf(w, "Byte")
case 0x02:
kfmt.Fprintf(w, "Word")
case 0x03:
kfmt.Fprintf(w, "Dword")
case 0x04:
kfmt.Fprintf(w, "Qword")
case 0x05:
kfmt.Fprintf(w, "Buffer, accAttr: ")
switch field.accessAttrib {
case 0x02:
kfmt.Fprintf(w, "Quick")
case 0x04:
kfmt.Fprintf(w, "SendReceive")
case 0x06:
kfmt.Fprintf(w, "Byte")
case 0x08:
kfmt.Fprintf(w, "Word")
case 0x0a:
kfmt.Fprintf(w, "Block")
case 0x0b:
kfmt.Fprintf(w, "Bytes(0x%x)", field.accessLength)
case 0x0c:
kfmt.Fprintf(w, "ProcessCall")
case 0x0d:
kfmt.Fprintf(w, "BlockProcessCall")
case 0x0e:
kfmt.Fprintf(w, "RawBytes(0x%x)", field.accessLength)
case 0x0f:
kfmt.Fprintf(w, "RawProcessBytes(0x%x)", field.accessLength)
}
/*
case 0x40:
kfmt.Fprintf(w, "Bytes(0x%x)", field.AccessAttrib)
case 0x80:
kfmt.Fprintf(w, "RawBytes(0x%x)", field.AccessAttrib)
case 0xc0:
kfmt.Fprintf(w, "RawProcessBytes(0x%x)", field.AccessAttrib)
*/
}
kfmt.Fprintf(w, ", lockType: ")
switch field.lockType {
case 0x00:
kfmt.Fprintf(w, "NoLock")
case 0x01:
kfmt.Fprintf(w, "Lock")
}
kfmt.Fprintf(w, ", updateType: ")
switch field.updateType {
case 0x00:
kfmt.Fprintf(w, "Preserve")
case 0x01:
kfmt.Fprintf(w, "WriteAsOnes")
case 0x02:
kfmt.Fprintf(w, "WriteAsZeroes")
}
switch field.connectionIndex {
case InvalidIndex:
kfmt.Fprintf(w, ", connection: -]")
default:
kfmt.Fprintf(w, ", connection: index %d]", field.connectionIndex)
}
} else if curObj.opcode == pOpStringPrefix {
kfmt.Fprintf(w, " -> [string value: \"%s\"]", curObj.value.([]byte))
} else if curObj.opcode == pOpIntNamePath {
kfmt.Fprintf(w, " -> [namepath: \"%s\"]", curObj.value.([]byte))
} else if curObj.value != nil {
switch v := curObj.value.(type) {
case uint64:
kfmt.Fprintf(w, " -> [num value; dec: %d, hex: 0x%x]", v, v)
// If this is an encoded EISA id convert it back to a string
if curObj.opcode == pOpDwordPrefix && tree.ObjectAt(curObj.parentIndex).name == [amlNameLen]byte{'_', 'H', 'I', 'D'} {
// Poor-man's ntohl
id := uint32((v>>24)&0xff) |
uint32((v>>16)&0xff)<<8 |
uint32((v>>8)&0xff)<<16 |
uint32(v&0xff)<<24
var eisaID = [7]byte{
'@' + (byte)((id>>26)&0x1f),
'@' + (byte)((id>>21)&0x1f),
'@' + (byte)((id>>16)&0x1f),
hexToASCII(id >> 12),
hexToASCII(id >> 8),
hexToASCII(id >> 4),
hexToASCII(id),
}
kfmt.Fprintf(w, " [EISA: \"%s\"]", eisaID[:])
}
case []byte:
kfmt.Fprintf(w, " -> [bytelist value; len: %d; data: [", len(v))
for i, b := range v {
if i != 0 {
kfmt.Fprintf(w, ", ")
}
kfmt.Fprintf(w, "0x%x", uint8(b))
}
kfmt.Fprintf(w, "]]")
}
}
kfmt.Fprintf(w, "\n")
padLen := padBuf.Len()
if curObj.nextSiblingIndex == InvalidIndex {
padBuf.WriteByte(' ')
} else {
padBuf.WriteByte('|')
}
padBuf.WriteByte(' ')
padBuf.WriteByte(' ')
for argIndex := curObj.firstArgIndex; argIndex != InvalidIndex; argIndex = tree.ObjectAt(argIndex).nextSiblingIndex {
tree.toString(w, padBuf, argIndex)
}
padBuf.Truncate(padLen)
}
func hexToASCII(val uint32) byte {
v := byte(val & 0xf)
if v <= 9 {
return '0' + v
}
return 'A' + (v - 0xa)
}
func nameOf(obj *Object) []byte {
var nameStartIndex, nameEndIndex int
for ; nameStartIndex < amlNameLen; nameStartIndex++ {
if ch := obj.name[nameStartIndex]; (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '\\' {
break
}
}
for nameEndIndex = nameStartIndex; nameEndIndex < amlNameLen; nameEndIndex++ {
if ch := obj.name[nameEndIndex]; !((ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '\\') {
break
}
}
if nameStartIndex != amlNameLen {
return obj.name[nameStartIndex:nameEndIndex]
}
return nil
}