2 * Emulation of privileged instructions
4 * Copyright 1995 Alexandre Julliard
5 * Copyright 2005 Ivan Leo Puoti
6 * Copyright 2005 Laurent Pinchart
7 * Copyright 2014-2015 Sebastian Lackner
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "wine/port.h"
32 #define WIN32_NO_STATUS
35 #include "wine/debug.h"
36 #include "wine/exception.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(int);
50 static LDT_ENTRY idt
[256];
52 static inline struct idtr
get_idtr(void)
56 __asm__( "sidtl %0" : "=m" (ret
) );
58 ret
.base
= (BYTE
*)idt
;
59 ret
.limit
= sizeof(idt
) - 1;
64 /* store an operand into a register */
65 static void store_reg_word( CONTEXT
*context
, BYTE regmodrm
, const BYTE
*addr
, int long_op
)
67 switch((regmodrm
>> 3) & 7)
70 if (long_op
) context
->Eax
= *(const DWORD
*)addr
;
71 else context
->Eax
= (context
->Eax
& 0xffff0000) | *(const WORD
*)addr
;
74 if (long_op
) context
->Ecx
= *(const DWORD
*)addr
;
75 else context
->Ecx
= (context
->Ecx
& 0xffff0000) | *(const WORD
*)addr
;
78 if (long_op
) context
->Edx
= *(const DWORD
*)addr
;
79 else context
->Edx
= (context
->Edx
& 0xffff0000) | *(const WORD
*)addr
;
82 if (long_op
) context
->Ebx
= *(const DWORD
*)addr
;
83 else context
->Ebx
= (context
->Ebx
& 0xffff0000) | *(const WORD
*)addr
;
86 if (long_op
) context
->Esp
= *(const DWORD
*)addr
;
87 else context
->Esp
= (context
->Esp
& 0xffff0000) | *(const WORD
*)addr
;
90 if (long_op
) context
->Ebp
= *(const DWORD
*)addr
;
91 else context
->Ebp
= (context
->Ebp
& 0xffff0000) | *(const WORD
*)addr
;
94 if (long_op
) context
->Esi
= *(const DWORD
*)addr
;
95 else context
->Esi
= (context
->Esi
& 0xffff0000) | *(const WORD
*)addr
;
98 if (long_op
) context
->Edi
= *(const DWORD
*)addr
;
99 else context
->Edi
= (context
->Edi
& 0xffff0000) | *(const WORD
*)addr
;
104 /* store an operand into a byte register */
105 static void store_reg_byte( CONTEXT
*context
, BYTE regmodrm
, const BYTE
*addr
)
107 switch((regmodrm
>> 3) & 7)
109 case 0: context
->Eax
= (context
->Eax
& 0xffffff00) | *addr
; break;
110 case 1: context
->Ecx
= (context
->Ecx
& 0xffffff00) | *addr
; break;
111 case 2: context
->Edx
= (context
->Edx
& 0xffffff00) | *addr
; break;
112 case 3: context
->Ebx
= (context
->Ebx
& 0xffffff00) | *addr
; break;
113 case 4: context
->Eax
= (context
->Eax
& 0xffff00ff) | (*addr
<< 8); break;
114 case 5: context
->Ecx
= (context
->Ecx
& 0xffff00ff) | (*addr
<< 8); break;
115 case 6: context
->Edx
= (context
->Edx
& 0xffff00ff) | (*addr
<< 8); break;
116 case 7: context
->Ebx
= (context
->Ebx
& 0xffff00ff) | (*addr
<< 8); break;
120 /***********************************************************************
121 * INSTR_GetOperandAddr
123 * Return the address of an instruction operand (from the mod/rm byte).
125 static BYTE
*INSTR_GetOperandAddr( CONTEXT
*context
, BYTE
*instr
,
126 int long_addr
, int segprefix
, int *len
)
128 int mod
, rm
, base
= 0, index
= 0, ss
= 0, off
;
130 #define GET_VAL(val,type) \
131 { *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); }
134 GET_VAL( &mod
, BYTE
);
142 case 0: return (BYTE
*)&context
->Eax
;
143 case 1: return (BYTE
*)&context
->Ecx
;
144 case 2: return (BYTE
*)&context
->Edx
;
145 case 3: return (BYTE
*)&context
->Ebx
;
146 case 4: return (BYTE
*)&context
->Esp
;
147 case 5: return (BYTE
*)&context
->Ebp
;
148 case 6: return (BYTE
*)&context
->Esi
;
149 case 7: return (BYTE
*)&context
->Edi
;
158 GET_VAL( &sib
, BYTE
);
161 switch((sib
>> 3) & 7)
163 case 0: index
= context
->Eax
; break;
164 case 1: index
= context
->Ecx
; break;
165 case 2: index
= context
->Edx
; break;
166 case 3: index
= context
->Ebx
; break;
167 case 4: index
= 0; break;
168 case 5: index
= context
->Ebp
; break;
169 case 6: index
= context
->Esi
; break;
170 case 7: index
= context
->Edi
; break;
176 case 0: base
= context
->Eax
; break;
177 case 1: base
= context
->Ecx
; break;
178 case 2: base
= context
->Edx
; break;
179 case 3: base
= context
->Ebx
; break;
180 case 4: base
= context
->Esp
; break;
181 case 5: base
= context
->Ebp
; break;
182 case 6: base
= context
->Esi
; break;
183 case 7: base
= context
->Edi
; break;
188 if (rm
== 5) /* special case: ds:(disp32) */
190 GET_VAL( &base
, DWORD
);
194 case 1: /* 8-bit disp */
195 GET_VAL( &off
, BYTE
);
196 base
+= (signed char)off
;
199 case 2: /* 32-bit disp */
200 GET_VAL( &off
, DWORD
);
201 base
+= (signed long)off
;
205 else /* short address */
209 case 0: /* ds:(bx,si) */
210 base
= LOWORD(context
->Ebx
) + LOWORD(context
->Esi
);
212 case 1: /* ds:(bx,di) */
213 base
= LOWORD(context
->Ebx
) + LOWORD(context
->Edi
);
215 case 2: /* ss:(bp,si) */
216 base
= LOWORD(context
->Ebp
) + LOWORD(context
->Esi
);
218 case 3: /* ss:(bp,di) */
219 base
= LOWORD(context
->Ebp
) + LOWORD(context
->Edi
);
221 case 4: /* ds:(si) */
222 base
= LOWORD(context
->Esi
);
224 case 5: /* ds:(di) */
225 base
= LOWORD(context
->Edi
);
227 case 6: /* ss:(bp) */
228 base
= LOWORD(context
->Ebp
);
230 case 7: /* ds:(bx) */
231 base
= LOWORD(context
->Ebx
);
238 if (rm
== 6) /* special case: ds:(disp16) */
240 GET_VAL( &base
, WORD
);
244 case 1: /* 8-bit disp */
245 GET_VAL( &off
, BYTE
);
246 base
+= (signed char)off
;
249 case 2: /* 16-bit disp */
250 GET_VAL( &off
, WORD
);
251 base
+= (signed short)off
;
256 /* FIXME: we assume that all segments have a base of 0 */
257 return (BYTE
*)(base
+ (index
<< ss
));
262 /***********************************************************************
263 * emulate_instruction
265 * Emulate a privileged instruction.
266 * Returns exception continuation status.
268 static DWORD
emulate_instruction( EXCEPTION_RECORD
*rec
, CONTEXT
*context
)
270 int prefix
, segprefix
, prefixlen
, len
, long_op
, long_addr
;
273 long_op
= long_addr
= 1;
274 instr
= (BYTE
*)context
->Eip
;
275 if (!instr
) return ExceptionContinueSearch
;
277 /* First handle any possible prefix */
279 segprefix
= -1; /* no prefix */
287 segprefix
= context
->SegCs
;
290 segprefix
= context
->SegSs
;
293 segprefix
= context
->SegDs
;
296 segprefix
= context
->SegEs
;
299 segprefix
= context
->SegFs
;
302 segprefix
= context
->SegGs
;
305 long_op
= !long_op
; /* opcode size prefix */
308 long_addr
= !long_addr
; /* addr size prefix */
310 case 0xf0: /* lock */
312 case 0xf2: /* repne */
314 case 0xf3: /* repe */
317 prefix
= 0; /* no more prefixes */
327 /* Now look at the actual instruction */
331 case 0x0f: /* extended instruction */
334 case 0x22: /* mov eax, crX */
338 TRACE("mov eax,cr0 at 0x%08x, EAX=0x%08x\n", context
->Eip
,context
->Eax
);
339 context
->Eip
+= prefixlen
+3;
340 return ExceptionContinueExecution
;
342 TRACE("mov eax,cr4 at 0x%08x, EAX=0x%08x\n", context
->Eip
,context
->Eax
);
343 context
->Eip
+= prefixlen
+3;
344 return ExceptionContinueExecution
;
346 break; /*fallthrough to bad instruction handling */
348 ERR("Unsupported EAX -> CR register, eip+2 is %02x\n", instr
[2]);
349 break; /*fallthrough to bad instruction handling */
350 case 0x20: /* mov crX, eax */
353 case 0xe0: /* mov cr4, eax */
354 /* CR4 register . See linux/arch/i386/mm/init.c, X86_CR4_ defs
355 * bit 0: VME Virtual Mode Exception ?
356 * bit 1: PVI Protected mode Virtual Interrupt
357 * bit 2: TSD Timestamp disable
358 * bit 3: DE Debugging extensions
359 * bit 4: PSE Page size extensions
360 * bit 5: PAE Physical address extension
361 * bit 6: MCE Machine check enable
362 * bit 7: PGE Enable global pages
363 * bit 8: PCE Enable performance counters at IPL3
365 TRACE("mov cr4,eax at 0x%08x\n",context
->Eip
);
367 context
->Eip
+= prefixlen
+3;
368 return ExceptionContinueExecution
;
369 case 0xc0: /* mov cr0, eax */
370 TRACE("mov cr0,eax at 0x%08x\n",context
->Eip
);
371 context
->Eax
= 0x10; /* FIXME: set more bits ? */
372 context
->Eip
+= prefixlen
+3;
373 return ExceptionContinueExecution
;
374 default: /* fallthrough to illegal instruction */
377 /* fallthrough to illegal instruction */
379 case 0x21: /* mov drX, eax */
382 case 0xc8: /* mov dr1, eax */
383 TRACE("mov dr1,eax at 0x%08x\n",context
->Eip
);
384 context
->Eax
= context
->Dr1
;
385 context
->Eip
+= prefixlen
+3;
386 return ExceptionContinueExecution
;
387 case 0xf8: /* mov dr7, eax */
388 TRACE("mov dr7,eax at 0x%08x\n",context
->Eip
);
389 context
->Eax
= 0x400;
390 context
->Eip
+= prefixlen
+3;
391 return ExceptionContinueExecution
;
393 ERR("Unsupported DR register -> EAX, eip+2 is %02x\n", instr
[2]);
394 /* fallthrough to illegal instruction */
396 case 0x23: /* mov eax drX */
399 case 0xc0: /* mov eax, dr0 */
400 context
->Dr0
= context
->Eax
;
401 context
->Eip
+= prefixlen
+3;
402 return ExceptionContinueExecution
;
403 case 0xc8: /* mov eax, dr1 */
404 context
->Dr1
= context
->Eax
;
405 context
->Eip
+= prefixlen
+3;
406 return ExceptionContinueExecution
;
407 case 0xd0: /* mov eax, dr2 */
408 context
->Dr2
= context
->Eax
;
409 context
->Eip
+= prefixlen
+3;
410 return ExceptionContinueExecution
;
411 case 0xd8: /* mov eax, dr3 */
412 context
->Dr3
= context
->Eax
;
413 context
->Eip
+= prefixlen
+3;
414 return ExceptionContinueExecution
;
415 case 0xf8: /* mov eax, dr7 */
416 context
->Dr7
= context
->Eax
;
417 context
->Eip
+= prefixlen
+3;
418 return ExceptionContinueExecution
;
420 ERR("Unsupported EAX -> DR register, eip+2 is %02x\n", instr
[2]);
421 /* fallthrough to illegal instruction */
424 break; /* Unable to emulate it */
426 case 0x8a: /* mov Eb, Gb */
427 case 0x8b: /* mov Ev, Gv */
429 BYTE
*data
= INSTR_GetOperandAddr(context
, instr
+ 1, long_addr
,
431 unsigned int data_size
= (*instr
== 0x8b) ? (long_op
? 4 : 2) : 1;
432 struct idtr idtr
= get_idtr();
433 unsigned int offset
= data
- idtr
.base
;
435 if (offset
<= idtr
.limit
+ 1 - data_size
)
437 idt
[1].LimitLow
= 0x100; /* FIXME */
438 idt
[2].LimitLow
= 0x11E; /* FIXME */
439 idt
[3].LimitLow
= 0x500; /* FIXME */
443 case 0x8a: store_reg_byte( context
, instr
[1], (BYTE
*)idt
+ offset
); break;
444 case 0x8b: store_reg_word( context
, instr
[1], (BYTE
*)idt
+ offset
, long_op
); break;
446 context
->Eip
+= prefixlen
+ len
+ 1;
447 return ExceptionContinueExecution
;
449 break; /* Unable to emulate it */
454 context
->Eip
+= prefixlen
+ 1;
455 return ExceptionContinueExecution
;
457 return ExceptionContinueSearch
; /* Unable to emulate it */
461 /***********************************************************************
464 * Vectored exception handler used to emulate protected instructions
467 LONG CALLBACK
vectored_handler( EXCEPTION_POINTERS
*ptrs
)
469 EXCEPTION_RECORD
*record
= ptrs
->ExceptionRecord
;
470 CONTEXT
*context
= ptrs
->ContextRecord
;
472 if ((record
->ExceptionCode
== EXCEPTION_ACCESS_VIOLATION
||
473 record
->ExceptionCode
== EXCEPTION_PRIV_INSTRUCTION
))
475 if (emulate_instruction( record
, context
) == ExceptionContinueExecution
)
476 return EXCEPTION_CONTINUE_EXECUTION
;
478 return EXCEPTION_CONTINUE_SEARCH
;
481 #elif defined(__x86_64__) /* __i386__ */
483 WINE_DEFAULT_DEBUG_CHANNEL(int);
490 #define REGMODRM_MOD( regmodrm, rex ) ((regmodrm) >> 6)
491 #define REGMODRM_REG( regmodrm, rex ) (((regmodrm) >> 3) & 7) | (((rex) & REX_R) ? 8 : 0)
492 #define REGMODRM_RM( regmodrm, rex ) (((regmodrm) & 7) | (((rex) & REX_B) ? 8 : 0))
494 #define SIB_SS( sib, rex ) ((sib) >> 6)
495 #define SIB_INDEX( sib, rex ) (((sib) >> 3) & 7) | (((rex) & REX_R) ? 8 : 0)
496 #define SIB_BASE( sib, rex ) (((sib) & 7) | (((rex) & REX_B) ? 8 : 0))
498 /* keep in sync with dlls/ntdll/thread.c:thread_init */
499 static const BYTE
*wine_user_shared_data
= (BYTE
*)0x7ffe0000;
500 static const BYTE
*user_shared_data
= (BYTE
*)0xfffff78000000000;
502 static inline DWORD64
*get_int_reg( CONTEXT
*context
, int index
)
504 return &context
->Rax
+ index
; /* index should be in range 0 .. 15 */
507 static inline int get_op_size( int long_op
, int rex
)
510 return sizeof(DWORD64
);
512 return sizeof(DWORD
);
517 /* store an operand into a register */
518 static void store_reg_word( CONTEXT
*context
, BYTE regmodrm
, const BYTE
*addr
, int long_op
, int rex
)
520 int index
= REGMODRM_REG( regmodrm
, rex
);
521 BYTE
*reg
= (BYTE
*)get_int_reg( context
, index
);
522 memcpy( reg
, addr
, get_op_size( long_op
, rex
) );
525 /* store an operand into a byte register */
526 static void store_reg_byte( CONTEXT
*context
, BYTE regmodrm
, const BYTE
*addr
, int rex
)
528 int index
= REGMODRM_REG( regmodrm
, rex
);
529 BYTE
*reg
= (BYTE
*)get_int_reg( context
, index
);
530 if (!rex
&& index
>= 4 && index
< 8) reg
-= (4 * sizeof(DWORD64
) - 1); /* special case: ah, ch, dh, bh */
534 /***********************************************************************
535 * INSTR_GetOperandAddr
537 * Return the address of an instruction operand (from the mod/rm byte).
539 static BYTE
*INSTR_GetOperandAddr( CONTEXT
*context
, BYTE
*instr
,
540 int long_addr
, int rex
, int segprefix
, int *len
)
542 int mod
, rm
, ss
= 0, off
, have_sib
= 0;
543 DWORD64 base
= 0, index
= 0;
545 #define GET_VAL( val, type ) \
546 { *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); }
549 GET_VAL( &mod
, BYTE
);
550 rm
= REGMODRM_RM( mod
, rex
);
551 mod
= REGMODRM_MOD( mod
, rex
);
554 return (BYTE
*)get_int_reg( context
, rm
);
561 GET_VAL( &sib
, BYTE
);
562 rm
= SIB_BASE( sib
, rex
);
563 id
= SIB_INDEX( sib
, rex
);
564 ss
= SIB_SS( sib
, rex
);
566 index
= (id
!= 4) ? *get_int_reg( context
, id
) : 0;
567 if (!long_addr
) index
&= 0xffffffff;
571 base
= *get_int_reg( context
, rm
);
572 if (!long_addr
) base
&= 0xffffffff;
577 if (rm
== 5) /* special case */
579 base
= have_sib
? 0 : context
->Rip
;
580 if (!long_addr
) base
&= 0xffffffff;
581 GET_VAL( &off
, DWORD
);
582 base
+= (signed long)off
;
586 case 1: /* 8-bit disp */
587 GET_VAL( &off
, BYTE
);
588 base
+= (signed char)off
;
591 case 2: /* 32-bit disp */
592 GET_VAL( &off
, DWORD
);
593 base
+= (signed long)off
;
597 /* FIXME: we assume that all segments have a base of 0 */
598 return (BYTE
*)(base
+ (index
<< ss
));
603 /***********************************************************************
604 * emulate_instruction
606 * Emulate a privileged instruction.
607 * Returns exception continuation status.
609 static DWORD
emulate_instruction( EXCEPTION_RECORD
*rec
, CONTEXT
*context
)
611 int prefix
, segprefix
, prefixlen
, len
, long_op
, long_addr
, rex
;
614 long_op
= long_addr
= 1;
615 instr
= (BYTE
*)context
->Rip
;
616 if (!instr
) return ExceptionContinueSearch
;
618 /* First handle any possible prefix */
620 segprefix
= -1; /* no seg prefix */
621 rex
= 0; /* no rex prefix */
629 segprefix
= context
->SegCs
;
632 segprefix
= context
->SegSs
;
635 segprefix
= context
->SegDs
;
638 segprefix
= context
->SegEs
;
641 segprefix
= context
->SegFs
;
644 segprefix
= context
->SegGs
;
647 long_op
= !long_op
; /* opcode size prefix */
650 long_addr
= !long_addr
; /* addr size prefix */
652 case 0xf0: /* lock */
654 case 0xf2: /* repne */
656 case 0xf3: /* repe */
659 prefix
= 0; /* no more prefixes */
662 if (*instr
>= 0x40 && *instr
< 0x50) /* rex */
674 /* Now look at the actual instruction */
678 case 0x0f: /* extended instruction */
681 case 0xb6: /* movzx Eb, Gv */
682 case 0xb7: /* movzx Ew, Gv */
684 BYTE
*data
= INSTR_GetOperandAddr( context
, instr
+ 2, long_addr
,
685 rex
, segprefix
, &len
);
686 unsigned int data_size
= (instr
[1] == 0xb7) ? 2 : 1;
687 SIZE_T offset
= data
- user_shared_data
;
689 if (offset
<= sizeof(KSHARED_USER_DATA
) - data_size
)
692 memcpy( &temp
, wine_user_shared_data
+ offset
, data_size
);
693 store_reg_word( context
, instr
[2], (BYTE
*)&temp
, long_op
, rex
);
694 context
->Rip
+= prefixlen
+ len
+ 2;
695 return ExceptionContinueExecution
;
697 break; /* Unable to emulate it */
700 break; /* Unable to emulate it */
702 case 0x8a: /* mov Eb, Gb */
703 case 0x8b: /* mov Ev, Gv */
705 BYTE
*data
= INSTR_GetOperandAddr( context
, instr
+ 1, long_addr
,
706 rex
, segprefix
, &len
);
707 unsigned int data_size
= (*instr
== 0x8b) ? get_op_size( long_op
, rex
) : 1;
708 SIZE_T offset
= data
- user_shared_data
;
710 if (offset
<= sizeof(KSHARED_USER_DATA
) - data_size
)
714 case 0x8a: store_reg_byte( context
, instr
[1], wine_user_shared_data
+ offset
, rex
); break;
715 case 0x8b: store_reg_word( context
, instr
[1], wine_user_shared_data
+ offset
, long_op
, rex
); break;
717 context
->Rip
+= prefixlen
+ len
+ 1;
718 return ExceptionContinueExecution
;
720 break; /* Unable to emulate it */
723 case 0xa0: /* mov Ob, AL */
724 case 0xa1: /* mov Ovqp, rAX */
726 BYTE
*data
= (BYTE
*)(long_addr
? *(DWORD64
*)(instr
+ 1) : *(DWORD
*)(instr
+ 1));
727 unsigned int data_size
= (*instr
== 0xa1) ? get_op_size( long_op
, rex
) : 1;
728 SIZE_T offset
= data
- user_shared_data
;
729 len
= long_addr
? sizeof(DWORD64
) : sizeof(DWORD
);
731 if (offset
<= sizeof(KSHARED_USER_DATA
) - data_size
)
733 memcpy( &context
->Rax
, wine_user_shared_data
+ offset
, data_size
);
734 context
->Rip
+= prefixlen
+ len
+ 1;
735 return ExceptionContinueExecution
;
737 break; /* Unable to emulate it */
740 return ExceptionContinueSearch
; /* Unable to emulate it */
744 /***********************************************************************
747 * Vectored exception handler used to emulate protected instructions
750 LONG CALLBACK
vectored_handler( EXCEPTION_POINTERS
*ptrs
)
752 EXCEPTION_RECORD
*record
= ptrs
->ExceptionRecord
;
753 CONTEXT
*context
= ptrs
->ContextRecord
;
755 if (record
->ExceptionCode
== EXCEPTION_ACCESS_VIOLATION
&&
756 record
->ExceptionInformation
[0] == EXCEPTION_READ_FAULT
)
758 if (emulate_instruction( record
, context
) == ExceptionContinueExecution
)
760 TRACE( "next instruction rip=%lx\n", context
->Rip
);
761 TRACE( " rax=%016lx rbx=%016lx rcx=%016lx rdx=%016lx\n",
762 context
->Rax
, context
->Rbx
, context
->Rcx
, context
->Rdx
);
763 TRACE( " rsi=%016lx rdi=%016lx rbp=%016lx rsp=%016lx\n",
764 context
->Rsi
, context
->Rdi
, context
->Rbp
, context
->Rsp
);
765 TRACE( " r8=%016lx r9=%016lx r10=%016lx r11=%016lx\n",
766 context
->R8
, context
->R9
, context
->R10
, context
->R11
);
767 TRACE( " r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n",
768 context
->R12
, context
->R13
, context
->R14
, context
->R15
);
770 return EXCEPTION_CONTINUE_EXECUTION
;
773 return EXCEPTION_CONTINUE_SEARCH
;
776 #endif /* __x86_64__ */