diff --git a/kernel/acpi.zig b/kernel/acpi.zig index a79ab80..aaa1526 100644 --- a/kernel/acpi.zig +++ b/kernel/acpi.zig @@ -3,9 +3,9 @@ const root = @import("root"); const vmm = @import("vmm.zig"); const apic = @import("apic.zig"); const log = std.log.scoped(.acpi); - const readIntNative = std.mem.readIntNative; +/// System Description Table const SDT = extern struct { signature: [4]u8, length: u32, @@ -23,6 +23,7 @@ const SDT = extern struct { } }; +/// Root System Description Pointer const RSDP = extern struct { signature: [8]u8, checksum: u8, @@ -45,66 +46,58 @@ pub fn init() void { log.info("revision: {}", .{rsdp.revision}); log.info("uses XSDT: {}", .{rsdp.useXSDT()}); - switch (rsdp.revision) { - 0 => parse(u32, @intCast(rsdp.rsdt_addr)), - 2 => parse(u64, rsdp.xsdt_addr), - else => unreachable, + if (rsdp.useXSDT()) { + parse(u64, rsdp.xsdt_addr); + } else { + parse(u32, @intCast(rsdp.rsdt_addr)); } } fn parse(comptime T: type, addr: u64) void { - const sdt: *SDT = @ptrFromInt(addr + vmm.higher_half); - const entries = std.mem.bytesAsSlice(T, sdt.data()); + const rsdt: *SDT = @ptrFromInt(addr + vmm.higher_half); - log.info("RSDT is at 0x{x}", .{@intFromPtr(sdt)}); + var sum: u8 = 0; + for (0..rsdt.length) |i| { + sum +%= @as([*]u8, @ptrCast(rsdt))[i]; + } + if (sum != 0) { + std.debug.panic("RSDT is invalid: sum = {}", .{sum}); + } + log.info("RSDT is at 0x{x}", .{@intFromPtr(rsdt)}); + + const entries = std.mem.bytesAsSlice(T, rsdt.data()); for (entries) |entry| { - handleTable(@ptrFromInt(entry + vmm.higher_half)); + const sdt: *const SDT = @ptrFromInt(entry + vmm.higher_half); + + switch (readIntNative(u32, &sdt.signature)) { + readIntNative(u32, "APIC") => handleMADT(sdt), + else => log.warn("unhandled ACPI table: {s}", .{sdt.signature}), + } } } -fn handleTable(sdt: *const SDT) void { - switch (readIntNative(u32, sdt.signature[0..4])) { - readIntNative(u32, "APIC") => { - var data = sdt.data()[8..]; - - while (data.len >= 2) { - const kind = data[0]; - const size = data[1]; - - if (size >= data.len) break; - - const record_data = data[2..size]; - switch (kind) { - 0 => {}, // TODO: find about this - 1 => apic.handleIOAPIC( - record_data[0], - readIntNative(u32, record_data[2..6]), - readIntNative(u32, record_data[6..10]), - ), - 2 => apic.handleIOAPICISO( - record_data[0], - record_data[1], - readIntNative(u32, record_data[2..6]), - readIntNative(u16, record_data[6..8]), - ), - 3 => log.debug("unhandled IO/APIC NMI source: {any}", .{record_data}), - 4 => log.debug("unhandled LAPIC NMI: {any}", .{record_data}), - 5 => log.debug("unhandled LAPIC Address Override: {any}", .{record_data}), - 9 => log.debug("unhandled x2LAPIC: {any}", .{record_data}), - else => log.warn("unknown MADT record 0x{x}: {any}", .{ kind, record_data }), - } - - data = data[@max(2, size)..]; - } - }, - // TODO: - // readIntNative(u32, "FACP") => { - // const fadt_flags = @as([*]const u32, @ptrCast(sdt))[28]; - // if (fadt_flags & (1 << 20) != 0) { - // @panic("Ubik does not support HW reduced ACPI systems"); - // } - // }, - else => log.debug("unhandled ACPI table: {s}", .{sdt.signature}), +fn handleMADT(madt: *const SDT) void { + var data = madt.data()[8..]; // discard madt header + + while (data.len > 2) { + const kind = data[0]; + const size = data[1]; + + if (size >= data.len) break; + + const entry = data[2..size]; + switch (kind) { + 0 => log.warn("unhandled LAPIC: {any}", .{entry}), + 1 => apic.io_apics.append(@ptrCast(entry)) catch unreachable, + 2 => apic.isos.append(@ptrCast(entry)) catch unreachable, + 3 => log.warn("unhandled IO/APIC NMI source: {any}", .{entry}), + 4 => log.warn("unhandled LAPIC NMI: {any}", .{entry}), + 5 => log.warn("unhandled LAPIC Address Override: {any}", .{entry}), + 9 => log.warn("unhandled x2LAPIC: {any}", .{entry}), + else => unreachable, + } + + data = data[@max(2, size)..]; } } diff --git a/kernel/apic.zig b/kernel/apic.zig index c6464bb..c8fa433 100644 --- a/kernel/apic.zig +++ b/kernel/apic.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const root = @import("root"); const vmm = @import("vmm.zig"); const cpu = @import("cpu.zig"); const idt = @import("idt.zig"); @@ -18,9 +19,12 @@ const Register = enum(u64) { timer_divide = 0x3e0, }; -const IOAPIC = struct { - addr: u32, - base_gsi: u32, +/// Input/Output Advanced Programmable Interrupt Controller +const IOAPIC = extern struct { + apic_id: u8 align(1), + reserved: u8 align(1), + addr: u32 align(1), + base_gsi: u32 align(1), const Self = @This(); @@ -39,37 +43,18 @@ const IOAPIC = struct { fn gsiCount(self: Self) u32 { return (self.read(1) & 0xff0000) >> 16; } - - fn setGSIRedirect(self: Self, gsi: u32, lapic_id: u32, vector: u8, flags: u16) void { - var redirect: u64 = vector; - if ((flags & (1 << 1)) != 0) { - redirect |= (1 << 13); - } - - if ((flags & (1 << 3)) != 0) { - redirect |= (1 << 15); - } - - // redirect |= @as(u64, @intCast(flags & 0b1010)) << 12; - redirect |= @as(u64, @intCast(lapic_id)) << 56; - - const io_redirect_table = 0x10 + (gsi - self.base_gsi) * 2; - self.write(io_redirect_table, @truncate(redirect)); - self.write(io_redirect_table + 1, @truncate(redirect >> 32)); - } }; -// TODO -const SourceOverride = struct { - ioapic_id: u8, - gsi: u32, - flags: u16, +/// Interrupt Source Override +const ISO = extern struct { + bus_source: u8 align(1), + irq_source: u8 align(1), + gsi: u32 align(1), + flags: u16 align(1), }; -// var time_vector: u8 = undefined; -// var one_shot_vector: u8 = undefined; -var io_apics = [1]?IOAPIC{null} ** 16; -var source_overrides = [1]?SourceOverride{null} ** 256; +pub var io_apics = std.ArrayList(*const IOAPIC).init(root.allocator); +pub var isos = std.ArrayList(*const ISO).init(root.allocator); pub fn init() void { // timerCalibrate(); @@ -96,10 +81,6 @@ pub fn eoi() void { writeRegister(.eoi, 0); } -pub fn localApicId() u32 { - return readRegister(.lapic_id); -} - // TODO // pub fn timerOneShot(us: u64, vector: u8) void { // _ = us; @@ -115,15 +96,15 @@ pub fn localApicId() u32 { // // interrupt_toggle(old_int_state); // } -// pub fn timerStop() void { -// writeRegister(.timer_initial_count, 0); -// writeRegister(.lvt_timer, 1 << 16); -// } +pub fn timerStop() void { + writeRegister(.timer_initial_count, 0); + writeRegister(.lvt_timer, 1 << 16); +} -// pub fn sendIPI(lapic_id: u32, vec: u32) void { -// writeRegister(.icr1, lapic_id << 24); -// writeRegister(.icr0, vec); -// } +pub fn sendIPI(lapic_id: u32, vec: u32) void { + writeRegister(.icr1, lapic_id << 24); + writeRegister(.icr0, vec); +} // pub fn timerCalibrate() void { // timerStop(); @@ -132,7 +113,7 @@ pub fn localApicId() u32 { // writeRegister(.lvt_timer, (1 << 16) | 0xff); // vector 0xff, masked // writeRegister(.timer_divide, 0); -// // pit_set_reload_value(0xffff); // Reset PIT +// // pit.setReloadValue(0xffff); // reset PIT // const samples = 0xfffff; // // const initial_tick = pit_get_current_count(); @@ -140,7 +121,7 @@ pub fn localApicId() u32 { // writeRegister(.timer_initial_count, samples); // while (readRegister(.timer_current_count) != 0) {} -// // const final_tick = pit_get_current_count(); +// // const final_tick = pit.getCurrentCount(); // // const total_ticks = initial_tick - final_tick; // // this_cpu().lapic_freq = (samples / total_ticks) * PIT_DIVIDEND; @@ -159,49 +140,38 @@ fn writeRegister(register: Register, val: u32) void { ptr.* = val; } -pub fn handleIOAPIC(id: u8, addr: u32, base_gsi: u32) void { - std.debug.assert(io_apics[id] == null); - - io_apics[id] = .{ - .addr = addr, - .base_gsi = base_gsi, - }; -} - -pub fn handleIOAPICISO(ioapic_id: u8, irq_source: u8, gsi: u32, flags: u16) void { - std.debug.assert(source_overrides[irq_source] == null); - - source_overrides[irq_source] = .{ - .ioapic_id = ioapic_id, - .gsi = gsi, - .flags = flags, +fn setGSIRedirect(lapic_id: u32, vector: u8, gsi: u8, flags: u16) void { + const io_apic = for (io_apics.items) |io_apic| { + if (gsi >= io_apic.base_gsi and gsi < io_apic.base_gsi + io_apic.gsiCount()) { + break io_apic; + } + } else { + std.debug.panic("could not find an IOAPIC for GSI {}", .{gsi}); }; -} -fn mapIRQtoGSI(irq: u8) SourceOverride { - return source_overrides[irq] orelse .{ - .ioapic_id = mapGSItoIOAPIC(irq), - .gsi = irq, - .flags = 0, - }; -} + var redirect: u64 = vector; + if ((flags & (1 << 1)) != 0) { + redirect |= (1 << 13); + } -fn mapGSItoIOAPIC(gsi: u32) u8 { - for (io_apics, 0..) |io_apic, i| { - if (io_apic) |ioa| { - if (gsi >= ioa.base_gsi and gsi < ioa.base_gsi + ioa.gsiCount()) { - return @intCast(i); - } - } + if ((flags & (1 << 3)) != 0) { + redirect |= (1 << 15); } - std.debug.panic("could not find an IOAPIC for GSI {}", .{gsi}); + redirect |= @as(u64, @intCast(lapic_id)) << 56; + + const io_redirect_table = 0x10 + (gsi - io_apic.base_gsi) * 2; + io_apic.write(io_redirect_table, @truncate(redirect)); + io_apic.write(io_redirect_table + 1, @truncate(redirect >> 32)); } pub fn setIRQRedirect(lapic_id: u32, vector: u8, irq: u8) void { - const gsi = mapIRQtoGSI(irq); - const ioapic_id = mapGSItoIOAPIC(gsi.gsi); - const ioapic = io_apics[ioapic_id].?; + for (isos.items) |iso| { + if (iso.irq_source == irq) { + setGSIRedirect(lapic_id, vector, @intCast(iso.gsi), iso.flags); + return; + } + } - ioapic.setGSIRedirect(gsi.gsi, lapic_id, vector, gsi.flags); + setGSIRedirect(lapic_id, vector, irq, 0); } diff --git a/kernel/cpu.zig b/kernel/cpu.zig index c64f3a5..981a083 100644 --- a/kernel/cpu.zig +++ b/kernel/cpu.zig @@ -1,5 +1,7 @@ const std = @import("std"); +const root = @import("root"); const SpinLock = @import("lock.zig").SpinLock; +const log = std.log.scoped(.cpu); // TODO: defined elsewhere pub const Thread = struct {}; @@ -31,6 +33,7 @@ pub const Context = extern struct { ss: u64, }; +/// Task State Segment pub const TSS = extern struct { reserved0: u32 align(1) = 0, rsp: [3]u64 align(1), @@ -56,7 +59,7 @@ pub const CpuLocal = struct { }; pub var sysenter: bool = false; -pub var bsp_lapic_id: u32 = undefined; +pub var bsp_lapic_id: u32 = undefined; // TODO: x86 specific pub var smp_started: bool = undefined; pub var cpus: []CpuLocal = undefined; @@ -67,6 +70,11 @@ pub var fpu_storage_size: usize = 0; pub var cpu_count: usize = undefined; -pub fn init() void {} +pub fn init() void { + const smp = root.smp_request.response.?; + bsp_lapic_id = smp.bsp_lapic_id; + cpu_count = smp.cpu_count; + log.info("{} processors detected", .{cpu_count}); +} // TODO: a lot of functions with inline assembly diff --git a/kernel/gdt.zig b/kernel/gdt.zig index f8b5cae..f7c70d9 100644 --- a/kernel/gdt.zig +++ b/kernel/gdt.zig @@ -24,6 +24,7 @@ const TSSDescriptor = packed struct { reserved: u32 = 0, }; +/// Global Descriptor Table const GDT = extern struct { null_entry: GDTEntry align(8), kernel_code: GDTEntry align(8), diff --git a/kernel/idt.zig b/kernel/idt.zig index fb23944..3d6d0bb 100644 --- a/kernel/idt.zig +++ b/kernel/idt.zig @@ -11,14 +11,15 @@ const trap_gate = 0b1000_1111; const InterruptStub = *const fn () callconv(.Naked) void; pub const InterruptHandler = *const fn (ctx: *cpu.Context) void; -const IDTEntry = packed struct { - offset_low: u16, - selector: u16, - ist: u8, - type_attributes: u8, - offset_mid: u16, - offset_high: u32, - reserved: u32, +/// Interrupt Descriptor Table Entry +const IDTEntry = extern struct { + offset_low: u16 align(1), + selector: u16 align(1), + ist: u8 align(1), + type_attributes: u8 align(1), + offset_mid: u16 align(1), + offset_high: u32 align(1), + reserved: u32 align(1), fn init(handler: u64, ist: u8, attributes: u8) IDTEntry { return .{ diff --git a/kernel/main.zig b/kernel/main.zig index cc68022..0e3c355 100644 --- a/kernel/main.zig +++ b/kernel/main.zig @@ -9,6 +9,7 @@ const gdt = @import("gdt.zig"); const idt = @import("idt.zig"); const pmm = @import("pmm.zig"); const vmm = @import("vmm.zig"); +const cpu = @import("cpu.zig"); const acpi = @import("acpi.zig"); const apic = @import("apic.zig"); const ps2 = @import("ps2.zig"); @@ -27,18 +28,19 @@ pub const os = struct { var gpa = std.heap.GeneralPurposeAllocator(.{ .thread_safe = false, - // .MutexType = SpinLock, // TODO + .MutexType = SpinLock, .verbose_log = if (builtin.mode == .Debug) true else false, }){}; pub const allocator = gpa.allocator(); -export var boot_info_request: limine.BootloaderInfoRequest = .{}; +pub export var boot_info_request: limine.BootloaderInfoRequest = .{}; pub export var hhdm_request: limine.HhdmRequest = .{}; pub export var framebuffer_request: limine.FramebufferRequest = .{}; pub export var memory_map_request: limine.MemoryMapRequest = .{}; pub export var kernel_file_request: limine.KernelFileRequest = .{}; pub export var kernel_address_request: limine.KernelAddressRequest = .{}; pub export var rsdp_request: limine.RsdpRequest = .{}; +pub export var smp_request: limine.SmpRequest = .{}; // pub export var module_request: limine.ModuleRequest = .{}; // pub export var boot_time_request: limine.BootTimeRequest = .{}; @@ -94,7 +96,7 @@ fn main() !void { // TODO: proc // TODO: sched - // TODO: cpu + cpu.init(); // TODO // TODO: threads <-- with priority level ? <- have a list of thread based // on priority level and state (accoriding to https://wiki.osdev.org/Going_further_on_x86 diff --git a/kernel/pit.zig b/kernel/pit.zig new file mode 100644 index 0000000..898b0b8 --- /dev/null +++ b/kernel/pit.zig @@ -0,0 +1,46 @@ +const std = @import("std"); +const arch = @import("arch.zig"); +const cpu = @import("cpu.zig"); +const idt = @import("idt.zig"); +const apic = @import("apic.zig"); + +pub const dividend = 1193182; + +pub fn init() void { + // TODO setFrequency(time.timer_freq) + const timer_vector = idt.allocateVector(); + idt.registerHandler(timer_vector, timerHandler); + apic.setIRQRedirect(cpu.bsp_lapic_id, timer_vector, 0); // TODO: status is true +} + +pub fn getCurrentCount() u16 { + arch.out(u8, 0x43, 0x00); + // return arch.in(u16, 0x40); + const lo = arch.in(u8, 0x40); + const hi = arch.in(u8, 0x40); + return (@as(u16, @intCast(hi)) << 8) | lo; +} + +pub fn setReloadValue(new_count: u16) void { + // TODO + // channel 0, lo/hi access mode, mode 2 (rate generator) + arch.out(u8, 0x43, 0x34); + // arch.out(u16, 0x40, new_count); + arch.out(u8, 0x40, @truncate(new_count)); + arch.out(u8, 0x40, @truncate(new_count >> 8)); +} + +pub fn setFrequency(frequency: u64) void { + var new_divisor = dividend / frequency; + if (dividend % frequency > frequency / 2) { + new_divisor += 1; + } + setReloadValue(@intCast(new_divisor)); +} + +fn timerHandler(ctx: *cpu.Context) void { + _ = ctx; + + // TODO time.timerHandler(); + apic.eoi(); +} diff --git a/kernel/pmm.zig b/kernel/pmm.zig index 63c2f0f..a5d2c9e 100644 --- a/kernel/pmm.zig +++ b/kernel/pmm.zig @@ -6,9 +6,8 @@ const log = std.log.scoped(.pmm); const page_size = std.mem.page_size; const free_page = false; -// TODO: grap reusable entries // TODO: use u64 and bitwise operation to speed up the process? -// TODO: useless stuff +// TODO: decide what to do with "useless" stuff var bitmap: []bool = undefined; var last_idx: u64 = 0; var usable_pages: u64 = 0; // useless? diff --git a/kernel/ps2.zig b/kernel/ps2.zig index 1efe240..12c35fb 100644 --- a/kernel/ps2.zig +++ b/kernel/ps2.zig @@ -7,7 +7,6 @@ const tty = @import("tty.zig"); // TODO: termios <- in tty/Terminal ? // TODO: user_write_lock -> user write on buffer + framebuffer, log user write ??? -// TODO: handle ESC, and other stuff const ScanCode = enum(u8) { ctrl = 0x1d, @@ -100,7 +99,7 @@ pub fn init() void { const keyboard_vector = idt.allocateVector(); idt.registerHandler(keyboard_vector, keyboardHandler); - apic.setIRQRedirect(apic.localApicId(), keyboard_vector, 1); + apic.setIRQRedirect(cpu.bsp_lapic_id, keyboard_vector, 1); _ = arch.in(u8, 0x60); } diff --git a/kernel/tty.zig b/kernel/tty.zig index 083a3fe..606daec 100644 --- a/kernel/tty.zig +++ b/kernel/tty.zig @@ -92,7 +92,7 @@ fn callback(ctx: *Terminal, cb: Terminal.Callback, arg1: u64, arg2: u64, arg3: u _ = ctx; // TODO: https://github.com/limine-bootloader/limine/blob/v5.x-branch/PROTOCOL.md#terminal-callback switch (cb) { - else => std.log.warn("unhandled callback `{}` with args: {}, {}, {}", .{ cb, arg1, arg2, arg3 }), + else => std.log.debug("unhandled callback `{}` with args: {}, {}, {}", .{ cb, arg1, arg2, arg3 }), } } diff --git a/kernel/fs.zig b/kernel/vfs.zig similarity index 100% rename from kernel/fs.zig rename to kernel/vfs.zig