Skip to content

Commit

Permalink
Add timespec, Timer and nanosleep to pit.zig
Browse files Browse the repository at this point in the history
  • Loading branch information
Ratakor committed Oct 6, 2023
1 parent 2ba12de commit 5627975
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 11 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ A kernel made with zig

# TODO
- Add a checklist/roadmap
- Provide compatibility with Linux ABI
- Don't make a monolithic kernel
- Support RISC-V64, aarch64 and x86_64
- Replace limine with a custom bootloader?

Expand Down
8 changes: 8 additions & 0 deletions kernel/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ export fn _start() callconv(.C) noreturn {
tty.hideCursor();
};

pit.nanosleep(3 * pit.ns_per_s); // TODO: testing

arch.halt();
}

Expand Down Expand Up @@ -102,7 +104,13 @@ fn main() !void {
ps2.init();
// TODO: pci

// TODO: process
// TODO: basic syscalls + lib for them (zig + C)
// TODO: server with more syscall for compat with Linux

// TODO: filesystem <- extern

// TODO: socket -> TCP/IP

// TODO: start /bin/init <- load elf with std.elf
}
149 changes: 138 additions & 11 deletions kernel/pit.zig
Original file line number Diff line number Diff line change
@@ -1,29 +1,118 @@
//! https://wiki.osdev.org/PIT

const std = @import("std");
const root = @import("root");
const arch = @import("arch.zig");
const cpu = @import("cpu.zig");
const idt = @import("idt.zig");
const apic = @import("apic.zig");
const SpinLock = @import("lock.zig").SpinLock;
const log = std.log.scoped(.pit);

pub const timespec = extern struct {
tv_sec: isize = 0,
tv_nsec: isize = 0,

pub inline fn add(self: *timespec, ts: timespec) void {
if (self.tv_nsec + ts.tv_nsec > max_ns) {
self.tv_nsec = (self.tv_nsec + ts.tv_nsec) - ns_per_s;
self.tv_sec += 1;
} else {
self.tv_nsec += ts.tv_nsec;
}
self.tv_sec += ts.tv_sec;
}

pub inline fn sub(self: *timespec, ts: timespec) void {
if (ts.tv_nsec > self.tv_nsec) {
self.tv_nsec = max_ns - (ts.tv_nsec - self.tv_nsec);
if (self.tv_sec == 0) {
self.tv_nsec = 0;
return;
}
self.tv_sec -= 1;
} else {
self.tv_nsec -= ts.tv_nsec;
}

if (ts.tv_sec > self.tv_sec) {
self.tv_sec = 0;
self.tv_nsec = 0;
} else {
self.tv_sec -= ts.tv_sec;
}
}
};

pub const Timer = struct {
idx: usize,
done: bool,
when: timespec,
// event: ev.Event;

pub fn init(when: timespec) !*Timer {
var timer = try root.allocator.create(Timer);
timer.idx = bad_idx;
timer.when = when;
timer.done = false;
try timer.arm();
return timer;
}

pub fn deinit(self: *Timer) void {
self.disarm();
root.allocator.destroy(self);
}

fn arm(self: *Timer) !void {
timers_lock.lock();
defer timers_lock.unlock();

self.idx = armed_timers.items.len;
self.done = false;
armed_timers.append(self) catch |err| return err;
}

fn disarm(self: *Timer) void {
timers_lock.lock();
defer timers_lock.unlock();

if (armed_timers.items.len == 0 or self.idx == bad_idx or self.idx >= armed_timers.items.len) {
return;
}

armed_timers.items[self.idx] = armed_timers.getLast();
armed_timers.items[self.idx].idx = self.idx;
_ = armed_timers.pop();
self.idx = bad_idx;
}
};

const bad_idx = std.math.maxInt(usize);
pub const dividend = 1_193_182;
const timer_freq = 1000;
pub const timer_freq = 100;
pub const ns_per_s = std.time.ns_per_s;
pub const max_ns = ns_per_s - 1;

pub var monotonic: timespec = .{};
pub var realtime: timespec = .{};

var timers_lock: SpinLock = .{};
var armed_timers = std.ArrayList(*Timer).init(root.allocator);

pub fn init() void {
const boot_time = root.boot_time_request.response.?.boot_time;
realtime.tv_sec = boot_time;

setFrequency(timer_freq);
const timer_vector = idt.allocateVector();
idt.registerHandler(timer_vector, timerHandler);
apic.setIRQRedirect(cpu.bsp_lapic_id, timer_vector, 0);
}

pub fn getCurrentCount() u16 {
arch.out(u8, 0x43, 0);
const lo = arch.in(u8, 0x40);
const hi = arch.in(u8, 0x40);
return (@as(u16, @intCast(hi)) << 8) | lo;
log.info("realtime: {}", .{realtime});
}

pub fn setFrequency(divisor: u64) void {
fn setFrequency(divisor: u64) void {
var count: u16 = @truncate(dividend / divisor);
if (dividend % divisor > divisor / 2) {
count += 1;
Expand All @@ -35,10 +124,48 @@ pub fn setFrequency(divisor: u64) void {
arch.out(u8, 0x40, @truncate(count >> 8));
}

pub fn getCurrentCount() u16 {
arch.out(u8, 0x43, 0);
const lo = arch.in(u8, 0x40);
const hi = arch.in(u8, 0x40);
return (@as(u16, @intCast(hi)) << 8) | lo;
}

fn timerHandler(ctx: *cpu.Context) void {
_ = ctx;

@import("tty.zig").write(".");
// TODO time.timerHandler();
apic.eoi();
defer apic.eoi();

const interval: timespec = .{ .tv_nsec = ns_per_s / timer_freq };
monotonic.add(interval);
realtime.add(interval);

if (timers_lock.tryLock()) {
for (armed_timers.items) |timer| {
if (timer.done) continue;

timer.when.sub(interval);
if (timer.when.tv_sec == 0 and timer.when.tv_nsec == 0) {
// ev.trigger(&timer.event, false);
timer.done = true;
}
}
timers_lock.unlock();
}
}

pub fn nanosleep(ns: u64) void {
const duration: timespec = .{
.tv_sec = @intCast(ns / ns_per_s),
.tv_nsec = @intCast(ns),
};
const timer = Timer.init(duration) catch return;
defer timer.deinit();
// const events: []*ev.Event = .{ &timer.event };
// ev.await(events, true);

// TODO: testing
log.debug("sleep for {}s", .{duration.tv_sec});
while (!timer.done) asm volatile ("hlt");
log.debug("done", .{});
}

0 comments on commit 5627975

Please sign in to comment.