diff options
Diffstat (limited to 'src/VBox/VMM/VMMSwitcher/AMD64andLegacy.mac')
-rw-r--r-- | src/VBox/VMM/VMMSwitcher/AMD64andLegacy.mac | 1259 |
1 files changed, 1259 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMSwitcher/AMD64andLegacy.mac b/src/VBox/VMM/VMMSwitcher/AMD64andLegacy.mac new file mode 100644 index 00000000..c7f26a02 --- /dev/null +++ b/src/VBox/VMM/VMMSwitcher/AMD64andLegacy.mac @@ -0,0 +1,1259 @@ +; $Id: AMD64andLegacy.mac $ +;; @file +; VMM - World Switchers, template for AMD64 to PAE and 32-bit. +; + +; +; Copyright (C) 2006-2019 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. +; + +;%define DEBUG_STUFF 1 +;%define STRICT_IF 1 + +;******************************************************************************* +;* Header Files * +;******************************************************************************* +%include "VBox/asmdefs.mac" +%include "VBox/apic.mac" +%include "iprt/x86.mac" +%include "VBox/vmm/cpum.mac" +%include "VBox/vmm/stam.mac" +%include "VBox/vmm/vm.mac" +%include "VBox/err.mac" +%include "CPUMInternal.mac" +%include "VMMSwitcher.mac" + + +; +; Start the fixup records +; We collect the fixups in the .data section as we go along +; It is therefore VITAL that no-one is using the .data section +; for anything else between 'Start' and 'End'. +; +BEGINDATA +GLOBALNAME Fixups + + + +BEGINCODE +GLOBALNAME Start + +BITS 64 + +;; +; The C interface. +; +; @param pVM gcc: rdi msc:rcx The cross context VM structure. +; +BEGINPROC vmmR0ToRawMode +%ifdef DEBUG_STUFF + COM64_S_NEWLINE + COM64_S_CHAR '^' +%endif + ; + ; The ordinary version of the code. + ; + + %ifdef STRICT_IF + pushf + pop rax + test eax, X86_EFL_IF + jz .if_clear_in + mov eax, 0c0ffee00h + ret +.if_clear_in: + %endif + + ; + ; make r9 = pVM and rdx = pCpum. + ; rax, rcx and r8 are scratch here after. + %ifdef RT_OS_WINDOWS + mov r9, rcx + %else + mov r9, rdi + %endif + lea rdx, [r9 + VM.cpum] + + %ifdef VBOX_WITH_STATISTICS + ; + ; Switcher stats. + ; + lea r8, [r9 + VM.StatSwitcherToGC] + STAM64_PROFILE_ADV_START r8 + %endif + + ; + ; Call worker (far return). + ; + mov eax, cs + push rax + call NAME(vmmR0ToRawModeAsm) + + %ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI + ; Unblock Local APIC NMI vectors + ; Do this here to ensure the host CS is already restored + mov r8d, [rdx + CPUM.offCPUMCPU0] + mov ecx, [rdx + r8 + CPUMCPU.fApicDisVectors] + test ecx, ecx + jz gth64_apic_done + cmp byte [rdx + r8 + CPUMCPU.fX2Apic], 1 + je gth64_x2apic + + ; Legacy xAPIC mode: + mov r8, [rdx + r8 + CPUMCPU.pvApicBase] + shr ecx, 1 + jnc gth64_nolint0 + and dword [r8 + APIC_REG_LVT_LINT0], ~APIC_REG_LVT_MASKED +gth64_nolint0: + shr ecx, 1 + jnc gth64_nolint1 + and dword [r8 + APIC_REG_LVT_LINT1], ~APIC_REG_LVT_MASKED +gth64_nolint1: + shr ecx, 1 + jnc gth64_nopc + and dword [r8 + APIC_REG_LVT_PC], ~APIC_REG_LVT_MASKED +gth64_nopc: + shr ecx, 1 + jnc gth64_notherm + and dword [r8 + APIC_REG_LVT_THMR], ~APIC_REG_LVT_MASKED +gth64_notherm: + shr ecx, 1 + jnc gth64_nocmci + and dword [r8 + APIC_REG_LVT_CMCI], ~APIC_REG_LVT_MASKED +gth64_nocmci: + shr ecx, 1 + jnc gth64_noeilvt0 + and dword [r8 + APIC_REG_EILVT0], ~APIC_REG_LVT_MASKED +gth64_noeilvt0: + shr ecx, 1 + jnc gth64_noeilvt1 + and dword [r8 + APIC_REG_EILVT1], ~APIC_REG_LVT_MASKED +gth64_noeilvt1: + shr ecx, 1 + jnc gth64_noeilvt2 + and dword [r8 + APIC_REG_EILVT2], ~APIC_REG_LVT_MASKED +gth64_noeilvt2: + shr ecx, 1 + jnc gth64_noeilvt3 + and dword [r8 + APIC_REG_EILVT3], ~APIC_REG_LVT_MASKED +gth64_noeilvt3: + + jmp gth64_apic_done + + ; x2APIC mode: +gth64_x2apic: + mov r8, rax ; save rax + mov r10, rcx + shr r10d, 1 + jnc gth64_x2_nolint0 + mov ecx, MSR_IA32_X2APIC_START + (APIC_REG_LVT_LINT0 >> 4) + rdmsr + and eax, ~APIC_REG_LVT_MASKED + wrmsr +gth64_x2_nolint0: + shr r10d, 1 + jnc gth64_x2_nolint1 + mov ecx, MSR_IA32_X2APIC_START + (APIC_REG_LVT_LINT1 >> 4) + rdmsr + and eax, ~APIC_REG_LVT_MASKED + wrmsr +gth64_x2_nolint1: + shr r10d, 1 + jnc gth64_x2_nopc + mov ecx, MSR_IA32_X2APIC_START + (APIC_REG_LVT_PC >> 4) + rdmsr + and eax, ~APIC_REG_LVT_MASKED + wrmsr +gth64_x2_nopc: + shr r10d, 1 + jnc gth64_x2_notherm + mov ecx, MSR_IA32_X2APIC_START + (APIC_REG_LVT_THMR >> 4) + rdmsr + and eax, ~APIC_REG_LVT_MASKED + wrmsr +gth64_x2_notherm: + shr r10d, 1 + jnc gth64_x2_nocmci + mov ecx, MSR_IA32_X2APIC_START + (APIC_REG_LVT_CMCI >> 4) + rdmsr + and eax, ~APIC_REG_LVT_MASKED + wrmsr +gth64_x2_nocmci: + mov rax, r8 ; restore rax + +gth64_apic_done: + %endif + + %ifdef VBOX_WITH_STATISTICS + ; + ; Switcher stats. + ; + lea r8, [r9 + VM.StatSwitcherToGC] + STAM64_PROFILE_ADV_STOP r8 + %endif + + ret +ENDPROC vmmR0ToRawMode + + + +; ***************************************************************************** +; vmmR0ToRawModeAsm +; +; Phase one of the switch from host to guest context (host MMU context) +; +; INPUT: +; - edx virtual address of CPUM structure (valid in host context) +; +; USES/DESTROYS: +; - eax, ecx, edx, r8 +; +; ASSUMPTION: +; - current CS and DS selectors are wide open +; +; ***************************************************************************** +ALIGNCODE(16) +BEGINPROC vmmR0ToRawModeAsm + ;; Store the offset from CPUM to CPUMCPU in r8 + mov r8d, [rdx + CPUM.offCPUMCPU0] + + ;; + ;; Save CPU host context + ;; Skip eax, edx and ecx as these are not preserved over calls. + ;; + ; general registers. + ; mov [rdx + r8 + CPUMCPU.Host.rax], rax - scratch + mov [rdx + r8 + CPUMCPU.Host.rbx], rbx + ; mov [rdx + r8 + CPUMCPU.Host.rcx], rcx - scratch + ; mov [rdx + r8 + CPUMCPU.Host.rdx], rdx - scratch + mov [rdx + r8 + CPUMCPU.Host.rdi], rdi + mov [rdx + r8 + CPUMCPU.Host.rsi], rsi + mov [rdx + r8 + CPUMCPU.Host.rsp], rsp + mov [rdx + r8 + CPUMCPU.Host.rbp], rbp + ; mov [rdx + r8 + CPUMCPU.Host.r8 ], r8 - scratch + ; mov [rdx + r8 + CPUMCPU.Host.r9 ], r9 - scratch + mov [rdx + r8 + CPUMCPU.Host.r10], r10 + mov [rdx + r8 + CPUMCPU.Host.r11], r11 + mov [rdx + r8 + CPUMCPU.Host.r12], r12 + mov [rdx + r8 + CPUMCPU.Host.r13], r13 + mov [rdx + r8 + CPUMCPU.Host.r14], r14 + mov [rdx + r8 + CPUMCPU.Host.r15], r15 + ; selectors. + mov [rdx + r8 + CPUMCPU.Host.ds], ds + mov [rdx + r8 + CPUMCPU.Host.es], es + mov [rdx + r8 + CPUMCPU.Host.fs], fs + mov [rdx + r8 + CPUMCPU.Host.gs], gs + mov [rdx + r8 + CPUMCPU.Host.ss], ss + ; MSRs + mov rbx, rdx + mov ecx, MSR_K8_FS_BASE + rdmsr + mov [rbx + r8 + CPUMCPU.Host.FSbase], eax + mov [rbx + r8 + CPUMCPU.Host.FSbase + 4], edx + mov ecx, MSR_K8_GS_BASE + rdmsr + mov [rbx + r8 + CPUMCPU.Host.GSbase], eax + mov [rbx + r8 + CPUMCPU.Host.GSbase + 4], edx + mov ecx, MSR_K6_EFER + rdmsr + mov [rbx + r8 + CPUMCPU.Host.efer], eax + mov [rbx + r8 + CPUMCPU.Host.efer + 4], edx + mov rdx, rbx + ; special registers. + sldt [rdx + r8 + CPUMCPU.Host.ldtr] + sidt [rdx + r8 + CPUMCPU.Host.idtr] + sgdt [rdx + r8 + CPUMCPU.Host.gdtr] + str [rdx + r8 + CPUMCPU.Host.tr] ; yasm BUG, generates sldt. YASMCHECK! + ; flags + pushf + pop qword [rdx + r8 + CPUMCPU.Host.rflags] + +%ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI + ; Block Local APIC NMI vectors + cmp byte [rdx + r8 + CPUMCPU.fX2Apic], 1 + je htg_x2apic + + ; Legacy xAPIC mode. No write completion required when writing to the + ; LVT registers as we have mapped the APIC page non-cacheable and the + ; MMIO is CPU-local. + mov rbx, [rdx + r8 + CPUMCPU.pvApicBase] + or rbx, rbx + jz htg_apic_done + xor edi, edi ; fApicDisVectors + mov eax, [rbx + APIC_REG_LVT_LINT0] + mov ecx, eax + and ecx, (APIC_REG_LVT_MASKED | APIC_REG_LVT_MODE_MASK) + cmp ecx, APIC_REG_LVT_MODE_NMI + jne htg_nolint0 + or edi, 0x01 + or eax, APIC_REG_LVT_MASKED + mov [rbx + APIC_REG_LVT_LINT0], eax +htg_nolint0: + mov eax, [rbx + APIC_REG_LVT_LINT1] + mov ecx, eax + and ecx, (APIC_REG_LVT_MASKED | APIC_REG_LVT_MODE_MASK) + cmp ecx, APIC_REG_LVT_MODE_NMI + jne htg_nolint1 + or edi, 0x02 + or eax, APIC_REG_LVT_MASKED + mov [rbx + APIC_REG_LVT_LINT1], eax +htg_nolint1: + mov eax, [rbx + APIC_REG_LVT_PC] + mov ecx, eax + and ecx, (APIC_REG_LVT_MASKED | APIC_REG_LVT_MODE_MASK) + cmp ecx, APIC_REG_LVT_MODE_NMI + jne htg_nopc + or edi, 0x04 + or eax, APIC_REG_LVT_MASKED + mov [rbx + APIC_REG_LVT_PC], eax +htg_nopc: + mov eax, [rbx + APIC_REG_VERSION] + shr eax, 16 + push rax + cmp al, 5 + jb htg_notherm + je htg_nocmci + mov eax, [rbx + APIC_REG_LVT_CMCI] + mov ecx, eax + and ecx, (APIC_REG_LVT_MASKED | APIC_REG_LVT_MODE_MASK) + cmp ecx, APIC_REG_LVT_MODE_NMI + jne htg_nocmci + or edi, 0x10 + or eax, APIC_REG_LVT_MASKED + mov [rbx + APIC_REG_LVT_CMCI], eax +htg_nocmci: + mov eax, [rbx + APIC_REG_LVT_THMR] + mov ecx, eax + and ecx, (APIC_REG_LVT_MASKED | APIC_REG_LVT_MODE_MASK) + cmp ecx, APIC_REG_LVT_MODE_NMI + jne htg_notherm + or edi, 0x08 + or eax, APIC_REG_LVT_MASKED + mov [rbx + APIC_REG_LVT_THMR], eax +htg_notherm: + pop rax + test ah, ah + jns htg_noeilvt + + ; AMD Extended LVT registers + mov esi, [rbx + 0x400] + shr esi, 16 + and esi, 0xff + jz htg_noeilvt + mov ebp, 0x20 +htg_tsteilvtx: + mov eax, [rbx + APIC_REG_EILVT0] + mov ecx, eax + and ecx, (APIC_REG_LVT_MASKED | APIC_REG_LVT_MODE_MASK) + cmp ecx, APIC_REG_LVT_MODE_NMI + jne htg_noeilvtx + or edi, ebp + or eax, APIC_REG_LVT_MASKED + mov [rbx + APIC_REG_EILVT0], eax +htg_noeilvtx: + add rbx, 0x10 ; clobbers rbx! + shl ebp, 1 + dec esi + jnz htg_tsteilvtx + +htg_noeilvt: + mov [rdx + r8 + CPUMCPU.fApicDisVectors], edi + jmp htg_apic_done + + ; x2APIC mode: +htg_x2apic: + mov r15, rdx ; save rdx + xor edi, edi ; fApicDisVectors + + mov ecx, MSR_IA32_X2APIC_START + (APIC_REG_LVT_LINT0 >> 4) + rdmsr + mov ebx, eax + and ebx, (APIC_REG_LVT_MASKED | APIC_REG_LVT_MODE_MASK) + cmp ebx, APIC_REG_LVT_MODE_NMI + jne htg_x2_nolint0 + or edi, 0x01 + or eax, APIC_REG_LVT_MASKED + wrmsr +htg_x2_nolint0: + mov ecx, MSR_IA32_X2APIC_START + (APIC_REG_LVT_LINT1 >> 4) + rdmsr + mov ebx, eax + and ebx, (APIC_REG_LVT_MASKED | APIC_REG_LVT_MODE_MASK) + cmp ebx, APIC_REG_LVT_MODE_NMI + jne htg_x2_nolint1 + or edi, 0x02 + or eax, APIC_REG_LVT_MASKED + wrmsr +htg_x2_nolint1: + mov ecx, MSR_IA32_X2APIC_START + (APIC_REG_LVT_PC >> 4) + rdmsr + mov ebx, eax + and ebx, (APIC_REG_LVT_MASKED | APIC_REG_LVT_MODE_MASK) + cmp ebx, APIC_REG_LVT_MODE_NMI + jne htg_x2_nopc + or edi, 0x04 + or eax, APIC_REG_LVT_MASKED + wrmsr +htg_x2_nopc: + mov ecx, MSR_IA32_X2APIC_START + (APIC_REG_VERSION >> 4) + rdmsr + shr eax, 16 + cmp al, 5 + jb htg_x2_notherm + je htg_x2_nocmci + mov ecx, MSR_IA32_X2APIC_START + (APIC_REG_LVT_CMCI >> 4) + rdmsr + mov ebx, eax + and ebx, (APIC_REG_LVT_MASKED | APIC_REG_LVT_MODE_MASK) + cmp ebx, APIC_REG_LVT_MODE_NMI + jne htg_x2_nocmci + or edi, 0x10 + or eax, APIC_REG_LVT_MASKED + wrmsr +htg_x2_nocmci: + mov ecx, MSR_IA32_X2APIC_START + (APIC_REG_LVT_THMR >> 4) + rdmsr + mov ebx, eax + and ebx, (APIC_REG_LVT_MASKED | APIC_REG_LVT_MODE_MASK) + cmp ebx, APIC_REG_LVT_MODE_NMI + jne htg_x2_notherm + or edi, 0x08 + or eax, APIC_REG_LVT_MASKED + wrmsr +htg_x2_notherm: + mov rdx, r15 + mov [rdx + r8 + CPUMCPU.fApicDisVectors], edi +htg_apic_done: + +%endif ; VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI + + FIXUP FIX_NO_SYSENTER_JMP, 0, htg_no_sysenter - NAME(Start) ; this will insert a jmp htg_no_sysenter if host doesn't use sysenter. + ; save MSR_IA32_SYSENTER_CS register. + mov rbx, rdx ; save edx + mov ecx, MSR_IA32_SYSENTER_CS + rdmsr ; edx:eax <- MSR[ecx] + mov [rbx + r8 + CPUMCPU.Host.SysEnter.cs], eax + mov [rbx + r8 + CPUMCPU.Host.SysEnter.cs + 4], edx + xor eax, eax ; load 0:0 to cause #GP upon sysenter + xor edx, edx + wrmsr + mov rdx, rbx ; restore edx + jmp short htg_no_sysenter + +ALIGNCODE(16) +htg_no_sysenter: + + ;; handle use flags. + mov esi, [rdx + r8 + CPUMCPU.fUseFlags] ; esi == use flags. + and esi, ~(CPUM_USED_FPU_GUEST | CPUM_USED_FPU_HOST) ; Clear CPUM_USED_* flags. + mov [rdx + r8 + CPUMCPU.fUseFlags], esi + + ; debug registers. + test esi, CPUM_USE_DEBUG_REGS_HYPER | CPUM_USE_DEBUG_REGS_HOST + jnz htg_debug_regs_save +htg_debug_regs_no: + DEBUG_CHAR('a') ; trashes esi + + ; control registers. + mov rax, cr0 + mov [rdx + r8 + CPUMCPU.Host.cr0], rax + ;mov rax, cr2 ; assume host os don't stuff things in cr2. (safe) + ;mov [rdx + r8 + CPUMCPU.Host.cr2], rax + mov rax, cr3 + mov [rdx + r8 + CPUMCPU.Host.cr3], rax + mov rax, cr4 + mov [rdx + r8 + CPUMCPU.Host.cr4], rax + + ;; + ;; Start switching to VMM context. + ;; + + ; + ; Change CR0 and CR4 so we can correctly emulate FPU/MMX/SSE[23] exceptions + ; Also disable WP. (eax==cr4 now) + ; Note! X86_CR4_PSE and X86_CR4_PAE are important if the host thinks so :-) + ; + and rax, X86_CR4_MCE | X86_CR4_PSE | X86_CR4_PAE + mov ecx, [rdx + r8 + CPUMCPU.Guest.cr4] + DEBUG_CHAR('b') ; trashes esi + ;; @todo Switcher cleanup: Determine base CR4 during CPUMR0Init / VMMR3SelectSwitcher putting it + ; in CPUMCPU.Hyper.cr4 (which isn't currently being used). That should + ; simplify this operation a bit (and improve locality of the data). + + ; + ; CR4.AndMask and CR4.OrMask are set in CPUMR3Init based on the presence of + ; FXSAVE and XSAVE support on the host CPU + ; + and ecx, [rdx + CPUM.CR4.AndMask] + or eax, ecx + or eax, [rdx + CPUM.CR4.OrMask] + mov cr4, rax + DEBUG_CHAR('c') ; trashes esi + + mov eax, [rdx + r8 + CPUMCPU.Guest.cr0] + and eax, X86_CR0_EM + or eax, X86_CR0_PE | X86_CR0_PG | X86_CR0_TS | X86_CR0_ET | X86_CR0_NE | X86_CR0_MP + mov cr0, rax + DEBUG_CHAR('0') ; trashes esi + + + ; Load new gdt so we can do far jump to guest code after cr3 reload. + lgdt [rdx + r8 + CPUMCPU.Hyper.gdtr] + DEBUG_CHAR('1') ; trashes esi + + ; Store the hypervisor cr3 for later loading + mov ebp, [rdx + r8 + CPUMCPU.Hyper.cr3] + + ;; + ;; Load Intermediate memory context. + ;; + FIXUP FIX_INTER_AMD64_CR3, 1 + mov eax, 0ffffffffh + mov cr3, rax + DEBUG_CHAR('2') ; trashes esi + + ;; + ;; 1. Switch to compatibility mode, placing ourselves in identity mapped code. + ;; + jmp far [NAME(fpIDEnterTarget) wrt rip] + +; 16:32 Pointer to IDEnterTarget. +NAME(fpIDEnterTarget): + FIXUP FIX_ID_32BIT, 0, NAME(IDEnterTarget) - NAME(Start) +dd 0 + FIXUP FIX_HYPER_CS, 0 +dd 0 + + +;; +; Detour for saving the host DR7 and DR6. +; esi and rdx must be preserved. +htg_debug_regs_save: +DEBUG_S_CHAR('s'); + mov rax, dr7 ; not sure, but if I read the docs right this will trap if GD is set. FIXME!!! + mov [rdx + r8 + CPUMCPU.Host.dr7], rax + mov ecx, X86_DR7_INIT_VAL + cmp eax, ecx + je .htg_debug_regs_dr7_disabled + mov dr7, rcx +.htg_debug_regs_dr7_disabled: + mov rax, dr6 ; just in case we save the state register too. + mov [rdx + r8 + CPUMCPU.Host.dr6], rax + ; save host DR0-3? + test esi, CPUM_USE_DEBUG_REGS_HYPER + jz htg_debug_regs_no +DEBUG_S_CHAR('S'); + mov rax, dr0 + mov [rdx + r8 + CPUMCPU.Host.dr0], rax + mov rbx, dr1 + mov [rdx + r8 + CPUMCPU.Host.dr1], rbx + mov rcx, dr2 + mov [rdx + r8 + CPUMCPU.Host.dr2], rcx + mov rax, dr3 + mov [rdx + r8 + CPUMCPU.Host.dr3], rax + or dword [rdx + r8 + CPUMCPU.fUseFlags], CPUM_USED_DEBUG_REGS_HOST + jmp htg_debug_regs_no + + + ; We're now on identity mapped pages in 32-bit compatibility mode. +BITS 32 +ALIGNCODE(16) +GLOBALNAME IDEnterTarget + DEBUG_CHAR('3') + + ; 2. Deactivate long mode by turning off paging. + mov ebx, cr0 + and ebx, (~X86_CR0_PG) & 0xffffffff ; prevent yasm warning + mov cr0, ebx + DEBUG_CHAR('4') + + ; 3. Load intermediate page table. + FIXUP SWITCHER_FIX_INTER_CR3_GC, 1 + mov edx, 0ffffffffh + mov cr3, edx + + ; 4. Disable long mode. + ; We also use the chance to disable syscall/sysret and fast fxsave/fxrstor. + mov ecx, MSR_K6_EFER + rdmsr + DEBUG_CHAR('5') + and eax, ~(MSR_K6_EFER_LME | MSR_K6_EFER_SCE | MSR_K6_EFER_FFXSR) + wrmsr + DEBUG_CHAR('6') + +%ifndef SWITCHER_TO_PAE + ; 4b. Disable PAE. + mov eax, cr4 + and eax, ~X86_CR4_PAE + mov cr4, eax +%else +%endif + + ; 5. Enable paging. + or ebx, X86_CR0_PG + mov cr0, ebx + jmp short just_a_jump +just_a_jump: + DEBUG_CHAR('7') + + ;; + ;; 6. Jump to guest code mapping of the code and load the Hypervisor CS. + ;; + FIXUP FIX_ID_2_GC_NEAR_REL, 1, NAME(JmpGCTarget) - NAME(Start) + jmp near NAME(JmpGCTarget) + + + ;; + ;; When we arrive at this label we're at the + ;; guest code mapping of the switching code. + ;; +ALIGNCODE(16) +GLOBALNAME JmpGCTarget + DEBUG_CHAR('-') + ; load final cr3 and do far jump to load cs. + mov cr3, ebp ; ebp set above + DEBUG_CHAR('0') + + ;; + ;; We're in VMM MMU context and VMM CS is loaded. + ;; Setup the rest of the VMM state. + ;; + ; Load selectors + DEBUG_CHAR('1') + FIXUP FIX_HYPER_DS, 1 + mov eax, 0ffffh + mov ds, eax + mov es, eax + xor eax, eax + mov gs, eax + mov fs, eax + ; Load pCpum into EDX + FIXUP FIX_GC_CPUMCPU_OFF, 1, 0 + mov edx, 0ffffffffh + ; Activate guest IDT + DEBUG_CHAR('2') + lidt [edx + CPUMCPU.Hyper.idtr] + + ; Setup the stack. + DEBUG_CHAR('3') + mov ax, [edx + CPUMCPU.Hyper.ss.Sel] + mov ss, ax + mov esp, [edx + CPUMCPU.Hyper.esp] + + ; Restore TSS selector; must mark it as not busy before using ltr (!) + DEBUG_S_CHAR('4') + FIXUP FIX_GC_TSS_GDTE_DW2, 2 + and dword [0ffffffffh], ~0200h ; clear busy flag (2nd type2 bit) + DEBUG_S_CHAR('5') + ltr word [edx + CPUMCPU.Hyper.tr.Sel] + DEBUG_S_CHAR('6') + + ; Activate the ldt (now we can safely crash). + lldt [edx + CPUMCPU.Hyper.ldtr.Sel] + DEBUG_S_CHAR('7') + + ;; Use flags. + mov esi, [edx + CPUMCPU.fUseFlags] + + ; debug registers + test esi, CPUM_USE_DEBUG_REGS_HYPER + jnz htg_debug_regs_guest +htg_debug_regs_guest_done: + DEBUG_S_CHAR('9') + + ; General registers (sans edx). + mov eax, [edx + CPUMCPU.Hyper.eax] + mov ebx, [edx + CPUMCPU.Hyper.ebx] + mov ecx, [edx + CPUMCPU.Hyper.ecx] + mov ebp, [edx + CPUMCPU.Hyper.ebp] + mov esi, [edx + CPUMCPU.Hyper.esi] + mov edi, [edx + CPUMCPU.Hyper.edi] + DEBUG_S_CHAR('!') + + ;; + ;; Return to the VMM code which either called the switcher or + ;; the code set up to run by HC. + ;; + push dword [edx + CPUMCPU.Hyper.eflags] + push cs + push dword [edx + CPUMCPU.Hyper.eip] + mov edx, [edx + CPUMCPU.Hyper.edx] ; !! edx is no longer pointing to CPUMCPU here !! + +%ifdef DEBUG_STUFF + COM32_S_PRINT ';eip=' + push eax + mov eax, [esp + 8] + COM32_S_DWORD_REG eax + pop eax + COM32_S_CHAR ';' +%endif +%ifdef VBOX_WITH_STATISTICS + push eax + FIXUP FIX_GC_VM_OFF, 1, VM.StatSwitcherToGC + mov eax, 0ffffffffh + STAM32_PROFILE_ADV_STOP eax + pop eax +%endif + + iret ; Use iret to make debugging and TF/RF work. + +;; +; Detour for saving host DR0-3 and loading hypervisor debug registers. +; esi and edx must be preserved. +htg_debug_regs_guest: + DEBUG_S_CHAR('D') + DEBUG_S_CHAR('R') + DEBUG_S_CHAR('x') + ; load hyper DR0-7 + mov ebx, [edx + CPUMCPU.Hyper.dr] + mov dr0, ebx + mov ecx, [edx + CPUMCPU.Hyper.dr + 8*1] + mov dr1, ecx + mov eax, [edx + CPUMCPU.Hyper.dr + 8*2] + mov dr2, eax + mov ebx, [edx + CPUMCPU.Hyper.dr + 8*3] + mov dr3, ebx + mov ecx, X86_DR6_INIT_VAL + mov dr6, ecx + mov eax, [edx + CPUMCPU.Hyper.dr + 8*7] + mov dr7, eax + or dword [edx + CPUMCPU.fUseFlags], CPUM_USED_DEBUG_REGS_HYPER + jmp htg_debug_regs_guest_done + +ENDPROC vmmR0ToRawModeAsm + + +;; +; Trampoline for doing a call when starting the hyper visor execution. +; +; Push any arguments to the routine. +; Push the argument frame size (cArg * 4). +; Push the call target (_cdecl convention). +; Push the address of this routine. +; +; +ALIGNCODE(16) +BEGINPROC vmmRCCallTrampoline +%ifdef DEBUG_STUFF + COM32_S_CHAR 'c' + COM32_S_CHAR 't' + COM32_S_CHAR '!' +%endif + + ; call routine + pop eax ; call address + pop edi ; argument count. +%ifdef DEBUG_STUFF + COM32_S_PRINT ';eax=' + COM32_S_DWORD_REG eax + COM32_S_CHAR ';' +%endif + call eax ; do call + add esp, edi ; cleanup stack + + ; return to the host context (eax = C returncode). +%ifdef DEBUG_STUFF + COM32_S_CHAR '`' +%endif +.to_host_again: + call NAME(vmmRCToHostAsm) + mov eax, VERR_VMM_SWITCHER_IPE_1 + jmp .to_host_again +ENDPROC vmmRCCallTrampoline + + + +;; +; The C interface. +; +ALIGNCODE(16) +BEGINPROC vmmRCToHost +%ifdef DEBUG_STUFF + push esi + COM_NEWLINE + DEBUG_CHAR('b') + DEBUG_CHAR('a') + DEBUG_CHAR('c') + DEBUG_CHAR('k') + DEBUG_CHAR('!') + COM_NEWLINE + pop esi +%endif + mov eax, [esp + 4] + jmp NAME(vmmRCToHostAsm) +ENDPROC vmmRCToHost + + +;; +; vmmRCToHostAsmNoReturn +; +; This is an entry point used by TRPM when dealing with raw-mode traps, +; i.e. traps in the hypervisor code. This will not return and saves no +; state, because the caller has already saved the state. +; +; @param eax Return code. +; +ALIGNCODE(16) +BEGINPROC vmmRCToHostAsmNoReturn + DEBUG_S_CHAR('%') + +%ifdef VBOX_WITH_STATISTICS + FIXUP FIX_GC_VM_OFF, 1, VM.StatTotalInGC + mov edx, 0ffffffffh + STAM32_PROFILE_ADV_STOP edx + + FIXUP FIX_GC_VM_OFF, 1, VM.StatTotalGCToQemu + mov edx, 0ffffffffh + STAM32_PROFILE_ADV_START edx + + FIXUP FIX_GC_VM_OFF, 1, VM.StatSwitcherToHC + mov edx, 0ffffffffh + STAM32_PROFILE_ADV_START edx +%endif + + FIXUP FIX_GC_CPUMCPU_OFF, 1, 0 + mov edx, 0ffffffffh + + jmp vmmRCToHostAsm_SaveNoGeneralRegs +ENDPROC vmmRCToHostAsmNoReturn + + +;; +; vmmRCToHostAsm +; +; This is an entry point used by TRPM to return to host context when an +; interrupt occured or an guest trap needs handling in host context. It +; is also used by the C interface above. +; +; The hypervisor context is saved and it will return to the caller if +; host context so desires. +; +; @param eax Return code. +; @uses eax, edx, ecx (or it may use them in the future) +; +ALIGNCODE(16) +BEGINPROC vmmRCToHostAsm + DEBUG_S_CHAR('%') + push edx + +%ifdef VBOX_WITH_STATISTICS + FIXUP FIX_GC_VM_OFF, 1, VM.StatTotalInGC + mov edx, 0ffffffffh + STAM32_PROFILE_ADV_STOP edx + + FIXUP FIX_GC_VM_OFF, 1, VM.StatTotalGCToQemu + mov edx, 0ffffffffh + STAM32_PROFILE_ADV_START edx + + FIXUP FIX_GC_VM_OFF, 1, VM.StatSwitcherToHC + mov edx, 0ffffffffh + STAM32_PROFILE_ADV_START edx +%endif + + ; + ; Load the CPUM pointer. + ; + FIXUP FIX_GC_CPUMCPU_OFF, 1, 0 + mov edx, 0ffffffffh + + ; Save register context. + pop dword [edx + CPUMCPU.Hyper.edx] + pop dword [edx + CPUMCPU.Hyper.eip] ; call return from stack + mov dword [edx + CPUMCPU.Hyper.esp], esp + mov dword [edx + CPUMCPU.Hyper.eax], eax + mov dword [edx + CPUMCPU.Hyper.ebx], ebx + mov dword [edx + CPUMCPU.Hyper.ecx], ecx + mov dword [edx + CPUMCPU.Hyper.esi], esi + mov dword [edx + CPUMCPU.Hyper.edi], edi + mov dword [edx + CPUMCPU.Hyper.ebp], ebp + + ; special registers which may change. +vmmRCToHostAsm_SaveNoGeneralRegs: +%ifdef STRICT_IF + pushf + pop ecx + test ecx, X86_EFL_IF + jz .if_clear_out + mov eax, 0c0ffee01h + cli +.if_clear_out: +%endif + mov edi, eax ; save return code in EDI (careful with COM_DWORD_REG from here on!) + + ; str [edx + CPUMCPU.Hyper.tr] - double fault only, and it won't be right then either. + sldt [edx + CPUMCPU.Hyper.ldtr.Sel] + + ; No need to save CRx here. They are set dynamically according to Guest/Host requirements. + ; FPU context is saved before restore of host saving (another) branch. + + ; Disable debug registers if active so they cannot trigger while switching. + test dword [edx + CPUMCPU.fUseFlags], CPUM_USED_DEBUG_REGS_HYPER + jz .gth_disabled_dr7 + mov eax, X86_DR7_INIT_VAL + mov dr7, eax +.gth_disabled_dr7: + + ;; + ;; Load Intermediate memory context. + ;; + FIXUP SWITCHER_FIX_INTER_CR3_GC, 1 + mov eax, 0ffffffffh + mov cr3, eax + DEBUG_CHAR('?') + + ;; We're now in intermediate memory context! + + ;; + ;; 0. Jump to identity mapped location + ;; + FIXUP FIX_GC_2_ID_NEAR_REL, 1, NAME(IDExitTarget) - NAME(Start) + jmp near NAME(IDExitTarget) + + ; We're now on identity mapped pages! +ALIGNCODE(16) +GLOBALNAME IDExitTarget + DEBUG_CHAR('1') + + ; 1. Disable paging. + mov ebx, cr0 + and ebx, (~X86_CR0_PG) & 0xffffffff ; prevent yasm warning + mov cr0, ebx + DEBUG_CHAR('2') + + ; 2. Enable PAE. +%ifdef SWITCHER_TO_PAE + ; - already enabled +%else + mov ecx, cr4 + or ecx, X86_CR4_PAE + mov cr4, ecx +%endif + + ; 3. Load long mode intermediate CR3. + FIXUP FIX_INTER_AMD64_CR3, 1 + mov ecx, 0ffffffffh + mov cr3, ecx + DEBUG_CHAR('3') + + ; 4. Enable long mode. + mov ebp, edx + mov ecx, MSR_K6_EFER + rdmsr + or eax, MSR_K6_EFER_LME + wrmsr + mov edx, ebp + DEBUG_CHAR('4') + + ; 5. Enable paging. + or ebx, X86_CR0_PG + mov cr0, ebx + DEBUG_CHAR('5') + + ; Jump from compatibility mode to 64-bit mode. + FIXUP FIX_ID_FAR32_TO_64BIT_MODE, 1, NAME(IDExit64Mode) - NAME(Start) + jmp 0ffffh:0fffffffeh + + ; + ; We're in 64-bit mode (ds, ss, es, fs, gs are all bogus). + ; Move on to the HC mapping. + ; +BITS 64 +ALIGNCODE(16) +NAME(IDExit64Mode): + DEBUG_CHAR('6') + jmp [NAME(pHCExitTarget) wrt rip] + +; 64-bit jump target +NAME(pHCExitTarget): +FIXUP FIX_HC_64BIT, 0, NAME(HCExitTarget) - NAME(Start) +dq 0ffffffffffffffffh + +; 64-bit pCpum address. +NAME(pCpumHC): +FIXUP FIX_HC_64BIT_CPUM, 0 +dq 0ffffffffffffffffh + + ; + ; When we arrive here we're at the host context + ; mapping of the switcher code. + ; +ALIGNCODE(16) +GLOBALNAME HCExitTarget + DEBUG_CHAR('9') + + ; Clear high dword of the CPUMCPU pointer + and rdx, 0ffffffffh + + ; load final cr3 + mov rsi, [rdx + CPUMCPU.Host.cr3] + mov cr3, rsi + DEBUG_CHAR('@') + + ;; + ;; Restore Host context. + ;; + ; Load CPUM pointer into edx + mov rdx, [NAME(pCpumHC) wrt rip] + ; Load the CPUMCPU offset. + mov r8d, [rdx + CPUM.offCPUMCPU0] + + ; activate host gdt and idt + lgdt [rdx + r8 + CPUMCPU.Host.gdtr] + DEBUG_CHAR('0') + lidt [rdx + r8 + CPUMCPU.Host.idtr] + DEBUG_CHAR('1') + ; Restore TSS selector; must mark it as not busy before using ltr (!) +%if 1 ; ASSUME that this is supposed to be 'BUSY'. (saves 20-30 ticks on the T42p) + movzx eax, word [rdx + r8 + CPUMCPU.Host.tr] ; eax <- TR + and al, 0F8h ; mask away TI and RPL bits, get descriptor offset. + add rax, [rdx + r8 + CPUMCPU.Host.gdtr + 2] ; eax <- GDTR.address + descriptor offset. + and dword [rax + 4], ~0200h ; clear busy flag (2nd type2 bit) + ltr word [rdx + r8 + CPUMCPU.Host.tr] +%else + movzx eax, word [rdx + r8 + CPUMCPU.Host.tr] ; eax <- TR + and al, 0F8h ; mask away TI and RPL bits, get descriptor offset. + add rax, [rdx + r8 + CPUMCPU.Host.gdtr + 2] ; eax <- GDTR.address + descriptor offset. + mov ecx, [rax + 4] ; ecx <- 2nd descriptor dword + mov ebx, ecx ; save original value + and ecx, ~0200h ; clear busy flag (2nd type2 bit) + mov [rax + 4], ccx ; not using xchg here is paranoia.. + ltr word [rdx + r8 + CPUMCPU.Host.tr] + xchg [rax + 4], ebx ; using xchg is paranoia too... +%endif + ; activate ldt + DEBUG_CHAR('2') + lldt [rdx + r8 + CPUMCPU.Host.ldtr] + ; Restore segment registers + mov eax, [rdx + r8 + CPUMCPU.Host.ds] + mov ds, eax + mov eax, [rdx + r8 + CPUMCPU.Host.es] + mov es, eax + mov eax, [rdx + r8 + CPUMCPU.Host.fs] + mov fs, eax + mov eax, [rdx + r8 + CPUMCPU.Host.gs] + mov gs, eax + ; restore stack + mov eax, [rdx + r8 + CPUMCPU.Host.ss] + mov ss, eax + mov rsp, [rdx + r8 + CPUMCPU.Host.rsp] + + FIXUP FIX_NO_SYSENTER_JMP, 0, gth_sysenter_no - NAME(Start) ; this will insert a jmp gth_sysenter_no if host doesn't use sysenter. + ; restore MSR_IA32_SYSENTER_CS register. + mov rbx, rdx ; save edx + mov ecx, MSR_IA32_SYSENTER_CS + mov eax, [rbx + r8 + CPUMCPU.Host.SysEnter.cs] + mov edx, [rbx + r8 + CPUMCPU.Host.SysEnter.cs + 4] + wrmsr ; MSR[ecx] <- edx:eax + mov rdx, rbx ; restore edx + jmp short gth_sysenter_no + +ALIGNCODE(16) +gth_sysenter_no: + + ;; @todo AMD syscall + + ; Restore FPU if guest has used it. + ; Using fxrstor should ensure that we're not causing unwanted exception on the host. + mov esi, [rdx + r8 + CPUMCPU.fUseFlags] ; esi == use flags. + test esi, (CPUM_USED_FPU_GUEST | CPUM_USED_FPU_HOST) + jz gth_fpu_no + mov rcx, cr0 + and rcx, ~(X86_CR0_TS | X86_CR0_EM) + mov cr0, rcx + + mov r10, rdx ; Save rdx. + + test esi, CPUM_USED_FPU_GUEST + jz gth_fpu_host + + mov eax, [r10 + r8 + CPUMCPU.Guest.fXStateMask] + mov r9, [r10 + r8 + CPUMCPU.Guest.pXStateR0] + or eax, eax + jz gth_fpu_guest_fxsave + mov edx, [r10 + r8 + CPUMCPU.Guest.fXStateMask + 4] + xsave [r9] + jmp gth_fpu_host +gth_fpu_guest_fxsave: + fxsave [r9] + +gth_fpu_host: + mov eax, [r10 + r8 + CPUMCPU.Host.fXStateMask] + mov r9, [r10 + r8 + CPUMCPU.Host.pXStateR0] + or eax, eax + jz gth_fpu_host_fxrstor + mov edx, [r10 + r8 + CPUMCPU.Host.fXStateMask + 4] + xrstor [r9] ; We saved 32-bit state, so only restore 32-bit. + jmp gth_fpu_done +gth_fpu_host_fxrstor: + fxrstor [r9] ; We saved 32-bit state, so only restore 32-bit. + +gth_fpu_done: + mov rdx, r10 ; Restore rdx. + jmp gth_fpu_no + +ALIGNCODE(16) +gth_fpu_no: + + ; Control registers. + ; Would've liked to have these higher up in case of crashes, but + ; the fpu stuff must be done before we restore cr0. + mov rcx, [rdx + r8 + CPUMCPU.Host.cr4] + test rcx, X86_CR4_PCIDE + jz gth_no_pcide + mov rax, [rdx + r8 + CPUMCPU.Host.cr3] + and rax, ~0xfff ; clear the PCID in cr3 + mov cr3, rax + mov cr4, rcx + mov rax, [rdx + r8 + CPUMCPU.Host.cr3] + mov cr3, rax ; reload it with the right PCID. + jmp gth_restored_cr4 +gth_no_pcide: + mov cr4, rcx +gth_restored_cr4: + mov rcx, [rdx + r8 + CPUMCPU.Host.cr0] + mov cr0, rcx + ;mov rcx, [rdx + r8 + CPUMCPU.Host.cr2] ; assumes this is waste of time. + ;mov cr2, rcx + + ; Restore MSRs + mov rbx, rdx + mov ecx, MSR_K8_FS_BASE + mov eax, [rbx + r8 + CPUMCPU.Host.FSbase] + mov edx, [rbx + r8 + CPUMCPU.Host.FSbase + 4] + wrmsr + mov ecx, MSR_K8_GS_BASE + mov eax, [rbx + r8 + CPUMCPU.Host.GSbase] + mov edx, [rbx + r8 + CPUMCPU.Host.GSbase + 4] + wrmsr + mov ecx, MSR_K6_EFER + mov eax, [rbx + r8 + CPUMCPU.Host.efer] + mov edx, [rbx + r8 + CPUMCPU.Host.efer + 4] + wrmsr + mov rdx, rbx + + ; Restore debug registers (if modified). (ESI must still be fUseFlags! Must be done late, at least after CR4!) + test esi, CPUM_USE_DEBUG_REGS_HOST | CPUM_USED_DEBUG_REGS_HOST | CPUM_USE_DEBUG_REGS_HYPER + jnz gth_debug_regs_restore +gth_debug_regs_done: + and dword [rdx + r8 + CPUMCPU.fUseFlags], ~(CPUM_USED_DEBUG_REGS_HOST | CPUM_USED_DEBUG_REGS_HYPER) + + ; Restore general registers. + mov eax, edi ; restore return code. eax = return code !! + ; mov rax, [rdx + r8 + CPUMCPU.Host.rax] - scratch + return code + mov rbx, [rdx + r8 + CPUMCPU.Host.rbx] + ; mov rcx, [rdx + r8 + CPUMCPU.Host.rcx] - scratch + ; mov rdx, [rdx + r8 + CPUMCPU.Host.rdx] - scratch + mov rdi, [rdx + r8 + CPUMCPU.Host.rdi] + mov rsi, [rdx + r8 + CPUMCPU.Host.rsi] + mov rsp, [rdx + r8 + CPUMCPU.Host.rsp] + mov rbp, [rdx + r8 + CPUMCPU.Host.rbp] + ; mov r8, [rdx + r8 + CPUMCPU.Host.r8 ] - scratch + ; mov r9, [rdx + r8 + CPUMCPU.Host.r9 ] - scratch + mov r10, [rdx + r8 + CPUMCPU.Host.r10] + mov r11, [rdx + r8 + CPUMCPU.Host.r11] + mov r12, [rdx + r8 + CPUMCPU.Host.r12] + mov r13, [rdx + r8 + CPUMCPU.Host.r13] + mov r14, [rdx + r8 + CPUMCPU.Host.r14] + mov r15, [rdx + r8 + CPUMCPU.Host.r15] + + ; finally restore flags. (probably not required) + push qword [rdx + r8 + CPUMCPU.Host.rflags] + popf + + +%ifdef DEBUG_STUFF + COM64_S_CHAR '4' +%endif + db 048h + retf + +;; +; Detour for restoring the host debug registers. +; edx and edi must be preserved. +gth_debug_regs_restore: + DEBUG_S_CHAR('d') + mov rax, dr7 ; Some DR7 paranoia first... + mov ecx, X86_DR7_INIT_VAL + cmp rax, rcx + je .gth_debug_skip_dr7_disabling + mov dr7, rcx +.gth_debug_skip_dr7_disabling: + test esi, CPUM_USED_DEBUG_REGS_HOST + jz .gth_debug_regs_dr7 + + DEBUG_S_CHAR('r') + mov rax, [rdx + r8 + CPUMCPU.Host.dr0] + mov dr0, rax + mov rbx, [rdx + r8 + CPUMCPU.Host.dr1] + mov dr1, rbx + mov rcx, [rdx + r8 + CPUMCPU.Host.dr2] + mov dr2, rcx + mov rax, [rdx + r8 + CPUMCPU.Host.dr3] + mov dr3, rax +.gth_debug_regs_dr7: + mov rbx, [rdx + r8 + CPUMCPU.Host.dr6] + mov dr6, rbx + mov rcx, [rdx + r8 + CPUMCPU.Host.dr7] + mov dr7, rcx + + ; We clear the USED flags in the main code path. + jmp gth_debug_regs_done + +ENDPROC vmmRCToHostAsm + + +GLOBALNAME End +; +; The description string (in the text section). +; +NAME(Description): + db SWITCHER_DESCRIPTION + db 0 + +extern NAME(Relocate) + +; +; End the fixup records. +; +BEGINDATA + db FIX_THE_END ; final entry. +GLOBALNAME FixupsEnd + +;; +; The switcher definition structure. +ALIGNDATA(16) +GLOBALNAME Def + istruc VMMSWITCHERDEF + at VMMSWITCHERDEF.pvCode, RTCCPTR_DEF NAME(Start) + at VMMSWITCHERDEF.pvFixups, RTCCPTR_DEF NAME(Fixups) + at VMMSWITCHERDEF.pszDesc, RTCCPTR_DEF NAME(Description) + at VMMSWITCHERDEF.pfnRelocate, RTCCPTR_DEF NAME(Relocate) + at VMMSWITCHERDEF.enmType, dd SWITCHER_TYPE + at VMMSWITCHERDEF.cbCode, dd NAME(End) - NAME(Start) + at VMMSWITCHERDEF.offR0ToRawMode, dd NAME(vmmR0ToRawMode) - NAME(Start) + at VMMSWITCHERDEF.offRCToHost, dd NAME(vmmRCToHost) - NAME(Start) + at VMMSWITCHERDEF.offRCCallTrampoline, dd NAME(vmmRCCallTrampoline) - NAME(Start) + at VMMSWITCHERDEF.offRCToHostAsm, dd NAME(vmmRCToHostAsm) - NAME(Start) + at VMMSWITCHERDEF.offRCToHostAsmNoReturn, dd NAME(vmmRCToHostAsmNoReturn) - NAME(Start) + ; disasm help + at VMMSWITCHERDEF.offHCCode0, dd 0 + at VMMSWITCHERDEF.cbHCCode0, dd NAME(IDEnterTarget) - NAME(Start) + at VMMSWITCHERDEF.offHCCode1, dd NAME(HCExitTarget) - NAME(Start) + at VMMSWITCHERDEF.cbHCCode1, dd NAME(End) - NAME(HCExitTarget) + at VMMSWITCHERDEF.offIDCode0, dd NAME(IDEnterTarget) - NAME(Start) + at VMMSWITCHERDEF.cbIDCode0, dd NAME(JmpGCTarget) - NAME(IDEnterTarget) + at VMMSWITCHERDEF.offIDCode1, dd NAME(IDExitTarget) - NAME(Start) + at VMMSWITCHERDEF.cbIDCode1, dd NAME(HCExitTarget) - NAME(IDExitTarget) + at VMMSWITCHERDEF.offGCCode, dd NAME(JmpGCTarget) - NAME(Start) + at VMMSWITCHERDEF.cbGCCode, dd NAME(IDExitTarget) - NAME(JmpGCTarget) + + iend + |