Microkernel thing OS experiment (Zig ⚡)

overhaul Interrupt subsystem, demonstrate preempting user

pci.express 329ce7f4 fce9655d

verified
Changed files
+403 -238
components
root_server
src
ukernel
+16 -2
components/root_server/src/main.zig
··· 2 2 const os = @import("os.zig"); 3 3 4 4 export fn _start() callconv(.c) noreturn { 5 - _ = os.syscall1(SYS_poke, 0xB16B00B5BADBABE); 6 - _ = os.syscall1(SYS_exit, 0x69696969); 5 + // _ = os.syscall1(SYS_poke, 0xB16B00B5BADBABE); 6 + // _ = os.syscall1(SYS_exit, 0x69696969); 7 + asm volatile ( 8 + \\ mov $0x69696969, %%rdi 9 + \\ xor %%rsi, %%rsi 10 + \\ xor %%rbx, %%rbx 11 + \\ mainloop: 12 + \\ xor %%rax, %%rax 13 + \\ delayloop: 14 + \\ inc %%rax 15 + \\ cmp $0x4000000, %%rax 16 + \\ jnz delayloop 17 + \\ inc %%rbx 18 + \\ syscall 19 + \\ jmp mainloop 20 + ); 7 21 8 22 die(); 9 23 }
+31 -50
components/ukernel/arch/amd64/boot.zig
··· 5 5 const console = @import("console"); 6 6 const flanterm = @import("flanterm"); 7 7 const log = std.log.scoped(.amd64_init); 8 - const Idt = arch.structures.Idt; 9 8 const StandardGdt = arch.structures.gdt.StandardGdt; 10 9 const Tss = arch.structures.tss.Tss; 11 10 ··· 77 76 arch.per_cpu_init_data.init(limine_requests.mp.response.?.cpu_count); 78 77 79 78 // Install the IDT 80 - initIdt(); 79 + arch.interrupts.idt.init(); 81 80 82 81 // Set up our own GDT and TSS 83 82 const gdt = &arch.per_cpu_init_data.gdt_buf[0]; ··· 85 84 const tss = &arch.per_cpu_init_data.tss_buf[0]; 86 85 // TSS rsp 0x3800 87 86 tss.* = .{ 88 - .rsp0 = 0x3800, 89 - .rsp1 = 0x3800, 90 - .rsp2 = 0x3800, 87 + .rsp0 = 0x7ffe_0000_8000, 88 + .rsp1 = 0x7ffe_0000_8000, 89 + .rsp2 = 0x7ffe_0000_8000, 91 90 }; 92 91 93 92 gdt.tss_desc.set_tss_addr(tss); ··· 117 116 118 117 // Allocate a stack (0x3000 - 0x4000) 119 118 common.mm.paging.map(.{ 120 - .vaddr = 0x3000, 121 - .size = 0x1000, 119 + .vaddr = 0x7ffe_0000_0000, 120 + .size = 65536, 122 121 .memory_type = .MemoryWriteBack, 123 122 .perms = .{ 124 123 .x = false, ··· 136 135 137 136 init_syscalls(); 138 137 139 - enter_userspace(entry, 0x69, 0x4000); 138 + enter_userspace(entry, 0x69, 0x7ffe_0001_0000); 140 139 } 141 140 142 141 // Get ready for system calls (set MSRs) ··· 311 310 } 312 311 } 313 312 314 - pub fn initIdt() void { 315 - const idt_addr: usize = @intFromPtr(arch.per_cpu_init_data.idt); 316 - 317 - // Install the known exception handlers 318 - arch.per_cpu_init_data.idt.breakpoint.installHandler(breakpoint_handler); 319 - arch.per_cpu_init_data.idt.double_fault.installHandler(double_fault); 320 - arch.per_cpu_init_data.idt.general_protection_fault.installHandler(gpf); 321 - arch.per_cpu_init_data.idt.page_fault.installHandler(page_fault); 322 - arch.per_cpu_init_data.idt.interrupts[0xFF - 32].installHandler(arch.interrupts.apic.spurious_interrupt_handler); 323 - arch.per_cpu_init_data.idt.interrupts[48 - 32].installHandler(arch.interrupts.apic.periodic_handler); 324 - 325 - // Load the Idt Register 326 - const reg: Idt.Idtr = .{ .addr = idt_addr, .limit = @sizeOf(Idt) - 1 }; 327 - reg.load(); 328 - } 329 - 330 313 // TODO: update the type reflection thing to make a custom 331 314 // function type for the ISR 332 315 pub const PageFaultErrorCode = packed struct(u64) { ··· 345 328 return @bitCast(self.*); 346 329 } 347 330 }; 348 - pub fn page_fault(stack_frame: *arch.structures.Idt.InterruptStackFrame, err_code_u64: u64) callconv(.{ .x86_64_interrupt = .{} }) void { 349 - const err_code: PageFaultErrorCode = @bitCast(err_code_u64); 350 - log.err("PAGE FAULT @ 0x{x:0>16}, code 0x{x}!!!!!!!!!!!", .{ stack_frame.instruction_pointer, err_code.val() }); 351 - const cr2 = arch.registers.ControlRegisters.Cr2.read(); 352 - switch (err_code.write) { 353 - true => log.err("Tried to write to vaddr 0x{x:0>16}", .{cr2}), 354 - false => log.err("Tried to read from vaddr 0x{x:0>16}", .{cr2}), 355 - } 356 - log.err("dying...", .{}); 357 - arch.instructions.die(); 358 - } 331 + // pub fn page_fault(stack_frame: *arch.structures.Idt.InterruptStackFrame, err_code_u64: u64) callconv(.{ .x86_64_interrupt = .{} }) void { 332 + // const err_code: PageFaultErrorCode = @bitCast(err_code_u64); 333 + // log.err("PAGE FAULT @ 0x{x:0>16}, code 0x{x}!!!!!!!!!!!", .{ stack_frame.instruction_pointer, err_code.val() }); 334 + // const cr2 = arch.registers.ControlRegisters.Cr2.read(); 335 + // switch (err_code.write) { 336 + // true => log.err("Tried to write to vaddr 0x{x:0>16}", .{cr2}), 337 + // false => log.err("Tried to read from vaddr 0x{x:0>16}", .{cr2}), 338 + // } 339 + // log.err("dying...", .{}); 340 + // arch.instructions.die(); 341 + // } 359 342 360 - pub fn breakpoint_handler(stack_frame: *Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void { 361 - log.warn("Breakpoint @ 0x{x:0>16}, returning execution...", .{stack_frame.instruction_pointer}); 362 - } 343 + // pub fn breakpoint_handler(stack_frame: *Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void { 344 + // log.warn("Breakpoint @ 0x{x:0>16}, returning execution...", .{stack_frame.instruction_pointer}); 345 + // } 363 346 364 - pub fn gpf(stack_frame: *Idt.InterruptStackFrame, err_code: u64) callconv(.{ .x86_64_interrupt = .{} }) void { 365 - log.warn("gpf @ 0x{x:0>16} ERR CODE {}, returning execution...", .{ stack_frame.instruction_pointer, err_code }); 366 - arch.instructions.die(); 367 - } 347 + // pub fn gpf(stack_frame: *Idt.InterruptStackFrame, err_code: u64) callconv(.{ .x86_64_interrupt = .{} }) void { 348 + // log.warn("gpf @ 0x{x:0>16} ERR CODE {}, returning execution...", .{ stack_frame.instruction_pointer, err_code }); 349 + // arch.instructions.die(); 350 + // } 368 351 369 - pub fn double_fault(stack_frame: *Idt.InterruptStackFrame, err_code: u64) callconv(.{ .x86_64_interrupt = .{} }) noreturn { 370 - log.err("FATAL DOUBLE FAULT @ 0x{x:0>16}, code 0x{x}!!!!!!!!!!!", .{ stack_frame.instruction_pointer, err_code }); 371 - log.err("dying...", .{}); 372 - arch.instructions.die(); 373 - } 352 + // pub fn double_fault(stack_frame: *Idt.InterruptStackFrame, err_code: u64) callconv(.{ .x86_64_interrupt = .{} }) noreturn { 353 + // log.err("FATAL DOUBLE FAULT @ 0x{x:0>16}, code 0x{x}!!!!!!!!!!!", .{ stack_frame.instruction_pointer, err_code }); 354 + // log.err("dying...", .{}); 355 + // arch.instructions.die(); 356 + // } 374 357 375 358 fn bootstrapAPs() void { 376 359 log.info("Bootstrapping APs...", .{}); ··· 382 365 383 366 fn ap_init(mp_info: *limine.SmpMpFeature.MpInfo) callconv(.c) noreturn { 384 367 // Set up the IDT 385 - const idt_addr: usize = @intFromPtr(arch.per_cpu_init_data.idt); 386 - const reg: Idt.Idtr = .{ .addr = idt_addr, .limit = @sizeOf(Idt) - 1 }; 387 - reg.load(); 368 + arch.interrupts.idt.load(); 388 369 389 370 // Set up our GDT and TSS 390 371 const gdt = &arch.per_cpu_init_data.gdt_buf[mp_info.processor_id];
+6 -3
components/ukernel/arch/amd64/interrupts/apic.zig
··· 234 234 // .priority_class = 0, 235 235 // .priority_sub_class = 0, 236 236 // }); 237 + arch.interrupts.idt.add_handler(.{ .interrupt = 0xFF }, spurious_interrupt_handler, 3, 0); 238 + arch.interrupts.idt.add_handler(.{ .interrupt = 48 }, periodic_handler, 3, 0); 237 239 } 238 240 239 241 pub fn calibrateTimer() void { ··· 291 293 } 292 294 }; 293 295 294 - pub fn spurious_interrupt_handler(_: *arch.structures.Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void { 296 + pub fn spurious_interrupt_handler(_: *arch.interrupts.idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void { 295 297 log.warn("Got a spurious interrupt!", .{}); 296 298 } 297 299 298 - pub fn periodic_handler(_: *arch.structures.Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void { 299 - log.warn("Got an APIC timer interrupt!", .{}); 300 + pub fn periodic_handler(stack_trace: *arch.interrupts.idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void { 301 + log.warn("Got an APIC timer interrupt, incrementing user's %rsi...", .{}); 302 + stack_trace.regs.rsi += 1; 300 303 singleton.setRegister(.eoi, 0); 301 304 }
+310
components/ukernel/arch/amd64/interrupts/idt.zig
··· 1 + const arch = @import("../root.zig"); 2 + const std = @import("std"); 3 + const interrupts = arch.interrupts; 4 + const StandardGdt = arch.structures.gdt.StandardGdt; 5 + 6 + // The actual IDT memory 7 + const entry_count = 256; 8 + export var interrupt_descriptor_table: [entry_count]Entry = undefined; 9 + 10 + // Pointers to the actual ISRs which the global interrupt handler call 11 + // Each IDT entry pushes the interrupt number then calls the global 12 + // handler, which pushes more information then calls the user 13 + // defined handler. Use common sense and don't return from an exception 14 + // which shouldn't be returned from. 15 + const DefinedHandler = *const fn (*InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void; 16 + pub export var defined_handlers: [entry_count]DefinedHandler = undefined; 17 + 18 + // The actual handlers with addresses in the IDT. 19 + const ActualHandler = *const fn () callconv(.naked) void; 20 + const actual_handlers: [entry_count]ActualHandler = blk: { 21 + @setEvalBranchQuota(100000000); 22 + var ret: [entry_count]ActualHandler = undefined; 23 + for (0..256) |i| { 24 + ret[i] = make_actual_handler(.{ .interrupt = i }); 25 + } 26 + break :blk ret; 27 + }; 28 + 29 + fn make_actual_handler(comptime interrupt: Interrupt) ActualHandler { 30 + var asm_code: []const u8 = ""; 31 + // Make the stack consistent 32 + if (!interrupt.has_error_code()) { 33 + asm_code = asm_code ++ "pushq $0\n"; 34 + } 35 + // Push the interrupt number 36 + asm_code = asm_code ++ std.fmt.comptimePrint("pushq ${}\n", .{interrupt.interrupt}); 37 + // Jump to the common interrupt handler code 38 + asm_code = asm_code ++ "jmp _int_handler_common"; 39 + const code = asm_code; 40 + 41 + const tmp_struct = struct { 42 + fn actual_handler() callconv(.naked) void { 43 + asm volatile (code); 44 + } 45 + }; 46 + return tmp_struct.actual_handler; 47 + } 48 + 49 + // The global assembly for the common interrupt handler 50 + comptime { 51 + // Construct the push and pop instructions from SavedRegisters 52 + var push_instrs: []const u8 = "\n"; 53 + var pop_instrs: []const u8 = "\n"; 54 + 55 + const saved_regs = @typeInfo(SavedRegisters).@"struct".fields; 56 + for (saved_regs) |saved_reg| { 57 + // We must prepend to push because pushes are basically 58 + // building the struct in reverse order, so reverse the effects 59 + push_instrs = "\n pushq %" ++ saved_reg.name ++ push_instrs; 60 + // Of course, pop in the opposite order as push 61 + pop_instrs = pop_instrs ++ "popq %" ++ saved_reg.name ++ "\n"; 62 + } 63 + 64 + asm ( 65 + \\ .global _int_handler_common 66 + \\ .type _int_handler_common, @function 67 + \\ _int_handler_common: 68 + 69 + // Push the general purpose registers and then CR3 70 + ++ push_instrs ++ 71 + \\ mov %cr3, %rax 72 + \\ pushq %rax 73 + 74 + // Now, rsp points to the start of InterruptFrame. Read int_num and call into an offset of 75 + // the defined handlers list after setting the first arg to the created structure. 76 + ++ std.fmt.comptimePrint("\nmov {}(%rsp), %rcx\n", .{@offsetOf(InterruptFrame(u64), "int_num")}) ++ 77 + \\ mov %rsp, %rdi 78 + \\ callq *defined_handlers(, %rcx, 8) 79 + 80 + // Skip the stack all the way down to the general purpose 81 + // registers, then pop all of them 82 + // TODO: restore CR3?? 83 + ++ std.fmt.comptimePrint("\nadd ${}, %rsp\n", .{@offsetOf(InterruptFrame(u64), "regs")}) ++ pop_instrs ++ 84 + // Skip the interrupt number and error code and iretq 85 + \\ add $16, %rsp 86 + \\ iretq 87 + ); 88 + } 89 + 90 + /// IDT Register 91 + const Idtr = packed struct(u80) { 92 + limit: u16, 93 + addr: u64, 94 + 95 + /// Load the IDT Register 96 + pub fn load(self: *const Idtr) void { 97 + asm volatile ("lidt (%[idtr_addr])" 98 + : 99 + : [idtr_addr] "r" (self), 100 + ); 101 + } 102 + }; 103 + 104 + // A raw IDT entry 105 + const Entry = extern struct { 106 + func_low: u16, 107 + gdt_selector: u16, 108 + options: Options, 109 + func_mid: u16, 110 + func_high: u32, 111 + _reserved0: u32 = 0, 112 + 113 + pub const Options = packed struct(u16) { 114 + ist_index: u3, 115 + _reserved0: u5 = 0, 116 + disable_interrupts: bool = true, 117 + // type: Type, 118 + must_be_one: u3 = 0b111, 119 + must_be_zero: u1 = 0, 120 + dpl: u2, 121 + present: bool, 122 + }; 123 + 124 + pub const Owner = enum { 125 + kernel, 126 + user, 127 + }; 128 + 129 + const Self = @This(); 130 + pub fn init(func_ptr: usize, dpl: u2, ist: u3) Entry { 131 + // _ = typ; 132 + return .{ 133 + .func_low = @truncate(func_ptr), 134 + .gdt_selector = StandardGdt.selectors.kernel_code, 135 + .options = .{ 136 + .ist_index = ist, 137 + .dpl = dpl, 138 + .present = true, 139 + }, 140 + .func_mid = @truncate(func_ptr >> 16), 141 + .func_high = @truncate(func_ptr >> 32), 142 + }; 143 + } 144 + 145 + // Changes the address without changing anything else 146 + pub fn set_func(self: *Self, ptr: usize) void { 147 + self.func_low = @truncate(ptr); 148 + self.func_mid = @truncate(ptr >> 16); 149 + self.func_high = @truncate(ptr >> 32); 150 + } 151 + }; 152 + 153 + /// A selector error code indexing into the GDT, IDT, or LDT. 154 + /// Used in a general protection fault for example. 155 + pub const SelectorErrorCode = packed struct(u64) { 156 + external: bool, 157 + interrupt: bool, 158 + // Only valid if not interrupt 159 + type: enum(u1) { 160 + gdt = 0, 161 + ldt = 1, 162 + }, 163 + idx: u13, 164 + _reserved0: u48 = 0, 165 + 166 + pub const Target = union(enum) { 167 + interrupt: Interrupt, 168 + gdt_sel: u16, 169 + ldt_sel: u13, 170 + }; 171 + 172 + const Self = @This(); 173 + pub fn parse(self: Self) Target { 174 + return switch (self.interrupt) { 175 + true => .{ .interrupt = @enumFromInt(self.idx) }, 176 + false => switch (self.type) { 177 + .gdt => .{ .gdt_sel = self.idx }, 178 + .ldt => .{ .ldt_sel = self.idx }, 179 + }, 180 + }; 181 + } 182 + }; 183 + 184 + /// List of the general built in exceptions 185 + pub const Exception = enum(u8) { 186 + divide_error = 0x00, 187 + debug_exeption = 0x01, 188 + non_maskable_interrupt = 0x02, 189 + breakpoint = 0x03, 190 + overflow = 0x04, 191 + bound_range_exceeded = 0x05, 192 + invalid_opcode = 0x06, 193 + device_not_available = 0x07, 194 + double_fault = 0x08, 195 + // _coprocessor_segment_overrun = 0x09, 196 + invalid_tss = 0x0a, 197 + segment_not_present = 0x0b, 198 + stack_segment_fault = 0x0c, 199 + general_protection_fault = 0x0d, 200 + page_fault = 0x0e, 201 + // _reserved0 = 0x0f, 202 + x87_floating_point = 0x10, 203 + alignment_check = 0x11, 204 + machine_check = 0x12, 205 + simd_floating_point = 0x13, 206 + virtualization = 0x14, 207 + control_protection = 0x15, 208 + hypervisor = 0x1c, 209 + vmm = 0x1d, 210 + security_fault = 0x1e, 211 + _, 212 + 213 + fn has_error_code(self: Exception) bool { 214 + return switch (self) { 215 + .double_fault, .invalid_tss, .segment_not_present, .general_protection_fault, .page_fault, .security_fault => true, 216 + else => false, 217 + }; 218 + } 219 + }; 220 + 221 + pub const Interrupt = packed union { 222 + exception: Exception, 223 + interrupt: u8, 224 + 225 + pub fn has_error_code(self: Interrupt) bool { 226 + return self.exception.has_error_code(); 227 + } 228 + }; 229 + 230 + /// Basically all the general purpose registers except rsp, 231 + /// because RSP is already in the InterruptFrame. Ordered 232 + /// in the order of the X.Reg from the instruction encoding lol 233 + pub const SavedRegisters = extern struct { 234 + rax: u64, 235 + rcx: u64, 236 + rdx: u64, 237 + rbx: u64, 238 + // rsp: u64, 239 + rbp: u64, 240 + rsi: u64, 241 + rdi: u64, 242 + r8: u64, 243 + r9: u64, 244 + r10: u64, 245 + r11: u64, 246 + r12: u64, 247 + r13: u64, 248 + r14: u64, 249 + r15: u64, 250 + }; 251 + 252 + /// The Interrupt frame which we help generate 253 + pub fn InterruptFrame(comptime ErrorCode: type) type { 254 + if (@bitSizeOf(ErrorCode) != 64) { 255 + @compileError("ErrorCode for InterruptFrame must be exactly 64 bits!"); 256 + } 257 + return extern struct { 258 + // CR3 259 + cr3: u64, 260 + // All the general purpose registers 261 + regs: SavedRegisters align(8), 262 + // The interrupt number 263 + int_num: Interrupt align(8), 264 + // Pushed by the CPU (error_code may be pushed by us) 265 + error_code: ErrorCode, 266 + rip: u64, 267 + cs: u16 align(8), 268 + eflags: u64, 269 + rsp: u64, 270 + ss: u16 align(8), 271 + }; 272 + } 273 + 274 + // Initialize the IDT with the default unhandled exception 275 + pub fn init() void { 276 + // Set every IDT entry to the corresponding ActualHandler 277 + for (0..entry_count) |i| { 278 + const actual_handler = @intFromPtr(actual_handlers[i]); 279 + interrupt_descriptor_table[i] = Entry.init(actual_handler, 3, 0); 280 + } 281 + // Now, set every defined handler to the default one 282 + @memset(&defined_handlers, arch.interrupts.unhandled_interrupt); 283 + 284 + // Finally, load the idt 285 + load(); 286 + 287 + add_handler(.{ .exception = .breakpoint }, arch.interrupts.breakpoint, 3, 0); 288 + add_handler(.{ .exception = .double_fault }, arch.interrupts.double_fault, 3, 0); 289 + add_handler(.{ .exception = .general_protection_fault }, arch.interrupts.general_protection_fault, 3, 0); 290 + add_handler(.{ .exception = .page_fault }, arch.mm.paging.page_fault_handler, 3, 0); 291 + } 292 + 293 + pub fn load() void { 294 + const idtr: Idtr = .{ 295 + .addr = @intFromPtr(&interrupt_descriptor_table), 296 + .limit = 0xFFF, 297 + }; 298 + idtr.load(); 299 + } 300 + 301 + pub fn add_handler(interrupt: Interrupt, handler: anytype, dpl: u2, ist: u3) void { 302 + // Modify the type, dpl, and ist in place 303 + var tmp = interrupt_descriptor_table[interrupt.interrupt]; 304 + tmp.options.dpl = dpl; 305 + tmp.options.ist_index = ist; 306 + interrupt_descriptor_table[interrupt.interrupt] = tmp; 307 + 308 + // Add the DefinedHandler 309 + defined_handlers[interrupt.interrupt] = @ptrCast(&handler); 310 + }
+9
components/ukernel/arch/amd64/interrupts/pic.zig
··· 1 1 /// Remap the 8259 PIC to an interrupt base of 0x32 2 2 const arch = @import("../root.zig"); 3 + const std = @import("std"); 4 + const log = std.log.scoped(.pic); 3 5 const out = arch.port.out; 4 6 const in = arch.port.in; 5 7 ··· 48 50 wait(); 49 51 out(u8, PIC_TWO_DATA_PORT, 0b1111_1111); 50 52 wait(); 53 + 54 + // Set up a spurious IRQ7 handler 55 + arch.interrupts.idt.add_handler(.{ .interrupt = 32 + 7 }, spurious_handler, 3, 0); 51 56 } 52 57 53 58 inline fn wait() void { ··· 58 63 pub fn end_of_timer_interrupt() void { 59 64 out(u8, PIC_ONE_CMD_PORT, CMD_EOI); 60 65 } 66 + 67 + pub fn spurious_handler(_: *arch.interrupts.idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void { 68 + std.log.warn("Got a spurious IRQ7 (8259)", .{}); 69 + }
+22
components/ukernel/arch/amd64/interrupts/root.zig
··· 1 1 pub const apic = @import("apic.zig"); 2 2 pub const pic = @import("pic.zig"); 3 3 pub const pit = @import("pit.zig"); 4 + pub const idt = @import("idt.zig"); 5 + const std = @import("std"); 6 + const arch = @import("../root.zig"); 4 7 5 8 pub inline fn enable() void { 6 9 asm volatile ("sti"); ··· 8 11 pub inline fn disable() void { 9 12 asm volatile ("cli"); 10 13 } 14 + 15 + pub fn unhandled_interrupt(stack_frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void { 16 + std.log.err("Unhandled interrupt (0x{x})!!! rip = 0x{x}", .{ stack_frame.int_num.interrupt, stack_frame.rip }); 17 + arch.instructions.die(); 18 + } 19 + 20 + pub fn breakpoint(stack_frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void { 21 + std.log.warn("Breakpoint @ 0x{x}, returning execution...", .{stack_frame.rip}); 22 + } 23 + 24 + pub fn double_fault(stack_frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void { 25 + std.log.err("Double fault @ 0x{x}, dying!!!", .{stack_frame.rip}); 26 + arch.instructions.die(); 27 + } 28 + 29 + pub fn general_protection_fault(stack_frame: *idt.InterruptFrame(idt.SelectorErrorCode)) callconv(.{ .x86_64_sysv = .{} }) void { 30 + std.log.warn("General Protection Fault @ 0x{x} (Error Code {}), returning execution...", .{ stack_frame.rip, stack_frame.error_code }); 31 + arch.instructions.die(); 32 + }
+6
components/ukernel/arch/amd64/mm/paging.zig
··· 3 3 const std = @import("std"); 4 4 const Cr3 = arch.registers.ControlRegisters.Cr3; 5 5 const Cr4 = arch.registers.ControlRegisters.Cr4; 6 + const idt = arch.interrupts.idt; 6 7 const Perms = common.mm.paging.Perms; 7 8 8 9 pub const PageTable = extern struct { ··· 329 330 len: usize, 330 331 ptr: usize, 331 332 }; 333 + 334 + pub fn page_fault_handler(stack_frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void { 335 + std.log.err("Page Fault @ 0x{x}, dying...", .{stack_frame.rip}); 336 + arch.instructions.die(); 337 + }
+2 -11
components/ukernel/arch/amd64/root.zig
··· 26 26 const PerCpuInitData = struct { 27 27 const StandardGdt = structures.gdt.StandardGdt; 28 28 const Tss = structures.tss.Tss; 29 - const Idt = structures.Idt; 30 29 31 30 gdt_buf: []StandardGdt = undefined, 32 31 tss_buf: []Tss = undefined, 33 - idt: *Idt = undefined, 34 32 35 33 const Self = @This(); 36 34 pub fn init(self: *Self, cpu_count: u64) void { 37 - // 1. Allocate an IDT 38 - const idt_addr = common.init_data.bootmem.allocMem(@sizeOf(Idt)) catch |err| { 39 - std.log.err("init PerCpuInitData: IDT alloc failed: {}", .{err}); 40 - @panic("rip bozo"); 41 - }; 42 - self.idt = @ptrFromInt(idt_addr); 43 - 44 - // 2. Allocate space for GDT and TSS data 35 + // 1. Allocate space for GDT and TSS data 45 36 const gdt_size = @sizeOf(StandardGdt); 46 37 const tss_size = @sizeOf(Tss); 47 38 ··· 51 42 @panic("rip bozo"); 52 43 }); 53 44 54 - // 3. Transmute and fill out the structure 45 + // 2. Transmute and fill out the structure 55 46 const gdt_buf: [*]StandardGdt = @ptrCast(@alignCast(buf[0 .. gdt_size * cpu_count])); 56 47 const tss_buf: [*]Tss = @ptrCast(@alignCast(buf[gdt_size * cpu_count ..][0 .. tss_size * cpu_count])); 57 48 self.gdt_buf = gdt_buf[0..cpu_count];
-170
components/ukernel/arch/amd64/structures/Idt.zig
··· 1 - //! The entire Interrupt Descriptor Table (IDT) structure for AMD64, 2 - //! including all the necessary ISR entries. Each of the defined 3 - //! ISRs is meant for a specific type of exception, while the 4 - //! array at the end of the IDT can be used for whatever is necessary. 5 - const std = @import("std"); 6 - const arch = @import("../root.zig"); 7 - const StandardGdt = arch.structures.gdt.StandardGdt; 8 - 9 - /// Faulty division (mostly divide by zero) 10 - divide_error: Entry(.handler), 11 - /// AMD64 Debug Exception, either a fault or a trap 12 - debug_exception: Entry(.handler), 13 - /// Non Maskable Interrupt 14 - non_maskable_interrupt: Entry(.handler), 15 - /// Breakpoint (int3) trap 16 - breakpoint: Entry(.handler), 17 - /// Overflow trap (INTO instruction) 18 - overflow: Entry(.handler), 19 - /// Bound Range Exception (BOUND instruction) 20 - bound_range_exceeded: Entry(.handler), 21 - /// Invalid Opcode Exception 22 - invalid_opcode: Entry(.handler), 23 - /// Device Not Available (FPU instructions when FPU disabled) 24 - device_not_available: Entry(.handler), 25 - /// Double Fault Exception 26 - double_fault: Entry(.abort_with_err_code), 27 - _coprocessor_segment_overrun: Entry(.handler), 28 - /// Invalid TSS: bad segment selector 29 - invalid_tss: Entry(.handler_with_err_code), 30 - /// Segment Not Present 31 - segment_not_present: Entry(.handler_with_err_code), 32 - /// Stack Segment Fault 33 - stack_segment_fault: Entry(.handler_with_err_code), 34 - /// General Protection Fault 35 - general_protection_fault: Entry(.handler_with_err_code), 36 - /// Page Fault 37 - page_fault: Entry(.handler_with_err_code), 38 - 39 - _reserved1: Entry(.handler), 40 - /// x87 Floating Point Exception 41 - x87_floating_point: Entry(.handler), 42 - /// Alignment Check Exception 43 - alignment_check: Entry(.handler_with_err_code), 44 - /// Machine Check Exception (MCE) 45 - machine_check: Entry(.abort), 46 - /// SIMD Floating Point Exception 47 - simd_floating_point: Entry(.handler), 48 - /// Virtualization Exception 49 - virtualization: Entry(.handler), 50 - /// Control Protection Exception 51 - control_protection: Entry(.handler_with_err_code), 52 - _reserved2: [10]Entry(.handler), 53 - /// User Accessible Interrupts 54 - interrupts: [256 - 32]Entry(.handler), 55 - 56 - /// An ISR Entry in the IDT 57 - pub const EntryType = union(enum) { 58 - abort: void, 59 - abort_with_err_code: void, 60 - handler: void, 61 - handler_with_err_code: void, 62 - handler_with_custom_err_code: type, 63 - }; 64 - pub fn Entry(comptime entry_type: EntryType) type { 65 - const return_type = switch (entry_type) { 66 - .abort, .abort_with_err_code => noreturn, 67 - .handler, .handler_with_err_code, .handler_with_custom_err_code => void, 68 - }; 69 - const params: []const std.builtin.Type.Fn.Param = switch (entry_type) { 70 - .handler, .abort => &.{ 71 - // Interrupt stack frame 72 - .{ .is_generic = false, .is_noalias = false, .type = *InterruptStackFrame }, 73 - }, 74 - .handler_with_err_code, .abort_with_err_code => &.{ 75 - // Interrupt stack frame 76 - .{ .is_generic = false, .is_noalias = false, .type = *InterruptStackFrame }, 77 - // Error code 78 - .{ .is_generic = false, .is_noalias = false, .type = u64 }, 79 - }, 80 - .handler_with_custom_err_code => |err_code_type| &.{ 81 - // Interrupt stack frame 82 - .{ .is_generic = false, .is_noalias = false, .type = *InterruptStackFrame }, 83 - // Custom Error code 84 - .{ .is_generic = false, .is_noalias = false, .type = err_code_type }, 85 - }, 86 - }; 87 - const FunctionTypeInfo: std.builtin.Type = .{ 88 - .@"fn" = .{ 89 - .calling_convention = .{ .x86_64_interrupt = .{} }, 90 - .is_generic = false, 91 - .is_var_args = false, 92 - .return_type = return_type, 93 - .params = params, 94 - }, 95 - }; 96 - 97 - // The actual IDT entry structure 98 - return extern struct { 99 - func_low: u16, 100 - gdt_selector: u16, 101 - options: Options, 102 - func_mid: u16, 103 - func_high: u32, 104 - _reserved: u32 = 0, 105 - 106 - const FuncType = @Type(FunctionTypeInfo); 107 - 108 - pub const Options = packed struct(u16) { 109 - /// Interrupt Stack Table Index 110 - ist_index: u3, 111 - _reserved: u5 = 0, 112 - disable_interrupts: bool, 113 - must_be_one: u3 = 0b111, 114 - must_be_zero: u1 = 0, 115 - /// Descriptor Privilege Level 116 - dpl: u2, 117 - present: bool, 118 - }; 119 - 120 - const Self = @This(); 121 - 122 - pub fn installHandler(self: *Self, func: *const FuncType) void { 123 - // Fetch the Code Segment 124 - const func_ptr = @intFromPtr(func); 125 - self.* = .{ 126 - // Set the function pointer 127 - .func_low = @truncate(func_ptr & 0xFFFF), 128 - .func_mid = @truncate((func_ptr >> 16) & 0xFFFF), 129 - .func_high = @truncate((func_ptr >> 32) & 0xFFFF_FFFF), 130 - .gdt_selector = StandardGdt.selectors.kernel_code, 131 - .options = .{ 132 - // No Interrupt Stack Table yet 133 - .ist_index = 0, 134 - // Mask interrupts while running ISR handler 135 - .disable_interrupts = true, 136 - // Ring 3 Minimum privilege level 137 - .dpl = 3, 138 - // Mark as present 139 - .present = true, 140 - }, 141 - }; 142 - } 143 - }; 144 - } 145 - 146 - /// IDT Register 147 - pub const Idtr = packed struct(u80) { 148 - limit: u16, 149 - addr: u64, 150 - 151 - /// Load the IDT Register 152 - pub fn load(self: *const Idtr) void { 153 - asm volatile ("lidt (%[idtr_addr])" 154 - : 155 - : [idtr_addr] "r" (self), 156 - ); 157 - } 158 - }; 159 - 160 - /// Interrupt Stack Frame 161 - /// TODO: maybe move this somewhere else 162 - pub const InterruptStackFrame = extern struct { 163 - instruction_pointer: u64, 164 - code_segment: u16, 165 - _reserved1: [6]u8, 166 - cpu_flags: u64, 167 - stack_pointer: u64, 168 - stack_segment: u16, 169 - _reserved2: [6]u8, 170 - };
-1
components/ukernel/arch/amd64/structures/root.zig
··· 1 1 pub const gdt = @import("gdt.zig"); 2 2 pub const tss = @import("tss.zig"); 3 - pub const Idt = @import("Idt.zig");
+1 -1
components/ukernel/build.zig
··· 90 90 }); 91 91 92 92 kernel.pie = false; 93 - kernel.want_lto = true; 93 + kernel.want_lto = false; 94 94 kernel.setLinkerScript(b.path(linker_script_path)); 95 95 b.installArtifact(kernel); 96 96 }