Some gate handlers may invoke Go runtime code that clobbers the XMM
registers that could be in use by the code that triggered the gate
handler.
One case where this happens is when a growing a slice and the new
location for the slice data resides in a RO page with a CoW flag. In
this scenario, runtime.growslice will invoke runtime.memmove which may
take a code path that uses XMM registers for copying data around.
During the copy, a page fault occurs and the kernel page fault handler
will detect the CoW flag, allocate a new frame and copy the original
data to the new frame before resuming execution. As the page copy is
performed using the built-in copy function this will cause the XMM
registers to be clobbered.
To prevent this from happening, the asm gate code that gets executed
when an exception occurs will now preserve the XMM regs on the stack
before invoking the registered exception handler.
In addition, for Go 1.9+ the gate code will also temporarily disable use
of AVX instructions by runtime.memmove by setting runtime.useAVXmemmove
to 0. This is required so the gate does not need to also preserve any
AVX registers.
This reverts commit 8f04deadc1f1620dbd0aaea07f04a6c7f82f58c0.
The actual issue was triggered by a memory corruption due to the fact
that the exception handling gate code did not preserve XMM regs.
The bug manifested itself as a series of null bytes appearing to the
tty device when the ring buffer gets linked to it and its contents are
flushed to the tty.
Using gdb, I was able to track the problem into the ringbuf's Read
method and specifically to the call to copy(). I have replaced the call
with a for loop that the compiler would anyway optimize into a rep stosb
or equivalent asm instruction.
The .travis.yml file has been updated so that CI will also attempt to
build the kernel using the latest go branches from 1.7, 1.8, 1.9, 1.10,
1.x.
The tests will only be run once against the latest go version.
Due to some changes in the runtime init functions between go 1.7 and
later versions (e.g. runtime.modulesInit() defined after go 1.7), the
kernel code that bootstraps the go runtime had to be split into separate
files which are conditionally compiled using +build flags.
Older go versions (1.7.x) specify a fixed page size (_PageSize const) as
part of their runtime whereas newer go versions populate the page size at
runtime.
The kernel asm bootstrap code was written with go 1.8 in mind. As a
result it attempts to populate the page size manually which obviously
breaks compilation in go 1.7.
The offsets tool has been updated to emit the special def
"SKIP_PAGESIZE_SETUP" when running under go 1.7 which allows us to
perform conditional compilation of the page setup code inside the
bootstrap asm code.
fixup
The code responsible for mapping the kernel sections worked under the
assumption that the linker would align all sections on a page boundary.
To figure out the page extents for a particular section, the
implementation would round the section VMA down to the nearest page and
add to that the section length rounded up to the nearest page size.
However, some sections (e.g. noptrbss) use 32 byte alignment. Depending
on the section length, the original implementation could in some cases
skip mapping of the last page in the section which would cause a page
fault when its contents were accessed.
The fix for this issue is quite simple: calculate the end page by
rounding up (section start addr + section length) to the next page.
The fuzzer can be invoked by running: "make test-fuzz". The AML parser
test suite has been augmented with a special "TestParserCrashers"
function that can be used to replay corpuses identified by go-fuzz as
causing parser crashes.
The test can be invoked as:
go test -v -run TestParserCrashers -aml-replace-crashers-from
$BUILD/fuzz/corpus/src_gopheros_device_acpi_aml/crashers
where $BUILD is the output directory (default: build/) defined in the
Makefile.
All dumps are located in the tabletest package. The DSDT/SSDT dumps were
obtained by running an aml dump tool inside a virtualbox instance. The
dumps were disassembled using the iasl tool (version 20180105) from
Intel's reference ACPICA implementation.
The parser-testsuite dumps were written by hand to ensure that all
possible happy-paths in the parser were followed and then compiled into
AML using the same iasl tool.
The added TestParser function attempts to parse various sets of AML
dumps and then uses the object tree pretty-printer to obtain a dump of
the tree. The dump is then compared to an expected value (.exp files are
also placed in the tabletest package). The test code supports passing
the "-aml-regenerate-parser-exp-files" flag to update the exp files:
go test -run TestParser -aml-regenerate-parser-exp-files
ACPI entity definitions form a tree whose roots are a sequence of
pre-defined namespace objects. The parser stores all AML entities using
a space-optimized structure (Object). These objects are organized into a
tree via the ObjectTree structure.
Instead of storing pointers to other objects (i.e. siblings, children or
parent), objects use uint32 indices to objects managed by the
ObjectTree. This has the nice advantage of reducing the memory
requirements for our tree in half when running on 64-bits (4-bytes per
index vs 8-bytes per pointer) while also allowing us to recycle objects
that are explicitly freed by the parser.
The opcode tables establish a 2-level mapping between AML opcodes (regular
or extended) and a secondary table that allows the parser to decode each
opcode. This information includes:
- the opcode name
- the opcode flags (e.g. specifies a named object or parsing must be
deferred to a later pass)
- the expected arguments for each opcode and their types.
The list provides a uniform mapping for regular (one byte), extended
(0x1b + one byte) opcodes as well as some "internal" opcodes that will be
used by the parser to represent method calls, named fields and resolved
named object references.
The existing parser implementation has several issues and will, in many
cases incorrectly parse AML bytestreams that contain (among other
things):
- ambiguous method calls (same method name defined in multiple scopes)
- bank fields
- buffer fields where the length arg contains a method call
- named objects containing one or more '^' prefixes when defined inside
nested Scope elements (e.g. Scope(_SBRG){ Device(^PCIE){...} })
Unfortunately, these issues were discovered quite late while working on
the AML interpreter and while an attempt was made to correct some of
these (see previous commits), it turns out that the current codebase
cannot be refactored to fix all issues.
I have therefore decided to get rid of the current implementation and
replace it with a new one which will be created from scratch to address
all the above issues.
This commit just cleans up the codebase so the new parser can be added
via a future PR.
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.
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)
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
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.