summaryrefslogtreecommitdiffstats
path: root/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-pf-1-template.mac
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/ValidationKit/bootsectors/bootsector2-cpu-pf-1-template.mac')
-rw-r--r--src/VBox/ValidationKit/bootsectors/bootsector2-cpu-pf-1-template.mac1061
1 files changed, 1061 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-pf-1-template.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-pf-1-template.mac
new file mode 100644
index 00000000..86c7ce4c
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bootsector2-cpu-pf-1-template.mac
@@ -0,0 +1,1061 @@
+; $Id: bootsector2-cpu-pf-1-template.mac $
+;; @file
+; Bootsector test for various types of #PFs - multi mode template.
+;
+
+;
+; 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
+;
+
+
+%include "bootsector2-template-header.mac"
+
+
+;*******************************************************************************
+;* Defined Constants And Macros *
+;*******************************************************************************
+%undef BIG_PAGE_SIZE
+%undef PXE_SIZE
+%ifdef TMPL_CMN_PP
+ %define BIG_PAGE_SIZE _4M
+ %define PXE_SIZE 4
+%else
+ %define BIG_PAGE_SIZE _2M
+ %define PXE_SIZE 8
+%endif
+
+
+;;
+; Do the tests for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(DoTestsForMode_rm)
+ push bp
+ mov bp, sp
+ push ax
+
+ ;
+ ; Check if the mode and NX is supported, do the switch.
+ ;
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .done
+ mov ax, [bp - 2]
+ test al, al
+ jz .nx_disabled
+ call Bs2IsNXSupported_r86
+ jz .done
+ call Bs2EnableNX_r86
+.nx_disabled:
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+
+ ;
+ ; Do the tests.
+ ;
+ call TMPL_NM(TestNotPresent)
+ ;; @todo call TMPL_NM(TestReadOnly)
+ ;; @todo call TMPL_NM(TestSupervisor)
+ ;; @todo call TMPL_NM(TestReservedBits)
+
+ ;
+ ; Back to real mode.
+ ;
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+ call Bs2DisableNX_r86
+
+.done:
+ pop ax
+ leave
+ ret
+ENDPROC TMPL_NM(DoTestsForMode_rm)
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+
+;;
+; Do the tests for this mode.
+;
+; @uses nothing
+;
+BEGINCODELOW
+BITS 16
+BEGINPROC TMPL_NM(DoBenchmarksForMode_rm)
+ push bp
+ mov bp, sp
+ push ax
+
+ ;
+ ; Check if the mode and NX is supported, do the switch.
+ ;
+ call TMPL_NM(Bs2IsModeSupported_rm)
+ jz .done
+ call TMPL_NM(Bs2EnterMode_rm)
+BITS TMPL_BITS
+
+ ;
+ ; Do the tests.
+ ;
+ call TMPL_NM(BenchmarkNotPresent)
+
+ ;
+ ; Back to real mode.
+ ;
+ call TMPL_NM(Bs2ExitMode)
+BITS 16
+.done:
+ pop ax
+ leave
+ ret
+ENDPROC TMPL_NM(DoBenchmarksForMode_rm)
+TMPL_BEGINCODE
+BITS TMPL_BITS
+
+
+;;
+; Does the page-not-present tests.
+;
+; @param al Set if NXE=1, clear if NXE=0.
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(TestNotPresent)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push xCX
+ push xDX
+ push xDI
+ push xSI
+
+ ;
+ ; Setup sCX for all the following tests.
+ ;
+ xor sCX, sCX
+ test al, al
+ jz .no_nxe
+ mov sCX, X86_TRAP_PF_ID
+.no_nxe:
+
+ ;
+ ; First test, big page not present.
+ ;
+ mov xAX, .s_szBigPageNotPresent
+ test sCX, sCX
+ jz .test1_nxe
+ mov xAX, .s_szBigPageNotPresentNX
+.test1_nxe:
+ call TMPL_NM_CMN(TestSub)
+ call TMPL_NM(TestFillTestAreaWithRet)
+
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TestGetPdeAddr)
+ and byte [sAX], ~X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ mov sAX, TST_SCRATCH_PD_BASE
+ mov sDX, 0 ; err code
+ call TMPL_NM(TestHammerPage)
+ jz .test1_cleanup
+
+ mov sAX, TST_SCRATCH_PD_BASE + (BIG_PAGE_SIZE / 2 - _4K)
+ call TMPL_NM(TestHammerPage)
+ jz .test1_cleanup
+
+ mov sAX, TST_SCRATCH_PD_BASE + (BIG_PAGE_SIZE - _4K)
+ call TMPL_NM(TestHammerPage)
+ jz .test1_cleanup
+
+.test1_cleanup:
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TestGetPdeAddr)
+ or byte [sAX], X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ ;
+ ; The second test, normal page not present.
+ ;
+ mov xAX, .s_szPageNotPresent
+ test sCX, sCX
+ jz .test2_nxe
+ mov xAX, .s_szPageNotPresentNX
+.test2_nxe:
+ call TMPL_NM_CMN(TestSub)
+
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TstPutPageTableAt)
+
+ ; Make the first and last page not-present.
+ and byte [BS2_USER_PX_0_ADDR], ~X86_PTE_P
+ and byte [BS2_USER_PX_0_ADDR + 01000h - PXE_SIZE], ~X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ ; Do the tests.
+ mov sAX, TST_SCRATCH_PD_BASE
+ mov sDX, 0 ; err code
+ call TMPL_NM(TestHammerPage)
+ jz .test2_cleanup
+
+ mov sAX, TST_SCRATCH_PD_BASE + (BIG_PAGE_SIZE - _4K)
+ call TMPL_NM(TestHammerPage)
+ jz .test2_cleanup
+
+.test2_cleanup:
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TstRestoreBigPageAt)
+
+
+%if PXE_SIZE == 8 ; PAE or LM
+ ;
+ ; The third test, mark a page directory pointer entry not present.
+ ;
+ mov xAX, .s_szPdpeNotPresent
+ test sCX, sCX
+ jz .test3_nxe
+ mov xAX, .s_szPdpeNotPresentNX
+.test3_nxe:
+ call TMPL_NM_CMN(TestSub)
+ call TMPL_NM(TestFillTestAreaWithRet)
+
+ mov sAX, TST_SCRATCH_PDPT_BASE
+ call TMPL_NM(TestGetPdpeAddr)
+ and byte [sAX], ~X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ mov sAX, TST_SCRATCH_PDPT_BASE
+ mov sDX, 0 ; err code
+ call TMPL_NM(TestHammerPage)
+ jz .test3_cleanup
+
+ mov sAX, TST_SCRATCH_PDPT_BASE + (BIG_PAGE_SIZE / 2 - _4K)
+ call TMPL_NM(TestHammerPage)
+ jz .test3_cleanup
+
+ mov sAX, TST_SCRATCH_PDPT_BASE + (BIG_PAGE_SIZE - _4K)
+ call TMPL_NM(TestHammerPage)
+ jz .test3_cleanup
+
+.test3_cleanup:
+ mov sAX, TST_SCRATCH_PDPT_BASE
+ call TMPL_NM(TestGetPdpeAddr)
+ or byte [sAX], X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+%endif ; PAE || LM
+
+
+%ifdef TMPL_LM64
+ ;
+ ; The fourth test, mark a page map level 4 entry not present.
+ ;
+ mov xAX, .s_szPml4eNotPresent
+ test sCX, sCX
+ jz .test4_nxe
+ mov xAX, .s_szPml4eNotPresentNX
+.test4_nxe:
+ call TMPL_NM_CMN(TestSub)
+ call TMPL_NM(TestFillTestAreaWithRet)
+
+ mov sAX, TST_SCRATCH_PML4_BASE
+ call TMPL_NM(TestGetPml4eAddr)
+ and byte [sAX], ~X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ mov sAX, TST_SCRATCH_PML4_BASE
+ mov sDX, 0 ; err code
+ call TMPL_NM(TestHammerPage)
+ jz .test4_cleanup
+
+ mov sAX, TST_SCRATCH_PML4_BASE + (BIG_PAGE_SIZE / 2 - _4K)
+ call TMPL_NM(TestHammerPage)
+ jz .test4_cleanup
+
+ mov sAX, TST_SCRATCH_PML4_BASE + (BIG_PAGE_SIZE - _4K)
+ call TMPL_NM(TestHammerPage)
+ jz .test4_cleanup
+
+.test4_cleanup:
+ mov sAX, TST_SCRATCH_PML4_BASE
+ call TMPL_NM(TestGetPml4eAddr)
+ or byte [sAX], X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+%endif
+
+ ;
+ ; Done.
+ ;
+ call TMPL_NM_CMN(TestSubDone)
+
+ pop xSI
+ pop xDI
+ pop xDX
+ pop xCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szBigPageNotPresent:
+ db TMPL_MODE_STR, ', !NX, big page NP', 0
+.s_szBigPageNotPresentNX:
+ db TMPL_MODE_STR, ', NX, big page NP', 0
+.s_szPageNotPresent:
+ db TMPL_MODE_STR, ', !NX, page NP', 0
+.s_szPageNotPresentNX:
+ db TMPL_MODE_STR, ', NX, page NP', 0
+%if PXE_SIZE == 8 ; PAE or LM
+.s_szPdpeNotPresent:
+ db TMPL_MODE_STR, ', !NX, PDPE NP', 0
+.s_szPdpeNotPresentNX:
+ db TMPL_MODE_STR, ', NX, PDPE NP', 0
+%endif
+%ifdef TMPL_LM64
+.s_szPml4eNotPresent:
+ db TMPL_MODE_STR, ', !NX, PML4E NP', 0
+.s_szPml4eNotPresentNX:
+ db TMPL_MODE_STR, ', NX, PML4E NP', 0
+%endif
+ENDPROC TMPL_NM(TestNotPresent)
+
+
+
+;;
+; Does the page-not-present benchmark.
+;
+; @uses nothing
+;
+BEGINPROC TMPL_NM(BenchmarkNotPresent)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push xBX
+ push sCX
+ push sDX
+ push xDI
+ push xSI
+ sub xSP, 20h
+
+ call TMPL_NM(TestFillTestAreaWithRet)
+
+ ;
+ ; The First benchmark: Big page not present.
+ ;
+
+ ; Mark the big test page not present.
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TestGetPdeAddr)
+ and byte [sAX], ~X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ ; Benchmark.
+ mov sAX, TST_SCRATCH_PD_BASE
+ mov xDX, .s_szBigPageNotPresent
+ mov xCX, .s_szBigPageNotPresentFailed
+ call TMPL_NM(TstBenchmark32BitReads)
+
+ ; Cleanup.
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TestGetPdeAddr)
+ or byte [sAX], X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ ;
+ ; The second benchmark: Normal page not present.
+ ;
+
+ ; Replace the big page with a page table and make the first and last
+ ; pages not-present.
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TstPutPageTableAt)
+
+ and byte [BS2_USER_PX_0_ADDR], ~X86_PTE_P
+ and byte [BS2_USER_PX_0_ADDR + 01000h - PXE_SIZE], ~X86_PTE_P
+ mov sAX, cr3
+ mov cr3, sAX
+
+ ; Benchmark.
+ mov sAX, TST_SCRATCH_PD_BASE
+ mov xDX, .s_szPageNotPresent
+ mov xCX, .s_szPageNotPresentFailed
+ call TMPL_NM(TstBenchmark32BitReads)
+
+ ; Cleanup
+ mov sAX, TST_SCRATCH_PD_BASE
+ call TMPL_NM(TstRestoreBigPageAt)
+
+
+ ;
+ ; Done.
+ ;
+ add xSP, 20h
+ pop xSI
+ pop xDI
+ pop sDX
+ pop sCX
+ pop xBX
+ pop sAX
+ leave
+ ret
+
+.s_szBigPageNotPresent:
+ db TMPL_MODE_STR, ', read NP big page', 0
+.s_szBigPageNotPresentFailed:
+ db TMPL_MODE_STR, ', reading NP big page failed', 13, 10, 0
+.s_szPageNotPresent:
+ db TMPL_MODE_STR, ', read NP page', 0
+.s_szPageNotPresentFailed:
+ db TMPL_MODE_STR, ', reading NP page failed', 13, 10, 0
+ENDPROC TMPL_NM(BenchmarkNotPresent)
+
+
+;;
+; Benchmark 32-bit reads at a give location.
+;
+; Will report the result under the name given via xDX. Will report any test
+; failure giving the string pointed to by xCX as explanation.
+;
+; @param sAX The location to do the reads.
+; @param xDX The test value name.
+; @param xCX The failure string
+; @uses nothing
+;
+BEGINPROC TMPL_NM(TstBenchmark32BitReads)
+ push xBP
+ mov xBP, xSP
+%define a_pu32Test [xBP - sCB]
+ push sAX
+%define a_pszValue [xBP - sCB*2]
+ push sDX
+%define a_pszFailure [xBP - sCB*3]
+ push sCX
+ push sSI
+ push sDI
+%define u64NanoTS [xBP - sCB*5 - 8h]
+ sub xSP, 8h
+
+ ;
+ ; Calibrate the test so it doesn't take forever.
+ ;
+ mov xAX, .calibrate_resume
+ mov dl, 0eh
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+ mov ecx, TST_CALIBRATE_LOOP_COUNT
+
+ lea xAX, u64NanoTS
+ call TMPL_NM_CMN(GetNanoTS)
+
+.calibrate_loop:
+ mov sAX, a_pu32Test
+ mov esi, [sAX]
+.calibrate_resume:
+ test sAX, sAX
+ jnz .failure
+ dec ecx
+ jnz .calibrate_loop
+
+ lea xAX, u64NanoTS
+ call TMPL_NM_CMN(GetElapsedNanoTS)
+ call TMPL_NM_CMN(Bs2TrapReset)
+
+ ; Figure out how many iterations is required for the full benchmark.
+ mov ecx, TST_BENCHMARK_PERIOD_IN_SECS
+ mov edx, TST_CALIBRATE_LOOP_COUNT
+ mov xAX, xSP
+ call TMPL_NM_CMN(CalcBenchmarkIterations)
+ mov ecx, eax ; iteration count.
+
+ ;
+ ; Do the full benchmark run.
+ ;
+ mov xAX, .bench_resume
+ mov dl, 0eh
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+ mov edx, ecx ; save test count for ReportResult.
+
+ lea xAX, u64NanoTS
+ call TMPL_NM_CMN(GetNanoTS)
+.bench_loop:
+ mov xAX, a_pu32Test
+ mov esi, [eax]
+.bench_resume:
+ test eax, eax
+ jnz .failure
+ dec ecx
+ jnz .bench_loop
+
+ lea xAX, u64NanoTS
+ call TMPL_NM_CMN(GetElapsedNanoTS)
+ call TMPL_NM_CMN(Bs2TrapReset)
+
+ mov xCX, a_pszValue
+ lea xAX, u64NanoTS
+ call TMPL_NM_CMN(ReportResult)
+
+.return:
+ pop sDI
+ pop sSI
+ pop sCX
+ pop sDX
+ pop sAX
+ leave
+ ret
+
+.failure:
+ call TMPL_NM_CMN(Bs2TrapReset)
+ mov xAX, a_pszFailure
+ call TMPL_NM_CMN(TestFailed)
+ jmp .return
+
+%undef a_pszFailure
+%undef a_pu32Test
+%undef a_pszValue
+%undef a_pszFailure
+%undef u64NanoTS
+ENDPROC TMPL_NM(TstBenchmark32BitReads)
+
+
+;;
+; Fills the test area with return instructions.
+;
+; @uses nothing.
+;
+BEGINPROC TMPL_NM(TestFillTestAreaWithRet)
+ push xBP
+ mov xBP, xSP
+ push xDI
+ push xCX
+ push xAX
+
+ mov xDI, TST_SCRATCH_PD_BASE
+ mov xCX, (_4M + _4M) / 4
+ mov eax, 0c3c3c3c3h
+ rep stosd
+
+ mov xDI, TST_SCRATCH_PDPT_BASE
+ mov xCX, (_4M + _4M) / 4
+ mov eax, 0c3c3c3c3h
+ rep stosd
+
+%ifdef TMPL_LM64
+ mov xDI, TST_SCRATCH_PML4_BASE
+ mov xCX, (_4M + _4M) / 8
+ mov rax, 0c3c3c3c3c3c3c3c3h
+ rep stosq
+%endif
+
+ pop xAX
+ pop xCX
+ pop xDI
+ leave
+ ret
+ENDPROC TMPL_NM(TestFillTestAreaWithRet)
+
+
+;;
+; Gets the page directory address.
+;
+; ASSUMES identity mapped page translation tables.
+;
+; @returns ds:xAX The page directory address.
+; @param sAX The virtual address in question.
+; @uses nothing
+;
+BEGINPROC TMPL_NM(TestGetPdeAddr)
+ push xBP
+ mov xBP, xSP
+ push sBX
+ push sCX
+
+%ifdef TMPL_CMN_PP
+ ; PDPE
+ shr sAX, X86_PD_SHIFT
+ and sAX, X86_PD_MASK
+ shl sAX, 2
+ mov sBX, cr3
+ and sBX, X86_CR3_PAGE_MASK
+ add sAX, sBX
+
+%else
+ %ifdef TMPL_CMN_LM
+ ; PML4E
+ mov sCX, sAX
+ shr sCX, X86_PML4_SHIFT
+ and sCX, X86_PML4_MASK
+ shl sCX, 3
+ mov sBX, cr3
+ and sBX, X86_CR3_AMD64_PAGE_MASK & 0ffffffffh
+ add sBX, sCX
+ mov sBX, [sBX]
+ and sBX, X86_PDPE_PG_MASK & 0ffffffffh
+ %else
+ mov sBX, cr3
+ and sBX, X86_CR3_PAE_PAGE_MASK
+ %endif
+
+ ; PDPE
+ mov sCX, sAX
+ shr sCX, X86_PDPT_SHIFT
+ %ifdef TMPL_CMN_LM
+ and sCX, X86_PDPT_MASK_AMD64
+ %else
+ and sCX, X86_PDPT_MASK_PAE
+ %endif
+ shl sCX, 3
+ add sBX, xCX
+ mov sBX, [sBX]
+ and sBX, X86_PDPE_PG_MASK & 0ffffffffh
+
+ ; PDE
+ shr sAX, X86_PD_PAE_SHIFT
+ and sAX, X86_PD_PAE_MASK
+ shl sAX, 3
+ add sAX, sBX
+%endif
+
+ pop sCX
+ pop sBX
+ leave
+ ret
+ENDPROC TMPL_NM(TestGetPdeAddr)
+
+
+%if PXE_SIZE == 8 ; PAE or LM
+;;
+; Gets the page directory pointer entry for an address.
+;
+; ASSUMES identity mapped page translation tables.
+;
+; @returns ds:xAX The pointer to the PDPE.
+; @param sAX The virtual address in question.
+; @uses nothing
+;
+BEGINPROC TMPL_NM(TestGetPdpeAddr)
+ push xBP
+ mov xBP, xSP
+ push sBX
+ push sCX
+
+%ifdef TMPL_CMN_PP
+ %error "misconfig"
+%endif
+
+%ifdef TMPL_CMN_LM
+ ; PML4E
+ mov sCX, sAX
+ shr sCX, X86_PML4_SHIFT
+ and sCX, X86_PML4_MASK
+ shl sCX, 3
+ mov sBX, cr3
+ and sBX, X86_CR3_AMD64_PAGE_MASK & 0ffffffffh
+ add sBX, sCX
+ mov sBX, [sBX]
+ and sBX, X86_PDPE_PG_MASK & 0ffffffffh
+%else
+ mov sBX, cr3
+ and sBX, X86_CR3_PAE_PAGE_MASK
+%endif
+
+ ; PDPE
+ shr sAX, X86_PDPT_SHIFT
+%ifdef TMPL_CMN_LM
+ and sAX, X86_PDPT_MASK_AMD64
+%else
+ and sAX, X86_PDPT_MASK_PAE
+%endif
+ shl sAX, 3
+ add sAX, sBX
+
+ pop sCX
+ pop sBX
+ leave
+ ret
+ENDPROC TMPL_NM(TestGetPdpeAddr)
+%endif ; PAE or LM
+
+
+%ifdef TMPL_CMN_LM
+;;
+; Gets the page map level 4 entry for an address.
+;
+; ASSUMES identity mapped page translation tables.
+;
+; @returns rax The pointer to the PML4E.
+; @param rax The virtual address in question.
+; @uses nothing
+;
+BEGINPROC TMPL_NM(TestGetPml4eAddr)
+ push xBP
+ mov xBP, xSP
+ push rbx
+
+ ; PML4E
+ shr rax, X86_PML4_SHIFT
+ and rax, X86_PML4_MASK
+ shl rax, 3
+ mov rbx, cr3
+ and rbx, X86_CR3_AMD64_PAGE_MASK & 0ffffffffh
+ add rax, rbx
+
+ pop rbx
+ leave
+ ret
+ENDPROC TMPL_NM(TestGetPml4eAddr)
+%endif ; TMPL_CMN_LM
+
+
+;;
+; Initialize page table #0 and hooks it up at the specified address.
+;
+; The page table will have identity mapped pages. The TLBs are flushed
+; wholesale. The caller will have to reconstruct the PDE when finished.
+;
+; @param sAX The virtual address (big page -> page table).
+; @uses nothing
+;
+BEGINPROC TMPL_NM(TstPutPageTableAt)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sCX
+ push sDI
+ push sSI
+
+ ; initialize a page table.
+ mov sDI, BS2_USER_PX_0_ADDR
+ mov sSI, sAX
+.init_loop:
+%if PXE_SIZE == 8
+ mov [sDI + 4], dword 0
+ mov [sDI], sSI
+%else
+ mov [sDI], esi
+%endif
+ or byte [sDI], X86_PTE_P | X86_PTE_RW
+ add sSI, _4K
+ add sDI, PXE_SIZE
+ test sDI, 0fffh
+ jnz .init_loop
+
+ ; hook it up instead of the big page.
+ and sAX, ~(BIG_PAGE_SIZE - 1)
+ mov sDI, sAX
+ call TMPL_NM(TestGetPdeAddr)
+ mov dword [sAX], BS2_USER_PX_0_ADDR | X86_PDE_P | X86_PDE_RW | X86_PDE_RW
+%if PXE_SIZE == 8
+ mov dword [sAX + 4], 0
+%endif
+ mov sAX, cr3
+ mov cr3, sAX
+
+ ; Make sure it works.
+ mov eax, 0c3c3c3c3h
+ mov ecx, BIG_PAGE_SIZE / 4
+ rep stosd
+
+ pop sSI
+ pop sDI
+ pop sCX
+ pop sAX
+ leave
+ ret
+ENDPROC TMPL_NM(TstPutPageTableAt)
+
+
+;;
+; Restores the big page for a virtual address, undoing harm done by a
+; previous TstPutPageTableAt call.
+;
+; @param sAX The virtual address to restore to a big page.
+; @uses nothing
+;
+BEGINPROC TMPL_NM(TstRestoreBigPageAt)
+ push xBP
+ mov xBP, xSP
+ push sAX
+ push sCX
+ push sDI
+
+ ; Set it up, inheriting bits from the previous PDE.
+ and sAX, ~(BIG_PAGE_SIZE - 1)
+ mov sDI, sAX ; save it for later.
+ call TMPL_NM(TestGetPdeAddr)
+ mov sCX, [sAX - PXE_SIZE]
+ and sCX, X86_PDE4M_US | X86_PDE4M_RW | X86_PDE4M_G | X86_PDE4M_PAT | X86_PDE4M_AVL | X86_PDE4M_PCD | X86_PDE4M_PWT
+ or sCX, X86_PDE4M_P | X86_PDE4M_PS
+ or sCX, sDI
+%if PXE_SIZE == 8
+ mov dword [sAX + 4], 0
+ mov [sAX], sCX
+%else
+ mov [sAX], ecx
+%endif
+ mov sAX, cr3
+ mov cr3, sAX
+
+ ; Make sure it works.
+ mov eax, 0c3c3c3c3h
+ mov ecx, BIG_PAGE_SIZE / 4
+ rep stosd
+
+ pop sDI
+ pop sCX
+ pop sAX
+ leave
+ ret
+ENDPROC TMPL_NM(TstRestoreBigPageAt)
+
+
+
+;;
+; Hammers a page.
+;
+; Accesses a page in a few different ways, expecting all of the accesses to
+; cause some kind of page fault. The caller just makes sure the page causes
+; a fault and points us to it.
+;
+; @returns al=1, ZF=0 on success.
+; @returns al=0, ZF=1 on failure.
+; @param sAX The page.
+; @param sDX The base error code to expect.
+; @param xCX X86_TRAP_PF_ID if NXE, otherwise 0.
+; @uses al
+;
+BEGINPROC TMPL_NM(TestHammerPage)
+ push xBP
+ mov xBP, xSP
+ push sBX
+%define a_uErrorExec sPRE [xBP - sCB*2]
+ push sCX
+%define a_uErrorFixed sPRE [xBP - sCB*3]
+ push sDX
+ push sDI
+ push sSI
+%define a_pPage sPRE [xBP - sCB*6]
+ push sAX
+
+ ;
+ ; First reads of different sizes.
+ ;
+ mov sDI, a_pPage
+.read_byte_loop:
+ mov dl, 0ffh
+ mov xAX, .read_byte_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+.read_byte:
+ mov cl, byte [sDI]
+.read_byte_resume:
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ mov sCX, .read_byte ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .read_byte_loop
+
+ mov sDI, a_pPage
+.read_word_loop:
+ mov dl, 0ffh
+ mov xAX, .read_word_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+.read_word:
+ mov cx, word [sDI]
+.read_word_resume:
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ mov sCX, .read_word ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .read_word_loop
+
+ mov sDI, a_pPage
+.read_dword_loop:
+ mov dl, 0ffh
+ mov xAX, .read_dword_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+.read_dword:
+ mov ecx, dword [sDI]
+.read_dword_resume:
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ mov sCX, .read_dword ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .read_dword_loop
+
+ ;
+ ; Then writes of different sizes.
+ ;
+ mov sDI, a_pPage
+.write_byte_loop:
+ mov dl, 0ffh
+ mov xAX, .write_byte_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+.write_byte:
+ mov byte [sDI], 0c3h ; (ret instruction)
+.write_byte_resume:
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ or sDX, X86_TRAP_PF_RW
+ mov sCX, .write_byte ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .write_byte_loop
+
+ mov sDI, a_pPage
+.write_word_loop:
+ mov dl, 0ffh
+ mov xAX, .write_word_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+.write_word:
+ mov word [sDI], 0c3c3h ; (2 ret instructions)
+.write_word_resume:
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ or sDX, X86_TRAP_PF_RW
+ mov sCX, .write_word ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .write_word_loop
+
+ mov sDI, a_pPage
+.write_dword_loop:
+ mov dl, 0ffh
+ mov xAX, .write_dword_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+.write_dword:
+ mov dword [sDI], 0c3c3c3c3h ; (4 ret instructions)
+.write_dword_resume:
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ or sDX, X86_TRAP_PF_RW
+ mov sCX, .write_dword ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .write_dword_loop
+
+ ;
+ ; Execute access.
+ ;
+ mov sDI, a_pPage
+ mov xSI, xSP
+.call_loop:
+ mov dl, 0ffh
+ mov xAX, .call_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+ call sDI
+.call_resume:
+ mov xSP, xSI ; restore xSP since the call will change it before #PF'ing.
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ or sDX, a_uErrorExec
+ mov sCX, sDI ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .call_loop
+
+
+ mov sDI, a_pPage
+ mov xSI, xSP
+.jmp_loop:
+ mov dl, 0ffh
+ mov xAX, .jmp_resume
+ call TMPL_NM_CMN(Bs2TrapPrepare)
+ push .jmp_resume ; push a return address in case of failure.
+ jmp sDI
+.jmp_resume:
+ mov xSP, xSI ; restore xSP in case the jmp didn't trap.
+ mov eax, 0eh ; trap #
+ mov sDX, a_uErrorFixed ; err
+ or sDX, a_uErrorExec
+ mov sCX, sDI ; fault eip
+ mov sBX, sDI ; fault address.
+ call TMPL_NM_CMN(TestCheckTrap)
+ jz .failed
+ inc sDI
+ test sDI, 0fffh
+ jnz .jmp_loop
+
+ ; successfull return.
+ pop sAX
+ xor al, al
+ inc al
+.return:
+ pop sSI
+ pop sDI
+ pop sDX
+ pop sCX
+ pop sBX
+ leave
+ ret
+
+.failed:
+ pop sAX
+ xor al, al
+ jmp .return
+%undef a_uErrorFixed
+%undef a_uErrorExec
+%undef a_pPage
+ENDPROC TMPL_NM(TestHammerPage)
+
+
+%include "bootsector2-template-footer.mac"
+