VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMSwitcher/AMD64andLegacy.mac@ 33223

Last change on this file since 33223 was 30164, checked in by vboxsync, 15 years ago

CPUM: Added /CPUM/PortableCpuIdLevel={0..3} for automatically stripping CPUID features that can cause trouble with teleportation and cold migration.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 34.1 KB
Line 
1; $Id: AMD64andLegacy.mac 30164 2010-06-11 14:16:09Z vboxsync $
2;; @file
3; VMM - World Switchers, template for AMD64 to PAE and 32-bit.
4;
5
6;
7; Copyright (C) 2006-2007 Oracle Corporation
8;
9; This file is part of VirtualBox Open Source Edition (OSE), as
10; available from http://www.virtualbox.org. This file is free software;
11; you can redistribute it and/or modify it under the terms of the GNU
12; General Public License (GPL) as published by the Free Software
13; Foundation, in version 2 as it comes in the "COPYING" file of the
14; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16;
17
18;%define DEBUG_STUFF 1
19;%define STRICT_IF 1
20
21;*******************************************************************************
22;* Header Files *
23;*******************************************************************************
24%include "VBox/asmdefs.mac"
25%include "VBox/x86.mac"
26%include "VBox/cpum.mac"
27%include "VBox/stam.mac"
28%include "VBox/vm.mac"
29%include "CPUMInternal.mac"
30%include "VMMSwitcher/VMMSwitcher.mac"
31
32
33;
34; Start the fixup records
35; We collect the fixups in the .data section as we go along
36; It is therefore VITAL that no-one is using the .data section
37; for anything else between 'Start' and 'End'.
38;
39BEGINDATA
40GLOBALNAME Fixups
41
42
43
44BEGINCODE
45GLOBALNAME Start
46
47%ifndef VBOX_WITH_HYBRID_32BIT_KERNEL
48BITS 64
49
50;;
51; The C interface.
52;
53; @param pVM GCC: rdi MSC:rcx The VM handle.
54;
55BEGINPROC vmmR0HostToGuest
56%ifdef DEBUG_STUFF
57 COM64_S_NEWLINE
58 COM64_S_CHAR '^'
59%endif
60 ;
61 ; The ordinary version of the code.
62 ;
63
64 %ifdef STRICT_IF
65 pushf
66 pop rax
67 test eax, X86_EFL_IF
68 jz .if_clear_in
69 mov eax, 0c0ffee00h
70 ret
71.if_clear_in:
72 %endif
73
74 ;
75 ; make r9 = pVM and rdx = pCpum.
76 ; rax, rcx and r8 are scratch here after.
77 %ifdef RT_OS_WINDOWS
78 mov r9, rcx
79 %else
80 mov r9, rdi
81 %endif
82 lea rdx, [r9 + VM.cpum]
83
84 %ifdef VBOX_WITH_STATISTICS
85 ;
86 ; Switcher stats.
87 ;
88 lea r8, [r9 + VM.StatSwitcherToGC]
89 STAM64_PROFILE_ADV_START r8
90 %endif
91
92 ;
93 ; Call worker (far return).
94 ;
95 mov eax, cs
96 push rax
97 call NAME(vmmR0HostToGuestAsm)
98
99 %ifdef VBOX_WITH_STATISTICS
100 ;
101 ; Switcher stats.
102 ;
103 lea r8, [r9 + VM.StatSwitcherToGC]
104 STAM64_PROFILE_ADV_STOP r8
105 %endif
106
107 ret
108ENDPROC vmmR0HostToGuest
109
110
111%else ; VBOX_WITH_HYBRID_32BIT_KERNEL
112
113
114BITS 32
115
116;;
117; The C interface.
118;
119BEGINPROC vmmR0HostToGuest
120%ifdef DEBUG_STUFF
121 COM32_S_NEWLINE
122 COM32_S_CHAR '^'
123%endif
124
125 %ifdef VBOX_WITH_STATISTICS
126 ;
127 ; Switcher stats.
128 ;
129 FIXUP FIX_HC_VM_OFF, 1, VM.StatSwitcherToGC
130 mov edx, 0ffffffffh
131 STAM_PROFILE_ADV_START edx
132 %endif
133
134 ; Thunk to/from 64 bit when invoking the worker routine.
135 ;
136 FIXUP FIX_HC_VM_OFF, 1, VM.cpum
137 mov edx, 0ffffffffh
138
139 push 0
140 push cs
141 push 0
142 FIXUP FIX_HC_32BIT, 1, .vmmR0HostToGuestReturn - NAME(Start)
143 push 0ffffffffh
144
145 FIXUP FIX_HC_64BIT_CS, 1
146 push 0ffffh
147 FIXUP FIX_HC_32BIT, 1, NAME(vmmR0HostToGuestAsm) - NAME(Start)
148 push 0ffffffffh
149 retf
150.vmmR0HostToGuestReturn:
151
152 ;
153 ; This selector reloading is probably not necessary, but we do it anyway to be quite sure
154 ; the CPU has the right idea about the selectors.
155 ;
156 mov edx, ds
157 mov ds, edx
158 mov ecx, es
159 mov es, ecx
160 mov edx, ss
161 mov ss, edx
162
163 %ifdef VBOX_WITH_STATISTICS
164 ;
165 ; Switcher stats.
166 ;
167 FIXUP FIX_HC_VM_OFF, 1, VM.StatSwitcherToHC
168 mov edx, 0ffffffffh
169 STAM_PROFILE_ADV_STOP edx
170 %endif
171
172 ret
173ENDPROC vmmR0HostToGuest
174
175BITS 64
176%endif ;!VBOX_WITH_HYBRID_32BIT_KERNEL
177
178
179
180; *****************************************************************************
181; vmmR0HostToGuestAsm
182;
183; Phase one of the switch from host to guest context (host MMU context)
184;
185; INPUT:
186; - edx virtual address of CPUM structure (valid in host context)
187;
188; USES/DESTROYS:
189; - eax, ecx, edx, r8
190;
191; ASSUMPTION:
192; - current CS and DS selectors are wide open
193;
194; *****************************************************************************
195ALIGNCODE(16)
196BEGINPROC vmmR0HostToGuestAsm
197 ;; Store the offset from CPUM to CPUMCPU in r8
198 mov r8d, [rdx + CPUM.offCPUMCPU0]
199
200 ;;
201 ;; Save CPU host context
202 ;; Skip eax, edx and ecx as these are not preserved over calls.
203 ;;
204 ; general registers.
205 ; mov [rdx + r8 + CPUMCPU.Host.rax], rax - scratch
206 mov [rdx + r8 + CPUMCPU.Host.rbx], rbx
207 ; mov [rdx + r8 + CPUMCPU.Host.rcx], rcx - scratch
208 ; mov [rdx + r8 + CPUMCPU.Host.rdx], rdx - scratch
209 mov [rdx + r8 + CPUMCPU.Host.rdi], rdi
210 mov [rdx + r8 + CPUMCPU.Host.rsi], rsi
211 mov [rdx + r8 + CPUMCPU.Host.rsp], rsp
212 mov [rdx + r8 + CPUMCPU.Host.rbp], rbp
213 ; mov [rdx + r8 + CPUMCPU.Host.r8 ], r8 - scratch
214 ; mov [rdx + r8 + CPUMCPU.Host.r9 ], r9 - scratch
215 mov [rdx + r8 + CPUMCPU.Host.r10], r10
216 mov [rdx + r8 + CPUMCPU.Host.r11], r11
217 mov [rdx + r8 + CPUMCPU.Host.r12], r12
218 mov [rdx + r8 + CPUMCPU.Host.r13], r13
219 mov [rdx + r8 + CPUMCPU.Host.r14], r14
220 mov [rdx + r8 + CPUMCPU.Host.r15], r15
221 ; selectors.
222 mov [rdx + r8 + CPUMCPU.Host.ds], ds
223 mov [rdx + r8 + CPUMCPU.Host.es], es
224 mov [rdx + r8 + CPUMCPU.Host.fs], fs
225 mov [rdx + r8 + CPUMCPU.Host.gs], gs
226 mov [rdx + r8 + CPUMCPU.Host.ss], ss
227 ; MSRs
228 mov rbx, rdx
229 mov ecx, MSR_K8_FS_BASE
230 rdmsr
231 mov [rbx + r8 + CPUMCPU.Host.FSbase], eax
232 mov [rbx + r8 + CPUMCPU.Host.FSbase + 4], edx
233 mov ecx, MSR_K8_GS_BASE
234 rdmsr
235 mov [rbx + r8 + CPUMCPU.Host.GSbase], eax
236 mov [rbx + r8 + CPUMCPU.Host.GSbase + 4], edx
237 mov ecx, MSR_K6_EFER
238 rdmsr
239 mov [rbx + r8 + CPUMCPU.Host.efer], eax
240 mov [rbx + r8 + CPUMCPU.Host.efer + 4], edx
241 mov ecx, MSR_K6_EFER
242 mov rdx, rbx
243 ; special registers.
244 sldt [rdx + r8 + CPUMCPU.Host.ldtr]
245 sidt [rdx + r8 + CPUMCPU.Host.idtr]
246 sgdt [rdx + r8 + CPUMCPU.Host.gdtr]
247 str [rdx + r8 + CPUMCPU.Host.tr] ; yasm BUG, generates sldt. YASMCHECK!
248 ; flags
249 pushf
250 pop qword [rdx + r8 + CPUMCPU.Host.rflags]
251
252 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.
253 ; save MSR_IA32_SYSENTER_CS register.
254 mov rbx, rdx ; save edx
255 mov ecx, MSR_IA32_SYSENTER_CS
256 rdmsr ; edx:eax <- MSR[ecx]
257 mov [rbx + r8 + CPUMCPU.Host.SysEnter.cs], eax
258 mov [rbx + r8 + CPUMCPU.Host.SysEnter.cs + 4], edx
259 xor eax, eax ; load 0:0 to cause #GP upon sysenter
260 xor edx, edx
261 wrmsr
262 mov rdx, rbx ; restore edx
263 jmp short htg_no_sysenter
264
265ALIGNCODE(16)
266htg_no_sysenter:
267
268 ;; handle use flags.
269 mov esi, [rdx + r8 + CPUMCPU.fUseFlags] ; esi == use flags.
270 and esi, ~CPUM_USED_FPU ; Clear CPUM_USED_* flags. ;;@todo FPU check can be optimized to use cr0 flags!
271 mov [rdx + r8 + CPUMCPU.fUseFlags], esi
272
273 ; debug registers.
274 test esi, CPUM_USE_DEBUG_REGS | CPUM_USE_DEBUG_REGS_HOST
275 jz htg_debug_regs_no
276 jmp htg_debug_regs_save
277htg_debug_regs_no:
278 DEBUG_CHAR('a') ; trashes esi
279
280 ; control registers.
281 mov rax, cr0
282 mov [rdx + r8 + CPUMCPU.Host.cr0], rax
283 ;mov rax, cr2 ; assume host os don't stuff things in cr2. (safe)
284 ;mov [rdx + r8 + CPUMCPU.Host.cr2], rax
285 mov rax, cr3
286 mov [rdx + r8 + CPUMCPU.Host.cr3], rax
287 mov rax, cr4
288 mov [rdx + r8 + CPUMCPU.Host.cr4], rax
289
290 ;;
291 ;; Start switching to VMM context.
292 ;;
293
294 ;
295 ; Change CR0 and CR4 so we can correctly emulate FPU/MMX/SSE[23] exceptions
296 ; Also disable WP. (eax==cr4 now)
297 ; Note! X86_CR4_PSE and X86_CR4_PAE are important if the host thinks so :-)
298 ;
299 and rax, X86_CR4_MCE | X86_CR4_PSE | X86_CR4_PAE
300 mov ecx, [rdx + r8 + CPUMCPU.Guest.cr4]
301 DEBUG_CHAR('b') ; trashes esi
302 ;; @todo Switcher cleanup: Determine base CR4 during CPUMR0Init / VMMR3SelectSwitcher putting it
303 ; in CPUMCPU.Hyper.cr4 (which isn't currently being used). That should
304 ; simplify this operation a bit (and improve locality of the data).
305
306 ;
307 ; CR4.AndMask and CR4.OrMask are set in CPUMR3Init based on the presence of
308 ; FXSAVE support on the host CPU
309 ;
310 and ecx, [rdx + CPUM.CR4.AndMask]
311 or eax, ecx
312 or eax, [rdx + CPUM.CR4.OrMask]
313 mov cr4, rax
314 DEBUG_CHAR('c') ; trashes esi
315
316 mov eax, [rdx + r8 + CPUMCPU.Guest.cr0]
317 and eax, X86_CR0_EM
318 or eax, X86_CR0_PE | X86_CR0_PG | X86_CR0_TS | X86_CR0_ET | X86_CR0_NE | X86_CR0_MP
319 mov cr0, rax
320 DEBUG_CHAR('0') ; trashes esi
321
322
323 ; Load new gdt so we can do far jump to guest code after cr3 reload.
324 lgdt [rdx + r8 + CPUMCPU.Hyper.gdtr]
325 DEBUG_CHAR('1') ; trashes esi
326
327 ; Store the hypervisor cr3 for later loading
328 mov ebp, [rdx + r8 + CPUMCPU.Hyper.cr3]
329
330 ;;
331 ;; Load Intermediate memory context.
332 ;;
333 FIXUP FIX_INTER_AMD64_CR3, 1
334 mov eax, 0ffffffffh
335 mov cr3, rax
336 DEBUG_CHAR('2') ; trashes esi
337
338 ;;
339 ;; 1. Switch to compatibility mode, placing ourselves in identity mapped code.
340 ;;
341 jmp far [NAME(fpIDEnterTarget) wrt rip]
342
343; 16:32 Pointer to IDEnterTarget.
344NAME(fpIDEnterTarget):
345 FIXUP FIX_ID_32BIT, 0, NAME(IDEnterTarget) - NAME(Start)
346dd 0
347 FIXUP FIX_HYPER_CS, 0
348dd 0
349
350
351;;
352; Detour for saving the host DR7 and DR6.
353; esi and rdx must be preserved.
354htg_debug_regs_save:
355DEBUG_S_CHAR('s');
356 mov rax, dr7 ; not sure, but if I read the docs right this will trap if GD is set. FIXME!!!
357 mov [rdx + r8 + CPUMCPU.Host.dr7], rax
358 xor eax, eax ; clear everything. (bit 12? is read as 1...)
359 mov dr7, rax
360 mov rax, dr6 ; just in case we save the state register too.
361 mov [rdx + r8 + CPUMCPU.Host.dr6], rax
362 ; save host DR0-3?
363 test esi, CPUM_USE_DEBUG_REGS
364 jz near htg_debug_regs_no
365DEBUG_S_CHAR('S');
366 mov rax, dr0
367 mov [rdx + r8 + CPUMCPU.Host.dr0], rax
368 mov rbx, dr1
369 mov [rdx + r8 + CPUMCPU.Host.dr1], rbx
370 mov rcx, dr2
371 mov [rdx + r8 + CPUMCPU.Host.dr2], rcx
372 mov rax, dr3
373 mov [rdx + r8 + CPUMCPU.Host.dr3], rax
374 jmp htg_debug_regs_no
375
376
377 ; We're now on an identity mapped pages! in 32-bit compatability mode.
378BITS 32
379ALIGNCODE(16)
380GLOBALNAME IDEnterTarget
381 DEBUG_CHAR('3')
382
383 ; 2. Deactivate long mode by turning off paging.
384 mov ebx, cr0
385 and ebx, ~X86_CR0_PG
386 mov cr0, ebx
387 DEBUG_CHAR('4')
388
389 ; 3. Load intermediate page table.
390 FIXUP SWITCHER_FIX_INTER_CR3_GC, 1
391 mov edx, 0ffffffffh
392 mov cr3, edx
393
394 ; 4. Disable long mode.
395 ; We also use the chance to disable syscall/sysret and fast fxsave/fxrstor.
396 mov ecx, MSR_K6_EFER
397 rdmsr
398 DEBUG_CHAR('5')
399 and eax, ~(MSR_K6_EFER_LME | MSR_K6_EFER_SCE | MSR_K6_EFER_FFXSR)
400 wrmsr
401 DEBUG_CHAR('6')
402
403%ifndef SWITCHER_TO_PAE
404 ; 4b. Disable PAE.
405 mov eax, cr4
406 and eax, ~X86_CR4_PAE
407 mov cr4, eax
408%else
409%endif
410
411 ; 5. Enable paging.
412 or ebx, X86_CR0_PG
413 mov cr0, ebx
414 jmp short just_a_jump
415just_a_jump:
416 DEBUG_CHAR('7')
417
418 ;;
419 ;; 6. Jump to guest code mapping of the code and load the Hypervisor CS.
420 ;;
421 FIXUP FIX_ID_2_GC_NEAR_REL, 1, NAME(JmpGCTarget) - NAME(Start)
422 jmp near NAME(JmpGCTarget)
423
424
425 ;;
426 ;; When we arrive at this label we're at the
427 ;; guest code mapping of the switching code.
428 ;;
429ALIGNCODE(16)
430GLOBALNAME JmpGCTarget
431 DEBUG_CHAR('-')
432;mov eax, 0ffff0000h
433;.delay_loop:
434;nop
435;dec eax
436;nop
437;jnz .delay_loop
438 ; load final cr3 and do far jump to load cs.
439 mov cr3, ebp ; ebp set above
440 DEBUG_CHAR('0')
441
442 ;;
443 ;; We're in VMM MMU context and VMM CS is loaded.
444 ;; Setup the rest of the VMM state.
445 ;;
446 ; Load selectors
447 DEBUG_CHAR('1')
448 FIXUP FIX_HYPER_DS, 1
449 mov eax, 0ffffh
450 mov ds, eax
451 mov es, eax
452 xor eax, eax
453 mov gs, eax
454 mov fs, eax
455 ; Load pCpum into EDX
456 FIXUP FIX_GC_CPUMCPU_OFF, 1, 0
457 mov edx, 0ffffffffh
458 ; Activate guest IDT
459 DEBUG_CHAR('2')
460 lidt [edx + CPUMCPU.Hyper.idtr]
461
462 ; Setup stack; use the lss_esp, ss pair for lss
463 DEBUG_CHAR('3')
464 mov eax, [edx + CPUMCPU.Hyper.esp]
465 mov [edx + CPUMCPU.Hyper.lss_esp], eax
466 lss esp, [edx + CPUMCPU.Hyper.lss_esp]
467
468 ; Restore TSS selector; must mark it as not busy before using ltr (!)
469 DEBUG_CHAR('4')
470 FIXUP FIX_GC_TSS_GDTE_DW2, 2
471 and dword [0ffffffffh], ~0200h ; clear busy flag (2nd type2 bit)
472 DEBUG_CHAR('5')
473 ltr word [edx + CPUMCPU.Hyper.tr]
474 DEBUG_CHAR('6')
475
476 ; Activate the ldt (now we can safely crash).
477 lldt [edx + CPUMCPU.Hyper.ldtr]
478 DEBUG_CHAR('7')
479
480 ;; use flags.
481 mov esi, [edx + CPUMCPU.fUseFlags]
482
483 ; debug registers
484 test esi, CPUM_USE_DEBUG_REGS
485 jz htg_debug_regs_guest_no
486 jmp htg_debug_regs_guest
487htg_debug_regs_guest_no:
488 DEBUG_CHAR('9')
489
490 ; General registers.
491 mov ebx, [edx + CPUMCPU.Hyper.ebx]
492 mov ebp, [edx + CPUMCPU.Hyper.ebp]
493 mov esi, [edx + CPUMCPU.Hyper.esi]
494 mov edi, [edx + CPUMCPU.Hyper.edi]
495 push dword [edx + CPUMCPU.Hyper.eflags]
496 popfd
497 DEBUG_CHAR('!')
498
499 ;;
500 ;; Return to the VMM code which either called the switcher or
501 ;; the code set up to run by HC.
502 ;;
503%ifdef DEBUG_STUFF
504 COM32_S_PRINT ';eip='
505 mov eax, [edx + CPUMCPU.Hyper.eip]
506 COM32_S_DWORD_REG eax
507 COM32_S_CHAR ';'
508%endif
509 mov eax, [edx + CPUMCPU.Hyper.eip]
510 ; callees expect CPUM ptr
511 CPUM_FROM_CPUMCPU(edx)
512
513%ifdef VBOX_WITH_STATISTICS
514 FIXUP FIX_GC_VM_OFF, 1, VM.StatSwitcherToGC
515 mov edx, 0ffffffffh
516 STAM32_PROFILE_ADV_STOP edx
517 FIXUP FIX_GC_CPUM_OFF, 1, 0
518 mov edx, 0ffffffffh
519%endif
520 jmp eax
521
522;;
523; Detour for saving host DR0-3 and loading hypervisor debug registers.
524; esi and edx must be preserved.
525htg_debug_regs_guest:
526 DEBUG_S_CHAR('D')
527 DEBUG_S_CHAR('R')
528 DEBUG_S_CHAR('x')
529 ; load hyper DR0-7
530 mov ebx, [edx + CPUMCPU.Hyper.dr]
531 mov dr0, ebx
532 mov ecx, [edx + CPUMCPU.Hyper.dr + 8*1]
533 mov dr1, ecx
534 mov eax, [edx + CPUMCPU.Hyper.dr + 8*2]
535 mov dr2, eax
536 mov ebx, [edx + CPUMCPU.Hyper.dr + 8*3]
537 mov dr3, ebx
538 ;mov eax, [edx + CPUMCPU.Hyper.dr + 8*6]
539 mov ecx, 0ffff0ff0h
540 mov dr6, ecx
541 mov eax, [edx + CPUMCPU.Hyper.dr + 8*7]
542 mov dr7, eax
543 jmp htg_debug_regs_guest_no
544
545ENDPROC vmmR0HostToGuestAsm
546
547
548;;
549; Trampoline for doing a call when starting the hyper visor execution.
550;
551; Push any arguments to the routine.
552; Push the argument frame size (cArg * 4).
553; Push the call target (_cdecl convention).
554; Push the address of this routine.
555;
556;
557ALIGNCODE(16)
558BEGINPROC vmmGCCallTrampoline
559%ifdef DEBUG_STUFF
560 COM32_S_CHAR 'c'
561 COM32_S_CHAR 't'
562 COM32_S_CHAR '!'
563%endif
564
565 ; call routine
566 pop eax ; call address
567 mov esi, edx ; save edx
568 pop edi ; argument count.
569%ifdef DEBUG_STUFF
570 COM32_S_PRINT ';eax='
571 COM32_S_DWORD_REG eax
572 COM32_S_CHAR ';'
573%endif
574 call eax ; do call
575 add esp, edi ; cleanup stack
576
577 ; return to the host context.
578 push byte 0 ; eip
579 mov edx, esi ; CPUM pointer
580
581%ifdef DEBUG_STUFF
582 COM32_S_CHAR '`'
583%endif
584 jmp NAME(VMMGCGuestToHostAsm) ; eax = returncode.
585ENDPROC vmmGCCallTrampoline
586
587
588
589;;
590; The C interface.
591;
592ALIGNCODE(16)
593BEGINPROC vmmGCGuestToHost
594%ifdef DEBUG_STUFF
595 push esi
596 COM_NEWLINE
597 DEBUG_CHAR('b')
598 DEBUG_CHAR('a')
599 DEBUG_CHAR('c')
600 DEBUG_CHAR('k')
601 DEBUG_CHAR('!')
602 COM_NEWLINE
603 pop esi
604%endif
605 mov eax, [esp + 4]
606 jmp NAME(VMMGCGuestToHostAsm)
607ENDPROC vmmGCGuestToHost
608
609
610;;
611; VMMGCGuestToHostAsmGuestCtx
612;
613; Switches from Guest Context to Host Context.
614; Of course it's only called from within the GC.
615;
616; @param eax Return code.
617; @param esp + 4 Pointer to CPUMCTXCORE.
618;
619; @remark ASSUMES interrupts disabled.
620;
621ALIGNCODE(16)
622BEGINPROC VMMGCGuestToHostAsmGuestCtx
623 DEBUG_CHAR('~')
624
625%ifdef VBOX_WITH_STATISTICS
626 FIXUP FIX_GC_VM_OFF, 1, VM.StatTotalInGC
627 mov edx, 0ffffffffh
628 STAM32_PROFILE_ADV_STOP edx
629
630 FIXUP FIX_GC_VM_OFF, 1, VM.StatTotalGCToQemu
631 mov edx, 0ffffffffh
632 STAM32_PROFILE_ADV_START edx
633
634 FIXUP FIX_GC_VM_OFF, 1, VM.StatSwitcherToHC
635 mov edx, 0ffffffffh
636 STAM32_PROFILE_ADV_START edx
637%endif
638
639 ;
640 ; Load the CPUMCPU pointer.
641 ;
642 FIXUP FIX_GC_CPUMCPU_OFF, 1, 0
643 mov edx, 0ffffffffh
644
645 ; Skip return address (assumes called!)
646 lea esp, [esp + 4]
647
648 ;
649 ; Guest Context (assumes esp now points to CPUMCTXCORE structure).
650 ;
651 ; general purpose registers
652 push eax ; save return code.
653 mov eax, [esp + 4 + CPUMCTXCORE.edi]
654 mov [edx + CPUMCPU.Guest.edi], eax
655 mov eax, [esp + 4 + CPUMCTXCORE.esi]
656 mov [edx + CPUMCPU.Guest.esi], eax
657 mov eax, [esp + 4 + CPUMCTXCORE.ebp]
658 mov [edx + CPUMCPU.Guest.ebp], eax
659 mov eax, [esp + 4 + CPUMCTXCORE.eax]
660 mov [edx + CPUMCPU.Guest.eax], eax
661 mov eax, [esp + 4 + CPUMCTXCORE.ebx]
662 mov [edx + CPUMCPU.Guest.ebx], eax
663 mov eax, [esp + 4 + CPUMCTXCORE.edx]
664 mov [edx + CPUMCPU.Guest.edx], eax
665 mov eax, [esp + 4 + CPUMCTXCORE.ecx]
666 mov [edx + CPUMCPU.Guest.ecx], eax
667 mov eax, [esp + 4 + CPUMCTXCORE.esp]
668 mov [edx + CPUMCPU.Guest.esp], eax
669 ; selectors
670 mov eax, [esp + 4 + CPUMCTXCORE.ss]
671 mov [edx + CPUMCPU.Guest.ss], eax
672 mov eax, [esp + 4 + CPUMCTXCORE.gs]
673 mov [edx + CPUMCPU.Guest.gs], eax
674 mov eax, [esp + 4 + CPUMCTXCORE.fs]
675 mov [edx + CPUMCPU.Guest.fs], eax
676 mov eax, [esp + 4 + CPUMCTXCORE.es]
677 mov [edx + CPUMCPU.Guest.es], eax
678 mov eax, [esp + 4 + CPUMCTXCORE.ds]
679 mov [edx + CPUMCPU.Guest.ds], eax
680 mov eax, [esp + 4 + CPUMCTXCORE.cs]
681 mov [edx + CPUMCPU.Guest.cs], eax
682 ; flags
683 mov eax, [esp + 4 + CPUMCTXCORE.eflags]
684 mov [edx + CPUMCPU.Guest.eflags], eax
685 ; eip
686 mov eax, [esp + 4 + CPUMCTXCORE.eip]
687 mov [edx + CPUMCPU.Guest.eip], eax
688 ; jump to common worker code.
689 pop eax ; restore return code.
690
691 add esp, CPUMCTXCORE_size ; skip CPUMCTXCORE structure
692
693 jmp vmmGCGuestToHostAsm_EIPDone
694ENDPROC VMMGCGuestToHostAsmGuestCtx
695
696
697;;
698; VMMGCGuestToHostAsmHyperCtx
699;
700; This is an alternative entry point which we'll be using
701; when the we have the hypervisor context and need to save
702; that before going to the host.
703;
704; This is typically useful when abandoning the hypervisor
705; because of a trap and want the trap state to be saved.
706;
707; @param eax Return code.
708; @param ecx Points to CPUMCTXCORE.
709; @uses eax,edx,ecx
710ALIGNCODE(16)
711BEGINPROC VMMGCGuestToHostAsmHyperCtx
712 DEBUG_CHAR('#')
713
714%ifdef VBOX_WITH_STATISTICS
715 FIXUP FIX_GC_VM_OFF, 1, VM.StatTotalInGC
716 mov edx, 0ffffffffh
717 STAM32_PROFILE_ADV_STOP edx
718
719 FIXUP FIX_GC_VM_OFF, 1, VM.StatTotalGCToQemu
720 mov edx, 0ffffffffh
721 STAM32_PROFILE_ADV_START edx
722
723 FIXUP FIX_GC_VM_OFF, 1, VM.StatSwitcherToHC
724 mov edx, 0ffffffffh
725 STAM32_PROFILE_ADV_START edx
726%endif
727
728 ;
729 ; Load the CPUM pointer.
730 ;
731 FIXUP FIX_GC_CPUMCPU_OFF, 1, 0
732 mov edx, 0ffffffffh
733
734 push eax ; save return code.
735 ; general purpose registers
736 mov eax, [ecx + CPUMCTXCORE.edi]
737 mov [edx + CPUMCPU.Hyper.edi], eax
738 mov eax, [ecx + CPUMCTXCORE.esi]
739 mov [edx + CPUMCPU.Hyper.esi], eax
740 mov eax, [ecx + CPUMCTXCORE.ebp]
741 mov [edx + CPUMCPU.Hyper.ebp], eax
742 mov eax, [ecx + CPUMCTXCORE.eax]
743 mov [edx + CPUMCPU.Hyper.eax], eax
744 mov eax, [ecx + CPUMCTXCORE.ebx]
745 mov [edx + CPUMCPU.Hyper.ebx], eax
746 mov eax, [ecx + CPUMCTXCORE.edx]
747 mov [edx + CPUMCPU.Hyper.edx], eax
748 mov eax, [ecx + CPUMCTXCORE.ecx]
749 mov [edx + CPUMCPU.Hyper.ecx], eax
750 mov eax, [ecx + CPUMCTXCORE.esp]
751 mov [edx + CPUMCPU.Hyper.esp], eax
752 ; selectors
753 mov eax, [ecx + CPUMCTXCORE.ss]
754 mov [edx + CPUMCPU.Hyper.ss], eax
755 mov eax, [ecx + CPUMCTXCORE.gs]
756 mov [edx + CPUMCPU.Hyper.gs], eax
757 mov eax, [ecx + CPUMCTXCORE.fs]
758 mov [edx + CPUMCPU.Hyper.fs], eax
759 mov eax, [ecx + CPUMCTXCORE.es]
760 mov [edx + CPUMCPU.Hyper.es], eax
761 mov eax, [ecx + CPUMCTXCORE.ds]
762 mov [edx + CPUMCPU.Hyper.ds], eax
763 mov eax, [ecx + CPUMCTXCORE.cs]
764 mov [edx + CPUMCPU.Hyper.cs], eax
765 ; flags
766 mov eax, [ecx + CPUMCTXCORE.eflags]
767 mov [edx + CPUMCPU.Hyper.eflags], eax
768 ; eip
769 mov eax, [ecx + CPUMCTXCORE.eip]
770 mov [edx + CPUMCPU.Hyper.eip], eax
771 ; jump to common worker code.
772 pop eax ; restore return code.
773 jmp vmmGCGuestToHostAsm_SkipHyperRegs
774
775ENDPROC VMMGCGuestToHostAsmHyperCtx
776
777
778;;
779; VMMGCGuestToHostAsm
780;
781; This is an alternative entry point which we'll be using
782; when the we have saved the guest state already or we haven't
783; been messing with the guest at all.
784;
785; @param eax Return code.
786; @uses eax, edx, ecx (or it may use them in the future)
787;
788ALIGNCODE(16)
789BEGINPROC VMMGCGuestToHostAsm
790 DEBUG_CHAR('%')
791
792%ifdef VBOX_WITH_STATISTICS
793 FIXUP FIX_GC_VM_OFF, 1, VM.StatTotalInGC
794 mov edx, 0ffffffffh
795 STAM32_PROFILE_ADV_STOP edx
796
797 FIXUP FIX_GC_VM_OFF, 1, VM.StatTotalGCToQemu
798 mov edx, 0ffffffffh
799 STAM32_PROFILE_ADV_START edx
800
801 FIXUP FIX_GC_VM_OFF, 1, VM.StatSwitcherToHC
802 mov edx, 0ffffffffh
803 STAM32_PROFILE_ADV_START edx
804%endif
805
806 ;
807 ; Load the CPUM pointer.
808 ;
809 FIXUP FIX_GC_CPUMCPU_OFF, 1, 0
810 mov edx, 0ffffffffh
811
812 pop dword [edx + CPUMCPU.Hyper.eip] ; call return from stack
813 jmp short vmmGCGuestToHostAsm_EIPDone
814
815ALIGNCODE(16)
816vmmGCGuestToHostAsm_EIPDone:
817 ; general registers which we care about.
818 mov dword [edx + CPUMCPU.Hyper.ebx], ebx
819 mov dword [edx + CPUMCPU.Hyper.esi], esi
820 mov dword [edx + CPUMCPU.Hyper.edi], edi
821 mov dword [edx + CPUMCPU.Hyper.ebp], ebp
822 mov dword [edx + CPUMCPU.Hyper.esp], esp
823
824 ; special registers which may change.
825vmmGCGuestToHostAsm_SkipHyperRegs:
826%ifdef STRICT_IF
827 pushf
828 pop ecx
829 test ecx, X86_EFL_IF
830 jz .if_clear_out
831 mov eax, 0c0ffee01h
832 cli
833.if_clear_out:
834%endif
835 ; str [edx + CPUMCPU.Hyper.tr] - double fault only, and it won't be right then either.
836 sldt [edx + CPUMCPU.Hyper.ldtr]
837
838 ; No need to save CRx here. They are set dynamically according to Guest/Host requirements.
839 ; FPU context is saved before restore of host saving (another) branch.
840
841
842 ;;
843 ;; Load Intermediate memory context.
844 ;;
845 mov edi, eax ; save return code in EDI (careful with COM_DWORD_REG from here on!)
846 FIXUP SWITCHER_FIX_INTER_CR3_GC, 1
847 mov eax, 0ffffffffh
848 mov cr3, eax
849 DEBUG_CHAR('?')
850
851 ;; We're now in intermediate memory context!
852
853 ;;
854 ;; 0. Jump to identity mapped location
855 ;;
856 FIXUP FIX_GC_2_ID_NEAR_REL, 1, NAME(IDExitTarget) - NAME(Start)
857 jmp near NAME(IDExitTarget)
858
859 ; We're now on identity mapped pages!
860ALIGNCODE(16)
861GLOBALNAME IDExitTarget
862 DEBUG_CHAR('1')
863
864 ; 1. Disable paging.
865 mov ebx, cr0
866 and ebx, ~X86_CR0_PG
867 mov cr0, ebx
868 DEBUG_CHAR('2')
869
870 ; 2. Enable PAE.
871%ifdef SWITCHER_TO_PAE
872 ; - already enabled
873%else
874 mov ecx, cr4
875 or ecx, X86_CR4_PAE
876 mov cr4, ecx
877%endif
878
879 ; 3. Load long mode intermediate CR3.
880 FIXUP FIX_INTER_AMD64_CR3, 1
881 mov ecx, 0ffffffffh
882 mov cr3, ecx
883 DEBUG_CHAR('3')
884
885 ; 4. Enable long mode.
886 mov ebp, edx
887 mov ecx, MSR_K6_EFER
888 rdmsr
889 or eax, MSR_K6_EFER_LME
890 wrmsr
891 mov edx, ebp
892 DEBUG_CHAR('4')
893
894 ; 5. Enable paging.
895 or ebx, X86_CR0_PG
896 mov cr0, ebx
897 DEBUG_CHAR('5')
898
899 ; Jump from compatability mode to 64-bit mode.
900 FIXUP FIX_ID_FAR32_TO_64BIT_MODE, 1, NAME(IDExit64Mode) - NAME(Start)
901 jmp 0ffffh:0fffffffeh
902
903 ;
904 ; We're in 64-bit mode (ds, ss, es, fs, gs are all bogus).
905 ; Move on to the HC mapping.
906 ;
907BITS 64
908ALIGNCODE(16)
909NAME(IDExit64Mode):
910 DEBUG_CHAR('6')
911 jmp [NAME(pHCExitTarget) wrt rip]
912
913; 64-bit jump target
914NAME(pHCExitTarget):
915FIXUP FIX_HC_64BIT, 0, NAME(HCExitTarget) - NAME(Start)
916dq 0ffffffffffffffffh
917
918; 64-bit pCpum address.
919NAME(pCpumHC):
920FIXUP FIX_HC_64BIT_CPUM, 0
921dq 0ffffffffffffffffh
922
923 ;
924 ; When we arrive here we're at the host context
925 ; mapping of the switcher code.
926 ;
927ALIGNCODE(16)
928GLOBALNAME HCExitTarget
929 DEBUG_CHAR('9')
930
931 ; Clear high dword of the CPUMCPU pointer
932 and rdx, 0ffffffffh
933
934 ; load final cr3
935 mov rsi, [rdx + CPUMCPU.Host.cr3]
936 mov cr3, rsi
937 DEBUG_CHAR('@')
938
939 ;;
940 ;; Restore Host context.
941 ;;
942 ; Load CPUM pointer into edx
943 mov rdx, [NAME(pCpumHC) wrt rip]
944 ; Load the CPUMCPU offset.
945 mov r8d, [rdx + CPUM.offCPUMCPU0]
946
947 ; activate host gdt and idt
948 lgdt [rdx + r8 + CPUMCPU.Host.gdtr]
949 DEBUG_CHAR('0')
950 lidt [rdx + r8 + CPUMCPU.Host.idtr]
951 DEBUG_CHAR('1')
952 ; Restore TSS selector; must mark it as not busy before using ltr (!)
953%if 1 ; ASSUME that this is supposed to be 'BUSY'. (saves 20-30 ticks on the T42p)
954 movzx eax, word [rdx + r8 + CPUMCPU.Host.tr] ; eax <- TR
955 and al, 0F8h ; mask away TI and RPL bits, get descriptor offset.
956 add rax, [rdx + r8 + CPUMCPU.Host.gdtr + 2] ; eax <- GDTR.address + descriptor offset.
957 and dword [rax + 4], ~0200h ; clear busy flag (2nd type2 bit)
958 ltr word [rdx + r8 + CPUMCPU.Host.tr]
959%else
960 movzx eax, word [rdx + r8 + CPUMCPU.Host.tr] ; eax <- TR
961 and al, 0F8h ; mask away TI and RPL bits, get descriptor offset.
962 add rax, [rdx + r8 + CPUMCPU.Host.gdtr + 2] ; eax <- GDTR.address + descriptor offset.
963 mov ecx, [rax + 4] ; ecx <- 2nd descriptor dword
964 mov ebx, ecx ; save orginal value
965 and ecx, ~0200h ; clear busy flag (2nd type2 bit)
966 mov [rax + 4], ccx ; not using xchg here is paranoia..
967 ltr word [rdx + r8 + CPUMCPU.Host.tr]
968 xchg [rax + 4], ebx ; using xchg is paranoia too...
969%endif
970 ; activate ldt
971 DEBUG_CHAR('2')
972 lldt [rdx + r8 + CPUMCPU.Host.ldtr]
973 ; Restore segment registers
974 mov eax, [rdx + r8 + CPUMCPU.Host.ds]
975 mov ds, eax
976 mov eax, [rdx + r8 + CPUMCPU.Host.es]
977 mov es, eax
978 mov eax, [rdx + r8 + CPUMCPU.Host.fs]
979 mov fs, eax
980 mov eax, [rdx + r8 + CPUMCPU.Host.gs]
981 mov gs, eax
982 ; restore stack
983 mov eax, [rdx + r8 + CPUMCPU.Host.ss]
984 mov ss, eax
985 mov rsp, [rdx + r8 + CPUMCPU.Host.rsp]
986
987 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.
988 ; restore MSR_IA32_SYSENTER_CS register.
989 mov rbx, rdx ; save edx
990 mov ecx, MSR_IA32_SYSENTER_CS
991 mov eax, [rbx + r8 + CPUMCPU.Host.SysEnter.cs]
992 mov edx, [rbx + r8 + CPUMCPU.Host.SysEnter.cs + 4]
993 wrmsr ; MSR[ecx] <- edx:eax
994 mov rdx, rbx ; restore edx
995 jmp short gth_sysenter_no
996
997ALIGNCODE(16)
998gth_sysenter_no:
999
1000 ;; @todo AMD syscall
1001
1002 ; Restore FPU if guest has used it.
1003 ; Using fxrstor should ensure that we're not causing unwanted exception on the host.
1004 mov esi, [rdx + r8 + CPUMCPU.fUseFlags] ; esi == use flags.
1005 test esi, CPUM_USED_FPU
1006 jz short gth_fpu_no
1007 mov rcx, cr0
1008 and rcx, ~(X86_CR0_TS | X86_CR0_EM)
1009 mov cr0, rcx
1010
1011 fxsave [rdx + r8 + CPUMCPU.Guest.fpu]
1012 fxrstor [rdx + r8 + CPUMCPU.Host.fpu]
1013 jmp short gth_fpu_no
1014
1015ALIGNCODE(16)
1016gth_fpu_no:
1017
1018 ; Control registers.
1019 ; Would've liked to have these highere up in case of crashes, but
1020 ; the fpu stuff must be done before we restore cr0.
1021 mov rcx, [rdx + r8 + CPUMCPU.Host.cr4]
1022 mov cr4, rcx
1023 mov rcx, [rdx + r8 + CPUMCPU.Host.cr0]
1024 mov cr0, rcx
1025 ;mov rcx, [rdx + r8 + CPUMCPU.Host.cr2] ; assumes this is waste of time.
1026 ;mov cr2, rcx
1027
1028 ; restore debug registers (if modified) (esi must still be fUseFlags!)
1029 ; (must be done after cr4 reload because of the debug extension.)
1030 test esi, CPUM_USE_DEBUG_REGS | CPUM_USE_DEBUG_REGS_HOST
1031 jz short gth_debug_regs_no
1032 jmp gth_debug_regs_restore
1033gth_debug_regs_no:
1034
1035 ; Restore MSRs
1036 mov rbx, rdx
1037 mov ecx, MSR_K8_FS_BASE
1038 mov eax, [rbx + r8 + CPUMCPU.Host.FSbase]
1039 mov edx, [rbx + r8 + CPUMCPU.Host.FSbase + 4]
1040 wrmsr
1041 mov ecx, MSR_K8_GS_BASE
1042 mov eax, [rbx + r8 + CPUMCPU.Host.GSbase]
1043 mov edx, [rbx + r8 + CPUMCPU.Host.GSbase + 4]
1044 wrmsr
1045 mov ecx, MSR_K6_EFER
1046 mov eax, [rbx + r8 + CPUMCPU.Host.efer]
1047 mov edx, [rbx + r8 + CPUMCPU.Host.efer + 4]
1048 wrmsr
1049 mov rdx, rbx
1050
1051
1052 ; restore general registers.
1053 mov eax, edi ; restore return code. eax = return code !!
1054 ; mov rax, [rdx + r8 + CPUMCPU.Host.rax] - scratch + return code
1055 mov rbx, [rdx + r8 + CPUMCPU.Host.rbx]
1056 ; mov rcx, [rdx + r8 + CPUMCPU.Host.rcx] - scratch
1057 ; mov rdx, [rdx + r8 + CPUMCPU.Host.rdx] - scratch
1058 mov rdi, [rdx + r8 + CPUMCPU.Host.rdi]
1059 mov rsi, [rdx + r8 + CPUMCPU.Host.rsi]
1060 mov rsp, [rdx + r8 + CPUMCPU.Host.rsp]
1061 mov rbp, [rdx + r8 + CPUMCPU.Host.rbp]
1062 ; mov r8, [rdx + r8 + CPUMCPU.Host.r8 ] - scratch
1063 ; mov r9, [rdx + r8 + CPUMCPU.Host.r9 ] - scratch
1064 mov r10, [rdx + r8 + CPUMCPU.Host.r10]
1065 mov r11, [rdx + r8 + CPUMCPU.Host.r11]
1066 mov r12, [rdx + r8 + CPUMCPU.Host.r12]
1067 mov r13, [rdx + r8 + CPUMCPU.Host.r13]
1068 mov r14, [rdx + r8 + CPUMCPU.Host.r14]
1069 mov r15, [rdx + r8 + CPUMCPU.Host.r15]
1070
1071 ; finally restore flags. (probably not required)
1072 push qword [rdx + r8 + CPUMCPU.Host.rflags]
1073 popf
1074
1075
1076%ifdef DEBUG_STUFF
1077 COM64_S_CHAR '4'
1078%endif
1079 db 048h
1080 retf
1081
1082;;
1083; Detour for restoring the host debug registers.
1084; edx and edi must be preserved.
1085gth_debug_regs_restore:
1086 DEBUG_S_CHAR('d')
1087 xor eax, eax
1088 mov dr7, rax ; paranoia or not?
1089 test esi, CPUM_USE_DEBUG_REGS
1090 jz short gth_debug_regs_dr7
1091 DEBUG_S_CHAR('r')
1092 mov rax, [rdx + r8 + CPUMCPU.Host.dr0]
1093 mov dr0, rax
1094 mov rbx, [rdx + r8 + CPUMCPU.Host.dr1]
1095 mov dr1, rbx
1096 mov rcx, [rdx + r8 + CPUMCPU.Host.dr2]
1097 mov dr2, rcx
1098 mov rax, [rdx + r8 + CPUMCPU.Host.dr3]
1099 mov dr3, rax
1100gth_debug_regs_dr7:
1101 mov rbx, [rdx + r8 + CPUMCPU.Host.dr6]
1102 mov dr6, rbx
1103 mov rcx, [rdx + r8 + CPUMCPU.Host.dr7]
1104 mov dr7, rcx
1105 jmp gth_debug_regs_no
1106
1107ENDPROC VMMGCGuestToHostAsm
1108
1109
1110GLOBALNAME End
1111;
1112; The description string (in the text section).
1113;
1114NAME(Description):
1115 db SWITCHER_DESCRIPTION
1116 db 0
1117
1118extern NAME(Relocate)
1119
1120;
1121; End the fixup records.
1122;
1123BEGINDATA
1124 db FIX_THE_END ; final entry.
1125GLOBALNAME FixupsEnd
1126
1127;;
1128; The switcher definition structure.
1129ALIGNDATA(16)
1130GLOBALNAME Def
1131 istruc VMMSWITCHERDEF
1132 at VMMSWITCHERDEF.pvCode, RTCCPTR_DEF NAME(Start)
1133 at VMMSWITCHERDEF.pvFixups, RTCCPTR_DEF NAME(Fixups)
1134 at VMMSWITCHERDEF.pszDesc, RTCCPTR_DEF NAME(Description)
1135 at VMMSWITCHERDEF.pfnRelocate, RTCCPTR_DEF NAME(Relocate)
1136 at VMMSWITCHERDEF.enmType, dd SWITCHER_TYPE
1137 at VMMSWITCHERDEF.cbCode, dd NAME(End) - NAME(Start)
1138 at VMMSWITCHERDEF.offR0HostToGuest, dd NAME(vmmR0HostToGuest) - NAME(Start)
1139 at VMMSWITCHERDEF.offGCGuestToHost, dd NAME(vmmGCGuestToHost) - NAME(Start)
1140 at VMMSWITCHERDEF.offGCCallTrampoline, dd NAME(vmmGCCallTrampoline) - NAME(Start)
1141 at VMMSWITCHERDEF.offGCGuestToHostAsm, dd NAME(VMMGCGuestToHostAsm) - NAME(Start)
1142 at VMMSWITCHERDEF.offGCGuestToHostAsmHyperCtx, dd NAME(VMMGCGuestToHostAsmHyperCtx)- NAME(Start)
1143 at VMMSWITCHERDEF.offGCGuestToHostAsmGuestCtx, dd NAME(VMMGCGuestToHostAsmGuestCtx)- NAME(Start)
1144 ; disasm help
1145 at VMMSWITCHERDEF.offHCCode0, dd 0
1146 at VMMSWITCHERDEF.cbHCCode0, dd NAME(IDEnterTarget) - NAME(Start)
1147 at VMMSWITCHERDEF.offHCCode1, dd NAME(HCExitTarget) - NAME(Start)
1148 at VMMSWITCHERDEF.cbHCCode1, dd NAME(End) - NAME(HCExitTarget)
1149 at VMMSWITCHERDEF.offIDCode0, dd NAME(IDEnterTarget) - NAME(Start)
1150 at VMMSWITCHERDEF.cbIDCode0, dd NAME(JmpGCTarget) - NAME(IDEnterTarget)
1151 at VMMSWITCHERDEF.offIDCode1, dd NAME(IDExitTarget) - NAME(Start)
1152 at VMMSWITCHERDEF.cbIDCode1, dd NAME(HCExitTarget) - NAME(IDExitTarget)
1153 at VMMSWITCHERDEF.offGCCode, dd NAME(JmpGCTarget) - NAME(Start)
1154 at VMMSWITCHERDEF.cbGCCode, dd NAME(IDExitTarget) - NAME(JmpGCTarget)
1155
1156 iend
1157
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette