bootsector2-common-routines-template-1.mac revision 2b41f73e305b2cd1a4dadff538705a7a8fb63f02
; $Id$
;; @file
; bootsector2 common routines - template containing code common to related modes.
;
;
; Copyright (C) 2007-2014 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.
;
%include "bootsector2-template-header.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 dx, 08900h
mov ax, ss
mov ds, ax
.retry:
mov ecx, 8
mov esi, .s_szShutdown
rep outsb
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_MAX
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"