Skip to content

Commit

Permalink
rework apic + WIP vfs
Browse files Browse the repository at this point in the history
Add a basic docs step to build system.
Cleanup and improve apic.zig.
Change idle thread to null.
Move pit to arch.
Start HPET.
  • Loading branch information
Ratakor committed Nov 12, 2023
1 parent d597931 commit f5c0af0
Show file tree
Hide file tree
Showing 19 changed files with 338 additions and 256 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
shell: bash
run: |
set +e
timeout 60 zig build run -Dnodisplay=true
timeout 30 zig build run -Dnodisplay=true
[ $? -ne 124 ] && exit 1 || exit 0
lint:
Expand Down
10 changes: 9 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ fn findModules(b: *std.Build) []const u8 {

while (iter.next()) |line| {
if (std.mem.startsWith(u8, line, "MODULE_PATH=boot://") and
!std.mem.endsWith(u8, line, ".tar")) {
!std.mem.endsWith(u8, line, ".tar"))
{
const i = std.mem.lastIndexOfScalar(u8, line, '/') orelse unreachable;
modules.append(line[i + 1 ..]) catch unreachable;
}
Expand Down Expand Up @@ -123,6 +124,13 @@ pub fn build(b: *std.Build) void {
run_cmd.step.dependOn(image_step);
run_step.dependOn(&run_cmd.step);

const docs_step = b.step("docs", "Generate documentations");
docs_step.dependOn(&b.addInstallDirectory(.{
.source_dir = kernel.getEmittedDocs(),
.install_dir = .prefix,
.install_subdir = "docs",
}).step);

const fmt_step = b.step("fmt", "Format all source files");
fmt_step.dependOn(&b.addFmt(.{ .paths = &[_][]const u8{ "kernel", "lib" } }).step);

Expand Down
53 changes: 37 additions & 16 deletions kernel/acpi.zig
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
//! https://uefi.org/specs/ACPI/6.5/
//! https://uefi.org/acpi

const std = @import("std");
const root = @import("root");
const arch = @import("arch.zig");
const apic = arch.apic;
const vmm = root.vmm;
const log = std.log.scoped(.acpi);
const readInt = std.mem.readInt;

/// System Description Table
const SDT = extern struct {
Expand Down Expand Up @@ -56,9 +58,9 @@ const RSDP = extern struct {

/// Generic Address Structure
const GAS = extern struct {
address_space: u8 align(1),
bit_width: u8 align(1),
bit_offset: u8 align(1),
address_space_id: u8 align(1),
register_bit_width: u8 align(1),
register_bit_offset: u8 align(1),
access_size: u8 align(1),
address: u64 align(1),
};
Expand Down Expand Up @@ -122,6 +124,25 @@ const FADT = extern struct {
x_gpe1_block: GAS align(1),
};

/// High Precision Event Timer
const HPET = packed struct(u160) {
hardware_rev_id: u8,
comparator_count: u5,
counter_size: u1,
reserved0: u1,
legacy_replacement: u1,
pci_vendor_id: u16,
// address: GAS, // `only packed structs layout are allowed in packed types`
address_space_id: u8,
register_bit_width: u8,
register_bit_offset: u8,
reserved1: u8,
address: u64,
hpet_number: u8,
minimum_tick: u16,
page_protection: u8,
};

var use_xsdt: bool = undefined;
var fadt: *const FADT = undefined;

Expand All @@ -135,35 +156,35 @@ pub fn init() void {
if (use_xsdt) {
parse(u64, rsdp.xsdt_addr);
} else {
parse(u32, @intCast(rsdp.rsdt_addr));
parse(u32, rsdp.rsdt_addr);
}
}

fn parse(comptime T: type, addr: u64) void {
fn parse(comptime T: type, addr: T) void {
const rsdt: *const SDT = @ptrFromInt(addr + vmm.hhdm_offset);
rsdt.doChecksum();
log.info("RSDT is at 0x{x}", .{@intFromPtr(rsdt)});
rsdt.doChecksum();

const entries = std.mem.bytesAsSlice(T, rsdt.data());
for (entries) |entry| {
const sdt: *const SDT = @ptrFromInt(entry + vmm.hhdm_offset);
sdt.doChecksum();

switch (std.mem.readInt(u32, &sdt.signature, arch.endian)) {
std.mem.readInt(u32, "APIC", arch.endian) => handleMADT(sdt),
std.mem.readInt(u32, "FACP", arch.endian) => handleFADT(sdt),
std.mem.readInt(u32, "HPET", arch.endian) => {}, // ignored
std.mem.readInt(u32, "WAET", arch.endian) => {}, // ignored
switch (readInt(u32, &sdt.signature, arch.endian)) {
readInt(u32, "APIC", arch.endian) => handleMADT(sdt),
readInt(u32, "FACP", arch.endian) => handleFADT(sdt),
readInt(u32, "HPET", arch.endian) => {}, // TODO
readInt(u32, "WAET", arch.endian) => {}, // ignored
else => log.warn("unhandled ACPI table: {s}", .{sdt.signature}),
}
}
}

/// https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#multiple-apic-description-table-madt
fn handleMADT(madt: *const SDT) void {
apic.lapic_base = std.mem.readInt(u32, madt.data()[0..4], arch.endian);
apic.lapic_base = readInt(u32, madt.data()[0..4], arch.endian) + vmm.hhdm_offset;
log.info("lapic base: 0x{x}", .{apic.lapic_base});
var data = madt.data()[8..]; // discard madt header
var data = madt.data()[8..]; // discard the rest of madt header

while (data.len > 2) {
const kind = data[0];
Expand Down Expand Up @@ -268,15 +289,15 @@ inline fn parseInt(s5_addr: []const u8, value: *u64) usize {
return 2;
},
0xb => {
value.* = std.mem.readInt(u16, s5_addr[1..3], arch.endian);
value.* = readInt(u16, s5_addr[1..3], arch.endian);
return 3;
},
0xc => {
value.* = std.mem.readInt(u32, s5_addr[1..5], arch.endian);
value.* = readInt(u32, s5_addr[1..5], arch.endian);
return 5;
},
0xe => {
value.* = std.mem.readInt(u64, s5_addr[1..9], arch.endian);
value.* = readInt(u64, s5_addr[1..9], arch.endian);
return 9;
},
else => unreachable,
Expand Down
1 change: 1 addition & 0 deletions kernel/arch/x86_64.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub const gdt = @import("x86_64/gdt.zig");
pub const idt = @import("x86_64/idt.zig");
pub const apic = @import("x86_64/apic.zig");
pub const cpu = @import("x86_64/cpu.zig");
pub const pit = @import("x86_64/pit.zig");
pub const Context = idt.Context;
const mem = @import("x86_64/mem.zig");

Expand Down
162 changes: 100 additions & 62 deletions kernel/arch/x86_64/apic.zig
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
const std = @import("std");
const root = @import("root");
const x86 = @import("x86_64.zig");
const pit = @import("pit.zig");
const vmm = root.vmm;
const smp = root.smp;
const pit = root.time;
const SpinLock = root.SpinLock;

const Register = enum(u64) {
lapic_id = 0x020,
eoi = 0x0b0, // end of interrupt
spurious = 0x0f0,
cmci = 0x2f0, // LVT corrected machine check interrupt
icr0 = 0x300, // interrupt command register
icr1 = 0x310,
lvt_timer = 0x320,
timer_initial_count = 0x380,
timer_current_count = 0x390,
timer_divide = 0x3e0,
/// Local Advanced Programmable Interrupt Controller
const LAPIC = struct {
// intel manual volume 3A page 11-6 (396)
const Register = enum(u64) {
lapic_id = 0x020,
eoi = 0x0b0, // end of interrupt
spurious = 0x0f0,
cmci = 0x2f0, // LVT corrected machine check interrupt
icr_low = 0x300,
icr_high = 0x310,
lvt_timer = 0x320,
lvt_thermal = 0x330, // LVT thermal sensor
lvt_perf = 0x340, // LVT performance monitoring counters
timer_initial_count = 0x380,
timer_current_count = 0x390,
timer_divide = 0x3e0,
};

fn read(register: Register) u32 {
const reg = @intFromEnum(register);
return @as(*volatile u32, @ptrFromInt(lapic_base + reg)).*;
}

fn write(register: Register, val: u32) void {
const reg = @intFromEnum(register);
const addr: *volatile u32 = @ptrFromInt(lapic_base + reg);
addr.* = val;
}
};

/// Input/Output Advanced Programmable Interrupt Controller
Expand All @@ -26,6 +43,27 @@ const IOAPIC = extern struct {
addr: u32 align(1),
base_gsi: u32 align(1),

const Redirect = packed struct(u64) {
vector: u8,
delivery_mode: u3 = 0,
destination_mode: u1 = 0,
delivery_status: u1 = 0,
pin_polarity: u1,
remote_irr: u1 = 0,
trigger_mode: u1,
mask: u1,
reserved: u39 = 0,
destination: u8,

inline fn low(self: Redirect) u32 {
return @truncate(@as(u64, @bitCast(self)));
}

inline fn high(self: Redirect) u32 {
return @truncate(@as(u64, @bitCast(self)) >> 32);
}
};

fn read(self: IOAPIC, reg: u32) u32 {
const base: [*]volatile u32 = @ptrFromInt(self.addr + vmm.hhdm_offset);
base[0] = reg;
Expand All @@ -51,8 +89,29 @@ const ISO = extern struct {
flags: u16 align(1),
};

pub var lapic_base: u32 = undefined; // set in acpi.zig with handleMADT()
// look intel manual volume 3A page 11-19 (409) for details about each field
/// Interrupt Command Register (bits 0-31)
const ICRLow = packed struct(u32) {
vector: u8,
delivery_mode: u3 = 0b000,
destination_mode: u1 = 0b0,
delivery_status: u1 = 0, // read-only
reserved0: u1 = 0,
level: u1 = 0b1,
trigger_mode: u1 = 0b0,
reserved1: u2 = 0,
destination_shorthand: DestinationShorthand = .no_shorthand,
reserved2: u12 = 0,

const DestinationShorthand = enum(u2) {
no_shorthand = 0b00,
self = 0b01,
all_including_self = 0b10,
all_excluding_self = 0b11,
};
};

pub var lapic_base: u64 = undefined; // set in acpi.zig with handleMADT()
pub var io_apics: std.ArrayListUnmanaged(*const IOAPIC) = .{};
pub var isos: std.ArrayListUnmanaged(*const ISO) = .{};

Expand All @@ -65,51 +124,44 @@ pub fn init() void {
timerCalibrate();

// configure spurious IRQ
writeRegister(.spurious, readRegister(.spurious) | (1 << 8) | 0xff);
LAPIC.write(.spurious, LAPIC.read(.spurious) | 0x1ff);
}

pub inline fn eoi() void {
writeRegister(.eoi, 0);
LAPIC.write(.eoi, 0);
}

pub fn timerOneShot(microseconds: u64, vector: u8) void {
const old_state = x86.toggleInterrupts(false);
defer _ = x86.toggleInterrupts(old_state);

timerStop();

const ticks = microseconds * (smp.thisCpu().lapic_freq / 1_000_000);

writeRegister(.lvt_timer, vector);
writeRegister(.timer_divide, 0);
writeRegister(.timer_initial_count, @truncate(ticks));
LAPIC.write(.lvt_timer, vector); // clear mask and set vector
LAPIC.write(.timer_divide, 0);
LAPIC.write(.timer_initial_count, @intCast(ticks));
}

pub fn timerStop() void {
writeRegister(.timer_initial_count, 0);
writeRegister(.lvt_timer, 1 << 16);
LAPIC.write(.timer_initial_count, 0); // stop timer
LAPIC.write(.lvt_timer, 1 << 16); // mask vector
}

pub fn sendIPI(lapic_id: u32, vector: u32) void {
writeRegister(.icr1, lapic_id << 24);
writeRegister(.icr0, vector | (1 << 14)); // clear init level
pub fn sendIPI(lapic_id: u32, icr_low: ICRLow) void {
LAPIC.write(.icr_high, lapic_id << 24); // bits 32-55 of ICR are reserved
LAPIC.write(.icr_low, @bitCast(icr_low));
}

// TODO
pub fn timerCalibrate() void {
fn timerCalibrate() void {
timerStop();

// init PIT
writeRegister(.lvt_timer, (1 << 16) | 0xff); // vector 0xff, masked
writeRegister(.timer_divide, 0);

LAPIC.write(.timer_divide, 0);
pit.setReloadValue(0xffff); // reset PIT

const samples: u64 = 0xfffff;
const samples = 0xfffff;
const initial_tick = pit.getCurrentCount();

writeRegister(.timer_initial_count, @truncate(samples));
while (readRegister(.timer_current_count) != 0) {}
LAPIC.write(.timer_initial_count, samples);
while (LAPIC.read(.timer_current_count) != 0) {}

const final_tick = pit.getCurrentCount();

Expand All @@ -119,18 +171,7 @@ pub fn timerCalibrate() void {
timerStop();
}

fn readRegister(register: Register) u32 {
const reg = @intFromEnum(register);
return @as(*volatile u32, @ptrFromInt(lapic_base + vmm.hhdm_offset + reg)).*;
}

fn writeRegister(register: Register, val: u32) void {
const reg = @intFromEnum(register);
const ptr: *volatile u32 = @ptrFromInt(lapic_base + vmm.hhdm_offset + reg);
ptr.* = val;
}

fn setGSIRedirect(lapic_id: u32, vector: u8, gsi: u8, flags: u16) void {
fn setGSIRedirect(lapic_id: u32, vector: u8, gsi: u8, flags: u16, enable: bool) 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;
Expand All @@ -139,29 +180,26 @@ fn setGSIRedirect(lapic_id: u32, vector: u8, gsi: u8, flags: u16) void {
std.debug.panic("Could not find an IOAPIC for GSI {}", .{gsi});
};

var redirect: u64 = vector;
if ((flags & (1 << 1)) != 0) {
redirect |= (1 << 13);
}

if ((flags & (1 << 3)) != 0) {
redirect |= (1 << 15);
}

redirect |= @as(u64, @intCast(lapic_id)) << 56;
const redirect: IOAPIC.Redirect = .{
.vector = vector,
.pin_polarity = @intFromBool(flags & (1 << 1) != 0),
.trigger_mode = @intFromBool(flags & (1 << 3) != 0),
.mask = @intFromBool(!enable),
.destination = @intCast(lapic_id),
};

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));
io_apic.write(io_redirect_table, redirect.low());
io_apic.write(io_redirect_table + 1, redirect.high());
}

pub fn setIRQRedirect(lapic_id: u32, vector: u8, irq: u8) void {
pub fn setIRQRedirect(lapic_id: u32, vector: u8, irq: u8, enable: bool) void {
for (isos.items) |iso| {
if (iso.irq_source == irq) {
setGSIRedirect(lapic_id, vector, @intCast(iso.gsi), iso.flags);
setGSIRedirect(lapic_id, vector, @intCast(iso.gsi), iso.flags, enable);
return;
}
}

setGSIRedirect(lapic_id, vector, irq, 0);
setGSIRedirect(lapic_id, vector, irq, 0, enable);
}
Loading

0 comments on commit f5c0af0

Please sign in to comment.