VirtualBox

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

Last change on this file since 74795 was 69221, checked in by vboxsync, 7 years ago

VMM: scm cleanups

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

© 2023 Oracle
ContactPrivacy policyTerms of Use