feat(amd64): add a bunch of stuff

This commit is contained in:
Loïc Grégoire
2026-04-15 19:37:24 -04:00
parent 986fe82383
commit a7cdfeb57a
4 changed files with 322 additions and 130 deletions
+102
View File
@@ -0,0 +1,102 @@
//! KVM paravirtualization support.
const std = @import("std");
const amd64 = @import("../root.zig");
pub const Features = packed struct(u32) {
clocksource: bool,
nop_io_delay: bool,
mmu_op: bool,
clocksource2: bool,
async_pf: bool,
steal_time: bool,
pv_eoi: bool,
pv_unhalt: bool,
_reserved0: u1,
pv_tlb_flush: bool,
async_pf_vmexit: bool,
pv_send_ipi: bool,
pv_poll_control: bool,
pv_sched_yield: bool,
async_pf_int: bool,
msi_ext_dest_id: bool,
hc_map_gpa_range: bool,
migration_control: bool,
_reserved1: u14,
};
pub const Info = struct {
features: Features,
};
const Msr = enum(u32) {
SystemTime = 0x4B564D01,
WallClock = 0x4B564D00,
AsyncPf = 0x4B564D02,
AsyncPfEn = 0x4B564D03,
StealTime = 0x4B564D04,
PvEoi = 0x4B564D05,
AsyncPfInt = 0x4B564D06,
_,
};
fn read_msr(msr: Msr) u64 {
return amd64.rdmsr(@intFromEnum(msr));
}
fn write_msr(msr: Msr, value: u64) void {
amd64.wrmsr(@intFromEnum(msr), value);
}
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,
};
pub const PVClockSource = struct {
page: *volatile PVClock,
time_at_boot: u64,
pub fn read(self: *PVClockSource) u64 {
while (true) {
const ver = @atomicLoad(u32, &self.page.version, .acquire);
if (ver & 1 != 0) continue;
const tsc_shift = self.page.tsc_shift;
const tsc_delta = amd64.rdtsc() - self.page.tsc_timestamp;
const shifted = if (tsc_shift >= 0)
tsc_delta << @intCast(tsc_shift)
else
tsc_delta >> @intCast(-tsc_shift);
const ns = self.page.system_time +
(shifted * @as(u128, self.page.tsc_to_system_mul) >> 32);
if (@atomicLoad(u32, &self.page.version, .acquire) == ver)
return @as(u64, @truncate(ns)) - self.time_at_boot;
}
}
pub fn init(phys_page: usize, virt_page: usize) PVClockSource {
const page: *volatile PVClock = @ptrFromInt(virt_page);
write_msr(.SystemTime, phys_page | 1);
var src = PVClockSource{ .page = page, .time_at_boot = 0 };
src.time_at_boot = src.read();
return src;
}
};
pub fn detect(highest_function: u32) Info {
if (highest_function < 0x40000001) {
return .{ .features = @bitCast(@as(u32, 0)) };
}
const r = amd64.cpuid(0x40000001, 0);
return Info{
.features = @bitCast(r.eax),
};
}
+61
View File
@@ -0,0 +1,61 @@
//! Hypervisor detection and identification.
const std = @import("std");
const amd64 = @import("root.zig");
pub const kvm = @import("hv/kvm.zig");
pub const Vendor = enum(u32) {
Unknown,
KVM,
TCG,
VMWare,
};
pub const VendorData = union(Vendor) {
Unknown: void,
KVM: kvm.Info,
TCG: void,
VMWare: void,
};
pub const Info = struct {
vendor: Vendor,
data: VendorData,
};
pub var info: ?Info = null;
const known_vendors = [_]struct {
string: *const [12:0]u8,
vendor: Vendor,
}{
.{ .string = "KVMKVMKVM\x00\x00\x00", .vendor = .KVM },
.{ .string = "TCGTCGTCG\x00\x00\x00", .vendor = .TCG },
.{ .string = "VMwareVMware", .vendor = .VMWare },
};
pub fn detect() void {
const r = amd64.CpuidRequest.execute(.HypervisorId);
var hypervisor_brand: [12]u8 = undefined;
std.mem.writeInt(u32, hypervisor_brand[0..4], r.ebx, .little);
std.mem.writeInt(u32, hypervisor_brand[4..8], r.ecx, .little);
std.mem.writeInt(u32, hypervisor_brand[8..12], r.edx, .little);
var vendor = Vendor.Unknown;
for (known_vendors) |h| {
if (std.mem.eql(u8, hypervisor_brand[0..], h.string[0..])) {
vendor = h.vendor;
break;
}
}
const data: VendorData = switch (vendor) {
.KVM => .{ .KVM = kvm.detect(r.eax) },
.Unknown, .TCG, .VMWare => .{ .Unknown = {} },
};
info = Info{
.vendor = vendor,
.data = data,
};
}
+155 -92
View File
@@ -1,6 +1,8 @@
//! Wrappers over amd64 CPU definitions and CPUID.
const std = @import("std");
pub const hypervisor = @import("hypervisor.zig");
pub const name = "amd64";
pub const Msr = enum(u32) {
@@ -24,6 +26,24 @@ pub const Msr = enum(u32) {
_,
};
pub const Efer = packed struct(u64) {
sce: bool,
reserved0: u7,
lme: bool,
reserved1: u1,
lma: bool,
nxe: bool,
svme: bool,
lmsle: bool,
ffxsr: bool,
tce: bool,
reserved2: u1,
mcommit: bool,
interruptible_wb: bool,
uaie: bool,
reserved3: u44,
};
pub const IrqFrame = extern struct {
r15: u64,
r14: u64,
@@ -173,11 +193,11 @@ pub fn pio_write(comptime T: type, port: u16, value: T) void {
pub const RFlags = packed struct(u64) {
carry: bool,
_reserved0: u1,
reserved0: u1,
parity: bool,
_reserved1: u1,
reserved1: u1,
auxiliary: bool,
_reserved2: u1,
reserved2: u1,
zero: bool,
sign: bool,
trap: bool,
@@ -186,14 +206,14 @@ pub const RFlags = packed struct(u64) {
overflow: bool,
iopl: u2,
nested_task: bool,
_reserved3: u1,
reserved3: u1,
resume_: bool,
virtual_8086_mode: bool,
alignment_check: bool,
virtual_interrupt: bool,
virtual_interrupt_pending: bool,
id: bool,
_reserved4: u42,
reserved4: u42,
};
pub inline fn rflags() RFlags {
@@ -221,17 +241,22 @@ pub const Cr0 = packed struct(u64) {
ts: bool,
et: bool,
ne: bool,
reserved: u11,
reserved0: u11,
wp: bool,
reserved2: u1,
reserved1: u1,
am: bool,
reserved3: u10,
reserved2: u10,
nw: bool,
cd: bool,
pg: bool,
reserved4: u32,
reserved3: u31,
};
comptime {
_ = Cr0;
_ = Cr4;
}
pub const Cr4 = packed struct(u64) {
vme: bool,
pvi: bool,
@@ -242,15 +267,15 @@ pub const Cr4 = packed struct(u64) {
mce: bool,
pge: bool,
pce: bool,
osfxr: bool,
osxmmEex: bool,
osfxsr: bool,
osxmmexcpt: bool,
umip: bool,
la57: bool,
reserved: u3,
reserved0: u3,
fsgsbase: bool,
pcide: bool,
osxsave: bool,
reserve2: u1,
reserved1: u1,
smep: bool,
smap: bool,
pke: bool,
@@ -292,37 +317,37 @@ pub inline fn cpuid(leaf: u32, subleaf: u32) CpuidResult {
}
pub const CpuidRequest = union(enum) {
vendor_info,
feature_info,
extended_info,
highest_extended_function,
extended_features: ExtendedFeaturesSubLeaf,
brand_string: BrandStringPart,
power_management_info,
hypervisor_id,
VendorInfo,
FeatureInfo,
ExtendedInfo,
HighestExtendedFunction,
ExtendedFeatures: ExtendedFeaturesSubLeaf,
BrandString: BrandStringPart,
PowerManagementInfo,
HypervisorId,
pub const ExtendedFeaturesSubLeaf = enum(u32) {
first = 0,
second = 1,
third = 3,
First = 0,
Second = 1,
Third = 3,
};
pub const BrandStringPart = enum(u32) {
part0 = 0x80000002,
part1 = 0x80000003,
part2 = 0x80000004,
Part0 = 0x80000002,
Part1 = 0x80000003,
Part2 = 0x80000004,
};
pub fn execute(self: CpuidRequest) CpuidResult {
const leaf, const subleaf = switch (self) {
.vendor_info => .{ 0x0, 0 },
.feature_info => .{ 0x1, 0 },
.highest_extended_function => .{ 0x80000000, 0 },
.extended_info => .{ 0x80000001, 0 },
.power_management_info => .{ 0x80000007, 0 },
.extended_features => |s| .{ 0x7, @intFromEnum(s) },
.brand_string => |p| .{ @intFromEnum(p), 0 },
.hypervisor_id => .{ 0x40000000, 0 },
.VendorInfo => .{ 0x0, 0 },
.FeatureInfo => .{ 0x1, 0 },
.HighestExtendedFunction => .{ 0x80000000, 0 },
.ExtendedInfo => .{ 0x80000001, 0 },
.PowerManagementInfo => .{ 0x80000007, 0 },
.ExtendedFeatures => |s| .{ 0x7, @intFromEnum(s) },
.BrandString => |p| .{ @intFromEnum(p), 0 },
.HypervisorId => .{ 0x40000000, 0 },
};
return cpuid(leaf, subleaf);
}
@@ -345,7 +370,7 @@ const FeatureInfoEcx = packed struct(u32) {
cx16: bool,
xtpr: bool,
pdcm: bool,
_reserved: u1,
reserved0: u1,
pcid: bool,
dca: bool,
sse4_1: bool,
@@ -374,7 +399,7 @@ const FeatureInfoEdx = packed struct(u32) {
mce: bool,
cx8: bool,
apic: bool,
_reserved0: u1,
reserved0: u1,
sep: bool,
mtrr: bool,
pge: bool,
@@ -384,44 +409,69 @@ const FeatureInfoEdx = packed struct(u32) {
pse36: bool,
psn: bool,
clfsh: bool,
_reserved1: u1,
reserved1: u1,
ds: bool,
acpi: bool,
mmx: bool,
fxsave: bool,
sse: bool,
sse2: bool,
ignore: u5,
reserved2: u5,
};
const ExtendedFeaturesEcx = packed struct(u32) {
_reserved0: u16,
reserved0: u2,
umip: bool,
reserved1: u13,
la57: bool,
_reserved1: u5,
reserved2: u5,
rdpid: bool,
_reserved2: u9,
reserved3: u9,
};
const ExtendedFeaturesEbx = packed struct(u32) {
ignore: u7,
reserved0: u7,
smep: bool,
ignore2: u12,
reserved1: u12,
smap: bool,
ignore3: u11,
reserved2: u11,
};
const ExtendedProcessorInfoEdx = packed struct(u32) {
ignore: u20,
fpu: bool,
vme: bool,
de: bool,
pse: bool,
tsc: bool,
msr: bool,
pae: bool,
mce: bool,
cx8: bool,
apic: bool,
reserved0: u1,
syscall_sysret: bool,
mtrr: bool,
pge: bool,
mca: bool,
cmov: bool,
pat: bool,
pse36: bool,
reserved1: u1,
mp: bool,
nx: bool,
ignore2: u5,
reserved2: u5,
pdpe1gb: bool,
ignore3: u5,
rdtscp: bool,
reserved3: u1,
lm: bool,
_3dnowext: bool,
_3dnow: bool,
};
const PowerManagementInfoEdx = packed struct(u32) {
ignore: u8,
reserved0: u8,
invtsc: bool,
ignore2: u23,
reserved1: u23,
};
fn assert_bit(comptime T: type, comptime field_name: []const u8, comptime expected_bit: u5) void {
@@ -446,6 +496,22 @@ comptime {
assert_bit(ExtendedFeaturesEbx, "smep", 7);
}
pub const CpuVendor = enum {
Intel,
Amd,
Hygon,
Unknown,
};
const known_vendors = [_]struct {
string: *const [12:0]u8,
vendor: CpuVendor,
}{
.{ .string = "GenuineIntel", .vendor = .Intel },
.{ .string = "AuthenticAMD", .vendor = .Amd },
.{ .string = "HygonGenuine", .vendor = .Hygon },
};
pub const CpuFeatures = struct {
x2apic: bool,
five_level_paging: bool,
@@ -459,7 +525,10 @@ pub const CpuFeatures = struct {
smap: bool,
smep: bool,
pge: bool,
vendor_string: [12]u8,
syscall: bool,
umip: bool,
vendor: CpuVendor,
family: u8,
brand_string: [48]u8,
};
@@ -474,42 +543,20 @@ pub const HypervisorInfo = struct {
highest_function: u32,
};
pub var hypervisor_info: ?HypervisorInfo = null;
pub var cpu_features: CpuFeatures = undefined;
const known_hypervisors = [_]struct {
vendor_string: *const [12:0]u8,
vendor: HypervisorVendor,
}{
.{ .vendor_string = "KVMKVMKVM\x00\x00\x00", .vendor = HypervisorVendor.KVM },
};
fn detect_hypervisor() void {
const r = CpuidRequest.execute(.hypervisor_id);
var hypervisor_brand: [12]u8 = undefined;
std.mem.writeInt(u32, hypervisor_brand[0..4], r.ebx, .little);
std.mem.writeInt(u32, hypervisor_brand[4..8], r.ecx, .little);
std.mem.writeInt(u32, hypervisor_brand[8..12], r.edx, .little);
var vendor = HypervisorVendor.Unknown;
for (known_hypervisors) |h| {
if (std.mem.eql(u8, hypervisor_brand[0..], h.vendor_string[0..])) {
vendor = h.vendor;
break;
fn detect_vendor(string: [12]u8) CpuVendor {
for (known_vendors) |v| {
if (std.mem.eql(u8, string[0..], v.string[0..])) {
return v.vendor;
}
}
hypervisor_info = HypervisorInfo{
.brand_string = hypervisor_brand,
.vendor = vendor,
.highest_function = r.eax,
};
return .Unknown;
}
pub fn detect_cpu_features() void {
const vendor_info = CpuidRequest.execute(.vendor_info);
const max_ext = CpuidRequest.execute(.highest_extended_function).eax;
const vendor_info = CpuidRequest.execute(.VendorInfo);
const max_ext = CpuidRequest.execute(.HighestExtendedFunction).eax;
const max_basic = vendor_info.eax;
@@ -525,7 +572,10 @@ pub fn detect_cpu_features() void {
var smap = false;
var smep = false;
var pge = false;
var hypervisor = false;
var hypervisor_flag = false;
var syscall = false;
var family: u8 = 0;
var umip = false;
var vendor_string: [12]u8 = undefined;
var brand_string: [48]u8 = undefined;
@@ -534,8 +584,10 @@ pub fn detect_cpu_features() void {
std.mem.writeInt(u32, vendor_string[4..8], vendor_info.edx, .little);
std.mem.writeInt(u32, vendor_string[8..12], vendor_info.ecx, .little);
const vendor = detect_vendor(vendor_string);
if (max_basic >= 0x1) {
const r = CpuidRequest.execute(.feature_info);
const r = CpuidRequest.execute(.FeatureInfo);
const ecx: FeatureInfoEcx = @bitCast(r.ecx);
const edx: FeatureInfoEdx = @bitCast(r.edx);
@@ -545,30 +597,38 @@ pub fn detect_cpu_features() void {
pcid = ecx.pcid;
fxsave = edx.fxsave;
pge = edx.pge;
hypervisor = ecx.hypervisor;
hypervisor_flag = ecx.hypervisor;
const sig = r.eax;
family = @truncate((sig >> 8) & 0xf);
if (family == 0xf) {
family +%= @truncate((sig >> 20) & 0xff);
}
}
if (max_basic >= 0x7) {
const r = CpuidRequest.execute(.{ .extended_features = .first });
const r = CpuidRequest.execute(.{ .ExtendedFeatures = .First });
const ecx: ExtendedFeaturesEcx = @bitCast(r.ecx);
const ebx: ExtendedFeaturesEbx = @bitCast(r.ebx);
smap = ebx.smap;
smep = ebx.smep;
five_level_paging = ecx.la57;
umip = ecx.umip;
}
if (max_ext >= 0x80000001) {
const r = CpuidRequest.execute(.extended_info);
const r = CpuidRequest.execute(.ExtendedInfo);
const edx: ExtendedProcessorInfoEdx = @bitCast(r.edx);
gib_pages = edx.pdpe1gb;
nx = edx.nx;
syscall = edx.syscall_sysret;
}
if (max_ext >= 0x80000004) {
const parts = [_]CpuidResult{
CpuidRequest.execute(.{ .brand_string = .part0 }),
CpuidRequest.execute(.{ .brand_string = .part1 }),
CpuidRequest.execute(.{ .brand_string = .part2 }),
CpuidRequest.execute(.{ .BrandString = .Part0 }),
CpuidRequest.execute(.{ .BrandString = .Part1 }),
CpuidRequest.execute(.{ .BrandString = .Part2 }),
};
var off: usize = 0;
@@ -582,13 +642,13 @@ pub fn detect_cpu_features() void {
}
if (max_ext >= 0x80000007) {
const r = CpuidRequest.execute(.power_management_info);
const r = CpuidRequest.execute(.PowerManagementInfo);
const edx: PowerManagementInfoEdx = @bitCast(r.edx);
invtsc = edx.invtsc;
}
if (hypervisor) {
detect_hypervisor();
if (hypervisor_flag) {
hypervisor.detect();
}
cpu_features = .{
@@ -603,9 +663,12 @@ pub fn detect_cpu_features() void {
.pcid = pcid,
.smap = smap,
.smep = smep,
.vendor_string = vendor_string,
.vendor = vendor,
.brand_string = brand_string,
.pge = pge,
.family = family,
.umip = umip,
.syscall = syscall,
};
}
+4 -38
View File
@@ -1,22 +1,11 @@
const amd64 = @import("arch");
const kvm = amd64.hypervisor.kvm;
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_src: kvm.PVClockSource = undefined;
var pvclock_tc: ke.TimeCounter = .{
.name = "pvclock",
.quality = 75,
@@ -27,35 +16,12 @@ var pvclock_tc: ke.TimeCounter = .{
.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.write_msr(.KvmSystemTimeNew, page | 1);
time_at_boot = read_pvclock();
pvclock_src = kvm.PVClockSource.init(page, mm.p2v(page));
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;
}
return pvclock_src.read();
}