/* $Id: bs3-cpu-basic-2-pf.c32 64776 2016-12-02 12:30:33Z vboxsync $ */ /** @file * BS3Kit - bs3-cpu-basic-2, 32-bit C code. */ /* * Copyright (C) 2007-2016 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the * VirtualBox OSE 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. */ /********************************************************************************************************************************* * 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) /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ typedef void BS3_CALL FNBS3CPUBASIC2PFSNIPPET(void); typedef struct FNBS3CPUBASIC2PFTSTCODE { FNBS3CPUBASIC2PFSNIPPET *pfn; uint8_t offUd2; uint8_t cbTmpl; } FNBS3CPUBASIC2PFTSTCODE; typedef FNBS3CPUBASIC2PFTSTCODE const *PCFNBS3CPUBASIC2PFTSTCODE; typedef struct BS3CPUBASIC2PFTTSTCMNMODE { uint8_t bMode; FNBS3CPUBASIC2PFTSTCODE MovLoad; FNBS3CPUBASIC2PFTSTCODE MovStore; FNBS3CPUBASIC2PFTSTCODE Xchg; FNBS3CPUBASIC2PFTSTCODE CmpXchg; } 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; /** Pointer to the test area (alias). */ uint8_t *pbTest; /** Pointer to the orignal test area mapping. */ uint8_t *pbOrgTest; /** The size of the test area (at least two pages). */ uint32_t cbTest; /** 16-bit data selector for pbTest. */ uint16_t uSel16TestData; /** 16-bit code selector for pbTest. */ uint16_t uSel16TestCode; /** Test paging information for pbTest. */ BS3PAGINGINFO4ADDR PgInfo; /** Set if we can use the INVLPG instruction. */ bool fUseInvlPg; /** Trap context frame. */ BS3TRAPFRAME TrapCtx; /** Expected result context. */ BS3REGCTX ExpectCtx; } BS3CPUBASIC2PFSTATE; /** Pointer to state for the \#PF test. */ typedef BS3CPUBASIC2PFSTATE *PBS3CPUBASIC2PFSTATE; /********************************************************************************************************************************* * 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_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_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; /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ /** Page table access functions. */ static const struct { const char *pszStore; 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 }, }, { 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 }, }, { 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 }, }, { 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 }, }, }; /** * 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); ASMHalt(); #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); ASMHalt(); #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); } static void bs3CpuBasic2Pf_DoExec(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint8_t bXcpt, uint8_t uPfErrCd, bool fPageLevel) { uint8_t *pbOrgTest = pThis->pbOrgTest; unsigned off; for (off = X86_PAGE_SIZE - 4; off < X86_PAGE_SIZE + 2; 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 = (uintptr_t)&pThis->pbTest[off]; break; case BS3_MODE_CODE_16: Bs3SelSetup16BitCode(&Bs3GdteSpare01, (uintptr_t)pThis->pbTest, 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) { unsigned offAddPC = !fPageLevel || off >= X86_PAGE_SIZE ? 0 : X86_PAGE_SIZE - off; bs3CpuBasic2Pf_CompareSimplePf(pThis, pCtx, offAddPC, uPfErrCd, (uintptr_t)pThis->pbTest + offAddPC); } else bs3CpuBasic2Pf_CompareSimpleUd(pThis, pCtx, 3); } } 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 bXcpt X86_XCPT_PF if this can cause \#PFs, otherwise * X86_XCPT_UD. * @param uPfErrCd The error code for \#PFs. * @param fPageLevel Set if we're pushing PTE level bits. */ static void bs3CpuBasic2Pf_DoMovLoad(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint8_t bXcpt, uint8_t uPfErrCd, bool fPageLevel) { 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 = X86_PAGE_SIZE * (i + 1) - pThis->cbAccess; unsigned offEnd = 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 = (uintptr_t)pThis->pbTest + 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 || (fPageLevel && off >= X86_PAGE_SIZE * 2) || (fPageLevel && off <= X86_PAGE_SIZE - pThis->cbAccess) ) { pThis->ExpectCtx.rax.u = uExpectRax; bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, pThis->pCmnMode->MovLoad.offUd2, bXcpt, 0 /*uErrCd*/); pThis->ExpectCtx.rax = pCtx->rax; } else { if (off < X86_PAGE_SIZE) pThis->ExpectCtx.cr2.u = (uintptr_t)pThis->pbTest + X86_PAGE_SIZE; else pThis->ExpectCtx.cr2.u = (uintptr_t)pThis->pbTest + off; bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, 0 /*cbPcAdjust*/, bXcpt, uPfErrCd); pThis->ExpectCtx.cr2 = pCtx->cr2; } } } } /** * Test a simple store instruction around the edges of page two. * * @param pThis The test stat data. * @param pCtx The test context. * @param bXcpt X86_XCPT_PF if this can cause \#PFs, otherwise * X86_XCPT_UD. * @param uPfErrCd The error code for \#PFs. * @param fPageLevel Set if we're pushing PTE level bits. */ static void bs3CpuBasic2Pf_DoMovStore(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint8_t bXcpt, uint8_t uPfErrCd, bool fPageLevel) { 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 = X86_PAGE_SIZE * (i + 1) - pThis->cbAccess; unsigned offEnd = 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 = (uintptr_t)pThis->pbTest + 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 || (fPageLevel && off >= X86_PAGE_SIZE * 2) || (fPageLevel && off <= X86_PAGE_SIZE - pThis->cbAccess) ) { bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, pThis->pCmnMode->MovStore.offUd2, bXcpt, 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 = (uintptr_t)pThis->pbTest + X86_PAGE_SIZE; else pThis->ExpectCtx.cr2.u = (uintptr_t)pThis->pbTest + 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); } } } 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 bXcpt X86_XCPT_PF if this can cause \#PFs, otherwise * X86_XCPT_UD. * @param uPfErrCd The error code for \#PFs. * @param fPageLevel Set if we're pushing PTE level bits. */ static void bs3CpuBasic2Pf_DoXchg(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint8_t bXcpt, uint8_t uPfErrCd, bool fPageLevel) { 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 = X86_PAGE_SIZE * (i + 1) - pThis->cbAccess; unsigned offEnd = 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 = (uintptr_t)pThis->pbTest + 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 || (fPageLevel && off >= X86_PAGE_SIZE * 2) || (fPageLevel && off <= X86_PAGE_SIZE - pThis->cbAccess) ) { pThis->ExpectCtx.rax.u = uExpectedRax; bs3CpuBasic2Pf_CompareCtx(pThis, &pThis->ExpectCtx, pThis->pCmnMode->Xchg.offUd2, bXcpt, 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 = (uintptr_t)pThis->pbTest + X86_PAGE_SIZE; else pThis->ExpectCtx.cr2.u = (uintptr_t)pThis->pbTest + 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); } } } 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 bXcpt X86_XCPT_PF if this can cause \#PFs, otherwise * X86_XCPT_UD. * @param uPfErrCd The error code for \#PFs. * @param fPageLevel Set if we're pushing PTE level bits. * @param fMissmatch Whether to fail and not store (@c true), or succeed * and do the store. */ static void bs3CpuBasic2Pf_DoCmpXchg(PBS3CPUBASIC2PFSTATE pThis, PBS3REGCTX pCtx, uint8_t bXcpt, uint8_t uPfErrCd, bool fPageLevel, 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 = X86_PAGE_SIZE * (i + 1) - pThis->cbAccess; unsigned offEnd = 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 = (uintptr_t)pThis->pbTest + 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 || (fPageLevel && off >= X86_PAGE_SIZE * 2) || (fPageLevel && 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, bXcpt, 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 = (uintptr_t)pThis->pbTest + X86_PAGE_SIZE; else pThis->ExpectCtx.cr2.u = (uintptr_t)pThis->pbTest + 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); } } } pCtx->rax.u = uRaxSaved; pCtx->rcx.u = uRcxSaved; } /** * Worker for bs3CpuBasic2_RaiseXcpt0e_c32 that does the actual testing. * * Caller does all the cleaning up. * * @returns Error count. * @param pThis Test state data. */ static uint8_t bs3CpuBasic2_RaiseXcpt0eWorker(PBS3CPUBASIC2PFSTATE register pThis) { unsigned iRing; BS3REGCTX aCtxts[4]; /** @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 = (uintptr_t)pThis->pbTest; } 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++) { /* 1. we can execute the test page. */ bs3CpuBasic2Pf_DoExec(pThis, &aCtxts[iRing], X86_XCPT_UD, UINT8_MAX, true /*fPageLevel*/); /* 2. we can read from the test page. */ bs3CpuBasic2Pf_DoMovLoad(pThis, &aCtxts[iRing], X86_XCPT_UD, UINT8_MAX, true /*fPageLevel*/); /* 3. we can write to the test page. */ bs3CpuBasic2Pf_DoMovStore(pThis, &aCtxts[iRing], X86_XCPT_UD, UINT8_MAX, true /*fPageLevel*/); /* 4. we can do locked read+write (a few variants). */ bs3CpuBasic2Pf_DoXchg(pThis, &aCtxts[iRing], X86_XCPT_UD, UINT8_MAX, true /*fPageLevel*/); bs3CpuBasic2Pf_DoCmpXchg(pThis, &aCtxts[iRing], X86_XCPT_UD, UINT8_MAX, true /*fPageLevel*/, false /*fMissmatch*/); bs3CpuBasic2Pf_DoCmpXchg(pThis, &aCtxts[iRing], X86_XCPT_UD, UINT8_MAX, true /*fPageLevel*/, true /*fMissmatch*/); } /* * Check the U bit on PTE level. */ 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; /* * 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 <= _8K) { Bs3TestFailed("Failed to allocate memory to play around with\n"); return 1; } } 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; } /* * Alias this memory far away from where our code and data lives. */ State.pbTest = (uint8_t *)UINT32_C(0x80000000); rc = Bs3PagingAlias((uintptr_t)State.pbTest, (uintptr_t)State.pbOrgTest, State.cbTest, X86_PTE_P | X86_PTE_RW | X86_PTE_US); if (RT_SUCCESS(rc)) { rc = Bs3PagingQueryAddressInfo((uintptr_t)State.pbTest, &State.PgInfo); if (RT_SUCCESS(rc)) { /* * Setup a 16-bit selector for accessing the alias. */ Bs3SelSetup16BitData(&Bs3GdteSpare00, (uintptr_t)State.pbTest); State.uSel16TestData = BS3_SEL_SPARE_00 | 3; //Bs3TestPrintf("RaiseXcpt0e_c32: bMode=%#x/%#x cbTest=%#x pbTest=%p pbAlias=%p\n", // bMode, g_bBs3CurrentMode, cbTest, pbTest, pbAlias); bRet = bs3CpuBasic2_RaiseXcpt0eWorker(&State); } else Bs3TestFailedF("Bs3PagingQueryAddressInfo failed: %d\n", rc); Bs3PagingUnalias((uintptr_t)State.pbTest, State.cbTest); } else Bs3TestFailedF("Bs3PagingAlias failed! rc=%d\n", rc); Bs3MemFree(pvTestUnaligned, cbTestUnaligned); return bRet; }