2 * Emulation of privileged instructions
4 * Copyright 1995 Alexandre Julliard
5 * Copyright 2005 Ivan Leo Puoti
6 * Copyright 2005 Laurent Pinchart
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "wine/port.h"
34 #include "wine/debug.h"
35 #include "wine/exception.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(int);
47 static LDT_ENTRY idt
[256];
49 static inline struct idtr
get_idtr(void)
53 __asm__( "sidtl %0" : "=m" (ret
) );
55 ret
.base
= (BYTE
*)idt
;
56 ret
.limit
= sizeof(idt
) - 1;
61 /* store an operand into a register */
62 static void store_reg_word( CONTEXT
*context
, BYTE regmodrm
, const BYTE
*addr
, int long_op
)
64 switch((regmodrm
>> 3) & 7)
67 if (long_op
) context
->Eax
= *(const DWORD
*)addr
;
68 else context
->Eax
= (context
->Eax
& 0xffff0000) | *(const WORD
*)addr
;
71 if (long_op
) context
->Ecx
= *(const DWORD
*)addr
;
72 else context
->Ecx
= (context
->Ecx
& 0xffff0000) | *(const WORD
*)addr
;
75 if (long_op
) context
->Edx
= *(const DWORD
*)addr
;
76 else context
->Edx
= (context
->Edx
& 0xffff0000) | *(const WORD
*)addr
;
79 if (long_op
) context
->Ebx
= *(const DWORD
*)addr
;
80 else context
->Ebx
= (context
->Ebx
& 0xffff0000) | *(const WORD
*)addr
;
83 if (long_op
) context
->Esp
= *(const DWORD
*)addr
;
84 else context
->Esp
= (context
->Esp
& 0xffff0000) | *(const WORD
*)addr
;
87 if (long_op
) context
->Ebp
= *(const DWORD
*)addr
;
88 else context
->Ebp
= (context
->Ebp
& 0xffff0000) | *(const WORD
*)addr
;
91 if (long_op
) context
->Esi
= *(const DWORD
*)addr
;
92 else context
->Esi
= (context
->Esi
& 0xffff0000) | *(const WORD
*)addr
;
95 if (long_op
) context
->Edi
= *(const DWORD
*)addr
;
96 else context
->Edi
= (context
->Edi
& 0xffff0000) | *(const WORD
*)addr
;
101 /* store an operand into a byte register */
102 static void store_reg_byte( CONTEXT
*context
, BYTE regmodrm
, const BYTE
*addr
)
104 switch((regmodrm
>> 3) & 7)
106 case 0: context
->Eax
= (context
->Eax
& 0xffffff00) | *addr
; break;
107 case 1: context
->Ecx
= (context
->Ecx
& 0xffffff00) | *addr
; break;
108 case 2: context
->Edx
= (context
->Edx
& 0xffffff00) | *addr
; break;
109 case 3: context
->Ebx
= (context
->Ebx
& 0xffffff00) | *addr
; break;
110 case 4: context
->Eax
= (context
->Eax
& 0xffff00ff) | (*addr
<< 8); break;
111 case 5: context
->Ecx
= (context
->Ecx
& 0xffff00ff) | (*addr
<< 8); break;
112 case 6: context
->Edx
= (context
->Edx
& 0xffff00ff) | (*addr
<< 8); break;
113 case 7: context
->Ebx
= (context
->Ebx
& 0xffff00ff) | (*addr
<< 8); break;
117 /***********************************************************************
118 * INSTR_GetOperandAddr
120 * Return the address of an instruction operand (from the mod/rm byte).
122 static BYTE
*INSTR_GetOperandAddr( CONTEXT
*context
, BYTE
*instr
,
123 int long_addr
, int segprefix
, int *len
)
125 int mod
, rm
, base
= 0, index
= 0, ss
= 0, off
;
127 #define GET_VAL(val,type) \
128 { *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); }
131 GET_VAL( &mod
, BYTE
);
139 case 0: return (BYTE
*)&context
->Eax
;
140 case 1: return (BYTE
*)&context
->Ecx
;
141 case 2: return (BYTE
*)&context
->Edx
;
142 case 3: return (BYTE
*)&context
->Ebx
;
143 case 4: return (BYTE
*)&context
->Esp
;
144 case 5: return (BYTE
*)&context
->Ebp
;
145 case 6: return (BYTE
*)&context
->Esi
;
146 case 7: return (BYTE
*)&context
->Edi
;
155 GET_VAL( &sib
, BYTE
);
158 switch((sib
>> 3) & 7)
160 case 0: index
= context
->Eax
; break;
161 case 1: index
= context
->Ecx
; break;
162 case 2: index
= context
->Edx
; break;
163 case 3: index
= context
->Ebx
; break;
164 case 4: index
= 0; break;
165 case 5: index
= context
->Ebp
; break;
166 case 6: index
= context
->Esi
; break;
167 case 7: index
= context
->Edi
; break;
173 case 0: base
= context
->Eax
; break;
174 case 1: base
= context
->Ecx
; break;
175 case 2: base
= context
->Edx
; break;
176 case 3: base
= context
->Ebx
; break;
177 case 4: base
= context
->Esp
; break;
178 case 5: base
= context
->Ebp
; break;
179 case 6: base
= context
->Esi
; break;
180 case 7: base
= context
->Edi
; break;
185 if (rm
== 5) /* special case: ds:(disp32) */
187 GET_VAL( &base
, DWORD
);
191 case 1: /* 8-bit disp */
192 GET_VAL( &off
, BYTE
);
193 base
+= (signed char)off
;
196 case 2: /* 32-bit disp */
197 GET_VAL( &off
, DWORD
);
198 base
+= (signed long)off
;
202 else /* short address */
206 case 0: /* ds:(bx,si) */
207 base
= LOWORD(context
->Ebx
) + LOWORD(context
->Esi
);
209 case 1: /* ds:(bx,di) */
210 base
= LOWORD(context
->Ebx
) + LOWORD(context
->Edi
);
212 case 2: /* ss:(bp,si) */
213 base
= LOWORD(context
->Ebp
) + LOWORD(context
->Esi
);
215 case 3: /* ss:(bp,di) */
216 base
= LOWORD(context
->Ebp
) + LOWORD(context
->Edi
);
218 case 4: /* ds:(si) */
219 base
= LOWORD(context
->Esi
);
221 case 5: /* ds:(di) */
222 base
= LOWORD(context
->Edi
);
224 case 6: /* ss:(bp) */
225 base
= LOWORD(context
->Ebp
);
227 case 7: /* ds:(bx) */
228 base
= LOWORD(context
->Ebx
);
235 if (rm
== 6) /* special case: ds:(disp16) */
237 GET_VAL( &base
, WORD
);
241 case 1: /* 8-bit disp */
242 GET_VAL( &off
, BYTE
);
243 base
+= (signed char)off
;
246 case 2: /* 16-bit disp */
247 GET_VAL( &off
, WORD
);
248 base
+= (signed short)off
;
253 /* FIXME: we assume that all segments have a base of 0 */
254 return (BYTE
*)(base
+ (index
<< ss
));
259 /***********************************************************************
260 * emulate_instruction
262 * Emulate a privileged instruction.
263 * Returns exception continuation status.
265 static DWORD
emulate_instruction( EXCEPTION_RECORD
*rec
, CONTEXT
*context
)
267 int prefix
, segprefix
, prefixlen
, len
, long_op
, long_addr
;
270 long_op
= long_addr
= 1;
271 instr
= (BYTE
*)context
->Eip
;
272 if (!instr
) return ExceptionContinueSearch
;
274 /* First handle any possible prefix */
276 segprefix
= -1; /* no prefix */
284 segprefix
= context
->SegCs
;
287 segprefix
= context
->SegSs
;
290 segprefix
= context
->SegDs
;
293 segprefix
= context
->SegEs
;
296 segprefix
= context
->SegFs
;
299 segprefix
= context
->SegGs
;
302 long_op
= !long_op
; /* opcode size prefix */
305 long_addr
= !long_addr
; /* addr size prefix */
307 case 0xf0: /* lock */
309 case 0xf2: /* repne */
311 case 0xf3: /* repe */
314 prefix
= 0; /* no more prefixes */
324 /* Now look at the actual instruction */
328 case 0x0f: /* extended instruction */
331 case 0x22: /* mov eax, crX */
335 TRACE("mov eax,cr0 at 0x%08x, EAX=0x%08x\n", context
->Eip
,context
->Eax
);
336 context
->Eip
+= prefixlen
+3;
337 return ExceptionContinueExecution
;
339 TRACE("mov eax,cr4 at 0x%08x, EAX=0x%08x\n", context
->Eip
,context
->Eax
);
340 context
->Eip
+= prefixlen
+3;
341 return ExceptionContinueExecution
;
343 break; /*fallthrough to bad instruction handling */
345 ERR("Unsupported EAX -> CR register, eip+2 is %02x\n", instr
[2]);
346 break; /*fallthrough to bad instruction handling */
347 case 0x20: /* mov crX, eax */
350 case 0xe0: /* mov cr4, eax */
351 /* CR4 register . See linux/arch/i386/mm/init.c, X86_CR4_ defs
352 * bit 0: VME Virtual Mode Exception ?
353 * bit 1: PVI Protected mode Virtual Interrupt
354 * bit 2: TSD Timestamp disable
355 * bit 3: DE Debugging extensions
356 * bit 4: PSE Page size extensions
357 * bit 5: PAE Physical address extension
358 * bit 6: MCE Machine check enable
359 * bit 7: PGE Enable global pages
360 * bit 8: PCE Enable performance counters at IPL3
362 TRACE("mov cr4,eax at 0x%08x\n",context
->Eip
);
364 context
->Eip
+= prefixlen
+3;
365 return ExceptionContinueExecution
;
366 case 0xc0: /* mov cr0, eax */
367 TRACE("mov cr0,eax at 0x%08x\n",context
->Eip
);
368 context
->Eax
= 0x10; /* FIXME: set more bits ? */
369 context
->Eip
+= prefixlen
+3;
370 return ExceptionContinueExecution
;
371 default: /* fallthrough to illegal instruction */
374 /* fallthrough to illegal instruction */
376 case 0x21: /* mov drX, eax */
379 case 0xc8: /* mov dr1, eax */
380 TRACE("mov dr1,eax at 0x%08x\n",context
->Eip
);
381 context
->Eax
= context
->Dr1
;
382 context
->Eip
+= prefixlen
+3;
383 return ExceptionContinueExecution
;
384 case 0xf8: /* mov dr7, eax */
385 TRACE("mov dr7,eax at 0x%08x\n",context
->Eip
);
386 context
->Eax
= 0x400;
387 context
->Eip
+= prefixlen
+3;
388 return ExceptionContinueExecution
;
390 ERR("Unsupported DR register -> EAX, eip+2 is %02x\n", instr
[2]);
391 /* fallthrough to illegal instruction */
393 case 0x23: /* mov eax drX */
396 case 0xc0: /* mov eax, dr0 */
397 context
->Dr0
= context
->Eax
;
398 context
->Eip
+= prefixlen
+3;
399 return ExceptionContinueExecution
;
400 case 0xc8: /* mov eax, dr1 */
401 context
->Dr1
= context
->Eax
;
402 context
->Eip
+= prefixlen
+3;
403 return ExceptionContinueExecution
;
404 case 0xd0: /* mov eax, dr2 */
405 context
->Dr2
= context
->Eax
;
406 context
->Eip
+= prefixlen
+3;
407 return ExceptionContinueExecution
;
408 case 0xd8: /* mov eax, dr3 */
409 context
->Dr3
= context
->Eax
;
410 context
->Eip
+= prefixlen
+3;
411 return ExceptionContinueExecution
;
412 case 0xf8: /* mov eax, dr7 */
413 context
->Dr7
= context
->Eax
;
414 context
->Eip
+= prefixlen
+3;
415 return ExceptionContinueExecution
;
417 ERR("Unsupported EAX -> DR register, eip+2 is %02x\n", instr
[2]);
418 /* fallthrough to illegal instruction */
421 break; /* Unable to emulate it */
423 case 0x8a: /* mov Eb, Gb */
424 case 0x8b: /* mov Ev, Gv */
426 BYTE
*data
= INSTR_GetOperandAddr(context
, instr
+ 1, long_addr
,
428 unsigned int data_size
= (*instr
== 0x8b) ? (long_op
? 4 : 2) : 1;
429 struct idtr idtr
= get_idtr();
430 unsigned int offset
= data
- idtr
.base
;
432 if (offset
<= idtr
.limit
+ 1 - data_size
)
434 idt
[1].LimitLow
= 0x100; /* FIXME */
435 idt
[2].LimitLow
= 0x11E; /* FIXME */
436 idt
[3].LimitLow
= 0x500; /* FIXME */
440 case 0x8a: store_reg_byte( context
, instr
[1], (BYTE
*)idt
+ offset
); break;
441 case 0x8b: store_reg_word( context
, instr
[1], (BYTE
*)idt
+ offset
, long_op
); break;
443 context
->Eip
+= prefixlen
+ len
+ 1;
444 return ExceptionContinueExecution
;
446 break; /* Unable to emulate it */
451 context
->Eip
+= prefixlen
+ 1;
452 return ExceptionContinueExecution
;
454 return ExceptionContinueSearch
; /* Unable to emulate it */
458 /***********************************************************************
461 * Vectored exception handler used to emulate protected instructions
464 LONG CALLBACK
vectored_handler( EXCEPTION_POINTERS
*ptrs
)
466 EXCEPTION_RECORD
*record
= ptrs
->ExceptionRecord
;
467 CONTEXT
*context
= ptrs
->ContextRecord
;
469 if ((record
->ExceptionCode
== EXCEPTION_ACCESS_VIOLATION
||
470 record
->ExceptionCode
== EXCEPTION_PRIV_INSTRUCTION
))
472 if (emulate_instruction( record
, context
) == ExceptionContinueExecution
)
473 return EXCEPTION_CONTINUE_EXECUTION
;
475 return EXCEPTION_CONTINUE_SEARCH
;
478 #endif /* __i386__ */