mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-27 13:58:20 +00:00
feat: initial ACPI support
This commit is contained in:
@@ -137,6 +137,30 @@ pub inline fn hlt() void {
|
||||
asm volatile ("hlt");
|
||||
}
|
||||
|
||||
pub fn pio_read(comptime T: type, port: u16) T {
|
||||
if (@sizeOf(T) == 1) {
|
||||
return inb(port);
|
||||
} else if (@sizeOf(T) == 2) {
|
||||
return inw(port);
|
||||
} else if (@sizeOf(T) == 4) {
|
||||
return inl(port);
|
||||
} else {
|
||||
@compileError("unsupported pio read size");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pio_write(comptime T: type, port: u16, value: T) void {
|
||||
if (@sizeOf(T) == 1) {
|
||||
outb(port, @as(u8, value));
|
||||
} else if (@sizeOf(T) == 2) {
|
||||
outw(port, @as(u16, value));
|
||||
} else if (@sizeOf(T) == 4) {
|
||||
outl(port, @as(u32, value));
|
||||
} else {
|
||||
@compileError("unsupported pio write size");
|
||||
}
|
||||
}
|
||||
|
||||
pub const RFlags = packed struct(u64) {
|
||||
carry: bool,
|
||||
_reserved0: u1,
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ const mm = b.mm;
|
||||
pub fn init(boot_info: *pl.BootInfo) void {
|
||||
mm.init(boot_info);
|
||||
|
||||
pl.late_init();
|
||||
pl.late_init(boot_info);
|
||||
ke.sched.late_init();
|
||||
|
||||
while (true) {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
const b = @import("base");
|
||||
const std = @import("std");
|
||||
const acpi = b.pl.acpi;
|
||||
const mm = b.mm;
|
||||
const ke = b.ke;
|
||||
|
||||
var hpet_timer: ke.TimeCounter = .{
|
||||
.name = "HPET",
|
||||
.quality = 50,
|
||||
.frequency = 0, // Filled at runtime
|
||||
.read_count = read_hpet,
|
||||
.mask = std.math.maxInt(u64),
|
||||
.p = 0,
|
||||
.n = 0,
|
||||
};
|
||||
|
||||
var hpet_regs: *volatile acpi.HpetRegs = undefined;
|
||||
|
||||
const femtos_per_s = 1_000_000_000_000_000;
|
||||
|
||||
pub fn init(hpet: *acpi.HpetTable) linksection(b.init) void {
|
||||
hpet_regs = @ptrFromInt(mm.p2v(hpet.base_address.address));
|
||||
|
||||
hpet_timer.frequency = femtos_per_s / (b.mmio_read(u64, @intFromPtr(&hpet_regs.general_capabilities)) >> 32);
|
||||
|
||||
// Disable timer.
|
||||
b.mmio_write(u64, @intFromPtr(&hpet_regs.general_configuration), 0);
|
||||
|
||||
// Reset counter.
|
||||
b.mmio_write(u64, @intFromPtr(&hpet_regs.main_counter_value), 0);
|
||||
|
||||
// Enable timer.
|
||||
b.mmio_write(u64, @intFromPtr(&hpet_regs.general_configuration), 1);
|
||||
|
||||
ke.time.register_source(&hpet_timer);
|
||||
}
|
||||
|
||||
fn read_hpet() u64 {
|
||||
return b.mmio_read(u64, @intFromPtr(&hpet_regs.main_counter_value));
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
const b = @import("base");
|
||||
const mm = b.mm;
|
||||
const pl = b.pl;
|
||||
const arch = @import("arch");
|
||||
const std = @import("std");
|
||||
const timer = @import("timer.zig");
|
||||
const hpet = @import("hpet.zig");
|
||||
|
||||
const rsdp_signature = "RSD PTR ";
|
||||
const rsdt_signature = "RSDT";
|
||||
const xsdt_signature = "XSDT";
|
||||
const madt_signature = "APIC";
|
||||
const fadt_signature = "FACP";
|
||||
const mcfg_signature = "MCFG";
|
||||
const hpet_signature = "HPET";
|
||||
|
||||
pub const AddressSpaceId = enum(u8) {
|
||||
SystemMemory = 0,
|
||||
SystemIo = 1,
|
||||
PciConfigurationSpace = 2,
|
||||
EmbeddedController = 3,
|
||||
SMBus = 4,
|
||||
SystemCMOS = 5,
|
||||
PciBarTarget = 6,
|
||||
IPMI = 7,
|
||||
GeneralPurposeIO = 8,
|
||||
GenericSerialBus = 9,
|
||||
PlatformCommunicationsChannel = 10,
|
||||
FunctionalFixedHardware = 0x7F,
|
||||
};
|
||||
|
||||
pub const Rsdp = extern struct {
|
||||
signature: [8]u8 align(1),
|
||||
checksum: u8 align(1),
|
||||
oem_id: [6]u8 align(1),
|
||||
revision: u8 align(1),
|
||||
rsdt_address: u32 align(1),
|
||||
|
||||
// The following fields are only valid if revision >= 2.
|
||||
length: u32 align(1),
|
||||
xsdt_address: u64 align(1),
|
||||
extended_checksum: u8 align(1),
|
||||
reserved: [3]u8 align(1),
|
||||
};
|
||||
|
||||
pub const SdtHeader = extern struct {
|
||||
signature: [4]u8 align(1),
|
||||
length: u32 align(1),
|
||||
revision: u8 align(1),
|
||||
checksum: u8 align(1),
|
||||
oem_id: [6]u8 align(1),
|
||||
oem_table_id: [8]u8 align(1),
|
||||
oem_revision: u32 align(1),
|
||||
creator_id: u32 align(1),
|
||||
creator_revision: u32 align(1),
|
||||
};
|
||||
|
||||
pub const Rsdt = extern struct {
|
||||
header: SdtHeader,
|
||||
entries: [0]u32,
|
||||
};
|
||||
|
||||
pub const Xsdt = extern struct {
|
||||
header: SdtHeader,
|
||||
entries: [0]u64,
|
||||
};
|
||||
|
||||
pub const Madt = extern struct {
|
||||
header: SdtHeader,
|
||||
lapic_address: u32 align(1),
|
||||
flags: u32 align(1),
|
||||
entries: [0]u8 align(1),
|
||||
};
|
||||
|
||||
/// Generic address structure.
|
||||
pub const Gas = extern struct {
|
||||
/// The address space where the structure exists.
|
||||
address_space_id: AddressSpaceId align(1),
|
||||
/// The size in bits of the given register.
|
||||
register_bit_width: u8 align(1),
|
||||
/// The bit offset of the given register at the given address.
|
||||
register_bit_offset: u8 align(1),
|
||||
/// The access size of the given register. (1 = byte, 2 = word, 3 = dword, 4 = qword).
|
||||
access_size: u8 align(1),
|
||||
address: u64 align(1),
|
||||
pub fn read(self: Gas) u64 {
|
||||
const raw: u64 = switch (self.address_space_id) {
|
||||
.SystemMemory => switch (self.access_size) {
|
||||
1 => b.mmio_read(u8, self.address),
|
||||
2 => b.mmio_read(u16, self.address),
|
||||
3 => b.mmio_read(u32, self.address),
|
||||
4 => b.mmio_read(u64, self.address),
|
||||
else => unreachable,
|
||||
},
|
||||
.SystemIo => switch (self.access_size) {
|
||||
1 => arch.pio_read(u8, @truncate(self.address)),
|
||||
2 => arch.pio_read(u16, @truncate(self.address)),
|
||||
3 => arch.pio_read(u32, @truncate(self.address)),
|
||||
else => unreachable,
|
||||
},
|
||||
else => @panic("unsupported GAS address space"),
|
||||
};
|
||||
|
||||
const shifted = raw >> @intCast(self.register_bit_offset);
|
||||
const mask = if (self.register_bit_width == 64)
|
||||
std.math.maxInt(u64)
|
||||
else
|
||||
(@as(u64, 1) << @intCast(self.register_bit_width)) - 1;
|
||||
return shifted & mask;
|
||||
}
|
||||
|
||||
pub fn write(self: Gas, value: u64) void {
|
||||
const masked_value = value & if (self.register_bit_width == 64)
|
||||
std.math.maxInt(u64)
|
||||
else
|
||||
(@as(u64, 1) << @intCast(self.register_bit_width)) - 1;
|
||||
const shifted_value = masked_value << self.register_bit_offset;
|
||||
|
||||
switch (self.address_space_id) {
|
||||
.SystemMemory => switch (self.access_size) {
|
||||
1 => b.mmio_write(u8, self.address, @truncate(shifted_value)),
|
||||
2 => b.mmio_write(u16, self.address, @truncate(shifted_value)),
|
||||
3 => b.mmio_write(u32, self.address, @truncate(shifted_value)),
|
||||
4 => b.mmio_write(u64, self.address, shifted_value),
|
||||
else => unreachable,
|
||||
},
|
||||
.SystemIo => switch (self.access_size) {
|
||||
1 => arch.pio_write(u8, @truncate(self.address), @truncate(shifted_value)),
|
||||
2 => arch.pio_write(u16, @truncate(self.address), @truncate(shifted_value)),
|
||||
3 => arch.pio_write(u32, @truncate(self.address), @truncate(shifted_value)),
|
||||
else => unreachable,
|
||||
},
|
||||
else => @panic("unsupported GAS address space"),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Fadt = extern struct {
|
||||
header: SdtHeader,
|
||||
firmware_ctrl: u32 align(1),
|
||||
dsdt: u32 align(1),
|
||||
reserved: u8 align(1),
|
||||
preferred_pm_profile: u8 align(1),
|
||||
sci_interrupt: u16 align(1),
|
||||
smi_command_port: u32 align(1),
|
||||
acpi_enable: u8 align(1),
|
||||
acpi_disable: u8 align(1),
|
||||
s4bios_req: u8 align(1),
|
||||
pstate_control: u8 align(1),
|
||||
pm1a_evt_blk: u32 align(1),
|
||||
pm1b_evt_blk: u32 align(1),
|
||||
pm1a_cnt_blk: u32 align(1),
|
||||
pm1b_cnt_blk: u32 align(1),
|
||||
pm2_cnt_blk: u32 align(1),
|
||||
pm_tmr_blk: u32 align(1),
|
||||
gpe0_blk: u32 align(1),
|
||||
gpe1_blk: u32 align(1),
|
||||
pm1_evt_len: u8 align(1),
|
||||
pm1_cnt_len: u8 align(1),
|
||||
pm2_cnt_len: u8 align(1),
|
||||
pm_tmr_len: u8 align(1),
|
||||
gpe0_blk_len: u8 align(1),
|
||||
gpe1_blk_len: u8 align(1),
|
||||
gpe1_base: u8 align(1),
|
||||
cst_cnt: u8 align(1),
|
||||
p_lvl2_lat: u16 align(1),
|
||||
p_lvl3_lat: u16 align(1),
|
||||
flush_size: u16 align(1),
|
||||
flush_stride: u16 align(1),
|
||||
duty_offset: u8 align(1),
|
||||
duty_width: u8 align(1),
|
||||
day_alarm: u8 align(1),
|
||||
month_alarm: u8 align(1),
|
||||
century: u8 align(1),
|
||||
iapc_boot_arch: u16 align(1),
|
||||
reserved2: u8 align(1),
|
||||
flags: u32 align(1),
|
||||
reset_reg: Gas,
|
||||
reset_value: u8 align(1),
|
||||
arm_boot_arch: u16 align(1),
|
||||
fadt_minor_version: u8 align(1),
|
||||
x_firmware_ctrl: u64 align(1),
|
||||
x_dsdt: u64 align(1),
|
||||
x_pm1a_evt_blk: Gas,
|
||||
x_pm1b_evt_blk: Gas,
|
||||
x_pm1a_cnt_blk: Gas,
|
||||
x_pm1b_cnt_blk: Gas,
|
||||
x_pm2_cnt_blk: Gas,
|
||||
x_pm_tmr_blk: Gas,
|
||||
x_gpe0_blk: Gas,
|
||||
x_gpe1_blk: Gas,
|
||||
sleep_control_reg: Gas,
|
||||
sleep_status_reg: Gas,
|
||||
hypervisor_vendor_id: [8]u8 align(1),
|
||||
|
||||
const version_sizes = [_]usize{ 116, 132, 244, 244, 268, 276 };
|
||||
|
||||
pub fn revision(self: Fadt) u8 {
|
||||
// We can't rely on firmware to set the revision field correctly, so we infer it from the length field.
|
||||
const declared: usize = self.header.revision;
|
||||
const len: u32 = self.header.length;
|
||||
|
||||
const idx = for (version_sizes, 0..) |size, i| {
|
||||
if (len <= size) break i;
|
||||
} else version_sizes.len;
|
||||
|
||||
const inferred: usize = @min(idx + 1, version_sizes.len);
|
||||
|
||||
if (declared == inferred or (inferred == 3 and declared == 4)) {
|
||||
return @intCast(declared);
|
||||
}
|
||||
|
||||
return @intCast(inferred);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HpetTable = extern struct {
|
||||
hdr: SdtHeader,
|
||||
hardware_rev_id: u8 align(1),
|
||||
bits: packed struct(u8) {
|
||||
comparator_count: u5,
|
||||
counter_size_capability: u1,
|
||||
reserved: u1,
|
||||
legacy_replacement_capability: u1,
|
||||
} align(1),
|
||||
pci_vendor_id: u16 align(1),
|
||||
base_address: Gas,
|
||||
hpet_number: u8 align(1),
|
||||
minimum_tick: u16 align(1),
|
||||
page_protection: u8 align(1),
|
||||
};
|
||||
|
||||
pub const HpetRegs = extern struct {
|
||||
general_capabilities: u64,
|
||||
reserved0: u64,
|
||||
general_configuration: u64,
|
||||
reserved1: u64,
|
||||
general_interrupt_status: u64,
|
||||
reserved2: [25]u64,
|
||||
main_counter_value: u64,
|
||||
};
|
||||
|
||||
pub var xsdt: ?*Xsdt = null;
|
||||
pub var rsdt: ?*Rsdt = null;
|
||||
|
||||
pub fn find_table(signature: []const u8) ?*SdtHeader {
|
||||
if (xsdt) |x| {
|
||||
const entry_count = (x.header.length - @sizeOf(SdtHeader)) / @sizeOf(u64);
|
||||
const entries_ptr: [*]u64 = @ptrCast(&x.entries);
|
||||
|
||||
for (0..entry_count) |i| {
|
||||
const hdr: *SdtHeader = @ptrFromInt(mm.p2v(entries_ptr[i]));
|
||||
if (std.mem.eql(u8, hdr.signature[0..], signature)) {
|
||||
return hdr;
|
||||
}
|
||||
}
|
||||
} else if (rsdt) |r| {
|
||||
const entry_count = (r.header.length - @sizeOf(SdtHeader)) / @sizeOf(u32);
|
||||
const entries_ptr: [*]u32 = @ptrCast(&r.entries);
|
||||
|
||||
for (0..entry_count) |i| {
|
||||
const hdr: *SdtHeader = @ptrFromInt(mm.p2v(entries_ptr[i]));
|
||||
if (std.mem.eql(u8, hdr.signature[0..], signature)) {
|
||||
return hdr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn format_table(hdr: *SdtHeader, phys_addr: u64) void {
|
||||
std.log.info("acpi: {s} 0x{x:0>16} {x:0>6} (v{d:0>2} {s} {s} {x:0>8} {s} {x:0>8})", .{
|
||||
hdr.signature,
|
||||
phys_addr,
|
||||
hdr.length,
|
||||
hdr.revision,
|
||||
std.mem.trimEnd(u8, &hdr.oem_id, " "),
|
||||
std.mem.trimEnd(u8, &hdr.oem_table_id, " "),
|
||||
hdr.oem_revision,
|
||||
std.mem.asBytes(&hdr.creator_id),
|
||||
hdr.creator_revision,
|
||||
});
|
||||
}
|
||||
|
||||
fn enumerate_tables() void {
|
||||
if (xsdt) |x| {
|
||||
const entry_count = (x.header.length - @sizeOf(SdtHeader)) / @sizeOf(u64);
|
||||
const entries_ptr: [*]u64 = @ptrCast(&x.entries);
|
||||
|
||||
for (0..entry_count) |i| {
|
||||
const phys = entries_ptr[i];
|
||||
const hdr: *SdtHeader = @ptrFromInt(mm.p2v(phys));
|
||||
format_table(hdr, phys);
|
||||
}
|
||||
} else if (rsdt) |r| {
|
||||
const entry_count = (r.header.length - @sizeOf(SdtHeader)) / @sizeOf(u32);
|
||||
const entries_ptr: [*]u32 = @ptrCast(&r.entries);
|
||||
|
||||
for (0..entry_count) |i| {
|
||||
const phys: u64 = entries_ptr[i];
|
||||
const hdr: *SdtHeader = @ptrFromInt(mm.p2v(phys));
|
||||
format_table(hdr, phys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(boot_info: *pl.BootInfo) linksection(b.init) void {
|
||||
const rsdp_addr = boot_info.rsdp orelse return;
|
||||
|
||||
const rsdp: *Rsdp = @ptrFromInt(rsdp_addr);
|
||||
|
||||
if (rsdp.revision >= 2 and rsdp.xsdt_address != 0) {
|
||||
xsdt = @ptrFromInt(mm.p2v(rsdp.xsdt_address));
|
||||
std.log.info("acpi: XSDT found at {x:0>16}", .{rsdp.xsdt_address});
|
||||
} else {
|
||||
rsdt = @ptrFromInt(mm.p2v(rsdp.rsdt_address));
|
||||
std.log.info("acpi: RSDT found at {x:0>16}", .{rsdp.rsdt_address});
|
||||
}
|
||||
|
||||
enumerate_tables();
|
||||
|
||||
const fadt = find_table(fadt_signature);
|
||||
|
||||
if (fadt) |f| {
|
||||
const fadt_struct: *Fadt = @ptrCast(f);
|
||||
timer.init(fadt_struct);
|
||||
}
|
||||
|
||||
const hpet_t = find_table(hpet_signature);
|
||||
if (hpet_t) |h| {
|
||||
const hpet_struct: *HpetTable = @ptrCast(h);
|
||||
hpet.init(hpet_struct);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
const b = @import("base");
|
||||
const std = @import("std");
|
||||
const acpi = b.pl.acpi;
|
||||
const ke = b.ke;
|
||||
const arch = @import("arch");
|
||||
|
||||
var pm_timer: ke.TimeCounter = .{
|
||||
.name = "ACPI",
|
||||
.quality = 25,
|
||||
.frequency = 3579545, // 3.579545 MHz
|
||||
.read_count = read_timer,
|
||||
.mask = std.math.maxInt(u24),
|
||||
.p = 0,
|
||||
.n = 0,
|
||||
};
|
||||
|
||||
var timer_gas: acpi.Gas = undefined;
|
||||
|
||||
pub fn init(fadt: *acpi.Fadt) linksection(b.init) void {
|
||||
const fadt_rev = fadt.revision();
|
||||
|
||||
if (fadt.pm_tmr_len == 0 or fadt.pm_tmr_blk == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fadt_rev >= 2 and fadt.x_pm_tmr_blk.address == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
timer_gas.address_space_id = .SystemIo;
|
||||
timer_gas.address = fadt.pm_tmr_blk;
|
||||
timer_gas.access_size = 3;
|
||||
timer_gas.register_bit_width = 32;
|
||||
timer_gas.register_bit_offset = 0;
|
||||
|
||||
if (fadt_rev >= 2) {
|
||||
timer_gas = fadt.x_pm_tmr_blk;
|
||||
}
|
||||
|
||||
// Indicates whether the timer is 32-bit
|
||||
if (fadt.flags & (1 << 8) != 0) {
|
||||
pm_timer.mask = std.math.maxInt(u32);
|
||||
}
|
||||
|
||||
ke.time.register_source(&pm_timer);
|
||||
}
|
||||
|
||||
fn read_timer() u64 {
|
||||
return timer_gas.read();
|
||||
}
|
||||
@@ -2,6 +2,8 @@ const b = @import("base");
|
||||
const pl = b.pl;
|
||||
const amd64 = b.arch;
|
||||
const std = @import("std");
|
||||
const tsc = @import("tsc.zig");
|
||||
const pvclock = @import("pvclock.zig");
|
||||
|
||||
const ki = b.ke.private;
|
||||
|
||||
@@ -56,7 +58,8 @@ pub fn early_init(boot_info: *pl.BootInfo) linksection(b.init) void {
|
||||
_ = boot_info;
|
||||
}
|
||||
|
||||
pub fn late_init() void {}
|
||||
pub fn late_init(boot_info: *pl.BootInfo) linksection(b.init) void {
|
||||
pl.acpi.init(boot_info);
|
||||
|
||||
pub fn devices_init() void {}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
const amd64 = @import("arch");
|
||||
const b = @import("base");
|
||||
const std = @import("std");
|
||||
const mm = b.mm;
|
||||
const ke = b.ke;
|
||||
|
||||
const PVClock = extern struct {
|
||||
version: u32,
|
||||
pad0: u32,
|
||||
tsc_timestamp: u64,
|
||||
system_time: u64,
|
||||
tsc_to_system_mul: u32,
|
||||
tsc_shift: i8,
|
||||
flags: u8,
|
||||
pad1: [2]u8,
|
||||
};
|
||||
|
||||
var pvclock: *PVClock = undefined;
|
||||
|
||||
var pvclock_tc: ke.TimeCounter = .{
|
||||
.name = "pvclock",
|
||||
.quality = 75,
|
||||
.frequency = std.time.ns_per_s,
|
||||
.read_count = read_pvclock,
|
||||
.mask = std.math.maxInt(u64),
|
||||
.p = 0,
|
||||
.n = 0,
|
||||
};
|
||||
|
||||
var time_at_boot: u64 = 0;
|
||||
|
||||
pub fn init() linksection(b.init) void {
|
||||
const page = mm.phys.alloc();
|
||||
pvclock = @ptrFromInt(mm.p2v(page));
|
||||
|
||||
amd64.wrmsr(.KvmSystemTimeNew, page | 1);
|
||||
|
||||
time_at_boot = read_pvclock();
|
||||
|
||||
ke.time.register_source(&pvclock_tc);
|
||||
}
|
||||
|
||||
fn read_pvclock() u64 {
|
||||
while (true) {
|
||||
const ver = b.mmio_read(u32, @intFromPtr(&pvclock.version));
|
||||
if (ver & 1 != 0) continue; // Update in progress.
|
||||
|
||||
const tsc_shift = b.mmio_read(i8, @intFromPtr(&pvclock.tsc_shift));
|
||||
|
||||
const tsc_delta = amd64.rdtsc() - b.mmio_read(u64, @intFromPtr(&pvclock.tsc_timestamp));
|
||||
const shifted = if (tsc_shift >= 0)
|
||||
tsc_delta << @intCast(tsc_shift)
|
||||
else
|
||||
tsc_delta >> @intCast(-tsc_shift);
|
||||
|
||||
const ns = b.mmio_read(u64, @intFromPtr(&pvclock.system_time)) +
|
||||
(shifted * @as(u128, b.mmio_read(u32, @intFromPtr(&pvclock.tsc_to_system_mul))) >> 32);
|
||||
|
||||
if (b.mmio_read(u32, @intFromPtr(&pvclock.version)) == ver) return @as(u64, @truncate(ns)) - time_at_boot;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// Not sure if this should be in platform/pc or in kern/
|
||||
const std = @import("std");
|
||||
const amd64 = @import("arch");
|
||||
const b = @import("base");
|
||||
const ke = b.ke;
|
||||
|
||||
var tsc_timer: ke.TimeCounter = .{
|
||||
.name = "TSC",
|
||||
.quality = 100,
|
||||
.frequency = 0,
|
||||
.read_count = read_tsc,
|
||||
.mask = std.math.maxInt(u64),
|
||||
.p = 0,
|
||||
.n = 0,
|
||||
};
|
||||
|
||||
pub fn init() linksection(b.init) void {
|
||||
const features = amd64.cpu_features;
|
||||
|
||||
if (!features.invariant_tsc) {
|
||||
std.log.info("tsc: invariant TSC not supported (get a new PC)", .{});
|
||||
if (ke.time.best()) |best| {
|
||||
std.log.info("pc: using {s} as timecounter source", .{best.name});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
const tsc_freq = (cpuid_state.ebx / cpuid_state.eax) * cpuid_state.ecx;
|
||||
std.log.info("tsc: TSC frequency determined via CPUID: {} Hz", .{tsc_freq});
|
||||
tsc_timer.frequency = tsc_freq;
|
||||
|
||||
ke.time.register_source(&tsc_timer);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback to the other best time source to calibrate.
|
||||
const best = ke.time.best() orelse return;
|
||||
|
||||
const calib_cost_runs = 5;
|
||||
var calib_cost: u64 = 0;
|
||||
|
||||
// Calculate how many ticks it costs to call tc_sleep.
|
||||
for (0..calib_cost_runs) |_| {
|
||||
const start = amd64.rdtsc();
|
||||
ke.time.sleep(0);
|
||||
const end = amd64.rdtsc();
|
||||
calib_cost += (end - start);
|
||||
}
|
||||
|
||||
calib_cost /= calib_cost_runs;
|
||||
|
||||
const runs = 5;
|
||||
const calib_time = 10 * std.time.ns_per_ms;
|
||||
|
||||
// Sleep for 10ms `runs` times and measure the TSC frequency, then average the middle values to get a stable estimate.
|
||||
var freqs: [runs]u64 = undefined;
|
||||
|
||||
for (0..runs) |i| {
|
||||
const start = amd64.rdtsc();
|
||||
ke.time.sleep(calib_time);
|
||||
const end = amd64.rdtsc();
|
||||
freqs[i] = (end - start - calib_cost) * (std.time.ns_per_s / calib_time);
|
||||
}
|
||||
|
||||
// Sort and average middle values, discarding min and max.
|
||||
std.mem.sort(u64, &freqs, {}, std.sort.asc(u64));
|
||||
var sum: u64 = 0;
|
||||
for (freqs[1 .. runs - 1]) |f| sum += f;
|
||||
|
||||
tsc_timer.frequency = sum / (runs - 2);
|
||||
|
||||
ke.time.register_source(&tsc_timer);
|
||||
|
||||
std.log.info("pc: TSC frequency calibrated using {s}: {}.{} MHz", .{ best.name, tsc_timer.frequency / 1_000_000, (tsc_timer.frequency % 1_000_000) / 1000 });
|
||||
}
|
||||
|
||||
fn read_tsc() u64 {
|
||||
return amd64.rdtsc();
|
||||
}
|
||||
@@ -10,6 +10,8 @@ else if (@hasDecl(config, "CONFIG_ARCH_UM"))
|
||||
else
|
||||
@compileError("unsupported architecture");
|
||||
|
||||
pub const acpi = @import("acpi/root.zig");
|
||||
|
||||
/// Early platform-specific initialization, called before anything else.
|
||||
pub inline fn early_init(boot_info: *BootInfo) void {
|
||||
return impl.early_init(boot_info);
|
||||
@@ -17,8 +19,8 @@ pub inline fn early_init(boot_info: *BootInfo) void {
|
||||
|
||||
/// Late platform init. Memory allocator is available at this point.
|
||||
/// SMP bringup must be done after this returns.
|
||||
pub inline fn late_init() void {
|
||||
return impl.late_init();
|
||||
pub inline fn late_init(boot_info: *BootInfo) void {
|
||||
return impl.late_init(boot_info);
|
||||
}
|
||||
|
||||
/// Write a single character to the debug console.
|
||||
|
||||
@@ -39,6 +39,14 @@ pub fn tib(comptime N: u32) usize {
|
||||
return gib(N) * 1024;
|
||||
}
|
||||
|
||||
pub inline fn mmio_read(comptime T: type, addr: usize) T {
|
||||
return @as(*volatile T, @ptrFromInt(addr)).*;
|
||||
}
|
||||
|
||||
pub inline fn mmio_write(comptime T: type, addr: usize, value: T) void {
|
||||
@as(*volatile T, @ptrFromInt(addr)).* = value;
|
||||
}
|
||||
|
||||
pub const std_options = std.Options{
|
||||
.logFn = ke.log.log,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user