diff options
author | dzwdz | 2024-07-25 20:15:40 +0200 |
---|---|---|
committer | dzwdz | 2024-07-25 20:15:40 +0200 |
commit | 24934406d5d39e013e22a9e6f4138c4169460d71 (patch) | |
tree | 358f6c1dc386c50b85900a557321a256048737cd /src/kernel/arch/amd64/32/boot.S | |
parent | a6fabfb78e70b8096a8bf336aa64a3358a2f5eca (diff) |
kernel: set up the GDT in assembly
This is just for simplicity's sake.
I think I could even omit the `movw $TSS, (GdtTss + 2)` and have the linker
fill that out as a relocation, but that would probably be more complex overall.
Diffstat (limited to 'src/kernel/arch/amd64/32/boot.S')
-rw-r--r-- | src/kernel/arch/amd64/32/boot.S | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/src/kernel/arch/amd64/32/boot.S b/src/kernel/arch/amd64/32/boot.S new file mode 100644 index 0000000..3f5798f --- /dev/null +++ b/src/kernel/arch/amd64/32/boot.S @@ -0,0 +1,114 @@ +.section .text +.global _start +.type _start, @function +_start: + cli + mov $_stack_top, %esp + push %ebx // save the address of the multiboot struct + + mov $0x80000000, %eax // check CPUID extended functions + cpuid + cmp $0x80000001, %eax + jb panic_early + + mov $0x80000001, %eax + cpuid + test $(1<<29), %edx // check long mode support + jz panic_early + + mov %cr4, %eax + or $(1<<5 | 1<<9 | 1<<10), %eax // PAE | SSE | SSE + mov %eax, %cr4 + + call pml4_identity_init + mov $pml4_identity, %eax + mov %eax, %cr3 + + mov $0xC0000080, %ecx // EFER MSR + rdmsr + or $(1 | 1<<8 | 1<<11), %eax // syscall/ret | long mode | NX + wrmsr + + mov %cr0, %eax + or $0x80000002, %eax // enable paging, coprocessor monitoring + and $(~4), %eax // disable coprocessor emulation + mov %eax, %cr0 + + /* fill out the TSS's address */ + movw $TSS, (GdtTss + 2) + lgdt (GdtPointer) + + pop %edi + + mov $(2 << 3), %eax // SEG_r0data + mov %eax, %ds + mov %eax, %ss + mov %eax, %es + mov %eax, %fs + mov %eax, %gs + + ljmp $(1 << 3), $boot64 // SEG_r0code + +panic_early: + // output a vga Fuck + movl $0x4F754F46, 0xB872A + movl $0x4F6B4F63, 0xB872E + jmp cpu_halt + +.global cpu_shutdown +.type cpu_shutdown, @function +cpu_shutdown: +/* This quits QEMU. While I couldn't find this officially documented anywhere, + * it is used by QEMU in tests/tcg/i386/system/boot.S (as of commit 40d6ee), so + * I assume that this is safe-ish to use */ + mov $0x604, %edx + mov $0x2000, %eax + outw %ax, %dx + +.global cpu_halt +.type cpu_halt, @function +cpu_halt: + cli +1: hlt + jmp 1b + + +.global cpu_pause +.type cpu_pause, @function +cpu_pause: + sti + hlt + cli + ret + +.section .shared +.global GDT +.align 8 +GDT: +#define GdtLimit(p) (p & 0xFFFF) | ((p >> 16) << 48) /* doesn't check if p fits in 20 bits */ +#define GdtAccessed (1<<40) +#define GdtReadWrite (1<<41) +#define GdtCode (1<<43) +#define GdtCodeOrData (1<<44) +#define GdtRing(r) (r<<45) +#define GdtPresent (1<<47) +#define GdtAvailable (1<<52) +#define GdtLongMode (1<<53) +#define GdtPageGran (1<<55) /* limit is in pages */ +#define GdtCommon (GdtLimit(0xFFFFF) | GdtPageGran | GdtAccessed | \ + GdtReadWrite | GdtCodeOrData | GdtPresent | GdtAvailable) +.8byte 0 /* [0] = SEG_null */ +.8byte GdtCommon | GdtRing(0) | GdtLongMode | GdtCode /* [1] = SEG_r0code */ +.8byte GdtCommon | GdtRing(0) | GdtLongMode /* [2] = SEG_r0data */ +.8byte GdtCommon | GdtRing(3) | GdtCode /* [3] = SEG_r3code32 */ +.8byte GdtCommon | GdtRing(3) | GdtLongMode /* [4] = SEG_r3data */ +.8byte GdtCommon | GdtRing(3) | GdtLongMode | GdtCode /* [5] = SEG_r3code */ +GdtTss: +.8byte GdtLimit(104) | (9<<40) | GdtPresent | GdtAvailable /* [6] = SEG_TSS */ +.8byte 0 /* [7] = SEG_TSS2 */ + +.section .data +.global GdtPointer +GdtPointer: +.2byte 63 /* size of the GDT - 1 */ +.4byte GDT /* address of the GDT */ |