Microkernel thing OS experiment (Zig ⚡)

add support for tsc_deadline

pci.express bee02dce 329ce7f4

verified
Changed files
+62 -93
components
root_server
src
ukernel
arch
amd64
instructions
interrupts
+1
components/root_server/src/main.zig
··· 4 4 export fn _start() callconv(.c) noreturn { 5 5 // _ = os.syscall1(SYS_poke, 0xB16B00B5BADBABE); 6 6 // _ = os.syscall1(SYS_exit, 0x69696969); 7 + asm volatile ("int3"); 7 8 asm volatile ( 8 9 \\ mov $0x69696969, %%rdi 9 10 \\ xor %%rsi, %%rsi
+5 -86
components/ukernel/arch/amd64/boot.zig
··· 51 51 52 52 // Add in a framebuffer if found 53 53 initConsole(); 54 + // Get basic information through CPUID 55 + arch.instructions.cpuid.init(); 54 56 55 57 // Add in ACPI/dtb if found, prefer ACPI 56 58 initHwDesc(); ··· 135 137 136 138 init_syscalls(); 137 139 140 + arch.interrupts.apic.armTimer(1000); 138 141 enter_userspace(entry, 0x69, 0x7ffe_0001_0000); 139 142 } 140 143 ··· 236 239 // Calibrate the APIC timer 237 240 arch.interrupts.apic.init.calibrateTimer(); 238 241 239 - // Enable periodic interrupts 240 - arch.interrupts.apic.init.enablePeriodicInterrupt(1000); 242 + // Enable one-shot interrupts 243 + arch.interrupts.apic.init.enableOneshotInterrupt(); 241 244 } 242 245 243 246 fn initConsole() void { ··· 258 261 .blue_mask_size = fb.blue_mask_size, 259 262 .blue_mask_shift = fb.blue_mask_shift, 260 263 }); 261 - // common.init_data.console = flanterm.init( 262 - // //malloc and free 263 - // null, 264 - // null, 265 - // // fb info 266 - // fb.address, 267 - // fb.width, 268 - // fb.height, 269 - // fb.pitch, 270 - // fb.red_mask_size, 271 - // fb.red_mask_shift, 272 - // fb.green_mask_size, 273 - // fb.green_mask_shift, 274 - // fb.blue_mask_size, 275 - // fb.blue_mask_shift, 276 - // // canvas 277 - // null, 278 - // // colors 279 - // flanterm.ansi_colors, 280 - // flanterm.bold_ansi_colors, 281 - // // default bg and fg 282 - // null, 283 - // null, 284 - // // default bright bg and fg 285 - // null, 286 - // null, 287 - // // font 288 - // null, 289 - // // font width and height 290 - // 0, 291 - // 0, 292 - // // font spacing 293 - // 1, 294 - // // font scale x and y 295 - // font_scale_x, 296 - // font_scale_y, 297 - // // margin 298 - // 0, 299 - // ); 300 264 } 301 265 } 302 266 } ··· 309 273 common.init_data.hardware_description = .{ .acpi_rsdp = rsdp_response.address }; 310 274 } 311 275 } 312 - 313 - // TODO: update the type reflection thing to make a custom 314 - // function type for the ISR 315 - pub const PageFaultErrorCode = packed struct(u64) { 316 - present: bool, 317 - write: bool, 318 - user: bool, 319 - reserved_write: bool, 320 - instruction_fetch: bool, 321 - protection_key: bool, 322 - shadow_stack: bool, 323 - _reserved: u8, 324 - sgx: bool, 325 - _reserved2: u48, 326 - 327 - pub fn val(self: *const PageFaultErrorCode) u64 { 328 - return @bitCast(self.*); 329 - } 330 - }; 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 - // } 342 - 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 - // } 346 - 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 - // } 351 - 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 - // } 357 276 358 277 fn bootstrapAPs() void { 359 278 log.info("Bootstrapping APs...", .{});
+31
components/ukernel/arch/amd64/instructions/cpuid.zig
··· 1 + // Do all the needed CPUID calls here, and store the info for later use 2 + const std = @import("std"); 3 + const arch = @import("../root.zig"); 4 + 5 + pub const captured = struct { 6 + pub var vendor_str: [12]u8 = undefined; 7 + }; 8 + pub fn init() void { 9 + capture_vendor_str(); 10 + capture_cpu_features(); 11 + } 12 + 13 + fn capture_vendor_str() void { 14 + const res = cpuid(0, 0); 15 + @memcpy(captured.vendor_str[0..4], std.mem.asBytes(&res.ebx)); 16 + @memcpy(captured.vendor_str[4..8], std.mem.asBytes(&res.edx)); 17 + @memcpy(captured.vendor_str[8..12], std.mem.asBytes(&res.ecx)); 18 + } 19 + 20 + fn capture_cpu_features() void { 21 + const res = cpuid(1, 0); 22 + const feat_ecx: FeaturesEcx = @bitCast(res.ecx); 23 + arch.interrupts.apic.tsc_deadline_available = feat_ecx.tsc_deadline; 24 + } 25 + 26 + const FeaturesEcx = packed struct(u32) { 27 + _reserved0: u23, 28 + tsc_deadline: bool, 29 + _reserved1: u8, 30 + }; 31 + 1 32 pub inline fn cpuid(leaf: u32, sub: u32) DefaultResults { 2 33 var eax: u32 = undefined; 3 34 var ebx: u32 = undefined;
+25 -7
components/ukernel/arch/amd64/interrupts/apic.zig
··· 3 3 const log = std.log.scoped(.apic); 4 4 5 5 pub var lapic_timer_khz: usize = 0; 6 + pub var tsc_deadline_available = false; 6 7 7 8 // tbh every cpu will be either x2apic or not, and if xapic it will 8 9 // have the exact same base address anyways so this is fine ··· 277 278 log.debug("APIC timer: {} kHz", .{lapic_timer_khz}); 278 279 } 279 280 280 - pub fn enablePeriodicInterrupt(ms: usize) void { 281 - singleton.setInitialCountRegister(0); 282 - singleton.setDivideConfigurationRegister(.div2); 281 + pub fn enableOneshotInterrupt() void { 282 + const mode: LAPIC.LVTTimerRegister.Mode = switch (tsc_deadline_available) { 283 + true => .tsc_deadline, 284 + false => blk: { 285 + singleton.setInitialCountRegister(0); 286 + singleton.setDivideConfigurationRegister(.div2); 287 + break :blk .oneshot; 288 + }, 289 + }; 283 290 291 + // TODO: detect and support tsc_deadline, ditto @ armTimer 284 292 singleton.setLVTTimerRegister(.{ 285 293 .idt_entry = 48, 286 - .mode = .periodic, 294 + .mode = mode, 287 295 .masked = false, 288 296 }); 297 + } 298 + }; 289 299 300 + pub fn armTimer(ms: usize) void { 301 + if (tsc_deadline_available) { 302 + const IA32_TSC_DEADLINE = arch.registers.MSR(u64, 0x6E0); 303 + const delta = arch.tsc.tsc_khz * ms; 304 + const target = arch.tsc.rdtsc() + delta; 305 + 306 + IA32_TSC_DEADLINE.write(target); 307 + } else { 290 308 const ticks: u32 = @truncate(lapic_timer_khz * ms); 291 - 292 309 singleton.setInitialCountRegister(ticks); 293 310 } 294 - }; 311 + } 295 312 296 313 pub fn spurious_interrupt_handler(_: *arch.interrupts.idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void { 297 314 log.warn("Got a spurious interrupt!", .{}); 298 315 } 299 316 300 317 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...", .{}); 318 + log.warn("Got an APIC timer interrupt, incrementing user's rsi...", .{}); 302 319 stack_trace.regs.rsi += 1; 303 320 singleton.setRegister(.eoi, 0); 321 + armTimer(1000); 304 322 }