My Project
Loading...
Searching...
No Matches
apic.c
Go to the documentation of this file.
1#include "../../includes/me.h"
2#include "../../includes/mh.h"
3#include "../../includes/mm.h"
4#include "../../includes/mg.h"
5#include "../../assert.h"
6#include <stddef.h>
7#include <stdint.h>
8
9#define IA32_APIC_BASE_MSR 0x1BULL
10#define APIC_BASE_RESERVED 0xFFF0000000000000ULL
11
12#define LAPIC_PAGE_SIZE 0x1000
13#define LAPIC_MAP_FLAGS (PAGE_PRESENT | PAGE_RW | PAGE_PCD)
14
15// LAPIC register offsets (32-bit registers)
16enum {
18 LAPIC_TPR = 0x080,
19 LAPIC_EOI = 0x0B0,
20 LAPIC_SVR = 0x0F0,
21 LAPIC_ESR = 0x280,
33};
34
35// --- low-level mmio helpers (assumes lapic mapped to virtual memory) ---
36uint32_t lapic_mmio_read(uint32_t off) {
37 // Hi matanel, if you ever encounter enormous page faults in this area, with MmAccessFault not even bugchecking.
38 // It is because an IPI was sent from an AP that hasn't been fully initialized.
39 // Thus somehow in gods existence corrupting stuff.
40 // Like in the case of MI_WRITE_PTE, it sent an IPI when it was used in map_lapic, and caused like a chain reaction that formed into a black hole.
41 // So I added another flag allApsInitialized, so that MI_WRITE_PTE will not send an IPI if they are not init yet.
42 return MeGetCurrentProcessor()->LapicAddressVirt[off / 4];
43}
44
45void lapic_mmio_write(uint32_t off, uint32_t val) {
46 // Read my lapic_mmio_read comment if page faults ever happen here.
48 (void)MeGetCurrentProcessor()->LapicAddressVirt[0]; // Serializing read
49}
50
51// Wait for ICR delivery to complete (ICR low: bit 12 = Delivery Status)
52static void lapic_wait_icr(void) {
53 while (lapic_mmio_read(LAPIC_ICR_LOW) & (1 << 12)) {
54 /* spin */
55 __pause();
56 }
57}
58
59// Initialize the Spurious Interrupt Vector
60void lapic_init_siv(void) {
61 uint32_t svr = lapic_mmio_read(LAPIC_SVR);
62 uint32_t vector = 0xFF; // IDT Entry
63 svr = (svr & 0xFFFFFF00) | vector; // preserve enable bit, update vector.
65}
66
67static void map_lapic(uint64_t lapicPhysicalAddr) {
68 if (MeGetCurrentProcessor()->LapicAddressVirt) return;
69
70 void* virt = (void*)(lapicPhysicalAddr + PhysicalMemoryOffset);
71
72 // Map the single LAPIC page (phys -> virt)
73 PMMPTE pte = MiGetPtePointer((uintptr_t)virt);
74 assert(pte != NULL);
75 if (!pte) return;
76 MI_WRITE_PTE(pte, virt, lapicPhysicalAddr, PAGE_PRESENT | PAGE_RW | PAGE_PCD);
77
78 // store the mmio base pointer
79 MeGetCurrentProcessor()->LapicAddressVirt = (volatile uint32_t*)virt;
80 MeGetCurrentProcessor()->LapicAddressPhys = lapicPhysicalAddr;
81}
82
83static inline uint64_t get_lapic_base_address(void) {
84 uint32_t eax, edx;
85
86 // The 'rdmsr' instruction reads a 64-bit MSR into the EDX:EAX registers.
87 __asm__ volatile("rdmsr" : "=a"(eax), "=d"(edx) : "c"(IA32_APIC_BASE_MSR));
88
89 // Combine the high (edx) and low (eax) parts into a 64-bit value.
90 uint64_t msr_value = ((uint64_t)edx << 32) | eax;
91
92 // The address is in bits 12 through the most significant bit.
93 // We must mask off the lower 12 bits which contain flags.
94 return msr_value & ~0xFFFULL;
95}
96
97// Enable local APIC via IA32_APIC_BASE MSR and set SVR
98void lapic_enable(void) {
99 uint64_t apic_msr = __readmsr(IA32_APIC_BASE_MSR);
100 if (!(apic_msr & (1ULL << 11))) {
101 // set APIC global enable
102 apic_msr |= (1ULL << 11);
104 }
105 map_lapic(get_lapic_base_address());
106
107 // Set Spurious Vector Register and enable (bit 8 = APIC enable)
108 uint32_t svr = (0xFF) | (1 << 8);
110}
111
112// Initialize CPU's LAPIC (call early from kernel init on BSP, and from each ap)
113void lapic_init_cpu(void) {
114 map_lapic(get_lapic_base_address());
115
116 lapic_enable();
117
118 // mask LINT0/LINT1 as appropriate, clear error status, etc.
119 lapic_mmio_write(LAPIC_LVT_LINT0, (1U << 16)); // mask
120 lapic_mmio_write(LAPIC_LVT_LINT1, (1U << 16)); // mask
121 lapic_mmio_write(LAPIC_LVT_ERROR, (1U << 16)); // mask (until handler in place)
123}
124
125// send IPI to APIC id
126// apic_id - APICId of the CPU.
127// vector - IDT Vector number
128// flags - specified cpu flags, 0 for none.
129void lapic_send_ipi(uint8_t apic_id, uint8_t vector, uint32_t flags) {
130 uint32_t high = ((uint32_t)apic_id) << 24;
132 lapic_mmio_write(LAPIC_ICR_LOW, (uint32_t)vector | flags);
133 lapic_wait_icr();
134}
135
136void lapic_eoi(void) {
138}
139
140// --- Timer calibration and init ---
141// NOTE: the APIC timer is a downward counter. Strategy:
142// 1. Set divide to known divisor.
143// 2. Write initcount = 0xFFFFFFFF.
144// 3. Wait EXACTLY 100 ms via PIT/HPET.
145// 4. curr = read current count -> ticks_in_100ms = start - curr
146// 5. ticks_per_period(10ms) = ticks_in_100ms / 10
147// 6. Program LVT timer to periodic and initial count = ticks_per_period
148//
149#define APIC_LVT_TIMER_PERIODIC (1U << 17)
150#define APIC_TIMER_MASKED (1U << 16)
151
152static uint32_t calibrate_lapic_ticks_per_10ms(void) {
153 // choose divide config: here set encode 0x3 (divide by 16). Adjust if needed.
155
156 const uint32_t start = 0xFFFFFFFFU;
158
159 pit_sleep_ms(100);
160
161 uint32_t curr = lapic_mmio_read(LAPIC_TIMER_CURRCNT);
162 uint32_t ticks = start - curr;
163 if (ticks == 0) return 0;
164 return ticks / 10; // ticks per 10ms -> for 100Hz (10ms period)
165}
166
167// Make the global variable static to this file
168static uint32_t g_apic_ticks_per_10ms = 0;
169
170// BSP-only calibration function
172 // Only calibrate if it hasn't been done. This is the single entry point.
173 if (g_apic_ticks_per_10ms == 0) {
174 g_apic_ticks_per_10ms = calibrate_lapic_ticks_per_10ms();
175 }
176}
177
178// Renamed and simplified init function
179int init_lapic_timer(uint32_t hz) {
180 if (hz == 0) return -1;
181
182 // This now assumes calibration is already done!
183 if (g_apic_ticks_per_10ms == 0) {
184 // Calibration failed or wasn't run, this is an error.
185 return -2;
186 }
187
188 uint32_t period_ms = 1000 / hz;
189 uint64_t initial = ((uint64_t)g_apic_ticks_per_10ms * (uint64_t)period_ms) / 10ULL;
190 if (initial == 0) initial = 1;
191
192 // Program THIS CPU's timer using the shared calibration value
194 lapic_mmio_write(LAPIC_TIMER_INITCNT, (uint32_t)initial);
195 return 0;
196}
197
198void
200 IN IRQL RequestIrql
201)
202
203/*++
204
205 Routine description :
206
207 This function is used to request a software interrupt to the current processor.
208
209 N.B: The function will 100% have the interrupt executed when it returns.
210
211 Arguments:
212
213 [IN] IRQL RequstIrql - The IRQL value to request an interrupt for.
214
215 Return Values:
216
217 None.
218
219 Notes:
220
221 The only IRQLs supported currently are DISPATCH_LEVEL and APC_LEVEL
222
223 To have this function serviced for a DPC or an APC, set the flag in the CPU accordingly and wait for IRQL to be equal or below to IRQL requested.
224
225--*/
226
227{
228 bool prev_if;
230 assert(cpu->DpcInterruptRequested == true);
231
232 // We only support DISPATCH_LEVEL.
233 assert(RequestIrql == DISPATCH_LEVEL || RequestIrql == APC_LEVEL);
234 if (RequestIrql != DISPATCH_LEVEL && RequestIrql != APC_LEVEL) MeBugCheckEx(INVALID_INTERRUPT_REQUEST, (void*)RETADDR(0), (void*)RequestIrql, NULL, NULL);
235
236 // Disable interrupts, and save IF flag.
237 prev_if = MeDisableInterrupts();
238
239 // Clear the flag.
240 if (RequestIrql == DISPATCH_LEVEL) {
241 cpu->DpcInterruptRequested = false;
242 }
243 else {
244 cpu->ApcInterruptRequested = false;
245 }
246
247 // wait until previous ICR is not busy
248 lapic_wait_icr();
249
250 // For a self IPI we can use the destination shorthand
251 uint32_t icr_low;
252 if (RequestIrql == DISPATCH_LEVEL) {
253 icr_low = (uint32_t)VECTOR_DPC | (1U << 18);
254 }
255 else {
256 icr_low = (uint32_t)VECTOR_APC | (1U << 18);
257 }
258
259 // ICR high is ignored when shorthand is used, but zero it for clarity.
262
263 // wait for delivery to complete
264 lapic_wait_icr();
265
266 // restore interrupts
267 MeEnableInterrupts(prev_if);
268}
#define IN
Definition annotations.h:7
void lapic_timer_calibrate(void)
Definition apic.c:171
void lapic_mmio_write(uint32_t off, uint32_t val)
Definition apic.c:45
int init_lapic_timer(uint32_t hz)
Definition apic.c:179
@ LAPIC_LVT_LINT1
Definition apic.c:28
@ LAPIC_SVR
Definition apic.c:20
@ LAPIC_LVT_THERMAL
Definition apic.c:25
@ LAPIC_LVT_LINT0
Definition apic.c:27
@ LAPIC_LVT_PCC
Definition apic.c:26
@ LAPIC_ICR_HIGH
Definition apic.c:23
@ LAPIC_TIMER_INITCNT
Definition apic.c:30
@ LAPIC_TIMER_CURRCNT
Definition apic.c:31
@ LAPIC_ICR_LOW
Definition apic.c:22
@ LAPIC_ESR
Definition apic.c:21
@ LAPIC_TPR
Definition apic.c:18
@ LAPIC_VERSION
Definition apic.c:17
@ LAPIC_LVT_TIMER
Definition apic.c:24
@ LAPIC_LVT_ERROR
Definition apic.c:29
@ LAPIC_EOI
Definition apic.c:19
@ LAPIC_TIMER_DIV
Definition apic.c:32
#define IA32_APIC_BASE_MSR
Definition apic.c:9
void lapic_send_ipi(uint8_t apic_id, uint8_t vector, uint32_t flags)
Definition apic.c:129
void lapic_eoi(void)
Definition apic.c:136
void MhRequestSoftwareInterrupt(IN IRQL RequestIrql)
Definition apic.c:199
void lapic_init_siv(void)
Definition apic.c:60
void lapic_init_cpu(void)
Definition apic.c:113
void lapic_enable(void)
Definition apic.c:98
uint32_t lapic_mmio_read(uint32_t off)
Definition apic.c:36
#define APIC_LVT_TIMER_PERIODIC
Definition apic.c:149
#define assert(...)
Definition assert.h:57
NORETURN void MeBugCheckEx(IN enum _BUGCHECK_CODES BugCheckCode, IN void *BugCheckParameter1, IN void *BugCheckParameter2, IN void *BugCheckParameter3, IN void *BugCheckParameter4)
Definition bugcheck.c:305
@ APC_LEVEL
Definition core.h:14
@ DISPATCH_LEVEL
Definition core.h:15
PROCESSOR * PPROCESSOR
Definition core.h:46
enum _IRQL IRQL
FORCEINLINE uint64_t __readmsr(uint32_t msr)
Definition intrin.h:194
FORCEINLINE void __writemsr(uint32_t msr, uint64_t value)
Definition intrin.h:200
FORCEINLINE void __pause(void)
Definition intrin.h:224
bool MeDisableInterrupts(void)
Definition irql.c:186
void MeEnableInterrupts(IN bool EnabledBefore)
Definition irql.c:199
#define RETADDR(level)
Definition macros.h:38
PMMPTE MiGetPtePointer(IN uintptr_t va)
Definition map.c:76
@ INVALID_INTERRUPT_REQUEST
Definition me.h:128
FORCEINLINE PPROCESSOR MeGetCurrentProcessor(void)
Definition me.h:356
uint32_t flags
Definition mh.h:2
#define VECTOR_DPC
Definition mh.h:47
#define VECTOR_APC
Definition mh.h:48
@ PAGE_RW
Definition mm.h:272
@ PAGE_PRESENT
Definition mm.h:268
@ PAGE_PCD
Definition mm.h:285
struct _MMPTE * PMMPTE
#define PhysicalMemoryOffset
Definition mm.h:56
#define MI_WRITE_PTE(_PtePointer, _Va, _Pa, _Flags)
Definition mm.h:90
void pit_sleep_ms(uint32_t ms)
Definition pit.c:19
volatile bool DpcInterruptRequested
Definition me.h:318
uintptr_t LapicAddressPhys
Definition me.h:296
volatile uint32_t * LapicAddressVirt
Definition me.h:295
volatile bool ApcInterruptRequested
Definition me.h:319