VirtualBox

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

Last change on this file since 65697 was 62478, checked in by vboxsync, 8 years ago

(C) 2016

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

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