; $Id: VMMR0A.asm 5999 2007-12-07 15:05:06Z vboxsync $ ;; @file ; VMM - R0 assembly routines. ; ; ; Copyright (C) 2006-2007 innotek GmbH ; ; 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. ; ;******************************************************************************* ;* Header Files * ;******************************************************************************* %include "VBox/asmdefs.mac" %include "VMMInternal.mac" %include "iprt/err.mac" %ifdef RT_ARCH_X86 ; The other architecture(s) use(s) C99 variadict macros. extern IMPNAME(RTLogLogger) %endif BEGINCODE ;; ; The setjmp variant used for calling Ring-3. ; ; This differs from the normal setjmp in that it will resume VMMR0CallHost if we're ; in the middle of a ring-3 call. Another differences is the function pointer and ; argument. This has to do with resuming code and the stack frame of the caller. ; ; @returns VINF_SUCCESS on success or whatever is passed to vmmR0CallHostLongJmp. ; @param pJmpBuf msc:rcx gcc:rdi x86:[esp+4] Our jmp_buf. ; @param pfn msc:rdx gcc:rsi x86:[esp+8] The function to be called when not resuming. ; @param pvUser msc:r8 gcc:rdx x86:[esp+c] The argument of that function. ; BEGINPROC vmmR0CallHostSetJmp GLOBALNAME vmmR0CallHostSetJmpEx %ifdef RT_ARCH_X86 ; ; Save the registers. ; mov edx, [esp + 4h] ; pJmpBuf mov [edx + VMMR0JMPBUF.ebx], ebx mov [edx + VMMR0JMPBUF.esi], esi mov [edx + VMMR0JMPBUF.edi], edi mov [edx + VMMR0JMPBUF.ebp], ebp mov eax, [esp] mov [edx + VMMR0JMPBUF.eip], eax lea ecx, [esp + 4] ; (used in resume) mov [edx + VMMR0JMPBUF.esp], ecx ; ; If we're not in a ring-3 call, call pfn and return. ; test byte [edx + VMMR0JMPBUF.fInRing3Call], 1 jnz .resume mov ecx, [esp + 0ch] ; pvArg mov eax, [esp + 08h] ; pfn sub esp, 12 ; align the stack on a 16-byte boundrary. mov [esp], ecx call eax add esp, 12 mov edx, [esp + 4h] ; pJmpBuf and dword [edx + VMMR0JMPBUF.eip], byte 0 ; used for valid check. ret ; ; Resume VMMR0CallHost the call. ; .resume: ; Sanity checks. cmp ecx, [edx + VMMR0JMPBUF.SpCheck] je .espCheck_ok .bad: and dword [edx + VMMR0JMPBUF.eip], byte 0 ; used for valid check. mov edi, [edx + VMMR0JMPBUF.edi] mov esi, [edx + VMMR0JMPBUF.esi] mov ebx, [edx + VMMR0JMPBUF.ebx] mov eax, VERR_INTERNAL_ERROR ; todo better return code! ret .espCheck_ok: mov ecx, [edx + VMMR0JMPBUF.cbSavedStack] cmp ecx, 8192 ja .bad test ecx, 3 jnz .bad mov edi, [edx + VMMR0JMPBUF.esp] sub edi, [edx + VMMR0JMPBUF.SpResume] cmp ecx, edi jne .bad ; ; Restore the stack. ; mov byte [edx + VMMR0JMPBUF.fInRing3Call], 0 mov ecx, [edx + VMMR0JMPBUF.cbSavedStack] shr ecx, 2 mov esi, [edx + VMMR0JMPBUF.pvSavedStack] mov edi, [edx + VMMR0JMPBUF.SpResume] mov esp, edi rep movsd ; ; Continue where we left off. ; popf pop ebx pop esi pop edi pop ebp xor eax, eax ; VINF_SUCCESS ret %endif ; RT_ARCH_X86 %ifdef RT_ARCH_AMD64 ; ; Save the registers. ; push rbp mov rbp, rsp %ifdef ASM_CALL64_MSC sub rsp, 30h mov r11, rdx ; pfn mov rdx, rcx ; pJmpBuf; %else sub rsp, 10h mov r8, rdx ; pvUser (save it like MSC) mov r11, rsi ; pfn mov rdx, rdi ; pJmpBuf %endif mov [rdx + VMMR0JMPBUF.rbx], rbx %ifdef ASM_CALL64_MSC mov [rdx + VMMR0JMPBUF.rsi], rsi mov [rdx + VMMR0JMPBUF.rdi], rdi %endif mov r10, [rbp] mov [rdx + VMMR0JMPBUF.rbp], r10 mov [rdx + VMMR0JMPBUF.r12], r12 mov [rdx + VMMR0JMPBUF.r13], r13 mov [rdx + VMMR0JMPBUF.r14], r14 mov [rdx + VMMR0JMPBUF.r15], r15 mov rax, [rbp + 8] mov [rdx + VMMR0JMPBUF.rip], rax lea r10, [rbp + 10h] ; (used in resume) mov [rdx + VMMR0JMPBUF.rsp], r10 ; ; If we're not in a ring-3 call, call pfn and return. ; test byte [rdx + VMMR0JMPBUF.fInRing3Call], 1 jnz .resume mov [rbp - 8], rdx ; Save it and fix stack alignment (16). %ifdef ASM_CALL64_MSC mov rcx, r8 ; pvUser -> arg0 %else mov rdi, r8 ; pvUser -> arg0 %endif call r11 mov rdx, [rbp - 8] ; pJmpBuf and qword [rdx + VMMR0JMPBUF.rip], byte 0 ; used for valid check. leave ret ; ; Resume VMMR0CallHost the call. ; .resume: ; Sanity checks. cmp r10, [rdx + VMMR0JMPBUF.SpCheck] je .rspCheck_ok .bad: and qword [rdx + VMMR0JMPBUF.rip], byte 0 ; used for valid check. mov rbx, [rdx + VMMR0JMPBUF.rbx] %ifdef ASM_CALL64_MSC mov rsi, [rdx + VMMR0JMPBUF.rsi] mov rdi, [rdx + VMMR0JMPBUF.rdi] %endif mov r12, [rdx + VMMR0JMPBUF.r12] mov r13, [rdx + VMMR0JMPBUF.r13] mov r14, [rdx + VMMR0JMPBUF.r14] mov r15, [rdx + VMMR0JMPBUF.r15] mov eax, VERR_INTERNAL_ERROR ; todo better return code! leave ret .rspCheck_ok: mov ecx, [rdx + VMMR0JMPBUF.cbSavedStack] cmp rcx, 8192 ja .bad test rcx, 3 jnz .bad mov rdi, [rdx + VMMR0JMPBUF.rsp] sub rdi, [rdx + VMMR0JMPBUF.SpResume] cmp rcx, rdi jne .bad ; ; Restore the stack. ; mov byte [rdx + VMMR0JMPBUF.fInRing3Call], 0 mov ecx, [rdx + VMMR0JMPBUF.cbSavedStack] shr ecx, 3 mov rsi, [rdx + VMMR0JMPBUF.pvSavedStack] mov rdi, [rdx + VMMR0JMPBUF.SpResume] mov rsp, rdi rep movsq ; ; Continue where we left off. ; popf pop rbx %ifdef ASM_CALL64_MSC pop rsi pop rdi %endif pop r12 pop r13 pop r14 pop r15 pop rbp xor eax, eax ; VINF_SUCCESS ret %endif ENDPROC vmmR0CallHostSetJmp ;; ; Worker for VMMR0CallHost. ; This will save the stack and registers. ; ; @param pJmpBuf msc:rcx gcc:rdi x86:[ebp+8] Pointer to the jump buffer. ; @param rc msc:rdx gcc:rsi x86:[ebp+c] The return code. ; BEGINPROC vmmR0CallHostLongJmp %ifdef RT_ARCH_X86 ; ; Save the registers on the stack. ; push ebp mov ebp, esp push edi push esi push ebx pushf ; ; Load parameters. ; mov edx, [ebp + 08h] ; pJmpBuf mov eax, [ebp + 0ch] ; rc ; ; Is the jump buffer armed? ; cmp dword [edx + VMMR0JMPBUF.eip], byte 0 je .nok ; ; Save the stack. ; mov edi, [edx + VMMR0JMPBUF.pvSavedStack] mov [edx + VMMR0JMPBUF.SpResume], esp mov esi, esp mov ecx, [edx + VMMR0JMPBUF.esp] sub ecx, esi ; two sanity checks on the size. cmp ecx, 8192 ; check max size. jbe .ok .nok: mov eax, VERR_INTERNAL_ERROR popf pop ebx pop esi pop edi leave ret .ok: test ecx, 3 ; check alignment jnz .nok mov [edx + VMMR0JMPBUF.cbSavedStack], ecx shr ecx, 2 rep movsd ; store the last pieces of info. mov ecx, [edx + VMMR0JMPBUF.esp] mov [edx + VMMR0JMPBUF.SpCheck], ecx mov byte [edx + VMMR0JMPBUF.fInRing3Call], 1 ; ; Do the long jump. ; mov ebx, [edx + VMMR0JMPBUF.ebx] mov esi, [edx + VMMR0JMPBUF.esi] mov edi, [edx + VMMR0JMPBUF.edi] mov ebp, [edx + VMMR0JMPBUF.ebp] mov ecx, [edx + VMMR0JMPBUF.eip] mov esp, [edx + VMMR0JMPBUF.esp] jmp ecx %endif ; RT_ARCH_X86 %ifdef RT_ARCH_AMD64 ; ; Save the registers on the stack. ; push rbp mov rbp, rsp push r15 push r14 push r13 push r12 %ifdef ASM_CALL64_MSC push rdi push rsi %endif push rbx pushf ; ; Normalize the parameters. ; %ifdef ASM_CALL64_MSC mov eax, edx ; rc mov rdx, rcx ; pJmpBuf %else mov rdx, rdi ; pJmpBuf mov eax, esi ; rc %endif ; ; Is the jump buffer armed? ; cmp qword [rdx + VMMR0JMPBUF.rip], byte 0 je .nok ; ; Save the stack. ; mov rdi, [rdx + VMMR0JMPBUF.pvSavedStack] mov [rdx + VMMR0JMPBUF.SpResume], rsp mov rsi, rsp mov rcx, [rdx + VMMR0JMPBUF.rsp] sub rcx, rsi ; two sanity checks on the size. cmp rcx, 8192 ; check max size. jbe .ok .nok: mov eax, VERR_INTERNAL_ERROR popf pop rbx %ifdef ASM_CALL64_MSC pop rsi pop rdi %endif pop r12 pop r13 pop r14 pop r15 leave ret .ok: test ecx, 7 ; check alignment jnz .nok mov [rdx + VMMR0JMPBUF.cbSavedStack], ecx shr ecx, 3 rep movsq ; store the last pieces of info. mov rcx, [rdx + VMMR0JMPBUF.rsp] mov [rdx + VMMR0JMPBUF.SpCheck], rcx mov byte [rdx + VMMR0JMPBUF.fInRing3Call], 1 ; ; Do the long jump. ; mov rbx, [rdx + VMMR0JMPBUF.rbx] %ifdef ASM_CALL64_MSC mov rsi, [rdx + VMMR0JMPBUF.rsi] mov rdi, [rdx + VMMR0JMPBUF.rdi] %endif mov r12, [rdx + VMMR0JMPBUF.r12] mov r13, [rdx + VMMR0JMPBUF.r13] mov r14, [rdx + VMMR0JMPBUF.r14] mov r15, [rdx + VMMR0JMPBUF.r15] mov rbp, [rdx + VMMR0JMPBUF.rbp] mov rcx, [rdx + VMMR0JMPBUF.rip] mov rsp, [rdx + VMMR0JMPBUF.rsp] jmp rcx %endif ENDPROC vmmR0CallHostLongJmp ;; ; Internal R0 logger worker: Logger wrapper. ; ; @cproto VMMR0DECL(void) vmmR0LoggerWrapper(const char *pszFormat, ...) ; EXPORTEDNAME vmmR0LoggerWrapper %ifdef RT_ARCH_X86 ; The other architecture(s) use(s) C99 variadict macros. push 0 ; assumes we're the wrapper for a default instance. call IMP(RTLogLogger) add esp, byte 4 ret %else int3 int3 int3 ret %endif ENDPROC vmmR0LoggerWrapper