summaryrefslogtreecommitdiffstats
path: root/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-pf.c32
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-pf.c32')
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-pf.c321890
1 files changed, 1890 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-pf.c32 b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-pf.c32
new file mode 100644
index 00000000..1323f092
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3-cpu-basic-2-pf.c32
@@ -0,0 +1,1890 @@
+/* $Id: bs3-cpu-basic-2-pf.c32 $ */
+/** @file
+ * BS3Kit - bs3-cpu-basic-2, 32-bit C code for testing \#PF.
+ */
+
+/*
+ * Copyright (C) 2007-2023 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 <https://www.gnu.org/licenses>.
+ *
+ * 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 <bs3kit.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* 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;
+}
+