mirror of
https://github.com/taigrr/gopher-os
synced 2026-03-23 09:22:23 -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:
26
src/arch/x86_64/asm/cgo_stubs.s
Normal file
26
src/arch/x86_64/asm/cgo_stubs.s
Normal 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
|
||||
12
src/arch/x86_64/asm/constants.inc
Normal file
12
src/arch/x86_64/asm/constants.inc
Normal 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
|
||||
|
||||
|
||||
0
src/arch/x86_64/asm/data.s
Normal file
0
src/arch/x86_64/asm/data.s
Normal file
41
src/arch/x86_64/asm/multiboot_header.s
Normal file
41
src/arch/x86_64/asm/multiboot_header.s
Normal 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:
|
||||
364
src/arch/x86_64/asm/rt0_32.s
Normal file
364
src/arch/x86_64/asm/rt0_32.s
Normal 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
|
||||
397
src/arch/x86_64/asm/rt0_64.s
Normal file
397
src/arch/x86_64/asm/rt0_64.s
Normal 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, ®s)
|
||||
; - 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, ®s)
|
||||
; - 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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user