diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/ValidationKit/bootsectors/bootsector2-common-routines-template-1.mac | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/VBox/ValidationKit/bootsectors/bootsector2-common-routines-template-1.mac | 2655 |
1 files changed, 2655 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-common-routines-template-1.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-common-routines-template-1.mac new file mode 100644 index 00000000..bc8802c4 --- /dev/null +++ b/src/VBox/ValidationKit/bootsectors/bootsector2-common-routines-template-1.mac @@ -0,0 +1,2655 @@ +; $Id: bootsector2-common-routines-template-1.mac $ +;; @file +; bootsector2 common routines - template containing code common to related modes. +; + +; +; 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" +%include "VBox/bios.mac" + +ALIGNCODE(32) +GLOBALNAME TMPL_NM_CMN(g_szMode) + db TMPL_MODE_STR, 0 + + +;; +; Shutdown routine. +; +; Does not return. +; +; @uses N/A +; +BEGINPROC TMPL_NM_CMN(Shutdown) +%ifdef TMPL_16BIT + jmp Bs2Shutdown +%else + cli + mov bl, 64 + mov ax, ss + mov ds, ax + mov dx, VBOX_BIOS_SHUTDOWN_PORT + mov ax, VBOX_BIOS_OLD_SHUTDOWN_PORT +.retry: + mov ecx, 8 + mov esi, .s_szShutdown + rep outsb + xchg dx, ax ; alternate between the new (VBox) and old (Bochs) ports. + dec bl + jnz .retry + ; Shutdown failed! + jmp Bs2Panic +.s_szShutdown: + db 'Shutdown', 0 +%endif +ENDPROC TMPL_NM_CMN(Shutdown) + + +;; +; Prints a 32-bit unsigned integer on the screen. +; +; @param eax The value to print. +; +; @uses nothing +; +BEGINPROC TMPL_NM_CMN(PrintU32) + push xBP + mov xBP, xSP + push sAX + push sDX + push sCX + push sBX +%ifdef TMPL_16BIT + push ds + + mov cx, ss + mov ds, cx +%endif + + ; Allocate a stack buffer and terminate it. ds:bx points ot the end. + sub xSP, 30h + mov xBX, xSP + add xBX, 2fh + mov byte [xBX], 0 + + mov ecx, 10 ; what to divide by +.next: + xor edx, edx + div ecx ; edx:eax / ecx -> eax and rest in edx. + add dl, '0' + dec xBX + mov [xBX], dl + cmp eax, 0 + jnz .next + + ; Print the string. + mov xAX, xBX + call TMPL_NM_CMN(PrintStr) + + add xSP, 30h +%ifdef TMPL_16BIT + pop ds +%endif + pop sBX + pop sCX + pop sDX + pop sAX + leave + ret +ENDPROC TMPL_NM_CMN(PrintU32) + + +;; +; Equivalent to RTPrintf, but a max output length of 1KB. +; +; @remarks This uses an entirely STACK BASED CALLING CONVENTION where the +; caller does the cleanup (cdecl sans volatile regs). +; +; @param fpszFormat The format string (far pointer on 16-bit +; systems). See StrFormatV for format details. +; @param ... Zero or more format string arguments. +; @uses nothing +; +BEGINPROC TMPL_NM_CMN(PrintF) + push xBP + mov xBP, xSP + push sAX + push xDX + push xCX + push xBX +%ifdef TMPL_16BIT + push ds + push es + push fs +%endif + sub xSP, 0400h ; string buffer (1024 bytes) + + ; + ; Format the failure string and call TestFailed. + ; +%ifdef TMPL_16BIT + mov ax, ss ; buffer address. + mov ds, ax + mov ax, sp + mov xDX, 0400h ; buffer size. + les cx, [bp + 4] ; format string + mov bx, ss ; argument list + mov fs, bx + mov bx, bp + add bx, 8 +%else + mov xAX, xSP ; buffer address + mov xDX, 0400h ; buffer size + mov xCX, [xBP + xCB * 2] ; format string + lea xBX, [xBP + xCB * 3] ; argument list. +%endif + call TMPL_NM_CMN(StrFormatV) + call TMPL_NM_CMN(PrintStr) + + add xSP, 0400h +%ifdef TMPL_16BIT + pop fs + pop es + pop ds +%endif + pop xBX + pop xCX + pop xDX + pop sAX + leave + ret +ENDPROC TMPL_NM_CMN(PrintF) + + +;; +; Print a string followed by a semicolon and at least one space. +; +; @param ds:ax The string to print. +; @param dx The desired minimum length of the output. That is +; string + colon + spaces. +; @uses nothing. +; +BEGINPROC TMPL_NM_CMN(PrintStrColonSpaces) + push xBP + mov xBP, xSP + push xAX + push xCX + + call TMPL_NM_CMN(PrintStr) + call TMPL_NM_CMN(StrLen) + mov cx, ax + mov al, ':' + call TMPL_NM_CMN(PrintChr) + inc cx + mov al, ' ' +.next_space: + call TMPL_NM_CMN(PrintChr) + inc cx + cmp cx, dx + jb .next_space + + pop xCX + pop xAX + leave + ret +ENDPROC TMPL_NM_CMN(PrintStrColonSpaces) + + +;; +; Print a string followed by a 0+ spaces, a semicolon and a space. +; +; @param ds:ax The string to print. +; @param dx The desired minimum length of the output. That is +; string + spaces + colon + space. +; @uses nothing. +; +BEGINPROC TMPL_NM_CMN(PrintStrSpacesColonSpace) + push xBP + mov xBP, xSP + push xAX + push xCX + + call TMPL_NM_CMN(PrintStr) + call TMPL_NM_CMN(StrLen) + mov cx, ax + inc cx + mov al, ' ' +.next_space: + inc cx + cmp cx, dx + jae .done_spaces + call TMPL_NM_CMN(PrintChr) + jmp .next_space +.done_spaces: + mov al, ':' + call TMPL_NM_CMN(PrintChr) + mov al, ' ' + call TMPL_NM_CMN(PrintChr) + + pop xCX + pop xAX + leave + ret +ENDPROC TMPL_NM_CMN(PrintStrSpacesColonSpace) + + +;; +; Store the current nanosecond timestamp in [ax] (qword). +; +; @param ds:ax Where to store the 64-bit timestamp. +; +; @uses nothing +; +BEGINPROC TMPL_NM_CMN(GetNanoTS) + push sAX + push sCX + push xDX +%ifdef TMPL_16BIT + movzx ecx, ax +%else + mov xCX, xAX +%endif + + mov dx, VMMDEV_TESTING_IOPORT_TS_LOW + in eax, dx + mov [sCX], eax + + mov dx, VMMDEV_TESTING_IOPORT_TS_HIGH + in eax, dx + mov [sCX + 4], eax + + pop xDX + pop sCX + pop sAX + ret +ENDPROC TMPL_NM_CMN(GetNanoTS) + + + +;; +; Calculates the time elapsed since [ax] (qword), storing it at [ax] (qword). +; +; @param ds:ax Where to get the start timestamp (input) and where +; to store the time elapsed (output). qword +; +; @uses nothing +; +BEGINPROC TMPL_NM_CMN(GetElapsedNanoTS) + push sAX + push sCX + push xDX +%ifdef TMPL_16BIT + movzx ecx, ax +%else + mov xCX, xAX +%endif + + mov dx, VMMDEV_TESTING_IOPORT_TS_LOW + in eax, dx + sub eax, [sCX] + mov [sCX], eax + + mov dx, VMMDEV_TESTING_IOPORT_TS_HIGH + in eax, dx + sbb eax, [sCX + 4] + mov [sCX + 4], eax + + pop xDX + pop sCX + pop sAX + ret +ENDPROC TMPL_NM_CMN(GetElapsedNanoTS) + + +;; +; Sends a command to VMMDev followed by a single string. +; +; If the VMMDev is not present or is not being used, this function will +; do nothing. +; +; @param eax The command. +; @param ds:dx The string (zero terminated). +; @uses nothing +; @internal +; +BEGINPROC TMPL_NM_CMN(testSendStrCmd) + push xBP + mov xBP, xSP + push sAX + push xBX + push xDX + + cmp byte [g_fbBs2VMMDevTesting], 0 + je .no_vmmdev + + mov dx, VMMDEV_TESTING_IOPORT_CMD + out dx, eax + + mov dx, VMMDEV_TESTING_IOPORT_DATA + pop xBX + push xBX + dec xBX +.next_char: + inc xBX + mov al, [xBX] + out dx, al + test al, al + jnz .next_char + +.no_vmmdev: + pop xDX + pop xBX + pop sAX + leave + ret +ENDPROC TMPL_NM_CMN(testSendStrCmd) + + +;; +; Equivalent to RTTestCreate + RTTestBanner +; +; @param DS16:xAX Pointer to a zero terminated string naming the +; test. Must be a global constant. +; @uses nothing +; +BEGINPROC TMPL_NM_CMN(TestInit) + push xBP + mov xBP, xSP + push sAX + push xDX + + ; Initialize the globals. + mov [g_npszBs2Test], xAX + xor eax, eax + mov [g_uscBs2TestErrors], ax + mov [g_npszBs2SubTest], eax + mov [g_uscBs2SubTestAtErrors], ax + mov byte [g_fbBs2SubTestReported], 1 + mov [g_uscBs2SubTests], ax + mov [g_uscBs2SubTestsFailed], ax + + ; Print the name. RTTestBanner + mov xAX, [g_npszBs2Test] + call TMPL_NM_CMN(PrintStr) + mov xAX, .s_szTesting + call TMPL_NM_CMN(PrintStr) + + ; Report it to the VMMDev. + mov eax, VMMDEV_TESTING_CMD_INIT + mov xDX, [g_npszBs2Test] + call TMPL_NM_CMN(testSendStrCmd) + + pop xDX + pop sAX + leave + ret +.s_szTesting: + db ': TESTING...', 13, 10, 0 +ENDPROC TMPL_NM_CMN(TestInit) + + +;; +; rtTestSubTestReport +; @uses nothing +; @internal +BEGINPROC TMPL_NM_CMN(testSubTestReport) + push xBP + mov xBP, xSP + push sAX + push sCX + push xDX + + ; Check if there is anything to do. + cmp byte [g_fbBs2SubTestReported], 0 + jne .done + xor xAX, xAX ; load the sub test name pointer for later + mov xAX, [g_npszBs2SubTest] + test xAX, xAX + jz .done + + ; Start the printing. + mov dx, 48 + call TMPL_NM_CMN(PrintStrSpacesColonSpace) + + mov byte [g_fbBs2SubTestReported], 1 + mov cx, [g_uscBs2TestErrors] + sub cx, [g_uscBs2SubTestAtErrors] + and ecx, 0ffffh + jnz .failed + + ; passed + mov xAX, .s_szPassed + call TMPL_NM_CMN(PrintStr) + jmp .vmmdev + + ; failed +.failed: + mov xAX, .s_szFailure + call TMPL_NM_CMN(PrintStr) + mov eax, ecx + call TMPL_NM_CMN(PrintU32) + mov xAX, .s_szFailureEnd + call TMPL_NM_CMN(PrintStr) + + ; report to VMMDev +.vmmdev: + cmp byte [g_fbBs2VMMDevTesting], 0 + je .no_vmmdev + + mov dx, VMMDEV_TESTING_IOPORT_CMD + mov eax, VMMDEV_TESTING_CMD_SUB_DONE + out dx, eax + + mov dx, VMMDEV_TESTING_IOPORT_DATA + mov eax, ecx + out dx, eax + +.no_vmmdev: +.done: + pop xDX + pop sCX + pop sAX + leave + ret +.s_szPassed: + db 'PASSED', 13, 10, 0 +.s_szFailure: + db 'FAILED (', 0 +.s_szFailureEnd: + db ' errors)', 13, 10, 0 +ENDPROC TMPL_NM_CMN(testSubTestReport) + + +;; +; rtTestSubCleanup +; @uses nothing +; @internal +BEGINPROC TMPL_NM_CMN(testSubCleanup) + push xBP + mov xBP, xSP + + cmp dword [g_npszBs2SubTest], 0 + je .cleaned_up + + call TMPL_NM_CMN(testSubTestReport) + mov dword [g_npszBs2SubTest], 0 + mov byte [g_fbBs2SubTestReported], 0 + +.cleaned_up: + leave + ret +ENDPROC TMPL_NM_CMN(testSubCleanup) + + +;; +; Equivalent to RTTestSub. +; +; @param ds:xAX Pointer to a zero terminated string naming the sub test. +; @uses nothing +; +BEGINPROC TMPL_NM_CMN(TestSub) + push xBP + mov xBP, xSP + push sAX + push xDX + + ; Complete and cleanup any current sub test. + call TMPL_NM_CMN(testSubCleanup) + + ; Start a new sub test. + inc word [g_uscBs2SubTests] + mov dx, [g_uscBs2TestErrors] + mov [g_uscBs2SubTestAtErrors], dx + mov [g_npszBs2SubTest], xAX + mov byte [g_fbBs2SubTestReported], 0 + + ; Report it to the VMMDev. + mov xDX, xAX + mov eax, VMMDEV_TESTING_CMD_SUB_NEW + call TMPL_NM_CMN(testSendStrCmd) + + pop xDX + pop sAX + leave + ret +ENDPROC TMPL_NM_CMN(TestSub) + + +;; +; Calculates the error count for the current sub test. +; +; @returns ax Error count for the current sub test. +; @uses ax +; +BEGINPROC TMPL_NM_CMN(TestSubErrorCount) + pushf + + mov ax, [g_uscBs2TestErrors] + sub ax, [g_uscBs2SubTestAtErrors] + + popf + ret +ENDPROC TMPL_NM_CMN(TestSubErrorCount) + + + +;; +; Equivalent to RTTestValue. +; +; @param ds:ax The value name. +; @param edx The 32-bit value to report. +; @param cl The unit (VMMDEV_TESTING_UNIT_XXX). +; @uses nothing +; +BEGINPROC TMPL_NM_CMN(TestValueU32) + push xBP + mov xBP, xSP + push sDX + push sCX + push sAX + push sSI + pushf + cld + + mov xSI, xAX ; xSI = name + + ; Print it. + mov dx, 48 + call TMPL_NM_CMN(PrintStrSpacesColonSpace) + mov eax, [xBP - sCB] + call TMPL_NM_CMN(PrintU32) + mov al, ' ' + call TMPL_NM_CMN(PrintChr) + movzx sAX, cl ; ASSUMES correct input. + mov edx, eax ; edx = unit + shl xAX, 4 ; * 16 + add xAX, g_aszBs2TestUnitNames + call TMPL_NM_CMN(PrintStr) + mov al, 13 + call TMPL_NM_CMN(PrintChr) + mov al, 10 + call TMPL_NM_CMN(PrintChr) + + ; Report it to the host. + cmp byte [g_fbBs2VMMDevTesting], 0 + je .no_vmmdev + mov ecx, edx ; ecx = unit + + mov dx, VMMDEV_TESTING_IOPORT_CMD + mov eax, VMMDEV_TESTING_CMD_VALUE + out dx, eax + + mov dx, VMMDEV_TESTING_IOPORT_DATA + mov eax, [xBP - sCB] + out dx, eax ; value - low dword + xor eax, eax + out dx, eax ; value - high dword + mov eax, ecx + out dx, eax ; unit +.next_char: + lodsb + out dx, al + test al, al + jnz .next_char + +.no_vmmdev: + popf + pop sSI + pop sAX + pop sCX + pop sDX + leave + ret +ENDPROC TMPL_NM_CMN(TestValueU32) + + +;; +; RTTestValue + DBGFR3RegNmQueryU64. +; +; @param ds:ax The value name and register name separated by a colon. +; @uses nothing +; +BEGINPROC TMPL_NM_CMN(TestValueReg) + push xBP + mov xBP, xSP + push sDX + push sAX + push sSI + pushf + cld + + mov xSI, xAX ; xSI = name + + ; Report it to the host. + cmp byte [g_fbBs2VMMDevTesting], 0 + je .no_vmmdev + + mov dx, VMMDEV_TESTING_IOPORT_CMD + mov eax, VMMDEV_TESTING_CMD_VALUE_REG + out dx, eax + + mov dx, VMMDEV_TESTING_IOPORT_DATA +.next_char: + lodsb + out dx, al + test al, al + jnz .next_char + +.no_vmmdev: + popf + pop sSI + pop sAX + pop sDX + leave + ret +ENDPROC TMPL_NM_CMN(TestValueReg) + + +;; +; Equivalent to RTTestFailed("%s", ds:xAX). +; +; @param ds:xAX Failure explanation. +; @uses nothing +; +BEGINPROC TMPL_NM_CMN(TestFailed) + push xBP + mov xBP, xSP + push sAX + push xDX + + ; Increment the error count. + inc word [g_uscBs2TestErrors] + + ; Print failure message. + call TMPL_NM_CMN(PrintStr) + + ; Report it to the VMMDev. + mov xDX, xAX + mov eax, VMMDEV_TESTING_CMD_FAILED + call TMPL_NM_CMN(testSendStrCmd) + + pop xDX + pop sAX + leave + ret +ENDPROC TMPL_NM_CMN(TestFailed) + + +;; +; Equivalent to RTTestFailed. +; +; @remarks This uses an entirely STACK BASED CALLING CONVENTION where the +; caller does the cleanup (cdecl sans volatile regs). +; +; @param fpszFormat The format string (far pointer on 16-bit +; systems). See StrFormatV for format details. +; @param ... Zero or more format string arguments. +; @uses nothing +; +BEGINPROC TMPL_NM_CMN(TestFailedF) + push xBP + mov xBP, xSP + push sAX + push xDX + push xCX + push xBX +%ifdef TMPL_16BIT + push ds + push es + push fs +%endif + sub xSP, 0400h ; string buffer (1024 bytes) + + ; + ; Format the failure string and call TestFailed. + ; +%ifdef TMPL_16BIT + mov ax, ss ; buffer address. + mov ds, ax + mov ax, sp + mov xDX, 0400h ; buffer size. + les cx, [bp + 4] ; format string + mov bx, ss ; argument list + mov fs, bx + mov bx, bp + add bx, 8 +%else + mov xAX, xSP ; buffer address + mov xDX, 0400h ; buffer size + mov xCX, [xBP + xCB * 2] ; format string + lea xBX, [xBP + xCB * 3] ; argument list. +%endif + call TMPL_NM_CMN(StrFormatV) + call TMPL_NM_CMN(TestFailed) + + add xSP, 0400h +%ifdef TMPL_16BIT + pop fs + pop es + pop ds +%endif + pop xBX + pop xCX + pop xDX + pop sAX + leave + ret +ENDPROC TMPL_NM_CMN(TestFailedF) + + +;; +; Equivalent to RTTestSkipped("%s", ds:xAX). +; +; @param ds:xAX Explanation. +; @uses nothing +; +BEGINPROC TMPL_NM_CMN(TestSkipped) + push xBP + mov xBP, xSP + push sAX + push xDX + + ; Print reason. + call TMPL_NM_CMN(PrintStr) + + ; Report it to the VMMDev. + mov xDX, xAX + mov eax, VMMDEV_TESTING_CMD_SKIPPED + call TMPL_NM_CMN(testSendStrCmd) + + pop xDX + pop sAX + leave + ret +ENDPROC TMPL_NM_CMN(TestSkipped) + + + +;; +; Equivalent to RTTestSubDone. +; +; @uses nothing +; +BEGINPROC TMPL_NM_CMN(TestSubDone) + jmp TMPL_NM_CMN(testSubCleanup) +ENDPROC TMPL_NM_CMN(TestSubDone) + + +;; +; Equivalent to RTTestSummaryAndDestroy, does not return. +; +BEGINPROC TMPL_NM_CMN(TestTerm) + push xBP + mov xBP, xSP + push sAX + push sCX + push xDX + + ; Complete and cleanup any current sub test. + call TMPL_NM_CMN(testSubCleanup) + + ; Print test summary. + mov xAX, [g_npszBs2Test] + call TMPL_NM_CMN(PrintStr) + + mov cx, [g_uscBs2TestErrors] + and ecx, 0ffffh + jnz .failure + + ; success + mov xAX, .s_szSuccess + call TMPL_NM_CMN(PrintStr) + jmp .vmmdev + + ; failure +.failure: + mov xAX, .s_szFailure + call TMPL_NM_CMN(PrintStr) + mov eax, ecx + call TMPL_NM_CMN(PrintU32) + mov xAX, .s_szFailureEnd + call TMPL_NM_CMN(PrintStr) + + ; report to VMMDev +.vmmdev: + cmp byte [g_fbBs2VMMDevTesting], 0 + je .no_vmmdev + + mov dx, VMMDEV_TESTING_IOPORT_CMD + mov eax, VMMDEV_TESTING_CMD_TERM + out dx, eax + + mov dx, VMMDEV_TESTING_IOPORT_DATA + mov eax, ecx + out dx, eax +.no_vmmdev: + + ; Shut down the VM by default. + call TMPL_NM_CMN(Shutdown) + + pop xDX + pop sCX + pop sAX + leave + ret +.s_szSuccess: + db ': SUCCESS', 13, 10, 0 +.s_szFailure: + db ': FAILURE - ', 0 +.s_szFailureEnd: + db ' errors', 13, 10, 0 +ENDPROC TMPL_NM_CMN(TestTerm) + + + + +;; +; Report a result (elapsed time). +; +; @param ds:ax Pointer to the elapsed time. +; @param edx The number of tests executed. +; @param ds:cx The test description. +; +; @users nothing +; +BEGINPROC TMPL_NM_CMN(ReportResult) + push xBP + mov xBP, xSP + push sAX + push sDX + push xCX + +%if 0 + push sDX + push xCX + push sDX + mov edx, [sAX] + push sDX + mov edx, [sAX + 4] + push sDX + push .szDbg + call TMPL_NM_CMN(PrintF) + add xSP, 4 * sCB + xCB + pop sDX + jmp .end_debug +.szDbg: + db 'ReportResult(%RX32.%RX32, %RX32, %s)', 13, 10, 0 +.end_debug: +%endif + + call TMPL_NM_CMN(CalcTestPerSecond) + mov edx, eax + mov xAX, xCX + mov cl, VMMDEV_TESTING_UNIT_INSTRS_PER_SEC + call TMPL_NM_CMN(TestValueU32) + + pop xCX + pop sDX + pop sAX + leave + ret +ENDPROC TMPL_NM_CMN(ReportResult) + + +%ifdef BS2_WITH_TRAPS +;; +; Checks a trap, complains if not the expected one. +; +; @param al The expected trap number. +; @param sDX The expected trap error code. +; @param sCX The expected fault eip. +; @param sBX The expected fault address. +; @returns al=1 and ZF=0 on success. +; @returns al=0 and ZF=1 on failure +; @uses Nothing. +; +BEGINPROC TMPL_NM_CMN(TestCheckTrap) + push xBP + mov xBP, xSP +%define a_u8ExpectedTrapNo byte [xBP - xCB] + push xAX +%define a_uExpectedErr sPRE [xBP - xCB - sCB*1] + push sDX +%define a_uExpectedFaultPC sPRE [xBP - xCB - sCB*2] + push sCX +%define a_uExpectedFaultAddr sPRE [xBP - xCB - sCB*3] + push sBX + + ; Any traps at all? + cmp dword [g_u32cTraps], 0 + jne .trapped + mov xAX, .s_szNoTrap + jmp .failed +.trapped: + + ; Exactly one trap. + cmp dword [g_u32cTraps], 1 + je .one_trap + mov xAX, .s_szToManyTraps + jmp .failed +.one_trap: + + ; The right trap. + cmp byte [g_u8LastTrapNo], al + je .right_trap_no + mov xAX, .s_szWrongTrapNo + jmp .failed +.right_trap_no: + + ; The right error code. + cmp [g_u64LastTrapErr], sDX +%ifndef TMPL_64BIT + jne .bad_err_cd + cmp dword [g_u64LastTrapErr + 4], 0 +%endif + je .right_err_cd +.bad_err_cd: + mov xAX, .s_szWrongErrCd + jmp .failed +.right_err_cd: + + ; The fault PC. + cmp [g_LastTrapRegs + BS2REGS.rip], sCX +%ifndef TMPL_64BIT + jne .bad_pc + cmp dword [g_LastTrapRegs + BS2REGS.rip + 4], 0 +%endif + je .right_pc +.bad_pc: + mov xAX, .s_szWrongPc + jmp .failed +.right_pc: + + + ; The fault address (PF only). + cmp al, 0eh + jne .right_fault_address + cmp [g_LastTrapRegs + BS2REGS.cr2], sBX +%ifndef TMPL_64BIT + jne .bad_fault_address + cmp dword [g_LastTrapRegs + BS2REGS.cr2 + 4], 0 +%endif + je .right_fault_address +.bad_fault_address: + mov xAX, .s_szWrongFaultAddress + jmp .failed +.right_fault_address: + + pop sBX + pop sCX + pop sDX + pop xAX + leave + mov al, 1 + test al, al + ret + + ; + ; Reportfailure + ; +.failed: + mov xDX, xSP ; save xSP - lazy bird. + cmp a_u8ExpectedTrapNo, 0eh + jne .not_pf2 +%ifndef TMPL_64BIT + push dword 0 +%endif + push a_uExpectedFaultAddr +.not_pf1: +%ifndef TMPL_64BIT + push dword 0 +%endif + push a_uExpectedErr +%ifndef TMPL_64BIT + push dword 0 +%endif + push a_uExpectedFaultPC + movzx xBX, a_u8ExpectedTrapNo + push xBX + ; line break + cmp a_u8ExpectedTrapNo, 0eh + jne .not_pf2 +%ifndef TMPL_64BIT + push dword [g_LastTrapRegs + BS2REGS.cr2 + 4] +%endif + push sPRE [g_LastTrapRegs + BS2REGS.cr2] +.not_pf2: +%ifndef TMPL_64BIT + push dword [g_u64LastTrapErr + 4] +%endif + push sPRE [g_u64LastTrapErr] +%ifndef TMPL_64BIT + push dword [g_LastTrapRegs + BS2REGS.rip + 4] +%endif + push sPRE [g_LastTrapRegs + BS2REGS.rip] + movzx xBX, byte [g_u8LastTrapNo] + push xBX + push xAX ; msg + mov xAX, .s_szFailureMsg + cmp a_u8ExpectedTrapNo, 0eh + jne .not_pf3 + mov xAX, .s_szFailurePfMsg +.not_pf3: + push xAX ; format string + call TMPL_NM_CMN(TestFailedF) + mov xSP, xDX ; clean up call frame + +.done: + pop sBX + pop sCX + pop sDX + pop xAX + leave + xor al, al + ret + +.s_szFailureMsg: + db '%s', 13, 10 + db ' got trap %RX8 pc=%RX64 err=%RX64', 13, 10 + db ' expected %RX8 pc=%RX64 err=%RX64', 13, 10, 0 +.s_szFailurePfMsg: + db '%s', 13, 10 + db ' got trap %RX8 pc=%RX64 err=%RX64 cr2=%RX64', 13, 10, + db ' expected %RX8 pc=%RX64 err=%RX64 cr2=%RX64', 13, 10, 0 +.s_szNoTrap: + db 'no traps', 0 +.s_szToManyTraps: + db 'too many traps', 0 +.s_szWrongTrapNo: + db 'wrong trap number', 0 +.s_szWrongErrCd: + db 'wrong error code', 0 +.s_szWrongPc: + db 'wrong xIP', 0 +.s_szWrongFaultAddress: + db 'wrong fault address', 0 +%undef a_u8ExpectedTrapNo +%undef a_u32ExpectedErr +%undef a_u32ExpectedFaultPC +%undef a_u32ExpectedFaultAddr +ENDPROC TMPL_NM_CMN(TestCheckTrap) +%endif ; BS2_WITH_TRAPS + + +%ifdef BS2_WITH_TRAPS +;; +; Installs the active list of trap records (BS2TRAPREC). +; +; @param sAX Flat address of the trap records (BS2TRAPREC). +; Assumes that DS is FLAT. +; @param edx The number of trap records. +; @param sCX Flat image base address, i.e. what BS2TRAPREC.offWhere +; is relative to. +; @returns al=1 and ZF=0 on success. +; @returns al=0 and ZF=1 on failure +; +; @uses sAX (return value) +; +BEGINPROC TMPL_NM_CMN(TestInstallTrapRecs) + push xBP + mov xBP, xSP + push sDX + push sBX + push sCX + push sDI + push sSI + + ; Make sure the record array is within limits. + cmp edx, _4M + jae .nok + + ; Scan the trap records. + mov sDI, sAX + mov esi, edx + or esi, esi + jnz .ok +.next: + cmp dword [sDI + BS2TRAPREC.offWhere], _2G + jae .nok + + cmp dword [sDI + BS2TRAPREC.offResumeAddend], 0 + je .nok + cmp dword [sDI + BS2TRAPREC.offResumeAddend], 0xff + je .nok + + cmp dword [sDI + BS2TRAPREC.u8TrapNo], X86_XCPT_LAST + ja .nok + + ; next. + add sDI, BS2TRAPREC_size + dec esi + jnz .next + + ; Set the global variables. +.ok: + xor esi, esi + mov [g_paTrapRecs + 4], esi + mov [g_paTrapRecs], sAX + mov [g_cTrapRecs], edx + mov [g_iTrapRecLast], esi + mov [g_pTrapRecBase + 4], esi + mov [g_pTrapRecBase], sCX + mov eax, 1 + or eax, eax + +.done: + pop sSI + pop sDI + pop sBX + pop sCX + pop sDX + leave + ret + +.nok: + xor eax, eax + jmp .done +ENDPROC TMPL_NM_CMN(TestInstallTrapRecs) +%endif ; BS2_WITH_TRAPS + + +;; +; Calculate the number of tests executed per second. +; +; @param ds:ax Pointer to the elapsed time. +; @param edx The number of tests executed. +; @returns The tests per second in eax. +; +; @uses eax (return value) +; +BEGINPROC TMPL_NM_CMN(CalcTestPerSecond) + push xBP + mov xBP, xSP + push sDX + push sCX +%ifdef TMPL_16BIT + movzx eax, ax +%endif + + ; Calc NS per test. + mov ecx, edx + cmp ecx, 0 + jz .div_zero + movzx eax, ax + mov edx, [sAX + 4] + cmp edx, ecx + jae .div_overflow + mov eax, [sAX] + shld edx, eax, 10 + shl eax,10 + div ecx ; eax = NS per test + + ; Calc tests per second. + mov ecx, eax + cmp ecx, 0 + jz .div_zero + mov edx, 0xee + mov eax, 0x6b280000 ; 1024G + div ecx ; eax = tests per second + +.done: + pop sCX + pop sDX + leave + ret + +.div_zero: + mov eax, 0 + jmp .done + +.div_overflow: + mov eax, 4242424242 + jmp .done +ENDPROC TMPL_NM_CMN(CalcTestPerSecond) + + +;; +; Calculate the number of iterations for a benchmark based on the time a short +; calibration run too. +; +; @param ds:xAX Pointer to the elapsed time. +; @param edx The number of tests iterations executed. +; @param ecx The desired test length, in seconds. +; @returns The tests iterations in eax. +; +; @uses eax (return value) +; +BEGINPROC TMPL_NM_CMN(CalcBenchmarkIterations) + push xBP + mov xBP, xSP + push sDX + push sCX +%ifdef TMPL_16BIT + movzx eax, ax +%endif + + ; Calc NS per test. + mov ecx, edx + cmp ecx, 0 + jz .div_zero + movzx eax, ax + mov edx, [sAX + 4] + cmp edx, ecx + jae .div_overflow + mov eax, [sAX] + div ecx ; eax = NS per test + + ; Calc tests per second. + mov ecx, eax + cmp ecx, 0 + jz .div_zero + xor edx, edx + mov eax, 1000000000 ; 1G + div ecx ; eax = tests per second + + ; Multiply this up to the desired number of seconds. + mov edx, [xBP - xCB*2] + mul edx + test edx, edx + jnz .mult_32bit_overflow + cmp eax, _64K + jle .too_small + +.done: + pop sCX + pop sDX + leave + ret + +.too_small: + mov eax, _64K + jmp .done + +.mult_32bit_overflow: + mov eax, 0ffff0000h + jmp .done + +.div_zero: + mov eax, [xBP - xCB] + shl eax, 8 + jmp .fudge + +.div_overflow: + mov eax, [xBP - xCB] + shr eax, 4 +.fudge: + add eax, 10 + jmp .done +ENDPROC TMPL_NM_CMN(CalcBenchmarkIterations) + + +;; +; Prints a string on the screen. +; +; @param ds:ax The string to find the length of. +; @returns The string length in ax. +; +; @uses ax (return value) +; +BEGINPROC TMPL_NM_CMN(StrLen) + push xBP + mov xBP, xSP + push xBX + + mov xBX, xAX + dec xBX +.next: + inc xBX + cmp byte [xBX], byte 0 + jnz .next + + xchg xAX, xBX + sub xAX, xBX + + pop xBX + leave + ret +ENDPROC TMPL_NM_CMN(StrLen) + + + +;; +; Simple string format, taking an argument list. +; +; Implemented: +; - %RX8 +; - %RX16 +; - %RX32 +; - %RX64 +; - %s +; +; Planned: +; - %RU8 +; - %RU16 +; - %RU32 +; - %RU64 +; - %RI8 +; - %RI16 +; - %RI32 +; - %RI64 +; +; @param ds:xAX The buffer address. +; @param xDX The buffer size. +; @param es:xCX The format string. +; @param fs:xBX The argument list. +; @uses nothing +; +BEGINPROC TMPL_NM_CMN(StrFormatV) + push xBP + mov xBP, xSP + push sAX + push sDX + push sCX + push sBX + push sDI + push sSI + pushf + cld +%ifdef TMPL_16BIT + push ds + push es + push fs + push gs + + mov si, ds + mov di, es + mov ds, di + mov es, si + mov di, ax ; es:di -> output buffer. + mov si, cx ; ds:si -> format string. +%define a_pArgs [fs:bx] +%define a_pu32ArgsHighDW dword [fs:bx + 4] +%else + mov xDI, xAX ; (es:)xDI -> output buffer. + mov xSI, xCX ; (ds:)xSI -> format string. +%define a_pArgs [xBX] +%define a_pu32ArgsHighDW dword [xBX + 4] +%endif + xchg xCX, xDX ; xCX=buffer size. + + ; + ; Make sure we've got space for a terminator char in the output buffer. + ; + test xCX, xCX + jz .return + dec xCX + jz .done + + ; + ; In this loop we're free to use sDX and (with some caution) sAX. + ; +.format_loop: + lodsb + test al, al + jz .done + cmp al, '%' + je .switch + + ; Emit the character in al if there is room for it. +.emit_al: + test xCX, xCX + jz .done + stosb + dec xCX + jmp .format_loop + + ; Try recognize the format specifier. +.switch: + lodsb + cmp al, 's' + je .switch_case_string + cmp al, 'c' + je .switch_case_char + cmp al, 'R' + jne .switch_default + lodsb + cmp al, 'X' + jne .switch_case_number + cmp al, 'U' + jne .switch_case_number + cmp al, 'I' + jne .switch_case_number + +.switch_default: + test al, al + jz .done + mov al, '!' + jmp .emit_al + + ; parse the number part. +.switch_case_number: + mov ah, al ; ah = {X,U,I} + lodsb + cmp al, '8' + je .switch_case_number_8bit + cmp al, '1' + je .switch_case_number_16bit + cmp al, '3' + je .switch_case_number_32bit + cmp al, '6' + je .switch_case_number_64bit + jmp .switch_default + + + ; + ; Common code for 8-bit, 16-bit and 32-bit. + ; + ; The first part load the value into edx, ah={X,U,I}, + ; al=(max-hex, max-unsigned-dec). + ; +.switch_case_number_8bit: + mov al, (2 << 4) | 2 + movzx edx, byte a_pArgs + add xBX, xCB + cmp ah, 'I' + jne .switch_case_number_common_32bit_hex_or_unsigned + movsx edx, dl + jmp .switch_case_number_common_32bit_signed + +.switch_case_number_16bit: + lodsb + cmp al, '6' + jne .switch_default + mov al, (4 << 4) | 5 + movzx edx, word a_pArgs + add xBX, xCB + cmp ah, 'I' + jne .switch_case_number_common_32bit_hex_or_unsigned + movsx edx, dx + jmp .switch_case_number_common_32bit_signed + +.switch_case_number_32bit: + lodsb + cmp al, '2' + jne .switch_default + mov al, (8 << 4) | 10 + mov edx, dword a_pArgs + add xBX, sCB + cmp ah, 'I' + je .switch_case_number_common_32bit_signed + +.switch_case_number_common_32bit_hex_or_unsigned: + cmp ah, 'X' + jne .switch_case_number_common_32bit_unsigned + shr al, 4 + and xAX, 0fh + cmp xCX, xAX + jb .switch_case_number_bad_buf + call .format_32bit_hex_subroutine + jmp .format_loop + +.switch_case_number_common_32bit_unsigned: + and xAX, 0fh + cmp xCX, xAX + jb .switch_case_number_bad_buf + call .format_32bit_dec_subroutine + jmp .format_loop + +.switch_case_number_common_32bit_signed: + cmp edx, 0 + jge .switch_case_number_common_32bit_unsigned + and xAX, 0fh + inc xAX ; sign char + cmp xCX, xAX + jb .switch_case_number_bad_buf + ; Emit the minus sign, invert the value and join the unsigned formatting. + push xAX + mov al, '-' + stosb + dec xCX + pop xAX + neg edx + call .format_32bit_dec_subroutine + jmp .format_loop + + + ; + ; 64-bit is special, to simplify we treat all formats as hex... + ; +.switch_case_number_64bit: + lodsb + cmp al, '4' + jne .switch_default + cmp ah, 'X' + je .switch_case_number_64bit_hex + cmp ah, 'I' + je .switch_case_number_64bit_signed + jmp .switch_case_number_64bit_unsigned + +.switch_case_number_64bit_hex: + mov eax, dword a_pArgs + mov edx, a_pu32ArgsHighDW + add xBX, 8 + cmp xCX, 8+1+8 + jb .switch_case_number_bad_buf + ; Simple, format it as two 32-bit hex values. + push sAX + mov eax, 8 + call .format_32bit_hex_subroutine + mov al, 27h ; '\'' - how do we escape this with yasm? + stosb + dec xCX + pop sDX + mov eax, 8 + call .format_32bit_hex_subroutine + jmp .format_loop + +.switch_case_number_64bit_unsigned: + cmp xCX, 19 + jb .switch_case_number_bad_buf +.switch_case_number_64bit_unsigned_format_it: + ;; @todo implement me + jmp .switch_case_number_64bit_hex + +.switch_case_number_64bit_signed: + mov eax, dword a_pArgs + mov edx, a_pu32ArgsHighDW + add xBX, 8 + cmp xCX, 20 + jb .switch_case_number_bad_buf + test edx, 080000000h + jz .switch_case_number_64bit_unsigned_format_it + ; Emit the minus sign, invert the value and join the unsigned formatting. + push xAX + mov al, '-' + stosb + dec xCX + pop xAX + neg eax + neg edx + jmp .switch_case_number_64bit_unsigned_format_it + + + ; The remaining buffer is too small to hold the number. +.switch_case_number_bad_buf: + mov al, '^' + jmp .emit_al + + + ; + ; Emit a string. + ; +.switch_case_string: +%ifdef TMPL_16BIT + lgs dx, a_pArgs + add xBX, 4 +%else + mov xDX, a_pArgs + add xBX, xCB +%endif + test xCX, xCX +.switch_case_string_loop: + jz .done +%ifdef TMPL_16BIT + mov al, [gs:edx] +%else + mov al, [xDX] +%endif + test al, al + jz .format_loop + inc xDX + stosb + dec xCX + jmp .switch_case_string_loop + + ; + ; Emit a char. + ; +.switch_case_char: + mov al, byte a_pArgs + add xBX, xCB + jmp .emit_al + + + ; + ; Done, just emit the terminator char. + ; +.done: + xor al, al + stosb + +.return: +%ifdef TMPL_16BIT + pop gs + pop fs + pop es + pop ds +%endif + popf + pop sSI + pop sDI + pop sBX + pop sCX + pop sDX + pop sAX + leave + ret +%undef a_pArgs +%undef a_pu32ArgsHighDW + +;; +; Internal subroutine for formatting a hex number into the buffer. +; @param al The precision (2, 4, 8). +; @param edx The value. +; +; @uses ecx, edi +; +.format_32bit_hex_subroutine: + push xAX + push sDX + push xBX + + ; Rotate edx into position. + mov ebx, 8 + sub bl, al + shl bl, 2 + xchg cl, bl + rol edx, cl + xchg bl, cl + + mov bl, al ; Width counter +.format_32bit_hex_subroutine_next: + rol edx, 4 + mov eax, edx + and eax, 0fh + add sAX, g_achHex + mov al, [cs:sAX] + stosb + dec xCX + dec bl + jnz .format_32bit_hex_subroutine_next + + pop xBX + pop sDX + pop xAX + ret + + +;; +; Internal subroutine for formatting a hex number into the buffer. +; @param al The max precision (2, 5, 10). +; @param edx The value. +; @param xCX Counter register to decrement as characters are emited. +; @param es:xDI Where to write the output, xDI is updated. +; +; @uses xCX, xDI +; +.format_32bit_dec_subroutine: +%if 0 ;; @todo implement this + sub xSP, 20h + ; Format in reverse order into a stack buffer. + + ; Append the stack buffer to the string, reversing it in the process. + + add xSP, 20h +%else + call .format_32bit_hex_subroutine +%endif + ret + +ENDPROC TMPL_NM_CMN(StrFormatV) + + +;; +; Very limited RTStrPrintf version. +; +; @remarks This uses an entirely STACK BASED CALLING CONVENTION where the +; caller does the cleanup (cdecl sans volatile regs). +; +; @param fpszBuf The output buffer. +; @param cbBuf The output buffer size (natural size). +; @param fpszFormat The format string (far pointer in 16-bit). +; See StrFormatV for format. +; @param ... Zero or more format string arguments. +; @uses nothing +; +BEGINPROC TMPL_NM_CMN(StrFormatF) + push xBP + mov xBP, xSP + push xAX + push xDX + push xCX + push xBX +%ifdef TMPL_16BIT + push ds + push es + push fs + + lds xAX, [bp + 04h] + mov dx, [bp + 08h] + les xCX, [bp + 0ah] + mov bx, ss + mov fs, bx + mov bx, bp + add bx, 0eh +%else + mov xAX, [xBP + xCB * 2] + mov xDX, [xBP + xCB * 3] + mov xCX, [xBP + xCB * 4] + lea xBX, [xBP + xCB * 5] +%endif + call TMPL_NM_CMN(StrFormatV) + +%ifdef TMPL_16BIT + pop fs + pop es + pop ds +%endif + pop xBX + pop xCX + pop xDX + pop xAX + leave + ret +ENDPROC TMPL_NM_CMN(StrFormatF) + + +;; +; Dumps the CPU registers. +; +; @uses Nothing. +; +BEGINPROC TMPL_NM_CMN(TestDumpCurrentRegisters) +%ifndef TMPL_64BIT + push xBP + mov xBP, xSP + push eax + pushfd + + push dword [xBP - sCB - 4] ; eflags + mov eax, cr2 + push eax + xor eax, eax + mov eax, gs + push eax + mov eax, fs + push eax + mov eax, es + push eax + mov eax, ds + push eax + mov eax, cs + push eax + + mov eax, cr4 + push eax + mov eax, cr3 + push eax + mov eax, cr0 + push eax + mov eax, ebp ; return EBP + mov xAX, [xBP] + push eax + + mov eax, ebp ; return ESP + add xAX, xCB + push eax + + mov xAX, [xBP + xCB] ; return EIP + %ifdef TMPL_16BIT + movzx eax, ax + %endif + push eax + + push edi + push esi + push edx + push ecx + push ebx + push dword [xBP - sCB] ; eax + + %ifdef TMPL_16BIT + push cs + %endif + push .s_szRegFmt + call TMPL_NM_CMN(PrintF) + + popfd + pop eax + leave + ret + +.s_szRegFmt: + db 'eax=%RX32 ebx=%RX32 ecx=%RX32 edx=%RX32 esi=%RX32 edi=%RX32', 13, 10 + db 'eip=%RX32 esp=%RX32 ebp=%RX32 cr0=%RX32 cr3=%RX32 cr4=%RX32', 13, 10 + db 'cs=%RX16 ds=%RX16 es=%RX16 fs=%RX16 gs=%RX16 ss=%RX16 cr2=%RX32 eflags=%RX32', 13, 10, 0 + +%else ; 64-bit + push .s_szRegFmt + call TMPL_NM_CMN(PrintF) + ret + +.s_szRegFmt: + db 'TestDumpCurrentRegisters not implemented', 13, 10, 0 + +%endif ; 64-bit +ENDPROC TMPL_NM_CMN(TestDumpCurrentRegisters) + + + +;; +; Dumps the CPU registers. +; +; @param ds:xAX Pointer to the register frame to dump. +; @uses Nothing. +; +BEGINPROC TMPL_NM_CMN(TestDumpRegisters) + push xBP + mov xBP, xSP + pushf + push sDX + push sBX + mov xBX, xAX + + cmp byte [xBX + BS2REGS.cBits], 64 + je .dump_64bit_regs + + push -1 ; sanity + mov edx, [xBX + BS2REGS.rflags] + push sDX + mov edx, [xBX + BS2REGS.cr2] + push sDX + xor edx, edx + mov dx, [xBX + BS2REGS.ss] + push xDX + mov dx, [xBX + BS2REGS.gs] + push xDX + mov dx, [xBX + BS2REGS.fs] + push xDX + mov dx, [xBX + BS2REGS.es] + push xDX + mov dx, [xBX + BS2REGS.ds] + push xDX + mov dx, [xBX + BS2REGS.cs] + push xDX + + mov edx, [xBX + BS2REGS.cr4] + push sDX + mov edx, [xBX + BS2REGS.cr3] + push sDX + mov edx, [xBX + BS2REGS.cr0] + push sDX + mov edx, [xBX + BS2REGS.rbp] + push sDX + mov edx, [xBX + BS2REGS.rsp] + push sDX + mov edx, [xBX + BS2REGS.rip] + push sDX + + mov edx, [xBX + BS2REGS.rdi] + push sDX + mov edx, [xBX + BS2REGS.rsi] + push sDX + mov edx, [xBX + BS2REGS.rdx] + push sDX + mov edx, [xBX + BS2REGS.rcx] + push sDX + mov edx, [xBX + BS2REGS.rbx] + push sDX + mov edx, [xBX + BS2REGS.rax] + push sDX + +%ifdef TMPL_16BIT + push cs +%endif + push .s_szReg32Fmt + call TMPL_NM_CMN(PrintF) + jmp .return + +.dump_64bit_regs: +%ifdef TMPL_16BIT + push cs +%endif + push .s_szReg64Fmt + call TMPL_NM_CMN(PrintF) + +.return: + lea xSP, [xBP - sCB*2 - xCB] + pop sBX + pop sDX + popf + leave + ret + +.s_szReg32Fmt: + db 'eax=%RX32 ebx=%RX32 ecx=%RX32 edx=%RX32 esi=%RX32 edi=%RX32', 13, 10 + db 'eip=%RX32 esp=%RX32 ebp=%RX32 cr0=%RX32 cr3=%RX32 cr4=%RX32', 13, 10 + db 'cs=%RX16 ds=%RX16 es=%RX16 fs=%RX16 gs=%RX16 ss=%RX16 cr2=%RX32 eflags=%RX32', 13, 10 + db 0 + +.s_szReg64Fmt: + db 'TestDumpCurrentRegisters not implemented', 13, 10, 0 +ENDPROC TMPL_NM_CMN(TestDumpRegisters) + + +;; +; Saves the CPU registers. +; +; @param ds:xAX Pointer to the register frame to dump. +; @uses Nothing. +; +BEGINPROC TMPL_NM_CMN(TestSaveRegisters) + push xBP + mov xBP, xSP +%ifdef TMPL_16BIT + pushfd +%else + pushf ; - 1*sCB +%endif + push sBX ; - 2*sCB + push sAX ; - 3*sCB + push sDX ; - 4*sCB + + xor edx, edx ; zero register. + mov xBX, xAX ; xBX for addressing, xAX for scratch. + +%ifdef TMPL_64BIT + mov rax, [xSP + sCB*1] + mov [xBX + BS2REGS.rax], rax + mov rax, [xSP + sCB*2] + mov [xBX + BS2REGS.rbx], rax + mov [xBX + BS2REGS.rcx], rcx + mov rax, [xSP] + mov [xBX + BS2REGS.rdx], rax + mov [xBX + BS2REGS.rdi], rdi + mov [xBX + BS2REGS.rsi], rsi + mov rax, [xBP] + mov [xBX + BS2REGS.rbp], rax + lea rax, [xBP + 16] + mov [xBX + BS2REGS.rsp], rax + mov rax, [xBP + 8] + mov [xBX + BS2REGS.rip], rax + mov [xBX + BS2REGS.r8], r8 + mov [xBX + BS2REGS.r9], r9 + mov [xBX + BS2REGS.r10], r10 + mov [xBX + BS2REGS.r11], r11 + mov [xBX + BS2REGS.r12], r12 + mov [xBX + BS2REGS.r13], r13 + mov [xBX + BS2REGS.r14], r14 + mov [xBX + BS2REGS.r15], r15 + mov rax, [xBP - sCB] + mov [xBX + BS2REGS.rflags], rax + mov ax, cs + mov [xBX + BS2REGS.cs], ax + mov ax, ds + mov [xBX + BS2REGS.ds], ax + mov ax, es + mov [xBX + BS2REGS.es], ax + mov ax, fs + mov [xBX + BS2REGS.fs], ax + mov ax, gs + mov [xBX + BS2REGS.gs], ax + mov ax, ss + mov [xBX + BS2REGS.ss], ax + mov byte [xBX + BS2REGS.cBits], 64 + mov [xBX + BS2REGS.pad ], dl + mov [xBX + BS2REGS.pad + 1], dx + mov rax, cr0 + mov [xBX + BS2REGS.cr0], rax + mov rax, cr2 + mov [xBX + BS2REGS.cr2], rax + mov rax, cr3 + mov [xBX + BS2REGS.cr3], rax + mov rax, cr4 + mov [xBX + BS2REGS.cr4], rax + mov rax, cr8 + mov [xBX + BS2REGS.cr8], rax +%else ; !TMPL_64 + mov eax, [xBP - sCB*3] + mov dword [xBX + BS2REGS.rax], eax + mov dword [xBX + BS2REGS.rax + 4], edx + mov eax, [xBP - sCB*2] + mov dword [xBX + BS2REGS.rbx], eax + mov dword [xBX + BS2REGS.rbx + 4], edx + mov dword [xBX + BS2REGS.rcx], ecx + mov dword [xBX + BS2REGS.rcx + 4], edx + mov eax, [xBP - sCB*4] + mov dword [xBX + BS2REGS.rdx], eax + mov dword [xBX + BS2REGS.rdx + 4], edx + mov dword [xBX + BS2REGS.rdi], edi + mov dword [xBX + BS2REGS.rdi + 4], edx + mov dword [xBX + BS2REGS.rsi], esi + mov dword [xBX + BS2REGS.rsi + 4], edx + mov eax, ebp + mov ax, [xBP] + mov dword [xBX + BS2REGS.rbp], eax + mov dword [xBX + BS2REGS.rbp + 4], edx + %ifdef TMPL_16BIT + mov eax, esp + mov ax, bp + sub ax, 4 + %else + lea eax, [ebp + 8] + %endif + mov dword [xBX + BS2REGS.rsp], eax + mov dword [xBX + BS2REGS.rsp + 4], edx + %ifdef TMPL_16BIT + movzx eax, word [xBP + 2] + %else + mov eax, [xBP + 4] + %endif + mov dword [xBX + BS2REGS.rip], eax + mov dword [xBX + BS2REGS.rip + 4], edx + mov dword [xBX + BS2REGS.r8 ], edx + mov dword [xBX + BS2REGS.r8 + 4], edx + mov dword [xBX + BS2REGS.r9 ], edx + mov dword [xBX + BS2REGS.r9 + 4], edx + mov dword [xBX + BS2REGS.r10 ], edx + mov dword [xBX + BS2REGS.r10 + 4], edx + mov dword [xBX + BS2REGS.r11 ], edx + mov dword [xBX + BS2REGS.r11 + 4], edx + mov dword [xBX + BS2REGS.r12 ], edx + mov dword [xBX + BS2REGS.r12 + 4], edx + mov dword [xBX + BS2REGS.r13 ], edx + mov dword [xBX + BS2REGS.r13 + 4], edx + mov dword [xBX + BS2REGS.r14 ], edx + mov dword [xBX + BS2REGS.r14 + 4], edx + mov dword [xBX + BS2REGS.r15 ], edx + mov dword [xBX + BS2REGS.r15 + 4], edx + mov eax, [xBP - sCB] + mov dword [xBX + BS2REGS.rflags], eax + mov dword [xBX + BS2REGS.rflags + 4], edx + mov ax, cs + mov [xBX + BS2REGS.cs], ax + mov ax, ds + mov [xBX + BS2REGS.ds], ax + mov ax, es + mov [xBX + BS2REGS.es], ax + mov ax, fs + mov [xBX + BS2REGS.fs], ax + mov ax, gs + mov [xBX + BS2REGS.gs], ax + mov ax, ss + mov [xBX + BS2REGS.ss], ax + %ifdef TMPL_16BIT + mov byte [xBX + BS2REGS.cBits], 16 + %else + mov byte [xBX + BS2REGS.cBits], 32 + %endif + mov [xBX + BS2REGS.pad ], dl + mov [xBX + BS2REGS.pad + 1], dx + mov eax, cr0 + mov dword [xBX + BS2REGS.cr0], eax + mov dword [xBX + BS2REGS.cr0 + 4], edx + mov eax, cr2 + mov dword [xBX + BS2REGS.cr2], eax + mov dword [xBX + BS2REGS.cr2 + 4], edx + mov eax, cr3 + mov dword [xBX + BS2REGS.cr3], eax + mov dword [xBX + BS2REGS.cr3 + 4], edx + mov eax, cr4 + mov dword [xBX + BS2REGS.cr4], eax + mov dword [xBX + BS2REGS.cr4 + 4], edx + mov dword [xBX + BS2REGS.cr8], edx + mov dword [xBX + BS2REGS.cr8 + 4], edx +%endif ; !TMPL_64 + +.return: + pop sDX + pop sAX + pop sBX +%ifdef TMPL_16BIT + popfd +%else + popf +%endif + leave + ret +ENDPROC TMPL_NM_CMN(TestSaveRegisters) + + +;; +; Restores the CPU registers, except for rsp, rip, cs, ss and ds. +; +; @param ds:xAX Pointer to the register frame to dump. +; @uses All, but RSP, RIP, CS, SS and DS. +; +BEGINPROC TMPL_NM_CMN(TestRestoreRegisters) + push xBP + mov xBP, xSP +%ifdef TMPL_16BIT + pushfd +%else + pushf ; - 1*sCB +%endif + push sBX ; - 2*sCB + push sAX ; - 3*sCB + + mov xBX, xAX ; xBX for addressing, xAX for scratch. + + mov sAX, [xBX + BS2REGS.rax] + mov [xBP - 3*sCB], sAX + mov sAX, [xBX + BS2REGS.rbx] + mov [xBP - 2*sCB], sAX + mov sCX, [xBX + BS2REGS.rcx] + mov sDX, [xBX + BS2REGS.rdx] + mov sDI, [xBX + BS2REGS.rdi] + mov sSI, [xBX + BS2REGS.rsi] + ; skip rsp, rbp or rip. +%ifdef TMPL_64BIT + mov r8, [xBX + BS2REGS.r8] + mov r9, [xBX + BS2REGS.r9] + mov r10, [xBX + BS2REGS.r10] + mov r11, [xBX + BS2REGS.r11] + mov r12, [xBX + BS2REGS.r12] + mov r13, [xBX + BS2REGS.r13] + mov r14, [xBX + BS2REGS.r14] + mov r15, [xBX + BS2REGS.r15] +%endif + mov sAX, [xBX + BS2REGS.rflags] + mov [xBP - sCB], sAX + ; skip cs & ds. + mov ax, [xBX + BS2REGS.es] + mov es, ax + mov ax, [xBX + BS2REGS.fs] + mov fs, ax + mov ax, [xBX + BS2REGS.gs] + mov gs, ax + ; skip ss + mov sAX, [xBX + BS2REGS.cr0] + mov cr0, sAX + mov sAX, [xBX + BS2REGS.cr2] + mov cr2, sAX + mov sAX, [xBX + BS2REGS.cr3] + mov cr3, sAX + mov sAX, [xBX + BS2REGS.cr4] + mov cr4, sAX + +.return: + pop sAX + pop sBX +%ifdef TMPL_16BIT + popfd +%else + popf +%endif + leave + ret +ENDPROC TMPL_NM_CMN(TestRestoreRegisters) + + +;; +; Enables the A20 gate. +; +; @uses Nothing. +; +BEGINPROC TMPL_NM_CMN(Bs2EnableA20) + call TMPL_NM_CMN(Bs2EnableA20ViaPortA) +; call TMPL_NM_CMN(Bs2EnableA20ViaKbd) + ret +ENDPROC TMPL_NM_CMN(Bs2EnableA20) + + +;; +; Disables the A20 gate. +; +; @uses Nothing. +; +BEGINPROC TMPL_NM_CMN(Bs2DisableA20) + ; Must call both because they may be ORed together on real HW. + call TMPL_NM_CMN(Bs2DisableA20ViaKbd) + call TMPL_NM_CMN(Bs2DisableA20ViaPortA) + ret +ENDPROC TMPL_NM_CMN(Bs2DisableA20) + + +;; +; Waits for the keyboard controller to become ready. +; +; @uses Nothing +; +BEGINPROC TMPL_NM_CMN(Bs2KbdWait) + push xAX + +.check_status: + in al, 64h + test al, 1 ; KBD_STAT_OBF + jnz .read_data_and_status + test al, 2 ; KBD_STAT_IBF + jnz .check_status + + pop xAX + ret + +.read_data_and_status: + in al, 60h + jmp .check_status +ENDPROC TMPL_NM_CMN(Bs2KbdWait) + + +;; +; Sends a read command to the keyboard controller and gets the result. +; +; The caller is responsible for making sure the keyboard controller is ready +; for a command (call Bs2KbdWait if unsure). +; +; @param al The read command. +; @returns The value read is returned. +; @uses al (obviously) +; +BEGINPROC TMPL_NM_CMN(Bs2KbdRead) + out 64h, al ; Write the command. + +.check_status: + in al, 64h + test al, 1 ; KBD_STAT_OBF + jz .check_status + + in al, 60h ; Read the data. + ret +ENDPROC TMPL_NM_CMN(Bs2KbdRead) + + +;; +; Sends a write command to the keyboard controller and then sends the data. +; +; The caller is responsible for making sure the keyboard controller is ready +; for a command (call Bs2KbdWait if unsure). +; +; @param al The write command. +; @param ah The data to write. +; @uses Nothing. +; +; @todo Return status? +; +BEGINPROC TMPL_NM_CMN(Bs2KbdWrite) + out 64h, al ; Write the command. + call TMPL_NM_CMN(Bs2KbdWait) + + xchg al, ah + out 60h, al ; Write the data. + call TMPL_NM_CMN(Bs2KbdWait) + xchg al, ah + + ret +ENDPROC TMPL_NM_CMN(Bs2KbdWrite) + + +;; +; Enables the A20 gate via the keyboard controller. +; +; @uses Nothing. +; +BEGINPROC TMPL_NM_CMN(Bs2EnableA20ViaKbd) + push xAX + pushf + cli + + call TMPL_NM_CMN(Bs2KbdWait) + mov al, 0d0h ; KBD_CCMD_READ_OUTPORT + call TMPL_NM_CMN(Bs2KbdRead) + + mov ah, 002h + or ah, al + mov al, 0d1h ; KBD_CCMD_WRITE_OUTPORT + call TMPL_NM_CMN(Bs2KbdWrite) + + mov al, 0ffh ; KBD_CMD_RESET + out 64h, al + call TMPL_NM_CMN(Bs2KbdWait) + + popf + pop xAX + ret +ENDPROC TMPL_NM_CMN(Bs2EnableA20ViaKbd) + + +;; +; Disables the A20 gate via the keyboard controller. +; +; @uses Nothing. +; +BEGINPROC TMPL_NM_CMN(Bs2DisableA20ViaKbd) + push xAX + pushf + cli + + call TMPL_NM_CMN(Bs2KbdWait) + mov al, 0d0h ; KBD_CCMD_READ_OUTPORT + call TMPL_NM_CMN(Bs2KbdRead) + + mov ah, 0fdh ; ~2 + and ah, al + mov al, 0d1h ; KBD_CCMD_WRITE_OUTPORT + call TMPL_NM_CMN(Bs2KbdWrite) + + mov al, 0ffh ; KBD_CMD_RESET + out 64h, al + call TMPL_NM_CMN(Bs2KbdWait) + + popf + pop xAX + ret +ENDPROC TMPL_NM_CMN(Bs2DisableA20ViaKbd) + + +;; +; Enables the A20 gate via control port A (PS/2 style). +; +; @uses Nothing. +; +BEGINPROC TMPL_NM_CMN(Bs2EnableA20ViaPortA) + push xAX + + ; Use Control port A, assuming a PS/2 style system. + in al, 092h + test al, 02h + jnz .done ; avoid trouble writing back the same value. + or al, 2 ; enable the A20 gate. + out 092h, al + +.done: + pop xAX + ret +ENDPROC TMPL_NM_CMN(Bs2EnableA20ViaPortA) + + +;; +; Disables the A20 gate via control port A (PS/2 style). +; +; @uses Nothing. +; +BEGINPROC TMPL_NM_CMN(Bs2DisableA20ViaPortA) + push xAX + + ; Use Control port A, assuming a PS/2 style system. + in al, 092h + test al, 02h + jz .done ; avoid trouble writing back the same value. + and al, 0fdh ; disable the A20 gate. + out 092h, al + +.done: + pop xAX + ret +ENDPROC TMPL_NM_CMN(Bs2DisableA20ViaPortA) + + +;; +; Checks if the no-execution bit is supported. +; +; @returns AL=1 and ZF=0 if supported. +; @returns AL=0 and ZF=1 if not. +; @uses al +; +BEGINPROC TMPL_NM_CMN(Bs2IsNXSupported) + push xBP + mov xBP, xSP + push sBX + push sDX + push sCX + push sAX + + mov eax, 080000000h + cpuid + cmp eax, 080000001h + jb .not_supported + cmp eax, 080001000h + jae .not_supported + + mov eax, 080000001h + cpuid + test edx, X86_CPUID_EXT_FEATURE_EDX_NX + jz .not_supported + + ; supported + pop sAX + mov al, 1 + +.return: + pop sCX + pop sDX + pop sBX + leave + ret +.not_supported: + pop sAX + xor al, al + jmp .return +ENDPROC TMPL_NM_CMN(Bs2IsNXSupported) + + +;; +; Sets EFER.NXE=al if NXE is supported. +; +; @param al 0 if NXE should be disabled, non-zero if it should +; be enabled. +; @uses nothing. +; +BEGINPROC TMPL_NM_CMN(Bs2SetupNX) + push xBP + mov xBP, xSP + push sAX + push sDX + push sCX + + call TMPL_NM_CMN(Bs2IsNXSupported) + jz .done + + mov ecx, MSR_K6_EFER + rdmsr + test byte [xBP - sCB], 0ffh + jz .disable_it + or eax, MSR_K6_EFER_NXE + jmp .set_it +.disable_it: + and eax, ~MSR_K6_EFER_NXE +.set_it: + wrmsr + +.done: + pop sCX + pop sDX + pop sAX + leave + ret +ENDPROC TMPL_NM_CMN(Bs2SetupNX) + + +;; +; Disables NX if supported. +; +; @uses nothing. +; +BEGINPROC TMPL_NM_CMN(Bs2DisableNX) + push xBP + mov xBP, xSP + push xAX + + xor al, al + call TMPL_NM_CMN(Bs2SetupNX) + + pop xAX + leave + ret +ENDPROC TMPL_NM_CMN(Bs2DisableNX) + + +;; +; Enables NX if supported. +; +; @uses nothing. +; +BEGINPROC TMPL_NM_CMN(Bs2EnableNX) + push xBP + mov xBP, xSP + push xAX + + mov al, 1 + call TMPL_NM_CMN(Bs2SetupNX) + + pop xAX + leave + ret +ENDPROC TMPL_NM_CMN(Bs2EnableNX) + + +;; +; Panics if the testing feature of the VMMDev is missing. +; +; A message will be printed. +; +; @uses Nothing. +; +BEGINPROC TMPL_NM_CMN(Bs2PanicIfVMMDevTestingIsMissing) + push xDX + push sAX + + xor eax, eax + mov dx, VMMDEV_TESTING_IOPORT_NOP + in eax, dx + cmp eax, VMMDEV_TESTING_NOP_RET + je .ok + + mov xAX, .s_szMissingVMMDevTesting + call TMPL_NM_CMN(PrintStr) + call Bs2Panic + +.ok: + pop sAX + pop xDX + ret + +.s_szMissingVMMDevTesting: + db 'fatal error: The testing feature of the VMMDevVMMDev is not present!', 13, 10, 0 +ENDPROC TMPL_NM_CMN(Bs2PanicIfVMMDevTestingIsMissing) + + + +%ifdef BS2_WITH_TRAPS + +;; +; Switches to ring-0 from whatever the current mode is. +; +; @uses cs, ss, ds, es, fs, gs +; +BEGINPROC TMPL_NM_CMN(Bs2ToRing0) + push sAX + mov sAX, BS2_SYSCALL_TO_RING0 + int BS2_TRAP_SYSCALL + pop sAX + ret +ENDPROC TMPL_NM_CMN(Bs2ToRing0) + +;; +; Switches to ring-1 from whatever the current mode is. +; +; @uses cs, ss, ds, es, fs, gs +; +BEGINPROC TMPL_NM_CMN(Bs2ToRing1) + push sAX + mov sAX, BS2_SYSCALL_TO_RING1 + int BS2_TRAP_SYSCALL + pop sAX + ret +ENDPROC TMPL_NM_CMN(Bs2ToRing1) + +;; +; Switches to ring-0 from whatever the current mode is. +; +; @uses cs, ss, ds, es, fs, gs +; +BEGINPROC TMPL_NM_CMN(Bs2ToRing2) + push sAX + mov sAX, BS2_SYSCALL_TO_RING2 + int BS2_TRAP_SYSCALL + pop sAX + ret +ENDPROC TMPL_NM_CMN(Bs2ToRing2) + +;; +; Switches to ring-3 from whatever the current mode is. +; +; @uses cs, ss, ds, es, fs, gs +; +BEGINPROC TMPL_NM_CMN(Bs2ToRing3) + push sAX + mov sAX, BS2_SYSCALL_TO_RING3 + int BS2_TRAP_SYSCALL + pop sAX + ret +ENDPROC TMPL_NM_CMN(Bs2ToRing3) + +;; +; Switches the given ring from whatever the current mode is. +; +; @param AL The desired ring. +; @uses cs, ss, ds, es, fs, gs +; +BEGINPROC TMPL_NM_CMN(Bs2ToRingN) + pushf + cmp al, 3 + je .ring3 + cmp al, 2 + je .ring2 + cmp al, 1 + je .ring1 +.ring0: + call TMPL_NM_CMN(Bs2ToRing0) +.done: + popf + ret + +.ring1: + call TMPL_NM_CMN(Bs2ToRing1) + jmp .done +.ring2: + call TMPL_NM_CMN(Bs2ToRing2) + jmp .done +.ring3: + call TMPL_NM_CMN(Bs2ToRing3) + jmp .done +ENDPROC TMPL_NM_CMN(Bs2ToRingN) + +%endif ; BS2_WITH_TRAPS + + + +; +; Wrapper for dynamically calling the right specific method. +; This avoid putting large portions of the code in the 2nd template. +; + +TMPL_NM_CMN(g_pfnPrintStrInternal): TMPL_PTR_DEF 0 +TMPL_NM_CMN(g_pfnPrintChrInternal): TMPL_PTR_DEF 0 + +BEGINPROC TMPL_NM_CMN(PrintStr) + jmp [TMPL_NM_CMN(g_pfnPrintStrInternal)] +ENDPROC TMPL_NM_CMN(PrintStr) + +BEGINPROC TMPL_NM_CMN(PrintChr) + jmp [TMPL_NM_CMN(g_pfnPrintChrInternal)] +ENDPROC TMPL_NM_CMN(PrintChr) + + +%include "bootsector2-template-footer.mac" + |