After some time spent with coding on my operating system, I am almost at the stage where keyboard input can be handled by the kernel!
It isn’t easy to get there. First you need an I/O APIC (=Advanced Programmable Interrupt Controller) that redirects the keyboard events to CPU interrupts. To find the I/O APIC address we need the MADT (=Multiple APIC Descriptor Table) from the ACPI (=Advanced Configuration and Power Interface). The MADT can be retrieved from the XSDT (=Extended System Descriptor Table, 64bit version) or the RSDT (=Root System Descriptor Table, 32bit version). But I should better stop talking about that stuff and get to the point
I already have the XSDT structure, which is documented on osdev. I just implemented a findEntry
function that finds the MADT entry in the XSDT, but that function crashed the kernel in a really weird way. No panic, just the firmware (UEFI) reappears, the bootloader loads and then I see the whole kernel log again.
You can find the function that crashes the kernel on codeberg. Using some debug logging messages, I found out that the part that crashes the kernel is at line 58 (the if statement).
Now: How can std.mem.eql / my if statement crash the kernel in a way that everything returns to UEFI / the bootloader?
Might be unrelated, but you shouldn’t need to dereference item_ptr
. Either:
item_ptr.signature[0..]
or
&item_ptr.signature
should work (pointer to an array coerces to a slice).
Null pointer dereference? Bad memory? Incorrect type that causes you to read past the end of the page? Endianness issue? Bad length?
Are you able to step through the individual steps? Does it error before std.mem.eql gets called? Does it error in debug mode? Does it error if you manually disable inlining for that function?
Indeed it seems to be bad memory. I just logged every entry (number), and here is the relevant log part:
[DEBG] [src/kernel/acpi/xsdt.zig:59:29 in findEntry()] Entry is 0x7450434146
[DEBG] [src/kernel/acpi/xsdt.zig:59:29 in findEntry()] Entry is 0x205348434f42bb01
Normally in QEMU, ACPI is somewhere around 0x7770000
…
Just, how can I solve this? I debug logged in that function the @intFromPtr(self)
. The result, as expected, was 0x777d0e8
.
EDIT: I just found the problem.
entries: [*]u64 align(1),
is declared as a pointer. But at that address are the data that the pointer points to, and not the pointer.
You need to declare the pointer as local and assign to it the correct address.
pub const XSDT = extern struct {
header: DescriptionHeader align(1),
/// Find XSDT Entry
pub fn findEntry(self: *XSDT, entry_signature: []const u8) FindError!*DescriptionHeader {
const len: u32 = (self.header.length - @sizeOf(DescriptionHeader)) / 8;
const entries: [*]u64 = @ptrFromInt(@intFromPtr(self.header) + @as(usize,self.header.length));
for (entries[0..len]) |entry| {
const item_ptr: *DescriptionHeader = @ptrFromInt(entry);
if (std.mem.eql(u8, item_ptr.*.signature[0..], entry_signature)) {
return item_ptr;
}
}
return error.NoMatchingEntry;
}
};
We’re almost there. Just getting incorrect alignments .
It errors me because the address of entries is not 8-byte-aligned (which seems to be necessary for [*]64
). I tried to solve it with [*]u64 align(1)
which didn’t work. Any idea?
EDIT: Fixed it using [*]align(1) u64
…