Skip to content

Commit 3ebc2fe

Browse files
ValidationKit/bootsectors/bs3-apic-1: Start implementing tests for excercising the APIC in SMP configurations
svn:sync-xref-src-repo-rev: r174572
1 parent 7258341 commit 3ebc2fe

4 files changed

Lines changed: 369 additions & 3 deletions

File tree

src/VBox/ValidationKit/bootsectors/Makefile.kmk

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: Makefile.kmk 114145 2026-05-16 11:03:28Z knut.osmundsen@oracle.com $
1+
# $Id: Makefile.kmk 114735 2026-07-21 07:31:36Z alexander.eichner@oracle.com $
22
## @file
33
# VirtualBox Validation Kit - Bootsector Tests for Test Drivers or standalone testing.
44
#
@@ -261,6 +261,7 @@ ifdef VBOX_WITH_BS3KIT
261261
bs3-apic-1_TEMPLATE = VBoxBS3KitImg
262262
bs3-apic-1_SOURCES = \
263263
bs3kit/bs3-first-rm.asm \
264+
bs3-apic-1-asm.asm \
264265
bs3-apic-1.c \
265266
bs3-apic-1-32.c32
266267

src/VBox/ValidationKit/bootsectors/bs3-apic-1-32.c32

Lines changed: 240 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $Id: bs3-apic-1-32.c32 112403 2026-01-11 19:29:08Z knut.osmundsen@oracle.com $ */
1+
/* $Id: bs3-apic-1-32.c32 114735 2026-07-21 07:31:36Z alexander.eichner@oracle.com $ */
22
/** @file
33
* BS3Kit - bs3-apic-1, 32-bit C code.
44
*/
@@ -39,10 +39,247 @@
3939
* Header Files *
4040
*********************************************************************************************************************************/
4141
#include <bs3kit.h>
42+
#include <iprt/asm.h>
4243
#include <iprt/asm-amd64-x86.h>
4344
#include <iprt/x86.h>
4445
#include <VBox/apic.h>
4546

47+
#define CPUS_MAX 32
48+
#define CPU_STACK_SIZE _4K
49+
50+
#define APIC_VECTOR 0x70
51+
52+
typedef struct APICCPU
53+
{
54+
/** The stack for this CPU. */
55+
uint8_t abStack[CPU_STACK_SIZE];
56+
/** The assigned CPU ID, equals APIC ID. */
57+
uint32_t idCpu;
58+
/** Flag whether the CPU should busy wait for the next interrupt. */
59+
bool fBusyWait;
60+
/** Padding. */
61+
bool afAlignment[11];
62+
} APICCPU;
63+
typedef APICCPU *PAPICCPU;
64+
AssertCompileSizeAlignment(APICCPU, 16);
65+
66+
BS3_DECL_CALLBACK(void) bs3ApicApTrampoline(void);
67+
BS3_DECL_CALLBACK(void) bs3ApicApTrampoline_EndProc(void);
68+
69+
/** The AP lock protecting against concurrent access to some APIs and the startup code (lives in the DATA16 segment). */
70+
#define g_fApLock BS3_DATA_NM(g_fApLock)
71+
extern volatile bool g_fApLock;
72+
/** The AP init stack (lives in the DATA16 segment). */
73+
#define g_AddrApInitStack BS3_DATA_NM(g_AddrApInitStack)
74+
extern volatile uint32_t g_AddrApInitStack;
75+
76+
static PAPICCPU g_paCpus;
77+
static volatile uint32_t g_cCpus = 0;
78+
static volatile uint32_t g_cCpusResponded = 0;
79+
80+
static void bs3ApicBusyWait(uint64_t u64Usec)
81+
{
82+
uint64_t cNsElapsed = Bs3TestNow();
83+
while (Bs3TestNow() < cNsElapsed + u64Usec * 1000)
84+
ASMNopPause();
85+
}
86+
87+
88+
static void bs3ApicLock(void)
89+
{
90+
for (;;)
91+
{
92+
if (ASMAtomicCmpXchgBool(&g_fApLock, true, false))
93+
break;
94+
}
95+
}
96+
97+
98+
static void bs3ApicUnlock(void)
99+
{
100+
ASMAtomicXchgBool(&g_fApLock, false);
101+
}
102+
103+
104+
static void bs3ApicTestPrintf(const char BS3_FAR *pszFormat, ...)
105+
{
106+
va_list va;
107+
va_start(va, pszFormat);
108+
bs3ApicLock();
109+
Bs3TestPrintfV(pszFormat, va);
110+
bs3ApicUnlock();
111+
va_end(va);
112+
}
113+
114+
115+
static void bs3ApicTestFailedF(const char BS3_FAR *pszFormat, ...)
116+
{
117+
va_list va;
118+
va_start(va, pszFormat);
119+
bs3ApicLock();
120+
Bs3TestFailedV(pszFormat, va);
121+
bs3ApicUnlock();
122+
va_end(va);
123+
}
124+
125+
126+
static uint32_t BS3_FAR volatile * const bs3ApicGetMmioBase(PAPICCPU *ppApicCpu)
127+
{
128+
uint64_t uApicBase;
129+
130+
uApicBase = ASMRdMsr(MSR_IA32_APICBASE);
131+
if (uApicBase & MSR_IA32_APICBASE_EN)
132+
{
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];
139+
140+
return pau32Apic;
141+
}
142+
143+
return NULL;
144+
}
145+
146+
147+
BS3_DECL_NEAR_CALLBACK(void) BS3_CMN_NM(bs3ApicIpiHandler)(PBS3TRAPFRAME pTrapFrame)
148+
{
149+
PAPICCPU pCpu = NULL;
150+
uint32_t BS3_FAR volatile * const pau32Apic = bs3ApicGetMmioBase(&pCpu);
151+
if (pau32Apic)
152+
{
153+
pau32Apic[XAPIC_OFF_EOI / sizeof(uint32_t)] = 0;
154+
ASMAtomicIncU32(&g_cCpusResponded);
155+
}
156+
else
157+
bs3ApicTestFailedF("APIC not enabled!");
158+
159+
RT_NOREF(pTrapFrame);
160+
}
161+
162+
163+
BS3_DECL(uint32_t) bs3ApicApStartup_pe32(void)
164+
{
165+
uint64_t uApicBase2, uApicBase;
166+
167+
/* Enabling the APIC: */
168+
uApicBase = ASMRdMsr(MSR_IA32_APICBASE);
169+
ASMWrMsr(MSR_IA32_APICBASE, uApicBase | MSR_IA32_APICBASE_EN);
170+
uApicBase2 = ASMRdMsr(MSR_IA32_APICBASE);
171+
if (uApicBase2 == (uApicBase | MSR_IA32_APICBASE_EN))
172+
{
173+
uint8_t BS3_FAR volatile * const pabApic = (uint8_t BS3_FAR volatile *)((uintptr_t)uApicBase & X86_PAGE_4K_BASE_MASK);
174+
uint32_t BS3_FAR volatile * const pau32Apic = (uint32_t BS3_FAR volatile *)pabApic;
175+
uint32_t const idApic = pau32Apic[XAPIC_OFF_ID / sizeof(uint32_t)] >> 24;
176+
PAPICCPU pApicCpu = &g_paCpus[idApic];
177+
178+
pApicCpu->idCpu = idApic;
179+
180+
pau32Apic[XAPIC_OFF_SVR / sizeof(uint32_t)] = XAPIC_SVR_SOFTWARE_ENABLE;
181+
ASMAtomicIncU32(&g_cCpus);
182+
return (uint32_t)&g_paCpus[idApic].abStack[CPU_STACK_SIZE];
183+
}
184+
else
185+
Bs3TestFailedF("Enabling the APIC did not work (%#RX64)", uApicBase2);
186+
187+
return 0;
188+
}
189+
190+
191+
DECLINLINE(void) apicWriteIcr(uint32_t BS3_FAR volatile * const pau32Apic, uint8_t bDestination,
192+
XAPICDESTSHORTHAND enmShorthand, XAPICDELIVERYMODE enmDeliveryMode,
193+
XAPICDESTMODE enmMode, XAPICTRIGGERMODE enmTriggerMode,
194+
XAPICLEVEL enmLevel, uint8_t bVector)
195+
{
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;
203+
}
204+
205+
206+
static bool bs3ApicStartAllAps(uint32_t BS3_FAR volatile * const pau32Apic)
207+
{
208+
g_paCpus = (PAPICCPU)Bs3MemAllocZ(BS3MEMKIND_FLAT32, CPUS_MAX * sizeof(*g_paCpus));
209+
if (g_paCpus)
210+
{
211+
/* 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. */
212+
void BS3_FAR *pv = Bs3MemAllocZ(BS3MEMKIND_REAL, _4K);
213+
uintptr_t const uPtr = (uintptr_t)pv;
214+
if ( !(uPtr & X86_PAGE_OFFSET_MASK)
215+
&& (uPtr >> X86_PAGE_SHIFT) < UINT8_MAX)
216+
{
217+
uint8_t bVector = (uint8_t)(uPtr >> X86_PAGE_SHIFT);
218+
Bs3TestPrintf("AP Trampoline code area %#RX32 -> vector %#RX8\n", uPtr, bVector);
219+
Bs3MemCpy(pv, bs3ApicApTrampoline, (uintptr_t)bs3ApicApTrampoline_EndProc - (uintptr_t)bs3ApicApTrampoline);
220+
g_AddrApInitStack = uPtr + _4K; /* Use the unused top of the trampoline code for the initial stack. */
221+
222+
Bs3TrapSetHandler(APIC_VECTOR, bs3ApicIpiHandler_c32);
223+
224+
/* Enable xAPIC. */
225+
pau32Apic[XAPIC_OFF_SVR / sizeof(uint32_t)] = XAPIC_SVR_SOFTWARE_ENABLE;
226+
227+
/* Send INIT to all APs. */
228+
apicWriteIcr(pau32Apic, 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_INIT,
229+
XAPICDESTMODE_PHYSICAL, XAPICTRIGGERMODE_EDGE, XAPICINITLEVEL_ASSERT, 0);
230+
bs3ApicBusyWait(10 * 1000);
231+
232+
/* Send INIT de-assert to all APs. */
233+
apicWriteIcr(pau32Apic, 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_INIT,
234+
XAPICDESTMODE_PHYSICAL, XAPICTRIGGERMODE_EDGE, XAPICINITLEVEL_DEASSERT, 0);
235+
bs3ApicBusyWait(10 * 1000);
236+
237+
/* Send SIPI to APs. */
238+
apicWriteIcr(pau32Apic, 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_STARTUP,
239+
XAPICDESTMODE_PHYSICAL, XAPICTRIGGERMODE_EDGE, XAPICINITLEVEL_ASSERT, bVector);
240+
bs3ApicBusyWait(200);
241+
242+
/* Send SIPI to APs. */
243+
apicWriteIcr(pau32Apic, 0, XAPICDESTSHORTHAND_ALL_EXCL_SELF, XAPICDELIVERYMODE_STARTUP,
244+
XAPICDESTMODE_PHYSICAL, XAPICTRIGGERMODE_EDGE, XAPICINITLEVEL_ASSERT, bVector);
245+
/** @todo This assumes that the CPUs start in that time frame,
246+
* which might not be the case for IEM and heavy instruction logging enabled.
247+
* At some point we might want to look for the MP table and verify the number of CPUs
248+
* given there actually started.
249+
*/
250+
bs3ApicBusyWait(10 * 1000);
251+
252+
bs3ApicTestPrintf("%u CPUs started\n", g_cCpus);
253+
return true;
254+
}
255+
else
256+
Bs3TestFailedF("Returned AP trampoline memory is invalid (%#RX32)", uPtr);
257+
}
258+
else
259+
Bs3TestFailedF("Out of memory trying to allocate %#x bytes for the per-CPU data", CPUS_MAX * sizeof(*g_paCpus));
260+
261+
return false;
262+
}
263+
264+
265+
static void ProtModeApicTestsAp(uint32_t BS3_FAR volatile * const pau32Apic)
266+
{
267+
Bs3TestSub("Starting APs");
268+
if (bs3ApicStartAllAps(pau32Apic))
269+
{
270+
if (g_cCpus)
271+
{
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);
276+
277+
while (ASMAtomicReadU32(&g_cCpusResponded) < g_cCpus)
278+
ASMNopPause();
279+
}
280+
}
281+
}
282+
46283

47284
static void printBitmap(const char BS3_FAR *pszName, uint32_t BS3_FAR volatile *pau32Bitmap)
48285
{
@@ -112,6 +349,8 @@ BS3_DECL(void) ProtModeApicTests(void)
112349
printBitmap("ISR", &pau32Apic[XAPIC_OFF_ISR0 / sizeof(uint32_t)]);
113350
printBitmap("TMR", &pau32Apic[XAPIC_OFF_TMR0 / sizeof(uint32_t)]);
114351
printBitmap("IRR", &pau32Apic[XAPIC_OFF_IRR0 / sizeof(uint32_t)]);
352+
353+
ProtModeApicTestsAp((uint32_t BS3_FAR volatile *)pabApic);
115354
}
116355
else
117356
Bs3TestFailedF("Enabling the APIC did not work (%#RX64)", uApicBase2);

0 commit comments

Comments
 (0)