Compare commits

..

6 Commits

Author SHA1 Message Date
da5335410d
core/x86_64: Add general protection fault handler 2025-02-15 22:19:08 +01:00
c4bb857c4e
core: Reserve physical page 0
In the kernel's early stages we access physical memory identity-mapped, so it's not very good for us to allocate that frame and then try to use it.
2025-02-15 22:18:49 +01:00
baf3d39e98
core: Make findMultibootTags take a context argument
Will be useful later.
2025-02-15 22:18:05 +01:00
d8236bc8f7
core: Add a page fault handler 2025-02-15 15:18:40 +01:00
18ec598394
core: Enable interrupts in platformEndInit()
This way, each architecture can re-enable interrupts whenever necessary
2025-02-15 15:18:24 +01:00
1ec239147a
core: Add workaround for QEMU-specific triple-faults without KVM
Without KVM, qemu inserts an invalid 12-GiB reserved entry at the end of the physical address space, making the kernel reserve way more space than necessary for the page bitmap (and overwriting the page directory pages).
2025-02-15 15:16:50 +01:00
6 changed files with 102 additions and 18 deletions

View File

@ -73,11 +73,80 @@ export fn asmInterruptEntry() callconv(.Naked) void {
); );
} }
const Exceptions = enum(u64) {
GeneralProtectionFault = 0xd,
PageFault = 0xe,
};
const PageFaultCodes = enum(u64) {
Present = 1 << 0,
Write = 1 << 1,
User = 1 << 2,
Reserved = 1 << 3,
NoExecuteViolation = 1 << 4,
};
const SYSCALL_INTERRUPT = 66; const SYSCALL_INTERRUPT = 66;
fn generalProtectionFault(frame: *InterruptStackFrame) void {
debug.print("General protection fault!\n", .{});
debug.print("Faulting instruction: {x}\n", .{frame.rip});
const code = frame.error_or_irq;
debug.print("Error code: {d}\n", .{code});
while (true) {}
}
fn pageFault(frame: *InterruptStackFrame) void {
var fault_address: u64 = undefined;
asm volatile ("mov %%cr2, %[cr2]"
: [cr2] "=r" (fault_address),
);
debug.print("Page fault while accessing {x}!\n", .{fault_address});
debug.print("Faulting instruction: {x}\n", .{frame.rip});
const code = frame.error_or_irq;
debug.print("Fault details: {s} | ", .{switch ((code & @intFromEnum(PageFaultCodes.Present)) > 0) {
true => "Present",
false => "Not present",
}});
debug.print("{s} | ", .{switch ((code & @intFromEnum(PageFaultCodes.Write)) > 0) {
true => "Write access",
false => "Read access",
}});
debug.print("{s}", .{switch ((code & @intFromEnum(PageFaultCodes.User)) > 0) {
true => "User mode",
false => "Kernel mode",
}});
debug.print("{s}", .{switch ((code & @intFromEnum(PageFaultCodes.Reserved)) > 0) {
true => " | Reserved bits set",
false => "",
}});
debug.print("{s}\n", .{switch ((code & @intFromEnum(PageFaultCodes.NoExecuteViolation)) > 0) {
true => " | NX Violation",
false => "",
}});
while (true) {}
}
export fn interruptEntry(frame: *InterruptStackFrame) callconv(.C) void { export fn interruptEntry(frame: *InterruptStackFrame) callconv(.C) void {
debug.print("Caught interrupt {d}\n", .{frame.isr}); debug.print("Caught interrupt {d}\n", .{frame.isr});
switch (frame.isr) { switch (frame.isr) {
@intFromEnum(Exceptions.PageFault) => {
pageFault(frame);
},
@intFromEnum(Exceptions.GeneralProtectionFault) => {
generalProtectionFault(frame);
},
SYSCALL_INTERRUPT => { SYSCALL_INTERRUPT => {
var args = sys.Arguments{ .arg0 = frame.rdi, .arg1 = frame.rsi, .arg2 = frame.rdx, .arg3 = frame.r10, .arg4 = frame.r8, .arg5 = frame.r9 }; var args = sys.Arguments{ .arg0 = frame.rdi, .arg1 = frame.rsi, .arg2 = frame.rdx, .arg3 = frame.r10, .arg4 = frame.r8, .arg5 = frame.r9 };
sys.invokeSyscall(frame.rax, frame, &args, @ptrFromInt(@as(usize, @intFromPtr(&frame.rax)))); sys.invokeSyscall(frame.rax, frame, &args, @ptrFromInt(@as(usize, @intFromPtr(&frame.rax))));

View File

@ -15,4 +15,5 @@ pub fn platformInit() void {
pub fn platformEndInit() void { pub fn platformEndInit() void {
pic.remapPIC(); pic.remapPIC();
interrupts.syncInterrupts(); interrupts.syncInterrupts();
interrupts.enableInterrupts();
} }

View File

@ -10,6 +10,8 @@ const pmm = @import("pmm.zig");
const MultibootInfo = [*c]u8; const MultibootInfo = [*c]u8;
export fn _start(magic: u32, info: MultibootInfo) callconv(.C) noreturn { export fn _start(magic: u32, info: MultibootInfo) callconv(.C) noreturn {
interrupts.disableInterrupts();
if (magic != easyboot.MULTIBOOT2_BOOTLOADER_MAGIC) { if (magic != easyboot.MULTIBOOT2_BOOTLOADER_MAGIC) {
debug.print("Invalid magic number: {x}\n", .{magic}); debug.print("Invalid magic number: {x}\n", .{magic});
while (true) {} while (true) {}
@ -19,14 +21,10 @@ export fn _start(magic: u32, info: MultibootInfo) callconv(.C) noreturn {
multiboot.parseMultibootTags(@ptrCast(info)); multiboot.parseMultibootTags(@ptrCast(info));
interrupts.disableInterrupts();
platform.platformInit(); platform.platformInit();
debug.print("GDT initialized\n", .{}); debug.print("GDT initialized\n", .{});
platform.platformEndInit();
interrupts.enableInterrupts();
if (multiboot.findMultibootTag(easyboot.multiboot_tag_mmap_t, @ptrCast(info))) |tag| { if (multiboot.findMultibootTag(easyboot.multiboot_tag_mmap_t, @ptrCast(info))) |tag| {
var allocator = pmm.initializeFrameAllocator(tag) catch |err| { var allocator = pmm.initializeFrameAllocator(tag) catch |err| {
debug.print("Error while initializing frame allocator: {}\n", .{err}); debug.print("Error while initializing frame allocator: {}\n", .{err});
@ -44,6 +42,8 @@ export fn _start(magic: u32, info: MultibootInfo) callconv(.C) noreturn {
debug.print("No memory map multiboot tag found!\n", .{}); debug.print("No memory map multiboot tag found!\n", .{});
} }
platform.platformEndInit();
asm volatile ("int3"); asm volatile ("int3");
while (true) {} while (true) {}

View File

@ -1,4 +1,5 @@
const easyboot = @cImport(@cInclude("easyboot.h")); const easyboot = @cImport(@cInclude("easyboot.h"));
const target = @import("builtin").target;
const MemoryMapIterator = struct { const MemoryMapIterator = struct {
tag: *easyboot.multiboot_tag_mmap_t, tag: *easyboot.multiboot_tag_mmap_t,
@ -15,6 +16,16 @@ const MemoryMapIterator = struct {
if (@intFromPtr(self.entry) >= self.end) self.entry = null; if (@intFromPtr(self.entry) >= self.end) self.entry = null;
if (target.cpu.arch == .x86_64) {
// Workaround for https://gitlab.com/qemu-project/qemu/-/commit/8504f129450b909c88e199ca44facd35d38ba4de
// This invalid 12GiB reserved entry is made up by QEMU (doesn't appear on any real hardware), so we can simply
// ignore it and move on to the next entry.
if (current_entry) |entry| {
if (entry.base_addr == 0x000000fd00000000 and entry.length == (0x000000ffffffffff - 0x000000fd00000000) + 1)
return self.next();
}
}
return current_entry; return current_entry;
} }

View File

@ -68,7 +68,7 @@ pub fn findMultibootTag(comptime Type: type, info: MultibootInfo) ?*Type {
} }
/// Find every multiboot tag of the given type. /// Find every multiboot tag of the given type.
pub fn findMultibootTags(comptime Type: type, info: MultibootInfo, callback: *const fn (tag: *Type) void) void { pub fn findMultibootTags(comptime Type: type, info: MultibootInfo, callback: *const fn (tag: *Type, ctx: *const anyopaque) void, ctx: *const anyopaque) void {
const mb_tag: *easyboot.multiboot_info_t = @alignCast(@ptrCast(info)); const mb_tag: *easyboot.multiboot_info_t = @alignCast(@ptrCast(info));
const mb_size = mb_tag.total_size; const mb_size = mb_tag.total_size;
@ -77,43 +77,43 @@ pub fn findMultibootTags(comptime Type: type, info: MultibootInfo, callback: *co
while ((@intFromPtr(tag) < last) and (tag.type != easyboot.MULTIBOOT_TAG_TYPE_END)) { while ((@intFromPtr(tag) < last) and (tag.type != easyboot.MULTIBOOT_TAG_TYPE_END)) {
switch (tag.type) { switch (tag.type) {
easyboot.MULTIBOOT_TAG_TYPE_CMDLINE => { easyboot.MULTIBOOT_TAG_TYPE_CMDLINE => {
if (Type == easyboot.multiboot_tag_cmdline_t) callback(@alignCast(@ptrCast(tag))); if (Type == easyboot.multiboot_tag_cmdline_t) callback(@alignCast(@ptrCast(tag)), ctx);
}, },
easyboot.MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME => { easyboot.MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME => {
if (Type == easyboot.multiboot_tag_loader_t) callback(@alignCast(@ptrCast(tag))); if (Type == easyboot.multiboot_tag_loader_t) callback(@alignCast(@ptrCast(tag)), ctx);
}, },
easyboot.MULTIBOOT_TAG_TYPE_MODULE => { easyboot.MULTIBOOT_TAG_TYPE_MODULE => {
if (Type == easyboot.multiboot_tag_module_t) callback(@alignCast(@ptrCast(tag))); if (Type == easyboot.multiboot_tag_module_t) callback(@alignCast(@ptrCast(tag)), ctx);
}, },
easyboot.MULTIBOOT_TAG_TYPE_MMAP => { easyboot.MULTIBOOT_TAG_TYPE_MMAP => {
if (Type == easyboot.multiboot_tag_mmap_t) callback(@alignCast(@ptrCast(tag))); if (Type == easyboot.multiboot_tag_mmap_t) callback(@alignCast(@ptrCast(tag)), ctx);
}, },
easyboot.MULTIBOOT_TAG_TYPE_FRAMEBUFFER => { easyboot.MULTIBOOT_TAG_TYPE_FRAMEBUFFER => {
if (Type == easyboot.multiboot_tag_framebuffer_t) callback(@alignCast(@ptrCast(tag))); if (Type == easyboot.multiboot_tag_framebuffer_t) callback(@alignCast(@ptrCast(tag)), ctx);
}, },
easyboot.MULTIBOOT_TAG_TYPE_EFI64 => { easyboot.MULTIBOOT_TAG_TYPE_EFI64 => {
if (Type == easyboot.multiboot_tag_efi64_t) callback(@alignCast(@ptrCast(tag))); if (Type == easyboot.multiboot_tag_efi64_t) callback(@alignCast(@ptrCast(tag)), ctx);
}, },
easyboot.MULTIBOOT_TAG_TYPE_EFI64_IH => { easyboot.MULTIBOOT_TAG_TYPE_EFI64_IH => {
if (Type == easyboot.multiboot_tag_efi64_ih_t) callback(@alignCast(@ptrCast(tag))); if (Type == easyboot.multiboot_tag_efi64_ih_t) callback(@alignCast(@ptrCast(tag)), ctx);
}, },
easyboot.MULTIBOOT_TAG_TYPE_SMBIOS => { easyboot.MULTIBOOT_TAG_TYPE_SMBIOS => {
if (Type == easyboot.multiboot_tag_smbios_t) callback(@alignCast(@ptrCast(tag))); if (Type == easyboot.multiboot_tag_smbios_t) callback(@alignCast(@ptrCast(tag)), ctx);
}, },
easyboot.MULTIBOOT_TAG_TYPE_ACPI_OLD => { easyboot.MULTIBOOT_TAG_TYPE_ACPI_OLD => {
if (Type == easyboot.multiboot_tag_old_acpi_t) callback(@alignCast(@ptrCast(tag))); if (Type == easyboot.multiboot_tag_old_acpi_t) callback(@alignCast(@ptrCast(tag)), ctx);
}, },
easyboot.MULTIBOOT_TAG_TYPE_ACPI_NEW => { easyboot.MULTIBOOT_TAG_TYPE_ACPI_NEW => {
if (Type == easyboot.multiboot_tag_new_acpi_t) callback(@alignCast(@ptrCast(tag))); if (Type == easyboot.multiboot_tag_new_acpi_t) callback(@alignCast(@ptrCast(tag)), ctx);
}, },
easyboot.MULTIBOOT_TAG_TYPE_SMP => { easyboot.MULTIBOOT_TAG_TYPE_SMP => {
if (Type == easyboot.multiboot_tag_smp_t) callback(@alignCast(@ptrCast(tag))); if (Type == easyboot.multiboot_tag_smp_t) callback(@alignCast(@ptrCast(tag)), ctx);
}, },
easyboot.MULTIBOOT_TAG_TYPE_PARTUUID => { easyboot.MULTIBOOT_TAG_TYPE_PARTUUID => {
if (Type == easyboot.multiboot_tag_partuuid_t) callback(@alignCast(@ptrCast(tag))); if (Type == easyboot.multiboot_tag_partuuid_t) callback(@alignCast(@ptrCast(tag)), ctx);
}, },
easyboot.MULTIBOOT_TAG_TYPE_EDID => { easyboot.MULTIBOOT_TAG_TYPE_EDID => {
if (Type == easyboot.multiboot_tag_edid_t) callback(@alignCast(@ptrCast(tag))); if (Type == easyboot.multiboot_tag_edid_t) callback(@alignCast(@ptrCast(tag)), ctx);
}, },
else => {}, else => {},
} }

View File

@ -99,5 +99,8 @@ pub fn initializeFrameAllocator(tag: *easyboot.multiboot_tag_mmap_t) !FrameAlloc
const frames_to_lock = try std.math.divCeil(usize, bitmap_size, platform.PAGE_SIZE); const frames_to_lock = try std.math.divCeil(usize, bitmap_size, platform.PAGE_SIZE);
try lockFrames(&allocator, @intFromPtr(bitmap_base_address), frames_to_lock); try lockFrames(&allocator, @intFromPtr(bitmap_base_address), frames_to_lock);
// Avoid causing trouble.
try lockFrame(&allocator, 0);
return allocator; return allocator;
} }