mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-28 22:18:22 +00:00
feat(pl/pc): add APIC timer support
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
const std = @import("std");
|
||||
const amd64 = @import("arch");
|
||||
const r = @import("root");
|
||||
const tsc = @import("tsc.zig");
|
||||
const acpi = r.pl.acpi;
|
||||
const mm = r.mm;
|
||||
const ke = r.ke;
|
||||
@@ -74,6 +75,7 @@ pub var apics: std.ArrayList(u32) = .empty;
|
||||
var xapic_address: ?usize = null;
|
||||
pub var xapic_base_physical: usize = 0;
|
||||
const in_x2apic_mode = ke.CpuLocal(bool, false);
|
||||
const timer_ticks_per_us = ke.CpuLocal(u64, 0);
|
||||
|
||||
const log = std.log.scoped(.apic);
|
||||
|
||||
@@ -154,6 +156,82 @@ pub fn send_ipi(apic_id: u32, vector: u8, delivery_mode: u8) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn lapic_calibrate(ms: u64) u64 {
|
||||
write(.LvtTimer, (1 << 16));
|
||||
write(.TimerInitialCount, std.math.maxInt(u32));
|
||||
|
||||
ke.time.sleep(std.time.ns_per_ms * ms);
|
||||
|
||||
const ticks = std.math.maxInt(u32) - read(.TimerCurrentCount);
|
||||
|
||||
write(.LvtTimer, (1 << 16));
|
||||
write(.TimerInitialCount, 0);
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
fn timer_init() linksection(r.init) void {
|
||||
if (amd64.cpu_features.tsc_deadline) {
|
||||
// TSC-deadline mode.
|
||||
write(.LvtTimer, 32 | (2 << 17));
|
||||
asm volatile ("mfence" ::: .{ .memory = true });
|
||||
return;
|
||||
}
|
||||
|
||||
// Divide by 16.
|
||||
write(.TimerDivideConfig, 0x3);
|
||||
|
||||
const runs = 5;
|
||||
const calib_ms = 10;
|
||||
var total_ticks: u64 = 0;
|
||||
|
||||
for (0..runs) |_| {
|
||||
total_ticks += lapic_calibrate(calib_ms);
|
||||
}
|
||||
|
||||
const avg_ticks = total_ticks / runs;
|
||||
const calib_us = calib_ms * std.time.us_per_ms;
|
||||
|
||||
timer_ticks_per_us.local().* = (avg_ticks / calib_us);
|
||||
}
|
||||
|
||||
pub fn arm_timer(ns: r.Nanoseconds) void {
|
||||
if (amd64.cpu_features.tsc_deadline) {
|
||||
const now = amd64.rdtsc();
|
||||
const deadline = now + (ns * tsc.tsc_timer.frequency / std.time.ns_per_s);
|
||||
amd64.write_msr(.TscDeadline, deadline);
|
||||
return;
|
||||
}
|
||||
|
||||
// Masked.
|
||||
write(.LvtTimer, 1 << 16);
|
||||
write(.TimerInitialCount, 0);
|
||||
|
||||
const us = ns / std.time.ns_per_us;
|
||||
const ticks: u32 = @truncate(@max(us * timer_ticks_per_us.local().*, 1));
|
||||
|
||||
// Setup IRQ.
|
||||
write(.LvtTimer, 32);
|
||||
|
||||
// Divide by 16
|
||||
write(.TimerDivideConfig, 0x3);
|
||||
write(.TimerInitialCount, @as(u32, ticks));
|
||||
}
|
||||
|
||||
pub fn stop_timer() void {
|
||||
if (amd64.cpu_features.tsc_deadline) {
|
||||
amd64.write_msr(.TscDeadline, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
write(.LvtTimer, (1 << 16));
|
||||
write(.TimerInitialCount, 0);
|
||||
}
|
||||
|
||||
pub fn eoi() void {
|
||||
write(.Eoi, 0);
|
||||
}
|
||||
|
||||
pub fn init() linksection(r.init) void {
|
||||
var iter = acpi.madt.iterator();
|
||||
|
||||
@@ -210,6 +288,7 @@ pub fn init_local() void {
|
||||
write(.Svr, @intCast(read(.Svr) | 0x1FF));
|
||||
write(.Tpr, 0);
|
||||
write(.Eoi, 0);
|
||||
timer_init();
|
||||
}
|
||||
|
||||
pub fn enter_x2apic() void {
|
||||
|
||||
@@ -4,7 +4,7 @@ const amd64 = r.arch;
|
||||
const std = @import("std");
|
||||
const tsc = @import("tsc.zig");
|
||||
const pvclock = @import("pvclock.zig");
|
||||
const apic = @import("apic.zig");
|
||||
pub const apic = @import("apic.zig");
|
||||
const smp = @import("smp.zig");
|
||||
|
||||
const ki = r.ke.private;
|
||||
@@ -80,12 +80,12 @@ pub fn init_ap() void {
|
||||
}
|
||||
|
||||
pub fn late_init(boot_info: *pl.BootInfo) linksection(r.init) void {
|
||||
pl.acpi.init(boot_info);
|
||||
|
||||
if (amd64.hypervisor.info) |hv| {
|
||||
init_hypervisor(hv);
|
||||
}
|
||||
|
||||
pl.acpi.init(boot_info);
|
||||
|
||||
tsc.init();
|
||||
apic.init();
|
||||
|
||||
@@ -102,4 +102,6 @@ pub fn debug_write(c: u8) void {
|
||||
com1_write(c);
|
||||
}
|
||||
|
||||
pub fn arm_timer(_: u64) void {}
|
||||
pub fn arm_timer(deadline: r.Nanoseconds) void {
|
||||
apic.arm_timer(deadline);
|
||||
}
|
||||
|
||||
@@ -32,8 +32,7 @@ const ApData = extern struct {
|
||||
};
|
||||
|
||||
fn ap_entry(cpu_id: u32) callconv(.c) noreturn {
|
||||
_ = aps_booted.fetchAdd(1, .monotonic);
|
||||
ki.impl.init.ap_entry(cpu_id);
|
||||
ki.impl.init.ap_entry(cpu_id, &aps_booted);
|
||||
}
|
||||
|
||||
pub fn init() linksection(r.init) void {
|
||||
@@ -70,7 +69,7 @@ pub fn init() linksection(r.init) void {
|
||||
ki.impl.cpu_offsets = @ptrCast(mm.zone.gpa.alloc(usize, apic.apics.items.len + 1) catch @panic("Failed to allocate AP local data offsets"));
|
||||
const percpu_size = @intFromPtr(&__percpu_end) - @intFromPtr(&__percpu_start);
|
||||
|
||||
ki.impl.cpu_offsets[0] = @intFromPtr(&__percpu_start);
|
||||
ki.impl.cpu_offsets[0] = 0;
|
||||
|
||||
cpu_id_to_apic_id[0] = apic.get_id();
|
||||
|
||||
@@ -89,8 +88,12 @@ pub fn init() linksection(r.init) void {
|
||||
const cpu_data = mm.zone.gpa.alloc(u8, percpu_size) catch @panic("Failed to allocate per-cpu data");
|
||||
@memcpy(cpu_data, @as([*]u8, @ptrCast(&__percpu_start))[0..percpu_size]);
|
||||
|
||||
const self_offset_offset = @intFromPtr(&ki.impl.cpu_self_offset) - @intFromPtr(&__percpu_start);
|
||||
ki.impl.cpu_offsets[cpu_id] = @intFromPtr(cpu_data.ptr) -% @intFromPtr(&__percpu_start);
|
||||
|
||||
// copy the offset into the AP's self_offset variable
|
||||
@as(*usize, @ptrCast(@alignCast(&cpu_data[self_offset_offset]))).* = ki.impl.cpu_offsets[cpu_id];
|
||||
|
||||
const stack_top = @intFromPtr(mm.heap.alloc(r.kib(16)) catch @panic("Failed to allocate AP stack")) + r.kib(16);
|
||||
|
||||
start_stack.remote(@intCast(cpu_id)).* = stack_top & ~@as(usize, 15);
|
||||
|
||||
@@ -6,7 +6,7 @@ const ke = r.ke;
|
||||
|
||||
const log = std.log.scoped(.@"amd64/tsc");
|
||||
|
||||
var tsc_timer: ke.TimeCounter = .{
|
||||
pub var tsc_timer: ke.TimeCounter = .{
|
||||
.name = "TSC",
|
||||
.quality = 100,
|
||||
.frequency = 0,
|
||||
@@ -30,7 +30,7 @@ pub fn init() linksection(r.init) void {
|
||||
// Try using cpuid leaf 0x15 to get the TSC frequency.
|
||||
const cpuid_state = amd64.cpuid(0x15, 0);
|
||||
|
||||
if (cpuid_state.ebx != 0 and cpuid_state.ecx != 0) {
|
||||
if (cpuid_state.ebx != 0 and cpuid_state.ecx != 0 and cpuid_state.eax != 0) {
|
||||
const tsc_freq = (cpuid_state.ebx / cpuid_state.eax) * cpuid_state.ecx;
|
||||
log.info("TSC frequency determined via CPUID: {} Hz", .{tsc_freq});
|
||||
tsc_timer.frequency = tsc_freq;
|
||||
|
||||
@@ -29,12 +29,11 @@ pub inline fn debug_write(c: u8) void {
|
||||
}
|
||||
|
||||
/// Arm a one-shot timer to fire in `ns` nanoseconds.
|
||||
/// The timer must call `ki_timer_expiry()` when it fires.
|
||||
pub inline fn arm_timer(ns: u64) void {
|
||||
return impl.arm_timer(ns);
|
||||
}
|
||||
|
||||
/// Pretty name for the platform
|
||||
/// Pretty name for the platform.
|
||||
pub const name = impl.name;
|
||||
|
||||
/// A generic struct representing information passed from the bootloader.
|
||||
|
||||
@@ -50,11 +50,13 @@ var tc: ke.TimeCounter = .{
|
||||
};
|
||||
|
||||
pub fn init() void {
|
||||
var act: posix.Sigaction = undefined;
|
||||
var act = std.mem.zeroes(posix.Sigaction);
|
||||
|
||||
act.handler.sigaction = timer_handler;
|
||||
act.flags = posix.SA.RESTART | posix.SA.SIGINFO;
|
||||
|
||||
act.mask = posix.sigfillset();
|
||||
|
||||
posix.sigaction(posix.SIG.ALRM, &act, null);
|
||||
|
||||
ke.time.register_source(&tc);
|
||||
|
||||
Reference in New Issue
Block a user