mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
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
201 lines
6.5 KiB
ArmAsm
201 lines
6.5 KiB
ArmAsm
; vim: set ft=nasm :
|
|
%include "constants.inc"
|
|
|
|
bits 64
|
|
|
|
section .bss
|
|
align 8
|
|
|
|
; According to the "ELF handling for TLS" document section 3.4.6
|
|
; (https://www.akkadia.org/drepper/tls.pdf) for the GNU variant for x86-64,
|
|
; fs:0x00 contains a pointer to the TCB. Variables in the TLS are stored
|
|
; before the TCB and are accessed using negative offsets from the TCB address.
|
|
r0_g_ptr: resq 1
|
|
tcb_ptr: resq 1
|
|
|
|
section .text
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Kernel 64-bit entry point
|
|
;
|
|
; The 32-bit entrypoint code jumps to this entrypoint after:
|
|
; - it has entered long mode and enabled paging
|
|
; - it has loaded a 64bit GDT
|
|
; - it has set up identity paging for the physical 0-8M region and the
|
|
; PAGE_OFFSET to PAGE_OFFSET+8M region.
|
|
;------------------------------------------------------------------------------
|
|
global _rt0_64_entry
|
|
_rt0_64_entry:
|
|
call _rt0_install_redirect_trampolines
|
|
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
|
|
extern multiboot_data
|
|
extern _kernel_start
|
|
extern _kernel_end
|
|
extern kernel.Kmain
|
|
|
|
mov rax, PAGE_OFFSET
|
|
push rax
|
|
mov rax, _kernel_end - PAGE_OFFSET
|
|
push rax
|
|
mov rax, _kernel_start - PAGE_OFFSET
|
|
push rax
|
|
mov rax, multiboot_data
|
|
push rax
|
|
call kernel.Kmain
|
|
|
|
; Main should never return; halt the CPU
|
|
mov rdi, err_kmain_returned
|
|
call write_string
|
|
|
|
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
|
|
|
|
%ifndef SKIP_PAGESIZE_SETUP
|
|
; The Go allocator expects this symbol to be set to the system page size
|
|
; As the kernel bypasses osinit() this needs to be manually set here.
|
|
extern runtime.physPageSize
|
|
mov rax, runtime.physPageSize
|
|
mov qword [rax], 0x1000 ; 4096
|
|
%endif
|
|
|
|
; 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_CURG], rsi ; m.curg = g0
|
|
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 requirements fs:0x0 should point to the
|
|
; TCB.
|
|
mov rax, tcb_ptr
|
|
mov qword [rax], rax
|
|
|
|
; Load 64-bit FS register address
|
|
; eax -> lower 32 bits
|
|
; edx -> upper 32 bits
|
|
mov ecx, 0xc0000100 ; fs_base
|
|
mov rsi, tcb_ptr
|
|
mov rax, rsi ; lower 32 bits
|
|
shr rsi, 32
|
|
mov rdx, rsi ; high 32 bits
|
|
wrmsr
|
|
|
|
ret
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Error messages
|
|
;------------------------------------------------------------------------------
|
|
err_kmain_returned db '[rt0_64] kmain returned', 0
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Write the NULL-terminated string contained in rdi to the screen using white
|
|
; text on red background. Assumes that text-mode is enabled and that its
|
|
; physical address is 0xb8000.
|
|
;------------------------------------------------------------------------------
|
|
write_string:
|
|
mov rbx,0xb8000
|
|
mov ah, 0x4F
|
|
.next_char:
|
|
mov al, byte[rdi]
|
|
test al, al
|
|
jz write_string.done
|
|
|
|
mov word [rbx], ax
|
|
add rbx, 2
|
|
inc rdi
|
|
jmp write_string.next_char
|
|
|
|
.done:
|
|
ret
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Install redirect trampolines. This hack allows us to redirect calls to Go
|
|
; runtime functions to the kernel's own implementation without the need to
|
|
; export/globalize any symbols. This works by first setting up a redirect table
|
|
; (populated by a post-link step) that contains the addresses of the symbol to
|
|
; hook and the address where calls to that symbol should be redirected.
|
|
;
|
|
; This function iterates the redirect table entries and for each entry it
|
|
; sets up a trampoline to the dst symbol and overwrites the code in src with
|
|
; the 14-byte long _rt0_redirect_trampoline code.
|
|
;
|
|
; Note: this code modification is only possible because we are currently
|
|
; operating in supervisor mode with no memory protection enabled. Under normal
|
|
; conditions the .text section should be flagged as read-only.
|
|
;------------------------------------------------------------------------------
|
|
_rt0_install_redirect_trampolines:
|
|
mov rax, _rt0_redirect_table
|
|
mov rdx, NUM_REDIRECTS
|
|
|
|
_rt0_install_redirect_rampolines.next:
|
|
mov rdi, [rax] ; the symbol address to hook
|
|
mov rbx, [rax+8] ; the symbol to redirect to
|
|
|
|
; setup trampoline target and copy it to the hooked symbol
|
|
mov rsi, _rt0_redirect_trampoline
|
|
mov qword [rsi+6], rbx
|
|
mov rcx, 14
|
|
rep movsb ; copy rcx bytes from rsi to rdi
|
|
|
|
add rax, 16
|
|
dec rdx
|
|
jnz _rt0_install_redirect_rampolines.next
|
|
|
|
ret
|
|
|
|
;------------------------------------------------------------------------------
|
|
; This trampoline exploits rip-relative addressing to allow a jump to a
|
|
; 64-bit address without the need to touch any registers. The generated
|
|
; code is equivalent to:
|
|
;
|
|
; jmp [rip+0]
|
|
; dq abs_address_to_jump_to
|
|
;------------------------------------------------------------------------------
|
|
_rt0_redirect_trampoline:
|
|
db 0xff ; the first 6 bytes encode a "jmp [rip+0]" instruction
|
|
db 0x25
|
|
dd 0x00
|
|
dq 0x00 ; the absolute address to jump to
|
|
|
|
;------------------------------------------------------------------------------
|
|
; The redirect table is placed in a dedicated section allowing us to easily
|
|
; find its offset in the kernel image file. As the VMA addresses of the src
|
|
; and target symbols for the redirect are now known in advance we just reserve
|
|
; enough space space for the src and dst addresses using the NUM_REDIRECTS
|
|
; define which is calculated by the Makefile and passed to nasm.
|
|
;------------------------------------------------------------------------------
|
|
section .goredirectstbl
|
|
|
|
_rt0_redirect_table:
|
|
%rep NUM_REDIRECTS
|
|
dq 0 ; src: address of the symbol we want to redirect
|
|
dq 0 ; dst: address of the symbol where calls to src are redirected to
|
|
%endrep
|
|
|
|
|