From 0a05164703809d56ac5abd8b3df2b05f67d1ae1f Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Thu, 30 Nov 2017 06:27:09 +0000 Subject: [PATCH 1/4] acpi: change scopeVisit to visit entities in a visited entity's arglist --- src/gopheros/device/acpi/aml/scope.go | 16 +++++-- src/gopheros/device/acpi/aml/scope_test.go | 52 +++++++++++++++++----- 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/src/gopheros/device/acpi/aml/scope.go b/src/gopheros/device/acpi/aml/scope.go index 51ed528..4057fea 100644 --- a/src/gopheros/device/acpi/aml/scope.go +++ b/src/gopheros/device/acpi/aml/scope.go @@ -36,12 +36,20 @@ func scopeVisit(depth int, ent Entity, entType EntityType, visitorFn Visitor) bo 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 + } + } } - // If the entity defines a scope we need to visit the child entities. - if scopeEnt, ok := ent.(ScopeEntity); ok { - for _, child := range scopeEnt.Children() { - scopeVisit(depth+1, child, entType, visitorFn) + 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) } } diff --git a/src/gopheros/device/acpi/aml/scope_test.go b/src/gopheros/device/acpi/aml/scope_test.go index 7dd8fec..fd2dc85 100644 --- a/src/gopheros/device/acpi/aml/scope_test.go +++ b/src/gopheros/device/acpi/aml/scope_test.go @@ -9,6 +9,9 @@ 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{}) @@ -26,26 +29,53 @@ func TestScopeVisit(t *testing.T) { 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 - keepRecursing bool - wantHits int + searchType EntityType + keepRecursingFn func(Entity) bool + wantHits int }{ - {EntityTypeAny, true, 21}, - {EntityTypeAny, false, 1}, - {EntityTypeDevice, true, 1}, - {EntityTypeProcessor, true, 2}, - {EntityTypePowerResource, true, 3}, - {EntityTypeThermalZone, true, 4}, - {EntityTypeMethod, true, 5}, + {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.keepRecursing + return spec.keepRecursingFn(obj) }) if hits != spec.wantHits { From 79833943900efc4f390f1283606619f66acea2a3 Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Sun, 3 Dec 2017 10:17:41 +0000 Subject: [PATCH 2/4] acpi: implement resolver interface for methodInvocation entities --- src/gopheros/device/acpi/aml/entity.go | 34 +++++++++++++++------ src/gopheros/device/acpi/aml/entity_test.go | 30 ++++++++++++++++++ 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/gopheros/device/acpi/aml/entity.go b/src/gopheros/device/acpi/aml/entity.go index 0e0658b..6a70839 100644 --- a/src/gopheros/device/acpi/aml/entity.go +++ b/src/gopheros/device/acpi/aml/entity.go @@ -88,10 +88,12 @@ func (ent *namedEntity) setArg(argIndex uint8, arg interface{}) bool { func (ent *namedEntity) TableHandle() uint8 { return ent.tableHandle } func (ent *namedEntity) setTableHandle(h uint8) { ent.tableHandle = h } -// constEntity is an unnamedEntity which always evaluates to a constant value. -// Calls to setArg for argument index 0 will memoize the argument value that is +// 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{} @@ -114,7 +116,7 @@ func (ent *constEntity) setOpcode(op opcode) { ent.val = uint64(1<<64 - 1) } } -func (ent *constEntity) Name() string { return "" } +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 } @@ -406,21 +408,33 @@ type namedReference struct { func (ref *namedReference) Resolve(errWriter io.Writer, rootNs ScopeEntity) bool { if ref.target == nil { - ref.target = scopeFind(ref.parent, rootNs, ref.targetName) + 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 + } } - if ref.target == nil { - kfmt.Fprintf(errWriter, "could not resolve referenced symbol: %s (parent: %s)\n", ref.targetName, ref.parent.Name()) - } - - return ref.target != nil + return true } // methodInvocationEntity describes an AML method invocation. type methodInvocationEntity struct { unnamedEntity - methodDef *Method + 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. diff --git a/src/gopheros/device/acpi/aml/entity_test.go b/src/gopheros/device/acpi/aml/entity_test.go index 7e49e97..97ea402 100644 --- a/src/gopheros/device/acpi/aml/entity_test.go +++ b/src/gopheros/device/acpi/aml/entity_test.go @@ -133,6 +133,8 @@ func TestEntityResolveErrors(t *testing.T) { &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 { @@ -141,3 +143,31 @@ func TestEntityResolveErrors(t *testing.T) { } } } + +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 70c798f40ab9ec1364b89f0c71a5bf53f46c85ff Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Sun, 3 Dec 2017 10:21:40 +0000 Subject: [PATCH 3/4] acpi: add pre-process step to capture arg counts for all function decls Since the ACPI standard allows forward function declarations this step is required so we can properly parse the argument list for function invocations. Contrary to other AML entities, method invocations do not include any sort of pkgLength information so unless we track the expected argument count for each function, our parser will not be able to figure out where the argument list ends. --- src/gopheros/device/acpi/aml/parser.go | 69 ++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/src/gopheros/device/acpi/aml/parser.go b/src/gopheros/device/acpi/aml/parser.go index 5a01ddb..6235396 100644 --- a/src/gopheros/device/acpi/aml/parser.go +++ b/src/gopheros/device/acpi/aml/parser.go @@ -21,13 +21,20 @@ type Parser struct { scopeStack []ScopeEntity 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 } // NewParser returns a new AML parser instance. func NewParser(errWriter io.Writer, rootEntity ScopeEntity) *Parser { return &Parser{ - errWriter: errWriter, - root: rootEntity, + errWriter: errWriter, + root: rootEntity, + methodArgCount: make(map[string]uint8), } } @@ -43,7 +50,12 @@ func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDT uint32(unsafe.Sizeof(table.SDTHeader{})), ) - // Pass 1: decode bytecode and build entitites + // 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 p.scopeStack = nil p.scopeEnter(p.root) if !p.parseObjList(header.Length) { @@ -69,6 +81,57 @@ 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 != 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. From 692155b44becfb1e3ce9f215cd0c9fa3ff1db762 Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Sun, 3 Dec 2017 10:25:21 +0000 Subject: [PATCH 4/4] acpi: refine post-parse entity processing rules This commit updates the post-parse step so that: - the visitor not longer recurses into method bodies. Since code inside methods may potentially generate dynamic/scoped entities or even use conditional invocations (if CondRefOf(X) { X(...) }), symbol resolution will be deferred to the AML interpreter. - parent-child relationships between entities are checked and updated if not properly specified --- src/gopheros/device/acpi/aml/parser.go | 71 ++++++----- src/gopheros/device/acpi/aml/parser_test.go | 116 ++++++++++++++++-- .../table/tabletest/parser-testsuite-DSDT.aml | Bin 488 -> 488 bytes .../table/tabletest/parser-testsuite-DSDT.dsl | 4 +- .../parser-testsuite-fwd-decls-DSDT.aml | Bin 0 -> 62 bytes .../parser-testsuite-fwd-decls-DSDT.dsl | 16 +++ 6 files changed, 167 insertions(+), 40 deletions(-) create mode 100644 src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.aml create mode 100644 src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.dsl diff --git a/src/gopheros/device/acpi/aml/parser.go b/src/gopheros/device/acpi/aml/parser.go index 6235396..2b25bdf 100644 --- a/src/gopheros/device/acpi/aml/parser.go +++ b/src/gopheros/device/acpi/aml/parser.go @@ -65,12 +65,27 @@ func (p *Parser) ParseAML(tableHandle uint8, tableName string, header *table.SDT } p.scopeExit() - // Pass 2: resolve forward references + // Pass 3: check parents and resolve forward references var resolveFailed bool scopeVisit(0, p.root, EntityTypeAny, func(_ int, ent Entity) bool { + // Skip method bodies; their contents will be lazily resolved by the interpreter + if _, isMethod := ent.(*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()) + } + } + if res, ok := ent.(resolver); ok && !res.Resolve(p.errWriter, p.root) { resolveFailed = true + return false } + return true }) @@ -159,7 +174,7 @@ func (p *Parser) parseObj() bool { curOffset = p.r.Offset() if info, ok = p.nextOpcode(); !ok { p.r.SetOffset(curOffset) - return p.parseMethodInvocationOrNameRef() + return p.parseNamedRef() } hasPkgLen := info.flags.is(opFlagHasPkgLen) || info.argFlags.contains(opArgTermList) || info.argFlags.contains(opArgFieldList) @@ -415,34 +430,29 @@ func (p *Parser) makeObjForOpcode(info *opcodeInfo) Entity { return obj } -// parseMethodInvocationOrNameRef attempts to parse a method invocation and its term -// args. This method first scans the NameString and performs a lookup. If the -// lookup returns a method definition then we consult it to figure out how many -// arguments we need to parse. +// 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. // // Grammar: // MethodInvocation := NameString TermArgList // TermArgList = Nothing | TermArg TermArgList // TermArg = Type2Opcode | DataObject | ArgObj | LocalObj | MethodInvocation -func (p *Parser) parseMethodInvocationOrNameRef() bool { - invocationStartOffset := p.r.Offset() +func (p *Parser) parseNamedRef() bool { name, ok := p.parseNameString() if !ok { return false } - // Lookup Name and try matching it to a function definition - if methodDef, ok := scopeFind(p.scopeCurrent(), p.root, name).(*Method); ok { - var ( - invocation = &methodInvocationEntity{ - methodDef: methodDef, - } - curOffset uint32 - argIndex uint8 - arg Entity - ) + var ( + curOffset uint32 + argIndex uint8 + arg Entity + argList []interface{} + ) - for argIndex < methodDef.argCount && !p.r.EOF() { + if argCount, isMethod := p.methodArgCount[name]; isMethod { + for argIndex < argCount && !p.r.EOF() { // Peek next opcode curOffset = p.r.Offset() nextOpcode, ok := p.nextOpcode() @@ -453,7 +463,7 @@ func (p *Parser) parseMethodInvocationOrNameRef() bool { arg, ok = p.parseArgObj() default: // It may be a nested invocation or named ref - ok = p.parseMethodInvocationOrNameRef() + ok = p.parseNamedRef() if ok { arg = p.scopeCurrent().lastChild() p.scopeCurrent().removeChild(arg) @@ -466,23 +476,24 @@ func (p *Parser) parseMethodInvocationOrNameRef() bool { break } - invocation.setArg(argIndex, arg) + argList = append(argList, arg) argIndex++ } - if argIndex != methodDef.argCount { - kfmt.Fprintf(p.errWriter, "[table: %s, offset: %d] argument mismatch (exp: %d, got %d) for invocation of method: %s\n", p.tableName, invocationStartOffset, methodDef.argCount, argIndex, name) + // 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) return false } - p.scopeCurrent().Append(invocation) - return true + return p.scopeCurrent().Append(&methodInvocationEntity{ + unnamedEntity: unnamedEntity{args: argList}, + methodName: name, + }) } - // This is a name reference; assume it's a forward reference for now - // and delegate its resolution to a post-parse step. - p.scopeCurrent().Append(&namedReference{targetName: name}) - return true + // Otherwise this is a reference to a named entity + return p.scopeCurrent().Append(&namedReference{targetName: name}) } func (p *Parser) nextOpcode() (*opcodeInfo, bool) { @@ -893,7 +904,7 @@ func (p *Parser) parseTarget() (interface{}, bool) { } // In this case, this is either a NameString or a control method invocation. - if ok := p.parseMethodInvocationOrNameRef(); ok { + if ok := p.parseNamedRef(); ok { obj := p.scopeCurrent().lastChild() p.scopeCurrent().removeChild(obj) return obj, ok diff --git a/src/gopheros/device/acpi/aml/parser_test.go b/src/gopheros/device/acpi/aml/parser_test.go index a7db7ec..654df99 100644 --- a/src/gopheros/device/acpi/aml/parser_test.go +++ b/src/gopheros/device/acpi/aml/parser_test.go @@ -3,6 +3,7 @@ package aml import ( "gopheros/device/acpi/table" "io/ioutil" + "os" "path/filepath" "runtime" "strings" @@ -14,6 +15,7 @@ func TestParser(t *testing.T) { specs := [][]string{ []string{"DSDT.aml", "SSDT.aml"}, []string{"parser-testsuite-DSDT.aml"}, + []string{"parser-testsuite-fwd-decls-DSDT.aml"}, } for specIndex, spec := range specs { @@ -29,7 +31,11 @@ func TestParser(t *testing.T) { 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) + // 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) for _, tableName := range spec { tableName = strings.Replace(tableName, ".aml", "", -1) @@ -91,6 +97,30 @@ func TestTableHandleAssignment(t *testing.T) { } } +func TestParserForwardDeclParsing(t *testing.T) { + var resolver = mockResolver{ + 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) + + for _, tableName := range resolver.tableFiles { + tableName = strings.Replace(tableName, ".aml", "", -1) + if err := p.ParseAML(0, tableName, resolver.LookupTable(tableName)); err != nil { + t.Errorf("[%s]: %v", tableName, err) + break + } + } +} + func TestParsePkgLength(t *testing.T) { specs := []struct { payload []byte @@ -307,13 +337,12 @@ func TestParserErrorHandling(t *testing.T) { } }) - t.Run("parseMethodInvocationOrNameRef errors", func(t *testing.T) { + t.Run("parseNamedRef errors", func(t *testing.T) { t.Run("missing args", func(t *testing.T) { p.root = &scopeEntity{op: opScope, name: `\`} - p.root.Append(&Method{ - scopeEntity: scopeEntity{name: "MTHD"}, - argCount: 10, - }) + p.methodArgCount = map[string]uint8{ + "MTHD": 10, + } mockParserPayload(p, []byte{ 'M', 'T', 'H', 'D', @@ -321,8 +350,8 @@ func TestParserErrorHandling(t *testing.T) { }) p.scopeEnter(p.root) - if p.parseMethodInvocationOrNameRef() { - t.Fatal("expected parseMethodInvocationOrNameRef to return false") + if p.parseNamedRef() { + t.Fatal("expected parseNamedRef to return false") } }) }) @@ -556,6 +585,77 @@ func TestParserErrorHandling(t *testing.T) { }) } +func TestDetectMethodDeclarations(t *testing.T) { + p := &Parser{ + errWriter: ioutil.Discard, + } + + validMethod := []byte{ + byte(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(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(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(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/table/tabletest/parser-testsuite-DSDT.aml b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.aml index dcf8dbd91c96c02be76594ec982c60da30c3e133..5d278a1ccf09445ba8bc3a0d49c02509eb819b12 100644 GIT binary patch delta 54 zcmaFC{DPUwCD{Wb_RUF<^{V22ub2N2`PAXcZ41|H*ZX FSpf3s5*h#i diff --git a/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.dsl b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.dsl index 54968ba..a81a1e2 100644 --- a/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.dsl +++ b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-DSDT.dsl @@ -49,6 +49,8 @@ DefinitionBlock ("parser-testsuite-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0 // Other entity types Event(HLO0) + Mutex(MUT0,1) + Signal(HLO0) // Other executable bits Method (EXE0, 1, Serialized) @@ -79,12 +81,10 @@ DefinitionBlock ("parser-testsuite-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0 Reset(HLO0) // Mutex support - Mutex(MUT0, 1) Acquire(MUT0, 0xffff) // no timeout Release(MUT0) // Signal/Wait - Signal(HLO0) Wait(HLO0, 0xffff) // Get monotonic timer value 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 new file mode 100644 index 0000000000000000000000000000000000000000..00d0f4d23bb18bdcc8ed07d53ee7c2129ff9bb4f GIT binary patch literal 62 zcmZ<>b_ua#U|?XXarY1Ka1DZx{=rN@F;Bk`9}Y$_1rZ*<;1C1GB|y?J1I#gGTvC+5 HrNsaMvk(pe literal 0 HcmV?d00001 diff --git a/src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.dsl b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.dsl new file mode 100644 index 0000000..0df4e12 --- /dev/null +++ b/src/gopheros/device/acpi/table/tabletest/parser-testsuite-fwd-decls-DSDT.dsl @@ -0,0 +1,16 @@ +DefinitionBlock ("parser-testsuite-fwd-decls-DSDT.aml", "DSDT", 2, "GOPHER", "GOPHEROS", 0x00000002) +{ + Method(NST0, 1, NotSerialized) + { + // Invoke a method which has not been defined at the time the parser + // reaches this block (forward declaration) + Return(NST1(Arg0)) + } + + // The declaration of NST1 in the AML stream occurs after the declaration + // of NST0 method above. + Method(NST1, 1, NotSerialized) + { + Return(Arg0+42) + } +}