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.