1- /* $Id: bs3-apic-1-32.c32 114735 2026-07-21 07:31:36Z alexander.eichner@oracle.com $ */
1+ /* $Id: bs3-apic-1-32.c32 114736 2026-07-21 10:54:49Z alexander.eichner@oracle.com $ */
22/** @file
33 * BS3Kit - bs3-apic-1, 32-bit C code.
44 */
4747#define CPUS_MAX 32
4848#define CPU_STACK_SIZE _4K
4949
50- #define APIC_VECTOR 0x70
50+ #define APIC_VECTOR 0x70
51+ #define APIC_X2APIC_VECTOR 0x71
5152
5253typedef struct APICCPU
5354{
5455 /** The stack for this CPU. */
55- uint8_t abStack[CPU_STACK_SIZE];
56+ uint8_t abStack[CPU_STACK_SIZE];
57+ /** The MMIO base for the APIC when in xAPIC mode. */
58+ uint32_t BS3_FAR volatile * pau32Apic;
5659 /** The assigned CPU ID, equals APIC ID. */
57- uint32_t idCpu;
60+ uint32_t idCpu;
5861 /** Flag whether the CPU should busy wait for the next interrupt. */
59- bool fBusyWait;
62+ bool fBusyWait;
63+ /** Flag whether the APIC is in x2Apic mode. */
64+ bool fX2Apic;
6065 /** Padding. */
61- bool afAlignment[11 ];
66+ bool afAlignment[6 ];
6267} APICCPU;
6368typedef APICCPU *PAPICCPU;
6469AssertCompileSizeAlignment(APICCPU, 16);
@@ -123,21 +128,24 @@ static void bs3ApicTestFailedF(const char BS3_FAR *pszFormat, ...)
123128}
124129
125130
126- static uint32_t BS3_FAR volatile * const bs3ApicGetMmioBase( PAPICCPU *ppApicCpu )
131+ static PAPICCPU bs3ApicGetCpu(void )
127132{
128133 uint64_t uApicBase;
129134
130135 uApicBase = ASMRdMsr(MSR_IA32_APICBASE);
131136 if (uApicBase & MSR_IA32_APICBASE_EN)
132137 {
133- uint8_t BS3_FAR volatile * const pabApic = (uint8_t BS3_FAR volatile *)((uintptr_t)uApicBase & X86_PAGE_4K_BASE_MASK);
134- uint32_t BS3_FAR volatile * const pau32Apic = (uint32_t BS3_FAR volatile *)pabApic;
135- uint32_t const idApic = pau32Apic[XAPIC_OFF_ID / sizeof(uint32_t)] >> 24;
136-
137- if (ppApicCpu)
138- *ppApicCpu = &g_paCpus[idApic];
138+ uint32_t idApic = UINT32_MAX;
139+ if (uApicBase & MSR_IA32_APICBASE_EXTD)
140+ idApic = (uint32_t)ASMRdMsr(MSR_IA32_X2APIC_ID);
141+ else
142+ {
143+ uint8_t BS3_FAR volatile * const pabApic = (uint8_t BS3_FAR volatile *)((uintptr_t)uApicBase & X86_PAGE_4K_BASE_MASK);
144+ uint32_t BS3_FAR volatile * const pau32Apic = (uint32_t BS3_FAR volatile *)pabApic;
145+ idApic = pau32Apic[XAPIC_OFF_ID / sizeof(uint32_t)] >> 24;
146+ }
139147
140- return pau32Apic ;
148+ return &g_paCpus[idApic] ;
141149 }
142150
143151 return NULL;
@@ -146,11 +154,32 @@ static uint32_t BS3_FAR volatile * const bs3ApicGetMmioBase(PAPICCPU *ppApicCpu)
146154
147155BS3_DECL_NEAR_CALLBACK(void) BS3_CMN_NM(bs3ApicIpiHandler)(PBS3TRAPFRAME pTrapFrame)
148156{
149- PAPICCPU pCpu = NULL;
150- uint32_t BS3_FAR volatile * const pau32Apic = bs3ApicGetMmioBase(&pCpu);
151- if (pau32Apic)
157+ PAPICCPU pCpu = bs3ApicGetCpu();
158+ if (pCpu)
159+ {
160+ if (pCpu->fX2Apic)
161+ ASMWrMsr(MSR_IA32_X2APIC_EOI, 0);
162+ else
163+ pCpu->pau32Apic[XAPIC_OFF_EOI / sizeof(uint32_t)] = 0;
164+ ASMAtomicIncU32(&g_cCpusResponded);
165+ }
166+ else
167+ bs3ApicTestFailedF("APIC not enabled!");
168+
169+ RT_NOREF(pTrapFrame);
170+ }
171+
172+
173+ BS3_DECL_NEAR_CALLBACK(void) BS3_CMN_NM(bs3ApicX2ApicSwitch)(PBS3TRAPFRAME pTrapFrame)
174+ {
175+ PAPICCPU pCpu = bs3ApicGetCpu();
176+ if (pCpu)
152177 {
153- pau32Apic[XAPIC_OFF_EOI / sizeof(uint32_t)] = 0;
178+ uint64_t uApicBase = ASMRdMsr(MSR_IA32_APICBASE);
179+ ASMWrMsr(MSR_IA32_APICBASE, uApicBase | MSR_IA32_APICBASE_EXTD);
180+ pCpu->fX2Apic = true;
181+ ASMWrMsr(MSR_IA32_X2APIC_SVR, XAPIC_SVR_SOFTWARE_ENABLE);
182+ ASMWrMsr(MSR_IA32_X2APIC_EOI, 0);
154183 ASMAtomicIncU32(&g_cCpusResponded);
155184 }
156185 else
@@ -175,7 +204,8 @@ BS3_DECL(uint32_t) bs3ApicApStartup_pe32(void)
175204 uint32_t const idApic = pau32Apic[XAPIC_OFF_ID / sizeof(uint32_t)] >> 24;
176205 PAPICCPU pApicCpu = &g_paCpus[idApic];
177206
178- pApicCpu->idCpu = idApic;
207+ pApicCpu->idCpu = idApic;
208+ pApicCpu->pau32Apic = pau32Apic;
179209
180210 pau32Apic[XAPIC_OFF_SVR / sizeof(uint32_t)] = XAPIC_SVR_SOFTWARE_ENABLE;
181211 ASMAtomicIncU32(&g_cCpus);
@@ -188,18 +218,34 @@ BS3_DECL(uint32_t) bs3ApicApStartup_pe32(void)
188218}
189219
190220
191- DECLINLINE(void) apicWriteIcr(uint32_t BS3_FAR volatile * const pau32Apic , uint8_t bDestination,
221+ DECLINLINE(void) apicWriteIcr(PAPICCPU pApicCpu , uint8_t bDestination,
192222 XAPICDESTSHORTHAND enmShorthand, XAPICDELIVERYMODE enmDeliveryMode,
193223 XAPICDESTMODE enmMode, XAPICTRIGGERMODE enmTriggerMode,
194224 XAPICLEVEL enmLevel, uint8_t bVector)
195225{
196- pau32Apic[XAPIC_OFF_ICR_HI / sizeof(uint32_t)] = (uint32_t)bDestination << 24;
197- pau32Apic[XAPIC_OFF_ICR_LO / sizeof(uint32_t)] = ((uint32_t)enmShorthand << 18)
198- | ((uint32_t)enmMode == XAPICDESTMODE_LOGICAL ? RT_BIT_32(11) : 0)
199- | ((uint32_t)enmDeliveryMode << 8)
200- | (enmTriggerMode == XAPICTRIGGERMODE_LEVEL ? RT_BIT_32(15) : 0)
201- | (enmLevel == XAPICINITLEVEL_ASSERT ? RT_BIT_32(14) : 0)
202- | bVector;
226+ if (pApicCpu->fX2Apic)
227+ {
228+ uint64_t const uWr = ((uint64_t)bDestination) << 32
229+ | ((uint64_t)enmShorthand << 18)
230+ | ((uint64_t)enmMode == XAPICDESTMODE_LOGICAL ? RT_BIT_64(11) : 0)
231+ | ((uint64_t)enmDeliveryMode << 8)
232+ | (enmTriggerMode == XAPICTRIGGERMODE_LEVEL ? RT_BIT_64(15) : 0)
233+ | (enmLevel == XAPICINITLEVEL_ASSERT ? RT_BIT_64(14) : 0)
234+ | bVector;
235+ ASMWrMsr(MSR_IA32_X2APIC_ICR, uWr);
236+ }
237+ else
238+ {
239+ uint32_t BS3_FAR volatile * const pau32Apic = pApicCpu->pau32Apic;
240+
241+ pau32Apic[XAPIC_OFF_ICR_HI / sizeof(uint32_t)] = (uint32_t)bDestination << 24;
242+ pau32Apic[XAPIC_OFF_ICR_LO / sizeof(uint32_t)] = ((uint32_t)enmShorthand << 18)
243+ | ((uint32_t)enmMode == XAPICDESTMODE_LOGICAL ? RT_BIT_32(11) : 0)
244+ | ((uint32_t)enmDeliveryMode << 8)
245+ | (enmTriggerMode == XAPICTRIGGERMODE_LEVEL ? RT_BIT_32(15) : 0)
246+ | (enmLevel == XAPICINITLEVEL_ASSERT ? RT_BIT_32(14) : 0)
247+ | bVector;
248+ }
203249}
204250
205251
@@ -209,8 +255,14 @@ static bool bs3ApicStartAllAps(uint32_t BS3_FAR volatile * const pau32Apic)
209255 if (g_paCpus)
210256 {
211257 /* Allocate a single 4K page and make sure it is aligned on a page boundary and the physical address fits into the 8-bit vector. */
258+ PAPICCPU pApicCpu = &g_paCpus[0];
212259 void BS3_FAR *pv = Bs3MemAllocZ(BS3MEMKIND_REAL, _4K);
213260 uintptr_t const uPtr = (uintptr_t)pv;
261+
262+ pApicCpu->idCpu = 0;
263+ pApicCpu->fX2Apic = false;
264+ pApicCpu->pau32Apic = pau32Apic;
265+
214266 if ( !(uPtr & X86_PAGE_OFFSET_MASK)
215267 && (uPtr >> X86_PAGE_SHIFT) < UINT8_MAX)
216268 {
@@ -219,28 +271,29 @@ static bool bs3ApicStartAllAps(uint32_t BS3_FAR volatile * const pau32Apic)
219271 Bs3MemCpy(pv, bs3ApicApTrampoline, (uintptr_t)bs3ApicApTrampoline_EndProc - (uintptr_t)bs3ApicApTrampoline);
220272 g_AddrApInitStack = uPtr + _4K; /* Use the unused top of the trampoline code for the initial stack. */
221273
222- Bs3TrapSetHandler(APIC_VECTOR, bs3ApicIpiHandler_c32);
274+ Bs3TrapSetHandler(APIC_VECTOR, bs3ApicIpiHandler_c32);
275+ Bs3TrapSetHandler(APIC_X2APIC_VECTOR, bs3ApicX2ApicSwitch_c32);
223276
224277 /* Enable xAPIC. */
225278 pau32Apic[XAPIC_OFF_SVR / sizeof(uint32_t)] = XAPIC_SVR_SOFTWARE_ENABLE;
226279
227280 /* Send INIT to all APs. */
228- apicWriteIcr(pau32Apic , 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_INIT,
281+ apicWriteIcr(pApicCpu , 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_INIT,
229282 XAPICDESTMODE_PHYSICAL, XAPICTRIGGERMODE_EDGE, XAPICINITLEVEL_ASSERT, 0);
230283 bs3ApicBusyWait(10 * 1000);
231284
232285 /* Send INIT de-assert to all APs. */
233- apicWriteIcr(pau32Apic , 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_INIT,
286+ apicWriteIcr(pApicCpu , 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_INIT,
234287 XAPICDESTMODE_PHYSICAL, XAPICTRIGGERMODE_EDGE, XAPICINITLEVEL_DEASSERT, 0);
235288 bs3ApicBusyWait(10 * 1000);
236289
237290 /* Send SIPI to APs. */
238- apicWriteIcr(pau32Apic , 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_STARTUP,
291+ apicWriteIcr(pApicCpu , 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_STARTUP,
239292 XAPICDESTMODE_PHYSICAL, XAPICTRIGGERMODE_EDGE, XAPICINITLEVEL_ASSERT, bVector);
240293 bs3ApicBusyWait(200);
241294
242295 /* Send SIPI to APs. */
243- apicWriteIcr(pau32Apic , 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_STARTUP,
296+ apicWriteIcr(pApicCpu , 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_STARTUP,
244297 XAPICDESTMODE_PHYSICAL, XAPICTRIGGERMODE_EDGE, XAPICINITLEVEL_ASSERT, bVector);
245298 /** @todo This assumes that the CPUs start in that time frame,
246299 * which might not be the case for IEM and heavy instruction logging enabled.
@@ -262,20 +315,56 @@ static bool bs3ApicStartAllAps(uint32_t BS3_FAR volatile * const pau32Apic)
262315}
263316
264317
318+ static void bs3ApicRunTests(PAPICCPU pApicCpu)
319+ {
320+ if (g_cCpus)
321+ {
322+ Bs3TestSubSub("Pinging all APs");
323+ ASMAtomicWriteU32(&g_cCpusResponded, 0);
324+ apicWriteIcr(pApicCpu, 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_FIXED,
325+ XAPICDESTMODE_PHYSICAL, XAPICTRIGGERMODE_EDGE, XAPICINITLEVEL_ASSERT, APIC_VECTOR);
326+
327+ while (ASMAtomicReadU32(&g_cCpusResponded) < g_cCpus)
328+ ASMNopPause();
329+ }
330+ }
331+
265332static void ProtModeApicTestsAp(uint32_t BS3_FAR volatile * const pau32Apic)
266333{
267334 Bs3TestSub("Starting APs");
268335 if (bs3ApicStartAllAps(pau32Apic))
269336 {
270- if (g_cCpus)
337+ PAPICCPU pApicCpu = &g_paCpus[0];
338+
339+ Bs3TestSub("xAPIC mode");
340+ bs3ApicRunTests(pApicCpu);
341+
342+ /* Check for x2APIC. */
343+ if (g_uBs3CpuDetected & BS3CPU_F_CPUID)
271344 {
272- Bs3TestSub("Pinging all APs" );
273- ASMAtomicWriteU32(&g_cCpusResponded, 0);
274- apicWriteIcr(pau32Apic, 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_FIXED,
275- XAPICDESTMODE_PHYSICAL, XAPICTRIGGERMODE_EDGE, XAPICINITLEVEL_ASSERT, APIC_VECTOR );
345+ uint32_t const uEcx = ASMCpuId_ECX(1 /*uOperator*/ );
346+ if (uEcx & X86_CPUID_FEATURE_ECX_X2APIC)
347+ {
348+ uint64_t uApicBase = ASMRdMsr(MSR_IA32_APICBASE );
276349
277- while (ASMAtomicReadU32(&g_cCpusResponded) < g_cCpus)
278- ASMNopPause();
350+ /* Switch all APs to x2APIC mode before doing it. */
351+ ASMAtomicWriteU32(&g_cCpusResponded, 0);
352+ apicWriteIcr(pApicCpu, 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_FIXED,
353+ XAPICDESTMODE_PHYSICAL, XAPICTRIGGERMODE_EDGE, XAPICINITLEVEL_ASSERT, APIC_X2APIC_VECTOR);
354+
355+ ASMWrMsr(MSR_IA32_APICBASE, uApicBase | MSR_IA32_APICBASE_EXTD);
356+ ASMWrMsr(MSR_IA32_X2APIC_SVR, XAPIC_SVR_SOFTWARE_ENABLE);
357+ pApicCpu->fX2Apic = true;
358+
359+ Bs3TestSub("x2APIC mode");
360+
361+ while (ASMAtomicReadU32(&g_cCpusResponded) < g_cCpus)
362+ ASMNopPause();
363+
364+ bs3ApicRunTests(pApicCpu);
365+ }
366+ else
367+ bs3ApicTestPrintf("Skipping x2APIC tests as CPU doesn't support it\n");
279368 }
280369 }
281370}
0 commit comments