VirtualBox

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

Last change on this file since 50653 was 45276, checked in by vboxsync, 11 years ago

Ring-1 compression patches, courtesy of trivirt AG:

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

© 2023 Oracle
ContactPrivacy policyTerms of Use