aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTianhao Wang <shrik3@mailbox.org>2024-06-04 17:50:10 +0200
committerTianhao Wang <shrik3@mailbox.org>2024-06-11 15:17:12 +0200
commit4bce609d2a0145e70f44227636a68348e9b23cf9 (patch)
tree8c753a99644172f0fd3bb5d382ae1b7790b137e6
parentf4b50dd826b81295dc9628b655fc5f360445230b (diff)
mm: fully map the kernel to high memory
-rw-r--r--Makefile5
-rw-r--r--boot/startup-x86_64.s68
-rw-r--r--defs/x86_64-hm-linker.ld119
-rw-r--r--src/arch/x86_64/asm/vectors.s3
4 files changed, 168 insertions, 27 deletions
diff --git a/Makefile b/Makefile
index a3236c0..3fbe202 100644
--- a/Makefile
+++ b/Makefile
@@ -19,9 +19,10 @@ ARCH = x86_64
ASM = nasm
ASMOBJFORMAT = elf64
ASMFLAGS = -w-zeroing
-LINKER_SCRIPT = ./defs/$(ARCH)-linker.ld
+LINKER_SCRIPT = ./defs/$(ARCH)-hm-linker.ld
CARGO_XBUILD_TARGET = ./defs/$(ARCH)-rustubs.json
CARGO_XBUILD_FLAGS =
+RUSTC_FLAGS := -C code-model=large
# ---------- No need to edit below this line --------------
# ---------- If you have to, something is wrong -----------
LDFLAGS = -no-warn-rwx-segment -static -e startup
@@ -65,7 +66,7 @@ $(BUILD)/_%.o : %.s | $(BUILD)
# define this, the linker will have troubles, especially when we use a "no_std" build
rust_kernel: check
@echo "---BUILDING RUST KERNEL---"
- @cargo xbuild --target $(CARGO_XBUILD_TARGET) $(CARGO_XBUILD_FLAGS)
+ @RUSTFLAGS="$(RUSTC_FLAGS)" cargo xbuild --target $(CARGO_XBUILD_TARGET) $(CARGO_XBUILD_FLAGS)
# need nasm
# TODO make this arch dependent
diff --git a/boot/startup-x86_64.s b/boot/startup-x86_64.s
index f08c752..c97db5e 100644
--- a/boot/startup-x86_64.s
+++ b/boot/startup-x86_64.s
@@ -20,18 +20,29 @@ MAX_MEM: equ 512
; they would be no longer accessable after the kernel switch to a higher half
; mapping. This is especially true for the multiboot info data.
+; Also be careful with imm width in asm instructions
+; many instructions does not take 64 bit imm value. e.g. cmp. If the operand is
+; an extern symbol the linker may tell you xyz "truncate to fit". In which case
+; you should load the addresses or values into an register before using them
; exported symbols
[GLOBAL startup]
[GLOBAL mb_magic]
[GLOBAL mb_info_addr]
; functions from other parts of rustubs
-[EXTERN _entry]
[EXTERN ___BSS_PM_START__]
[EXTERN ___BSS_PM_END__]
-
-[SECTION .text]
-
+[EXTERN KERNEL_OFFSET]
+[EXTERN _entry]
+; =====================================================================
+; begin of the text secion: unlike the text* sections from the rust code
+; the text here is not supposed to be relocated to an higher memory,
+; as we can not use high memory until we completely set up longmode paging.
+; Therefore we explicitly link the startup text section to low address.
+; the same goes for the ".data32" section: they are not necessarily 32bit,
+; the point is to confine all address within 4GB (32bit) range
+; =====================================================================
+[SECTION .text32]
; symbols used in 32bit mode:
; mb_magic
; mab_info_addr
@@ -45,6 +56,7 @@ startup:
; EAX: magic value 0x2BADB002
; EBX: 32-bit physical address of the multiboot information struct
; we store them in global variables for future uses in rust code.
+ ; TODO place them on the stack and pass as parameters to _entry
mov dword [mb_magic], eax
mov dword [mb_info_addr], ebx
; setup GDT by loading GDT descriptor
@@ -59,7 +71,7 @@ startup:
; define stack
mov ss, ax
- mov esp, init_stack+STACKSIZE
+ lea esp, init_stack+STACKSIZE
init_longmode:
; activate address extension (PAE)
@@ -81,12 +93,13 @@ clear_pt:
; table levels needed. see docs/x86_paging.txt
; PML4 (Page Map Level 4 / 1st level)
+ ; PML4 entry flag: 0xf = PRESENG | R/W | USER | Write Through
mov eax, pdp0
or eax, 0xf
mov dword [pml4+0], eax
mov dword [pml4+4], 0
- ; PDPE flags
- mov eax, 0x0 | 0x87 ; start-address bytes bit [30:31] + flags
+ ; PDPE flags 0x87 = PageSize=1G | USER | R/W | PRESENT
+ mov eax, 0x0 | 0x83 ; start-address bytes bit [30:31] + flags
mov ebx, 0 ; start-address bytes bit [32:38]
mov ecx, 0
fill_tables2:
@@ -126,22 +139,20 @@ activate_long_mode:
; - symbols defined in 64 bit code below, if mapped to higher memory (VA)
; - all symbols exported from rust code or linker script
; =====================================================================
-
[BITS 64]
longmode_start:
; now we set the pagetables for higher half memory
; since we have Provisional paging now, why not using 64bit code?
- mov eax, pdp1
- or eax, 0xf
- mov dword [pml4+256], eax
- mov dword [pml4+256+4], 0
- ; PDPE flags, see above
-
+ ; the 256th entry of pml4 points to memory from 0xffff_8000_0000_0000
+ mov rax, pdp1
+ ; privileged, r/w, present
+ or rax, 0x3
+ mov qword [pml4+256*8], rax
; entry 0~63 is an identical mapping with offset 0x8000_0000_0000
- ; clear the BSS section before going to rust code
+ ; 1G Page | Privileged | R/W | PRESENT
; TODO this should not be executable
mov rax, 0x0
- or rax, 0x87
+ or rax, 0x83
mov rdi, 0
fill_kvma1:
mov qword [pdp1 + 8*rdi], rax
@@ -153,7 +164,7 @@ fill_kvma1:
; entry 64~127 is a hole (also some sort of protection)
; entry 128~191 are mapping of the kernel image itself
mov rax, 0x0
- or rax, 0x87
+ or rax, 0x83
mov rdi, 128
fill_kvma2:
mov qword [pdp1 + 8*rdi], rax
@@ -162,22 +173,31 @@ fill_kvma2:
cmp rdi, 192
jne fill_kvma2
; done :-)
-
; clear BSS section for the rust code.
mov rdi, ___BSS_PM_START__
+ mov rax, ___BSS_PM_END__
clear_bss:
+ ; clear the BSS section before going to rust code
+ ; TODO speed this up by clearing 8 bytes at once. Alignment should be taken
+ ; care of..
mov byte [rdi], 0
inc rdi
- cmp rdi, ___BSS_PM_END__
+ cmp rdi, rax
jne clear_bss
+
; enable FPU
fninit
; NOTE: must NOT use sse target features for rust compiler, if sse not
; enabled here.
+ ; shift the rsp to high memory mapping:
+ mov rax, KERNEL_OFFSET,
+ or rsp, rax
; finally go to the rust code!
- call _entry
+ mov rax, _entry
+ jmp rax
+
; should not reach below
cli
hlt
@@ -185,10 +205,11 @@ clear_bss:
; =====================================================================
; data sections they should all have VAs identical to their PAs
; so we map these symbols differently than those generated by rust code
+; the "data" itself doesn't care about 64 or 32 bit width, but we need
+; to make sure they are not relocated to an address bigger then 4G (32)
; =====================================================================
-[SECTION .data]
-
+[SECTION .data32]
gdt:
; see docs/x86_gdt.txt
@@ -232,8 +253,7 @@ mb_magic:
mb_info_addr:
dd 0x00000000
-[SECTION .bss]
-
+[SECTION .init_k_stack]
global init_stack:data (init_stack.end - init_stack)
init_stack:
resb STACKSIZE
diff --git a/defs/x86_64-hm-linker.ld b/defs/x86_64-hm-linker.ld
new file mode 100644
index 0000000..2b12539
--- /dev/null
+++ b/defs/x86_64-hm-linker.ld
@@ -0,0 +1,119 @@
+
+/* defs for Multiboot headers */
+/* https://www.gnu.org/software/grub/manual/multiboot/multiboot.txt */
+MB_MAGIC = 0x1badb002;
+/* bit 0
+ * all boot modules loaded along with the operating system must be
+ * aligned on page (4KB) bit 1 must include mem_* structures
+ */
+MB_FLAGS = 0x3;
+MB_CHKSUM = 0x100000000 - (MB_MAGIC + MB_FLAGS);
+
+PROVIDE(KERNEL_OFFSET = 0xffff800000000000);
+
+SECTIONS
+{
+ . = 0x100000;
+ PROVIDE (___KERNEL_PM_START__ = . );
+ .boot :
+ {
+ header_start = .;
+ LONG(MB_MAGIC)
+ LONG(MB_FLAGS)
+ LONG(MB_CHKSUM)
+ LONG(0)
+ LONG(0)
+ LONG(0)
+ LONG(0)
+ LONG(0)
+ LONG(0)
+ LONG(0)
+ LONG(0)
+ LONG(0)
+ header_end = .;
+ }
+
+
+ .d32 :
+ {
+ *(".data32")
+ }
+
+ .reserved :
+ {
+ *(".reserved")
+ *(".reserved.*")
+ *(".gdt")
+ }
+
+ /*
+ * basically the same as BSS, but I want some flexibility and I don't care
+ * for zeroing because it's explicitly overwritten anyways. I KNOW WHAT I'M
+ * DOING! An example is the idt.
+ */
+ .reserved_0 (NOLOAD) :
+ {
+ *(".init_k_stack")
+ *(".reserved_0")
+ *(".reserved_0.*")
+ *(".reserved_0.init_stack")
+ }
+
+
+ /* global page table for 64-bit long mode */
+ .global_pagetable ALIGN(4096) (NOLOAD) :
+ {
+ *(".global_pagetable")
+ }
+
+ . = ALIGN(4096);
+ /* reserve space for a premitive stack based physical frame allocator */
+ /* each frame is 4KiB in size and has a 64bit (physical) address. e.g. */
+ /* for every 1 GiB physical memory we need 2 MiB space reserved for the */
+ /* free stack. For a easier bootstraping we are using a fix-sized stack */
+ /* array. Currently using 4GiB, therefore reserve 8MiB. */
+ PROVIDE (___FREE_PAGE_STACK__ = .);
+ .global_free_page_stack ALIGN(4096) (NOLOAD) :
+ {
+ *("..global_free_page_stack")
+ }
+ . = ALIGN(4096);
+
+ .t32 :
+ {
+ *(".text32")
+ *(".text.interrupt_gate")
+ }
+
+ . = . + KERNEL_OFFSET;
+ .text : AT(ADDR(.text) - KERNEL_OFFSET)
+ {
+ *(".text")
+ *(".text.*")
+ *(".text$")
+ }
+
+ .data : AT(ADDR(.data) - KERNEL_OFFSET)
+ {
+ *(".data")
+ *(".data.*")
+ *(".data$")
+ }
+
+ .bss : AT(ADDR(.bss) - KERNEL_OFFSET)
+ {
+ PROVIDE (___BSS_PM_START__ = .);
+ *(".bss")
+ *(".bss.*")
+ PROVIDE (___BSS_PM_END__ = .);
+ }
+
+ .rodata : AT(ADDR(.rodata) - KERNEL_OFFSET)
+ {
+ *(".rodata")
+ *(".rodata$")
+ *(".rodata.*")
+ }
+
+ PROVIDE (___KERNEL_PM_END__ = . - KERNEL_OFFSET);
+}
diff --git a/src/arch/x86_64/asm/vectors.s b/src/arch/x86_64/asm/vectors.s
index d5a2357..56aabf4 100644
--- a/src/arch/x86_64/asm/vectors.s
+++ b/src/arch/x86_64/asm/vectors.s
@@ -1,5 +1,6 @@
+; vi: ft=nasm
; vectors.s - idt for x86_64
-
+[BITS 64]
[GLOBAL idt]
[GLOBAL idt_descr]
[GLOBAL vectors_start]