VirtualBox

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

Last change on this file since 14776 was 14254, checked in by vboxsync, 16 years ago

Safety precautions.

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