Microkernel thing OS experiment (Zig ⚡)
1pub const apic = @import("apic.zig");
2pub const pic = @import("pic.zig");
3pub const pit = @import("pit.zig");
4pub const idt = @import("idt.zig");
5const std = @import("std");
6const log = std.log.scoped(.interrupts);
7const arch = @import("../root.zig");
8const common = @import("common");
9
10pub inline fn enable() void {
11 asm volatile ("sti");
12}
13pub inline fn disable() void {
14 asm volatile ("cli");
15}
16
17const syscall_entry = @extern(*anyopaque, .{
18 .name = "syscall_entry",
19});
20
21export fn syscall_handler(rdi: usize, rsi: usize) callconv(.c) void {
22 std.log.info("Got a syscall! rdi=0x{x}, rsi=0x{x}", .{ rdi, rsi });
23}
24
25pub fn init_syscalls() void {
26 // Set up the STAR MSR with the segment descriptors
27 const IA32_STAR = arch.registers.MSR(u64, 0xC0000081);
28 const star_value: u64 = 0 | @as(u64, arch.structures.gdt.StandardGdt.selectors.kernel_code) << 32 | (@as(u64, arch.structures.gdt.StandardGdt.selectors.tss_desc + 8) | 3) << 48;
29 IA32_STAR.write(star_value);
30
31 // Set up the EFER MSR with SCE (System Call Enable)
32 const IA32_EFER = arch.registers.MSR(u64, 0xC0000080);
33 const efer_val = IA32_EFER.read() | 0b1;
34 IA32_EFER.write(efer_val);
35
36 // Set up LSTAR with the syscall handler and FMASK to clear interrupts
37 const IA32_LSTAR = arch.registers.MSR(u64, 0xC0000082);
38 IA32_LSTAR.write(@intFromPtr(syscall_entry));
39
40 const IA32_FMASK = arch.registers.MSR(u64, 0xC0000084);
41 IA32_FMASK.write(1 << 9);
42}
43
44pub fn print_regs(frame: *idt.InterruptFrame(u64)) void {
45 std.log.err("CR3: 0x{x:0>16}", .{frame.cr3});
46 std.log.err("RAX: 0x{x:0>16}, RBX: 0x{x:0>16}, RCX: 0x{x:0>16}, RDX: 0x{x:0>16}", .{ frame.regs.rax, frame.regs.rbx, frame.regs.rcx, frame.regs.rdx });
47 std.log.err("RSI: 0x{x:0>16}, RDI: 0x{x:0>16}, RBP: 0x{x:0>16}, RSP: 0x{x:0>16}", .{ frame.regs.rsi, frame.regs.rdi, frame.regs.rbp, frame.rsp });
48 std.log.err("R8: 0x{x:0>16}, R9: 0x{x:0>16}, R10: 0x{x:0>16}, R11: 0x{x:0>16}", .{ frame.regs.r8, frame.regs.r9, frame.regs.r10, frame.regs.r11 });
49 std.log.err("R12: 0x{x:0>16}, R13: 0x{x:0>16}, R14: 0x{x:0>16}, R15: 0x{x:0>16}", .{ frame.regs.r12, frame.regs.r13, frame.regs.r14, frame.regs.r15 });
50 std.log.err("RFL: 0x{x:0>16}, RIP: 0x{x:0>16}, CS: 0x{x:0>16}, SS: 0x{x:0>16}", .{ frame.eflags, frame.rip, frame.cs, frame.ss });
51}
52
53pub fn unhandled_interrupt(frame: *idt.InterruptFrame(u64)) callconv(idt.CallConv) void {
54 if (std.enums.tagName(idt.Exception, frame.int_num.exception)) |exception_name| {
55 std.log.err("Unhandled interrupt (0x{x} : {s})!!!", .{ frame.int_num.interrupt, exception_name });
56 } else {
57 std.log.err("Unhandled interrupt (0x{x})!!!", .{frame.int_num.interrupt});
58 }
59
60 print_regs(frame);
61
62 arch.interrupts.disable();
63 arch.instructions.die();
64}
65
66pub fn breakpoint(stack_frame: *idt.InterruptFrame(u64)) callconv(idt.CallConv) void {
67 std.log.warn("Breakpoint @ 0x{x}, returning execution...", .{stack_frame.rip});
68}
69
70pub fn double_fault(stack_frame: *idt.InterruptFrame(u64)) callconv(idt.CallConv) void {
71 std.log.err("Double fault @ 0x{x}, dying!!!", .{stack_frame.rip});
72 print_regs(stack_frame);
73 arch.interrupts.disable();
74 arch.instructions.die();
75}
76
77pub fn general_protection_fault(stack_frame: *idt.InterruptFrame(idt.SelectorErrorCode)) callconv(idt.CallConv) void {
78 arch.interrupts.disable();
79 std.log.warn("General Protection Fault @ 0x{x}", .{stack_frame.rip});
80
81 const target = stack_frame.error_code.parse();
82 switch (target) {
83 .interrupt => |int| {
84 if (std.enums.tagName(idt.Exception, int.exception)) |exc_name| {
85 std.log.warn("Caused by interrupt 0x{x} ({s})", .{ int.interrupt, exc_name });
86 } else {
87 std.log.warn("Caused by interrupt 0x{x}", .{int.interrupt});
88 }
89 },
90 .gdt_sel => |gdt_sel| {
91 std.log.warn("GDT selector: 0x{x}", .{gdt_sel});
92 },
93 .ldt_sel => |ldt_sel| {
94 std.log.warn("LDT selector: 0x{x}", .{ldt_sel});
95 },
96 }
97 print_regs(stack_frame.normalize());
98 arch.instructions.die();
99}
100
101// Start scheduling
102pub fn startScheduling() noreturn {
103 // 1. Pop off the task to run
104 const task = common.scheduler.getNextTask() orelse {
105 std.log.scoped(.startScheduling).err("No root task!", .{});
106 @panic("startScheduling");
107 };
108 // 2. Apply the paging context
109 task.getPagingContext().apply();
110 // 3. Give a slice of 1000ms and fire away
111 apic.armTimer(20);
112 enter_userspace(task.rip, 0x69, task.rsp);
113}
114
115// Set up the IDT, PIC, TSC, and APIC
116pub fn init() void {
117 // Set up the IDT and associated vectors
118 idt.init();
119 idt.add_handler(.{ .exception = .breakpoint }, u64, arch.interrupts.breakpoint, 3, 0);
120 idt.add_handler(.{ .exception = .double_fault }, u64, arch.interrupts.double_fault, 0, 0);
121 idt.add_handler(.{ .exception = .general_protection_fault }, idt.SelectorErrorCode, arch.interrupts.general_protection_fault, 0, 0);
122 idt.add_handler(.{ .exception = .page_fault }, u64, arch.mm.paging.page_fault_handler, 0, 0);
123 // Set up the 8254's (we need 8259 timer to calibrate tsc)
124 pic.init();
125 // Calibrate the TSC against the 8259
126 arch.tsc.calibrate_pit() catch |err| {
127 log.err("Failed to calibrate TSC: {}", .{err});
128 };
129 // Set up everything needed to arm the timer
130 apic.init.initialSetup();
131}
132
133// TODO: make this slightly less shit
134pub fn enter_userspace(entry: u64, arg: u64, stack: u64) noreturn {
135 log.info("usercode64 GDT 0x{x}, userdata64 GDT 0x{x}", .{ arch.structures.gdt.StandardGdt.selectors.user_code, arch.structures.gdt.StandardGdt.selectors.user_data });
136 const cr3 = arch.registers.ControlRegisters.Cr3.read();
137 arch.registers.ControlRegisters.Cr3.write(cr3);
138 asm volatile (
139 \\ push %[userdata64]
140 \\ push %[stack]
141 \\ push $0x202
142 \\ push %[usercode64]
143 \\ push %[entry]
144 \\
145 \\ mov %[userdata64], %%rax
146 \\ mov %%rax, %%es
147 \\ mov %%rax, %%ds
148 \\
149 \\ xor %%rsi, %%rsi
150 \\ xor %%rax, %%rax
151 \\ xor %%rdx, %%rdx
152 \\ xor %%rcx, %%rcx
153 \\ xor %%rbp, %%rbp
154 \\ xor %%rbx, %%rbx
155 \\
156 \\ xor %%r8, %%r8
157 \\ xor %%r9, %%r9
158 \\ xor %%r10, %%r10
159 \\ xor %%r11, %%r11
160 \\ xor %%r12, %%r12
161 \\ xor %%r13, %%r13
162 \\ xor %%r14, %%r14
163 \\ xor %%r15, %%r15
164 \\
165 \\ iretq
166 \\
167 :
168 : [arg] "{rdi}" (arg),
169 [stack] "r" (stack),
170 [entry] "r" (entry),
171 [userdata64] "i" (arch.structures.gdt.StandardGdt.selectors.user_data),
172 [usercode64] "i" (arch.structures.gdt.StandardGdt.selectors.user_code),
173 );
174 unreachable;
175}