kernel
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 (The flag is set in the MeInsertQueueDpc/Apc functions)
225
226--*/
227
228{
229 bool prev_if;
231 assert(cpu->DpcInterruptRequested == true);
232
233 // We only support DISPATCH_LEVEL.
234 assert(RequestIrql == DISPATCH_LEVEL || RequestIrql == APC_LEVEL);
235 if (RequestIrql != DISPATCH_LEVEL && RequestIrql != APC_LEVEL) MeBugCheckEx(INVALID_INTERRUPT_REQUEST, (void*)RETADDR(0), (void*)RequestIrql, NULL, NULL);
236
237 // Disable interrupts, and save IF flag.
238 prev_if = MeDisableInterrupts();
239
240 // Clear the flag.
241 if (RequestIrql == DISPATCH_LEVEL) {
242 cpu->DpcInterruptRequested = false;
243 }
244 else {
245 cpu->ApcInterruptRequested = false;
246 }
247
248 // wait until previous ICR is not busy
249 lapic_wait_icr();
250
251 // For a self IPI we can use the destination shorthand
252 uint32_t icr_low;
253 if (RequestIrql == DISPATCH_LEVEL) {
254 icr_low = (uint32_t)VECTOR_DPC | (1U << 18);
255 }
256 else {
257 icr_low = (uint32_t)VECTOR_APC | (1U << 18);
258 }
259
260 // ICR high is ignored when shorthand is used, but zero it for clarity.
263
264 // wait for delivery to complete
265 lapic_wait_icr();
266
267 // restore interrupts
268 MeEnableInterrupts(prev_if);
269}
#define IN
Definition annotations.h:8
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:245
@ APC_LEVEL
Definition core.h:16
@ DISPATCH_LEVEL
Definition core.h:17
PROCESSOR * PPROCESSOR
Definition core.h:48
enum _IRQL IRQL
FORCEINLINE uint64_t __readmsr(uint32_t msr)
Definition intrin.h:209
FORCEINLINE void __writemsr(uint32_t msr, uint64_t value)
Definition intrin.h:215
FORCEINLINE void __pause(void)
Definition intrin.h:239
bool MeDisableInterrupts(void)
Definition irql.c:203
void MeEnableInterrupts(IN bool EnabledBefore)
Definition irql.c:216
#define RETADDR(level)
Definition macros.h:53
PMMPTE MiGetPtePointer(IN uintptr_t va)
Definition map.c:76
@ INVALID_INTERRUPT_REQUEST
Definition me.h:128
FORCEINLINE PPROCESSOR MeGetCurrentProcessor(void)
Definition me.h:369
uint32_t flags
Definition mh.h:2
#define VECTOR_CLOCK
Definition mh.h:37
#define VECTOR_DPC
Definition mh.h:36
#define VECTOR_APC
Definition mh.h:35
@ PAGE_RW
Definition mm.h:311
@ PAGE_PRESENT
Definition mm.h:307
@ PAGE_PCD
Definition mm.h:324
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:326
uintptr_t LapicAddressPhys
Definition me.h:304
volatile uint32_t * LapicAddressVirt
Definition me.h:303
volatile bool ApcInterruptRequested
Definition me.h:327