Microkernel thing OS experiment (Zig ⚡)
1const std = @import("std"); 2const arch = @import("../root.zig"); 3const log = std.log.scoped(.apic); 4 5pub var lapic_timer_khz: usize = 0; 6 7// tbh every cpu will be either x2apic or not, and if xapic it will 8// have the exact same base address anyways so this is fine 9pub var singleton: LAPIC = undefined; 10 11// Must instantiate this! 12pub const LAPIC = union(enum) { 13 xapic: [*]volatile u8, 14 x2apic, 15 16 const Self = @This(); 17 18 // ID Register 19 20 pub const IdRegister = packed struct(u32) { 21 _reserved0: u24 = 0, 22 id: u8, 23 }; 24 25 pub fn getIdRegister(lapic: Self) IdRegister { 26 return @bitCast(lapic.getRegister(.lapic_id)); 27 } 28 29 // Version Register 30 pub const VersionRegister = packed struct(u32) { 31 version: u8, 32 _reserved0: u8 = 0, 33 max_lvt_entry: u8, 34 support_for_eoi_broadcast_suppression: bool, 35 _reserved1: u7 = 0, 36 }; 37 38 pub fn getVersionRegister(lapic: Self) VersionRegister { 39 return @bitCast(lapic.getRegister(.version)); 40 } 41 42 // Spurious Interrupt 43 pub const SpuriousInterruptRegister = packed struct(u32) { 44 idt_entry: u8, 45 apic_soft_enable: bool, 46 focus_processor_checking: bool = false, 47 _reserved0: u2 = 0, 48 eoi_broadcast_suppression: bool = false, 49 _reserved1: u19 = 0, 50 }; 51 52 pub fn getSpuriousInterruptRegister(lapic: Self) SpuriousInterruptRegister { 53 return @bitCast(lapic.getRegister(.spurious_vector)); 54 } 55 56 pub fn setSpuriousInterruptRegister(lapic: Self, val: SpuriousInterruptRegister) void { 57 lapic.setRegister(.spurious_vector, @bitCast(val)); 58 } 59 60 // Task Priority 61 pub const TaskPriorityRegister = packed struct(u32) { 62 priority_sub_class: u4, 63 priority_class: u4, 64 _reserved0: u24 = 0, 65 }; 66 67 pub fn getTaskPriorityRegister(lapic: Self) TaskPriorityRegister { 68 return @bitCast(lapic.getRegister(.task_priority)); 69 } 70 71 pub fn setTaskPriorityRegister(lapic: Self, val: TaskPriorityRegister) void { 72 lapic.setRegister(.task_priority, @bitCast(val)); 73 } 74 75 // Initial Count 76 pub fn setInitialCountRegister(lapic: Self, count: u32) void { 77 lapic.setRegister(.initial_count, count); 78 } 79 80 // Current Count 81 pub fn getCurrentCountRegister(lapic: Self) u32 { 82 return lapic.getRegister(.current_count); 83 } 84 85 // Divide Configuration 86 pub const DivideConfigurationRegister = enum(u32) { 87 div2 = 0, 88 div4 = 1, 89 div8 = 2, 90 div16 = 3, 91 div32 = 8, 92 div64 = 9, 93 div128 = 10, 94 div1 = 11, 95 }; 96 97 pub fn getDivideConfigurationRegister(lapic: LAPIC) DivideConfigurationRegister { 98 return @enumFromInt(lapic.getRegister(.divide_configuration)); 99 } 100 101 pub fn setDivideConfigurationRegister(lapic: LAPIC, register: DivideConfigurationRegister) void { 102 lapic.setRegister(.divide_configuration, @intFromEnum(register)); 103 } 104 105 // LVT Timer Register 106 pub const LVTTimerRegister = packed struct(u32) { 107 idt_entry: u8, 108 _reserved0: u4 = 0, 109 status: DeliveryStatus = .idle, 110 _reserved1: u3 = 0, 111 masked: bool, 112 mode: Mode, 113 _reserved2: u13 = 0, 114 115 pub const DeliveryStatus = enum(u1) { 116 idle = 0, 117 send_pending = 1, 118 }; 119 120 pub const Mode = enum(u2) { 121 oneshot = 0, 122 periodic = 1, 123 tsc_deadline = 2, 124 }; 125 }; 126 127 pub fn getLVTTimerRegister(lapic: LAPIC) LVTTimerRegister { 128 return @enumFromInt(lapic.getRegister(.lvt_timer)); 129 } 130 131 pub fn setLVTTimerRegister(lapic: LAPIC, register: LVTTimerRegister) void { 132 lapic.setRegister(.lvt_timer, @bitCast(register)); 133 } 134 135 pub fn getRegister(lapic: Self, reg: Register) u32 { 136 switch (lapic) { 137 .xapic => |base| { 138 const ptr: *align(0x10) volatile u32 = @ptrCast(@alignCast(base + reg.xapic())); 139 return ptr.*; 140 }, 141 .x2apic => { 142 return arch.registers.readMSR(u32, reg.x2apic()); 143 }, 144 } 145 } 146 147 pub fn setRegister(lapic: Self, reg: Register, value: u32) void { 148 switch (lapic) { 149 .xapic => |base| { 150 const ptr: *align(0x10) volatile u32 = @ptrCast(@alignCast(base + reg.xapic())); 151 ptr.* = value; 152 }, 153 .x2apic => { 154 arch.registers.writeMSR(u32, reg.x2apic(), value); 155 }, 156 } 157 } 158 159 pub const Register = enum(u32) { 160 // From Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3 161 lapic_id = 0x2, 162 version = 0x3, 163 task_priority = 0x8, 164 process_priority = 0xa, 165 eoi = 0xb, 166 logical_destination = 0xd, 167 spurious_vector = 0xf, 168 169 in_service_0_31 = 0x10, 170 in_service_63_32 = 0x11, 171 in_service_95_64 = 0x12, 172 in_service_127_96 = 0x13, 173 in_service_159_128 = 0x14, 174 in_service_191_160 = 0x15, 175 in_service_223_192 = 0x16, 176 in_service_255_224 = 0x17, 177 178 trigger_mode_0_31 = 0x18, 179 trigger_mode_63_32 = 0x19, 180 trigger_mode_95_64 = 0x1a, 181 trigger_mode_127_96 = 0x1b, 182 trigger_mode_159_128 = 0x1c, 183 trigger_mode_191_160 = 0x1d, 184 trigger_mode_223_192 = 0x1e, 185 trigger_mode_255_224 = 0x1f, 186 187 interrupt_request_0_31 = 0x20, 188 interrupt_request_63_32 = 0x21, 189 interrupt_request_95_64 = 0x22, 190 interrupt_request_127_96 = 0x23, 191 interrupt_request_159_128 = 0x24, 192 interrupt_request_191_160 = 0x25, 193 interrupt_request_223_192 = 0x26, 194 interrupt_request_255_224 = 0x27, 195 196 error_status = 0x28, 197 lvt_cmi = 0x2f, 198 199 interrupt_command_0_31 = 0x30, 200 interrupt_command_32_63 = 0x31, 201 lvt_timer = 0x32, 202 lvt_thermal_sensor = 0x33, 203 lvt_performance_monitoring = 0x34, 204 lvt_lint0 = 0x35, 205 lvt_lint1 = 0x36, 206 lvt_error = 0x37, 207 initial_count = 0x38, 208 current_count = 0x39, 209 divide_configuration = 0x3e, 210 self_ipi = 0x3f, 211 212 const Self = @This(); 213 214 // Get an offset to apply to the xAPIC base 215 pub fn xapic(reg: Register) usize { 216 return @intFromEnum(reg) * 0x10; 217 } 218 219 // Get an MSR number to write to 220 pub fn x2apic(reg: Register) u32 { 221 return 0x800 | @intFromEnum(reg); 222 } 223 }; 224}; 225 226pub const init = struct { 227 // Get the APIC ready (call first) 228 pub fn initialSetup() void { 229 singleton.setSpuriousInterruptRegister(.{ 230 .apic_soft_enable = true, 231 .idt_entry = 0xFF, 232 }); 233 // lapic.setTaskPriorityRegister(.{ 234 // .priority_class = 0, 235 // .priority_sub_class = 0, 236 // }); 237 } 238 239 pub fn calibrateTimer() void { 240 singleton.setDivideConfigurationRegister(.div2); 241 singleton.setLVTTimerRegister(.{ 242 .idt_entry = 0x69, 243 .mode = .oneshot, 244 .masked = true, 245 }); 246 247 var warmup_clk_count: u64 = 0; 248 for (0..5) |_| { 249 singleton.setInitialCountRegister(0xFFFF_FFFF); 250 arch.tsc.delay_poll(1); 251 const count = singleton.getCurrentCountRegister(); 252 singleton.setInitialCountRegister(0); 253 254 warmup_clk_count += 0xFFFF_FFFF - count; 255 } 256 257 std.mem.doNotOptimizeAway(&warmup_clk_count); 258 259 // Now, do it again 260 var tick_count: u64 = 0; 261 for (0..5) |_| { 262 singleton.setInitialCountRegister(0xFFFF_FFFF); 263 arch.tsc.delay_poll(5); 264 const count = singleton.getCurrentCountRegister(); 265 singleton.setInitialCountRegister(0); 266 267 tick_count += 0xFFFF_FFFF - count; 268 } 269 270 // This would be the number of ticks per 5ms interval 271 const norm = tick_count / 5; 272 273 lapic_timer_khz = norm / 5; 274 275 log.debug("APIC timer: {} kHz", .{lapic_timer_khz}); 276 } 277 278 pub fn enablePeriodicInterrupt(ms: usize) void { 279 singleton.setInitialCountRegister(0); 280 singleton.setDivideConfigurationRegister(.div2); 281 282 singleton.setLVTTimerRegister(.{ 283 .idt_entry = 48, 284 .mode = .periodic, 285 .masked = false, 286 }); 287 288 const ticks: u32 = @truncate(lapic_timer_khz * ms); 289 290 singleton.setInitialCountRegister(ticks); 291 } 292}; 293 294pub fn spurious_interrupt_handler(_: *arch.structures.Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void { 295 log.warn("Got a spurious interrupt!", .{}); 296} 297 298pub fn periodic_handler(_: *arch.structures.Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void { 299 log.warn("Got an ACPI timer interrupt!", .{}); 300 singleton.setRegister(.eoi, 0); 301}