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
···
const os = @import("os.zig");
export fn _start() callconv(.c) noreturn {
-
_ = os.syscall1(SYS_poke, 0xB16B00B5BADBABE);
-
_ = os.syscall1(SYS_exit, 0x69696969);
die();
}
···
const os = @import("os.zig");
export fn _start() callconv(.c) noreturn {
+
// _ = os.syscall1(SYS_poke, 0xB16B00B5BADBABE);
+
// _ = os.syscall1(SYS_exit, 0x69696969);
+
asm volatile (
+
\\ mov $0x69696969, %%rdi
+
\\ xor %%rsi, %%rsi
+
\\ xor %%rbx, %%rbx
+
\\ mainloop:
+
\\ xor %%rax, %%rax
+
\\ delayloop:
+
\\ inc %%rax
+
\\ cmp $0x4000000, %%rax
+
\\ jnz delayloop
+
\\ inc %%rbx
+
\\ syscall
+
\\ jmp mainloop
+
);
die();
}
+31 -50
components/ukernel/arch/amd64/boot.zig
···
const console = @import("console");
const flanterm = @import("flanterm");
const log = std.log.scoped(.amd64_init);
-
const Idt = arch.structures.Idt;
const StandardGdt = arch.structures.gdt.StandardGdt;
const Tss = arch.structures.tss.Tss;
···
arch.per_cpu_init_data.init(limine_requests.mp.response.?.cpu_count);
// Install the IDT
-
initIdt();
// Set up our own GDT and TSS
const gdt = &arch.per_cpu_init_data.gdt_buf[0];
···
const tss = &arch.per_cpu_init_data.tss_buf[0];
// TSS rsp 0x3800
tss.* = .{
-
.rsp0 = 0x3800,
-
.rsp1 = 0x3800,
-
.rsp2 = 0x3800,
};
gdt.tss_desc.set_tss_addr(tss);
···
// Allocate a stack (0x3000 - 0x4000)
common.mm.paging.map(.{
-
.vaddr = 0x3000,
-
.size = 0x1000,
.memory_type = .MemoryWriteBack,
.perms = .{
.x = false,
···
init_syscalls();
-
enter_userspace(entry, 0x69, 0x4000);
}
// Get ready for system calls (set MSRs)
···
}
}
-
pub fn initIdt() void {
-
const idt_addr: usize = @intFromPtr(arch.per_cpu_init_data.idt);
-
-
// Install the known exception handlers
-
arch.per_cpu_init_data.idt.breakpoint.installHandler(breakpoint_handler);
-
arch.per_cpu_init_data.idt.double_fault.installHandler(double_fault);
-
arch.per_cpu_init_data.idt.general_protection_fault.installHandler(gpf);
-
arch.per_cpu_init_data.idt.page_fault.installHandler(page_fault);
-
arch.per_cpu_init_data.idt.interrupts[0xFF - 32].installHandler(arch.interrupts.apic.spurious_interrupt_handler);
-
arch.per_cpu_init_data.idt.interrupts[48 - 32].installHandler(arch.interrupts.apic.periodic_handler);
-
-
// Load the Idt Register
-
const reg: Idt.Idtr = .{ .addr = idt_addr, .limit = @sizeOf(Idt) - 1 };
-
reg.load();
-
}
-
// TODO: update the type reflection thing to make a custom
// function type for the ISR
pub const PageFaultErrorCode = packed struct(u64) {
···
return @bitCast(self.*);
}
};
-
pub fn page_fault(stack_frame: *arch.structures.Idt.InterruptStackFrame, err_code_u64: u64) callconv(.{ .x86_64_interrupt = .{} }) void {
-
const err_code: PageFaultErrorCode = @bitCast(err_code_u64);
-
log.err("PAGE FAULT @ 0x{x:0>16}, code 0x{x}!!!!!!!!!!!", .{ stack_frame.instruction_pointer, err_code.val() });
-
const cr2 = arch.registers.ControlRegisters.Cr2.read();
-
switch (err_code.write) {
-
true => log.err("Tried to write to vaddr 0x{x:0>16}", .{cr2}),
-
false => log.err("Tried to read from vaddr 0x{x:0>16}", .{cr2}),
-
}
-
log.err("dying...", .{});
-
arch.instructions.die();
-
}
-
pub fn breakpoint_handler(stack_frame: *Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void {
-
log.warn("Breakpoint @ 0x{x:0>16}, returning execution...", .{stack_frame.instruction_pointer});
-
}
-
pub fn gpf(stack_frame: *Idt.InterruptStackFrame, err_code: u64) callconv(.{ .x86_64_interrupt = .{} }) void {
-
log.warn("gpf @ 0x{x:0>16} ERR CODE {}, returning execution...", .{ stack_frame.instruction_pointer, err_code });
-
arch.instructions.die();
-
}
-
pub fn double_fault(stack_frame: *Idt.InterruptStackFrame, err_code: u64) callconv(.{ .x86_64_interrupt = .{} }) noreturn {
-
log.err("FATAL DOUBLE FAULT @ 0x{x:0>16}, code 0x{x}!!!!!!!!!!!", .{ stack_frame.instruction_pointer, err_code });
-
log.err("dying...", .{});
-
arch.instructions.die();
-
}
fn bootstrapAPs() void {
log.info("Bootstrapping APs...", .{});
···
fn ap_init(mp_info: *limine.SmpMpFeature.MpInfo) callconv(.c) noreturn {
// Set up the IDT
-
const idt_addr: usize = @intFromPtr(arch.per_cpu_init_data.idt);
-
const reg: Idt.Idtr = .{ .addr = idt_addr, .limit = @sizeOf(Idt) - 1 };
-
reg.load();
// Set up our GDT and TSS
const gdt = &arch.per_cpu_init_data.gdt_buf[mp_info.processor_id];
···
const console = @import("console");
const flanterm = @import("flanterm");
const log = std.log.scoped(.amd64_init);
const StandardGdt = arch.structures.gdt.StandardGdt;
const Tss = arch.structures.tss.Tss;
···
arch.per_cpu_init_data.init(limine_requests.mp.response.?.cpu_count);
// Install the IDT
+
arch.interrupts.idt.init();
// Set up our own GDT and TSS
const gdt = &arch.per_cpu_init_data.gdt_buf[0];
···
const tss = &arch.per_cpu_init_data.tss_buf[0];
// TSS rsp 0x3800
tss.* = .{
+
.rsp0 = 0x7ffe_0000_8000,
+
.rsp1 = 0x7ffe_0000_8000,
+
.rsp2 = 0x7ffe_0000_8000,
};
gdt.tss_desc.set_tss_addr(tss);
···
// Allocate a stack (0x3000 - 0x4000)
common.mm.paging.map(.{
+
.vaddr = 0x7ffe_0000_0000,
+
.size = 65536,
.memory_type = .MemoryWriteBack,
.perms = .{
.x = false,
···
init_syscalls();
+
enter_userspace(entry, 0x69, 0x7ffe_0001_0000);
}
// Get ready for system calls (set MSRs)
···
}
}
// TODO: update the type reflection thing to make a custom
// function type for the ISR
pub const PageFaultErrorCode = packed struct(u64) {
···
return @bitCast(self.*);
}
};
+
// pub fn page_fault(stack_frame: *arch.structures.Idt.InterruptStackFrame, err_code_u64: u64) callconv(.{ .x86_64_interrupt = .{} }) void {
+
// const err_code: PageFaultErrorCode = @bitCast(err_code_u64);
+
// log.err("PAGE FAULT @ 0x{x:0>16}, code 0x{x}!!!!!!!!!!!", .{ stack_frame.instruction_pointer, err_code.val() });
+
// const cr2 = arch.registers.ControlRegisters.Cr2.read();
+
// switch (err_code.write) {
+
// true => log.err("Tried to write to vaddr 0x{x:0>16}", .{cr2}),
+
// false => log.err("Tried to read from vaddr 0x{x:0>16}", .{cr2}),
+
// }
+
// log.err("dying...", .{});
+
// arch.instructions.die();
+
// }
+
// pub fn breakpoint_handler(stack_frame: *Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void {
+
// log.warn("Breakpoint @ 0x{x:0>16}, returning execution...", .{stack_frame.instruction_pointer});
+
// }
+
// pub fn gpf(stack_frame: *Idt.InterruptStackFrame, err_code: u64) callconv(.{ .x86_64_interrupt = .{} }) void {
+
// log.warn("gpf @ 0x{x:0>16} ERR CODE {}, returning execution...", .{ stack_frame.instruction_pointer, err_code });
+
// arch.instructions.die();
+
// }
+
// pub fn double_fault(stack_frame: *Idt.InterruptStackFrame, err_code: u64) callconv(.{ .x86_64_interrupt = .{} }) noreturn {
+
// log.err("FATAL DOUBLE FAULT @ 0x{x:0>16}, code 0x{x}!!!!!!!!!!!", .{ stack_frame.instruction_pointer, err_code });
+
// log.err("dying...", .{});
+
// arch.instructions.die();
+
// }
fn bootstrapAPs() void {
log.info("Bootstrapping APs...", .{});
···
fn ap_init(mp_info: *limine.SmpMpFeature.MpInfo) callconv(.c) noreturn {
// Set up the IDT
+
arch.interrupts.idt.load();
// Set up our GDT and TSS
const gdt = &arch.per_cpu_init_data.gdt_buf[mp_info.processor_id];
+6 -3
components/ukernel/arch/amd64/interrupts/apic.zig
···
// .priority_class = 0,
// .priority_sub_class = 0,
// });
}
pub fn calibrateTimer() void {
···
}
};
-
pub fn spurious_interrupt_handler(_: *arch.structures.Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void {
log.warn("Got a spurious interrupt!", .{});
}
-
pub fn periodic_handler(_: *arch.structures.Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void {
-
log.warn("Got an APIC timer interrupt!", .{});
singleton.setRegister(.eoi, 0);
}
···
// .priority_class = 0,
// .priority_sub_class = 0,
// });
+
arch.interrupts.idt.add_handler(.{ .interrupt = 0xFF }, spurious_interrupt_handler, 3, 0);
+
arch.interrupts.idt.add_handler(.{ .interrupt = 48 }, periodic_handler, 3, 0);
}
pub fn calibrateTimer() void {
···
}
};
+
pub fn spurious_interrupt_handler(_: *arch.interrupts.idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
log.warn("Got a spurious interrupt!", .{});
}
+
pub fn periodic_handler(stack_trace: *arch.interrupts.idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
+
log.warn("Got an APIC timer interrupt, incrementing user's %rsi...", .{});
+
stack_trace.regs.rsi += 1;
singleton.setRegister(.eoi, 0);
}
+310
components/ukernel/arch/amd64/interrupts/idt.zig
···
···
+
const arch = @import("../root.zig");
+
const std = @import("std");
+
const interrupts = arch.interrupts;
+
const StandardGdt = arch.structures.gdt.StandardGdt;
+
+
// The actual IDT memory
+
const entry_count = 256;
+
export var interrupt_descriptor_table: [entry_count]Entry = undefined;
+
+
// Pointers to the actual ISRs which the global interrupt handler call
+
// Each IDT entry pushes the interrupt number then calls the global
+
// handler, which pushes more information then calls the user
+
// defined handler. Use common sense and don't return from an exception
+
// which shouldn't be returned from.
+
const DefinedHandler = *const fn (*InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void;
+
pub export var defined_handlers: [entry_count]DefinedHandler = undefined;
+
+
// The actual handlers with addresses in the IDT.
+
const ActualHandler = *const fn () callconv(.naked) void;
+
const actual_handlers: [entry_count]ActualHandler = blk: {
+
@setEvalBranchQuota(100000000);
+
var ret: [entry_count]ActualHandler = undefined;
+
for (0..256) |i| {
+
ret[i] = make_actual_handler(.{ .interrupt = i });
+
}
+
break :blk ret;
+
};
+
+
fn make_actual_handler(comptime interrupt: Interrupt) ActualHandler {
+
var asm_code: []const u8 = "";
+
// Make the stack consistent
+
if (!interrupt.has_error_code()) {
+
asm_code = asm_code ++ "pushq $0\n";
+
}
+
// Push the interrupt number
+
asm_code = asm_code ++ std.fmt.comptimePrint("pushq ${}\n", .{interrupt.interrupt});
+
// Jump to the common interrupt handler code
+
asm_code = asm_code ++ "jmp _int_handler_common";
+
const code = asm_code;
+
+
const tmp_struct = struct {
+
fn actual_handler() callconv(.naked) void {
+
asm volatile (code);
+
}
+
};
+
return tmp_struct.actual_handler;
+
}
+
+
// The global assembly for the common interrupt handler
+
comptime {
+
// Construct the push and pop instructions from SavedRegisters
+
var push_instrs: []const u8 = "\n";
+
var pop_instrs: []const u8 = "\n";
+
+
const saved_regs = @typeInfo(SavedRegisters).@"struct".fields;
+
for (saved_regs) |saved_reg| {
+
// We must prepend to push because pushes are basically
+
// building the struct in reverse order, so reverse the effects
+
push_instrs = "\n pushq %" ++ saved_reg.name ++ push_instrs;
+
// Of course, pop in the opposite order as push
+
pop_instrs = pop_instrs ++ "popq %" ++ saved_reg.name ++ "\n";
+
}
+
+
asm (
+
\\ .global _int_handler_common
+
\\ .type _int_handler_common, @function
+
\\ _int_handler_common:
+
+
// Push the general purpose registers and then CR3
+
++ push_instrs ++
+
\\ mov %cr3, %rax
+
\\ pushq %rax
+
+
// Now, rsp points to the start of InterruptFrame. Read int_num and call into an offset of
+
// the defined handlers list after setting the first arg to the created structure.
+
++ std.fmt.comptimePrint("\nmov {}(%rsp), %rcx\n", .{@offsetOf(InterruptFrame(u64), "int_num")}) ++
+
\\ mov %rsp, %rdi
+
\\ callq *defined_handlers(, %rcx, 8)
+
+
// Skip the stack all the way down to the general purpose
+
// registers, then pop all of them
+
// TODO: restore CR3??
+
++ std.fmt.comptimePrint("\nadd ${}, %rsp\n", .{@offsetOf(InterruptFrame(u64), "regs")}) ++ pop_instrs ++
+
// Skip the interrupt number and error code and iretq
+
\\ add $16, %rsp
+
\\ iretq
+
);
+
}
+
+
/// IDT Register
+
const Idtr = packed struct(u80) {
+
limit: u16,
+
addr: u64,
+
+
/// Load the IDT Register
+
pub fn load(self: *const Idtr) void {
+
asm volatile ("lidt (%[idtr_addr])"
+
:
+
: [idtr_addr] "r" (self),
+
);
+
}
+
};
+
+
// A raw IDT entry
+
const Entry = extern struct {
+
func_low: u16,
+
gdt_selector: u16,
+
options: Options,
+
func_mid: u16,
+
func_high: u32,
+
_reserved0: u32 = 0,
+
+
pub const Options = packed struct(u16) {
+
ist_index: u3,
+
_reserved0: u5 = 0,
+
disable_interrupts: bool = true,
+
// type: Type,
+
must_be_one: u3 = 0b111,
+
must_be_zero: u1 = 0,
+
dpl: u2,
+
present: bool,
+
};
+
+
pub const Owner = enum {
+
kernel,
+
user,
+
};
+
+
const Self = @This();
+
pub fn init(func_ptr: usize, dpl: u2, ist: u3) Entry {
+
// _ = typ;
+
return .{
+
.func_low = @truncate(func_ptr),
+
.gdt_selector = StandardGdt.selectors.kernel_code,
+
.options = .{
+
.ist_index = ist,
+
.dpl = dpl,
+
.present = true,
+
},
+
.func_mid = @truncate(func_ptr >> 16),
+
.func_high = @truncate(func_ptr >> 32),
+
};
+
}
+
+
// Changes the address without changing anything else
+
pub fn set_func(self: *Self, ptr: usize) void {
+
self.func_low = @truncate(ptr);
+
self.func_mid = @truncate(ptr >> 16);
+
self.func_high = @truncate(ptr >> 32);
+
}
+
};
+
+
/// A selector error code indexing into the GDT, IDT, or LDT.
+
/// Used in a general protection fault for example.
+
pub const SelectorErrorCode = packed struct(u64) {
+
external: bool,
+
interrupt: bool,
+
// Only valid if not interrupt
+
type: enum(u1) {
+
gdt = 0,
+
ldt = 1,
+
},
+
idx: u13,
+
_reserved0: u48 = 0,
+
+
pub const Target = union(enum) {
+
interrupt: Interrupt,
+
gdt_sel: u16,
+
ldt_sel: u13,
+
};
+
+
const Self = @This();
+
pub fn parse(self: Self) Target {
+
return switch (self.interrupt) {
+
true => .{ .interrupt = @enumFromInt(self.idx) },
+
false => switch (self.type) {
+
.gdt => .{ .gdt_sel = self.idx },
+
.ldt => .{ .ldt_sel = self.idx },
+
},
+
};
+
}
+
};
+
+
/// List of the general built in exceptions
+
pub const Exception = enum(u8) {
+
divide_error = 0x00,
+
debug_exeption = 0x01,
+
non_maskable_interrupt = 0x02,
+
breakpoint = 0x03,
+
overflow = 0x04,
+
bound_range_exceeded = 0x05,
+
invalid_opcode = 0x06,
+
device_not_available = 0x07,
+
double_fault = 0x08,
+
// _coprocessor_segment_overrun = 0x09,
+
invalid_tss = 0x0a,
+
segment_not_present = 0x0b,
+
stack_segment_fault = 0x0c,
+
general_protection_fault = 0x0d,
+
page_fault = 0x0e,
+
// _reserved0 = 0x0f,
+
x87_floating_point = 0x10,
+
alignment_check = 0x11,
+
machine_check = 0x12,
+
simd_floating_point = 0x13,
+
virtualization = 0x14,
+
control_protection = 0x15,
+
hypervisor = 0x1c,
+
vmm = 0x1d,
+
security_fault = 0x1e,
+
_,
+
+
fn has_error_code(self: Exception) bool {
+
return switch (self) {
+
.double_fault, .invalid_tss, .segment_not_present, .general_protection_fault, .page_fault, .security_fault => true,
+
else => false,
+
};
+
}
+
};
+
+
pub const Interrupt = packed union {
+
exception: Exception,
+
interrupt: u8,
+
+
pub fn has_error_code(self: Interrupt) bool {
+
return self.exception.has_error_code();
+
}
+
};
+
+
/// Basically all the general purpose registers except rsp,
+
/// because RSP is already in the InterruptFrame. Ordered
+
/// in the order of the X.Reg from the instruction encoding lol
+
pub const SavedRegisters = extern struct {
+
rax: u64,
+
rcx: u64,
+
rdx: u64,
+
rbx: u64,
+
// rsp: u64,
+
rbp: u64,
+
rsi: u64,
+
rdi: u64,
+
r8: u64,
+
r9: u64,
+
r10: u64,
+
r11: u64,
+
r12: u64,
+
r13: u64,
+
r14: u64,
+
r15: u64,
+
};
+
+
/// The Interrupt frame which we help generate
+
pub fn InterruptFrame(comptime ErrorCode: type) type {
+
if (@bitSizeOf(ErrorCode) != 64) {
+
@compileError("ErrorCode for InterruptFrame must be exactly 64 bits!");
+
}
+
return extern struct {
+
// CR3
+
cr3: u64,
+
// All the general purpose registers
+
regs: SavedRegisters align(8),
+
// The interrupt number
+
int_num: Interrupt align(8),
+
// Pushed by the CPU (error_code may be pushed by us)
+
error_code: ErrorCode,
+
rip: u64,
+
cs: u16 align(8),
+
eflags: u64,
+
rsp: u64,
+
ss: u16 align(8),
+
};
+
}
+
+
// Initialize the IDT with the default unhandled exception
+
pub fn init() void {
+
// Set every IDT entry to the corresponding ActualHandler
+
for (0..entry_count) |i| {
+
const actual_handler = @intFromPtr(actual_handlers[i]);
+
interrupt_descriptor_table[i] = Entry.init(actual_handler, 3, 0);
+
}
+
// Now, set every defined handler to the default one
+
@memset(&defined_handlers, arch.interrupts.unhandled_interrupt);
+
+
// Finally, load the idt
+
load();
+
+
add_handler(.{ .exception = .breakpoint }, arch.interrupts.breakpoint, 3, 0);
+
add_handler(.{ .exception = .double_fault }, arch.interrupts.double_fault, 3, 0);
+
add_handler(.{ .exception = .general_protection_fault }, arch.interrupts.general_protection_fault, 3, 0);
+
add_handler(.{ .exception = .page_fault }, arch.mm.paging.page_fault_handler, 3, 0);
+
}
+
+
pub fn load() void {
+
const idtr: Idtr = .{
+
.addr = @intFromPtr(&interrupt_descriptor_table),
+
.limit = 0xFFF,
+
};
+
idtr.load();
+
}
+
+
pub fn add_handler(interrupt: Interrupt, handler: anytype, dpl: u2, ist: u3) void {
+
// Modify the type, dpl, and ist in place
+
var tmp = interrupt_descriptor_table[interrupt.interrupt];
+
tmp.options.dpl = dpl;
+
tmp.options.ist_index = ist;
+
interrupt_descriptor_table[interrupt.interrupt] = tmp;
+
+
// Add the DefinedHandler
+
defined_handlers[interrupt.interrupt] = @ptrCast(&handler);
+
}
+9
components/ukernel/arch/amd64/interrupts/pic.zig
···
/// Remap the 8259 PIC to an interrupt base of 0x32
const arch = @import("../root.zig");
const out = arch.port.out;
const in = arch.port.in;
···
wait();
out(u8, PIC_TWO_DATA_PORT, 0b1111_1111);
wait();
}
inline fn wait() void {
···
pub fn end_of_timer_interrupt() void {
out(u8, PIC_ONE_CMD_PORT, CMD_EOI);
}
···
/// Remap the 8259 PIC to an interrupt base of 0x32
const arch = @import("../root.zig");
+
const std = @import("std");
+
const log = std.log.scoped(.pic);
const out = arch.port.out;
const in = arch.port.in;
···
wait();
out(u8, PIC_TWO_DATA_PORT, 0b1111_1111);
wait();
+
+
// Set up a spurious IRQ7 handler
+
arch.interrupts.idt.add_handler(.{ .interrupt = 32 + 7 }, spurious_handler, 3, 0);
}
inline fn wait() void {
···
pub fn end_of_timer_interrupt() void {
out(u8, PIC_ONE_CMD_PORT, CMD_EOI);
}
+
+
pub fn spurious_handler(_: *arch.interrupts.idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
+
std.log.warn("Got a spurious IRQ7 (8259)", .{});
+
}
+22
components/ukernel/arch/amd64/interrupts/root.zig
···
pub const apic = @import("apic.zig");
pub const pic = @import("pic.zig");
pub const pit = @import("pit.zig");
pub inline fn enable() void {
asm volatile ("sti");
···
pub inline fn disable() void {
asm volatile ("cli");
}
···
pub const apic = @import("apic.zig");
pub const pic = @import("pic.zig");
pub const pit = @import("pit.zig");
+
pub const idt = @import("idt.zig");
+
const std = @import("std");
+
const arch = @import("../root.zig");
pub inline fn enable() void {
asm volatile ("sti");
···
pub inline fn disable() void {
asm volatile ("cli");
}
+
+
pub fn unhandled_interrupt(stack_frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
+
std.log.err("Unhandled interrupt (0x{x})!!! rip = 0x{x}", .{ stack_frame.int_num.interrupt, stack_frame.rip });
+
arch.instructions.die();
+
}
+
+
pub fn breakpoint(stack_frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
+
std.log.warn("Breakpoint @ 0x{x}, returning execution...", .{stack_frame.rip});
+
}
+
+
pub fn double_fault(stack_frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
+
std.log.err("Double fault @ 0x{x}, dying!!!", .{stack_frame.rip});
+
arch.instructions.die();
+
}
+
+
pub fn general_protection_fault(stack_frame: *idt.InterruptFrame(idt.SelectorErrorCode)) callconv(.{ .x86_64_sysv = .{} }) void {
+
std.log.warn("General Protection Fault @ 0x{x} (Error Code {}), returning execution...", .{ stack_frame.rip, stack_frame.error_code });
+
arch.instructions.die();
+
}
+6
components/ukernel/arch/amd64/mm/paging.zig
···
const std = @import("std");
const Cr3 = arch.registers.ControlRegisters.Cr3;
const Cr4 = arch.registers.ControlRegisters.Cr4;
const Perms = common.mm.paging.Perms;
pub const PageTable = extern struct {
···
len: usize,
ptr: usize,
};
···
const std = @import("std");
const Cr3 = arch.registers.ControlRegisters.Cr3;
const Cr4 = arch.registers.ControlRegisters.Cr4;
+
const idt = arch.interrupts.idt;
const Perms = common.mm.paging.Perms;
pub const PageTable = extern struct {
···
len: usize,
ptr: usize,
};
+
+
pub fn page_fault_handler(stack_frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
+
std.log.err("Page Fault @ 0x{x}, dying...", .{stack_frame.rip});
+
arch.instructions.die();
+
}
+2 -11
components/ukernel/arch/amd64/root.zig
···
const PerCpuInitData = struct {
const StandardGdt = structures.gdt.StandardGdt;
const Tss = structures.tss.Tss;
-
const Idt = structures.Idt;
gdt_buf: []StandardGdt = undefined,
tss_buf: []Tss = undefined,
-
idt: *Idt = undefined,
const Self = @This();
pub fn init(self: *Self, cpu_count: u64) void {
-
// 1. Allocate an IDT
-
const idt_addr = common.init_data.bootmem.allocMem(@sizeOf(Idt)) catch |err| {
-
std.log.err("init PerCpuInitData: IDT alloc failed: {}", .{err});
-
@panic("rip bozo");
-
};
-
self.idt = @ptrFromInt(idt_addr);
-
-
// 2. Allocate space for GDT and TSS data
const gdt_size = @sizeOf(StandardGdt);
const tss_size = @sizeOf(Tss);
···
@panic("rip bozo");
});
-
// 3. Transmute and fill out the structure
const gdt_buf: [*]StandardGdt = @ptrCast(@alignCast(buf[0 .. gdt_size * cpu_count]));
const tss_buf: [*]Tss = @ptrCast(@alignCast(buf[gdt_size * cpu_count ..][0 .. tss_size * cpu_count]));
self.gdt_buf = gdt_buf[0..cpu_count];
···
const PerCpuInitData = struct {
const StandardGdt = structures.gdt.StandardGdt;
const Tss = structures.tss.Tss;
gdt_buf: []StandardGdt = undefined,
tss_buf: []Tss = undefined,
const Self = @This();
pub fn init(self: *Self, cpu_count: u64) void {
+
// 1. Allocate space for GDT and TSS data
const gdt_size = @sizeOf(StandardGdt);
const tss_size = @sizeOf(Tss);
···
@panic("rip bozo");
});
+
// 2. Transmute and fill out the structure
const gdt_buf: [*]StandardGdt = @ptrCast(@alignCast(buf[0 .. gdt_size * cpu_count]));
const tss_buf: [*]Tss = @ptrCast(@alignCast(buf[gdt_size * cpu_count ..][0 .. tss_size * cpu_count]));
self.gdt_buf = gdt_buf[0..cpu_count];
-170
components/ukernel/arch/amd64/structures/Idt.zig
···
-
//! The entire Interrupt Descriptor Table (IDT) structure for AMD64,
-
//! including all the necessary ISR entries. Each of the defined
-
//! ISRs is meant for a specific type of exception, while the
-
//! array at the end of the IDT can be used for whatever is necessary.
-
const std = @import("std");
-
const arch = @import("../root.zig");
-
const StandardGdt = arch.structures.gdt.StandardGdt;
-
-
/// Faulty division (mostly divide by zero)
-
divide_error: Entry(.handler),
-
/// AMD64 Debug Exception, either a fault or a trap
-
debug_exception: Entry(.handler),
-
/// Non Maskable Interrupt
-
non_maskable_interrupt: Entry(.handler),
-
/// Breakpoint (int3) trap
-
breakpoint: Entry(.handler),
-
/// Overflow trap (INTO instruction)
-
overflow: Entry(.handler),
-
/// Bound Range Exception (BOUND instruction)
-
bound_range_exceeded: Entry(.handler),
-
/// Invalid Opcode Exception
-
invalid_opcode: Entry(.handler),
-
/// Device Not Available (FPU instructions when FPU disabled)
-
device_not_available: Entry(.handler),
-
/// Double Fault Exception
-
double_fault: Entry(.abort_with_err_code),
-
_coprocessor_segment_overrun: Entry(.handler),
-
/// Invalid TSS: bad segment selector
-
invalid_tss: Entry(.handler_with_err_code),
-
/// Segment Not Present
-
segment_not_present: Entry(.handler_with_err_code),
-
/// Stack Segment Fault
-
stack_segment_fault: Entry(.handler_with_err_code),
-
/// General Protection Fault
-
general_protection_fault: Entry(.handler_with_err_code),
-
/// Page Fault
-
page_fault: Entry(.handler_with_err_code),
-
-
_reserved1: Entry(.handler),
-
/// x87 Floating Point Exception
-
x87_floating_point: Entry(.handler),
-
/// Alignment Check Exception
-
alignment_check: Entry(.handler_with_err_code),
-
/// Machine Check Exception (MCE)
-
machine_check: Entry(.abort),
-
/// SIMD Floating Point Exception
-
simd_floating_point: Entry(.handler),
-
/// Virtualization Exception
-
virtualization: Entry(.handler),
-
/// Control Protection Exception
-
control_protection: Entry(.handler_with_err_code),
-
_reserved2: [10]Entry(.handler),
-
/// User Accessible Interrupts
-
interrupts: [256 - 32]Entry(.handler),
-
-
/// An ISR Entry in the IDT
-
pub const EntryType = union(enum) {
-
abort: void,
-
abort_with_err_code: void,
-
handler: void,
-
handler_with_err_code: void,
-
handler_with_custom_err_code: type,
-
};
-
pub fn Entry(comptime entry_type: EntryType) type {
-
const return_type = switch (entry_type) {
-
.abort, .abort_with_err_code => noreturn,
-
.handler, .handler_with_err_code, .handler_with_custom_err_code => void,
-
};
-
const params: []const std.builtin.Type.Fn.Param = switch (entry_type) {
-
.handler, .abort => &.{
-
// Interrupt stack frame
-
.{ .is_generic = false, .is_noalias = false, .type = *InterruptStackFrame },
-
},
-
.handler_with_err_code, .abort_with_err_code => &.{
-
// Interrupt stack frame
-
.{ .is_generic = false, .is_noalias = false, .type = *InterruptStackFrame },
-
// Error code
-
.{ .is_generic = false, .is_noalias = false, .type = u64 },
-
},
-
.handler_with_custom_err_code => |err_code_type| &.{
-
// Interrupt stack frame
-
.{ .is_generic = false, .is_noalias = false, .type = *InterruptStackFrame },
-
// Custom Error code
-
.{ .is_generic = false, .is_noalias = false, .type = err_code_type },
-
},
-
};
-
const FunctionTypeInfo: std.builtin.Type = .{
-
.@"fn" = .{
-
.calling_convention = .{ .x86_64_interrupt = .{} },
-
.is_generic = false,
-
.is_var_args = false,
-
.return_type = return_type,
-
.params = params,
-
},
-
};
-
-
// The actual IDT entry structure
-
return extern struct {
-
func_low: u16,
-
gdt_selector: u16,
-
options: Options,
-
func_mid: u16,
-
func_high: u32,
-
_reserved: u32 = 0,
-
-
const FuncType = @Type(FunctionTypeInfo);
-
-
pub const Options = packed struct(u16) {
-
/// Interrupt Stack Table Index
-
ist_index: u3,
-
_reserved: u5 = 0,
-
disable_interrupts: bool,
-
must_be_one: u3 = 0b111,
-
must_be_zero: u1 = 0,
-
/// Descriptor Privilege Level
-
dpl: u2,
-
present: bool,
-
};
-
-
const Self = @This();
-
-
pub fn installHandler(self: *Self, func: *const FuncType) void {
-
// Fetch the Code Segment
-
const func_ptr = @intFromPtr(func);
-
self.* = .{
-
// Set the function pointer
-
.func_low = @truncate(func_ptr & 0xFFFF),
-
.func_mid = @truncate((func_ptr >> 16) & 0xFFFF),
-
.func_high = @truncate((func_ptr >> 32) & 0xFFFF_FFFF),
-
.gdt_selector = StandardGdt.selectors.kernel_code,
-
.options = .{
-
// No Interrupt Stack Table yet
-
.ist_index = 0,
-
// Mask interrupts while running ISR handler
-
.disable_interrupts = true,
-
// Ring 3 Minimum privilege level
-
.dpl = 3,
-
// Mark as present
-
.present = true,
-
},
-
};
-
}
-
};
-
}
-
-
/// IDT Register
-
pub const Idtr = packed struct(u80) {
-
limit: u16,
-
addr: u64,
-
-
/// Load the IDT Register
-
pub fn load(self: *const Idtr) void {
-
asm volatile ("lidt (%[idtr_addr])"
-
:
-
: [idtr_addr] "r" (self),
-
);
-
}
-
};
-
-
/// Interrupt Stack Frame
-
/// TODO: maybe move this somewhere else
-
pub const InterruptStackFrame = extern struct {
-
instruction_pointer: u64,
-
code_segment: u16,
-
_reserved1: [6]u8,
-
cpu_flags: u64,
-
stack_pointer: u64,
-
stack_segment: u16,
-
_reserved2: [6]u8,
-
};
···
-1
components/ukernel/arch/amd64/structures/root.zig
···
pub const gdt = @import("gdt.zig");
pub const tss = @import("tss.zig");
-
pub const Idt = @import("Idt.zig");
···
pub const gdt = @import("gdt.zig");
pub const tss = @import("tss.zig");
+1 -1
components/ukernel/build.zig
···
});
kernel.pie = false;
-
kernel.want_lto = true;
kernel.setLinkerScript(b.path(linker_script_path));
b.installArtifact(kernel);
}
···
});
kernel.pie = false;
+
kernel.want_lto = false;
kernel.setLinkerScript(b.path(linker_script_path));
b.installArtifact(kernel);
}