hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/boot/gdt.zig
diff options
context:
space:
mode:
Diffstat (limited to 'boot/gdt.zig')
-rw-r--r--boot/gdt.zig43
1 files changed, 43 insertions, 0 deletions
diff --git a/boot/gdt.zig b/boot/gdt.zig
new file mode 100644
index 0000000..c428e2c
--- /dev/null
+++ b/boot/gdt.zig
@@ -0,0 +1,43 @@
+// we have to make our own gdt even though limine gives us cause the
+// spec says that the one they provide is only a temp one.
+// in long mode, segment base/limits are ignrored so all we really encode
+// is the access byte and the long mode bit.
+// see: https://wiki.osdev.org/Global_Descriptor_Table
+
+pub const KERNEL_CODE: u16 = 0x08;
+pub const KERNEL_DATA: u16 = 0x10;
+
+var gdt = [_]u64{
+ 0x0000000000000000, // null
+ 0x00209A0000000000, // 0x08 kernel code: present, ring 0, executable, L=1
+ 0x0000920000000000, // 0x10 kernel data: present, ring 0, writable
+};
+
+const Gdtr = packed struct {
+ limit: u16,
+ base: u64,
+};
+
+var gdtr: Gdtr = undefined;
+
+// load the GDT, then reload CS (via a far return) and the data segments so the
+// CPU actually uses our descriptors instead of Limine's shit one.
+pub fn load() void {
+ gdtr = .{ .limit = @sizeOf(@TypeOf(gdt)) - 1, .base = @intFromPtr(&gdt) };
+ asm volatile (
+ \\ lgdt (%[gdtr])
+ \\ pushq $0x08
+ \\ leaq 1f(%rip), %rax
+ \\ pushq %rax
+ \\ lretq
+ \\ 1:
+ \\ movw $0x10, %ax
+ \\ movw %ax, %ds
+ \\ movw %ax, %es
+ \\ movw %ax, %fs
+ \\ movw %ax, %gs
+ \\ movw %ax, %ss
+ :
+ : [gdtr] "r" (&gdtr),
+ : .{ .rax = true, .memory = true });
+}