VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-invlpg.c32@ 106743

Last change on this file since 106743 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.1 KB
Line 
1/* $Id: bs3-cpu-basic-2-invlpg.c32 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * BS3Kit - bs3-cpu-basic-2, 32-bit C code for testing \#PF.
4 */
5
6/*
7 * Copyright (C) 2007-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <bs3kit.h>
42#include <iprt/asm-amd64-x86.h>
43
44
45
46/*********************************************************************************************************************************
47* Structures and Typedefs *
48*********************************************************************************************************************************/
49typedef void BS3_CALL FNBS3CPUBASIC2INVLPGFUNC(void);
50
51typedef struct BS3CPUBASIC2INVLPGTSTCMNMODE
52{
53 uint8_t bMode;
54 bool fPae;
55 FNBS3CPUBASIC2INVLPGFUNC *pfnInvlPg;
56} BS3CPUBASIC2INVLPGTSTCMNMODE;
57typedef BS3CPUBASIC2INVLPGTSTCMNMODE const *PCBS3CPUBASIC2INVLPGTSTCMNMODE;
58
59typedef struct BS3CPUBASIC2INVLPGSTATE
60{
61 /** The mode we're currently testing. */
62 uint8_t bMode;
63 /** CR4.PGE value. */
64 bool fGlobalPages;
65 /** Index into g_aCmnModes. */
66 uint8_t idxCmnMode;
67 uint8_t abReserved[5];
68
69 /** Address of the test area (alias). */
70 union
71 {
72 uint64_t u;
73 uint32_t u32;
74 uint16_t u16;
75 } uTestAddr;
76 /** Pointer to the orignal test area mapping. */
77 uint8_t *pbOrgTest;
78 /** The size of the test area (4 or 8 MB). */
79 uint32_t cbTest;
80 /** The large page size (2 or 4 MB). */
81 uint32_t cbLargePg;
82
83 /** Test paging information for the 1st large page (uTestAddr.u). */
84 BS3PAGINGINFO4ADDR PgInfo1;
85 /** Test paging information for the 2nd large page (uTestAddr.u + cbLargePg). */
86 BS3PAGINGINFO4ADDR PgInfo2;
87
88 /** Trap context frame. */
89 BS3TRAPFRAME TrapCtx;
90 /** Test context. */
91 BS3REGCTX Ctx;
92
93 /** The PML4E backup. */
94 uint64_t u64Pml4eBackup;
95 /** The PDPTE backup. */
96 uint64_t u64PdpteBackup;
97 /** The PDE backups. */
98 union
99 {
100 uint32_t Legacy;
101 uint64_t Pae;
102 } aPdeBackup[2];
103 /** The PTE backups. */
104 union
105 {
106 uint32_t Legacy[X86_PG_ENTRIES];
107 uint64_t Pae[X86_PG_PAE_ENTRIES];
108 } aPteBackup[2];
109
110} BS3CPUBASIC2INVLPGSTATE;
111/** Pointer to state for the \#PF test. */
112typedef BS3CPUBASIC2INVLPGSTATE *PBS3CPUBASIC2INVLPGSTATE;
113
114
115
116/*********************************************************************************************************************************
117* Internal Functions *
118*********************************************************************************************************************************/
119FNBS3CPUBASIC2INVLPGFUNC bs3CpuBasic2_invlpg_4mb_c16;
120FNBS3CPUBASIC2INVLPGFUNC bs3CpuBasic2_invlpg_4mb_c32;
121FNBS3CPUBASIC2INVLPGFUNC bs3CpuBasic2_invlpg_2mb_c16;
122FNBS3CPUBASIC2INVLPGFUNC bs3CpuBasic2_invlpg_2mb_c32;
123FNBS3CPUBASIC2INVLPGFUNC bs3CpuBasic2_invlpg_2mb_c64;
124
125
126/*********************************************************************************************************************************
127* Global Variables *
128*********************************************************************************************************************************/
129static const BS3CPUBASIC2INVLPGTSTCMNMODE g_aCmnModes[] =
130{
131 { BS3_MODE_CODE_16, false, bs3CpuBasic2_invlpg_4mb_c16 },
132 { BS3_MODE_CODE_16, true, bs3CpuBasic2_invlpg_2mb_c16 },
133 { BS3_MODE_CODE_32, false, bs3CpuBasic2_invlpg_4mb_c32 },
134 { BS3_MODE_CODE_32, true, bs3CpuBasic2_invlpg_2mb_c32 },
135 { BS3_MODE_CODE_64, true, bs3CpuBasic2_invlpg_2mb_c64 },
136};
137
138
139
140/**
141 * Flush everything.
142 */
143static void bs3CpuBasic2InvlPg_FlushAll(void)
144{
145 uint32_t uCr4 = ASMGetCR4();
146 if (uCr4 & (X86_CR4_PGE | X86_CR4_PCIDE))
147 {
148 ASMSetCR4(uCr4 & ~(X86_CR4_PGE | X86_CR4_PCIDE));
149 ASMSetCR4(uCr4);
150 }
151 else
152 ASMReloadCR3();
153}
154
155
156/**
157 * Restores all the paging entries from backup and flushes everything.
158 *
159 * @param pThis Test state data.
160 */
161static void bs3CpuBasic2InvlPg_RestoreFromBackups(PBS3CPUBASIC2INVLPGSTATE pThis)
162{
163 Bs3MemCpy(pThis->PgInfo1.u.Legacy.pPte, &pThis->aPteBackup[0], sizeof(pThis->aPteBackup[0]));
164 Bs3MemCpy(pThis->PgInfo2.u.Legacy.pPte, &pThis->aPteBackup[1], sizeof(pThis->aPteBackup[1]));
165 if (pThis->PgInfo1.cEntries == 2)
166 {
167 pThis->PgInfo1.u.Legacy.pPde->u = pThis->aPdeBackup[0].Legacy;
168 pThis->PgInfo2.u.Legacy.pPde->u = pThis->aPdeBackup[1].Legacy;
169 }
170 else
171 {
172 pThis->PgInfo1.u.Pae.pPde->u = pThis->aPdeBackup[0].Pae;
173 pThis->PgInfo2.u.Pae.pPde->u = pThis->aPdeBackup[1].Pae;
174 if (pThis->PgInfo1.cEntries > 2)
175 pThis->PgInfo1.u.Pae.pPdpe->u = pThis->u64PdpteBackup;
176 if (pThis->PgInfo1.cEntries > 3)
177 pThis->PgInfo1.u.Pae.pPml4e->u = pThis->u64Pml4eBackup;
178 }
179 bs3CpuBasic2InvlPg_FlushAll();
180}
181
182
183/**
184 * Worker for bs3CpuBasic2_InvlPg_c32 that does the actual testing.
185 *
186 * Caller does all the cleaning up.
187 *
188 * @returns Error count.
189 * @param pThis Test state data.
190 * @param fGlobalPages Whether global pages are enabled.
191 */
192static uint8_t bs3CpuBasic2_InvlPgWorker(PBS3CPUBASIC2INVLPGSTATE pThis, bool const fGlobalPages)
193{
194 /*
195 * Set up the test context.
196 */
197 uint32_t fPdeAttribs = X86_PDE4M_P | X86_PDE4M_RW | X86_PDE4M_US | X86_PDE4M_A | X86_PDE4M_PS;
198 pThis->fGlobalPages = fGlobalPages;
199 if (fGlobalPages)
200 fPdeAttribs |= X86_PDE4M_G;
201
202 Bs3RegCtxSaveEx(&pThis->Ctx, pThis->bMode, 640);
203 pThis->Ctx.rbx.u = pThis->uTestAddr.u;
204 if (pThis->PgInfo1.cEntries == 2)
205 pThis->Ctx.rdi.u = (uintptr_t)pThis->PgInfo1.u.Legacy.pPde;
206 else
207 pThis->Ctx.rdi.u = (uintptr_t)pThis->PgInfo1.u.Pae.pPde;
208 pThis->Ctx.rdx.u = (uintptr_t)pThis->pbOrgTest | fPdeAttribs;
209 pThis->Ctx.rsi.u = ((uintptr_t)pThis->pbOrgTest | fPdeAttribs) + pThis->cbLargePg;
210
211 /*
212 * Execute the testcase.
213 */
214 Bs3RegCtxSetRipCsFromCurPtr(&pThis->Ctx, g_aCmnModes[pThis->idxCmnMode].pfnInvlPg);
215 Bs3TrapSetJmpAndRestore(&pThis->Ctx, &pThis->TrapCtx);
216
217 if ( pThis->TrapCtx.bXcpt != X86_XCPT_BP
218 || pThis->TrapCtx.Ctx.rax.u != 0)
219 {
220 Bs3TestFailedF("bXcpt=%#x rax=%#RX64 rdx=%#RX64\n", pThis->TrapCtx.bXcpt,
221 pThis->TrapCtx.Ctx.rax.u, pThis->TrapCtx.Ctx.rdx.u);
222 Bs3TrapPrintFrame(&pThis->TrapCtx);
223 }
224
225
226 /*
227 * Cleanup.
228 */
229 bs3CpuBasic2InvlPg_RestoreFromBackups(pThis);
230 return 0;
231}
232
233#include "bs3-cmn-memory.h"
234
235
236BS3_DECL_CALLBACK(uint8_t) bs3CpuBasic2_InvlPg_c32(uint8_t bMode)
237{
238 uint32_t const cbLargePg = BS3_MODE_IS_LEGACY_PAGING(bMode) ? _4M : _2M;
239 void *pvTestUnaligned;
240 uint32_t cbTestUnaligned;
241 void *pvTmp;
242 uint8_t bRet = 1;
243 int rc;
244 PBS3CPUBASIC2INVLPGSTATE pState;
245
246 /*
247 * Only test on 486+ with 4M / 2MB page support.
248 */
249 if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) < BS3CPU_80486)
250 return BS3TESTDOMODE_SKIPPED;
251 if (!(g_uBs3CpuDetected & BS3CPU_F_CPUID))
252 return BS3TESTDOMODE_SKIPPED;
253 if (!(ASMCpuId_EDX(1) & X86_CPUID_FEATURE_EDX_PSE))
254 return BS3TESTDOMODE_SKIPPED;
255
256 /* Skip v86 modes, as those are ring-0 and invlpg doesn't work there.
257 Also, they can't address a whole page. (Test config should ensure
258 we don't get these, but just to be on the safe side.)*/
259 if (BS3_MODE_IS_V86(bMode))
260 return BS3TESTDOMODE_SKIPPED;
261
262 /*
263 * Allocate two large pages.
264 *
265 * HACK ALERT! We allocate a chunk of temporary memory before the two large
266 * pages to prevent heap fragmentation by the Bs3PagingAlias calls causing
267 * trouble in later iterations and tests.
268 */
269 pvTmp = Bs3MemAlloc(BS3MEMKIND_FLAT32, _512K);
270 cbTestUnaligned = cbLargePg * 3;
271 pvTestUnaligned = Bs3MemAlloc(BS3MEMKIND_FLAT32, cbTestUnaligned);
272 Bs3MemFree(pvTmp, _512K);
273 if (!pvTestUnaligned)
274 {
275 cbTestUnaligned = cbLargePg * 2;
276 pvTestUnaligned = Bs3MemAlloc(BS3MEMKIND_FLAT32, cbTestUnaligned);
277 if ((uintptr_t)pvTestUnaligned & (cbLargePg - 1))
278 {
279 Bs3MemFree(pvTestUnaligned, cbTestUnaligned);
280 pvTestUnaligned = NULL;
281 }
282 if (!pvTestUnaligned)
283 {
284 Bs3TestFailedF("Failed to allocate two large pages (%#x bytes)!\n", cbTestUnaligned);
285 return 1;
286 }
287 }
288
289 /*
290 * Allocate and initialize the state.
291 */
292 pState = (PBS3CPUBASIC2INVLPGSTATE)Bs3MemAllocZ(BS3MEMKIND_FLAT32, sizeof(*pState));
293 if (pState)
294 {
295 pState->bMode = bMode;
296
297 pState->idxCmnMode = 0;
298 while ( g_aCmnModes[pState->idxCmnMode].bMode != (bMode & BS3_MODE_CODE_MASK)
299 || g_aCmnModes[pState->idxCmnMode].fPae != !BS3_MODE_IS_LEGACY_PAGING(bMode))
300 pState->idxCmnMode++;
301 if (pState->idxCmnMode >= RT_ELEMENTS(g_aCmnModes))
302 {
303 Bs3TestFailed("No matching g_aCmnModes entry!");
304 pState->idxCmnMode = 0;
305 }
306
307 /* Complete the allocation of the two large pages. */
308 pState->cbLargePg = cbLargePg;
309 pState->cbTest = cbLargePg * 2;
310 pState->pbOrgTest = (uint8_t *)(((uintptr_t)pvTestUnaligned + cbLargePg - 1) & ~(cbLargePg - 1));
311
312 /*
313 * Alias this memory far away from where our code and data lives.
314 */
315 if (bMode & BS3_MODE_CODE_64)
316 pState->uTestAddr.u = UINT64_C(0xffffc486b0000000) + cbLargePg * 7;
317 else
318 pState->uTestAddr.u = UINT32_C(0x80000000) + cbLargePg * 3;
319 rc = Bs3PagingAlias(pState->uTestAddr.u, (uintptr_t)pState->pbOrgTest, pState->cbTest,
320 X86_PTE_P | X86_PTE_RW | X86_PTE_US);
321 if (RT_SUCCESS(rc))
322 {
323 rc = Bs3PagingQueryAddressInfo(pState->uTestAddr.u, &pState->PgInfo1);
324 if (RT_SUCCESS(rc))
325 rc = Bs3PagingQueryAddressInfo(pState->uTestAddr.u + cbLargePg, &pState->PgInfo2);
326 if (RT_SUCCESS(rc))
327 {
328 /* Backup the paging structures. */
329 /** @todo try use this to attempt mixing 4/2M and 4K entries in the TLB for
330 * the same large page. */
331 Bs3MemCpy(&pState->aPteBackup[0], pState->PgInfo1.u.Legacy.pPte, sizeof(pState->aPteBackup[0].Legacy));
332 Bs3MemCpy(&pState->aPteBackup[1], pState->PgInfo2.u.Legacy.pPte, sizeof(pState->aPteBackup[1].Legacy));
333 if (pState->PgInfo1.cEntries == 2)
334 {
335 pState->aPdeBackup[0].Legacy = pState->PgInfo1.u.Legacy.pPde->u;
336 pState->aPdeBackup[1].Legacy = pState->PgInfo2.u.Legacy.pPde->u;
337 }
338 else
339 {
340 pState->aPdeBackup[0].Pae = pState->PgInfo1.u.Pae.pPde->u;
341 pState->aPdeBackup[1].Pae = pState->PgInfo2.u.Pae.pPde->u;
342 if (pState->PgInfo1.cEntries > 2)
343 pState->u64PdpteBackup = pState->PgInfo1.u.Pae.pPdpe->u;
344 if (pState->PgInfo1.cEntries > 3)
345 pState->u64Pml4eBackup = pState->PgInfo1.u.Pae.pPml4e->u;
346 }
347
348 /** @todo to 16-bit properly? */
349
350 /*
351 * Initialize the memory buffers by stamping each dword with the low
352 * 32-bits of its aliased address.
353 */
354 {
355 uint32_t uValue = pState->uTestAddr.u32;
356 uint32_t *pu32Dst = (uint32_t *)pState->pbOrgTest;
357 uint32_t cLeft = pState->cbTest / sizeof(*pu32Dst);
358 while (cLeft-- > 0)
359 {
360 *pu32Dst = uValue;
361 uValue += sizeof(*pu32Dst);
362 pu32Dst++;
363 }
364 }
365
366 /*
367 * Do the testing.
368 */
369 {
370 uint32_t const cr0 = ASMGetCR0();
371 uint32_t const cr4 = ASMGetCR4();
372
373 ASMSetCR0(cr0 | X86_CR0_WP);
374 ASMSetCR4(cr4 & ~X86_CR4_PGE);
375 bRet = bs3CpuBasic2_InvlPgWorker(pState, false /*fGlobalPages*/);
376
377 ASMSetCR4(cr4 | X86_CR4_PGE);
378 bRet = bs3CpuBasic2_InvlPgWorker(pState, true /*fGlobalPages*/);
379
380 ASMSetCR4(cr4);
381 ASMSetCR0(cr0);
382 }
383
384 bs3CpuBasic2InvlPg_RestoreFromBackups(pState);
385 }
386 else
387 Bs3TestFailedF("Bs3PagingQueryAddressInfo failed: %d\n", rc);
388 Bs3PagingUnalias(pState->uTestAddr.u, pState->cbTest);
389 }
390 else
391 Bs3TestFailedF("Bs3PagingAlias failed! rc=%d\n", rc);
392 Bs3MemFree(pState, sizeof(*pState));
393 }
394 else
395 Bs3TestFailedF("Bs3MemAlloc/state failed! cb=%#x\n", sizeof(*pState));
396 Bs3MemFree(pvTestUnaligned, cbTestUnaligned);
397 return bRet;
398}
399
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette