Microkernel thing OS experiment (Zig ⚡)

Refactor to be more porting-compatible

Now the root module is the common bits and not the arch bits. This
should be more friendly to eventually getting the aarch64 port working
again.

pci.express f49cd591 69fe1671

verified
Changed files
+327 -261
components
+46 -184
components/ukernel/arch/amd64/boot.zig
···
const StandardGdt = arch.structures.gdt.StandardGdt;
const Tss = arch.structures.tss.Tss;
-
var pg_ctx: arch.mm.paging.Context = undefined;
-
pub const limine_requests = struct {
export var start_marker: limine.RequestsStartMarker linksection(".limine_reqs_start") = .{};
export var end_marker: limine.RequestsEndMarker linksection(".limine_reqs_end") = .{};
···
pub export var mp: limine.SmpMpFeature.MpRequest linksection(".limine_reqs") = .{ .flags = .{ .x2apic = true } };
};
-
pub fn bsp_init() callconv(.c) noreturn {
+
pub fn early_init() void {
// Don't optimize away the limine requests
inline for (@typeInfo(limine_requests).@"struct".decls) |decl| {
std.mem.doNotOptimizeAway(&@field(limine_requests, decl.name));
···
arch.instructions.die();
}
+
// If the base revision isn't supported, we can't boot
+
if (!limine_requests.base_revision.isSupported()) {
+
@branchHint(.cold);
+
arch.instructions.die();
+
}
+
// Die if we don't have a memory map or Higher Half Direct Mapping
if (limine_requests.memmap.response == null) {
@branchHint(.cold);
···
const hhdm_offset = limine_requests.hhdm.response.?.offset;
common.init_data.hhdm_slide = hhdm_offset;
-
// Set up the temporary Physical Memory Allocator
-
common.mm.bootmem.init();
-
-
// Add in a framebuffer if found
-
initConsole();
-
-
// Get basic information through CPUID
+
// Get CPUID info
arch.instructions.cpuid.init();
-
// Add in ACPI/dtb if found, prefer ACPI
-
initHwDesc();
-
-
// Attach the root task
-
if (limine_requests.modules.response) |module_response| {
-
if (module_response.module_count > 0) {
-
const mod = module_response.modules.?[0];
-
const mod_addr: [*]align(4096) u8 = @ptrCast(mod.address);
-
const mod_size = mod.size;
-
log.info("Loading root task with {s} @ {*}", .{ mod.path, mod.address });
-
common.init_data.root_task = mod_addr[0..mod_size];
-
}
-
} else {
-
@branchHint(.unlikely);
-
@panic("No root task found!");
-
}
+
// Set up the kernel paging context
+
common.init_data.kernel_paging_ctx = arch.mm.paging.Context.get_current();
+
}
-
// Initialize per-cpu data (GDT and TSS)
+
pub fn bsp_init() void {
+
// Set up per-cpu data
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];
···
gdt.tss_desc.set_tss_addr(tss);
gdt.load();
-
log.info("BSP successfully setup GDT+TSS!", .{});
-
-
// AP bootstrap
-
bootstrapAPs();
-
-
// Calibrate our TSC
-
arch.tsc.calibrate_pit() catch {
-
log.info("Failed to calibrate with PIT!", .{});
-
arch.instructions.die();
-
};
-
log.info("TSC estimate: {} MHz", .{arch.tsc.tsc_khz / 1000});
-
-
log.info("Setting up scheduling...", .{});
-
-
pg_ctx = arch.mm.paging.Context.get_current();
-
initApic() catch |err| {
-
log.err("Failed to set up APIC! {}", .{err});
-
@panic("apic");
-
};
-
-
log.info("Allocating code for userspace...", .{});
-
-
// Allocate a stack (0x3000 - 0x4000)
-
common.mm.paging.map(.{
-
.vaddr = 0x7ffe_0000_0000,
-
.size = 65536,
-
.memory_type = .MemoryWriteBack,
-
.perms = .{
-
.x = false,
-
.u = true,
-
.w = true,
-
},
-
.context = &pg_ctx,
-
}) catch @panic("couldn't map user stack");
-
-
const entry = common.loadRootTask(&pg_ctx) catch |err| {
-
log.err("Couldn't load the root task! {}", .{err});
-
@panic("ggz");
-
};
-
log.info("Dropping to userspace entry 0x{x:0>16}", .{entry});
-
-
init_syscalls();
-
-
arch.interrupts.apic.armTimer(1000);
-
enter_userspace(entry, 0x69, 0x7ffe_0001_0000);
-
}
-
-
// Get ready for system calls (set MSRs)
-
fn init_syscalls() void {
-
// Set up the STAR MSR with the segment descriptors
-
const IA32_STAR = arch.registers.MSR(u64, 0xC0000081);
-
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;
-
IA32_STAR.write(star_value);
-
-
// Set up the EFER MSR with SCE (System Call Enable)
-
const IA32_EFER = arch.registers.MSR(u64, 0xC0000080);
-
const efer_val = IA32_EFER.read() | 0b1;
-
IA32_EFER.write(efer_val);
-
-
// Set up LSTAR with the syscall handler and FMASK to clear interrupts
-
const IA32_LSTAR = arch.registers.MSR(u64, 0xC0000082);
-
IA32_LSTAR.write(@intFromPtr(syscall_entry));
-
-
const IA32_FMASK = arch.registers.MSR(u64, 0xC0000084);
-
IA32_FMASK.write(1 << 9);
-
}
-
-
const syscall_entry = @extern(*anyopaque, .{
-
.name = "syscall_entry",
-
});
-
export fn syscall_handler(rdi: usize, rsi: usize) callconv(.c) void {
-
std.log.info("Got a syscall! rdi=0x{x}, rsi=0x{x}", .{ rdi, rsi });
-
}
-
-
fn enter_userspace(entry: u64, arg: u64, stack: u64) noreturn {
-
log.info("usercode64 GDT 0x{x}, userdata64 GDT 0x{x}", .{ arch.structures.gdt.StandardGdt.selectors.user_code, arch.structures.gdt.StandardGdt.selectors.user_data });
-
const cr3 = arch.registers.ControlRegisters.Cr3.read();
-
arch.registers.ControlRegisters.Cr3.write(cr3);
-
asm volatile (
-
\\ push %[userdata64]
-
\\ push %[stack]
-
\\ push $0x202
-
\\ push %[usercode64]
-
\\ push %[entry]
-
\\
-
\\ mov %[userdata64], %%rax
-
\\ mov %%rax, %%es
-
\\ mov %%rax, %%ds
-
\\
-
\\ xor %%rsi, %%rsi
-
\\ xor %%rax, %%rax
-
\\ xor %%rdx, %%rdx
-
\\ xor %%rcx, %%rcx
-
\\ xor %%rbp, %%rbp
-
\\ xor %%rbx, %%rbx
-
\\
-
\\ xor %%r8, %%r8
-
\\ xor %%r9, %%r9
-
\\ xor %%r10, %%r10
-
\\ xor %%r11, %%r11
-
\\ xor %%r12, %%r12
-
\\ xor %%r13, %%r13
-
\\ xor %%r14, %%r14
-
\\ xor %%r15, %%r15
-
\\
-
\\ iretq
-
\\
-
:
-
: [arg] "{rdi}" (arg),
-
[stack] "r" (stack),
-
[entry] "r" (entry),
-
[userdata64] "i" (arch.structures.gdt.StandardGdt.selectors.user_data),
-
[usercode64] "i" (arch.structures.gdt.StandardGdt.selectors.user_code),
-
);
-
unreachable;
-
}
-
-
fn initApic() !void {
-
const has_x2apic = limine_requests.mp.response.?.flags.x2apic;
-
arch.interrupts.apic.singleton = switch (has_x2apic) {
-
true => .x2apic,
-
false => blk: {
-
// Map the APIC first!
-
const apic_base = common.mm.physToHHDM([*]volatile u8, 0xFEE0_0000);
-
try common.mm.paging.mapPhys(.{
-
.vaddr = @intFromPtr(apic_base),
-
.paddr = 0xFEE0_0000,
-
.size = 0x1000,
-
.memory_type = .DeviceUncacheable,
-
.perms = .{
-
.x = false,
-
.u = false,
-
.w = true,
-
},
-
.context = &pg_ctx,
-
});
-
break :blk .{ .xapic = apic_base };
-
},
-
};
-
// Set up the spurious vector, TPR, and
-
// calibrate the timer
-
arch.interrupts.apic.init.initialSetup();
-
-
// Enable one-shot interrupts
-
arch.interrupts.apic.init.enableOneshotInterrupt();
-
}
-
-
fn initConsole() void {
+
// Add in the framebuffer
if (limine_requests.framebuffer.response) |fb_response| {
if (fb_response.framebuffer_count > 0) {
-
const fb = common.aux.fb_from_limine(fb_response.getFramebuffers()[0]);
-
common.init_data.framebuffer = fb;
-
// Create a canvas for the console to render to
-
const canvas: [*]u8 = @ptrFromInt(common.init_data.bootmem.allocMem(fb.width * fb.height * fb.bypp) catch @panic("Couldn't allocate a canvas"));
-
@memset(canvas[0 .. fb.width * fb.height * fb.bypp], 0);
+
const fb = fb_response.getFramebuffers()[0];
+
common.init_data.framebuffer = .{
+
.address = @ptrCast(@alignCast(fb.address)),
+
.width = fb.width,
+
.height = fb.height,
+
.pitch = fb.pitch,
+
.red_mask_size = fb.red_mask_size,
+
.red_mask_shift = fb.red_mask_shift,
+
.green_mask_size = fb.green_mask_size,
+
.green_mask_shift = fb.green_mask_shift,
+
.blue_mask_size = fb.blue_mask_size,
+
.blue_mask_shift = fb.blue_mask_shift,
+
.bypp = fb.bpp / 8,
+
};
+
}
+
}
-
common.init_data.console = console.Console.init(fb, canvas);
+
// Add in ACPI/dtb if found, prefer ACPI
+
initHwDesc();
+
+
// Attach the root task
+
if (limine_requests.modules.response) |module_response| {
+
if (module_response.module_count > 0) {
+
const mod = module_response.modules.?[0];
+
const mod_addr: [*]align(4096) u8 = @ptrCast(mod.address);
+
const mod_size = mod.size;
+
log.info("Loading root task with {s} @ {*}", .{ mod.path, mod.address });
+
common.init_data.root_task = mod_addr[0..mod_size];
}
+
} else {
+
@branchHint(.unlikely);
+
@panic("No root task found!");
}
+
+
bootstrapAPs();
}
fn initHwDesc() void {
+32 -2
components/ukernel/arch/amd64/instructions/cpuid.zig
···
const res = cpuid(1, 0);
const feat_ecx: FeaturesEcx = @bitCast(res.ecx);
arch.interrupts.apic.tsc_deadline_available = feat_ecx.tsc_deadline;
+
arch.interrupts.apic.has_x2apic = feat_ecx.x2apic;
}
const FeaturesEcx = packed struct(u32) {
-
_reserved0: u24,
+
sse3: bool,
+
pclmulqdq: bool,
+
dtes64: bool,
+
monitor: bool,
+
ds_cpl: bool,
+
vmx: bool,
+
smx: bool,
+
est: bool,
+
tm2: bool,
+
ssse3: bool,
+
cnxt_id: bool,
+
sdbg: bool,
+
fma: bool,
+
cx16: bool,
+
xtpr: bool,
+
pdcm: bool,
+
_reserved0: bool,
+
pcid: bool,
+
dca: bool,
+
sse4_1: bool,
+
sse4_2: bool,
+
x2apic: bool,
+
movbe: bool,
+
popcnt: bool,
tsc_deadline: bool,
-
_reserved1: u7,
+
aesni: bool,
+
xsave: bool,
+
osxsave: bool,
+
avx: bool,
+
f16c: bool,
+
rdrand: bool,
+
hypervisor: bool,
};
pub inline fn cpuid(leaf: u32, sub: u32) DefaultResults {
+40 -7
components/ukernel/arch/amd64/interrupts/apic.zig
···
const std = @import("std");
const arch = @import("../root.zig");
+
const idt = arch.interrupts.idt;
const log = std.log.scoped(.apic);
+
const common = @import("common");
pub var lapic_timer_khz: usize = 0;
pub var tsc_deadline_available = false;
+
pub var has_x2apic: bool = false;
// tbh every cpu will be either x2apic or not, and if xapic it will
// have the exact same base address anyways so this is fine
···
};
pub const init = struct {
-
// Get the APIC ready (call first)
pub fn initialSetup() void {
+
// First, make the APIC accessible
+
initSingleton() catch |err| {
+
log.err("Failed to map APIC! {}", .{err});
+
@panic("initSingleton");
+
};
+
// Set up the interrupt handlers
singleton.setSpuriousInterruptRegister(.{
.apic_soft_enable = true,
.idt_entry = 0xFF,
···
// .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);
+
arch.interrupts.idt.add_handler(.{ .interrupt = 0xFF }, u64, spurious_interrupt_handler, 3, 0);
+
arch.interrupts.idt.add_handler(.{ .interrupt = 48 }, u64, timer_handler, 3, 0);
+
// Calibrate against the TSC
calibrateTimer();
+
// Set up the LVT Timer Register
+
enableOneshotInterrupt();
+
}
+
+
fn initSingleton() !void {
+
arch.interrupts.apic.singleton = switch (has_x2apic) {
+
true => .x2apic,
+
false => blk: {
+
// Map the APIC first!
+
const apic_base = common.mm.physToHHDM([*]volatile u8, 0xFEE0_0000);
+
try common.mm.paging.mapPhys(.{
+
.vaddr = @intFromPtr(apic_base),
+
.paddr = 0xFEE0_0000,
+
.size = 0x1000,
+
.memory_type = .DeviceUncacheable,
+
.perms = .{
+
.x = false,
+
.u = false,
+
.w = true,
+
},
+
});
+
break :blk .{ .xapic = apic_base };
+
},
+
};
}
fn calibrateTimer() void {
···
lapic_timer_khz = norm / 5;
-
log.debug("APIC timer: {} kHz", .{lapic_timer_khz});
+
log.debug("timer: {} kHz", .{lapic_timer_khz});
}
-
pub fn enableOneshotInterrupt() void {
+
fn enableOneshotInterrupt() void {
const mode: LAPIC.LVTTimerRegister.Mode = switch (tsc_deadline_available) {
true => .tsc_deadline,
false => blk: {
···
}
}
-
pub fn spurious_interrupt_handler(_: *arch.interrupts.idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
+
pub fn spurious_interrupt_handler(_: *idt.InterruptFrame(u64)) callconv(idt.CallConv) void {
log.warn("Got a spurious interrupt!", .{});
}
-
pub fn periodic_handler(stack_trace: *arch.interrupts.idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
+
pub fn timer_handler(stack_trace: *idt.InterruptFrame(u64)) callconv(idt.CallConv) void {
log.warn("Got an APIC timer interrupt, incrementing user's rsi...", .{});
stack_trace.regs.rsi += 1;
singleton.setRegister(.eoi, 0);
+12 -10
components/ukernel/arch/amd64/interrupts/idt.zig
···
// 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;
+
pub const CallConv: std.builtin.CallingConvention = .{ .x86_64_sysv = .{} };
+
pub fn InterruptHandler(comptime E: type) type {
+
return *const fn (*InterruptFrame(E)) callconv(CallConv) void;
+
}
+
pub export var defined_handlers: [entry_count]InterruptHandler(u64) = undefined;
// The actual handlers with addresses in the IDT.
const ActualHandler = *const fn () callconv(.naked) void;
···
eflags: u64,
rsp: u64,
ss: u16 align(8),
+
+
pub fn normalize(self: *InterruptFrame(ErrorCode)) *InterruptFrame(u64) {
+
return @ptrCast(self);
+
}
};
}
···
// 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 {
···
idtr.load();
}
-
pub fn add_handler(interrupt: Interrupt, handler: anytype, dpl: u2, ist: u3) void {
+
pub fn add_handler(interrupt: Interrupt, comptime E: type, handler: InterruptHandler(E), 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);
+
// Add the InterruptHandler
+
defined_handlers[interrupt.interrupt] = @ptrCast(handler);
}
+3 -2
components/ukernel/arch/amd64/interrupts/pic.zig
···
/// Remap the 8259 PIC to an interrupt base of 0x32
const arch = @import("../root.zig");
+
const idt = arch.interrupts.idt;
const std = @import("std");
const log = std.log.scoped(.pic);
const out = arch.port.out;
···
wait();
// Set up a spurious IRQ7 handler
-
arch.interrupts.idt.add_handler(.{ .interrupt = 32 + 7 }, spurious_handler, 3, 0);
+
arch.interrupts.idt.add_handler(.{ .interrupt = 32 + 7 }, u64, spurious_handler, 3, 0);
}
inline fn wait() void {
···
out(u8, PIC_ONE_CMD_PORT, CMD_EOI);
}
-
pub fn spurious_handler(_: *arch.interrupts.idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
+
pub fn spurious_handler(_: *idt.InterruptFrame(u64)) callconv(idt.CallConv) void {
std.log.warn("Got a spurious IRQ7 (8259)", .{});
}
+96 -6
components/ukernel/arch/amd64/interrupts/root.zig
···
pub const pit = @import("pit.zig");
pub const idt = @import("idt.zig");
const std = @import("std");
+
const log = std.log.scoped(.interrupts);
const arch = @import("../root.zig");
pub inline fn enable() void {
···
asm volatile ("cli");
}
-
fn print_regs(frame: *idt.InterruptFrame(u64)) void {
+
const syscall_entry = @extern(*anyopaque, .{
+
.name = "syscall_entry",
+
});
+
+
export fn syscall_handler(rdi: usize, rsi: usize) callconv(.c) void {
+
std.log.info("Got a syscall! rdi=0x{x}, rsi=0x{x}", .{ rdi, rsi });
+
}
+
+
pub fn init_syscalls() void {
+
// Set up the STAR MSR with the segment descriptors
+
const IA32_STAR = arch.registers.MSR(u64, 0xC0000081);
+
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;
+
IA32_STAR.write(star_value);
+
+
// Set up the EFER MSR with SCE (System Call Enable)
+
const IA32_EFER = arch.registers.MSR(u64, 0xC0000080);
+
const efer_val = IA32_EFER.read() | 0b1;
+
IA32_EFER.write(efer_val);
+
+
// Set up LSTAR with the syscall handler and FMASK to clear interrupts
+
const IA32_LSTAR = arch.registers.MSR(u64, 0xC0000082);
+
IA32_LSTAR.write(@intFromPtr(syscall_entry));
+
+
const IA32_FMASK = arch.registers.MSR(u64, 0xC0000084);
+
IA32_FMASK.write(1 << 9);
+
}
+
+
pub fn print_regs(frame: *idt.InterruptFrame(u64)) void {
std.log.err("CR3: 0x{x:0>16}", .{frame.cr3});
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 });
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 });
···
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 });
}
-
pub fn unhandled_interrupt(frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
+
pub fn unhandled_interrupt(frame: *idt.InterruptFrame(u64)) callconv(idt.CallConv) void {
if (std.enums.tagName(idt.Exception, frame.int_num.exception)) |exception_name| {
std.log.err("Unhandled interrupt (0x{x} : {s})!!!", .{ frame.int_num.interrupt, exception_name });
} else {
···
arch.instructions.die();
}
-
pub fn breakpoint(stack_frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
+
pub fn breakpoint(stack_frame: *idt.InterruptFrame(u64)) callconv(idt.CallConv) 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 {
+
pub fn double_fault(stack_frame: *idt.InterruptFrame(u64)) callconv(idt.CallConv) void {
std.log.err("Double fault @ 0x{x}, dying!!!", .{stack_frame.rip});
print_regs(stack_frame);
arch.interrupts.disable();
arch.instructions.die();
}
-
pub fn general_protection_fault(stack_frame: *idt.InterruptFrame(idt.SelectorErrorCode)) callconv(.{ .x86_64_sysv = .{} }) void {
+
pub fn general_protection_fault(stack_frame: *idt.InterruptFrame(idt.SelectorErrorCode)) callconv(idt.CallConv) void {
std.log.warn("General Protection Fault @ 0x{x}", .{stack_frame.rip});
const target = stack_frame.error_code.parse();
···
std.log.warn("LDT selector: 0x{x}", .{ldt_sel});
},
}
-
print_regs(@ptrCast(stack_frame));
+
print_regs(stack_frame.normalize());
arch.instructions.die();
}
+
+
// Set up the IDT, PIC, TSC, and APIC
+
pub fn init() void {
+
// Set up the IDT and associated vectors
+
idt.init();
+
idt.add_handler(.{ .exception = .breakpoint }, u64, arch.interrupts.breakpoint, 3, 0);
+
idt.add_handler(.{ .exception = .double_fault }, u64, arch.interrupts.double_fault, 3, 0);
+
idt.add_handler(.{ .exception = .general_protection_fault }, idt.SelectorErrorCode, arch.interrupts.general_protection_fault, 3, 0);
+
idt.add_handler(.{ .exception = .page_fault }, u64, arch.mm.paging.page_fault_handler, 3, 0);
+
// Set up the 8254's (we need 8259 timer to calibrate tsc)
+
pic.init();
+
// Calibrate the TSC against the 8259
+
arch.tsc.calibrate_pit() catch |err| {
+
log.err("Failed to calibrate TSC: {}", .{err});
+
};
+
// Set up everything needed to arm the timer
+
apic.init.initialSetup();
+
}
+
+
// TODO: make this slightly less shit
+
pub fn enter_userspace(entry: u64, arg: u64, stack: u64) noreturn {
+
log.info("usercode64 GDT 0x{x}, userdata64 GDT 0x{x}", .{ arch.structures.gdt.StandardGdt.selectors.user_code, arch.structures.gdt.StandardGdt.selectors.user_data });
+
const cr3 = arch.registers.ControlRegisters.Cr3.read();
+
arch.registers.ControlRegisters.Cr3.write(cr3);
+
asm volatile (
+
\\ push %[userdata64]
+
\\ push %[stack]
+
\\ push $0x202
+
\\ push %[usercode64]
+
\\ push %[entry]
+
\\
+
\\ mov %[userdata64], %%rax
+
\\ mov %%rax, %%es
+
\\ mov %%rax, %%ds
+
\\
+
\\ xor %%rsi, %%rsi
+
\\ xor %%rax, %%rax
+
\\ xor %%rdx, %%rdx
+
\\ xor %%rcx, %%rcx
+
\\ xor %%rbp, %%rbp
+
\\ xor %%rbx, %%rbx
+
\\
+
\\ xor %%r8, %%r8
+
\\ xor %%r9, %%r9
+
\\ xor %%r10, %%r10
+
\\ xor %%r11, %%r11
+
\\ xor %%r12, %%r12
+
\\ xor %%r13, %%r13
+
\\ xor %%r14, %%r14
+
\\ xor %%r15, %%r15
+
\\
+
\\ iretq
+
\\
+
:
+
: [arg] "{rdi}" (arg),
+
[stack] "r" (stack),
+
[entry] "r" (entry),
+
[userdata64] "i" (arch.structures.gdt.StandardGdt.selectors.user_data),
+
[usercode64] "i" (arch.structures.gdt.StandardGdt.selectors.user_code),
+
);
+
unreachable;
+
}
+3 -1
components/ukernel/arch/amd64/mm/paging.zig
···
ptr: usize,
};
-
pub fn page_fault_handler(stack_frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
+
pub fn page_fault_handler(stack_frame: *idt.InterruptFrame(u64)) callconv(idt.CallConv) void {
std.log.err("Page Fault @ 0x{x}, dying...", .{stack_frame.rip});
+
arch.interrupts.print_regs(stack_frame.normalize());
+
std.log.err("Error CR2: 0x{x:0>16}, Error Code: 0x{x:0>16}", .{ arch.registers.ControlRegisters.Cr2.read(), stack_frame.error_code });
arch.instructions.die();
}
+7 -15
components/ukernel/arch/amd64/root.zig
···
const common = @import("common");
const std = @import("std");
-
fn pageSize() usize {
-
return 4 << 10;
-
}
-
-
pub const std_options: std.Options = .{
-
.logFn = common.aux.logFn,
-
.page_size_min = 4 << 10,
-
.page_size_max = 4 << 10,
-
.queryPageSize = pageSize,
+
// needed by std options
+
pub const page_size = struct {
+
pub const min = 4 << 10;
+
pub const max = 4 << 10;
+
pub fn get() usize {
+
return 4 << 10;
+
}
};
-
pub const panic = std.debug.FullPanic(common.aux.panic);
pub var per_cpu_init_data: PerCpuInitData = .{};
···
self.tss_buf = tss_buf[0..cpu_count];
}
};
-
-
comptime {
-
// Entry point (_start)
-
@export(&boot.bsp_init, .{ .name = "_start", .linkage = .strong });
-
}
+4
components/ukernel/arch/amd64/tsc.zig
···
const arch = @import("root.zig");
+
const std = @import("std");
const out = arch.port.out;
const in = arch.port.in;
+
const log = std.log.scoped(.tsc);
pub var tsc_khz: usize = 0;
···
if (pollcnt < 1000) return error.PitError;
tsc_khz = (end - start) / 50;
+
+
log.debug("{} MHz", .{tsc_khz / 1000});
}
/// Delay for a set amount of ms using crappy polling
+9 -17
components/ukernel/build.zig
···
const target = b.resolveTargetQuery(target_query);
const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseSafe });
-
const arch_module = b.createModule(.{
-
.root_source_file = b.path(arch_root_path),
+
const common_mod = b.createModule(.{
+
.root_source_file = b.path("common/root.zig"),
.target = target,
.optimize = optimize,
.code_model = code_model,
});
+
const arch_module = b.createModule(.{
+
.root_source_file = b.path(arch_root_path),
+
});
switch (arch) {
.amd64 => {
arch_module.addAssemblyFile(b.path("arch/amd64/asm/traps.S"));
···
else => {},
}
-
const limine_dep = b.dependency("limine", .{
-
.api_revision = 3,
-
});
-
const spinlock_dep = b.dependency("spinlock", .{});
-
const console_dep = b.dependency("console", .{});
-
-
const limine_mod = limine_dep.module("limine");
-
const spinlock_mod = spinlock_dep.module("spinlock");
-
const console_mod = console_dep.module("console");
-
-
const common_mod = b.createModule(.{
-
.root_source_file = b.path("common/root.zig"),
-
});
+
const spinlock_mod = b.dependency("spinlock", .{}).module("spinlock");
+
const limine_mod = b.dependency("limine", .{ .api_revision = 3 }).module("limine");
+
const console_mod = b.dependency("console", .{}).module("console");
arch_module.addImport("limine", limine_mod);
arch_module.addImport("console", console_mod);
···
common_mod.addImport("arch", arch_module);
common_mod.addImport("spinlock", spinlock_mod);
common_mod.addImport("console", console_mod);
-
common_mod.addImport("limine", limine_mod);
const kernel = b.addExecutable(.{
.name = "ukernel",
-
.root_module = arch_module,
+
.root_module = common_mod,
// TODO: remove when x86 backend is less broken with removing CPU features
.use_llvm = true,
});
+9 -15
components/ukernel/common/aux.zig
···
const std = @import("std");
const arch = @import("arch");
const spinlock = @import("spinlock");
-
const limine = @import("limine");
// Types
pub const HardwareDescription = union(enum) {
···
hardware_description: HardwareDescription = .none,
root_task: []align(4096) u8 = undefined,
hhdm_slide: usize = 0,
+
kernel_paging_ctx: arch.mm.paging.Context = undefined,
};
-
pub fn fb_from_limine(fb: *const limine.Framebuffer) console.Framebuffer {
-
return .{
-
.address = @ptrCast(@alignCast(fb.address)),
-
.width = fb.width,
-
.height = fb.height,
-
.pitch = fb.pitch,
-
.red_mask_size = fb.red_mask_size,
-
.red_mask_shift = fb.red_mask_shift,
-
.green_mask_size = fb.green_mask_size,
-
.green_mask_shift = fb.green_mask_shift,
-
.blue_mask_size = fb.blue_mask_size,
-
.blue_mask_shift = fb.blue_mask_shift,
-
.bypp = fb.bpp / 8,
-
};
+
pub fn initConsole() void {
+
const fb = common.init_data.framebuffer.?;
+
// Create a canvas for the console to render to
+
const canvas: [*]u8 = @ptrFromInt(common.init_data.bootmem.allocMem(fb.width * fb.height * fb.bypp) catch @panic("Couldn't allocate a canvas"));
+
@memset(canvas[0 .. fb.width * fb.height * fb.bypp], 0);
+
+
// Set the console instance
+
common.init_data.console = console.Console.init(fb, canvas);
}
var stdout_lock: spinlock.Spinlock = .{};
+3 -2
components/ukernel/common/mm/paging.zig
···
const arch = @import("arch");
const std = @import("std");
+
const common = @import("../root.zig");
const TableHandle = arch.mm.paging.TableHandle;
const MemoryType = arch.mm.paging.MemoryType;
const Context = arch.mm.paging.Context;
···
size: usize,
perms: Perms,
memory_type: MemoryType,
-
context: *Context,
+
context: *Context = &common.init_data.kernel_paging_ctx,
}) !void {
const root = args.context.root_table(args.vaddr);
var vaddr = args.vaddr;
···
size: usize,
perms: Perms,
memory_type: MemoryType,
-
context: *Context,
+
context: *Context = &common.init_data.kernel_paging_ctx,
}) !void {
const root = args.context.root_table(args.vaddr);
var vaddr = args.vaddr;
+63
components/ukernel/common/root.zig
···
pub const aux = @import("aux.zig");
pub const mm = @import("mm/root.zig");
pub const loadRootTask = loader.loadRootTask;
+
const arch = @import("arch");
+
const std = @import("std");
const loader = @import("loader.zig");
// Arch init must set up appropriate fields!
pub var init_data: aux.InitState = .{};
+
+
// Generic bsp init
+
pub fn generic_init() callconv(.c) noreturn {
+
const log = std.log.scoped(.generic_init);
+
// First, do early arch init
+
arch.boot.early_init();
+
+
// Now, set up the bootmem and console
+
mm.bootmem.init();
+
+
// Now, do the rest of the arch init
+
arch.boot.bsp_init();
+
+
// Next, set up the console
+
aux.initConsole();
+
+
// Now, set up interrupts
+
arch.interrupts.init();
+
+
log.info("Loading root task...", .{});
+
+
// The following needs to be genericized and unshittified
+
+
// Allocate a stack
+
mm.paging.map(.{
+
.vaddr = 0x7ffe_0000_0000,
+
.size = 65536,
+
.memory_type = .MemoryWriteBack,
+
.perms = .{
+
.x = false,
+
.u = true,
+
.w = true,
+
},
+
}) catch @panic("couldn't map user stack");
+
+
// TODO: make user page tables!
+
const entry = loadRootTask(&init_data.kernel_paging_ctx) catch |err| {
+
log.err("Couldn't load the root task! {}", .{err});
+
@panic("ggz");
+
};
+
log.info("Dropping to userspace entry 0x{x:0>16}", .{entry});
+
+
arch.interrupts.init_syscalls();
+
+
arch.interrupts.apic.armTimer(1000);
+
arch.interrupts.enter_userspace(entry, 0x69, 0x7ffe_0001_0000);
+
}
+
+
// std options etc.
+
pub const panic = std.debug.FullPanic(aux.panic);
+
pub const std_options: std.Options = .{
+
.logFn = aux.logFn,
+
.page_size_min = arch.page_size.min,
+
.page_size_max = arch.page_size.max,
+
.queryPageSize = arch.page_size.get,
+
};
+
+
comptime {
+
// Entry point (_start)
+
@export(&generic_init, .{ .name = "_start", .linkage = .strong });
+
}