1
0
mirror of https://github.com/taigrr/gopher-os synced 2026-03-23 08:12:26 -07:00

Use pwd as a workspace; move sources to src/gopheros and rewrite imports

By setting up pwd as a Go workspace, we can trim import paths from
something like "github.com/achilleasa/gopher-os/kernel" to just
"kernel".

These changes make forking easier and also allows us to move the code to
a different git hosting provider without having to rewrite the imports.
This commit is contained in:
Achilleas Anagnostopoulos
2017-07-01 20:37:09 +01:00
parent 7b93d01c6e
commit 8dfc5d4e92
61 changed files with 93 additions and 114 deletions

View File

@@ -0,0 +1,26 @@
; vim: set ft=nasm :
section .text
bits 64
global x_cgo_callers
global x_cgo_init
global x_cgo_mmap
global x_cgo_notify_runtime_init_done
global x_cgo_sigaction
global x_cgo_thread_start
global x_cgo_setenv
global x_cgo_unsetenv
global _cgo_yield
; Stubs for missing cgo functions to keep the linker happy
x_cgo_callers:
x_cgo_init:
x_cgo_mmap:
x_cgo_notify_runtime_init_done:
x_cgo_sigaction:
x_cgo_thread_start:
x_cgo_setenv:
x_cgo_unsetenv:
_cgo_yield:
ret

View File

@@ -0,0 +1,12 @@
; vim: set ft=nasm :
; The bootloader load the kernel at LOAD_ADDRESS and jumps to the rt0_32 entrypoint
; at this address.
LOAD_ADDRESS equ 0x100000
; Page offset is the start of the 48-bit upper half canonical memory region
; The kernel is compiled with a VMA equal to PAGE_OFFSET + LOAD_ADDRESS but
; loaded at physical address LOAD_ADDRESS.
PAGE_OFFSET equ 0xffff800000000000

View File

View File

@@ -0,0 +1,41 @@
; vim: set ft=nasm :
section .multiboot_header
MAGIC equ 0xe85250d6
ARCH equ 0x0
; Define the multiboot header (multiboot 1.6)
; http://nongnu.askapache.com/grub/phcoder/multiboot.pdf
header_start:
dd MAGIC ; magic number
dd ARCH ; i386 protected mode
dd header_end - header_start ; header length
; The field checksum is a 32-bit unsigned value which, when added to the other
; magic fields (i.e. magic, architecture and header_length), must have a
; 32-bit unsigned sum of zero.
dd (1 << 32) - (MAGIC + ARCH + (header_end - header_start))
; Console flags tag
align 8 ; tags should be 64-bit aligned
dw 4 ; type
dw 0 ; flags
dd 12 ; size
dd 0x3 ; kernel supports EGA console
; Define graphics mode tag
;align 8 ; tags should be 64-bit aligned
;dw 5 ; type
;dw 0 ; flags
;dd 20 ; size
;dd 80 ; width (pixels or chars)
;dd 25 ; height (pixels or chars)
;dd 0 ; bpp (0 for text mode
; According to page 6 of the spec, the tag list is terminated by a tag with
; type 0 and size 8
align 8 ; tags should be 64-bit aligned
dd 0 ; type & flag = 0
dd 8 ; size
header_end:

View File

@@ -0,0 +1,364 @@
; vim: set ft=nasm :
%include "constants.inc"
section .data
align 4
; GDT definition
gdt0:
gdt0_nil_seg: dw 0 ; Limit (low)
dw 0 ; Base (low)
db 0 ; Base (middle)
db 0 ; Access (exec/read)
db 0 ; Granularity
db 0 ; Base (high)
gdt0_cs_seg: dw 0 ; Limit (low)
dw 0 ; Base (low)
db 0 ; Base (middle)
db 10011010b ; Access (exec/read)
db 00100000b ; Granularity
db 0 ; Base (high)
gdt0_ds_seg: dw 0 ; Limit (low)
dw 0 ; Base (low)
db 0 ; Base (middle)
db 10010010b ; Access (read/write)
db 00000000b ; Granularity
db 0 ; Base (high)
gdt0_desc:
dw $ - gdt0 - 1 ; gdt size should be 1 byte less than actual length
dq gdt0 - PAGE_OFFSET
NULL_SEG equ gdt0_nil_seg - gdt0
CS_SEG equ gdt0_cs_seg - gdt0
DS_SEG equ gdt0_ds_seg - gdt0
;------------------------------------------------------------------------------
; Error messages
;------------------------------------------------------------------------------
err_unsupported_bootloader db '[rt0_32] kernel not loaded by multiboot-compliant bootloader', 0
err_multiboot_data_too_big db '[rt0_32] multiboot information data length exceeds local buffer size', 0
err_cpuid_not_supported db '[rt0_32] the processor does not support the CPUID instruction', 0
err_longmode_not_supported db '[rt0_32] the processor does not support longmode which is required by this kernel', 0
err_sse_not_supported db '[rt0_32] the processor does not support SSE instructions which are required by this kernel', 0
section .bss
align 4096
; Reserve 3 pages for the initial page tables
page_table_l4: resb 4096
page_table_l3: resb 4096
page_table_l2: resb 4096
; Reserve 16K for storing multiboot data and for the kernel stack
global multiboot_data ; Make this available to the 64-bit entrypoint
global stack_bottom
global stack_top
multiboot_data: resb 16384
stack_bottom: resb 16384
stack_top:
section .rt0
bits 32
align 4
;------------------------------------------------------------------------------
; Kernel 32-bit entry point
;
; The boot loader will jump to this symbol after setting up the CPU according
; to the multiboot standard. At this point:
; - A20 is enabled
; - The CPU is using 32-bit protected mode
; - Interrupts are disabled
; - Paging is disabled
; - EAX contains the magic value 0x36d76289; the presence of this value indicates
; to the operating system that it was loaded by a Multiboot-compliant boot loader
; - EBX contains the 32-bit physical address of the Multiboot information structure
;------------------------------------------------------------------------------
global _rt0_32_entry
_rt0_32_entry:
; Provide a stack
mov esp, stack_top - PAGE_OFFSET
; Ensure we were booted by a bootloader supporting multiboot
cmp eax, 0x36d76289
jne _rt0_32_entry.unsupported_bootloader
; Copy multiboot struct to our own buffer
call _rt0_copy_multiboot_data
; Check processor features
call _rt0_check_cpuid_support
call _rt0_check_longmode_support
call _rt0_check_sse_support
; Setup initial page tables, enable paging and enter longmode
call _rt0_populate_initial_page_tables
call _rt0_enter_long_mode
call _rt0_64_entry_trampoline
.unsupported_bootloader:
mov edi, err_unsupported_bootloader - PAGE_OFFSET
call write_string
jmp _rt0_32_entry.halt
.halt:
cli
hlt
;------------------------------------------------------------------------------
; Copy multiboot information blocks from the address pointed to by ebx into a
; local buffer. This enables the kernel code to access them once paging is enabled.
;------------------------------------------------------------------------------
_rt0_copy_multiboot_data:
mov esi, ebx
mov edi, multiboot_data - PAGE_OFFSET
mov ecx, dword [esi]
cmp ecx, 16384
jle _rt0_copy_multiboot_data.copy
mov edi, err_multiboot_data_too_big - PAGE_OFFSET
call write_string
jmp _rt0_32_entry.halt
.copy:
test ecx, ecx
jz _rt0_copy_multiboot_data.done
mov eax, dword[esi]
mov dword [edi], eax
add esi, 4
add edi, 4
sub ecx, 4
jmp _rt0_copy_multiboot_data.copy
.done:
ret
;------------------------------------------------------------------------------
; Check that the processor supports the CPUID instruction.
;
; To check if CPUID is supported, we need to attempt to flip the ID bit (bit 21)
; in the FLAGS register. If that works, CPUID is available.
;
; Code taken from: http://wiki.osdev.org/Setting_Up_Long_Mode#x86_or_x86-64
;------------------------------------------------------------------------------
_rt0_check_cpuid_support:
; Copy FLAGS in to EAX via stack
pushfd
pop eax
; Copy to ECX as well for comparing later on
mov ecx, eax
; Flip the ID bit
xor eax, 1 << 21
; Copy EAX to FLAGS via the stack
push eax
popfd
; Copy FLAGS back to EAX (with the flipped bit if CPUID is supported)
pushfd
pop eax
; Restore FLAGS from the old version stored in ECX (i.e. flipping the
; ID bit back if it was ever flipped).
push ecx
popfd
; Compare EAX and ECX. If they are equal then that means the bit
; wasn't flipped, and CPUID isn't supported.
cmp eax, ecx
je _rt0_check_cpuid_support.no_cpuid
ret
.no_cpuid:
mov edi, err_cpuid_not_supported - PAGE_OFFSET
call write_string
jmp _rt0_32_entry.halt
;------------------------------------------------------------------------------
; Check that the processor supports long mode
; Code taken from: http://wiki.osdev.org/Setting_Up_Long_Mode#x86_or_x86-64
;------------------------------------------------------------------------------
_rt0_check_longmode_support:
; To check for longmode support we need to ensure that the CPUID instruction
; can report it. To do this we need to query it first.
mov eax, 0x80000000 ; Set the A-register to 0x80000000.
cpuid
cmp eax, 0x80000001 ; We need at least 0x80000001 to check for long mode.
jb _rt0_check_longmode_support.no_long_mode
mov eax, 0x80000001 ; Set the A-register to 0x80000001.
cpuid
test edx, 1 << 29 ; Test if the LM-bit, which is bit 29, is set in the D-register.
jz _rt0_check_longmode_support.no_long_mode
ret
.no_long_mode:
mov edi, err_longmode_not_supported - PAGE_OFFSET
call write_string
jmp _rt0_32_entry.halt
;------------------------------------------------------------------------------
; Check for and enabl SSE support. Code taken from:
; http://wiki.osdev.org/SSE#Checking_for_SSE
;------------------------------------------------------------------------------
_rt0_check_sse_support:
; check for SSE
mov eax, 0x1
cpuid
test edx, 1<<25
jz _rt0_check_sse_support.no_sse
; Enable SSE
mov eax, cr0
and ax, 0xfffb ; Clear coprocessor emulation CR0.EM
or ax, 0x2 ; Set coprocessor monitoring CR0.MP
mov cr0, eax
mov eax, cr4
or ax, 3 << 9 ; Set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
mov cr4, eax
ret
.no_sse:
mov edi, err_sse_not_supported - PAGE_OFFSET
call write_string
jmp _rt0_32_entry.halt
;------------------------------------------------------------------------------
; Setup minimal page tables to allow access to the following regions:
; - 0 to 8M
; - PAGE_OFFSET to PAGE_OFFSET + 8M
;
; The second region mapping allows us to access the kernel at its VMA when
; paging is enabled.
;------------------------------------------------------------------------------
PAGE_PRESENT equ (1 << 0)
PAGE_WRITABLE equ (1 << 1)
PAGE_2MB equ (1 << 7)
_rt0_populate_initial_page_tables:
; The CPU uses bits 39-47 of the virtual address as an index to the P4 table.
mov eax, page_table_l3 - PAGE_OFFSET
or eax, PAGE_PRESENT | PAGE_WRITABLE
mov ebx, page_table_l4 - PAGE_OFFSET
mov [ebx], eax
; Recursively map the last P4 entry to itself. This allows us to use
; specially crafted memory addresses to access the page tables themselves
mov ecx, ebx
or ecx, PAGE_PRESENT | PAGE_WRITABLE
mov [ebx + 511*8], ecx
; Also map the addresses starting at PAGE_OFFSET to the same P3 table.
; To find the P4 index for PAGE_OFFSET we need to extract bits 39-47
; of its address.
mov ecx, (PAGE_OFFSET >> 39) & 511
mov [ebx + ecx*8], eax
; The CPU uses bits 30-38 as an index to the P3 table. We just need to map
; entry 0 from the P3 table to point to the P2 table .
mov eax, page_table_l2 - PAGE_OFFSET
or eax, PAGE_PRESENT | PAGE_WRITABLE
mov ebx, page_table_l3 - PAGE_OFFSET
mov [ebx], eax
; For the L2 table we enable the huge page bit which allows us to specify
; 2M pages without needing to use the L1 table. To cover the required
; 0-8M region we need to provide 4 2M page entries at indices 0 to 4.
mov ecx, 0
mov ebx, page_table_l2 - PAGE_OFFSET
.next_page:
mov eax, 1 << 21 ; 2M
mul ecx ; eax *= ecx
or eax, PAGE_PRESENT | PAGE_WRITABLE | PAGE_2MB
mov [ebx + ecx*8], eax
inc ecx
cmp ecx, 4
jne _rt0_populate_initial_page_tables.next_page
ret
;------------------------------------------------------------------------------
; Load P4 table, enable PAE, enter long mode and finally enable paging
;------------------------------------------------------------------------------
_rt0_enter_long_mode:
; Load page table map pointer to cr3
mov eax, page_table_l4 - PAGE_OFFSET
mov cr3, eax
; Enable PAE support
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
; Now enable long mode (bit 8) and the no-execute support (bit 11) by
; modifying the EFER MSR
mov ecx, 0xc0000080
rdmsr ; read msr value to eax
or eax, (1 << 8) | (1<<11)
wrmsr
; Finally enable paging (bit 31) and user/kernel page write protection (bit 16)
mov eax, cr0
or eax, (1 << 31) | (1<<16)
mov cr0, eax
; We are in 32-bit compatibility submode. We need to load a 64bit GDT
; and perform a far jmp to switch to long mode
mov eax, gdt0_desc - PAGE_OFFSET
lgdt [eax]
; set ds and es segments
; to set the cs segment we need to perform a far jmp
mov ax, DS_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp CS_SEG:.flush_gdt - PAGE_OFFSET
.flush_gdt:
ret
;------------------------------------------------------------------------------
; Write the NULL-terminated string contained in edi 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 ebx,0xb8000
mov ah, 0x4F
.next_char:
mov al, byte[edi]
test al, al
jz write_string.done
mov word [ebx], ax
add ebx, 2
inc edi
jmp write_string.next_char
.done:
ret
;------------------------------------------------------------------------------
; Set up the stack pointer to the virtual address of the stack and jump to the
; 64-bit entrypoint.
;------------------------------------------------------------------------------
bits 64
_rt0_64_entry_trampoline:
mov rsp, stack_top ; now that paging is enabled we can load the stack
; with the virtual address of the allocated stack.
; Jump to 64-bit entry
extern _rt0_64_entry
mov rax, _rt0_64_entry
jmp rax

View File

@@ -0,0 +1,397 @@
; vim: set ft=nasm :
%include "constants.inc"
bits 64
section .bss
align 8
; Allocate space for the interrupt descriptor table (IDT).
; This arch supports up to 256 interrupt handlers
%define IDT_ENTRIES 0xff
_rt0_idt_start:
resq 2 * IDT_ENTRIES ; each 64-bit IDT entry is 16 bytes
_rt0_idt_end:
_rt0_idt_desc:
resw 1
resq 1
; Allocates space for the IRQ handlers pointers registered by the IRQ package
_rt0_irq_handlers resq IDT_ENTRIES
; 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
;------------------------------------------------------------------------------
; 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_load_idt
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, _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
; 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
; but set it as inactive. The code in irq_amd64 is responsible for enabling
; individual IDT entries when handlers are installed.
;------------------------------------------------------------------------------
_rt0_64_load_idt:
mov rax, _rt0_idt_start
%assign gate_num 0
%rep IDT_ENTRIES
mov rbx, _rt0_64_gate_entry_%+ gate_num
mov word [rax], bx ; gate entry bits 0-15
mov word [rax+2], 0x8 ; GDT descriptor
mov byte [rax+5], 0x0 ; Mark the entry as NOT present
shr rbx, 16
mov word [rax+6], bx ; gate entry bits 16-31
shr rbx, 16
mov dword [rax+8], ebx ; gate entry bits 32-63
add rax, 16 ; size of IDT entry
%assign gate_num gate_num+1
%endrep
mov rax, _rt0_idt_desc
mov word [rax], _rt0_idt_end - _rt0_idt_start - 1 ; similar to GDT this must be len(IDT) - 1
mov rbx, _rt0_idt_start
mov qword [rax+2], rbx
lidt [rax]
ret
;------------------------------------------------------------------------------
; Generate gate entries. Each gate handler pushes the address of the registered
; handler to the stack before jumping to a dispatcher function.
;
; Some exceptions push an error code to the stack after the stack frame. This
; code must be popped off the stack before calling iretq. The generated handlers
; are aware whether they need to deal with the code or not and jump to the
; appropriate get dispatcher.
;------------------------------------------------------------------------------
%assign gate_num 0
%rep IDT_ENTRIES
extern _rt0_interrupt_handlers
_rt0_64_gate_entry_%+ gate_num:
push rax
mov rax, _rt0_interrupt_handlers
add rax, 8*gate_num
mov rax, [rax]
xchg rax, [rsp] ; store handler address and restore original rax
; For a list of gate numbers that push an error code see:
; http://wiki.osdev.org/Exceptions
%if (gate_num == 8) || (gate_num >= 10 && gate_num <= 14) || (gate_num == 17) || (gate_num == 30)
jmp _rt0_64_gate_dispatcher_with_code
%else
jmp _rt0_64_gate_dispatcher_without_code
%endif
%assign gate_num gate_num+1
%endrep
%macro save_regs 0
push r15
push r14
push r13
push r12
push r11
push r10
push r9
push r8
push rbp
push rdi
push rsi
push rdx
push rcx
push rbx
push rax
%endmacro
%macro restore_regs 0
pop rax
pop rbx
pop rcx
pop rdx
pop rsi
pop rdi
pop rbp
pop r8
pop r9
pop r10
pop r11
pop r12
pop r13
pop r14
pop r15
%endmacro
;------------------------------------------------------------------------------
; This dispatcher is invoked by gate entries that expect a code to be pushed
; by the CPU to the stack. It performs the following functions:
; - save registers
; - push pointer to saved regs
; - push pointer to stack frame
; - read and push exception code
; - invoke handler(code, &frame, &regs)
; - restore registers
; - pop exception code from stack so rsp points to the stack frame
;------------------------------------------------------------------------------
_rt0_64_gate_dispatcher_with_code:
; This is how the stack looks like when entering this function:
; (each item is 8-bytes wide)
;
;------------------
; handler address | <- pushed by gate_entry_xxx (RSP points here)
;-----------------|
; Exception code | <- needs to be removed from stack before calling iretq
;-----------------|
; RIP | <- exception frame
; CS |
; RFLAGS |
; RSP |
; SS |
;-----------------
cld
; save regs and push a pointer to them
save_regs
mov rax, rsp ; rax points to saved rax
push rax ; push pointer to saved regs
; push pointer to exception stack frame (we have used 15 qwords for the
; saved registers plus one qword for the data pushed by the gate entry
; plus one extra qword to jump over the exception code)
add rax, 17*8
push rax
; push exception code (located between the stack frame and the saved regs)
sub rax, 8
push qword [rax]
call [rsp + 18*8] ; call registered irq handler
add rsp, 3 * 8 ; unshift the pushed arguments so rsp points to the saved regs
restore_regs
add rsp, 16 ; pop handler address and exception code off the stack before returning
iretq
;------------------------------------------------------------------------------
; This dispatcher is invoked by gate entries that do not use exception codes.
; It performs the following functions:
; - save registers
; - push pointer to saved regs
; - push pointer to stack frame
; - invoke handler(&frame, &regs)
; - restore registers
;------------------------------------------------------------------------------
_rt0_64_gate_dispatcher_without_code:
; This is how the stack looks like when entering this function:
; (each item is 8-bytes wide)
;
;------------------
; handler address | <- pushed by gate_entry_xxx (RSP points here)
;-----------------|
; RIP | <- exception frame
; CS |
; RFLAGS |
; RSP |
; SS |
;-----------------
cld
; save regs and push a pointer to them
save_regs
mov rax, rsp ; rax points to saved rax
push rax ; push pointer to saved regs
; push pointer to exception stack frame (we have used 15 qwords for the
; saved registers plus one qword for the data pushed by the gate entry)
add rax, 16*8
push rax
call [rsp + 17*8] ; call registered irq handler
add rsp, 2 * 8 ; unshift the pushed arguments so rsp points to the saved regs
restore_regs
add rsp, 8 ; pop handler address off the stack before returning
iretq
;------------------------------------------------------------------------------
; 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

View File

@@ -0,0 +1,7 @@
set timeout=0
set default=0
menuentry "gopheros" {
multiboot2 /boot/kernel.bin
boot
}

View File

@@ -0,0 +1,52 @@
VMA = PAGE_OFFSET + LOAD_ADDRESS;
ENTRY(_rt0_32_entry)
SECTIONS {
/* Set the kernel VMA at PAGE_OFFSET + 1M
* but load it at physical address 1M */
. = VMA;
_kernel_start = .;
.text BLOCK(4K) : AT(ADDR(.text) - PAGE_OFFSET)
{
/* The multiboot header must be present in the first 4K of the kernel
* image so that the bootloader can find it */
*(.multiboot_header)
*(.rt0)
*(.text)
}
/* Read-only data. */
.rodata ALIGN(4K) : AT(ADDR(.rodata) - PAGE_OFFSET)
{
*(.rodata)
}
/* Read-write data (initialized) */
.data ALIGN(4K) : AT(ADDR(.data) - PAGE_OFFSET)
{
*(.data)
}
/* Read-write data (zeroed) */
.bss ALIGN(4K) : AT(ADDR(.bss) - PAGE_OFFSET)
{
*(COMMON)
*(.bss)
}
/* Go function redirection table. This table is used for hooking
* Go runtime function symbols so that calls to them are redirected to
* functions provided by the kernel.
*/
.goredirectstbl ALIGN(4K): AT(ADDR(.goredirectstbl) - PAGE_OFFSET)
{
*(.goredirectstbl)
}
_kernel_end = ALIGN(4K);
}