This change was made to align the arch name with the one used by Go.
The generic "arch/XXX/asm" dir has also been renamed to "arch/XXX/rt0"
to clearly indicate its purpose
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.
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 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.
The implementation of Printf has been moved from the early package to
the kfmt package. The dependency to ActiveTerminal has been removed and
the code now uses an io.Writer for its output. As Go interfaces cannot
be used before bootstrapping the Go runtime, the code uses a ring-buffer
fallback for storing any kernel output emitted before that point.
The Makefile contains rules for invoking the offsets tool to generate
the offset definitions for members of the g, m and stack structs. The
definitions are stored in BUILD_DIR and BUILD_DIR is passed as an
include target to nasm.
The generated DWARF information contains absolute file paths for the
source files which causes issues when debugging on OSX as GDB cannot
lookup the source files.
Summary of changes:
- when building the gdb target, the source is built with optimizations
and inlining disabled (-N -l)
- source Go gdb helpers when running the gdb target
- set split layout (asm + code)
Allow "make run" and "make gdb" to be used on Linux, not just
non-Linux.
Allow the user to set the go binary from the commandline.
Pass GOROOT in to make different Go installs use the right
Go root.
Check that xorriso is installed, because if it is not, grub-mkrescue
will silently exit.
The switch to 64-bit mode allows us to use 48-bit addressing and to
relocate the kernel to virtual address 0xffff800000000000 + 1M. The
actual kernel is loaded by the bootloader at physical address 1M.
The rt0 code has been split in two parts. The 32-bit part provides the
entrypoint that the bootloader jumps to after loading the kernel. Its
purpose is to make sure that:
- the kernel was booted by a multiboot-compliant bootloader
- the multiboot info structures are copied to a reserved memory block
where they can be accessed after enabling paging
- the CPU meets the minimum requirements for the kernel (CPUID, SSE,
support for long-mode)
Since paging is not enabled when the 32-bit code runs, it needs to
translate all memory addresses it accesses to physical memory addresses
by subtracting PAGE_OFFSET. The 32-bit rt0 code will set up a page table
that identity-maps region: 0 to 8M and region: PAGE_OFFSET to
PAGE_OFFSET+8M. This ensures that when paging gets enabled, we will still
be able to access the kernel using both physical and virtual memory
addresses. After enabling paging, the 32-bit rt0 will jump to a small
64-bit trampoline function that updates the stack pointer to use the
proper virtual address and jumps to the virtual address of the 64-bit
entry point.
The 64-bit entrypoint sets up the minimal g0 structure required by the
go function prologue for stack checks and sets up the FS register to
point to it. The principle is the same as with 32-bit code (a segment
register has the address of a pointer to the active g) with the
difference that in 64-bit mode, the FS register is used instead of GS
and that in order to set its value we need to write to a MSR.
If kernel.Kmain defines a nested function then a symbol like `github.com/achilleasa/gopher-os/kernel.Kmain.func1` will be generated. To make sure we always pick the `github.com/achilleasa/gopher-os/kernel.Kmain` symbol address we just need to add a `$` to the grep argument.
The go compiler exposes a fully qualified symbol for kernel.Kmain that
also includes the full package name (github.com/.../kernel.Kmain). Since nasm
cannot work with external symbols that include slashes we use objcopy to
create an alias symbol "kernel.Kmain" that points to the symbol
generated by the go compiler.
To use the "--add-symbol" argument we need to use objcopy 2.6+. The
makefile was modified to include an additional pre-compile check for the
installed objcopy version.
The makefile provides the following targets:
- kernel
- iso
- run
- gdb
It sniffs the OS type and when running on non-linux hosts it uses
vagrant ssh and runs make with the above targets inside the vagrant box.
The kernel build process consists of the following steps:
1) Compile arch-specific (only x86 for now) assembly files.
2) Run go build -n to obtain the build commands for our kernel. The
makefile sets the build target to 386/linux so that our current rt0
implementation does not need to switch to long-mode.
3) The build commands are then patched to:
- use build/ as the output directory
- force the go linker to use external link mode and to place its output files
(--tmpdir) to the build folder. By forcing external link mode, the go
linker will emit a single go.o file which can be used by ld.
4) We run our own link step and use ld to link the rt0 .o files with the
go.o file and provide a custom linker script to ensure that our
multiboot record is located at the top of the kernel image so that grub
can find it.
The ISO build process sets up a minimal folder structure for building a
bootable ISO (basically the kernel image plus a grub configuration) and
runs grub-mkrescue to produce the ISO file.
Both the run and the gdb targets assume that qemu is installed. The gdb
target starts qemu, attaches the debugger and sets a breakpoint to the
rt0 entrypoint.