/* $Id: bs3-cpu-basic-2-pf.c32 $ */ /** @file * BS3Kit - bs3-cpu-basic-2, 32-bit C code for testing \#PF. */ /* * Copyright (C) 2007-2022 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included * in the VirtualBox distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. * * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #include #include /********************************************************************************************************************************* * Defined Constants And Macros * *********************************************************************************************************************************/ #define CHECK_MEMBER(a_pszMode, a_szName, a_szFmt, a_Actual, a_Expected) \ do { \ if ((a_Actual) == (a_Expected)) { /* likely */ } \ else Bs3TestFailedF("%u - %s: " a_szName "=" a_szFmt " expected " a_szFmt, \ g_usBs3TestStep, (a_pszMode), (a_Actual), (a_Expected)); \ } while (0) #define BS3CPUBASIC2PF_HALT(pThis) \ do { \ Bs3TestPrintf("Halting: pteworker=%s store=%s accessor=%s\n", \ pThis->pszPteWorker, pThis->pszStore, pThis->pszAccessor); \ ASMHalt(); \ } while (0) /** @def BS3CPUBASIC2PF_FASTER * This is useful for IEM execution. */ #define BS3CPUBASIC2PF_FASTER /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ typedef void BS3_CALL FNBS3CPUBASIC2PFSNIPPET(void); typedef struct FNBS3CPUBASIC2PFTSTCODE { FNBS3CPUBASIC2PFSNIPPET *pfn; uint8_t offUd2; } FNBS3CPUBASIC2PFTSTCODE; typedef FNBS3CPUBASIC2PFTSTCODE const *PCFNBS3CPUBASIC2PFTSTCODE; typedef struct BS3CPUBASIC2PFTTSTCMNMODE { uint8_t bMode; FNBS3CPUBASIC2PFTSTCODE MovLoad; FNBS3CPUBASIC2PFTSTCODE MovStore; FNBS3CPUBASIC2PFTSTCODE Xchg; FNBS3CPUBASIC2PFTSTCODE CmpXchg; FNBS3CPUBASIC2PFTSTCODE DivMem; } BS3CPUBASIC2PFTTSTCMNMODE; typedef BS3CPUBASIC2PFTTSTCMNMODE const *PCBS3CPUBASIC2PFTTSTCMNMODE; typedef struct BS3CPUBASIC2PFSTATE { /** The mode we're currently testing. */ uint8_t bMode; /** The size of a natural access. */ uint8_t cbAccess; /** The common mode functions. */ PCBS3CPUBASIC2PFTTSTCMNMODE pCmnMode; /** Address of the test area (alias). */ union { uint64_t u; uint32_t u32; uint16_t u16; } uTestAddr; /** Pointer to the orignal test area mapping. */ uint8_t *pbOrgTest; /** The size of the test area (at least two pages). */ uint32_t cbTest; /** cbTest expressed as a page count. */ uint16_t cTestPages; /** The number of PTEs in the first PTE, i.e. what we can * safely access via PgInfo.u.Pae.pPte/PgInfo.u.Legacy.pPte. */ uint16_t cTest1stPtes; /** The number of PDEs for cTestPages. */ uint16_t cTestPdes; /** 16-bit data selector for uTestAddr.u32. */ uint16_t uSel16TestData; /** 16-bit code selector for uTestAddr.u32. */ uint16_t uSel16TestCode; /** The size of the PDE backup. */ uint16_t cbPdeBackup; /** The size of the PTE backup. */ uint16_t cbPteBackup; /** Test paging information for uTestAddr.u. */ BS3PAGINGINFO4ADDR PgInfo; /** Set if we can use the INVLPG instruction. */ bool fUseInvlPg; /** Physical addressing width. */ uint8_t cBitsPhysWidth; /** Reflects CR0.WP. */ bool fWp; /** Reflects EFER.NXE & CR4.PAE. */ bool fNxe; const char *pszAccessor; const char *pszPteWorker; const char *pszStore; /** Trap context frame. */ BS3TRAPFRAME TrapCtx; /** Expected result context. */ BS3REGCTX ExpectCtx; /** The PML4E backup. */ uint64_t u64Pml4eBackup; /** The PDPTE backup. */ uint64_t u64PdpteBackup; /** The PDE backup. */ uint64_t au64PdeBackup[16]; /** The PTE backup. */ union { uint32_t Legacy[X86_PG_ENTRIES]; uint64_t Pae[X86_PG_PAE_ENTRIES]; } PteBackup; } BS3CPUBASIC2PFSTATE; /** Pointer to state for the \#PF test. */ typedef BS3CPUBASIC2PFSTATE *PBS3CPUBASIC2PFSTATE; /** * Paging modification worker. */ typedef struct BS3CPUBASIC2PFMODPT { const char *pszName; uint32_t fPresent : 1; uint32_t fUser : 1; uint32_t fWriteable : 1; uint32_t fNoExecute : 1; uint32_t fReserved : 1; uint32_t uModifyArg : 24; void (*pfnModify)(PBS3CPUBASIC2PFSTATE pThis, unsigned iStore, struct BS3CPUBASIC2PFMODPT const *pEntry, uint32_t fClearMask, uint32_t fSetMask); bool (*pfnApplicable)(PBS3CPUBASIC2PFSTATE pThis, struct BS3CPUBASIC2PFMODPT const *pEntry); } BS3CPUBASIC2PFMODPT; typedef BS3CPUBASIC2PFMODPT const *PCBS3CPUBASIC2PFMODPT; /** Page level protection. Alternative is page directory or higher level. */ #define BS3CB2PFACC_F_PAGE_LEVEL RT_BIT(0) /** Directly access the boobytrapped page, no edging on or off it. */ #define BS3CB2PFACC_F_DIRECT RT_BIT(1) /** * Memory accessor. */ typedef struct BS3CPUBASIC2PFACCESSOR { /** Accessor name. */ const char *pszName; /** The accessor. */ void (*pfnAccessor)(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags, uint8_t bXcpt, uint8_t uPfErrCd); /** The X86_TRAP_PF_XXX access flags this access sets. */ uint32_t fAccess; /** The exception when things are fine. */ uint8_t bOkayXcpt; } BS3CPUBASIC2PFACCESSOR; typedef const BS3CPUBASIC2PFACCESSOR *PCBS3CPUBASIC2PFACCESSOR; /********************************************************************************************************************************* * Internal Functions * *********************************************************************************************************************************/ FNBS3TESTDOMODE bs3CpuBasic2_RaiseXcpt0e_c32; /* bs3-cpu-basic-2-asm.asm: */ void BS3_CALL bs3CpuBasic2_Store_mov_c32(void *pvDst, uint32_t uValue, uint32_t uOld); void BS3_CALL bs3CpuBasic2_Store_xchg_c32(void *pvDst, uint32_t uValue, uint32_t uOld); void BS3_CALL bs3CpuBasic2_Store_cmpxchg_c32(void *pvDst, uint32_t uValue, uint32_t uOld); /* bs3-cpu-basic-2-template.mac: */ FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_mov_ax_ds_bx__ud2_c16; FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_mov_ds_bx_ax__ud2_c16; FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_xchg_ds_bx_ax__ud2_c16; FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c16; FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_div_ds_bx__ud2_c16; FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_mov_ax_ds_bx__ud2_c32; FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_mov_ds_bx_ax__ud2_c32; FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_xchg_ds_bx_ax__ud2_c32; FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c32; FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_div_ds_bx__ud2_c32; FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_mov_ax_ds_bx__ud2_c64; FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_mov_ds_bx_ax__ud2_c64; FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_xchg_ds_bx_ax__ud2_c64; FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c64; FNBS3CPUBASIC2PFSNIPPET bs3CpuBasic2_div_ds_bx__ud2_c64; /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ /** Page table access functions. */ static const struct { const char *pszName; void (BS3_CALL *pfnStore)(void *pvDst, uint32_t uValue, uint32_t uOld); } g_aStoreMethods[] = { { "mov", bs3CpuBasic2_Store_mov_c32 }, { "xchg", bs3CpuBasic2_Store_xchg_c32 }, { "cmpxchg", bs3CpuBasic2_Store_cmpxchg_c32 }, }; static const BS3CPUBASIC2PFTTSTCMNMODE g_aCmnModes[] = { { BS3_MODE_CODE_16, { bs3CpuBasic2_mov_ax_ds_bx__ud2_c16, 2 }, { bs3CpuBasic2_mov_ds_bx_ax__ud2_c16, 2 }, { bs3CpuBasic2_xchg_ds_bx_ax__ud2_c16, 2 }, { bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c16, 3 }, { bs3CpuBasic2_div_ds_bx__ud2_c16, 2 }, }, { BS3_MODE_CODE_32, { bs3CpuBasic2_mov_ax_ds_bx__ud2_c32, 2 }, { bs3CpuBasic2_mov_ds_bx_ax__ud2_c32, 2 }, { bs3CpuBasic2_xchg_ds_bx_ax__ud2_c32, 2 }, { bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c32, 3 }, { bs3CpuBasic2_div_ds_bx__ud2_c32, 2 }, }, { BS3_MODE_CODE_64, { bs3CpuBasic2_mov_ax_ds_bx__ud2_c64, 2 + 1 }, { bs3CpuBasic2_mov_ds_bx_ax__ud2_c64, 2 + 1 }, { bs3CpuBasic2_xchg_ds_bx_ax__ud2_c64, 2 + 1 }, { bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c64, 3 + 1 }, { bs3CpuBasic2_div_ds_bx__ud2_c64, 2 + 1 }, }, { BS3_MODE_CODE_V86, { bs3CpuBasic2_mov_ax_ds_bx__ud2_c16, 2 }, { bs3CpuBasic2_mov_ds_bx_ax__ud2_c16, 2 }, { bs3CpuBasic2_xchg_ds_bx_ax__ud2_c16, 2 }, { bs3CpuBasic2_cmpxchg_ds_bx_cx__ud2_c16, 3 }, { bs3CpuBasic2_div_ds_bx__ud2_c16, 2 }, }, }; /** * Compares a CPU trap. */ static void bs3CpuBasic2Pf_CompareCtx(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pExpectCtx, int cbPcAdjust, uint8_t bXcpt, unsigned uErrCd) { const char *pszHint = "xxxx"; uint16_t const cErrorsBefore = Bs3TestSubErrorCount(); uint32_t fExtraEfl; CHECK_MEMBER(pszHint, "bXcpt", "%#04x", pThis->TrapCtx.bXcpt, bXcpt); CHECK_MEMBER(pszHint, "uErrCd", "%#06RX16", (uint16_t)pThis->TrapCtx.uErrCd, (uint16_t)uErrCd); /* 486 only writes a word */ fExtraEfl = X86_EFL_RF; if (BS3_MODE_IS_16BIT_SYS(g_bBs3CurrentMode)) fExtraEfl = 0; else fExtraEfl = X86_EFL_RF; Bs3TestCheckRegCtxEx(&pThis->TrapCtx.Ctx, pExpectCtx, cbPcAdjust, 0 /*cbSpAdjust*/, fExtraEfl, pszHint, g_usBs3TestStep); if (Bs3TestSubErrorCount() != cErrorsBefore) { Bs3TrapPrintFrame(&pThis->TrapCtx); #if 1 Bs3TestPrintf("Halting: g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected); Bs3TestPrintf("Halting: bXcpt=%#x uErrCd=%#x\n", bXcpt, uErrCd); BS3CPUBASIC2PF_HALT(pThis); #endif } } /** * Compares a CPU trap. */ static void bs3CpuBasic2Pf_CompareSimpleCtx(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pStartCtx, int offAddPC, uint8_t bXcpt, unsigned uErrCd, uint64_t uCr2) { const char *pszHint = "xxxx"; uint16_t const cErrorsBefore = Bs3TestSubErrorCount(); uint64_t const uSavedCr2 = pStartCtx->cr2.u; uint32_t fExtraEfl; CHECK_MEMBER(pszHint, "bXcpt", "%#04x", pThis->TrapCtx.bXcpt, bXcpt); CHECK_MEMBER(pszHint, "uErrCd", "%#06RX16", (uint16_t)pThis->TrapCtx.uErrCd, (uint16_t)uErrCd); /* 486 only writes a word */ fExtraEfl = X86_EFL_RF; if (BS3_MODE_IS_16BIT_SYS(g_bBs3CurrentMode)) fExtraEfl = 0; else fExtraEfl = X86_EFL_RF; pStartCtx->cr2.u = uCr2; Bs3TestCheckRegCtxEx(&pThis->TrapCtx.Ctx, pStartCtx, offAddPC, 0 /*cbSpAdjust*/, fExtraEfl, pszHint, g_usBs3TestStep); pStartCtx->cr2.u = uSavedCr2; if (Bs3TestSubErrorCount() != cErrorsBefore) { Bs3TrapPrintFrame(&pThis->TrapCtx); #if 1 Bs3TestPrintf("Halting: g_uBs3CpuDetected=%#x\n", g_uBs3CpuDetected); Bs3TestPrintf("Halting: bXcpt=%#x uErrCd=%#x\n", bXcpt, uErrCd); BS3CPUBASIC2PF_HALT(pThis); #endif } } /** * Checks the trap context for a simple \#PF trap. */ static void bs3CpuBasic2Pf_CompareSimplePf(PBS3CPUBASIC2PFSTATE pThis, PCBS3REGCTX pStartCtx, int offAddPC, unsigned uErrCd, uint64_t uCr2) { bs3CpuBasic2Pf_CompareSimpleCtx(pThis, (PBS3REGCTX)pStartCtx, offAddPC, X86_XCPT_PF, uErrCd, uCr2); } /** * Checks the trap context for a simple \#UD trap. */ static void bs3CpuBasic2Pf_CompareSimpleUd(PBS3CPUBASIC2PFSTATE pThis, PCBS3REGCTX pStartCtx, int offAddPC) { bs3CpuBasic2Pf_CompareSimpleCtx(pThis, (PBS3REGCTX)pStartCtx, offAddPC, X86_XCPT_UD, 0, pStartCtx->cr2.u); } /** * Restores all the paging entries from backup and flushes everything. */ static void bs3CpuBasic2Pf_FlushAll(void) { if ((g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486) { uint32_t uCr4 = ASMGetCR4(); if (uCr4 & (X86_CR4_PGE | X86_CR4_PCIDE)) { ASMSetCR4(uCr4 & ~(X86_CR4_PGE | X86_CR4_PCIDE)); ASMSetCR4(uCr4); return; } } ASMReloadCR3(); } /** * Restores all the paging entries from backup and flushes everything. * * @param pThis Test state data. */ static void bs3CpuBasic2Pf_RestoreFromBackups(PBS3CPUBASIC2PFSTATE pThis) { Bs3MemCpy(pThis->PgInfo.u.Legacy.pPte, &pThis->PteBackup, pThis->cbPteBackup); Bs3MemCpy(pThis->PgInfo.u.Legacy.pPde, pThis->au64PdeBackup, pThis->cbPdeBackup); if (pThis->PgInfo.cEntries > 2) pThis->PgInfo.u.Pae.pPdpe->u = pThis->u64PdpteBackup; if (pThis->PgInfo.cEntries > 3) pThis->PgInfo.u.Pae.pPml4e->u = pThis->u64Pml4eBackup; bs3CpuBasic2Pf_FlushAll(); } /** @name BS3CPUBASIC2PFACCESSOR::pfnAccessor Implementations * @{ */ static void bs3CpuBasic2Pf_DoExec(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags, uint8_t bXcpt, uint8_t uPfErrCd) { uint8_t *pbOrgTest = pThis->pbOrgTest; unsigned offEnd = fFlags & BS3CB2PFACC_F_DIRECT ? X86_PAGE_SIZE + 1 : X86_PAGE_SIZE + 2; unsigned off = fFlags & BS3CB2PFACC_F_DIRECT ? offEnd - 1 : X86_PAGE_SIZE - 5; for (; off < offEnd; off++) { /* Emit a little bit of code (using the original allocation mapping) and point pCtx to it. */ pbOrgTest[off + 0] = X86_OP_PRF_SIZE_ADDR; pbOrgTest[off + 1] = X86_OP_PRF_SIZE_OP; pbOrgTest[off + 2] = 0x90; /* NOP */ pbOrgTest[off + 3] = 0x0f; /* UD2 */ pbOrgTest[off + 4] = 0x0b; pbOrgTest[off + 5] = 0xeb; /* JMP $-4 */ pbOrgTest[off + 6] = 0xfc; switch (pThis->bMode & BS3_MODE_CODE_MASK) { default: pCtx->rip.u = pThis->uTestAddr.u + off; break; case BS3_MODE_CODE_16: Bs3SelSetup16BitCode(&Bs3GdteSpare01, pThis->uTestAddr.u32, pCtx->bCpl); pCtx->rip.u = off; pCtx->cs = BS3_SEL_SPARE_01 | pCtx->bCpl; break; case BS3_MODE_CODE_V86: /** @todo fix me. */ return; } //Bs3TestPrintf("cs:rip=%04x:%010RX64 iRing=%d\n", pCtx->cs, pCtx->rip.u, pCtx->bCpl); Bs3TrapSetJmpAndRestore(pCtx, &pThis->TrapCtx); //Bs3TestPrintf("off=%#06x bXcpt=%#x uErrCd=%#RX64\n", off, pThis->TrapCtx.bXcpt, pThis->TrapCtx.uErrCd); if ( bXcpt != X86_XCPT_PF || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off < X86_PAGE_SIZE - 4)) bs3CpuBasic2Pf_CompareSimpleUd(pThis, pCtx, 3); else if (!(fFlags & BS3CB2PFACC_F_PAGE_LEVEL) || off >= X86_PAGE_SIZE) bs3CpuBasic2Pf_CompareSimplePf(pThis, pCtx, 0, uPfErrCd, pThis->uTestAddr.u + off); else bs3CpuBasic2Pf_CompareSimplePf(pThis, pCtx, off + 3 == X86_PAGE_SIZE || off + 4 == X86_PAGE_SIZE ? RT_MIN(X86_PAGE_SIZE, off + 3) - off : 0, uPfErrCd, pThis->uTestAddr.u + RT_MIN(X86_PAGE_SIZE, off + 4)); } } static void bs3CpuBasic2Pf_SetCsEip(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, PCFNBS3CPUBASIC2PFTSTCODE pCode) { switch (pThis->bMode & BS3_MODE_CODE_MASK) { default: pCtx->rip.u = (uintptr_t)pCode->pfn; break; case BS3_MODE_CODE_16: { uint32_t uFar16 = Bs3SelFlatCodeToProtFar16((uintptr_t)pCode->pfn); pCtx->rip.u = (uint16_t)uFar16; pCtx->cs = (uint16_t)(uFar16 >> 16) | pCtx->bCpl; pCtx->cs += (uint16_t)pCtx->bCpl << BS3_SEL_RING_SHIFT; break; } case BS3_MODE_CODE_V86: { uint32_t uFar16 = Bs3SelFlatCodeToRealMode((uintptr_t)pCode->pfn); pCtx->rip.u = (uint16_t)uFar16; pCtx->cs = (uint16_t)(uFar16 >> 16); break; } } } /** * Test a simple load instruction around the edges of page two. * * @param pThis The test stat data. * @param pCtx The test context. * @param fFlags BS3CB2PFACC_F_XXX. * @param bXcpt X86_XCPT_PF if this can cause \#PFs, otherwise * X86_XCPT_UD. * @param uPfErrCd The error code for \#PFs. */ static void bs3CpuBasic2Pf_DoMovLoad(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags, uint8_t bXcpt, uint8_t uPfErrCd) { static uint64_t const s_uValue = UINT64_C(0x7c4d0114428d); uint64_t uExpectRax; unsigned i; /* * Adjust the incoming context and calculate our expections. */ bs3CpuBasic2Pf_SetCsEip(pThis, pCtx, &pThis->pCmnMode->MovLoad); Bs3MemCpy(&pThis->ExpectCtx, pCtx, sizeof(pThis->ExpectCtx)); switch (pThis->bMode & BS3_MODE_CODE_MASK) { case BS3_MODE_CODE_16: case BS3_MODE_CODE_V86: uExpectRax = (uint16_t)s_uValue | (pCtx->rax.u & UINT64_C(0xffffffffffff0000)); break; case BS3_MODE_CODE_32: uExpectRax = (uint32_t)s_uValue | (pCtx->rax.u & UINT64_C(0xffffffff00000000)); break; case BS3_MODE_CODE_64: uExpectRax = s_uValue; break; } if (uExpectRax == pCtx->rax.u) pCtx->rax.u = ~pCtx->rax.u; /* * Make two approaches to the test page (the 2nd one): * - i=0: Start on the 1st page and edge into the 2nd. * - i=1: Start at the end of the 2nd page and edge off it and into the 3rd. */ for (i = 0; i < 2; i++) { unsigned off = fFlags & BS3CB2PFACC_F_DIRECT ? X86_PAGE_SIZE : X86_PAGE_SIZE * (i + 1) - pThis->cbAccess; unsigned offEnd = fFlags & BS3CB2PFACC_F_DIRECT ? off + 1 : X86_PAGE_SIZE * (i + 1) + (i == 0 ? 8 : 7); for (; off < offEnd; off++) { *(uint64_t *)&pThis->pbOrgTest[off] = s_uValue; if (BS3_MODE_IS_16BIT_CODE(pThis->bMode)) pThis->ExpectCtx.rbx.u = pCtx->rbx.u = off; else pThis->ExpectCtx.rbx.u = pCtx->rbx.u = pThis->uTestAddr.u + off; Bs3TrapSetJmpAndRestore(pCtx, &pThis->TrapCtx); //Bs3TestPrintf("off=%#06x bXcpt=%#x uErrCd=%#RX64\n", off, pThis->TrapCtx.bXcpt, pThis->TrapCtx.uErrCd); if ( bXcpt != X86_XCPT_PF || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off >= X86_PAGE_SIZE * 2) || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off <= X86_PAGE_SIZE - pThis->cbAccess) ) { pThis->ExpectCtx.rax.u = uExpectRax; bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, pThis->pCmnMode->MovLoad.offUd2, X86_XCPT_UD, 0 /*uErrCd*/); pThis->ExpectCtx.rax = pCtx->rax; } else { if (off < X86_PAGE_SIZE) pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + X86_PAGE_SIZE; else pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + off; bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, 0 /*cbPcAdjust*/, bXcpt, uPfErrCd); pThis->ExpectCtx.cr2 = pCtx->cr2; } } if (fFlags & BS3CB2PFACC_F_DIRECT) break; } } /** * Test a simple store instruction around the edges of page two. * * @param pThis The test stat data. * @param pCtx The test context. * @param fFlags BS3CB2PFACC_F_XXX. * @param bXcpt X86_XCPT_PF if this can cause \#PFs, otherwise * X86_XCPT_UD. * @param uPfErrCd The error code for \#PFs. */ static void bs3CpuBasic2Pf_DoMovStore(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags, uint8_t bXcpt, uint8_t uPfErrCd) { static uint64_t const s_uValue = UINT64_C(0x3af45ead86a34a26); static uint64_t const s_uValueFlipped = UINT64_C(0xc50ba152795cb5d9); uint64_t const uRaxSaved = pCtx->rax.u; uint64_t uExpectStored; unsigned i; /* * Adjust the incoming context and calculate our expections. */ bs3CpuBasic2Pf_SetCsEip(pThis, pCtx, &pThis->pCmnMode->MovStore); if ((pThis->bMode & BS3_MODE_CODE_MASK) != BS3_MODE_CODE_64) pCtx->rax.u = (uint32_t)s_uValue; /* leave the upper part zero */ else pCtx->rax.u = s_uValue; Bs3MemCpy(&pThis->ExpectCtx, pCtx, sizeof(pThis->ExpectCtx)); switch (pThis->bMode & BS3_MODE_CODE_MASK) { case BS3_MODE_CODE_16: case BS3_MODE_CODE_V86: uExpectStored = (uint16_t)s_uValue | (s_uValueFlipped & UINT64_C(0xffffffffffff0000)); break; case BS3_MODE_CODE_32: uExpectStored = (uint32_t)s_uValue | (s_uValueFlipped & UINT64_C(0xffffffff00000000)); break; case BS3_MODE_CODE_64: uExpectStored = s_uValue; break; } /* * Make two approaches to the test page (the 2nd one): * - i=0: Start on the 1st page and edge into the 2nd. * - i=1: Start at the end of the 2nd page and edge off it and into the 3rd. */ for (i = 0; i < 2; i++) { unsigned off = fFlags & BS3CB2PFACC_F_DIRECT ? X86_PAGE_SIZE : X86_PAGE_SIZE * (i + 1) - pThis->cbAccess; unsigned offEnd = fFlags & BS3CB2PFACC_F_DIRECT ? off + 1 : X86_PAGE_SIZE * (i + 1) + (i == 0 ? 8 : 7); for (; off < offEnd; off++) { *(uint64_t *)&pThis->pbOrgTest[off] = s_uValueFlipped; if (BS3_MODE_IS_16BIT_CODE(pThis->bMode)) pThis->ExpectCtx.rbx.u = pCtx->rbx.u = off; else pThis->ExpectCtx.rbx.u = pCtx->rbx.u = pThis->uTestAddr.u + off; Bs3TrapSetJmpAndRestore(pCtx, &pThis->TrapCtx); //Bs3TestPrintf("off=%#06x bXcpt=%#x uErrCd=%#RX64\n", off, pThis->TrapCtx.bXcpt, pThis->TrapCtx.uErrCd); if ( bXcpt != X86_XCPT_PF || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off >= X86_PAGE_SIZE * 2) || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off <= X86_PAGE_SIZE - pThis->cbAccess) ) { bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, pThis->pCmnMode->MovStore.offUd2, X86_XCPT_UD, 0 /*uErrCd*/); if (*(uint64_t *)&pThis->pbOrgTest[off] != uExpectStored) Bs3TestFailedF("%u - %s: Stored %#RX64, expected %#RX64", g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], uExpectStored); } else { if (off < X86_PAGE_SIZE) pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + X86_PAGE_SIZE; else pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + off; bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, 0 /*cbPcAdjust*/, bXcpt, uPfErrCd); pThis->ExpectCtx.cr2 = pCtx->cr2; if (*(uint64_t *)&pThis->pbOrgTest[off] != s_uValueFlipped) Bs3TestFailedF("%u - %s: #PF'ed store modified memory: %#RX64, expected %#RX64", g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], s_uValueFlipped); } } if (fFlags & BS3CB2PFACC_F_DIRECT) break; } pCtx->rax.u = uRaxSaved; } /** * Test a xchg instruction around the edges of page two. * * @param pThis The test stat data. * @param pCtx The test context. * @param fFlags BS3CB2PFACC_F_XXX. * @param bXcpt X86_XCPT_PF if this can cause \#PFs, otherwise * X86_XCPT_UD. * @param uPfErrCd The error code for \#PFs. */ static void bs3CpuBasic2Pf_DoXchg(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags, uint8_t bXcpt, uint8_t uPfErrCd) { static uint64_t const s_uValue = UINT64_C(0xea58699648e2f32c); static uint64_t const s_uValueFlipped = UINT64_C(0x15a79669b71d0cd3); uint64_t const uRaxSaved = pCtx->rax.u; uint64_t uRaxIn; uint64_t uExpectedRax; uint64_t uExpectStored; unsigned i; /* * Adjust the incoming context and calculate our expections. */ bs3CpuBasic2Pf_SetCsEip(pThis, pCtx, &pThis->pCmnMode->Xchg); if ((pThis->bMode & BS3_MODE_CODE_MASK) != BS3_MODE_CODE_64) uRaxIn = (uint32_t)s_uValue; /* leave the upper part zero */ else uRaxIn = s_uValue; Bs3MemCpy(&pThis->ExpectCtx, pCtx, sizeof(pThis->ExpectCtx)); switch (pThis->bMode & BS3_MODE_CODE_MASK) { case BS3_MODE_CODE_16: case BS3_MODE_CODE_V86: uExpectedRax = (uint16_t)s_uValueFlipped | (uRaxIn & UINT64_C(0xffffffffffff0000)); uExpectStored = (uint16_t)s_uValue | (s_uValueFlipped & UINT64_C(0xffffffffffff0000)); break; case BS3_MODE_CODE_32: uExpectedRax = (uint32_t)s_uValueFlipped | (uRaxIn & UINT64_C(0xffffffff00000000)); uExpectStored = (uint32_t)s_uValue | (s_uValueFlipped & UINT64_C(0xffffffff00000000)); break; case BS3_MODE_CODE_64: uExpectedRax = s_uValueFlipped; uExpectStored = s_uValue; break; } /* * Make two approaches to the test page (the 2nd one): * - i=0: Start on the 1st page and edge into the 2nd. * - i=1: Start at the end of the 2nd page and edge off it and into the 3rd. */ for (i = 0; i < 2; i++) { unsigned off = fFlags & BS3CB2PFACC_F_DIRECT ? X86_PAGE_SIZE : X86_PAGE_SIZE * (i + 1) - pThis->cbAccess; unsigned offEnd = fFlags & BS3CB2PFACC_F_DIRECT ? off + 1 : X86_PAGE_SIZE * (i + 1) + (i == 0 ? 8 : 7); for (; off < offEnd; off++) { *(uint64_t *)&pThis->pbOrgTest[off] = s_uValueFlipped; pCtx->rax.u = uRaxIn; if (BS3_MODE_IS_16BIT_CODE(pThis->bMode)) pThis->ExpectCtx.rbx.u = pCtx->rbx.u = off; else pThis->ExpectCtx.rbx.u = pCtx->rbx.u = pThis->uTestAddr.u + off; Bs3TrapSetJmpAndRestore(pCtx, &pThis->TrapCtx); //Bs3TestPrintf("off=%#06x bXcpt=%#x uErrCd=%#RX64\n", off, pThis->TrapCtx.bXcpt, pThis->TrapCtx.uErrCd); if ( bXcpt != X86_XCPT_PF || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off >= X86_PAGE_SIZE * 2) || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off <= X86_PAGE_SIZE - pThis->cbAccess) ) { pThis->ExpectCtx.rax.u = uExpectedRax; bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, pThis->pCmnMode->Xchg.offUd2, X86_XCPT_UD, 0 /*uErrCd*/); if (*(uint64_t *)&pThis->pbOrgTest[off] != uExpectStored) Bs3TestFailedF("%u - %s: Stored %#RX64, expected %#RX64", g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], uExpectStored); } else { pThis->ExpectCtx.rax.u = uRaxIn; if (off < X86_PAGE_SIZE) pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + X86_PAGE_SIZE; else pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + off; bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, 0 /*cbPcAdjust*/, bXcpt, uPfErrCd); pThis->ExpectCtx.cr2 = pCtx->cr2; if (*(uint64_t *)&pThis->pbOrgTest[off] != s_uValueFlipped) Bs3TestFailedF("%u - %s: #PF'ed store modified memory: %#RX64, expected %#RX64", g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], s_uValueFlipped); } } if (fFlags & BS3CB2PFACC_F_DIRECT) break; } pCtx->rax.u = uRaxSaved; } /** * Test a cmpxchg instruction around the edges of page two. * * @param pThis The test stat data. * @param pCtx The test context. * @param fFlags BS3CB2PFACC_F_XXX. * @param bXcpt X86_XCPT_PF if this can cause \#PFs, otherwise * X86_XCPT_UD. * @param uPfErrCd The error code for \#PFs. * @param fMissmatch Whether to fail and not store (@c true), or succeed * and do the store. */ static void bs3CpuBasic2Pf_DoCmpXchg(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags, uint8_t bXcpt, uint8_t uPfErrCd, bool fMissmatch) { static uint64_t const s_uValue = UINT64_C(0xea58699648e2f32c); static uint64_t const s_uValueFlipped = UINT64_C(0x15a79669b71d0cd3); static uint64_t const s_uValueOther = UINT64_C(0x2171239bcb044c81); uint64_t const uRaxSaved = pCtx->rax.u; uint64_t const uRcxSaved = pCtx->rcx.u; uint64_t uRaxIn; uint64_t uExpectedRax; uint32_t uExpectedFlags; uint64_t uExpectStored; unsigned i; /* * Adjust the incoming context and calculate our expections. * Hint: CMPXCHG [xBX],xCX ; xAX compare and update implicit, ZF set to !fMissmatch. */ bs3CpuBasic2Pf_SetCsEip(pThis, pCtx, &pThis->pCmnMode->CmpXchg); if ((pThis->bMode & BS3_MODE_CODE_MASK) != BS3_MODE_CODE_64) { uRaxIn = (uint32_t)(fMissmatch ? s_uValueOther : s_uValueFlipped); /* leave the upper part zero */ pCtx->rcx.u = (uint32_t)s_uValue; /* ditto */ } else { uRaxIn = fMissmatch ? s_uValueOther : s_uValueFlipped; pCtx->rcx.u = s_uValue; } if (fMissmatch) pCtx->rflags.u32 |= X86_EFL_ZF; else pCtx->rflags.u32 &= ~X86_EFL_ZF; Bs3MemCpy(&pThis->ExpectCtx, pCtx, sizeof(pThis->ExpectCtx)); uExpectedFlags = pCtx->rflags.u32 & ~(X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_SF | X86_EFL_OF | X86_EFL_ZF); switch (pThis->bMode & BS3_MODE_CODE_MASK) { case BS3_MODE_CODE_16: case BS3_MODE_CODE_V86: uExpectedRax = (uint16_t)s_uValueFlipped | (uRaxIn & UINT64_C(0xffffffffffff0000)); uExpectStored = (uint16_t)s_uValue | (s_uValueFlipped & UINT64_C(0xffffffffffff0000)); uExpectedFlags |= !fMissmatch ? X86_EFL_ZF | X86_EFL_PF : X86_EFL_AF; break; case BS3_MODE_CODE_32: uExpectedRax = (uint32_t)s_uValueFlipped | (uRaxIn & UINT64_C(0xffffffff00000000)); uExpectStored = (uint32_t)s_uValue | (s_uValueFlipped & UINT64_C(0xffffffff00000000)); uExpectedFlags |= !fMissmatch ? X86_EFL_ZF | X86_EFL_PF : X86_EFL_AF; break; case BS3_MODE_CODE_64: uExpectedRax = s_uValueFlipped; uExpectStored = s_uValue; uExpectedFlags |= !fMissmatch ? X86_EFL_ZF | X86_EFL_PF : X86_EFL_AF; break; } if (fMissmatch) uExpectStored = s_uValueFlipped; /* * Make two approaches to the test page (the 2nd one): * - i=0: Start on the 1st page and edge into the 2nd. * - i=1: Start at the end of the 2nd page and edge off it and into the 3rd. */ for (i = 0; i < 2; i++) { unsigned off = fFlags & BS3CB2PFACC_F_DIRECT ? X86_PAGE_SIZE : X86_PAGE_SIZE * (i + 1) - pThis->cbAccess; unsigned offEnd = fFlags & BS3CB2PFACC_F_DIRECT ? off + 1 : X86_PAGE_SIZE * (i + 1) + (i == 0 ? 8 : 7); for (; off < offEnd; off++) { *(uint64_t *)&pThis->pbOrgTest[off] = s_uValueFlipped; pCtx->rax.u = uRaxIn; if (BS3_MODE_IS_16BIT_CODE(pThis->bMode)) pThis->ExpectCtx.rbx.u = pCtx->rbx.u = off; else pThis->ExpectCtx.rbx.u = pCtx->rbx.u = pThis->uTestAddr.u + off; Bs3TrapSetJmpAndRestore(pCtx, &pThis->TrapCtx); //Bs3TestPrintf("off=%#06x bXcpt=%#x uErrCd=%#RX64\n", off, pThis->TrapCtx.bXcpt, pThis->TrapCtx.uErrCd); if ( bXcpt != X86_XCPT_PF || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off >= X86_PAGE_SIZE * 2) || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off <= X86_PAGE_SIZE - pThis->cbAccess) ) { pThis->ExpectCtx.rax.u = uExpectedRax; pThis->ExpectCtx.rflags.u32 = uExpectedFlags; bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, pThis->pCmnMode->CmpXchg.offUd2, X86_XCPT_UD, 0 /*uErrCd*/); if (*(uint64_t *)&pThis->pbOrgTest[off] != uExpectStored) Bs3TestFailedF("%u - %s: Stored %#RX64, expected %#RX64", g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], uExpectStored); } else { pThis->ExpectCtx.rax.u = uRaxIn; pThis->ExpectCtx.rflags = pCtx->rflags; if (off < X86_PAGE_SIZE) pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + X86_PAGE_SIZE; else pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + off; bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, 0 /*cbPcAdjust*/, bXcpt, uPfErrCd); pThis->ExpectCtx.cr2 = pCtx->cr2; if (*(uint64_t *)&pThis->pbOrgTest[off] != s_uValueFlipped) Bs3TestFailedF("%u - %s: #PF'ed store modified memory: %#RX64, expected %#RX64", g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], s_uValueFlipped); } } if (fFlags & BS3CB2PFACC_F_DIRECT) break; } pCtx->rax.u = uRaxSaved; pCtx->rcx.u = uRcxSaved; } static void bs3CpuBasic2Pf_DoCmpXchgMiss(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags, uint8_t bXcpt, uint8_t uPfErrCd) { bs3CpuBasic2Pf_DoCmpXchg(pThis, pCtx, fFlags, bXcpt, uPfErrCd, true /*fMissmatch*/ ); } static void bs3CpuBasic2Pf_DoCmpXchgMatch(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags, uint8_t bXcpt, uint8_t uPfErrCd) { bs3CpuBasic2Pf_DoCmpXchg(pThis, pCtx, fFlags, bXcpt, uPfErrCd , false /*fMissmatch*/ ); } /** * @interface_method_impl{BS3CPUBASIC2PFACCESSOR,pfnAccessor, * DIV [MEM=0] for checking the accessed bit} */ static void bs3CpuBasic2Pf_DoDivByZero(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint32_t fFlags, uint8_t bXcpt, uint8_t uPfErrCd) { static uint64_t const s_uFiller = UINT64_C(0x9856703711f4069e); uint64_t uZeroAndFill; unsigned i; /* * Adjust the incoming context and calculate our expections. */ bs3CpuBasic2Pf_SetCsEip(pThis, pCtx, &pThis->pCmnMode->DivMem); Bs3MemCpy(&pThis->ExpectCtx, pCtx, sizeof(pThis->ExpectCtx)); switch (pThis->bMode & BS3_MODE_CODE_MASK) { case BS3_MODE_CODE_16: case BS3_MODE_CODE_V86: uZeroAndFill = s_uFiller & UINT64_C(0xffffffffffff0000); break; case BS3_MODE_CODE_32: uZeroAndFill = s_uFiller & UINT64_C(0xffffffff00000000); break; case BS3_MODE_CODE_64: uZeroAndFill = 0; break; } /* * Make two approaches to the test page (the 2nd one): * - i=0: Start on the 1st page and edge into the 2nd. * - i=1: Start at the end of the 2nd page and edge off it and into the 3rd. */ for (i = 0; i < 2; i++) { unsigned off = fFlags & BS3CB2PFACC_F_DIRECT ? X86_PAGE_SIZE : X86_PAGE_SIZE * (i + 1) - pThis->cbAccess; unsigned offEnd = fFlags & BS3CB2PFACC_F_DIRECT ? off + 1 : X86_PAGE_SIZE * (i + 1) + (i == 0 ? 8 : 7); for (; off < offEnd; off++) { *(uint64_t *)&pThis->pbOrgTest[off] = uZeroAndFill; if (BS3_MODE_IS_16BIT_CODE(pThis->bMode)) pThis->ExpectCtx.rbx.u = pCtx->rbx.u = off; else pThis->ExpectCtx.rbx.u = pCtx->rbx.u = pThis->uTestAddr.u + off; Bs3TrapSetJmpAndRestore(pCtx, &pThis->TrapCtx); //if (pThis->bMode == BS3_MODE_PP16_32) Bs3TestPrintf("off=%#06x bXcpt=%#x uErrCd=%#RX64\n", off, pThis->TrapCtx.bXcpt, pThis->TrapCtx.uErrCd); if ( bXcpt != X86_XCPT_PF || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off >= X86_PAGE_SIZE * 2) || ((fFlags & BS3CB2PFACC_F_PAGE_LEVEL) && off <= X86_PAGE_SIZE - pThis->cbAccess) ) { bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, 0 /*cbPcAdjust*/, X86_XCPT_DE, 0 /*uErrCd*/); if (*(uint64_t *)&pThis->pbOrgTest[off] != uZeroAndFill) Bs3TestFailedF("%u - %s: Modified source op: %#RX64, expected %#RX64", g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], uZeroAndFill); } else { if (off < X86_PAGE_SIZE) pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + X86_PAGE_SIZE; else pThis->ExpectCtx.cr2.u = pThis->uTestAddr.u + off; bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, 0 /*cbPcAdjust*/, bXcpt, uPfErrCd); pThis->ExpectCtx.cr2 = pCtx->cr2; if (*(uint64_t *)&pThis->pbOrgTest[off] != uZeroAndFill) Bs3TestFailedF("%u - %s: Modified source op: %#RX64, expected %#RX64", g_usBs3TestStep, "xxxx", *(uint64_t *)&pThis->pbOrgTest[off], uZeroAndFill); } } if (fFlags & BS3CB2PFACC_F_DIRECT) break; } } static BS3CPUBASIC2PFACCESSOR const g_aAccessors[] = { { "DoExec", bs3CpuBasic2Pf_DoExec, X86_TRAP_PF_ID, X86_XCPT_UD }, { "DoMovLoad", bs3CpuBasic2Pf_DoMovLoad, 0, X86_XCPT_UD }, { "DoMovStore", bs3CpuBasic2Pf_DoMovStore, X86_TRAP_PF_RW, X86_XCPT_UD }, { "DoXchg", bs3CpuBasic2Pf_DoXchg, X86_TRAP_PF_RW, X86_XCPT_UD }, { "DoCmpXchgMiss", bs3CpuBasic2Pf_DoCmpXchgMiss, X86_TRAP_PF_RW, X86_XCPT_UD }, { "DoCmpXhcgMatch", bs3CpuBasic2Pf_DoCmpXchgMatch, X86_TRAP_PF_RW, X86_XCPT_UD }, { "DoDivByZero", bs3CpuBasic2Pf_DoDivByZero, 0, X86_XCPT_DE }, }; /** @} */ /** @name BS3CPUBASIC2PFMODPT::pfnModify implementations. * @{ */ static void bs3CpuBasic2Pf_ClearMask(PBS3CPUBASIC2PFSTATE pThis, unsigned iStore, PCBS3CPUBASIC2PFMODPT pEntry, uint32_t fClearMask, uint32_t fSetMask) { if (pThis->PgInfo.cbEntry == 4) { uint32_t const uOrg = pThis->PteBackup.Legacy[1]; uint32_t uNew = ((uOrg & ~fClearMask) | fSetMask) & ~(uint32_t)pEntry->uModifyArg; uint32_t const uOld = pThis->PgInfo.u.Legacy.pPte[1].u; g_aStoreMethods[iStore].pfnStore(pThis->PgInfo.u.Legacy.pPte + 1, uNew, uOld); } else { uint64_t const uOrg = pThis->PteBackup.Pae[1]; uint64_t uNew = ((uOrg & ~(uint64_t)fClearMask) | fSetMask) & ~(uint64_t)pEntry->uModifyArg; uint64_t const uOld = pThis->PgInfo.u.Pae.pPte[1].u; g_aStoreMethods[iStore].pfnStore(&pThis->PgInfo.u.Pae.pPte[1].au32[0], (uint32_t)uNew, (uint32_t)uOld); if ((uint32_t)(uNew >> 32) != (uint32_t)(uOld >> 32)) g_aStoreMethods[iStore].pfnStore(&pThis->PgInfo.u.Pae.pPte[1].au32[1], (uint32_t)(uNew >> 32), (uint32_t)(uOld >> 32)); } } static void bs3CpuBasic2Pf_SetBit(PBS3CPUBASIC2PFSTATE pThis, unsigned iStore, PCBS3CPUBASIC2PFMODPT pEntry, uint32_t fClearMask, uint32_t fSetMask) { if (pThis->PgInfo.cbEntry == 4) { uint32_t const uOrg = pThis->PteBackup.Legacy[1]; uint32_t uNew = (uOrg & ~fClearMask) | fSetMask | RT_BIT_32(pEntry->uModifyArg); uint32_t const uOld = pThis->PgInfo.u.Legacy.pPte[1].u; g_aStoreMethods[iStore].pfnStore(pThis->PgInfo.u.Legacy.pPte + 1, uNew, uOld); } else { uint64_t const uOrg = pThis->PteBackup.Pae[1]; uint64_t uNew = ((uOrg & ~(uint64_t)fClearMask) | fSetMask) | RT_BIT_64(pEntry->uModifyArg); uint64_t const uOld = pThis->PgInfo.u.Pae.pPte[1].u; if (pEntry->uModifyArg < 32 || (uint32_t)uNew != (uint32_t)uOld) g_aStoreMethods[iStore].pfnStore(&pThis->PgInfo.u.Pae.pPte[1].au32[0], (uint32_t)uNew, (uint32_t)uOld); if (pEntry->uModifyArg >= 32 || (uint32_t)(uNew >> 32) != (uint32_t)(uOld >> 32)) g_aStoreMethods[iStore].pfnStore(&pThis->PgInfo.u.Pae.pPte[1].au32[1], (uint32_t)(uNew >> 32), (uint32_t)(uOld >> 32)); } } static void bs3CpuBasic2Pf_NoChange(PBS3CPUBASIC2PFSTATE pThis, unsigned iStore, PCBS3CPUBASIC2PFMODPT pEntry, uint32_t fClearMask, uint32_t fSetMask) { if (pThis->PgInfo.cbEntry == 4) { uint32_t const uOrg = pThis->PteBackup.Legacy[1]; uint32_t uNew = (uOrg & ~fClearMask) | fSetMask; uint32_t const uOld = pThis->PgInfo.u.Legacy.pPte[1].u; if (uNew != uOld) g_aStoreMethods[iStore].pfnStore(&pThis->PgInfo.u.Legacy.pPte[1], uNew, uOld); } else { uint64_t const uOrg = pThis->PteBackup.Pae[1]; uint64_t uNew = (uOrg & ~(uint64_t)fClearMask) | fSetMask; uint64_t const uOld = pThis->PgInfo.u.Pae.pPte[1].u; if (uNew != uOld) { if ((uint32_t)uNew != (uint32_t)uOld) g_aStoreMethods[iStore].pfnStore(&pThis->PgInfo.u.Pae.pPte[1].au32[0], (uint32_t)uNew, (uint32_t)uOld); if ((uint32_t)(uNew >> 32) != (uint32_t)(uOld >> 32)) g_aStoreMethods[iStore].pfnStore(&pThis->PgInfo.u.Pae.pPte[1].au32[1], (uint32_t)(uNew >> 32), (uint32_t)(uOld >> 32)); } } } /** @} */ /** @name BS3CPUBASIC2PFMODPT::pfnApplicable implementations. * @{ */ static bool bs3CpuBasic2Pf_IsPteBitReserved(PBS3CPUBASIC2PFSTATE pThis, PCBS3CPUBASIC2PFMODPT pEntry) { if (pThis->PgInfo.cbEntry == 8) { /* Bits 52..63 or 62 (NXE=1). */ if (pThis->PgInfo.cEntries == 3) { if ((uint32_t)(pEntry->uModifyArg - 52U) < (uint32_t)(12 - pThis->fNxe)) return true; } else if (pEntry->uModifyArg == 63 && !pThis->fNxe) return true; /* Reserved physical address bits. */ if (pEntry->uModifyArg < 52) { if ((uint32_t)pEntry->uModifyArg >= (uint32_t)pThis->cBitsPhysWidth) return true; } } return false; } static bool bs3CpuBasic2Pf_IsPteBitSoftwareUsable(PBS3CPUBASIC2PFSTATE pThis, PCBS3CPUBASIC2PFMODPT pEntry) { if (pThis->PgInfo.cbEntry == 8) { if (pThis->PgInfo.cEntries != 3) { if ((uint32_t)(pEntry->uModifyArg - 52U) < (uint32_t)11) return true; } } return false; } static bool bs3CpuBasic2Pf_IsNxe(PBS3CPUBASIC2PFSTATE pThis, PCBS3CPUBASIC2PFMODPT pEntry) { return pThis->fNxe && pThis->PgInfo.cbEntry == 8; } /** @} */ static const BS3CPUBASIC2PFMODPT g_aPteWorkers[] = { /* { pszName, P U W NX RSV ModiyfArg pfnModify, pfnApplicable }, */ { "org", 1, 1, 1, 0, 0, 0, bs3CpuBasic2Pf_NoChange, NULL }, { "!US", 1, 0, 1, 0, 0, X86_PTE_US, bs3CpuBasic2Pf_ClearMask, NULL }, { "!RW", 1, 1, 0, 0, 0, X86_PTE_RW, bs3CpuBasic2Pf_ClearMask, NULL }, { "!RW+!US", 1, 0, 0, 0, 0, X86_PTE_RW | X86_PTE_US, bs3CpuBasic2Pf_ClearMask, NULL }, { "!P", 0, 0, 0, 0, 0, X86_PTE_P, bs3CpuBasic2Pf_ClearMask, NULL }, { "NX", 1, 1, 1, 1, 0, 63, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsNxe }, { "RSVPH[32]", 0, 0, 0, 0, 1, 32, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[33]", 0, 0, 0, 0, 1, 33, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[34]", 0, 0, 0, 0, 1, 34, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[35]", 0, 0, 0, 0, 1, 35, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[36]", 0, 0, 0, 0, 1, 36, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[37]", 0, 0, 0, 0, 1, 37, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[38]", 0, 0, 0, 0, 1, 38, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[39]", 0, 0, 0, 0, 1, 39, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[40]", 0, 0, 0, 0, 1, 40, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[41]", 0, 0, 0, 0, 1, 41, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[42]", 0, 0, 0, 0, 1, 42, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[43]", 0, 0, 0, 0, 1, 43, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[44]", 0, 0, 0, 0, 1, 44, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[45]", 0, 0, 0, 0, 1, 45, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[46]", 0, 0, 0, 0, 1, 46, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[47]", 0, 0, 0, 0, 1, 47, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[48]", 0, 0, 0, 0, 1, 48, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[49]", 0, 0, 0, 0, 1, 49, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[50]", 0, 0, 0, 0, 1, 50, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSVPH[51]", 0, 0, 0, 0, 1, 51, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSV[52]", 0, 0, 0, 0, 1, 52, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSV[53]", 0, 0, 0, 0, 1, 53, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSV[54]", 0, 0, 0, 0, 1, 54, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSV[55]", 0, 0, 0, 0, 1, 55, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSV[56]", 0, 0, 0, 0, 1, 56, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSV[57]", 0, 0, 0, 0, 1, 57, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSV[58]", 0, 0, 0, 0, 1, 58, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSV[59]", 0, 0, 0, 0, 1, 59, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSV[60]", 0, 0, 0, 0, 1, 60, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSV[61]", 0, 0, 0, 0, 1, 61, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSV[62]", 0, 0, 0, 0, 1, 62, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSV[62]", 0, 0, 0, 0, 1, 62, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "RSV[63]", 0, 0, 0, 0, 1, 63, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitReserved }, { "!RSV[52]", 1, 1, 1, 0, 0, 52, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable }, { "!RSV[53]", 1, 1, 1, 0, 0, 53, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable }, { "!RSV[54]", 1, 1, 1, 0, 0, 54, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable }, { "!RSV[55]", 1, 1, 1, 0, 0, 55, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable }, { "!RSV[56]", 1, 1, 1, 0, 0, 56, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable }, { "!RSV[57]", 1, 1, 1, 0, 0, 57, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable }, { "!RSV[58]", 1, 1, 1, 0, 0, 58, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable }, { "!RSV[59]", 1, 1, 1, 0, 0, 59, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable }, { "!RSV[60]", 1, 1, 1, 0, 0, 60, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable }, { "!RSV[61]", 1, 1, 1, 0, 0, 61, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable }, { "!RSV[62]", 1, 1, 1, 0, 0, 62, bs3CpuBasic2Pf_SetBit, bs3CpuBasic2Pf_IsPteBitSoftwareUsable }, }; /** * Worker for bs3CpuBasic2_RaiseXcpt0e_c32 that does the actual testing. * * Caller does all the cleaning up. * * @returns Error count. * @param pThis Test state data. * @param fNxe Whether NX is enabled. */ static uint8_t bs3CpuBasic2_RaiseXcpt0eWorker(PBS3CPUBASIC2PFSTATE register pThis, bool const fWp, bool const fNxe) { unsigned iLevel; unsigned iRing; unsigned iStore; unsigned iAccessor; unsigned iOuter; unsigned cPml4Tests; unsigned cPdPtrTests; uint32_t const fPfIdMask = fNxe ? UINT32_MAX : ~X86_TRAP_PF_ID; BS3REGCTX aCtxts[4]; pThis->fWp = fWp; pThis->fNxe = fNxe; /** @todo figure out V8086 testing. */ if ((pThis->bMode & BS3_MODE_CODE_MASK) == BS3_MODE_CODE_V86) return BS3TESTDOMODE_SKIPPED; /* paranoia: Touch the various big stack structures to ensure the compiler has allocated stack for them. */ for (iRing = 0; iRing < RT_ELEMENTS(aCtxts); iRing++) Bs3MemZero(&aCtxts[iRing], sizeof(aCtxts[iRing])); /* * Set up a few contexts for testing this stuff. */ Bs3RegCtxSaveEx(&aCtxts[0], pThis->bMode, 2048); for (iRing = 1; iRing < 4; iRing++) { aCtxts[iRing] = aCtxts[0]; Bs3RegCtxConvertToRingX(&aCtxts[iRing], iRing); } if (!BS3_MODE_IS_16BIT_CODE(pThis->bMode)) { for (iRing = 0; iRing < 4; iRing++) aCtxts[iRing].rbx.u = pThis->uTestAddr.u; } else { for (iRing = 0; iRing < 4; iRing++) { aCtxts[iRing].ds = pThis->uSel16TestData; aCtxts[iRing].rbx.u = 0; } } /* * Check basic operation: */ for (iRing = 0; iRing < 4; iRing++) for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++) g_aAccessors[iAccessor].pfnAccessor(pThis, &aCtxts[iRing], BS3CB2PFACC_F_PAGE_LEVEL, X86_XCPT_UD, UINT8_MAX); /* * Some PTE checks. We only mess with the 2nd page. */ for (iOuter = 0; iOuter < 2; iOuter++) { uint32_t const fAccessor = (iOuter == 0 ? BS3CB2PFACC_F_DIRECT : 0) | BS3CB2PFACC_F_PAGE_LEVEL; unsigned iPteWrk; bs3CpuBasic2Pf_FlushAll(); for (iPteWrk = 0; iPteWrk < RT_ELEMENTS(g_aPteWorkers); iPteWrk++) { BS3CPUBASIC2PFMODPT EffWrk; const BS3CPUBASIC2PFMODPT *pPteWrk = &g_aPteWorkers[iPteWrk]; if (pPteWrk->pfnApplicable && !pPteWrk->pfnApplicable(pThis, pPteWrk)) continue; pThis->pszPteWorker = pPteWrk->pszName; EffWrk = *pPteWrk; #if 1 /* * Do the modification once, then test all different accesses * without flushing the TLB or anything in-between. */ for (iStore = 0; iStore < RT_ELEMENTS(g_aStoreMethods); iStore++) { pThis->pszStore = g_aStoreMethods[iStore].pszName; pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, 0); for (iRing = 0; iRing < 4; iRing++) { PBS3REGCTX const pCtx = &aCtxts[iRing]; if ( EffWrk.fReserved || !EffWrk.fPresent || (!EffWrk.fUser && iRing == 3)) { uint32_t const fPfBase = ( EffWrk.fReserved ? X86_TRAP_PF_P | X86_TRAP_PF_RSVD : EffWrk.fPresent ? X86_TRAP_PF_P : 0) | (iRing == 3 ? X86_TRAP_PF_US : 0); for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++) { pThis->pszAccessor = g_aAccessors[iAccessor].pszName; g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask)); } } else { uint32_t const fPfBase = X86_TRAP_PF_P | (iRing == 3 ? X86_TRAP_PF_US : 0); for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++) { pThis->pszAccessor = g_aAccessors[iAccessor].pszName; if ( ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_ID) && EffWrk.fNoExecute) || ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_RW) && !EffWrk.fWriteable && (fWp || iRing == 3)) ) g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask)); else g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX); } } } /* Reset the paging + full flush. */ bs3CpuBasic2Pf_RestoreFromBackups(pThis); } #endif #define CHECK_AD_BITS(a_fExpectedAD) \ do { \ uint32_t fActualAD = ( pThis->PgInfo.cbEntry == 8 \ ? pThis->PgInfo.u.Pae.pPte[1].au32[0] : pThis->PgInfo.u.Legacy.pPte[1].au32[0]) \ & (X86_PTE_A | X86_PTE_D); \ if (fActualAD != (a_fExpectedAD)) \ { \ Bs3TestFailedF("%u - %s/%u: unexpected A/D bits: %#x, expected %#x\n", \ g_usBs3TestStep, "xxxx", __LINE__, fActualAD, a_fExpectedAD); \ BS3CPUBASIC2PF_HALT(pThis); \ } \ } while (0) /* * Again, but redoing everything for each accessor. */ for (iStore = 0; iStore < RT_ELEMENTS(g_aStoreMethods); iStore++) { pThis->pszStore = g_aStoreMethods[iStore].pszName; for (iRing = 0; iRing < 4; iRing++) { PBS3REGCTX const pCtx = &aCtxts[iRing]; if ( EffWrk.fReserved || !EffWrk.fPresent || (!EffWrk.fUser && iRing == 3)) { uint32_t const fPfBase = ( EffWrk.fReserved ? X86_TRAP_PF_P | X86_TRAP_PF_RSVD : EffWrk.fPresent ? X86_TRAP_PF_P : 0) | (iRing == 3 ? X86_TRAP_PF_US : 0); for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++) { pThis->pszAccessor = g_aAccessors[iAccessor].pszName; pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, 0); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask)); CHECK_AD_BITS(0); bs3CpuBasic2Pf_RestoreFromBackups(pThis); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask)); CHECK_AD_BITS(0); bs3CpuBasic2Pf_RestoreFromBackups(pThis); } } else { uint32_t const fPfBase = X86_TRAP_PF_P | (iRing == 3 ? X86_TRAP_PF_US : 0); for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++) { pThis->pszAccessor = g_aAccessors[iAccessor].pszName; if ( ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_ID) && EffWrk.fNoExecute) || ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_RW) && !EffWrk.fWriteable && (fWp || iRing == 3)) ) { uint32_t const fErrCd = fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd); CHECK_AD_BITS(0); bs3CpuBasic2Pf_RestoreFromBackups(pThis); pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, X86_PTE_A | X86_PTE_D); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd); CHECK_AD_BITS(X86_PTE_A | X86_PTE_D); bs3CpuBasic2Pf_RestoreFromBackups(pThis); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A, X86_PTE_D); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd); CHECK_AD_BITS(X86_PTE_D); bs3CpuBasic2Pf_RestoreFromBackups(pThis); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_D, X86_PTE_A); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd); CHECK_AD_BITS(X86_PTE_A); bs3CpuBasic2Pf_RestoreFromBackups(pThis); } else { uint32_t const fExpectedAD = (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_RW) ? X86_PTE_A | X86_PTE_D : X86_PTE_A; pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX); CHECK_AD_BITS(fExpectedAD); bs3CpuBasic2Pf_RestoreFromBackups(pThis); pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, X86_PTE_A | X86_PTE_D); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX); CHECK_AD_BITS(X86_PTE_A | X86_PTE_D); bs3CpuBasic2Pf_RestoreFromBackups(pThis); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A, X86_PTE_D); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX); CHECK_AD_BITS(fExpectedAD | X86_PTE_D); bs3CpuBasic2Pf_RestoreFromBackups(pThis); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_D, X86_PTE_A); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX); CHECK_AD_BITS(fExpectedAD | X86_PTE_A); bs3CpuBasic2Pf_RestoreFromBackups(pThis); } } } } } /* * Again, but using invalidate page. */ if (pThis->fUseInvlPg) { bs3CpuBasic2Pf_RestoreFromBackups(pThis); for (iStore = 0; iStore < RT_ELEMENTS(g_aStoreMethods); iStore++) { pThis->pszStore = g_aStoreMethods[iStore].pszName; for (iRing = 0; iRing < 4; iRing++) { PBS3REGCTX const pCtx = &aCtxts[iRing]; if ( EffWrk.fReserved || !EffWrk.fPresent || (!EffWrk.fUser && iRing == 3)) { uint32_t const fPfBase = ( EffWrk.fReserved ? X86_TRAP_PF_P | X86_TRAP_PF_RSVD : EffWrk.fPresent ? X86_TRAP_PF_P : 0) | (iRing == 3 ? X86_TRAP_PF_US : 0); for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++) { pThis->pszAccessor = g_aAccessors[iAccessor].pszName; pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, 0); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask)); CHECK_AD_BITS(0); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask)); CHECK_AD_BITS(0); } } else { uint32_t const fPfBase = X86_TRAP_PF_P | (iRing == 3 ? X86_TRAP_PF_US : 0); for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++) { pThis->pszAccessor = g_aAccessors[iAccessor].pszName; if ( ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_ID) && EffWrk.fNoExecute) || ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_RW) && !EffWrk.fWriteable && (fWp || iRing == 3)) ) { uint32_t const fErrCd = fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd); CHECK_AD_BITS(0); pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, X86_PTE_A | X86_PTE_D); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd); CHECK_AD_BITS(X86_PTE_A | X86_PTE_D); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A, X86_PTE_D); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd); CHECK_AD_BITS(X86_PTE_D); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_D, X86_PTE_A); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd); CHECK_AD_BITS(X86_PTE_A); } else { uint32_t const fExpectedAD = (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_RW) ? X86_PTE_A | X86_PTE_D : X86_PTE_A; pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX); CHECK_AD_BITS(fExpectedAD); pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, X86_PTE_A | X86_PTE_D); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX); CHECK_AD_BITS(X86_PTE_A | X86_PTE_D); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A, X86_PTE_D); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX); CHECK_AD_BITS(fExpectedAD | X86_PTE_D); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_D, X86_PTE_A); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX); CHECK_AD_BITS(fExpectedAD | X86_PTE_A); } } } } } bs3CpuBasic2Pf_RestoreFromBackups(pThis); } } } /* * Do all 4 paging levels. We start out with full access to the page and * restrict it in various ways. * * (On the final level we only mess with the 2nd page for now.) */ cPdPtrTests = 1; cPml4Tests = 1; if (pThis->uTestAddr.u >= UINT64_C(0x8000000000)) { cPml4Tests = 2; cPdPtrTests = 2; } else if (pThis->PgInfo.cEntries == 3) cPdPtrTests = 2; #if 0 /* Loop 1: Accessor flags. */ for (iOuter = 0; iOuter < 2; iOuter++) { uint32_t const fAccessor = (iOuter == 0 ? BS3CB2PFACC_F_DIRECT : 0) | BS3CB2PFACC_F_PAGE_LEVEL; /* Loop 2: Paging store method. */ for (iStore = 0; iStore < RT_ELEMENTS(g_aStoreMethods); iStore++) { unsigned iPml4Test; int8_t cReserved = 0; int8_t cNotPresent = 0; int8_t cNotWrite = 0; int8_t cNotUser = 0; int8_t cExecute = 0; /* Loop 3: Page map level 4 */ for (iPml4Test = 0; iPml4Test < cPml4Tests; iPml4Test++) { unsigned iPdPtrTest; /* Loop 4: Page directory pointer table. */ for (iPdPtrTest = 0; iPdPtrTest < cPdPtrTests; iPdPtrTest++) { unsigned iPdTest; /* Loop 5: Page directory. */ for (iPdTest = 0; iPdTest < 2; iPdTest++) { unsigned iPtTest; /* Loop 6: Page table. */ for (iPtTest = 0; iPtTest < 2; iPtTest++) { /* Loop 7: Accessor ring. */ for (iRing = 0; iRing < 4; iRing++) { PBS3REGCTX const pCtx = &aCtxts[iRing]; if ( EffWrk.fReserved || !EffWrk.fPresent || (!EffWrk.fUser && iRing == 3)) { uint32_t const fPfBase = ( EffWrk.fReserved ? X86_TRAP_PF_P | X86_TRAP_PF_RSVD : EffWrk.fPresent ? X86_TRAP_PF_P : 0) | (iRing == 3 ? X86_TRAP_PF_US : 0); for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++) { pThis->pszAccessor = g_aAccessors[iAccessor].pszName; pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, 0); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask)); CHECK_AD_BITS(0); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask)); CHECK_AD_BITS(0); } } else { uint32_t const fPfBase = X86_TRAP_PF_P | (iRing == 3 ? X86_TRAP_PF_US : 0); for (iAccessor = 0; iAccessor < RT_ELEMENTS(g_aAccessors); iAccessor++) { pThis->pszAccessor = g_aAccessors[iAccessor].pszName; if ( ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_ID) && EffWrk.fNoExecute) || ( (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_RW) && !EffWrk.fWriteable && (fWp || iRing == 3)) ) { uint32_t const fErrCd = fPfBase | (g_aAccessors[iAccessor].fAccess & fPfIdMask); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd); CHECK_AD_BITS(0); pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, X86_PTE_A | X86_PTE_D); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd); CHECK_AD_BITS(X86_PTE_A | X86_PTE_D); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A, X86_PTE_D); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd); CHECK_AD_BITS(X86_PTE_D); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_D, X86_PTE_A); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_PF, fErrCd); CHECK_AD_BITS(X86_PTE_A); } else { uint32_t const fExpectedAD = (g_aAccessors[iAccessor].fAccess & X86_TRAP_PF_RW) ? X86_PTE_A | X86_PTE_D : X86_PTE_A; pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A | X86_PTE_D, 0); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX); CHECK_AD_BITS(fExpectedAD); pPteWrk->pfnModify(pThis, iStore, pPteWrk, 0, X86_PTE_A | X86_PTE_D); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX); CHECK_AD_BITS(X86_PTE_A | X86_PTE_D); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_A, X86_PTE_D); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX); CHECK_AD_BITS(fExpectedAD | X86_PTE_D); pPteWrk->pfnModify(pThis, iStore, pPteWrk, X86_PTE_D, X86_PTE_A); ASMInvalidatePage(pThis->uTestAddr.u + X86_PAGE_SIZE); g_aAccessors[iAccessor].pfnAccessor(pThis, pCtx, fAccessor, X86_XCPT_UD, UINT8_MAX); CHECK_AD_BITS(fExpectedAD | X86_PTE_A); } } } } } } } } } } #endif /* * Check reserved bits on each paging level. */ /* Loop 1: Accessor flags (only direct for now). */ for (iOuter = 0; iOuter < 1; iOuter++) { uint32_t const fAccessor = BS3CB2PFACC_F_DIRECT; /* Loop 2: Paging store method. */ for (iStore = 0; iStore < RT_ELEMENTS(g_aStoreMethods); iStore++) { /* Loop 3: Accessor ring. */ for (iRing = 0; iRing < 4; iRing++) { /* Loop 4: Which level we mess up. */ for (iLevel = 0; iLevel < pThis->PgInfo.cEntries; iLevel++) { #if 0 const BS3CPUBASIC2PFMODPT *pPteWrk = &g_aPteWorkers[iPteWrk]; if (pThis->PgInfo.) { } #endif } } } } return 0; } BS3_DECL_CALLBACK(uint8_t) bs3CpuBasic2_RaiseXcpt0e_c32(uint8_t bMode) { void *pvTestUnaligned; uint32_t cbTestUnaligned = _8M; uint8_t bRet = 1; int rc; BS3CPUBASIC2PFSTATE State; /* * Initalize the state data. */ Bs3MemZero(&State, sizeof(State)); State.bMode = bMode; switch (bMode & BS3_MODE_CODE_MASK) { case BS3_MODE_CODE_16: State.cbAccess = sizeof(uint16_t); break; case BS3_MODE_CODE_V86: State.cbAccess = sizeof(uint16_t); break; case BS3_MODE_CODE_32: State.cbAccess = sizeof(uint32_t); break; case BS3_MODE_CODE_64: State.cbAccess = sizeof(uint64_t); break; } State.pCmnMode = &g_aCmnModes[0]; while (State.pCmnMode->bMode != (bMode & BS3_MODE_CODE_MASK)) State.pCmnMode++; State.fUseInvlPg = (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486; /* Figure physical addressing width. */ State.cBitsPhysWidth = 32; if ( (g_uBs3CpuDetected & BS3CPU_F_CPUID) && (ASMCpuId_EDX(1) & (X86_CPUID_FEATURE_EDX_PSE36 | X86_CPUID_FEATURE_EDX_PAE)) ) State.cBitsPhysWidth = 36; if ( (g_uBs3CpuDetected & BS3CPU_F_CPUID_EXT_LEAVES) && ASMCpuId_EAX(0x80000000) >= 0x80000008) { uint8_t cBits = (uint8_t)ASMCpuId_EAX(0x80000008); if (cBits >= 32 && cBits <= 52) State.cBitsPhysWidth = cBits; else Bs3TestPrintf("CPUID 0x80000008: Physical bitcount out of range: %u\n", cBits); } //Bs3TestPrintf("Physical bitcount: %u\n", State.cBitsPhysWidth); /* * Allocate a some memory we can play around with, then carve a size aligned * chunk out of it so we might be able to maybe play with 2/4MB pages too. */ cbTestUnaligned = _8M * 2; while ((pvTestUnaligned = Bs3MemAlloc(BS3MEMKIND_FLAT32, cbTestUnaligned)) == NULL) { cbTestUnaligned >>= 1; if (cbTestUnaligned <= _16K) { Bs3TestFailed("Failed to allocate memory to play around with\n"); return 1; } } /* align. */ if ((uintptr_t)pvTestUnaligned & (cbTestUnaligned - 1)) { State.cbTest = cbTestUnaligned >> 1; State.pbOrgTest = (uint8_t *)(((uintptr_t)pvTestUnaligned + State.cbTest - 1) & ~(State.cbTest - 1)); } else { State.pbOrgTest = pvTestUnaligned; State.cbTest = cbTestUnaligned; } State.cTestPages = State.cbTest >> X86_PAGE_SHIFT; /* * Alias this memory far away from where our code and data lives. */ if (bMode & BS3_MODE_CODE_64) State.uTestAddr.u = UINT64_C(0x0000648680000000); else State.uTestAddr.u = UINT32_C(0x80000000); rc = Bs3PagingAlias(State.uTestAddr.u, (uintptr_t)State.pbOrgTest, State.cbTest, X86_PTE_P | X86_PTE_RW | X86_PTE_US); if (RT_SUCCESS(rc)) { rc = Bs3PagingQueryAddressInfo(State.uTestAddr.u, &State.PgInfo); if (RT_SUCCESS(rc)) { //if (bMode & BS3_MODE_CODE_64) ASMHalt(); /* Set values that derives from the test memory size and paging info. */ if (State.PgInfo.cEntries == 2) { State.cTestPdes = (State.cTestPages + X86_PG_ENTRIES - 1) / X86_PG_ENTRIES; State.cTest1stPtes = RT_MIN(State.cTestPages, X86_PG_ENTRIES); State.cbPdeBackup = State.cTestPdes * (X86_PAGE_SIZE / X86_PG_ENTRIES); State.cbPteBackup = State.cTest1stPtes * (X86_PAGE_SIZE / X86_PG_ENTRIES); } else { State.cTestPdes = (State.cTestPages + X86_PG_PAE_ENTRIES - 1) / X86_PG_PAE_ENTRIES; State.cTest1stPtes = RT_MIN(State.cTestPages, X86_PG_PAE_ENTRIES); State.cbPdeBackup = State.cTestPdes * (X86_PAGE_SIZE / X86_PG_PAE_ENTRIES); State.cbPteBackup = State.cTest1stPtes * (X86_PAGE_SIZE / X86_PG_PAE_ENTRIES); } #ifdef BS3CPUBASIC2PF_FASTER State.cbPteBackup = State.PgInfo.cbEntry * 4; #endif if (State.cTestPdes <= RT_ELEMENTS(State.au64PdeBackup)) { uint32_t cr0 = ASMGetCR0(); /* Back up the structures. */ Bs3MemCpy(&State.PteBackup, State.PgInfo.u.Legacy.pPte, State.cbPteBackup); Bs3MemCpy(State.au64PdeBackup, State.PgInfo.u.Legacy.pPde, State.cbPdeBackup); if (State.PgInfo.cEntries > 2) State.u64PdpteBackup = State.PgInfo.u.Pae.pPdpe->u; if (State.PgInfo.cEntries > 3) State.u64Pml4eBackup = State.PgInfo.u.Pae.pPml4e->u; /* * Setup a 16-bit selector for accessing the alias. */ Bs3SelSetup16BitData(&Bs3GdteSpare00, State.uTestAddr.u32); State.uSel16TestData = BS3_SEL_SPARE_00 | 3; /* * Do the testing. */ ASMSetCR0(ASMGetCR0() & ~X86_CR0_WP); bRet = bs3CpuBasic2_RaiseXcpt0eWorker(&State, false /*fWp*/, false /*fNxe*/); if (bRet == 0 && (g_uBs3CpuDetected & BS3CPU_TYPE_MASK) >= BS3CPU_80486) { ASMSetCR0(ASMGetCR0() | X86_CR0_WP); bRet = bs3CpuBasic2_RaiseXcpt0eWorker(&State, true /*fWp*/, false /*fNxe*/); } /* Do again with NX enabled. */ if (bRet == 0 && (g_uBs3CpuDetected & BS3CPU_F_NX)) { ASMWrMsr(MSR_K6_EFER, ASMRdMsr(MSR_K6_EFER) | MSR_K6_EFER_NXE); ASMSetCR0(ASMGetCR0() & ~X86_CR0_WP); bRet = bs3CpuBasic2_RaiseXcpt0eWorker(&State, false /*fWp*/, State.PgInfo.cbEntry == 8 /*fNxe*/); ASMSetCR0(ASMGetCR0() | X86_CR0_WP); bRet = bs3CpuBasic2_RaiseXcpt0eWorker(&State, true /*fWp*/, State.PgInfo.cbEntry == 8 /*fNxe*/); ASMWrMsr(MSR_K6_EFER, ASMRdMsr(MSR_K6_EFER) & ~MSR_K6_EFER_NXE); } bs3CpuBasic2Pf_RestoreFromBackups(&State); ASMSetCR0((ASMGetCR0() & ~X86_CR0_WP) | (cr0 & X86_CR0_WP)); } else Bs3TestFailedF("cTestPdes=%u!\n", State.cTestPdes); } else Bs3TestFailedF("Bs3PagingQueryAddressInfo failed: %d\n", rc); Bs3PagingUnalias(State.uTestAddr.u, State.cbTest); } else Bs3TestFailedF("Bs3PagingAlias failed! rc=%d\n", rc); Bs3MemFree(pvTestUnaligned, cbTestUnaligned); return bRet; }