1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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 });
}
|