Microkernel thing OS experiment (Zig ⚡)
1//! The Global Descriptor Table (GDT) structure for AMD64
2const std = @import("std");
3const arch = @import("../root.zig");
4
5pub const Descriptor = packed struct(u64) {
6 limit_low: u16 = 0,
7 base_low: u16 = 0,
8 base_mid: u8 = 0,
9 access: Access,
10 limit_high: u4 = 0,
11 flags: Flags = .{},
12 base_high: u8 = 0,
13
14 const Self = @This();
15
16 pub const Access = packed struct(u8) {
17 // Accessed
18 accessed: bool = true,
19 // Readable/Writable
20 rw: bool = false,
21 // Direction bit or Conforming bit
22 dc: bool = false,
23 // Executable
24 executable: bool,
25 // Descriptor Type bit
26 descriptor_type: DescriptorType = .code_or_data,
27 // Descriptor Privilege Level
28 dpl: u2,
29 // Present bit
30 p: bool = true,
31
32 pub const DescriptorType = enum(u1) {
33 tss = 0,
34 code_or_data = 1,
35 };
36 };
37
38 pub const Flags = packed struct(u4) {
39 // Reserved
40 _reserved: u1 = 0,
41 // Long Mode code flag
42 long_mode: bool = true,
43 // Size flag (16 vs 32)
44 db: DB = .protected_16,
45 // Granularity flag
46 granularity: Granularity = .byte,
47
48 pub const Granularity = enum(u1) {
49 byte = 0,
50 page = 1,
51 };
52
53 pub const DB = enum(u1) {
54 protected_16 = 0,
55 protected_32 = 1,
56 };
57 };
58
59 pub const null_desc = std.mem.zeroes(Descriptor);
60 pub const kernel_code: Self = .{ .access = .{ .dpl = 0, .executable = true } };
61 pub const kernel_data: Self = .{ .access = .{ .dpl = 0, .executable = false, .rw = true } };
62 pub const user_code: Self = .{ .access = .{ .dpl = 3, .executable = true } };
63 pub const user_data: Self = .{ .access = .{ .dpl = 3, .executable = false, .rw = true } };
64};
65
66pub const StandardGdt = extern struct {
67 null_desc: Descriptor = .null_desc,
68 kernel_code: Descriptor = .kernel_code,
69 kernel_data: Descriptor = .kernel_data,
70 tss_desc: TssDescriptor align(@alignOf(Descriptor)) = .{},
71 user_data: Descriptor = .user_data,
72 user_code: Descriptor = .user_code,
73
74 pub const selectors = struct {
75 pub const null_desc = @offsetOf(StandardGdt, "null_desc");
76 pub const kernel_code = @offsetOf(StandardGdt, "kernel_code");
77 pub const kernel_data = @offsetOf(StandardGdt, "kernel_data");
78 pub const tss_desc = @offsetOf(StandardGdt, "tss_desc");
79 pub const user_data = @offsetOf(StandardGdt, "user_data") | 0b11;
80 pub const user_code = @offsetOf(StandardGdt, "user_code") | 0b11;
81 };
82
83 const Self = @This();
84
85 pub fn load(self: *Self) void {
86 // 1. Load the GDTR
87 const gdtr: Gdtr = .{
88 .limit = @truncate(@sizeOf(StandardGdt) - 1),
89 .base = @intFromPtr(self),
90 };
91 gdtr.load();
92
93 // 2. Set the kernel data segments
94 asm volatile (
95 \\ mov %[sel], %%ds
96 \\ mov %[sel], %%es
97 \\ mov %[sel], %%fs
98 \\ mov %[sel], %%gs
99 \\ mov %[sel], %%ss
100 :
101 : [sel] "rm" (@as(u16, selectors.kernel_data)),
102 );
103
104 // 3. Reload kernel code segments (far return)
105 asm volatile (
106 \\ push %[sel]
107 \\ lea 1f(%%rip), %%rax
108 \\ push %%rax
109 \\ .byte 0x48, 0xcb // retfq
110 \\ 1:
111 :
112 : [sel] "i" (@as(u16, selectors.kernel_code)),
113 : .{ .rax = true });
114
115 // 4. Set the TSS descriptor
116 asm volatile (
117 \\ ltr %[sel]
118 :
119 : [sel] "r" (@as(u16, selectors.tss_desc)),
120 );
121 }
122};
123
124pub const Gdtr = packed struct(u80) {
125 limit: u16,
126 base: u64,
127
128 pub fn load(self: *const Gdtr) void {
129 asm volatile ("lgdt %[gdtr_ptr]"
130 :
131 : [gdtr_ptr] "*p" (self),
132 );
133 }
134};
135
136const TssDescriptor = extern struct {
137 const Low = packed struct(u64) {
138 limit_low: u16 = 0,
139 base_low: u16 = 0,
140 base_mid: u8 = 0,
141 seg_type: u4 = 0b1001,
142 _reserved0: u1 = 0b0,
143 dpl: u2 = 0,
144 p: bool = true,
145 limit_high: u4 = 0,
146 unused: u1 = 0,
147 _reserved1: u2 = 0b00,
148 granularity: u1 = 0,
149 base_high: u8 = 0,
150 };
151 descriptor: Low = .{},
152 base_top: u32 = 0,
153 _reserved: u32 = 0,
154
155 const Self = @This();
156
157 pub fn set_tss_addr(self: *Self, tss: *const arch.structures.tss.Tss) void {
158 const tss_ptr: usize = @intFromPtr(tss);
159 // Set the base
160 self.descriptor.base_low = @truncate(tss_ptr);
161 self.descriptor.base_mid = @truncate(tss_ptr >> 16);
162 self.descriptor.base_high = @truncate(tss_ptr >> 24);
163 self.base_top = @truncate(tss_ptr >> 32);
164 // Set the limit
165 self.descriptor.limit_low = @sizeOf(arch.structures.tss.Tss);
166 }
167};