Microkernel thing OS experiment (Zig ⚡)

8254 timer demo

Successfully set up the 8254 and 8259 for the sole purpose of
calibrating the TSC on AMD.

pci.express 0e62c4b3 495d11bf

verified
Changed files
+219 -37
components
root_server
src
ukernel
+6 -1
components/root_server/src/main.zig
···
_ = os.syscall1(SYS_poke, 0xB16B00B5BADBABE);
_ = os.syscall1(SYS_exit, 0x69696969);
-
unreachable;
+
die();
+
}
+
+
pub inline fn die() noreturn {
+
while (true) {}
}
+
pub const SYS_exit = 0x420;
pub const SYS_poke = 0x69;
+1 -1
components/ukernel/arch/amd64/apic.zig components/ukernel/arch/amd64/interrupts/apic.zig
···
const std = @import("std");
-
const arch = @import("root.zig");
+
const arch = @import("../root.zig");
const log = std.log.scoped(.apic);
// Must instantiate this!
+27 -22
components/ukernel/arch/amd64/boot.zig
···
// Install the IDT
initIdt();
-
// AP bootstrap
-
bootstrapAPs();
-
// Set up our own GDT and TSS
const gdt = &arch.per_cpu_init_data.gdt_buf[0];
gdt.* = .{};
···
gdt.load();
log.info("BSP successfully setup GDT+TSS!", .{});
+
// AP bootstrap
+
bootstrapAPs();
+
+
// Calibrate our TSC
+
arch.tsc.calibrate();
+
log.info("Setting up scheduling...", .{});
-
// Initialize the APIC
-
// Map the APIC first!
-
const apic_base = common.mm.physToHHDM([*]volatile u8, 0xFEE0_0000);
-
common.mm.paging.mapPhys(.{
-
.vaddr = @intFromPtr(apic_base),
-
.paddr = 0xFEE0_0000,
-
.size = 0x1000,
-
.memory_type = .DeviceUncacheable,
-
.perms = .{
-
.executable = false,
-
.userspace_accessible = false,
-
.writable = true,
-
},
-
}) catch @panic("apic bruh");
-
const apic: arch.apic.LAPIC = .{ .xapic = apic_base };
-
arch.apic.init.initialSetup(apic);
+
// // Initialize the APIC
+
// // Map the APIC first!
+
// const apic_base = common.mm.physToHHDM([*]volatile u8, 0xFEE0_0000);
+
// common.mm.paging.mapPhys(.{
+
// .vaddr = @intFromPtr(apic_base),
+
// .paddr = 0xFEE0_0000,
+
// .size = 0x1000,
+
// .memory_type = .DeviceUncacheable,
+
// .perms = .{
+
// .executable = false,
+
// .userspace_accessible = false,
+
// .writable = true,
+
// },
+
// }) catch @panic("apic bruh");
+
// const apic: arch.interrupts.apic.LAPIC = .{ .xapic = apic_base };
+
// arch.interrupts.apic.init.initialSetup(apic);
log.info("Allocating code for userspace...", .{});
···
});
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 });
-
if (rdi == 0x420) arch.instructions.die();
}
fn enter_userspace(entry: u64, arg: u64, stack: u64) noreturn {
···
asm volatile (
\\ push %[userdata64]
\\ push %[stack]
-
\\ push $0x2
+
\\ push $0x202
\\ push %[usercode64]
\\ push %[entry]
\\
···
arch.per_cpu_init_data.idt.double_fault.installHandler(double_fault);
arch.per_cpu_init_data.idt.general_protection_fault.installHandler(gpf);
arch.per_cpu_init_data.idt.page_fault.installHandler(page_fault);
-
arch.per_cpu_init_data.idt.interrupts[0xFF - 32].installHandler(arch.apic.spurious_interrupt_handler);
+
arch.per_cpu_init_data.idt.interrupts[0xFF - 32].installHandler(arch.interrupts.apic.spurious_interrupt_handler);
+
+
// Install the PIT handler
+
arch.per_cpu_init_data.idt.interrupts[0].installHandler(arch.interrupts.pit.handler);
// Load the Idt Register
const reg: Idt.Idtr = .{ .addr = idt_addr, .limit = @sizeOf(Idt) - 1 };
-12
components/ukernel/arch/amd64/instructions/root.zig
···
asm volatile ("hlt");
}
}
-
-
pub inline fn rdtsc() u64 {
-
var low: u32 = undefined;
-
var high: u32 = undefined;
-
-
asm volatile ("rdtsc"
-
: [low] "={eax}" (low),
-
[high] "={eax}" (high),
-
);
-
-
return (@as(u64, high) << 32) | @as(u64, low);
-
}
+60
components/ukernel/arch/amd64/interrupts/pic.zig
···
+
/// Remap the 8259 PIC to an interrupt base of 0x32
+
const arch = @import("../root.zig");
+
const out = arch.port.out;
+
const in = arch.port.in;
+
+
const PIC_ONE_CMD_PORT = 0x20;
+
const PIC_ONE_DATA_PORT = 0x21;
+
const PIC_ONE_IDT_BASE = 32;
+
+
const PIC_TWO_CMD_PORT = 0xA0;
+
const PIC_TWO_DATA_PORT = 0xA1;
+
const PIC_TWO_IDT_BASE = 40;
+
+
const CMD_INIT = 0x11;
+
const CMD_EOI = 0x20;
+
+
// Remap the sixteen 8259 interrupts out of the way to
+
// base interrupt number 32, and mask all interrupts
+
// except the 8254 PIT
+
pub fn init() void {
+
// Init the 8259's
+
out(u8, PIC_ONE_CMD_PORT, CMD_INIT);
+
wait();
+
out(u8, PIC_TWO_CMD_PORT, CMD_INIT);
+
wait();
+
+
// Write the interrupt base numbers for both
+
out(u8, PIC_ONE_DATA_PORT, PIC_ONE_IDT_BASE);
+
wait();
+
out(u8, PIC_TWO_DATA_PORT, PIC_TWO_IDT_BASE);
+
wait();
+
+
// Tell first 8259 that there's a second 8259 at IRQ 2, and tell
+
// the second 8259 that it is at that position.
+
out(u8, PIC_ONE_DATA_PORT, 1 << 2);
+
wait();
+
out(u8, PIC_TWO_DATA_PORT, 2);
+
wait();
+
+
// Tell the PICs to use 8086 mode
+
out(u8, PIC_ONE_DATA_PORT, 1);
+
wait();
+
out(u8, PIC_ONE_DATA_PORT, 1);
+
wait();
+
+
// Unmask only the 8254
+
out(u8, PIC_ONE_DATA_PORT, 0b1111_1110);
+
wait();
+
out(u8, PIC_TWO_DATA_PORT, 0b1111_1111);
+
wait();
+
}
+
+
inline fn wait() void {
+
out(u8, 0x80, 0);
+
}
+
+
// Notify the first 8259 that we're done with the timer interrupt
+
pub fn end_of_timer_interrupt() void {
+
out(u8, PIC_ONE_CMD_PORT, CMD_EOI);
+
}
+25
components/ukernel/arch/amd64/interrupts/pit.zig
···
+
const arch = @import("../root.zig");
+
const out = arch.port.out;
+
const pic = @import("pic.zig");
+
const Idt = arch.structures.Idt;
+
const PIT_HZ = 1_193_180;
+
const DATA_PORT = 0x40;
+
const CMD_PORT = 0x43;
+
+
pub fn set_frequency(hz: u32) void {
+
const divider = PIT_HZ / hz;
+
@import("std").log.debug("divider = {}", .{divider});
+
// Square Wave Generator, lobyte/hibyte
+
const mode = 0b0011_0110;
+
+
out(u8, CMD_PORT, mode);
+
out(u8, DATA_PORT, @truncate(divider));
+
out(u8, DATA_PORT, @truncate(divider >> 8));
+
}
+
var ctr: usize = 0;
+
pub fn handler(_: *Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void {
+
if (ctr % 1000 == 0)
+
@import("std").log.warn("PIT!", .{});
+
ctr += 1;
+
pic.end_of_timer_interrupt();
+
}
+10
components/ukernel/arch/amd64/interrupts/root.zig
···
+
pub const apic = @import("apic.zig");
+
pub const pic = @import("pic.zig");
+
pub const pit = @import("pit.zig");
+
+
pub inline fn enable() void {
+
asm volatile ("sti");
+
}
+
pub inline fn disable() void {
+
asm volatile ("cli");
+
}
+62
components/ukernel/arch/amd64/port.zig
···
+
pub fn out(comptime T: type, port: u16, value: T) void {
+
switch (T) {
+
u8 => outb(port, value),
+
u16 => outw(port, value),
+
u32 => outl(port, value),
+
else => @compileError("Bad I/O port out size!"),
+
}
+
}
+
+
pub fn in(comptime T: type, port: u16) T {
+
return switch (T) {
+
u8 => inb(port),
+
u16 => inw(port),
+
u32 => inl(port),
+
else => @compileError("Bad I/O port in size!"),
+
};
+
}
+
+
pub fn outb(port: u16, val: u8) void {
+
asm volatile ("outb %[val], %[port]"
+
:
+
: [val] "{al}" (val),
+
[port] "N{dx}" (port),
+
);
+
}
+
+
pub fn outw(port: u16, val: u16) void {
+
asm volatile ("outw %[val], %[port]"
+
:
+
: [val] "{ax}" (val),
+
[port] "N{dx}" (port),
+
);
+
}
+
+
pub fn outl(port: u16, val: u32) void {
+
asm volatile ("outl %[val], %[port]"
+
:
+
: [val] "{eax}" (val),
+
[port] "N{dx}" (port),
+
);
+
}
+
+
pub fn inb(port: u16) u8 {
+
return asm volatile ("inb %[port], %[result]"
+
: [result] "={al}" (-> u8),
+
: [port] "N{dx}" (port),
+
);
+
}
+
+
fn inw(port: u16) u16 {
+
return asm volatile ("inw %[port], %[result]"
+
: [result] "={ax}" (-> u16),
+
: [port] "N{dx}" (port),
+
);
+
}
+
+
pub fn inl(port: u16) u32 {
+
return asm volatile ("inl %[port], %[result]"
+
: [result] "={eax}" (-> u32),
+
: [port] "N{dx}" (port),
+
);
+
}
+2 -1
components/ukernel/arch/amd64/root.zig
···
-
pub const apic = @import("apic.zig");
pub const boot = @import("boot.zig");
pub const instructions = @import("instructions/root.zig");
+
pub const interrupts = @import("interrupts/root.zig");
pub const mm = @import("mm/root.zig");
pub const port = @import("port.zig");
pub const structures = @import("structures/root.zig");
+
pub const tsc = @import("tsc.zig");
pub const registers = @import("registers.zig");
const common = @import("common");
const std = @import("std");
+26
components/ukernel/arch/amd64/tsc.zig
···
+
const arch = @import("root.zig");
+
pub inline fn rdtsc() u64 {
+
var low: u32 = undefined;
+
var high: u32 = undefined;
+
+
asm volatile ("rdtsc"
+
: [low] "={eax}" (low),
+
[high] "={eax}" (high),
+
);
+
+
return (@as(u64, high) << 32) | @as(u64, low);
+
}
+
+
/// This should be called if we cannot get the TSC rate from
+
/// CPUID.15h and 16h on Intel platforms. The calibration will be done
+
/// against the 8254 PIT.
+
pub fn calibrate() void {
+
// Set up the PIC
+
arch.interrupts.pic.init();
+
+
// Set up the PIT at 1 kHz
+
arch.interrupts.pit.set_frequency(1000);
+
+
// Enable interrupts
+
// arch.interrupts.enable();
+
}