summaryrefslogtreecommitdiff
path: root/src/kernel/arch/amd64/32/boot.S
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/arch/amd64/32/boot.S')
-rw-r--r--src/kernel/arch/amd64/32/boot.S114
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 */