diff --git a/arch/x86_64/asm/rt0_64.s b/arch/x86_64/asm/rt0_64.s index 83c2077..40e9a0d 100644 --- a/arch/x86_64/asm/rt0_64.s +++ b/arch/x86_64/asm/rt0_64.s @@ -20,12 +20,9 @@ _rt0_idt_desc: ; Allocates space for the IRQ handlers pointers registered by the IRQ package _rt0_irq_handlers resq IDT_ENTRIES -r0_g_ptr: resq 1 ; fs:0x00 is a pointer to the current g struct -r0_g: -r0_g_stack_lo: resq 1 -r0_g_stack_hi: resq 1 -r0_g_stackguard0: resq 1 ; rsp compared to this value in go stack growth prologue -r0_g_stackguard1: resq 1 ; rsp compared to this value in C stack growth prologue +; The FS register is loaded with the address of r0_g_ptr. fs:0x00 should contain +; a pointer to the currently active g struct (in this case runtime.g0) +r0_g_ptr: resq 1 section .text @@ -42,34 +39,7 @@ global _rt0_64_entry _rt0_64_entry: call _rt0_install_redirect_trampolines call _rt0_64_load_idt - - ; According to the x86_64 ABI, the fs:0 should point to the address of - ; the user-space thread structure. The actual TLS structure is located - ; just before that (aligned). Go code tries to fetch the address to the - ; active go-routine's g struct by accessing fs:-8. What we need to do - ; is to setup a mock g0 struct, populate its stack_lo/hi/guard fields - ; and then use wrmsr to update the FS register - extern stack_top - extern stack_bottom - - ; Setup r0_g - mov rax, stack_bottom - mov rbx, stack_top - mov rsi, r0_g - mov qword [rsi+0], rax ; stack_lo - mov qword [rsi+8], rbx ; stack_hi - mov qword [rsi+16], rax ; stackguard0 - mov rax, r0_g_ptr - mov qword [rax], rsi - - ; Load 64-bit FS register address - ; rax -> lower 32 bits - ; rdx -> upper 32 bits - mov ecx, 0xc0000100 ; fs_base - mov rax, rsi ; lower 32 bits - shr rsi, 32 - mov rdx, rsi ; high 32 bits - wrmsr + call _rt0_64_setup_go_runtime_structs ; Call the kernel entry point passing a pointer to the multiboot data ; copied by the 32-bit entry code @@ -93,6 +63,58 @@ _rt0_64_entry: cli hlt +;------------------------------------------------------------------------------ +; Setup m0, g0 and other symbols required for bootstrapping the Go runtime. +; For the definitions of g and m see the Go runtime src: src/runtime/runtime2.go +;------------------------------------------------------------------------------ +_rt0_64_setup_go_runtime_structs: + %include "go_asm_offsets.inc" ; generated by tools/offsets + + ; The Go allocator expects this symbol to be set to the system page size + ; As the kernel bypass osinit() this needs to be set here. + extern runtime.physPageSize + mov rax, runtime.physPageSize + mov qword [rax], 0x1000 ; 4096 + + ; Setup r0_g stack limits using the reserved stack + extern stack_top + extern stack_bottom + extern runtime.g0 + mov rax, stack_bottom + mov rbx, stack_top + mov rsi, runtime.g0 + mov qword [rsi+GO_G_STACK+GO_STACK_LO], rax ; g.stack.lo + mov qword [rsi+GO_G_STACK+GO_STACK_HI], rbx ; g.stack.hi + mov qword [rsi+GO_G_STACKGUARD0], rax ; g.stackguard0 + + ; Link m0 to the g0 + extern runtime.m0 + mov rbx, runtime.m0 + mov qword [rbx+GO_M_G0], rsi ; m.g0 = g0 + mov qword [rsi+GO_G_M], rbx ; g.m = m + + ; Store the address of g0 in r0_g_ptr + mov rax, r0_g_ptr + mov qword [rax], rsi + + ; According to the x86_64 ABI, the fs register should contain the + ; address after the pointer to the pointer to the user-space thread + ; structure. This allows the Go runtime to retrieve the address of + ; the currently active g structure by accessing fs:-0x8. + ; + ; Load 64-bit FS register address + ; eax -> lower 32 bits + ; edx -> upper 32 bits + mov ecx, 0xc0000100 ; fs_base + mov rsi, r0_g_ptr + add rsi, 8 ; fs -> r0_g_ptr + 0x8 + mov rax, rsi ; lower 32 bits + shr rsi, 32 + mov rdx, rsi ; high 32 bits + wrmsr + + ret + ;------------------------------------------------------------------------------ ; Setup and load IDT. We preload each IDT entry with a pointer to a gate handler