VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/PATMA.asm@ 54699

Last change on this file since 54699 was 54692, checked in by vboxsync, 10 years ago

Oops

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 79.9 KB
Line 
1; $Id: PATMA.asm 54692 2015-03-09 08:40:21Z vboxsync $
2;; @file
3; PATM Assembly Routines.
4;
5
6; Copyright (C) 2006-2015 Oracle Corporation
7;
8; This file is part of VirtualBox Open Source Edition (OSE), as
9; available from http://www.virtualbox.org. This file is free software;
10; you can redistribute it and/or modify it under the terms of the GNU
11; General Public License (GPL) as published by the Free Software
12; Foundation, in version 2 as it comes in the "COPYING" file of the
13; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15;
16
17;;
18; @note This method has problems in theory. If we fault for any reason, then we won't be able to restore
19; the guest's context properly!!
20; E.g if one of the push instructions causes a fault or SS isn't wide open and our patch GC state accesses aren't valid.
21; @assumptions
22; - Enough stack for a few pushes
23; - The SS selector has base 0 and limit 0xffffffff
24;
25; @todo stack probing is currently hardcoded and not present everywhere (search for 'probe stack')
26
27
28;*******************************************************************************
29;* Header Files *
30;*******************************************************************************
31%include "VBox/asmdefs.mac"
32%include "VBox/err.mac"
33%include "iprt/x86.mac"
34%include "VBox/vmm/vm.mac"
35%include "PATMA.mac"
36
37
38;*******************************************************************************
39;* Defined Constants And Macros *
40;*******************************************************************************
41%ifdef DEBUG
42; Noisy, but useful for debugging certain problems
43;;;%define PATM_LOG_PATCHINSTR
44;;%define PATM_LOG_PATCHIRET
45%endif
46
47;;
48; Simple PATCHASMRECORD initializer
49; @param %1 The patch function name.
50; @param %2 The number of fixups.
51;
52%macro PATCHASMRECORD_INIT 2
53istruc PATCHASMRECORD
54 at PATCHASMRECORD.pbFunction, RTCCPTR_DEF NAME(%1)
55 at PATCHASMRECORD.offJump, DD 0
56 at PATCHASMRECORD.offRelJump, DD 0
57 at PATCHASMRECORD.offSizeOverride,DD 0
58 at PATCHASMRECORD.cbFunction, DD NAME(%1 %+ _EndProc) - NAME(%1)
59 at PATCHASMRECORD.cRelocs, DD %2
60iend
61%endmacro
62
63;;
64; Simple PATCHASMRECORD initializer
65; @param %1 The patch function name.
66; @param %2 Jump lable.
67; @param %3 The number of fixups.
68;
69%macro PATCHASMRECORD_INIT_JUMP 3
70istruc PATCHASMRECORD
71 at PATCHASMRECORD.pbFunction, RTCCPTR_DEF NAME(%1)
72 at PATCHASMRECORD.offJump, DD %2 - NAME(%1)
73 at PATCHASMRECORD.offRelJump, DD 0
74 at PATCHASMRECORD.offSizeOverride,DD 0
75 at PATCHASMRECORD.cbFunction, DD NAME(%1 %+ _EndProc) - NAME(%1)
76 at PATCHASMRECORD.cRelocs, DD %3
77iend
78%endmacro
79
80;;
81; Simple PATCHASMRECORD initializer
82; @param %1 The patch function name.
83; @param %2 Jump lable (or nothing).
84; @param %3 Relative jump label (or nothing).
85; @param %4 Size override label (or nothing).
86; @param %5 The number of fixups.
87;
88%macro PATCHASMRECORD_INIT_EX 5
89istruc PATCHASMRECORD
90 at PATCHASMRECORD.pbFunction, RTCCPTR_DEF NAME(%1)
91%ifid %2
92 at PATCHASMRECORD.offJump, DD %2 - NAME(%1)
93%else
94 at PATCHASMRECORD.offJump, DD 0
95%endif
96%ifid %3
97 at PATCHASMRECORD.offRelJump, DD %3 - NAME(%1)
98%else
99 at PATCHASMRECORD.offRelJump, DD 0
100%endif
101%ifid %4
102 at PATCHASMRECORD.offSizeOverride,DD %4 - NAME(%1)
103%else
104 at PATCHASMRECORD.offSizeOverride,DD 0
105%endif
106 at PATCHASMRECORD.cbFunction, DD NAME(%1 %+ _EndProc) - NAME(%1)
107 at PATCHASMRECORD.cRelocs, DD %5
108iend
109%endmacro
110
111;;
112; Switches to the code section and aligns the function.
113;
114; @remarks This section must be different from the patch readonly data section!
115;
116%macro BEGIN_PATCH_CODE_SECTION 0
117BEGINCODE
118align 32
119%endmacro
120%macro BEGIN_PATCH_CODE_SECTION_NO_ALIGN 0
121BEGINCODE
122%endmacro
123
124;;
125; Switches to the data section for the read-only patch descriptor data and
126; aligns it appropriately.
127;
128; @remarks This section must be different from the patch code section!
129;
130%macro BEGIN_PATCH_RODATA_SECTION 0
131BEGINDATA
132align 16
133%endmacro
134%macro BEGIN_PATCH_RODATA_SECTION_NO_ALIGN 0
135BEGINDATA
136%endmacro
137
138
139;;
140; Starts a patch.
141;
142; @param %1 The patch record name (externally visible).
143; @param %2 The patch function name (considered internal).
144;
145%macro BEGIN_PATCH 2
146; The patch record.
147BEGIN_PATCH_RODATA_SECTION
148GLOBALNAME %1
149PATCHASMRECORD_INIT PATMCpuidReplacement, (RT_CONCAT(%1,_FixupEnd) - RT_CONCAT(%1,_FixupStart)) / 8
150RT_CONCAT(%1,_FixupStart):
151
152; The patch code.
153BEGIN_PATCH_CODE_SECTION
154BEGINPROC %2
155%endmacro
156
157;;
158; Emit a fixup.
159; @param %1 The fixup type.
160%macro PATCH_FIXUP 1
161BEGIN_PATCH_RODATA_SECTION_NO_ALIGN
162 dd %1, 0
163BEGIN_PATCH_CODE_SECTION_NO_ALIGN
164%endmacro
165
166;;
167; Emit a fixup with extra info.
168; @param %1 The fixup type.
169; @param %2 The extra fixup info.
170%macro PATCH_FIXUP_2 2
171BEGIN_PATCH_RODATA_SECTION_NO_ALIGN
172 dd %1, %2
173BEGIN_PATCH_CODE_SECTION_NO_ALIGN
174%endmacro
175
176;;
177; Ends a patch.
178;
179; This terminates the function and fixup array.
180;
181; @param %1 The patch record name (externally visible).
182; @param %2 The patch function name (considered internal).
183;
184%macro END_PATCH 2
185ENDPROC %2
186
187; Terminate the fixup array.
188BEGIN_PATCH_RODATA_SECTION_NO_ALIGN
189RT_CONCAT(%1,_FixupEnd):
190 dd 0ffffffffh, 0ffffffffh
191BEGIN_PATCH_CODE_SECTION_NO_ALIGN
192%endmacro
193
194
195;
196; Switch to 32-bit mode (x86).
197;
198%ifdef RT_ARCH_AMD64
199 BITS 32
200%endif
201
202
203%ifdef VBOX_WITH_STATISTICS
204;
205; Patch call statistics
206;
207BEGIN_PATCH_CODE_SECTION
208BEGINPROC PATMStats
209 mov dword [ss:PATM_INTERRUPTFLAG], 0
210 pushf
211 inc dword [ss:PATM_ALLPATCHCALLS]
212 inc dword [ss:PATM_PERPATCHCALLS]
213 popf
214 mov dword [ss:PATM_INTERRUPTFLAG], 1
215ENDPROC PATMStats
216
217; Patch record for statistics
218BEGIN_PATCH_RODATA_SECTION
219GLOBALNAME g_patmStatsRecord
220 PATCHASMRECORD_INIT PATMStats, 4
221 DD PATM_INTERRUPTFLAG, 0
222 DD PATM_ALLPATCHCALLS, 0
223 DD PATM_PERPATCHCALLS, 0
224 DD PATM_INTERRUPTFLAG, 0
225 DD 0ffffffffh, 0ffffffffh
226%endif ; VBOX_WITH_STATISTICS
227
228
229;
230; Set PATM_INTERRUPTFLAG
231;
232BEGIN_PATCH_CODE_SECTION
233BEGINPROC PATMSetPIF
234 mov dword [ss:PATM_INTERRUPTFLAG], 1
235ENDPROC PATMSetPIF
236
237; Patch record for setting PATM_INTERRUPTFLAG
238BEGIN_PATCH_RODATA_SECTION
239GLOBALNAME g_patmSetPIFRecord
240 PATCHASMRECORD_INIT PATMSetPIF, 1
241 DD PATM_INTERRUPTFLAG, 0
242 DD 0ffffffffh, 0ffffffffh
243
244;
245; Clear PATM_INTERRUPTFLAG
246;
247BEGIN_PATCH_CODE_SECTION
248BEGINPROC PATMClearPIF
249 ; probe stack here as we can't recover from page faults later on
250 not dword [esp-64]
251 not dword [esp-64]
252 mov dword [ss:PATM_INTERRUPTFLAG], 0
253ENDPROC PATMClearPIF
254
255; Patch record for clearing PATM_INTERRUPTFLAG
256BEGIN_PATCH_RODATA_SECTION
257GLOBALNAME g_patmClearPIFRecord
258 PATCHASMRECORD_INIT PATMClearPIF, 1
259 DD PATM_INTERRUPTFLAG, 0
260 DD 0ffffffffh, 0ffffffffh
261
262;
263; Clear PATM_INHIBITIRQADDR and fault if IF=0
264;
265BEGIN_PATCH_CODE_SECTION
266BEGINPROC PATMClearInhibitIRQFaultIF0
267 mov dword [ss:PATM_INTERRUPTFLAG], 0
268 mov dword [ss:PATM_INHIBITIRQADDR], 0
269 pushf
270
271 test dword [ss:PATM_VMFLAGS], X86_EFL_IF
272 jz PATMClearInhibitIRQFaultIF0_Fault
273
274 ; if interrupts are pending, then we must go back to the host context to handle them!
275 test dword [ss:PATM_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST
276 jz PATMClearInhibitIRQFaultIF0_Continue
277
278 ; Go to our hypervisor trap handler to dispatch the pending irq
279 mov dword [ss:PATM_TEMP_EAX], eax
280 mov dword [ss:PATM_TEMP_ECX], ecx
281 mov dword [ss:PATM_TEMP_EDI], edi
282 mov dword [ss:PATM_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
283 mov eax, PATM_ACTION_DISPATCH_PENDING_IRQ
284 lock or dword [ss:PATM_PENDINGACTION], eax
285 mov ecx, PATM_ACTION_MAGIC
286 mov edi, PATM_NEXTINSTRADDR
287 popfd ; restore flags we pushed above (the or instruction changes the flags as well)
288 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
289 ; does not return
290
291PATMClearInhibitIRQFaultIF0_Fault:
292 popf
293 mov dword [ss:PATM_INTERRUPTFLAG], 1
294 PATM_INT3
295
296PATMClearInhibitIRQFaultIF0_Continue:
297 popf
298 mov dword [ss:PATM_INTERRUPTFLAG], 1
299ENDPROC PATMClearInhibitIRQFaultIF0
300
301; Patch record for clearing PATM_INHIBITIRQADDR
302BEGIN_PATCH_RODATA_SECTION
303GLOBALNAME g_patmClearInhibitIRQFaultIF0Record
304 PATCHASMRECORD_INIT PATMClearInhibitIRQFaultIF0, 12
305 DD PATM_INTERRUPTFLAG, 0
306 DD PATM_INHIBITIRQADDR, 0
307 DD PATM_VMFLAGS, 0
308 DD PATM_VM_FORCEDACTIONS, 0
309 DD PATM_TEMP_EAX, 0
310 DD PATM_TEMP_ECX, 0
311 DD PATM_TEMP_EDI, 0
312 DD PATM_TEMP_RESTORE_FLAGS, 0
313 DD PATM_PENDINGACTION, 0
314 DD PATM_NEXTINSTRADDR, 0
315 DD PATM_INTERRUPTFLAG, 0
316 DD PATM_INTERRUPTFLAG, 0
317 DD 0ffffffffh, 0ffffffffh
318
319
320;
321; Clear PATM_INHIBITIRQADDR and continue if IF=0 (duplicated function only; never jump back to guest code afterwards!!)
322;
323BEGIN_PATCH_CODE_SECTION
324BEGINPROC PATMClearInhibitIRQContIF0
325 mov dword [ss:PATM_INTERRUPTFLAG], 0
326 mov dword [ss:PATM_INHIBITIRQADDR], 0
327 pushf
328
329 test dword [ss:PATM_VMFLAGS], X86_EFL_IF
330 jz PATMClearInhibitIRQContIF0_Continue
331
332 ; if interrupts are pending, then we must go back to the host context to handle them!
333 test dword [ss:PATM_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST
334 jz PATMClearInhibitIRQContIF0_Continue
335
336 ; Go to our hypervisor trap handler to dispatch the pending irq
337 mov dword [ss:PATM_TEMP_EAX], eax
338 mov dword [ss:PATM_TEMP_ECX], ecx
339 mov dword [ss:PATM_TEMP_EDI], edi
340 mov dword [ss:PATM_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
341 mov eax, PATM_ACTION_DISPATCH_PENDING_IRQ
342 lock or dword [ss:PATM_PENDINGACTION], eax
343 mov ecx, PATM_ACTION_MAGIC
344 mov edi, PATM_NEXTINSTRADDR
345 popfd ; restore flags we pushed above (the or instruction changes the flags as well)
346 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
347 ; does not return
348
349PATMClearInhibitIRQContIF0_Continue:
350 popf
351 mov dword [ss:PATM_INTERRUPTFLAG], 1
352ENDPROC PATMClearInhibitIRQContIF0
353
354; Patch record for clearing PATM_INHIBITIRQADDR
355BEGIN_PATCH_RODATA_SECTION
356GLOBALNAME g_patmClearInhibitIRQContIF0Record
357 PATCHASMRECORD_INIT PATMClearInhibitIRQContIF0, 11
358 DD PATM_INTERRUPTFLAG, 0
359 DD PATM_INHIBITIRQADDR, 0
360 DD PATM_VMFLAGS, 0
361 DD PATM_VM_FORCEDACTIONS, 0
362 DD PATM_TEMP_EAX, 0
363 DD PATM_TEMP_ECX, 0
364 DD PATM_TEMP_EDI, 0
365 DD PATM_TEMP_RESTORE_FLAGS, 0
366 DD PATM_PENDINGACTION, 0
367 DD PATM_NEXTINSTRADDR, 0
368 DD PATM_INTERRUPTFLAG, 0
369 DD 0ffffffffh, 0ffffffffh
370
371
372;
373;
374;
375BEGIN_PATCH_CODE_SECTION
376BEGINPROC PATMCliReplacement
377 mov dword [ss:PATM_INTERRUPTFLAG], 0
378 pushf
379%ifdef PATM_LOG_PATCHINSTR
380 push eax
381 push ecx
382 mov eax, PATM_ACTION_LOG_CLI
383 lock or dword [ss:PATM_PENDINGACTION], eax
384 mov ecx, PATM_ACTION_MAGIC
385 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
386 pop ecx
387 pop eax
388%endif
389
390 and dword [ss:PATM_VMFLAGS], ~X86_EFL_IF
391 popf
392
393 mov dword [ss:PATM_INTERRUPTFLAG], 1
394 DB 0xE9
395PATMCliJump:
396 DD PATM_JUMPDELTA
397ENDPROC PATMCliReplacement
398
399; Patch record for 'cli'
400BEGIN_PATCH_RODATA_SECTION
401GLOBALNAME g_patmCliRecord
402%ifdef PATM_LOG_PATCHINSTR
403 PATCHASMRECORD_INIT_JUMP PATMCliReplacement, PATMCliJump, 4
404%else
405 PATCHASMRECORD_INIT_JUMP PATMCliReplacement, PATMCliJump, 3
406%endif
407 DD PATM_INTERRUPTFLAG, 0
408%ifdef PATM_LOG_PATCHINSTR
409 DD PATM_PENDINGACTION, 0
410%endif
411 DD PATM_VMFLAGS, 0
412 DD PATM_INTERRUPTFLAG, 0
413 DD 0ffffffffh, 0ffffffffh
414
415
416;
417;
418;
419BEGIN_PATCH_CODE_SECTION
420BEGINPROC PATMStiReplacement
421 mov dword [ss:PATM_INTERRUPTFLAG], 0
422 mov dword [ss:PATM_INHIBITIRQADDR], PATM_NEXTINSTRADDR
423 pushf
424%ifdef PATM_LOG_PATCHINSTR
425 push eax
426 push ecx
427 mov eax, PATM_ACTION_LOG_STI
428 lock or dword [ss:PATM_PENDINGACTION], eax
429 mov ecx, PATM_ACTION_MAGIC
430 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
431 pop ecx
432 pop eax
433%endif
434 or dword [ss:PATM_VMFLAGS], X86_EFL_IF
435 popf
436 mov dword [ss:PATM_INTERRUPTFLAG], 1
437ENDPROC PATMStiReplacement
438
439; Patch record for 'sti'
440BEGIN_PATCH_RODATA_SECTION
441GLOBALNAME g_patmStiRecord
442%ifdef PATM_LOG_PATCHINSTR
443 PATCHASMRECORD_INIT PATMStiReplacement, 6
444%else
445 PATCHASMRECORD_INIT PATMStiReplacement, 5
446%endif
447 DD PATM_INTERRUPTFLAG, 0
448 DD PATM_INHIBITIRQADDR, 0
449 DD PATM_NEXTINSTRADDR, 0
450%ifdef PATM_LOG_PATCHINSTR
451 DD PATM_PENDINGACTION, 0
452%endif
453 DD PATM_VMFLAGS, 0
454 DD PATM_INTERRUPTFLAG, 0
455 DD 0ffffffffh, 0ffffffffh
456
457
458;
459; Trampoline code for trap entry (without error code on the stack)
460;
461; esp + 32 - GS (V86 only)
462; esp + 28 - FS (V86 only)
463; esp + 24 - DS (V86 only)
464; esp + 20 - ES (V86 only)
465; esp + 16 - SS (if transfer to inner ring)
466; esp + 12 - ESP (if transfer to inner ring)
467; esp + 8 - EFLAGS
468; esp + 4 - CS
469; esp - EIP
470;
471BEGIN_PATCH_CODE_SECTION
472BEGINPROC PATMTrapEntry
473 mov dword [ss:PATM_INTERRUPTFLAG], 0
474 pushf
475
476%ifdef PATM_LOG_PATCHIRET
477 push eax
478 push ecx
479 push edx
480 lea edx, dword [ss:esp+12+4] ;3 dwords + pushed flags -> iret eip
481 mov eax, PATM_ACTION_LOG_GATE_ENTRY
482 lock or dword [ss:PATM_PENDINGACTION], eax
483 mov ecx, PATM_ACTION_MAGIC
484 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
485 pop edx
486 pop ecx
487 pop eax
488%endif
489
490 test dword [esp+12], X86_EFL_VM
491 jnz PATMTrapNoRing1
492
493 ; make sure the saved CS selector for ring 1 is made 0
494 test dword [esp+8], 2
495 jnz PATMTrapNoRing1
496 test dword [esp+8], 1
497 jz PATMTrapNoRing1
498 and dword [esp+8], dword ~1 ; yasm / nasm dword
499PATMTrapNoRing1:
500
501 ; correct EFLAGS on the stack to include the current IOPL
502 push eax
503 mov eax, dword [ss:PATM_VMFLAGS]
504 and eax, X86_EFL_IOPL
505 and dword [esp+16], ~X86_EFL_IOPL ; esp+16 = eflags = esp+8+4(efl)+4(eax)
506 or dword [esp+16], eax
507 pop eax
508
509 popf
510 mov dword [ss:PATM_INTERRUPTFLAG], 1
511 DB 0xE9
512PATMTrapEntryJump:
513 DD PATM_JUMPDELTA
514ENDPROC PATMTrapEntry
515
516; Patch record for trap gate entrypoint
517BEGIN_PATCH_RODATA_SECTION
518GLOBALNAME g_patmTrapEntryRecord
519%ifdef PATM_LOG_PATCHIRET
520 PATCHASMRECORD_INIT_JUMP PATMTrapEntry, PATMTrapEntryJump, 4
521%else
522 PATCHASMRECORD_INIT_JUMP PATMTrapEntry, PATMTrapEntryJump, 3
523%endif
524 DD PATM_INTERRUPTFLAG, 0
525%ifdef PATM_LOG_PATCHIRET
526 DD PATM_PENDINGACTION, 0
527%endif
528 DD PATM_VMFLAGS, 0
529 DD PATM_INTERRUPTFLAG, 0
530 DD 0ffffffffh, 0ffffffffh
531
532
533;
534; Trampoline code for trap entry (with error code on the stack)
535;
536; esp + 36 - GS (V86 only)
537; esp + 32 - FS (V86 only)
538; esp + 28 - DS (V86 only)
539; esp + 24 - ES (V86 only)
540; esp + 20 - SS (if transfer to inner ring)
541; esp + 16 - ESP (if transfer to inner ring)
542; esp + 12 - EFLAGS
543; esp + 8 - CS
544; esp + 4 - EIP
545; esp - error code
546;
547BEGIN_PATCH_CODE_SECTION
548BEGINPROC PATMTrapEntryErrorCode
549PATMTrapErrorCodeEntryStart:
550 mov dword [ss:PATM_INTERRUPTFLAG], 0
551 pushf
552
553%ifdef PATM_LOG_PATCHIRET
554 push eax
555 push ecx
556 push edx
557 lea edx, dword [ss:esp+12+4+4] ;3 dwords + pushed flags + error code -> iret eip
558 mov eax, PATM_ACTION_LOG_GATE_ENTRY
559 lock or dword [ss:PATM_PENDINGACTION], eax
560 mov ecx, PATM_ACTION_MAGIC
561 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
562 pop edx
563 pop ecx
564 pop eax
565%endif
566
567 test dword [esp+16], X86_EFL_VM
568 jnz PATMTrapErrorCodeNoRing1
569
570 ; make sure the saved CS selector for ring 1 is made 0
571 test dword [esp+12], 2
572 jnz PATMTrapErrorCodeNoRing1
573 test dword [esp+12], 1
574 jz PATMTrapErrorCodeNoRing1
575 and dword [esp+12], dword ~1 ; yasm / nasm dword
576PATMTrapErrorCodeNoRing1:
577
578 ; correct EFLAGS on the stack to include the current IOPL
579 push eax
580 mov eax, dword [ss:PATM_VMFLAGS]
581 and eax, X86_EFL_IOPL
582 and dword [esp+20], ~X86_EFL_IOPL ; esp+20 = eflags = esp+8+4(efl)+4(error code)+4(eax)
583 or dword [esp+20], eax
584 pop eax
585
586 popf
587 mov dword [ss:PATM_INTERRUPTFLAG], 1
588 DB 0xE9
589PATMTrapErrorCodeEntryJump:
590 DD PATM_JUMPDELTA
591ENDPROC PATMTrapEntryErrorCode
592
593; Patch record for trap gate entrypoint
594BEGIN_PATCH_RODATA_SECTION
595GLOBALNAME g_patmTrapEntryRecordErrorCode
596%ifdef PATM_LOG_PATCHIRET
597 PATCHASMRECORD_INIT_JUMP PATMTrapEntryErrorCode, PATMTrapErrorCodeEntryJump, 4
598%else
599 PATCHASMRECORD_INIT_JUMP PATMTrapEntryErrorCode, PATMTrapErrorCodeEntryJump, 3
600%endif
601 DD PATM_INTERRUPTFLAG, 0
602%ifdef PATM_LOG_PATCHIRET
603 DD PATM_PENDINGACTION, 0
604%endif
605 DD PATM_VMFLAGS, 0
606 DD PATM_INTERRUPTFLAG, 0
607 DD 0ffffffffh, 0ffffffffh
608
609
610;
611; Trampoline code for interrupt gate entry (without error code on the stack)
612;
613; esp + 32 - GS (V86 only)
614; esp + 28 - FS (V86 only)
615; esp + 24 - DS (V86 only)
616; esp + 20 - ES (V86 only)
617; esp + 16 - SS (if transfer to inner ring)
618; esp + 12 - ESP (if transfer to inner ring)
619; esp + 8 - EFLAGS
620; esp + 4 - CS
621; esp - EIP
622;
623BEGIN_PATCH_CODE_SECTION
624BEGINPROC PATMIntEntry
625 mov dword [ss:PATM_INTERRUPTFLAG], 0
626 pushf
627
628%ifdef PATM_LOG_PATCHIRET
629 push eax
630 push ecx
631 push edx
632 lea edx, dword [ss:esp+12+4] ;3 dwords + pushed flags -> iret eip
633 mov eax, PATM_ACTION_LOG_GATE_ENTRY
634 lock or dword [ss:PATM_PENDINGACTION], eax
635 mov ecx, PATM_ACTION_MAGIC
636 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
637 pop edx
638 pop ecx
639 pop eax
640%endif
641
642 test dword [esp+12], X86_EFL_VM
643 jnz PATMIntNoRing1
644
645 ; make sure the saved CS selector for ring 1 is made 0
646 test dword [esp+8], 2
647 jnz PATMIntNoRing1
648 test dword [esp+8], 1
649 jz PATMIntNoRing1
650 and dword [esp+8], dword ~1 ; yasm / nasm dword
651PATMIntNoRing1:
652
653 ; correct EFLAGS on the stack to include the current IOPL
654 push eax
655 mov eax, dword [ss:PATM_VMFLAGS]
656 and eax, X86_EFL_IOPL
657 and dword [esp+16], ~X86_EFL_IOPL ; esp+16 = eflags = esp+8+4(efl)+4(eax)
658 or dword [esp+16], eax
659 pop eax
660
661 popf
662 mov dword [ss:PATM_INTERRUPTFLAG], 1
663ENDPROC PATMIntEntry
664
665; Patch record for interrupt gate entrypoint
666BEGIN_PATCH_RODATA_SECTION
667GLOBALNAME g_patmIntEntryRecord
668%ifdef PATM_LOG_PATCHIRET
669 PATCHASMRECORD_INIT PATMIntEntry, 4
670%else
671 PATCHASMRECORD_INIT PATMIntEntry, 3
672%endif
673 DD PATM_INTERRUPTFLAG, 0
674%ifdef PATM_LOG_PATCHIRET
675 DD PATM_PENDINGACTION, 0
676%endif
677 DD PATM_VMFLAGS, 0
678 DD PATM_INTERRUPTFLAG, 0
679 DD 0ffffffffh, 0ffffffffh
680
681
682;
683; Trampoline code for interrupt gate entry (*with* error code on the stack)
684;
685; esp + 36 - GS (V86 only)
686; esp + 32 - FS (V86 only)
687; esp + 28 - DS (V86 only)
688; esp + 24 - ES (V86 only)
689; esp + 20 - SS (if transfer to inner ring)
690; esp + 16 - ESP (if transfer to inner ring)
691; esp + 12 - EFLAGS
692; esp + 8 - CS
693; esp + 4 - EIP
694; esp - error code
695;
696BEGIN_PATCH_CODE_SECTION
697BEGINPROC PATMIntEntryErrorCode
698 mov dword [ss:PATM_INTERRUPTFLAG], 0
699 pushf
700
701%ifdef PATM_LOG_PATCHIRET
702 push eax
703 push ecx
704 push edx
705 lea edx, dword [ss:esp+12+4+4] ;3 dwords + pushed flags + error code -> iret eip
706 mov eax, PATM_ACTION_LOG_GATE_ENTRY
707 lock or dword [ss:PATM_PENDINGACTION], eax
708 mov ecx, PATM_ACTION_MAGIC
709 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
710 pop edx
711 pop ecx
712 pop eax
713%endif
714
715 test dword [esp+16], X86_EFL_VM
716 jnz PATMIntNoRing1_ErrorCode
717
718 ; make sure the saved CS selector for ring 1 is made 0
719 test dword [esp+12], 2
720 jnz PATMIntNoRing1_ErrorCode
721 test dword [esp+12], 1
722 jz PATMIntNoRing1_ErrorCode
723 and dword [esp+12], dword ~1 ; yasm / nasm dword
724PATMIntNoRing1_ErrorCode:
725
726 ; correct EFLAGS on the stack to include the current IOPL
727 push eax
728 mov eax, dword [ss:PATM_VMFLAGS]
729 and eax, X86_EFL_IOPL
730 and dword [esp+20], ~X86_EFL_IOPL ; esp+20 = eflags = esp+8+4(efl)+4(eax)+4(error code)
731 or dword [esp+20], eax
732 pop eax
733
734 popf
735 mov dword [ss:PATM_INTERRUPTFLAG], 1
736ENDPROC PATMIntEntryErrorCode
737
738; Patch record for interrupt gate entrypoint
739BEGIN_PATCH_RODATA_SECTION
740GLOBALNAME g_patmIntEntryRecordErrorCode
741%ifdef PATM_LOG_PATCHIRET
742 PATCHASMRECORD_INIT PATMIntEntryErrorCode, 4
743%else
744 PATCHASMRECORD_INIT PATMIntEntryErrorCode, 3
745%endif
746 DD PATM_INTERRUPTFLAG, 0
747%ifdef PATM_LOG_PATCHIRET
748 DD PATM_PENDINGACTION, 0
749%endif
750 DD PATM_VMFLAGS, 0
751 DD PATM_INTERRUPTFLAG, 0
752 DD 0ffffffffh, 0ffffffffh
753
754
755;
756; 32 bits Popf replacement that faults when IF remains 0
757;
758BEGIN_PATCH_CODE_SECTION
759BEGINPROC PATMPopf32Replacement
760 mov dword [ss:PATM_INTERRUPTFLAG], 0
761%ifdef PATM_LOG_PATCHINSTR
762 push eax
763 push ecx
764 mov eax, PATM_ACTION_LOG_POPF_IF1
765 test dword [esp+8], X86_EFL_IF
766 jnz PATMPopf32_Log
767 mov eax, PATM_ACTION_LOG_POPF_IF0
768
769PATMPopf32_Log:
770 lock or dword [ss:PATM_PENDINGACTION], eax
771 mov ecx, PATM_ACTION_MAGIC
772 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
773 pop ecx
774 pop eax
775%endif
776
777 test dword [esp], X86_EFL_IF
778 jnz PATMPopf32_Ok
779 mov dword [ss:PATM_INTERRUPTFLAG], 1
780 PATM_INT3
781
782PATMPopf32_Ok:
783 ; Note: we don't allow popf instructions to change the current IOPL; we simply ignore such changes (!!!)
784 ; In this particular patch it's rather unlikely the pushf was included, so we have no way to check if the flags on the stack were correctly synced
785 ; PATMPopf32Replacement_NoExit is different, because it's only used in IDT and function patches
786 or dword [ss:PATM_VMFLAGS], X86_EFL_IF
787
788 ; if interrupts are pending, then we must go back to the host context to handle them!
789 test dword [ss:PATM_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST
790 jz PATMPopf32_Continue
791
792 ; Go to our hypervisor trap handler to dispatch the pending irq
793 mov dword [ss:PATM_TEMP_EAX], eax
794 mov dword [ss:PATM_TEMP_ECX], ecx
795 mov dword [ss:PATM_TEMP_EDI], edi
796 mov dword [ss:PATM_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
797 mov eax, PATM_ACTION_DISPATCH_PENDING_IRQ
798 lock or dword [ss:PATM_PENDINGACTION], eax
799 mov ecx, PATM_ACTION_MAGIC
800 mov edi, PATM_NEXTINSTRADDR
801
802 popfd ; restore flags we pushed above (the or instruction changes the flags as well)
803 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
804 ; does not return
805
806PATMPopf32_Continue:
807 popfd ; restore flags we pushed above
808 mov dword [ss:PATM_INTERRUPTFLAG], 1
809 DB 0xE9
810PATMPopf32Jump:
811 DD PATM_JUMPDELTA
812ENDPROC PATMPopf32Replacement
813
814; Patch record for 'popfd'
815BEGIN_PATCH_RODATA_SECTION
816GLOBALNAME g_patmPopf32Record
817%ifdef PATM_LOG_PATCHINSTR
818 PATCHASMRECORD_INIT_JUMP PATMPopf32Replacement, PATMPopf32Jump, 12
819%else
820 PATCHASMRECORD_INIT_JUMP PATMPopf32Replacement, PATMPopf32Jump, 11
821%endif
822 DD PATM_INTERRUPTFLAG, 0
823%ifdef PATM_LOG_PATCHINSTR
824 DD PATM_PENDINGACTION, 0
825%endif
826 DD PATM_INTERRUPTFLAG, 0
827 DD PATM_VMFLAGS, 0
828 DD PATM_VM_FORCEDACTIONS, 0
829 DD PATM_TEMP_EAX, 0
830 DD PATM_TEMP_ECX, 0
831 DD PATM_TEMP_EDI, 0
832 DD PATM_TEMP_RESTORE_FLAGS, 0
833 DD PATM_PENDINGACTION, 0
834 DD PATM_NEXTINSTRADDR, 0
835 DD PATM_INTERRUPTFLAG, 0
836 DD 0ffffffffh, 0ffffffffh
837
838
839;
840; no need to check the IF flag when popf isn't an exit point of a patch (e.g. function duplication)
841;
842BEGIN_PATCH_CODE_SECTION
843BEGINPROC PATMPopf32Replacement_NoExit
844 mov dword [ss:PATM_INTERRUPTFLAG], 0
845%ifdef PATM_LOG_PATCHINSTR
846 push eax
847 push ecx
848 mov eax, PATM_ACTION_LOG_POPF_IF1
849 test dword [esp+8], X86_EFL_IF
850 jnz PATMPopf32_NoExitLog
851 mov eax, PATM_ACTION_LOG_POPF_IF0
852
853PATMPopf32_NoExitLog:
854 lock or dword [ss:PATM_PENDINGACTION], eax
855 mov ecx, PATM_ACTION_MAGIC
856 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
857 pop ecx
858 pop eax
859%endif
860 test dword [esp], X86_EFL_IF
861 jz PATMPopf32_NoExit_Continue
862
863 ; if interrupts are pending, then we must go back to the host context to handle them!
864 test dword [ss:PATM_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST
865 jz PATMPopf32_NoExit_Continue
866
867 ; Go to our hypervisor trap handler to dispatch the pending irq
868 mov dword [ss:PATM_TEMP_EAX], eax
869 mov dword [ss:PATM_TEMP_ECX], ecx
870 mov dword [ss:PATM_TEMP_EDI], edi
871 mov dword [ss:PATM_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
872 mov eax, PATM_ACTION_DISPATCH_PENDING_IRQ
873 lock or dword [ss:PATM_PENDINGACTION], eax
874 mov ecx, PATM_ACTION_MAGIC
875 mov edi, PATM_NEXTINSTRADDR
876
877 pop dword [ss:PATM_VMFLAGS] ; restore flags now (the or instruction changes the flags as well)
878 push dword [ss:PATM_VMFLAGS]
879 popfd
880
881 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
882 ; does not return
883
884PATMPopf32_NoExit_Continue:
885 pop dword [ss:PATM_VMFLAGS]
886 push dword [ss:PATM_VMFLAGS]
887 popfd
888 mov dword [ss:PATM_INTERRUPTFLAG], 1
889ENDPROC PATMPopf32Replacement_NoExit
890
891; Patch record for 'popfd'
892BEGIN_PATCH_RODATA_SECTION
893GLOBALNAME g_patmPopf32Record_NoExit
894%ifdef PATM_LOG_PATCHINSTR
895 PATCHASMRECORD_INIT PATMPopf32Replacement_NoExit, 14
896%else
897 PATCHASMRECORD_INIT PATMPopf32Replacement_NoExit, 13
898%endif
899 DD PATM_INTERRUPTFLAG, 0
900%ifdef PATM_LOG_PATCHINSTR
901 DD PATM_PENDINGACTION, 0
902%endif
903 DD PATM_VM_FORCEDACTIONS, 0
904 DD PATM_TEMP_EAX, 0
905 DD PATM_TEMP_ECX, 0
906 DD PATM_TEMP_EDI, 0
907 DD PATM_TEMP_RESTORE_FLAGS, 0
908 DD PATM_PENDINGACTION, 0
909 DD PATM_NEXTINSTRADDR, 0
910 DD PATM_VMFLAGS, 0
911 DD PATM_VMFLAGS, 0
912 DD PATM_VMFLAGS, 0
913 DD PATM_VMFLAGS, 0
914 DD PATM_INTERRUPTFLAG, 0
915 DD 0ffffffffh, 0ffffffffh
916
917
918;
919; 16 bits Popf replacement that faults when IF remains 0
920;
921BEGIN_PATCH_CODE_SECTION
922BEGINPROC PATMPopf16Replacement
923 mov dword [ss:PATM_INTERRUPTFLAG], 0
924 test word [esp], X86_EFL_IF
925 jnz PATMPopf16_Ok
926 mov dword [ss:PATM_INTERRUPTFLAG], 1
927 PATM_INT3
928
929PATMPopf16_Ok:
930 ; if interrupts are pending, then we must go back to the host context to handle them!
931 ; @note we destroy the flags here, but that should really not matter (PATM_INT3 case)
932 test dword [ss:PATM_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST
933 jz PATMPopf16_Continue
934 mov dword [ss:PATM_INTERRUPTFLAG], 1
935 PATM_INT3
936
937PATMPopf16_Continue:
938
939 pop word [ss:PATM_VMFLAGS]
940 push word [ss:PATM_VMFLAGS]
941 and dword [ss:PATM_VMFLAGS], PATM_VIRTUAL_FLAGS_MASK
942 or dword [ss:PATM_VMFLAGS], PATM_VIRTUAL_FLAGS_MASK
943
944 DB 0x66 ; size override
945 popf ;after the and and or operations!! (flags must be preserved)
946 mov dword [ss:PATM_INTERRUPTFLAG], 1
947
948 DB 0xE9
949PATMPopf16Jump:
950 DD PATM_JUMPDELTA
951ENDPROC PATMPopf16Replacement
952
953; Patch record for 'popf'
954BEGIN_PATCH_RODATA_SECTION
955GLOBALNAME g_patmPopf16Record
956 PATCHASMRECORD_INIT_JUMP PATMPopf16Replacement, PATMPopf16Jump, 9
957 DD PATM_INTERRUPTFLAG, 0
958 DD PATM_INTERRUPTFLAG, 0
959 DD PATM_VM_FORCEDACTIONS, 0
960 DD PATM_INTERRUPTFLAG, 0
961 DD PATM_VMFLAGS, 0
962 DD PATM_VMFLAGS, 0
963 DD PATM_VMFLAGS, 0
964 DD PATM_VMFLAGS, 0
965 DD PATM_INTERRUPTFLAG, 0
966 DD 0ffffffffh, 0ffffffffh
967
968
969;
970; 16 bits Popf replacement that faults when IF remains 0
971; @todo not necessary to fault in that case (see 32 bits version)
972;
973BEGIN_PATCH_CODE_SECTION
974BEGINPROC PATMPopf16Replacement_NoExit
975 mov dword [ss:PATM_INTERRUPTFLAG], 0
976 test word [esp], X86_EFL_IF
977 jnz PATMPopf16_Ok_NoExit
978 mov dword [ss:PATM_INTERRUPTFLAG], 1
979 PATM_INT3
980
981PATMPopf16_Ok_NoExit:
982 ; if interrupts are pending, then we must go back to the host context to handle them!
983 ; @note we destroy the flags here, but that should really not matter (PATM_INT3 case)
984 test dword [ss:PATM_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST
985 jz PATMPopf16_Continue_NoExit
986 mov dword [ss:PATM_INTERRUPTFLAG], 1
987 PATM_INT3
988
989PATMPopf16_Continue_NoExit:
990
991 pop word [ss:PATM_VMFLAGS]
992 push word [ss:PATM_VMFLAGS]
993 and dword [ss:PATM_VMFLAGS], PATM_VIRTUAL_FLAGS_MASK
994 or dword [ss:PATM_VMFLAGS], PATM_VIRTUAL_FLAGS_MASK
995
996 DB 0x66 ; size override
997 popf ;after the and and or operations!! (flags must be preserved)
998 mov dword [ss:PATM_INTERRUPTFLAG], 1
999ENDPROC PATMPopf16Replacement_NoExit
1000
1001; Patch record for 'popf'
1002BEGIN_PATCH_RODATA_SECTION
1003GLOBALNAME g_patmPopf16Record_NoExit
1004 PATCHASMRECORD_INIT PATMPopf16Replacement_NoExit, 9
1005 DD PATM_INTERRUPTFLAG, 0
1006 DD PATM_INTERRUPTFLAG, 0
1007 DD PATM_VM_FORCEDACTIONS, 0
1008 DD PATM_INTERRUPTFLAG, 0
1009 DD PATM_VMFLAGS, 0
1010 DD PATM_VMFLAGS, 0
1011 DD PATM_VMFLAGS, 0
1012 DD PATM_VMFLAGS, 0
1013 DD PATM_INTERRUPTFLAG, 0
1014 DD 0ffffffffh, 0ffffffffh
1015
1016
1017;
1018;
1019;
1020BEGIN_PATCH_CODE_SECTION
1021BEGINPROC PATMPushf32Replacement
1022 mov dword [ss:PATM_INTERRUPTFLAG], 0
1023 pushfd
1024%ifdef PATM_LOG_PATCHINSTR
1025 push eax
1026 push ecx
1027 mov eax, PATM_ACTION_LOG_PUSHF
1028 lock or dword [ss:PATM_PENDINGACTION], eax
1029 mov ecx, PATM_ACTION_MAGIC
1030 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
1031 pop ecx
1032 pop eax
1033%endif
1034
1035 pushfd
1036 push eax
1037 mov eax, dword [esp+8]
1038 and eax, PATM_FLAGS_MASK
1039 or eax, dword [ss:PATM_VMFLAGS]
1040 mov dword [esp+8], eax
1041 pop eax
1042 popfd
1043 mov dword [ss:PATM_INTERRUPTFLAG], 1
1044ENDPROC PATMPushf32Replacement
1045
1046; Patch record for 'pushfd'
1047BEGIN_PATCH_RODATA_SECTION
1048GLOBALNAME g_patmPushf32Record
1049%ifdef PATM_LOG_PATCHINSTR
1050 PATCHASMRECORD_INIT PATMPushf32Replacement, 4
1051%else
1052 PATCHASMRECORD_INIT PATMPushf32Replacement, 3
1053%endif
1054 DD PATM_INTERRUPTFLAG, 0
1055%ifdef PATM_LOG_PATCHINSTR
1056 DD PATM_PENDINGACTION, 0
1057%endif
1058 DD PATM_VMFLAGS, 0
1059 DD PATM_INTERRUPTFLAG, 0
1060 DD 0ffffffffh, 0ffffffffh
1061
1062
1063;
1064;
1065;
1066BEGIN_PATCH_CODE_SECTION
1067BEGINPROC PATMPushf16Replacement
1068 mov dword [ss:PATM_INTERRUPTFLAG], 0
1069 DB 0x66 ; size override
1070 pushf
1071 DB 0x66 ; size override
1072 pushf
1073 push eax
1074 xor eax, eax
1075 mov ax, word [esp+6]
1076 and eax, PATM_FLAGS_MASK
1077 or eax, dword [ss:PATM_VMFLAGS]
1078 mov word [esp+6], ax
1079 pop eax
1080
1081 DB 0x66 ; size override
1082 popf
1083 mov dword [ss:PATM_INTERRUPTFLAG], 1
1084ENDPROC PATMPushf16Replacement
1085
1086; Patch record for 'pushf'
1087BEGIN_PATCH_RODATA_SECTION
1088GLOBALNAME g_patmPushf16Record
1089 PATCHASMRECORD_INIT PATMPushf16Replacement, 3
1090 DD PATM_INTERRUPTFLAG, 0
1091 DD PATM_VMFLAGS, 0
1092 DD PATM_INTERRUPTFLAG, 0
1093 DD 0ffffffffh, 0ffffffffh
1094
1095
1096;
1097;
1098;
1099BEGIN_PATCH_CODE_SECTION
1100BEGINPROC PATMPushCSReplacement
1101 mov dword [ss:PATM_INTERRUPTFLAG], 0
1102 push cs
1103 pushfd
1104
1105 test dword [esp+4], 2
1106 jnz pushcs_notring1
1107
1108 ; change dpl from 1 to 0
1109 and dword [esp+4], dword ~1 ; yasm / nasm dword
1110
1111pushcs_notring1:
1112 popfd
1113
1114 mov dword [ss:PATM_INTERRUPTFLAG], 1
1115 DB 0xE9
1116PATMPushCSJump:
1117 DD PATM_JUMPDELTA
1118ENDPROC PATMPushCSReplacement
1119
1120; Patch record for 'push cs'
1121BEGIN_PATCH_RODATA_SECTION
1122GLOBALNAME g_patmPushCSRecord
1123 PATCHASMRECORD_INIT_JUMP PATMPushCSReplacement, PATMPushCSJump, 2
1124 DD PATM_INTERRUPTFLAG, 0
1125 DD PATM_INTERRUPTFLAG, 0
1126 DD 0ffffffffh, 0ffffffffh
1127
1128
1129;
1130;
1131;****************************************************
1132; Abstract:
1133;
1134; if eflags.NT==0 && iretstack.eflags.VM==0 && iretstack.eflags.IOPL==0
1135; then
1136; if return to ring 0 (iretstack.new_cs & 3 == 0)
1137; then
1138; if iretstack.new_eflags.IF == 1 && iretstack.new_eflags.IOPL == 0
1139; then
1140; iretstack.new_cs |= 1
1141; else
1142; int 3
1143; endif
1144; uVMFlags &= ~X86_EFL_IF
1145; iret
1146; else
1147; int 3
1148;****************************************************
1149;
1150; Stack:
1151;
1152; esp + 32 - GS (V86 only)
1153; esp + 28 - FS (V86 only)
1154; esp + 24 - DS (V86 only)
1155; esp + 20 - ES (V86 only)
1156; esp + 16 - SS (if transfer to outer ring)
1157; esp + 12 - ESP (if transfer to outer ring)
1158; esp + 8 - EFLAGS
1159; esp + 4 - CS
1160; esp - EIP
1161;;
1162BEGIN_PATCH_CODE_SECTION
1163BEGINPROC PATMIretReplacement
1164 mov dword [ss:PATM_INTERRUPTFLAG], 0
1165 pushfd
1166
1167%ifdef PATM_LOG_PATCHIRET
1168 push eax
1169 push ecx
1170 push edx
1171 lea edx, dword [ss:esp+12+4] ;3 dwords + pushed flags -> iret eip
1172 mov eax, PATM_ACTION_LOG_IRET
1173 lock or dword [ss:PATM_PENDINGACTION], eax
1174 mov ecx, PATM_ACTION_MAGIC
1175 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
1176 pop edx
1177 pop ecx
1178 pop eax
1179%endif
1180
1181 test dword [esp], X86_EFL_NT
1182 jnz near iret_fault1
1183
1184 ; we can't do an iret to v86 code, as we run with CPL=1. The iret would attempt a protected mode iret and (most likely) fault.
1185 test dword [esp+12], X86_EFL_VM
1186 jnz near iret_return_to_v86
1187
1188 ;;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1189 ;;@todo: not correct for iret back to ring 2!!!!!
1190 ;;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1191
1192 test dword [esp+8], 2
1193 jnz iret_notring0
1194
1195 test dword [esp+12], X86_EFL_IF
1196 jz near iret_clearIF
1197
1198 ; force ring 1 CS RPL
1199 or dword [esp+8], 1 ;-> @todo we leave traces or raw mode if we jump back to the host context to handle pending interrupts! (below)
1200iret_notring0:
1201
1202; if interrupts are pending, then we must go back to the host context to handle them!
1203; Note: This is very important as pending pic interrupts can be overridden by apic interrupts if we don't check early enough (Fedora 5 boot)
1204; @@todo fix this properly, so we can dispatch pending interrupts in GC
1205 test dword [ss:PATM_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC
1206 jz iret_continue
1207
1208; Go to our hypervisor trap handler to dispatch the pending irq
1209 mov dword [ss:PATM_TEMP_EAX], eax
1210 mov dword [ss:PATM_TEMP_ECX], ecx
1211 mov dword [ss:PATM_TEMP_EDI], edi
1212 mov dword [ss:PATM_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
1213 mov eax, PATM_ACTION_PENDING_IRQ_AFTER_IRET
1214 lock or dword [ss:PATM_PENDINGACTION], eax
1215 mov ecx, PATM_ACTION_MAGIC
1216 mov edi, PATM_CURINSTRADDR
1217
1218 popfd
1219 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
1220 ; does not return
1221
1222iret_continue :
1223 ; This section must *always* be executed (!!)
1224 ; Extract the IOPL from the return flags, save them to our virtual flags and
1225 ; put them back to zero
1226 ; @note we assume iretd doesn't fault!!!
1227 push eax
1228 mov eax, dword [esp+16]
1229 and eax, X86_EFL_IOPL
1230 and dword [ss:PATM_VMFLAGS], ~X86_EFL_IOPL
1231 or dword [ss:PATM_VMFLAGS], eax
1232 pop eax
1233 and dword [esp+12], ~X86_EFL_IOPL
1234
1235 ; Set IF again; below we make sure this won't cause problems.
1236 or dword [ss:PATM_VMFLAGS], X86_EFL_IF
1237
1238 ; make sure iret is executed fully (including the iret below; cli ... iret can otherwise be interrupted)
1239 mov dword [ss:PATM_INHIBITIRQADDR], PATM_CURINSTRADDR
1240
1241 popfd
1242 mov dword [ss:PATM_INTERRUPTFLAG], 1
1243 iretd
1244 PATM_INT3
1245
1246iret_fault:
1247 popfd
1248 mov dword [ss:PATM_INTERRUPTFLAG], 1
1249 PATM_INT3
1250
1251iret_fault1:
1252 nop
1253 popfd
1254 mov dword [ss:PATM_INTERRUPTFLAG], 1
1255 PATM_INT3
1256
1257iret_clearIF:
1258 push dword [esp+4] ; eip to return to
1259 pushfd
1260 push eax
1261 push PATM_FIXUP
1262 DB 0E8h ; call
1263 DD PATM_IRET_FUNCTION
1264 add esp, 4 ; pushed address of jump table
1265
1266 cmp eax, 0
1267 je near iret_fault3
1268
1269 mov dword [esp+12+4], eax ; stored eip in iret frame
1270 pop eax
1271 popfd
1272 add esp, 4 ; pushed eip
1273
1274 ; always ring 0 return -> change to ring 1 (CS in iret frame)
1275 or dword [esp+8], 1
1276
1277 ; This section must *always* be executed (!!)
1278 ; Extract the IOPL from the return flags, save them to our virtual flags and
1279 ; put them back to zero
1280 push eax
1281 mov eax, dword [esp+16]
1282 and eax, X86_EFL_IOPL
1283 and dword [ss:PATM_VMFLAGS], ~X86_EFL_IOPL
1284 or dword [ss:PATM_VMFLAGS], eax
1285 pop eax
1286 and dword [esp+12], ~X86_EFL_IOPL
1287
1288 ; Clear IF
1289 and dword [ss:PATM_VMFLAGS], ~X86_EFL_IF
1290 popfd
1291
1292 ; the patched destination code will set PATM_INTERRUPTFLAG after the return!
1293 iretd
1294
1295iret_return_to_v86:
1296 test dword [esp+12], X86_EFL_IF
1297 jz iret_fault
1298
1299 ; Go to our hypervisor trap handler to perform the iret to v86 code
1300 mov dword [ss:PATM_TEMP_EAX], eax
1301 mov dword [ss:PATM_TEMP_ECX], ecx
1302 mov dword [ss:PATM_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX
1303 mov eax, PATM_ACTION_DO_V86_IRET
1304 lock or dword [ss:PATM_PENDINGACTION], eax
1305 mov ecx, PATM_ACTION_MAGIC
1306
1307 popfd
1308
1309 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
1310 ; does not return
1311
1312
1313iret_fault3:
1314 pop eax
1315 popfd
1316 add esp, 4 ; pushed eip
1317 jmp iret_fault
1318
1319align 4
1320PATMIretTable:
1321 DW PATM_MAX_JUMPTABLE_ENTRIES ; nrSlots
1322 DW 0 ; ulInsertPos
1323 DD 0 ; cAddresses
1324 TIMES PATCHJUMPTABLE_SIZE DB 0 ; lookup slots
1325
1326ENDPROC PATMIretReplacement
1327
1328; Patch record for 'iretd'
1329BEGIN_PATCH_RODATA_SECTION
1330GLOBALNAME g_patmIretRecord
1331%ifdef PATM_LOG_PATCHIRET
1332 PATCHASMRECORD_INIT PATMIretReplacement, 26
1333%else
1334 PATCHASMRECORD_INIT PATMIretReplacement, 25
1335%endif
1336 DD PATM_INTERRUPTFLAG, 0
1337%ifdef PATM_LOG_PATCHIRET
1338 DD PATM_PENDINGACTION, 0
1339%endif
1340 DD PATM_VM_FORCEDACTIONS, 0
1341 DD PATM_TEMP_EAX, 0
1342 DD PATM_TEMP_ECX, 0
1343 DD PATM_TEMP_EDI, 0
1344 DD PATM_TEMP_RESTORE_FLAGS, 0
1345 DD PATM_PENDINGACTION, 0
1346 DD PATM_CURINSTRADDR, 0
1347 DD PATM_VMFLAGS, 0
1348 DD PATM_VMFLAGS, 0
1349 DD PATM_VMFLAGS, 0
1350 DD PATM_INHIBITIRQADDR, 0
1351 DD PATM_CURINSTRADDR, 0
1352 DD PATM_INTERRUPTFLAG, 0
1353 DD PATM_INTERRUPTFLAG, 0
1354 DD PATM_INTERRUPTFLAG, 0
1355 DD PATM_FIXUP, PATMIretTable - NAME(PATMIretReplacement)
1356 DD PATM_IRET_FUNCTION, 0
1357 DD PATM_VMFLAGS, 0
1358 DD PATM_VMFLAGS, 0
1359 DD PATM_VMFLAGS, 0
1360 DD PATM_TEMP_EAX, 0
1361 DD PATM_TEMP_ECX, 0
1362 DD PATM_TEMP_RESTORE_FLAGS, 0
1363 DD PATM_PENDINGACTION, 0
1364 DD 0ffffffffh, 0ffffffffh
1365
1366
1367;
1368;
1369;****************************************************
1370; Abstract:
1371;
1372; if eflags.NT==0 && iretstack.eflags.VM==0 && iretstack.eflags.IOPL==0
1373; then
1374; if return to ring 0 (iretstack.new_cs & 3 == 0)
1375; then
1376; if iretstack.new_eflags.IF == 1 && iretstack.new_eflags.IOPL == 0
1377; then
1378; iretstack.new_cs |= 1
1379; else
1380; int 3
1381; endif
1382; uVMFlags &= ~X86_EFL_IF
1383; iret
1384; else
1385; int 3
1386;****************************************************
1387;
1388; Stack:
1389;
1390; esp + 32 - GS (V86 only)
1391; esp + 28 - FS (V86 only)
1392; esp + 24 - DS (V86 only)
1393; esp + 20 - ES (V86 only)
1394; esp + 16 - SS (if transfer to outer ring)
1395; esp + 12 - ESP (if transfer to outer ring)
1396; esp + 8 - EFLAGS
1397; esp + 4 - CS
1398; esp - EIP
1399;
1400BEGIN_PATCH_CODE_SECTION
1401BEGINPROC PATMIretRing1Replacement
1402 mov dword [ss:PATM_INTERRUPTFLAG], 0
1403 pushfd
1404
1405%ifdef PATM_LOG_PATCHIRET
1406 push eax
1407 push ecx
1408 push edx
1409 lea edx, dword [ss:esp+12+4] ;3 dwords + pushed flags -> iret eip
1410 mov eax, PATM_ACTION_LOG_IRET
1411 lock or dword [ss:PATM_PENDINGACTION], eax
1412 mov ecx, PATM_ACTION_MAGIC
1413 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
1414 pop edx
1415 pop ecx
1416 pop eax
1417%endif
1418
1419 test dword [esp], X86_EFL_NT
1420 jnz near iretring1_fault1
1421
1422 ; we can't do an iret to v86 code, as we run with CPL=1. The iret would attempt a protected mode iret and (most likely) fault.
1423 test dword [esp+12], X86_EFL_VM
1424 jnz near iretring1_return_to_v86
1425
1426 ;;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1427 ;;@todo: not correct for iret back to ring 2!!!!!
1428 ;;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1429
1430 test dword [esp+8], 2
1431 jnz iretring1_checkpendingirq
1432
1433 test dword [esp+12], X86_EFL_IF
1434 jz near iretring1_clearIF
1435
1436iretring1_checkpendingirq:
1437
1438; if interrupts are pending, then we must go back to the host context to handle them!
1439; Note: This is very important as pending pic interrupts can be overridden by apic interrupts if we don't check early enough (Fedora 5 boot)
1440; @@todo fix this properly, so we can dispatch pending interrupts in GC
1441 test dword [ss:PATM_VM_FORCEDACTIONS], VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC
1442 jz iretring1_continue
1443
1444; Go to our hypervisor trap handler to dispatch the pending irq
1445 mov dword [ss:PATM_TEMP_EAX], eax
1446 mov dword [ss:PATM_TEMP_ECX], ecx
1447 mov dword [ss:PATM_TEMP_EDI], edi
1448 mov dword [ss:PATM_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX | PATM_RESTORE_EDI
1449 mov eax, PATM_ACTION_PENDING_IRQ_AFTER_IRET
1450 lock or dword [ss:PATM_PENDINGACTION], eax
1451 mov ecx, PATM_ACTION_MAGIC
1452 mov edi, PATM_CURINSTRADDR
1453
1454 popfd
1455 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
1456 ; does not return
1457
1458iretring1_continue:
1459
1460 test dword [esp+8], 2
1461 jnz iretring1_notring01
1462
1463 test dword [esp+8], 1
1464 jz iretring1_ring0
1465
1466 ; ring 1 return change CS & SS RPL to 2 from 1
1467 and dword [esp+8], ~1 ; CS
1468 or dword [esp+8], 2
1469
1470 and dword [esp+20], ~1 ; SS
1471 or dword [esp+20], 2
1472
1473 jmp short iretring1_notring01
1474iretring1_ring0:
1475 ; force ring 1 CS RPL
1476 or dword [esp+8], 1
1477
1478iretring1_notring01:
1479 ; This section must *always* be executed (!!)
1480 ; Extract the IOPL from the return flags, save them to our virtual flags and
1481 ; put them back to zero
1482 ; @note we assume iretd doesn't fault!!!
1483 push eax
1484 mov eax, dword [esp+16]
1485 and eax, X86_EFL_IOPL
1486 and dword [ss:PATM_VMFLAGS], ~X86_EFL_IOPL
1487 or dword [ss:PATM_VMFLAGS], eax
1488 pop eax
1489 and dword [esp+12], ~X86_EFL_IOPL
1490
1491 ; Set IF again; below we make sure this won't cause problems.
1492 or dword [ss:PATM_VMFLAGS], X86_EFL_IF
1493
1494 ; make sure iret is executed fully (including the iret below; cli ... iret can otherwise be interrupted)
1495 mov dword [ss:PATM_INHIBITIRQADDR], PATM_CURINSTRADDR
1496
1497 popfd
1498 mov dword [ss:PATM_INTERRUPTFLAG], 1
1499 iretd
1500 PATM_INT3
1501
1502iretring1_fault:
1503 popfd
1504 mov dword [ss:PATM_INTERRUPTFLAG], 1
1505 PATM_INT3
1506
1507iretring1_fault1:
1508 nop
1509 popfd
1510 mov dword [ss:PATM_INTERRUPTFLAG], 1
1511 PATM_INT3
1512
1513iretring1_clearIF:
1514 push dword [esp+4] ; eip to return to
1515 pushfd
1516 push eax
1517 push PATM_FIXUP
1518 DB 0E8h ; call
1519 DD PATM_IRET_FUNCTION
1520 add esp, 4 ; pushed address of jump table
1521
1522 cmp eax, 0
1523 je near iretring1_fault3
1524
1525 mov dword [esp+12+4], eax ; stored eip in iret frame
1526 pop eax
1527 popfd
1528 add esp, 4 ; pushed eip
1529
1530 ; This section must *always* be executed (!!)
1531 ; Extract the IOPL from the return flags, save them to our virtual flags and
1532 ; put them back to zero
1533 push eax
1534 mov eax, dword [esp+16]
1535 and eax, X86_EFL_IOPL
1536 and dword [ss:PATM_VMFLAGS], ~X86_EFL_IOPL
1537 or dword [ss:PATM_VMFLAGS], eax
1538 pop eax
1539 and dword [esp+12], ~X86_EFL_IOPL
1540
1541 ; Clear IF
1542 and dword [ss:PATM_VMFLAGS], ~X86_EFL_IF
1543 popfd
1544
1545 test dword [esp+8], 1
1546 jz iretring1_clearIF_ring0
1547
1548 ; ring 1 return change CS & SS RPL to 2 from 1
1549 and dword [esp+8], ~1 ; CS
1550 or dword [esp+8], 2
1551
1552 and dword [esp+20], ~1 ; SS
1553 or dword [esp+20], 2
1554 ; the patched destination code will set PATM_INTERRUPTFLAG after the return!
1555 iretd
1556
1557iretring1_clearIF_ring0:
1558 ; force ring 1 CS RPL
1559 or dword [esp+8], 1
1560 ; the patched destination code will set PATM_INTERRUPTFLAG after the return!
1561 iretd
1562
1563iretring1_return_to_v86:
1564 test dword [esp+12], X86_EFL_IF
1565 jz iretring1_fault
1566
1567 ; Go to our hypervisor trap handler to perform the iret to v86 code
1568 mov dword [ss:PATM_TEMP_EAX], eax
1569 mov dword [ss:PATM_TEMP_ECX], ecx
1570 mov dword [ss:PATM_TEMP_RESTORE_FLAGS], PATM_RESTORE_EAX | PATM_RESTORE_ECX
1571 mov eax, PATM_ACTION_DO_V86_IRET
1572 lock or dword [ss:PATM_PENDINGACTION], eax
1573 mov ecx, PATM_ACTION_MAGIC
1574
1575 popfd
1576
1577 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
1578 ; does not return
1579
1580
1581iretring1_fault3:
1582 pop eax
1583 popfd
1584 add esp, 4 ; pushed eip
1585 jmp iretring1_fault
1586
1587align 4
1588PATMIretRing1Table:
1589 DW PATM_MAX_JUMPTABLE_ENTRIES ; nrSlots
1590 DW 0 ; ulInsertPos
1591 DD 0 ; cAddresses
1592 TIMES PATCHJUMPTABLE_SIZE DB 0 ; lookup slots
1593
1594ENDPROC PATMIretRing1Replacement
1595
1596; Patch record for 'iretd'
1597BEGIN_PATCH_RODATA_SECTION
1598GLOBALNAME g_patmIretRing1Record
1599%ifdef PATM_LOG_PATCHIRET
1600 PATCHASMRECORD_INIT PATMIretRing1Replacement, 26
1601%else
1602 PATCHASMRECORD_INIT PATMIretRing1Replacement, 25
1603%endif
1604 DD PATM_INTERRUPTFLAG, 0
1605%ifdef PATM_LOG_PATCHIRET
1606 DD PATM_PENDINGACTION, 0
1607%endif
1608 DD PATM_VM_FORCEDACTIONS, 0
1609 DD PATM_TEMP_EAX, 0
1610 DD PATM_TEMP_ECX, 0
1611 DD PATM_TEMP_EDI, 0
1612 DD PATM_TEMP_RESTORE_FLAGS, 0
1613 DD PATM_PENDINGACTION, 0
1614 DD PATM_CURINSTRADDR, 0
1615 DD PATM_VMFLAGS, 0
1616 DD PATM_VMFLAGS, 0
1617 DD PATM_VMFLAGS, 0
1618 DD PATM_INHIBITIRQADDR, 0
1619 DD PATM_CURINSTRADDR, 0
1620 DD PATM_INTERRUPTFLAG, 0
1621 DD PATM_INTERRUPTFLAG, 0
1622 DD PATM_INTERRUPTFLAG, 0
1623 DD PATM_FIXUP, PATMIretRing1Table - NAME(PATMIretRing1Replacement)
1624 DD PATM_IRET_FUNCTION, 0
1625 DD PATM_VMFLAGS, 0
1626 DD PATM_VMFLAGS, 0
1627 DD PATM_VMFLAGS, 0
1628 DD PATM_TEMP_EAX, 0
1629 DD PATM_TEMP_ECX, 0
1630 DD PATM_TEMP_RESTORE_FLAGS, 0
1631 DD PATM_PENDINGACTION, 0
1632 DD 0ffffffffh, 0ffffffffh
1633
1634
1635;
1636; global function for implementing 'iret' to code with IF cleared
1637;
1638; Caller is responsible for right stack layout
1639; + 16 original return address
1640; + 12 eflags
1641; + 8 eax
1642; + 4 Jump table address
1643;( + 0 return address )
1644;
1645; @note assumes PATM_INTERRUPTFLAG is zero
1646; @note assumes it can trash eax and eflags
1647;
1648; @returns eax=0 on failure
1649; otherwise return address in eax
1650;
1651; @note NEVER change this without bumping the SSM version
1652BEGIN_PATCH_CODE_SECTION
1653BEGINPROC PATMIretFunction
1654 push ecx
1655 push edx
1656 push edi
1657
1658 ; Event order:
1659 ; 1) Check if the return patch address can be found in the lookup table
1660 ; 2) Query return patch address from the hypervisor
1661
1662 ; 1) Check if the return patch address can be found in the lookup table
1663 mov edx, dword [esp+12+16] ; pushed target address
1664
1665 xor eax, eax ; default result -> nothing found
1666 mov edi, dword [esp+12+4] ; jump table
1667 mov ecx, [ss:edi + PATCHJUMPTABLE.cAddresses]
1668 cmp ecx, 0
1669 je near PATMIretFunction_AskHypervisor
1670
1671PATMIretFunction_SearchStart:
1672 cmp [ss:edi + PATCHJUMPTABLE.Slot_pInstrGC + eax*8], edx ; edx = GC address to search for
1673 je near PATMIretFunction_SearchHit
1674 inc eax
1675 cmp eax, ecx
1676 jl near PATMIretFunction_SearchStart
1677
1678PATMIretFunction_AskHypervisor:
1679 ; 2) Query return patch address from the hypervisor
1680 ; @todo private ugly interface, since we have nothing generic at the moment
1681 lock or dword [ss:PATM_PENDINGACTION], PATM_ACTION_LOOKUP_ADDRESS
1682 mov eax, PATM_ACTION_LOOKUP_ADDRESS
1683 mov ecx, PATM_ACTION_MAGIC
1684 mov edi, dword [esp+12+4] ; jump table address
1685 mov edx, dword [esp+12+16] ; original return address
1686 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
1687 jmp near PATMIretFunction_SearchEnd
1688
1689PATMIretFunction_SearchHit:
1690 mov eax, [ss:edi + PATCHJUMPTABLE.Slot_pRelPatchGC + eax*8] ; found a match!
1691 ;@note can be zero, so the next check is required!!
1692
1693PATMIretFunction_SearchEnd:
1694 cmp eax, 0
1695 jz PATMIretFunction_Failure
1696
1697 add eax, PATM_PATCHBASE
1698
1699 pop edi
1700 pop edx
1701 pop ecx
1702 ret
1703
1704PATMIretFunction_Failure:
1705 ;signal error
1706 xor eax, eax
1707 pop edi
1708 pop edx
1709 pop ecx
1710 ret
1711ENDPROC PATMIretFunction
1712
1713BEGIN_PATCH_RODATA_SECTION
1714GLOBALNAME g_patmIretFunctionRecord
1715 PATCHASMRECORD_INIT PATMIretFunction, 2
1716 DD PATM_PENDINGACTION, 0
1717 DD PATM_PATCHBASE, 0
1718 DD 0ffffffffh, 0ffffffffh
1719
1720
1721;
1722; PATMCpuidReplacement
1723;
1724BEGIN_PATCH g_patmCpuidRecord, PATMCpuidReplacement
1725 mov dword [ss:PATM_INTERRUPTFLAG], 0
1726PATCH_FIXUP PATM_INTERRUPTFLAG
1727 pushf
1728
1729 cmp eax, PATM_CPUID_STD_MAX
1730PATCH_FIXUP PATM_CPUID_STD_MAX
1731 jb cpuid_std
1732 cmp eax, 0x80000000
1733 jb cpuid_def
1734 cmp eax, PATM_CPUID_EXT_MAX
1735PATCH_FIXUP PATM_CPUID_EXT_MAX
1736 jb cpuid_ext
1737 cmp eax, 0xc0000000
1738 jb cpuid_def
1739 cmp eax, PATM_CPUID_CENTAUR_MAX
1740PATCH_FIXUP PATM_CPUID_CENTAUR_MAX
1741 jb cpuid_centaur
1742
1743cpuid_def:
1744 mov eax, PATM_CPUID_DEF_PTR
1745PATCH_FIXUP PATM_CPUID_DEF_PTR
1746 jmp cpuid_fetch
1747
1748cpuid_std:
1749 mov edx, PATM_CPUID_STD_PTR
1750PATCH_FIXUP PATM_CPUID_STD_PTR
1751 jmp cpuid_calc
1752
1753cpuid_ext:
1754 and eax, 0ffh
1755 mov edx, PATM_CPUID_EXT_PTR
1756PATCH_FIXUP PATM_CPUID_EXT_PTR
1757 jmp cpuid_calc
1758
1759cpuid_centaur:
1760 and eax, 0ffh
1761 mov edx, PATM_CPUID_CENTAUR_PTR
1762PATCH_FIXUP PATM_CPUID_CENTAUR_PTR
1763
1764cpuid_calc:
1765 lea eax, [ss:eax * 4] ; 4 entries...
1766 lea eax, [ss:eax * 4] ; 4 bytes each
1767 add eax, edx
1768
1769cpuid_fetch:
1770 mov edx, [ss:eax + 12] ; CPUMCPUID layout assumptions!
1771 mov ecx, [ss:eax + 8]
1772 mov ebx, [ss:eax + 4]
1773 mov eax, [ss:eax]
1774
1775 popf
1776 mov dword [ss:PATM_INTERRUPTFLAG], 1
1777PATCH_FIXUP PATM_INTERRUPTFLAG
1778END_PATCH g_patmCpuidRecord, PATMCpuidReplacement
1779
1780
1781;
1782;
1783;
1784BEGIN_PATCH_CODE_SECTION
1785BEGINPROC PATMJEcxReplacement
1786 mov dword [ss:PATM_INTERRUPTFLAG], 0
1787 pushfd
1788PATMJEcxSizeOverride:
1789 DB 0x90 ; nop
1790 cmp ecx, dword 0 ; yasm / nasm dword
1791 jnz PATMJEcxContinue
1792
1793 popfd
1794 mov dword [ss:PATM_INTERRUPTFLAG], 1
1795 DB 0xE9
1796PATMJEcxJump:
1797 DD PATM_JUMPDELTA
1798
1799PATMJEcxContinue:
1800 popfd
1801 mov dword [ss:PATM_INTERRUPTFLAG], 1
1802ENDPROC PATMJEcxReplacement
1803
1804; Patch record for 'JEcx'
1805BEGIN_PATCH_RODATA_SECTION
1806GLOBALNAME g_patmJEcxRecord
1807 PATCHASMRECORD_INIT_EX PATMJEcxReplacement, , PATMJEcxJump, PATMJEcxSizeOverride, 3
1808 DD PATM_INTERRUPTFLAG, 0
1809 DD PATM_INTERRUPTFLAG, 0
1810 DD PATM_INTERRUPTFLAG, 0
1811 DD 0ffffffffh, 0ffffffffh
1812
1813
1814;
1815;
1816;
1817BEGIN_PATCH_CODE_SECTION
1818BEGINPROC PATMLoopReplacement
1819 mov dword [ss:PATM_INTERRUPTFLAG], 0
1820 pushfd
1821PATMLoopSizeOverride:
1822 DB 0x90 ; nop
1823 dec ecx
1824 jz PATMLoopContinue
1825
1826 popfd
1827 mov dword [ss:PATM_INTERRUPTFLAG], 1
1828 DB 0xE9
1829PATMLoopJump:
1830 DD PATM_JUMPDELTA
1831
1832PATMLoopContinue:
1833 popfd
1834 mov dword [ss:PATM_INTERRUPTFLAG], 1
1835ENDPROC PATMLoopReplacement
1836
1837; Patch record for 'Loop'
1838BEGIN_PATCH_RODATA_SECTION
1839GLOBALNAME g_patmLoopRecord
1840 PATCHASMRECORD_INIT_EX PATMLoopReplacement, , PATMLoopJump, PATMLoopSizeOverride, 3
1841 DD PATM_INTERRUPTFLAG, 0
1842 DD PATM_INTERRUPTFLAG, 0
1843 DD PATM_INTERRUPTFLAG, 0
1844 DD 0ffffffffh, 0ffffffffh
1845
1846
1847;
1848; jump if ZF=1 AND (E)CX != 0
1849;
1850BEGIN_PATCH_CODE_SECTION
1851BEGINPROC PATMLoopZReplacement
1852 mov dword [ss:PATM_INTERRUPTFLAG], 0
1853 jnz NAME(PATMLoopZReplacement_EndProc)
1854 pushfd
1855PATMLoopZSizeOverride:
1856 DB 0x90 ; nop
1857 dec ecx
1858 jz PATMLoopZContinue
1859
1860 popfd
1861 mov dword [ss:PATM_INTERRUPTFLAG], 1
1862 DB 0xE9
1863PATMLoopZJump:
1864 DD PATM_JUMPDELTA
1865
1866PATMLoopZContinue:
1867 popfd
1868 mov dword [ss:PATM_INTERRUPTFLAG], 1
1869ENDPROC PATMLoopZReplacement
1870
1871; Patch record for 'Loopz'
1872BEGIN_PATCH_RODATA_SECTION
1873GLOBALNAME g_patmLoopZRecord
1874 PATCHASMRECORD_INIT_EX PATMLoopZReplacement, , PATMLoopZJump, PATMLoopZSizeOverride, 3
1875 DD PATM_INTERRUPTFLAG, 0
1876 DD PATM_INTERRUPTFLAG, 0
1877 DD PATM_INTERRUPTFLAG, 0
1878 DD 0ffffffffh, 0ffffffffh
1879
1880
1881;
1882; jump if ZF=0 AND (E)CX != 0
1883;
1884BEGIN_PATCH_CODE_SECTION
1885BEGINPROC PATMLoopNZReplacement
1886 mov dword [ss:PATM_INTERRUPTFLAG], 0
1887 jz NAME(PATMLoopNZReplacement_EndProc)
1888 pushfd
1889PATMLoopNZSizeOverride:
1890 DB 0x90 ; nop
1891 dec ecx
1892 jz PATMLoopNZContinue
1893
1894 popfd
1895 mov dword [ss:PATM_INTERRUPTFLAG], 1
1896 DB 0xE9
1897PATMLoopNZJump:
1898 DD PATM_JUMPDELTA
1899
1900PATMLoopNZContinue:
1901 popfd
1902 mov dword [ss:PATM_INTERRUPTFLAG], 1
1903ENDPROC PATMLoopNZReplacement
1904
1905; Patch record for 'LoopNZ'
1906BEGIN_PATCH_RODATA_SECTION
1907GLOBALNAME g_patmLoopNZRecord
1908 PATCHASMRECORD_INIT_EX PATMLoopNZReplacement, , PATMLoopNZJump, PATMLoopNZSizeOverride, 3
1909 DD PATM_INTERRUPTFLAG, 0
1910 DD PATM_INTERRUPTFLAG, 0
1911 DD PATM_INTERRUPTFLAG, 0
1912 DD 0ffffffffh, 0ffffffffh
1913
1914
1915;
1916; Global patch function for indirect calls
1917; Caller is responsible for clearing PATM_INTERRUPTFLAG and doing:
1918; + 20 push [pTargetGC]
1919; + 16 pushfd
1920; + 12 push [JumpTableAddress]
1921; + 8 push [PATMRelReturnAddress]
1922; + 4 push [GuestReturnAddress]
1923;( + 0 return address )
1924;
1925; @note NEVER change this without bumping the SSM version
1926;
1927BEGIN_PATCH_CODE_SECTION
1928BEGINPROC PATMLookupAndCall
1929 push eax
1930 push edx
1931 push edi
1932 push ecx
1933
1934 mov eax, dword [esp+16+4] ; guest return address
1935 mov dword [ss:PATM_CALL_RETURN_ADDR], eax ; temporary storage
1936
1937 mov edx, dword [esp+16+20] ; pushed target address
1938
1939 xor eax, eax ; default result -> nothing found
1940 mov edi, dword [esp+16+12] ; jump table
1941 mov ecx, [ss:edi + PATCHJUMPTABLE.cAddresses]
1942 cmp ecx, 0
1943 je near PATMLookupAndCall_QueryPATM
1944
1945PATMLookupAndCall_SearchStart:
1946 cmp [ss:edi + PATCHJUMPTABLE.Slot_pInstrGC + eax*8], edx ; edx = GC address to search for
1947 je near PATMLookupAndCall_SearchHit
1948 inc eax
1949 cmp eax, ecx
1950 jl near PATMLookupAndCall_SearchStart
1951
1952PATMLookupAndCall_QueryPATM:
1953 ; nothing found -> let our trap handler try to find it
1954 ; @todo private ugly interface, since we have nothing generic at the moment
1955 lock or dword [ss:PATM_PENDINGACTION], PATM_ACTION_LOOKUP_ADDRESS
1956 mov eax, PATM_ACTION_LOOKUP_ADDRESS
1957 mov ecx, PATM_ACTION_MAGIC
1958 ; edx = GC address to find
1959 ; edi = jump table address
1960 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
1961
1962 jmp near PATMLookupAndCall_SearchEnd
1963
1964PATMLookupAndCall_Failure:
1965 ; return to caller; it must raise an error, due to patch to guest address translation (remember that there's only one copy of this code block).
1966 pop ecx
1967 pop edi
1968 pop edx
1969 pop eax
1970 ret
1971
1972PATMLookupAndCall_SearchHit:
1973 mov eax, [ss:edi + PATCHJUMPTABLE.Slot_pRelPatchGC + eax*8] ; found a match!
1974
1975 ;@note can be zero, so the next check is required!!
1976
1977PATMLookupAndCall_SearchEnd:
1978 cmp eax, 0
1979 je near PATMLookupAndCall_Failure
1980
1981 mov ecx, eax ; ECX = target address (relative!)
1982 add ecx, PATM_PATCHBASE ; Make it absolute
1983
1984 mov edx, dword PATM_STACKPTR
1985 cmp dword [ss:edx], PATM_STACK_SIZE
1986 ja near PATMLookupAndCall_Failure ; should never happen actually!!!
1987 cmp dword [ss:edx], 0
1988 je near PATMLookupAndCall_Failure ; no more room
1989
1990 ; save the patch return address on our private stack
1991 sub dword [ss:edx], 4 ; sizeof(RTGCPTR)
1992 mov eax, dword PATM_STACKBASE
1993 add eax, dword [ss:edx] ; stack base + stack position
1994 mov edi, dword [esp+16+8] ; PATM return address
1995 mov dword [ss:eax], edi ; relative address of patch return (instruction following this block)
1996
1997 ; save the original return address as well (checked by ret to make sure the guest hasn't messed around with the stack)
1998 mov edi, dword PATM_STACKBASE_GUEST
1999 add edi, dword [ss:edx] ; stack base (guest) + stack position
2000 mov eax, dword [esp+16+4] ; guest return address
2001 mov dword [ss:edi], eax
2002
2003 mov dword [ss:PATM_CALL_PATCH_TARGET_ADDR], ecx ; temporarily store the target address
2004 pop ecx
2005 pop edi
2006 pop edx
2007 pop eax
2008 add esp, 24 ; parameters + return address pushed by caller (changes the flags, but that shouldn't matter)
2009
2010%ifdef PATM_LOG_PATCHINSTR
2011 push eax
2012 push ecx
2013 push edx
2014 lea edx, [esp + 12 - 4] ; stack address to store return address
2015 lock or dword [ss:PATM_PENDINGACTION], PATM_ACTION_LOG_CALL
2016 mov eax, PATM_ACTION_LOG_CALL
2017 mov ecx, PATM_ACTION_MAGIC
2018 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
2019 pop edx
2020 pop ecx
2021 pop eax
2022%endif
2023
2024 push dword [ss:PATM_CALL_RETURN_ADDR] ; push original guest return address
2025
2026 ; the called function will set PATM_INTERRUPTFLAG (!!)
2027 jmp dword [ss:PATM_CALL_PATCH_TARGET_ADDR]
2028 ; returning here -> do not add code here or after the jmp!!!!!
2029ENDPROC PATMLookupAndCall
2030
2031; Patch record for indirect calls and jumps
2032BEGIN_PATCH_RODATA_SECTION
2033GLOBALNAME g_patmLookupAndCallRecord
2034%ifdef PATM_LOG_PATCHINSTR
2035 PATCHASMRECORD_INIT PATMLookupAndCall, 10
2036%else
2037 PATCHASMRECORD_INIT PATMLookupAndCall, 9
2038%endif
2039 DD PATM_CALL_RETURN_ADDR, 0
2040 DD PATM_PENDINGACTION, 0
2041 DD PATM_PATCHBASE, 0
2042 DD PATM_STACKPTR, 0
2043 DD PATM_STACKBASE, 0
2044 DD PATM_STACKBASE_GUEST, 0
2045 DD PATM_CALL_PATCH_TARGET_ADDR, 0
2046%ifdef PATM_LOG_PATCHINSTR
2047 DD PATM_PENDINGACTION, 0
2048%endif
2049 DD PATM_CALL_RETURN_ADDR, 0
2050 DD PATM_CALL_PATCH_TARGET_ADDR, 0
2051 DD 0ffffffffh, 0ffffffffh
2052
2053
2054;
2055; Global patch function for indirect jumps
2056; Caller is responsible for clearing PATM_INTERRUPTFLAG and doing:
2057; + 8 push [pTargetGC]
2058; + 4 push [JumpTableAddress]
2059;( + 0 return address )
2060; And saving eflags in PATM_TEMP_EFLAGS
2061;
2062; @note NEVER change this without bumping the SSM version
2063;
2064BEGIN_PATCH_CODE_SECTION
2065BEGINPROC PATMLookupAndJump
2066 push eax
2067 push edx
2068 push edi
2069 push ecx
2070
2071 mov edx, dword [esp+16+8] ; pushed target address
2072
2073 xor eax, eax ; default result -> nothing found
2074 mov edi, dword [esp+16+4] ; jump table
2075 mov ecx, [ss:edi + PATCHJUMPTABLE.cAddresses]
2076 cmp ecx, 0
2077 je near PATMLookupAndJump_QueryPATM
2078
2079PATMLookupAndJump_SearchStart:
2080 cmp [ss:edi + PATCHJUMPTABLE.Slot_pInstrGC + eax*8], edx ; edx = GC address to search for
2081 je near PATMLookupAndJump_SearchHit
2082 inc eax
2083 cmp eax, ecx
2084 jl near PATMLookupAndJump_SearchStart
2085
2086PATMLookupAndJump_QueryPATM:
2087 ; nothing found -> let our trap handler try to find it
2088 ; @todo private ugly interface, since we have nothing generic at the moment
2089 lock or dword [ss:PATM_PENDINGACTION], PATM_ACTION_LOOKUP_ADDRESS
2090 mov eax, PATM_ACTION_LOOKUP_ADDRESS
2091 mov ecx, PATM_ACTION_MAGIC
2092 ; edx = GC address to find
2093 ; edi = jump table address
2094 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
2095
2096 jmp near PATMLookupAndJump_SearchEnd
2097
2098PATMLookupAndJump_Failure:
2099 ; return to caller; it must raise an error, due to patch to guest address translation (remember that there's only one copy of this code block).
2100 pop ecx
2101 pop edi
2102 pop edx
2103 pop eax
2104 ret
2105
2106PATMLookupAndJump_SearchHit:
2107 mov eax, [ss:edi + PATCHJUMPTABLE.Slot_pRelPatchGC + eax*8] ; found a match!
2108
2109 ;@note can be zero, so the next check is required!!
2110
2111PATMLookupAndJump_SearchEnd:
2112 cmp eax, 0
2113 je near PATMLookupAndJump_Failure
2114
2115 mov ecx, eax ; ECX = target address (relative!)
2116 add ecx, PATM_PATCHBASE ; Make it absolute
2117
2118 ; save jump patch target
2119 mov dword [ss:PATM_TEMP_EAX], ecx
2120 pop ecx
2121 pop edi
2122 pop edx
2123 pop eax
2124 add esp, 12 ; parameters + return address pushed by caller
2125 ; restore flags (just to be sure)
2126 push dword [ss:PATM_TEMP_EFLAGS]
2127 popfd
2128
2129 ; the jump destination will set PATM_INTERRUPTFLAG (!!)
2130 jmp dword [ss:PATM_TEMP_EAX] ; call duplicated patch destination address
2131ENDPROC PATMLookupAndJump
2132
2133; Patch record for indirect calls and jumps
2134BEGIN_PATCH_RODATA_SECTION
2135GLOBALNAME g_patmLookupAndJumpRecord
2136 PATCHASMRECORD_INIT PATMLookupAndJump, 5
2137 DD PATM_PENDINGACTION, 0
2138 DD PATM_PATCHBASE, 0
2139 DD PATM_TEMP_EAX, 0
2140 DD PATM_TEMP_EFLAGS, 0
2141 DD PATM_TEMP_EAX, 0
2142 DD 0ffffffffh, 0ffffffffh
2143
2144
2145; Patch function for static calls
2146; @note static calls have only one lookup slot!
2147; Caller is responsible for clearing PATM_INTERRUPTFLAG and adding:
2148; push [pTargetGC]
2149;
2150BEGIN_PATCH_CODE_SECTION
2151BEGINPROC PATMCall
2152 pushfd
2153 push PATM_FIXUP ; fixup for jump table below
2154 push PATM_PATCHNEXTBLOCK
2155 push PATM_RETURNADDR
2156 DB 0E8h ; call
2157 DD PATM_LOOKUP_AND_CALL_FUNCTION
2158 ; we only return in case of a failure
2159 add esp, 12 ; pushed address of jump table
2160 popfd
2161 add esp, 4 ; pushed by caller (changes the flags, but that shouldn't matter (@todo))
2162 mov dword [ss:PATM_INTERRUPTFLAG], 1
2163 PATM_INT3
2164%ifdef DEBUG
2165 ; for disassembly
2166 jmp NAME(PATMCall_EndProc)
2167%endif
2168
2169align 4
2170PATMCallTable:
2171 DW 1 ; nrSlots
2172 DW 0 ; ulInsertPos
2173 DD 0 ; cAddresses
2174 TIMES PATCHDIRECTJUMPTABLE_SIZE DB 0 ; only one lookup slot
2175
2176 ; returning here -> do not add code here or after the jmp!!!!!
2177ENDPROC PATMCall
2178
2179; Patch record for direct calls
2180BEGIN_PATCH_RODATA_SECTION
2181GLOBALNAME g_patmCallRecord
2182 PATCHASMRECORD_INIT PATMCall, 5
2183 DD PATM_FIXUP, PATMCallTable - NAME(PATMCall)
2184 DD PATM_PATCHNEXTBLOCK, 0
2185 DD PATM_RETURNADDR, 0
2186 DD PATM_LOOKUP_AND_CALL_FUNCTION, 0
2187 DD PATM_INTERRUPTFLAG, 0
2188 DD 0ffffffffh, 0ffffffffh
2189
2190
2191; Patch function for indirect calls
2192; Caller is responsible for clearing PATM_INTERRUPTFLAG and adding:
2193; push [pTargetGC]
2194;
2195BEGIN_PATCH_CODE_SECTION
2196BEGINPROC PATMCallIndirect
2197 pushfd
2198 push PATM_FIXUP ; fixup for jump table below
2199 push PATM_PATCHNEXTBLOCK
2200 push PATM_RETURNADDR
2201 DB 0E8h ; call
2202 DD PATM_LOOKUP_AND_CALL_FUNCTION
2203 ; we only return in case of a failure
2204 add esp, 12 ; pushed address of jump table
2205 popfd
2206 add esp, 4 ; pushed by caller (changes the flags, but that shouldn't matter (@todo))
2207 mov dword [ss:PATM_INTERRUPTFLAG], 1
2208 PATM_INT3
2209%ifdef DEBUG
2210 ; for disassembly
2211 jmp NAME(PATMCallIndirect_EndProc)
2212%endif
2213
2214align 4
2215PATMCallIndirectTable:
2216 DW PATM_MAX_JUMPTABLE_ENTRIES ; nrSlots
2217 DW 0 ; ulInsertPos
2218 DD 0 ; cAddresses
2219 TIMES PATCHJUMPTABLE_SIZE DB 0 ; lookup slots
2220
2221 ; returning here -> do not add code here or after the jmp!!!!!
2222ENDPROC PATMCallIndirect
2223
2224; Patch record for indirect calls
2225BEGIN_PATCH_RODATA_SECTION
2226GLOBALNAME g_patmCallIndirectRecord
2227 PATCHASMRECORD_INIT PATMCallIndirect, 5
2228 DD PATM_FIXUP, PATMCallIndirectTable - NAME(PATMCallIndirect)
2229 DD PATM_PATCHNEXTBLOCK, 0
2230 DD PATM_RETURNADDR, 0
2231 DD PATM_LOOKUP_AND_CALL_FUNCTION, 0
2232 DD PATM_INTERRUPTFLAG, 0
2233 DD 0ffffffffh, 0ffffffffh
2234
2235
2236;
2237; Patch function for indirect jumps
2238; Caller is responsible for clearing PATM_INTERRUPTFLAG and adding:
2239; push [pTargetGC]
2240;
2241BEGIN_PATCH_CODE_SECTION
2242BEGINPROC PATMJumpIndirect
2243 ; save flags (just to be sure)
2244 pushfd
2245 pop dword [ss:PATM_TEMP_EFLAGS]
2246
2247 push PATM_FIXUP ; fixup for jump table below
2248 DB 0E8h ; call
2249 DD PATM_LOOKUP_AND_JUMP_FUNCTION
2250 ; we only return in case of a failure
2251 add esp, 8 ; pushed address of jump table + pushed target address
2252
2253 ; restore flags (just to be sure)
2254 push dword [ss:PATM_TEMP_EFLAGS]
2255 popfd
2256
2257 mov dword [ss:PATM_INTERRUPTFLAG], 1
2258 PATM_INT3
2259
2260%ifdef DEBUG
2261 ; for disassembly
2262 jmp NAME(PATMJumpIndirect_EndProc)
2263%endif
2264
2265align 4
2266PATMJumpIndirectTable:
2267 DW PATM_MAX_JUMPTABLE_ENTRIES ; nrSlots
2268 DW 0 ; ulInsertPos
2269 DD 0 ; cAddresses
2270 TIMES PATCHJUMPTABLE_SIZE DB 0 ; lookup slots
2271
2272 ; returning here -> do not add code here or after the jmp!!!!!
2273ENDPROC PATMJumpIndirect
2274
2275; Patch record for indirect jumps
2276BEGIN_PATCH_RODATA_SECTION
2277GLOBALNAME g_patmJumpIndirectRecord
2278 PATCHASMRECORD_INIT PATMJumpIndirect, 5
2279 DD PATM_TEMP_EFLAGS, 0
2280 DD PATM_FIXUP, PATMJumpIndirectTable - NAME(PATMJumpIndirect)
2281 DD PATM_LOOKUP_AND_JUMP_FUNCTION, 0
2282 DD PATM_TEMP_EFLAGS, 0
2283 DD PATM_INTERRUPTFLAG, 0
2284 DD 0ffffffffh, 0ffffffffh
2285
2286
2287;
2288; return from duplicated function
2289;
2290BEGIN_PATCH_CODE_SECTION
2291BEGINPROC PATMRet
2292 ; probe stack here as we can't recover from page faults later on
2293 not dword [esp-32]
2294 not dword [esp-32]
2295 mov dword [ss:PATM_INTERRUPTFLAG], 0
2296 pushfd
2297 push eax
2298 push PATM_FIXUP
2299 DB 0E8h ; call
2300 DD PATM_RETURN_FUNCTION
2301 add esp, 4 ; pushed address of jump table
2302
2303 cmp eax, 0
2304 jne near PATMRet_Success
2305
2306 pop eax
2307 popfd
2308 mov dword [ss:PATM_INTERRUPTFLAG], 1
2309 PATM_INT3
2310
2311%ifdef DEBUG
2312 ; for disassembly
2313 jmp PATMRet_Success
2314%endif
2315align 4
2316PATMRetTable:
2317 DW PATM_MAX_JUMPTABLE_ENTRIES ; nrSlots
2318 DW 0 ; ulInsertPos
2319 DD 0 ; cAddresses
2320 TIMES PATCHJUMPTABLE_SIZE DB 0 ; lookup slots
2321
2322PATMRet_Success:
2323 mov dword [esp+8], eax ; overwrite the saved return address
2324 pop eax
2325 popf
2326 ; caller will duplicate the ret or ret n instruction
2327 ; the patched call will set PATM_INTERRUPTFLAG after the return!
2328ENDPROC PATMRet
2329
2330BEGIN_PATCH_RODATA_SECTION
2331GLOBALNAME g_patmRetRecord
2332 PATCHASMRECORD_INIT PATMRet, 4
2333 DD PATM_INTERRUPTFLAG, 0
2334 DD PATM_FIXUP, PATMRetTable - NAME(PATMRet)
2335 DD PATM_RETURN_FUNCTION, 0
2336 DD PATM_INTERRUPTFLAG, 0
2337 DD 0ffffffffh, 0ffffffffh
2338
2339
2340;
2341; global function for implementing 'retn'
2342;
2343; Caller is responsible for right stack layout
2344; + 16 original return address
2345; + 12 eflags
2346; + 8 eax
2347; + 4 Jump table address
2348;( + 0 return address )
2349;
2350; @note assumes PATM_INTERRUPTFLAG is zero
2351; @note assumes it can trash eax and eflags
2352;
2353; @returns eax=0 on failure
2354; otherwise return address in eax
2355;
2356; @note NEVER change this without bumping the SSM version
2357;
2358BEGIN_PATCH_CODE_SECTION
2359BEGINPROC PATMRetFunction
2360 push ecx
2361 push edx
2362 push edi
2363
2364 ; Event order:
2365 ; (@todo figure out which path is taken most often (1 or 2))
2366 ; 1) Check if the return patch address was pushed onto the PATM stack
2367 ; 2) Check if the return patch address can be found in the lookup table
2368 ; 3) Query return patch address from the hypervisor
2369
2370
2371 ; 1) Check if the return patch address was pushed on the PATM stack
2372 cmp dword [ss:PATM_STACKPTR], PATM_STACK_SIZE
2373 jae near PATMRetFunction_FindReturnAddress
2374
2375 mov edx, dword PATM_STACKPTR
2376
2377 ; check if the return address is what we expect it to be
2378 mov eax, dword PATM_STACKBASE_GUEST
2379 add eax, dword [ss:edx] ; stack base + stack position
2380 mov eax, dword [ss:eax] ; original return address
2381 cmp eax, dword [esp+12+16] ; pushed return address
2382
2383 ; the return address was changed -> let our trap handler try to find it
2384 ; (can happen when the guest messes with the stack (seen it) or when we didn't call this function ourselves)
2385 jne near PATMRetFunction_FindReturnAddress
2386
2387 ; found it, convert relative to absolute patch address and return the result to the caller
2388 mov eax, dword PATM_STACKBASE
2389 add eax, dword [ss:edx] ; stack base + stack position
2390 mov eax, dword [ss:eax] ; relative patm return address
2391 add eax, PATM_PATCHBASE
2392
2393%ifdef PATM_LOG_PATCHINSTR
2394 push eax
2395 push ebx
2396 push ecx
2397 push edx
2398 mov edx, eax ; return address
2399 lea ebx, [esp+16+12+16] ; stack address containing the return address
2400 lock or dword [ss:PATM_PENDINGACTION], PATM_ACTION_LOG_RET
2401 mov eax, PATM_ACTION_LOG_RET
2402 mov ecx, PATM_ACTION_MAGIC
2403 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
2404 pop edx
2405 pop ecx
2406 pop ebx
2407 pop eax
2408%endif
2409
2410 add dword [ss:edx], 4 ; pop return address from the PATM stack (sizeof(RTGCPTR); @note hardcoded assumption!)
2411
2412 pop edi
2413 pop edx
2414 pop ecx
2415 ret
2416
2417PATMRetFunction_FindReturnAddress:
2418 ; 2) Check if the return patch address can be found in the lookup table
2419 mov edx, dword [esp+12+16] ; pushed target address
2420
2421 xor eax, eax ; default result -> nothing found
2422 mov edi, dword [esp+12+4] ; jump table
2423 mov ecx, [ss:edi + PATCHJUMPTABLE.cAddresses]
2424 cmp ecx, 0
2425 je near PATMRetFunction_AskHypervisor
2426
2427PATMRetFunction_SearchStart:
2428 cmp [ss:edi + PATCHJUMPTABLE.Slot_pInstrGC + eax*8], edx ; edx = GC address to search for
2429 je near PATMRetFunction_SearchHit
2430 inc eax
2431 cmp eax, ecx
2432 jl near PATMRetFunction_SearchStart
2433
2434PATMRetFunction_AskHypervisor:
2435 ; 3) Query return patch address from the hypervisor
2436 ; @todo private ugly interface, since we have nothing generic at the moment
2437 lock or dword [ss:PATM_PENDINGACTION], PATM_ACTION_LOOKUP_ADDRESS
2438 mov eax, PATM_ACTION_LOOKUP_ADDRESS
2439 mov ecx, PATM_ACTION_MAGIC
2440 mov edi, dword [esp+12+4] ; jump table address
2441 mov edx, dword [esp+12+16] ; original return address
2442 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
2443 jmp near PATMRetFunction_SearchEnd
2444
2445PATMRetFunction_SearchHit:
2446 mov eax, [ss:edi + PATCHJUMPTABLE.Slot_pRelPatchGC + eax*8] ; found a match!
2447 ;@note can be zero, so the next check is required!!
2448
2449PATMRetFunction_SearchEnd:
2450 cmp eax, 0
2451 jz PATMRetFunction_Failure
2452
2453 add eax, PATM_PATCHBASE
2454
2455%ifdef PATM_LOG_PATCHINSTR
2456 push eax
2457 push ebx
2458 push ecx
2459 push edx
2460 mov edx, eax ; return address
2461 lea ebx, [esp+16+12+16] ; stack address containing the return address
2462 lock or dword [ss:PATM_PENDINGACTION], PATM_ACTION_LOG_RET
2463 mov eax, PATM_ACTION_LOG_RET
2464 mov ecx, PATM_ACTION_MAGIC
2465 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
2466 pop edx
2467 pop ecx
2468 pop ebx
2469 pop eax
2470%endif
2471
2472 pop edi
2473 pop edx
2474 pop ecx
2475 ret
2476
2477PATMRetFunction_Failure:
2478 ;signal error
2479 xor eax, eax
2480 pop edi
2481 pop edx
2482 pop ecx
2483 ret
2484ENDPROC PATMRetFunction
2485
2486BEGIN_PATCH_RODATA_SECTION
2487GLOBALNAME g_patmRetFunctionRecord
2488%ifdef PATM_LOG_PATCHINSTR
2489 PATCHASMRECORD_INIT PATMRetFunction, 9
2490%else
2491 PATCHASMRECORD_INIT PATMRetFunction, 7
2492%endif
2493 DD PATM_STACKPTR, 0
2494 DD PATM_STACKPTR, 0
2495 DD PATM_STACKBASE_GUEST, 0
2496 DD PATM_STACKBASE, 0
2497 DD PATM_PATCHBASE, 0
2498%ifdef PATM_LOG_PATCHINSTR
2499 DD PATM_PENDINGACTION, 0
2500%endif
2501 DD PATM_PENDINGACTION, 0
2502 DD PATM_PATCHBASE, 0
2503%ifdef PATM_LOG_PATCHINSTR
2504 DD PATM_PENDINGACTION, 0
2505%endif
2506 DD 0ffffffffh, 0ffffffffh
2507
2508
2509;
2510; Jump to original instruction if IF=1
2511;
2512BEGIN_PATCH_CODE_SECTION
2513BEGINPROC PATMCheckIF
2514 mov dword [ss:PATM_INTERRUPTFLAG], 0
2515 pushf
2516 test dword [ss:PATM_VMFLAGS], X86_EFL_IF
2517 jnz PATMCheckIF_Safe
2518 nop
2519
2520 ; IF=0 -> unsafe, so we must call the duplicated function (which we don't do here)
2521 popf
2522 mov dword [ss:PATM_INTERRUPTFLAG], 1
2523 jmp NAME(PATMCheckIF_EndProc)
2524
2525PATMCheckIF_Safe:
2526 ; invalidate the PATM stack as we'll jump back to guest code
2527 mov dword [ss:PATM_STACKPTR], PATM_STACK_SIZE
2528
2529%ifdef PATM_LOG_PATCHINSTR
2530 push eax
2531 push ecx
2532 lock or dword [ss:PATM_PENDINGACTION], PATM_ACTION_LOG_IF1
2533 mov eax, PATM_ACTION_LOG_IF1
2534 mov ecx, PATM_ACTION_MAGIC
2535 db 0fh, 0bh ; illegal instr (hardcoded assumption in PATMHandleIllegalInstrTrap)
2536 pop ecx
2537 pop eax
2538%endif
2539 popf
2540 mov dword [ss:PATM_INTERRUPTFLAG], 1
2541 ; IF=1 -> we can safely jump back to the original instruction
2542 DB 0xE9
2543PATMCheckIF_Jump:
2544 DD PATM_JUMPDELTA
2545ENDPROC PATMCheckIF
2546
2547; Patch record for call instructions
2548BEGIN_PATCH_RODATA_SECTION
2549GLOBALNAME g_patmCheckIFRecord
2550%ifdef PATM_LOG_PATCHINSTR
2551 PATCHASMRECORD_INIT_JUMP PATMCheckIF, PATMCheckIF_Jump, 6
2552%else
2553 PATCHASMRECORD_INIT_JUMP PATMCheckIF, PATMCheckIF_Jump, 5
2554%endif
2555 DD PATM_INTERRUPTFLAG, 0
2556 DD PATM_VMFLAGS, 0
2557 DD PATM_INTERRUPTFLAG, 0
2558 DD PATM_STACKPTR, 0
2559%ifdef PATM_LOG_PATCHINSTR
2560 DD PATM_PENDINGACTION, 0
2561%endif
2562 DD PATM_INTERRUPTFLAG, 0
2563 DD 0ffffffffh, 0ffffffffh
2564
2565
2566;
2567; Jump back to guest if IF=1, else fault
2568;
2569BEGIN_PATCH_CODE_SECTION
2570BEGINPROC PATMJumpToGuest_IF1
2571 mov dword [ss:PATM_INTERRUPTFLAG], 0
2572 pushf
2573 test dword [ss:PATM_VMFLAGS], X86_EFL_IF
2574 jnz PATMJumpToGuest_IF1_Safe
2575 nop
2576
2577 ; IF=0 -> unsafe, so fault
2578 popf
2579 mov dword [ss:PATM_INTERRUPTFLAG], 1
2580 PATM_INT3
2581
2582PATMJumpToGuest_IF1_Safe:
2583 ; IF=1 -> we can safely jump back to the original instruction
2584 popf
2585 mov dword [ss:PATM_INTERRUPTFLAG], 1
2586 DB 0xE9
2587PATMJumpToGuest_IF1_Jump:
2588 DD PATM_JUMPDELTA
2589ENDPROC PATMJumpToGuest_IF1
2590
2591; Patch record for call instructions
2592BEGIN_PATCH_RODATA_SECTION
2593GLOBALNAME PATMJumpToGuest_IF1Record
2594 PATCHASMRECORD_INIT_JUMP PATMJumpToGuest_IF1, PATMJumpToGuest_IF1_Jump, 4
2595 DD PATM_INTERRUPTFLAG, 0
2596 DD PATM_VMFLAGS, 0
2597 DD PATM_INTERRUPTFLAG, 0
2598 DD PATM_INTERRUPTFLAG, 0
2599 DD 0ffffffffh, 0ffffffffh
2600
2601
2602;
2603; Check and correct RPL of pushed ss.
2604;
2605BEGIN_PATCH_CODE_SECTION
2606BEGINPROC PATMMovFromSS
2607 push eax
2608 pushfd
2609 mov ax, ss
2610 and ax, 3
2611 cmp ax, 1
2612 jne near PATMMovFromSS_Continue
2613
2614 and dword [esp+8], ~3 ; clear RPL 1
2615PATMMovFromSS_Continue:
2616 popfd
2617 pop eax
2618ENDPROC PATMMovFromSS
2619
2620BEGIN_PATCH_RODATA_SECTION
2621GLOBALNAME g_patmMovFromSSRecord
2622 PATCHASMRECORD_INIT PATMMovFromSS, 0
2623 DD 0ffffffffh, 0ffffffffh
2624
2625
2626
2627
2628;; For assertion during init (to make absolutely sure the flags are in sync in vm.mac & vm.h)
2629BEGINCONST
2630GLOBALNAME g_fPatmInterruptFlag
2631 DD VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST
2632
Note: See TracBrowser for help on using the repository browser.

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