diff --git a/build.zig b/build.zig index 2845827..14e8222 100644 --- a/build.zig +++ b/build.zig @@ -236,6 +236,13 @@ fn addKernel(b: *std.Build, plat: config.Platform, optimize: std.builtin.Optimiz else => {}, } + switch (plat.arch) { + .x86_64 => { + kernel.root_module.addAssemblyFile(b.path("src/kern/amd64/locore.s")); + }, + else => {}, + } + kernel.linker_script = b.path(b.fmt("build/linker-scripts/{s}.lds", .{@tagName(plat.arch)})); } else { kernel.linker_script = b.path("build/linker-scripts/uml.lds"); diff --git a/src/arch/amd64/cpu.zig b/src/arch/amd64/cpu.zig deleted file mode 100644 index ab143f4..0000000 --- a/src/arch/amd64/cpu.zig +++ /dev/null @@ -1,447 +0,0 @@ -//! 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 rflags() u64 { - var flags: u64 = undefined; - asm volatile ("pushfq; pop %[flags]" - : [flags] "=r" (flags), - ); - return flags; -} - -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 inline fn read_cr(comptime n: u2) u64 { - comptime std.debug.assert(n != 1); - return asm volatile (std.fmt.comptimePrint("mov %%cr{}, %[ret]", .{n}) - : [ret] "=r" (-> u64), - ); -} - -pub inline fn write_cr(comptime n: u2, value: u64) void { - comptime std.debug.assert(n != 1); - asm volatile (std.fmt.comptimePrint("mov %[value], %%cr{}", .{n}) - : - : [value] "r" (value), - ); -} - -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, - pge: 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 pge = 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; - pge = edx.pge; - } - - 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, - .pge = pge, - }; -} diff --git a/src/arch/amd64/root.zig b/src/arch/amd64/root.zig index d7bff18..8a69adc 100644 --- a/src/arch/amd64/root.zig +++ b/src/arch/amd64/root.zig @@ -1,2 +1,575 @@ -pub const cpu = @import("cpu.zig"); +//! Wrappers over amd64 CPU definitions and CPUID. +const std = @import("std"); + pub const name = "amd64"; + +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 IrqFrame = extern 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 const RFlags = packed struct(u64) { + carry: bool, + _reserved0: u1, + parity: bool, + _reserved1: u1, + auxiliary: bool, + _reserved2: u1, + zero: bool, + sign: bool, + trap: bool, + interrupt_enable: bool, + direction: bool, + overflow: bool, + iopl: u2, + nested_task: bool, + _reserved3: u1, + resume_: bool, + virtual_8086_mode: bool, + alignment_check: bool, + virtual_interrupt: bool, + virtual_interrupt_pending: bool, + id: bool, + _reserved4: u42, +}; + +pub inline fn rflags() RFlags { + var flags: u64 = undefined; + asm volatile ("pushfq; pop %[flags]" + : [flags] "=r" (flags), + ); + return @bitCast(flags); +} + +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 Cr0 = packed struct(u64) { + pe: bool, + mp: bool, + em: bool, + ts: bool, + et: bool, + ne: bool, + reserved: u11, + wp: bool, + reserved2: u1, + am: bool, + reserved3: u10, + nw: bool, + cd: bool, + pg: bool, + reserved4: u32, +}; + +pub const Cr4 = packed struct(u64) { + vme: bool, + pvi: bool, + tsd: bool, + de: bool, + pse: bool, + pae: bool, + mce: bool, + pge: bool, + pce: bool, + osfxr: bool, + osxmmEex: bool, + umip: bool, + la57: bool, + reserved: u4, + fsgsbase: bool, + pcide: bool, + osxsave: bool, + reserve2: u1, + smep: bool, + smap: bool, + pke: bool, + cet: bool, +}; + +pub inline fn read_cr(comptime n: u2) u64 { + comptime std.debug.assert(n != 1); + return asm volatile (std.fmt.comptimePrint("mov %%cr{}, %[ret]", .{n}) + : [ret] "=r" (-> u64), + ); +} + +pub inline fn write_cr(comptime n: u2, value: u64) void { + comptime std.debug.assert(n != 1); + asm volatile (std.fmt.comptimePrint("mov %[value], %%cr{}", .{n}) + : + : [value] "r" (value), + ); +} + +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, + pge: 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 pge = 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; + pge = edx.pge; + } + + 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, + .pge = pge, + }; +} + +pub const TssDescriptor = extern struct { + length: u16 align(1), + base_low: u16 align(1), + base_mid: u8 align(1), + access: u8 align(1), + flags_limit_high: u8 align(1), + base_high: u8 align(1), + base_upper: u32 align(1), + reserved: u32 align(1), +}; + +pub const Tss = extern struct { + reserved1: u32 align(1), + rsp: [3]usize align(1), + reserved2: u64 align(1), + ist: [7]usize align(1), + reserved3: u64 align(1), + reserved4: u16 align(1), + iopb: u16 align(1), +}; + +pub const Gdtr = extern struct { + limit: u16 align(1), + base: usize align(1), +}; + +pub const Idtr = extern struct { + limit: u16 align(1), + base: usize align(1), +}; + +pub const IdtEntry = extern struct { + offset_low: u16 align(1), + selector: u16 align(1), + ist: u8 align(1), + type_attr: u8 align(1), + offset_mid: u16 align(1), + offset_high: u32 align(1), + reserved: u32 align(1), + + pub const Attr = enum(u8) { + InterruptGate = 0x8E, + TrapGate = 0x8F, + }; + + pub fn init(cs: u16, ist: u8, attr: Attr, offset: usize) @This() { + return .{ + .offset_low = @truncate(offset), + .selector = cs, + .ist = ist, + .type_attr = @intFromEnum(attr), + .offset_mid = @truncate(offset >> 16), + .offset_high = @truncate(offset >> 32), + .reserved = 0, + }; + } +}; diff --git a/src/kern/amd64/impl.zig b/src/kern/amd64/impl.zig index 89817d8..df673b1 100644 --- a/src/kern/amd64/impl.zig +++ b/src/kern/amd64/impl.zig @@ -5,6 +5,8 @@ const amd64 = b.arch; const std = @import("std"); +pub const early_init = @import("init.zig").early_init; + pub const Cpu = struct { self_ptr: *Cpu, percpu_offset: usize, @@ -122,15 +124,15 @@ pub inline fn set_hardware_ipl(ipl: ke.Ipl) void { } pub inline fn enable_interrupts() void { - amd64.cpu.sti(); + amd64.sti(); } pub inline fn disable_interrupts() bool { - const rflags = amd64.cpu.rflags(); + const ie = amd64.rflags().interrupt_enable; - amd64.cpu.cli(); + amd64.cli(); - return rflags & (1 << 9) != 0; + return ie; } pub inline fn restore_interrupts(state: bool) void { diff --git a/src/kern/amd64/init.zig b/src/kern/amd64/init.zig new file mode 100644 index 0000000..c46ff95 --- /dev/null +++ b/src/kern/amd64/init.zig @@ -0,0 +1,37 @@ +const amd64 = @import("arch"); +const int = @import("int.zig"); +const b = @import("base"); + +var gdt = extern struct { + entries: [9]u64 align(1), + tss: amd64.TssDescriptor align(1), +}{ + .entries = .{ + 0, // null + 0x00009a000000ffff, // code16 + 0x000093000000ffff, // data16 + 0x00cf9a000000ffff, // code32 + 0x00cf93000000ffff, // data32 + 0x00af9b000000ffff, // kernel code64 + 0x00af93000000ffff, // kernel data64 + 0x00aff3000000ffff, // user code64 + 0x00affb000000ffff, // user data64 + }, + + .tss = undefined, +}; + +extern fn gdt_load(gdtr: *const amd64.Gdtr) callconv(.{ .x86_64_sysv = .{} }) void; + +pub fn early_init() linksection(b.init) void { + const gdtr: amd64.Gdtr = .{ + .limit = @sizeOf(@TypeOf(gdt)) - 1, + .base = @intFromPtr(&gdt), + }; + + gdt_load(&gdtr); + + int.init(); + + asm volatile ("int3"); +} diff --git a/src/kern/amd64/int.zig b/src/kern/amd64/int.zig new file mode 100644 index 0000000..1c6571d --- /dev/null +++ b/src/kern/amd64/int.zig @@ -0,0 +1,73 @@ +const amd64 = @import("arch"); +const std = @import("std"); +const b = @import("base"); + +extern const __interrupt_vectors: [256]usize; + +var idt: [256]amd64.IdtEntry align(16) = undefined; + +const exception_msg = [32][]const u8{ + "Divide Error", + "Debug", + "Non-Maskable Interrupt", + "Breakpoint", + "Overflow", + "Bound Range Exceeded", + "Invalid Opcode", + "Device Not Available", + "Double Fault", + "Coprocessor Segment Overrun", + "Invalid TSS", + "Segment Not Present", + "Stack-Segment Fault", + "General Protection Fault", + "Page Fault", + "???", + "x87 Floating-Point Exception", + "Alignment Check", + "Machine Check", + "SIMD Floating-Point Exception", + "Virtualization Exception", + "Control Protection Exception", + "???", + "???", + "???", + "???", + "???", + "???", + "???", + "???", + "???", + "Security Exception", +}; + +export fn isr_handler_main(frame: *const amd64.IrqFrame) callconv(.{ .x86_64_sysv = .{} }) void { + if (frame.intno < 32) { + std.log.info("Unhandled exception: 0x{x} ({s}), err=0x{x}, pc=0x{x}", .{ frame.intno, exception_msg[frame.intno], frame.errcode, frame.rip }); + std.log.info("RAX=0x{x:0>16} RBX=0x{x:0>16} RCX=0x{x:0>16} RDX=0x{x:0>16}", .{ frame.rax, frame.rbx, frame.rcx, frame.rdx }); + std.log.info("RSI=0x{x:0>16} RDI=0x{x:0>16} RBP=0x{x:0>16} RSP=0x{x:0>16}", .{ frame.rsi, frame.rdi, frame.rbp, frame.rsp }); + std.log.info("R8= 0x{x:0>16} R9=0x{x:0>16} R10=0x{x:0>16} R11=0x{x:0>16}", .{ frame.r8, frame.r9, frame.r10, frame.r11 }); + std.log.info("R12=0x{x:0>16} R13=0x{x:0>16} R14=0x{x:0>16} R15=0x{x:0>16}", .{ frame.r12, frame.r13, frame.r14, frame.r15 }); + const cr2 = amd64.read_cr(2); + const cr3 = amd64.read_cr(3); + const rflags = amd64.rflags(); + std.log.info("CR2=0x{x:0>16} CR3=0x{x:0>16} RFLAGS={x}", .{ cr2, cr3, @as(u64, @bitCast(rflags)) }); + + @panic("Unhandled exception"); + } +} + +extern fn idt_load(idt_ptr: *const amd64.Idtr) callconv(.{ .x86_64_sysv = .{} }) void; + +pub fn init() linksection(b.init) void { + const idtr: amd64.Idtr = .{ + .limit = @sizeOf(@TypeOf(idt)) - 1, + .base = @intFromPtr(&idt), + }; + + for (&idt, 0..256) |*entry, i| { + entry.* = .init(0x28, 0, .InterruptGate, __interrupt_vectors[i]); + } + + idt_load(&idtr); +} diff --git a/src/kern/amd64/locore.s b/src/kern/amd64/locore.s new file mode 100644 index 0000000..0b9fc7b --- /dev/null +++ b/src/kern/amd64/locore.s @@ -0,0 +1,133 @@ +.altmacro +.code64 + +.section .text +.globl gdt_load +gdt_load: + lgdt (%rdi) + + /* Set ss, ds and es to data segment */ + mov $0x30, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %ss + + /* Save return address */ + pop %rdi + + /* Push CS so the CPU will pop it off with retfq */ + mov $0x28, %rax + push %rax + push %rdi + lretq + + +.globl idt_load +idt_load: + lidt (%rdi) + ret + +.macro pushall + push %rax + push %rbx + push %rcx + push %rdx + push %rbp + push %rdi + push %rsi + push %r8 + push %r9 + push %r10 + push %r11 + push %r12 + push %r13 + push %r14 + push %r15 +.endm + +.macro popall + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %r11 + pop %r10 + pop %r9 + pop %r8 + pop %rsi + pop %rdi + pop %rbp + pop %rdx + pop %rcx + pop %rbx + pop %rax +.endm + +.extern isr_handler_main +isr_handler: + cld + pushall + mov %rsp, %rdi + call isr_handler_main + popall + cli + add $16, %rsp + iretq + +.macro INTERRUPT_NOERR num + .globl isr\num + isr\num : + pushq $0 + pushq $\num + jmp isr_handler +.endm + +.macro INTERRUPT_ERR num + .globl isr\num + isr\num : + pushq $\num + jmp isr_handler +.endm + +INTERRUPT_NOERR 0 +INTERRUPT_NOERR 1 +INTERRUPT_NOERR 2 +INTERRUPT_NOERR 3 +INTERRUPT_NOERR 4 +INTERRUPT_NOERR 5 +INTERRUPT_NOERR 6 +INTERRUPT_NOERR 7 +INTERRUPT_ERR 8 +INTERRUPT_NOERR 9 +INTERRUPT_ERR 10 +INTERRUPT_ERR 11 +INTERRUPT_ERR 12 +INTERRUPT_ERR 13 +INTERRUPT_ERR 14 +INTERRUPT_NOERR 15 +INTERRUPT_NOERR 16 +INTERRUPT_ERR 17 +INTERRUPT_NOERR 18 +INTERRUPT_NOERR 19 +INTERRUPT_NOERR 20 +INTERRUPT_NOERR 21 +INTERRUPT_NOERR 22 +INTERRUPT_NOERR 23 +INTERRUPT_NOERR 24 +INTERRUPT_NOERR 25 +INTERRUPT_NOERR 26 +INTERRUPT_NOERR 27 +INTERRUPT_NOERR 28 +INTERRUPT_NOERR 29 +INTERRUPT_ERR 30 +INTERRUPT_NOERR 31 + +.rept 224 +INTERRUPT_NOERR %32+\+ +.endr + +.global __interrupt_vectors +__interrupt_vectors: +.rept 256 +.quad isr\()\+ +.endr diff --git a/src/kern/init.zig b/src/kern/init.zig index 35dd360..00aa9e1 100644 --- a/src/kern/init.zig +++ b/src/kern/init.zig @@ -11,6 +11,7 @@ var stack: [8192]u8 align(16) = undefined; pub fn init(boot_info: *pl.BootInfo) linksection(b.init) void { std.log.info("hello, world", .{}); + ki.impl.early_init(); pl.early_init(); ki.cpu.init_cpu(); diff --git a/src/platform/pc/impl.zig b/src/platform/pc/impl.zig index 38d5b16..f0c1f68 100644 --- a/src/platform/pc/impl.zig +++ b/src/platform/pc/impl.zig @@ -3,35 +3,34 @@ const amd64 = b.arch; const std = @import("std"); const ki = b.ke.private; -const cpu = amd64.cpu; pub const name = "PC"; const com1 = 0x3F8; fn com1_init() linksection(b.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); + amd64.outb(com1 + 0x3, 0x80); + amd64.outb(com1 + 0x0, 0x0c); + amd64.outb(com1 + 0x1, 0x00); + amd64.outb(com1 + 0x3, 0x03); + amd64.outb(com1 + 0x2, 0xc7); + amd64.outb(com1 + 0x4, 0x00); } fn com1_write(c: u8) void { - while ((cpu.inb(com1 + 0x5) & 0x20) == 0) {} + while ((amd64.inb(com1 + 0x5) & 0x20) == 0) {} if (c == '\n') { - cpu.outb(com1, '\r'); + amd64.outb(com1, '\r'); } - cpu.outb(com1, c); + amd64.outb(com1, c); } pub fn early_init() linksection(b.init) void { com1_init(); - cpu.detect_cpu_features(); + amd64.detect_cpu_features(); - const f = cpu.cpu_features; + const f = amd64.cpu_features; std.log.info("amd64/cpu: {s}", .{&f.brand_string}); std.log.info("amd64/cpu: vendor={s} x2apic={} la57={} nx={} pcid={} pge={} smap={} smep={} pdpe1g={} invtsc={} xsave={} fxsave={} tsc-deadline={}", .{