From 88cdefe9296911e4103fb8eff0cbf1d976c39ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Gr=C3=A9goire?= Date: Sun, 29 Mar 2026 21:01:39 -0400 Subject: [PATCH] feat(amd64): cpu stuff --- build/run.zig | 2 +- src/kern/init.zig | 2 +- src/platform/amd64/cpu.zig | 420 ++++++++++++++++++++++++++++++++++++ src/platform/amd64/impl.zig | 52 ++++- 4 files changed, 467 insertions(+), 9 deletions(-) create mode 100644 src/platform/amd64/cpu.zig diff --git a/build/run.zig b/build/run.zig index 05fedcc..587f29f 100644 --- a/build/run.zig +++ b/build/run.zig @@ -52,7 +52,7 @@ pub fn addRun(b: *std.Build, kernel: *std.Build.Step.Compile, plat: config.Platf switch (plat.arch) { .x86_64 => { - qemu.addArgs(&.{ "-debugcon", "stdio" }); + qemu.addArgs(&.{ "-serial", "stdio" }); if (!debug) qemu.addArgs(&.{ "-enable-kvm", "-cpu", "host" }); }, diff --git a/src/kern/init.zig b/src/kern/init.zig index b414a8e..17c0236 100644 --- a/src/kern/init.zig +++ b/src/kern/init.zig @@ -9,6 +9,7 @@ var thread0: ke.Thread = undefined; var stack: [8192]u8 align(16) = undefined; pub fn init(boot_info: *pl.BootInfo) linksection(b.init) void { + std.log.info("hello, world", .{}); pl.early_init(); ki.bootstrap_cpu.init(&thread0); @@ -16,7 +17,6 @@ pub fn init(boot_info: *pl.BootInfo) linksection(b.init) void { thread0.priority = 0; thread0.priority_class = .Idle; - std.log.info("hello, world", .{}); std.log.info("Zag for {s}, cmdline is \"{?s}\"", .{ pl.name, boot_info.cmdline }); ex.private.init(boot_info); diff --git a/src/platform/amd64/cpu.zig b/src/platform/amd64/cpu.zig new file mode 100644 index 0000000..d6f09c5 --- /dev/null +++ b/src/platform/amd64/cpu.zig @@ -0,0 +1,420 @@ +//! Wrappers over amd64 CPU instructions and CPUID feature detection. +const std = @import("std"); + +pub const Msr = enum(u32) { + /// IA32_APIC_BASE + LapicBase = 0x1B, + /// IA32_FS_BASE + FsBase = 0xC000_0100, + /// IA32_GS_BASE + GsBase = 0xC000_0101, + /// IA32_KERNEL_GS_BASE + KernelGsBase = 0xC000_0102, + /// IA32_EFER + Efer = 0xC000_0080, + /// IA32_STAR + Star = 0xC000_0081, + /// IA32_LSTAR + LStar = 0xC000_0082, +}; + +pub const InterruptFrame = packed struct { + r15: u64, + r14: u64, + r13: u64, + r12: u64, + r11: u64, + r10: u64, + r9: u64, + r8: u64, + rsi: u64, + rdi: u64, + rbp: u64, + rdx: u64, + rcx: u64, + rbx: u64, + rax: u64, + intno: u64, + errcode: u64, + rip: u64, + cs: u64, + rflags: u64, + rsp: u64, + ss: u64, +}; + +pub inline fn outb(port: u16, value: u8) void { + asm volatile ("outb %[value], %[port]" + : + : [value] "{al}" (value), + [port] "{dx}" (port), + ); +} + +pub inline fn outw(port: u16, value: u16) void { + asm volatile ("outw %[value], %[port]" + : + : [value] "{ax}" (value), + [port] "{dx}" (port), + ); +} + +pub inline fn outl(port: u16, value: u32) void { + asm volatile ("outl %[value], %[port]" + : + : [value] "{eax}" (value), + [port] "{dx}" (port), + ); +} + +pub inline fn inb(port: u16) u8 { + return asm volatile ("inb %[port], %[ret]" + : [ret] "={al}" (-> u8), + : [port] "{dx}" (port), + ); +} + +pub inline fn inw(port: u16) u16 { + return asm volatile ("inw %[port], %[ret]" + : [ret] "={ax}" (-> u16), + : [port] "{dx}" (port), + ); +} + +pub inline fn inl(port: u16) u32 { + return asm volatile ("inl %[port], %[ret]" + : [ret] "={eax}" (-> u32), + : [port] "{dx}" (port), + ); +} + +pub inline fn rdmsr(msr: Msr) u64 { + var low: u32 = undefined; + var high: u32 = undefined; + asm volatile ("rdmsr" + : [low] "={eax}" (low), + [high] "={edx}" (high), + : [msr] "{ecx}" (@intFromEnum(msr)), + ); + return @as(u64, high) << 32 | low; +} + +pub inline fn wrmsr(msr: Msr, value: u64) void { + asm volatile ("wrmsr" + : + : [low] "{eax}" (@as(u32, @truncate(value))), + [high] "{edx}" (@as(u32, @truncate(value >> 32))), + [msr] "{ecx}" (@intFromEnum(msr)), + ); +} + +pub inline fn invlpg(addr: u64) void { + asm volatile ("invlpg (%[addr])" + : + : [addr] "r" (addr), + : .{ .memory = true }); +} + +pub inline fn sti() void { + asm volatile ("sti"); +} + +pub inline fn cli() void { + asm volatile ("cli"); +} + +pub inline fn hlt() void { + asm volatile ("hlt"); +} + +pub inline fn rdtsc() u64 { + var low: u32 = undefined; + var high: u32 = undefined; + asm volatile ("rdtsc" + : [low] "={eax}" (low), + [high] "={edx}" (high), + ); + return @as(u64, high) << 32 | low; +} + +pub const CpuidResult = struct { eax: u32, ebx: u32, ecx: u32, edx: u32 }; + +pub inline fn cpuid(leaf: u32, subleaf: u32) CpuidResult { + var eax: u32 = undefined; + var ebx: u32 = undefined; + var ecx: u32 = undefined; + var edx: u32 = undefined; + asm volatile ("cpuid" + : [eax] "={eax}" (eax), + [ebx] "={ebx}" (ebx), + [ecx] "={ecx}" (ecx), + [edx] "={edx}" (edx), + : [leaf] "{eax}" (leaf), + [subleaf] "{ecx}" (subleaf), + ); + return .{ .eax = eax, .ebx = ebx, .ecx = ecx, .edx = edx }; +} + +pub const CpuidRequest = union(enum) { + vendor_info, + feature_info, + extended_info, + highest_extended_function, + extended_features: ExtendedFeaturesSubLeaf, + brand_string: BrandStringPart, + power_management_info, + + pub const ExtendedFeaturesSubLeaf = enum(u32) { + first = 0, + second = 1, + third = 3, + }; + + pub const BrandStringPart = enum(u32) { + 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 }, + }; + return cpuid(leaf, subleaf); + } +}; + +const FeatureInfoEcx = packed struct(u32) { + sse3: bool, + pclmul: bool, + dtes64: bool, + monitor: bool, + ds_cpl: bool, + vmx: bool, + smx: bool, + est: bool, + tm2: bool, + ssse3: bool, + cnxt_id: bool, + sdbg: bool, + fma: bool, + cx16: bool, + xtpr: bool, + pdcm: bool, + _reserved: u1, + pcid: bool, + dca: bool, + sse4_1: bool, + sse4_2: bool, + x2apic: bool, + movbe: bool, + popcnt: bool, + tsc_deadline: bool, + aes: bool, + xsave: bool, + osxsave: bool, + avx: bool, + f16c: bool, + rdrand: bool, + hypervisor: bool, +}; + +const FeatureInfoEdx = packed struct(u32) { + fpu: bool, + vme: bool, + de: bool, + pse: bool, + tsc: bool, + msr: bool, + pae: bool, + mce: bool, + cx8: bool, + apic: bool, + _reserved0: u1, + sep: bool, + mtrr: bool, + pge: bool, + mca: bool, + cmov: bool, + pat: bool, + pse36: bool, + psn: bool, + clfsh: bool, + _reserved1: u1, + ds: bool, + acpi: bool, + mmx: bool, + fxsave: bool, + sse: bool, + sse2: bool, + ignore: u5, +}; + +const ExtendedFeaturesEcx = packed struct(u32) { + _reserved0: u16, + la57: bool, + _reserved1: u5, + rdpid: bool, + _reserved2: u9, +}; + +const ExtendedFeaturesEbx = packed struct(u32) { + ignore: u7, + smep: bool, + ignore2: u12, + smap: bool, + ignore3: u11, +}; + +const ExtendedProcessorInfoEdx = packed struct(u32) { + ignore: u20, + nx: bool, + ignore2: u5, + pdpe1gb: bool, + ignore3: u5, +}; + +const PowerManagementInfoEdx = packed struct(u32) { + ignore: u8, + invtsc: bool, + ignore2: u23, +}; + +fn assert_bit(comptime T: type, comptime field_name: []const u8, comptime expected_bit: u5) void { + var v: T = @bitCast(@as(u32, 0)); + @field(v, field_name) = true; + const raw: u32 = @bitCast(v); + const want = (@as(u32, 1) << expected_bit); + if (raw != want) @compileError("bit mismatch for field " ++ field_name); +} + +comptime { + assert_bit(FeatureInfoEcx, "pcid", 17); + assert_bit(FeatureInfoEcx, "x2apic", 21); + assert_bit(FeatureInfoEcx, "tsc_deadline", 24); + assert_bit(FeatureInfoEcx, "xsave", 26); + assert_bit(ExtendedFeaturesEcx, "la57", 16); + assert_bit(ExtendedProcessorInfoEdx, "nx", 20); + assert_bit(ExtendedProcessorInfoEdx, "pdpe1gb", 26); + assert_bit(PowerManagementInfoEdx, "invtsc", 8); + assert_bit(FeatureInfoEdx, "fxsave", 24); + assert_bit(ExtendedFeaturesEbx, "smap", 20); + assert_bit(ExtendedFeaturesEbx, "smep", 7); +} + +pub const CpuFeatures = struct { + x2apic: bool, + five_level_paging: bool, + gib_pages: bool, + tsc_deadline: bool, + fxsave: bool, + xsave: bool, + invariant_tsc: bool, + nx: bool, + pcid: bool, + smap: bool, + smep: bool, + vendor_string: [12]u8, + brand_string: [48]u8, +}; + +pub var cpu_features: CpuFeatures = undefined; + +pub fn detect_cpu_features() void { + const vendor_info = CpuidRequest.execute(.vendor_info); + const max_ext = CpuidRequest.execute(.highest_extended_function).eax; + + const max_basic = vendor_info.eax; + + var x2apic = false; + var five_level_paging = false; + var gib_pages = false; + var tsc_deadline = false; + var xsave = false; + var fxsave = false; + var invtsc = false; + var nx = false; + var pcid = false; + var smap = false; + var smep = false; + + var vendor_string: [12]u8 = undefined; + var brand_string: [48]u8 = undefined; + + std.mem.writeInt(u32, vendor_string[0..4], vendor_info.ebx, .little); + std.mem.writeInt(u32, vendor_string[4..8], vendor_info.edx, .little); + std.mem.writeInt(u32, vendor_string[8..12], vendor_info.ecx, .little); + + if (max_basic >= 0x1) { + const r = CpuidRequest.execute(.feature_info); + const ecx: FeatureInfoEcx = @bitCast(r.ecx); + const edx: FeatureInfoEdx = @bitCast(r.edx); + + x2apic = ecx.x2apic; + tsc_deadline = ecx.tsc_deadline; + xsave = ecx.xsave; + pcid = ecx.pcid; + fxsave = edx.fxsave; + } + + if (max_basic >= 0x7) { + const r = CpuidRequest.execute(.{ .extended_features = .first }); + const ecx: ExtendedFeaturesEcx = @bitCast(r.ecx); + const ebx: ExtendedFeaturesEbx = @bitCast(r.ebx); + smap = ebx.smap; + smep = ebx.smep; + five_level_paging = ecx.la57; + } + + if (max_ext >= 0x80000001) { + const r = CpuidRequest.execute(.extended_info); + const edx: ExtendedProcessorInfoEdx = @bitCast(r.edx); + gib_pages = edx.pdpe1gb; + nx = edx.nx; + } + + if (max_ext >= 0x80000004) { + const parts = [_]CpuidResult{ + CpuidRequest.execute(.{ .brand_string = .part0 }), + CpuidRequest.execute(.{ .brand_string = .part1 }), + CpuidRequest.execute(.{ .brand_string = .part2 }), + }; + + var off: usize = 0; + for (parts) |p| { + for ([_]u32{ p.eax, p.ebx, p.ecx, p.edx }) |reg| { + const dst: *[4]u8 = @ptrCast(brand_string[off..].ptr); + std.mem.writeInt(u32, dst, reg, .little); + off += 4; + } + } + } + + if (max_ext >= 0x80000007) { + const r = CpuidRequest.execute(.power_management_info); + const edx: PowerManagementInfoEdx = @bitCast(r.edx); + invtsc = edx.invtsc; + } + + cpu_features = .{ + .x2apic = x2apic, + .five_level_paging = five_level_paging, + .gib_pages = gib_pages, + .tsc_deadline = tsc_deadline, + .fxsave = fxsave, + .xsave = xsave, + .invariant_tsc = invtsc, + .nx = nx, + .pcid = pcid, + .smap = smap, + .smep = smep, + .vendor_string = vendor_string, + .brand_string = brand_string, + }; +} diff --git a/src/platform/amd64/impl.zig b/src/platform/amd64/impl.zig index bb46052..1956a05 100644 --- a/src/platform/amd64/impl.zig +++ b/src/platform/amd64/impl.zig @@ -1,9 +1,32 @@ const b = @import("base"); +const cpu = @import("cpu.zig"); const ki = b.ke.private; +const std = @import("std"); pub const name = "amd64"; -pub fn early_init() void { +const com1 = 0x3F8; + +fn com1_init() void { + cpu.outb(com1 + 0x3, 0x80); + cpu.outb(com1 + 0x0, 0x0c); + cpu.outb(com1 + 0x1, 0x00); + cpu.outb(com1 + 0x3, 0x03); + cpu.outb(com1 + 0x2, 0xc7); + cpu.outb(com1 + 0x4, 0x00); +} + +fn com1_write(c: u8) void { + while ((cpu.inb(com1 + 0x5) & 0x20) == 0) {} + if (c == '\n') { + cpu.outb(com1, '\r'); + } + cpu.outb(com1, c); +} + +pub fn early_init() linksection(b.init) void { + com1_init(); + const impl_cpu = &ki.bootstrap_cpu.impl; impl_cpu.self_ptr = impl_cpu; impl_cpu.percpu_offset = 0; @@ -15,6 +38,26 @@ pub fn early_init() void { [_] "{edx}" (@as(u32, @truncate(impl_cpu_ptr >> 32))), [_] "{ecx}" (0xC000_0101), ); + + cpu.detect_cpu_features(); + + const f = cpu.cpu_features; + + std.log.info("amd64/cpu: {s}", .{&f.brand_string}); + std.log.info("amd64/cpu: vendor={s} x2apic={} la57={} nx={} pcid={} smap={} smep={} pdpe1g={} invtsc={} xsave={} fxsave={} tsc-deadline={}", .{ + &f.vendor_string, + @intFromBool(f.x2apic), + @intFromBool(f.five_level_paging), + @intFromBool(f.nx), + @intFromBool(f.pcid), + @intFromBool(f.smap), + @intFromBool(f.smep), + @intFromBool(f.gib_pages), + @intFromBool(f.invariant_tsc), + @intFromBool(f.xsave), + @intFromBool(f.fxsave), + @intFromBool(f.tsc_deadline), + }); } pub fn late_init() void {} @@ -22,12 +65,7 @@ pub fn late_init() void {} pub fn devices_init() void {} pub fn debug_write(c: u8) void { - // Write to debugcon - asm volatile ("outb %[c], %[port]" - : - : [c] "{al}" (c), - [port] "N{dx}" (@as(u16, 0xe9)), - ); + com1_write(c); } pub fn arm_timer(_: u64) void {}