2 * Emulation of priviledged instructions
4 * Copyright 1995 Alexandre Julliard
7 #include "wine/winuser16.h"
13 #include "sig_context.h"
14 #include "selectors.h"
15 #include "debugtools.h"
17 DECLARE_DEBUG_CHANNEL(int)
18 DECLARE_DEBUG_CHANNEL(io)
22 #define IS_V86(context) (EFL_sig(context)&V86_FLAG)
23 #define IS_SEL_32(context,seg) \
24 (IS_V86(context) ? FALSE : IS_SELECTOR_32BIT(seg))
26 #define STACK_sig(context) \
27 (IS_SEL_32(context,SS_sig(context)) ? ESP_sig(context) : (DWORD)SP_sig(context))
29 #define ADD_STACK_sig(context,offset) \
30 do { if (IS_SEL_32(context,SS_sig(context))) ESP_sig(context) += (offset); \
31 else SP_sig(context) += (offset); } while(0)
33 #define MAKE_PTR(seg,off) \
34 (IS_SELECTOR_SYSTEM(seg) ? (void *)(off) : PTR_SEG_OFF_TO_LIN(seg,off))
36 #define MK_PTR(context,seg,off) \
37 (IS_V86(context) ? DOSMEM_MapRealToLinear(MAKELONG(off,seg)) \
40 #define STACK_PTR(context) \
41 (IS_V86(context) ? DOSMEM_MapRealToLinear(MAKELONG(SP_sig(context),SS_sig(context))) : \
42 (IS_SELECTOR_SYSTEM(SS_sig(context)) ? (void *)ESP_sig(context) : \
43 (PTR_SEG_OFF_TO_LIN(SS_sig(context),STACK_sig(context)))))
45 /* For invalid registers fixup */
46 int (*INSTR_IsRelay)( const void *addr ) = NULL;
48 /***********************************************************************
49 * INSTR_ReplaceSelector
51 * Try to replace an invalid selector by a valid one.
52 * The only selector where it is allowed to do "mov ax,40;mov es,ax"
53 * is the so called 'bimodal' selector 0x40, which points to the BIOS
54 * data segment. Used by (at least) Borland products (and programs compiled
55 * using Borland products).
57 * See Undocumented Windows, Chapter 5, __0040.
59 static BOOL INSTR_ReplaceSelector( SIGCONTEXT *context, WORD *sel )
61 if (IS_SELECTOR_SYSTEM(CS_sig(context)))
62 if (INSTR_IsRelay && INSTR_IsRelay( (void *)EIP_sig(context) ))
64 /* Saved selector may have become invalid when the relay code */
65 /* tries to restore it. We simply clear it. */
72 static WORD sys_timer = 0;
74 sys_timer = CreateSystemTimer( 55, DOSMEM_Tick );
75 *sel = DOSMEM_BiosDataSeg;
78 if (!IS_SELECTOR_SYSTEM(*sel) && !IS_SELECTOR_FREE(*sel))
79 ERR_(int)("Got protection fault on valid selector, maybe your kernel is too old?\n" );
80 return FALSE; /* Can't replace selector, crashdump */
84 /***********************************************************************
85 * INSTR_GetOperandAddr
87 * Return the address of an instruction operand (from the mod/rm byte).
89 static BYTE *INSTR_GetOperandAddr( SIGCONTEXT *context, BYTE *instr,
90 int long_addr, int segprefix, int *len )
92 int mod, rm, base, index = 0, ss = 0, seg = 0, off;
94 #define GET_VAL(val,type) \
95 { *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); }
98 GET_VAL( &mod, BYTE );
106 case 0: return (BYTE *)&EAX_sig(context);
107 case 1: return (BYTE *)&ECX_sig(context);
108 case 2: return (BYTE *)&EDX_sig(context);
109 case 3: return (BYTE *)&EBX_sig(context);
110 case 4: return (BYTE *)&ESP_sig(context);
111 case 5: return (BYTE *)&EBP_sig(context);
112 case 6: return (BYTE *)&ESI_sig(context);
113 case 7: return (BYTE *)&EDI_sig(context);
122 GET_VAL( &sib, BYTE );
127 case 0: index = EAX_sig(context); break;
128 case 1: index = ECX_sig(context); break;
129 case 2: index = EDX_sig(context); break;
130 case 3: index = EBX_sig(context); break;
131 case 4: index = 0; break;
132 case 5: index = EBP_sig(context); break;
133 case 6: index = ESI_sig(context); break;
134 case 7: index = EDI_sig(context); break;
140 case 0: base = EAX_sig(context); seg = DS_sig(context); break;
141 case 1: base = ECX_sig(context); seg = DS_sig(context); break;
142 case 2: base = EDX_sig(context); seg = DS_sig(context); break;
143 case 3: base = EBX_sig(context); seg = DS_sig(context); break;
144 case 4: base = ESP_sig(context); seg = SS_sig(context); break;
145 case 5: base = EBP_sig(context); seg = SS_sig(context); break;
146 case 6: base = ESI_sig(context); seg = DS_sig(context); break;
147 case 7: base = EDI_sig(context); seg = DS_sig(context); break;
152 if (rm == 5) /* special case: ds:(disp32) */
154 GET_VAL( &base, DWORD );
155 seg = DS_sig(context);
159 case 1: /* 8-bit disp */
160 GET_VAL( &off, BYTE );
161 base += (signed char)off;
164 case 2: /* 32-bit disp */
165 GET_VAL( &off, DWORD );
166 base += (signed long)off;
170 else /* short address */
174 case 0: /* ds:(bx,si) */
175 base = BX_sig(context) + SI_sig(context);
176 seg = DS_sig(context);
178 case 1: /* ds:(bx,di) */
179 base = BX_sig(context) + DI_sig(context);
180 seg = DS_sig(context);
182 case 2: /* ss:(bp,si) */
183 base = BP_sig(context) + SI_sig(context);
184 seg = SS_sig(context);
186 case 3: /* ss:(bp,di) */
187 base = BP_sig(context) + DI_sig(context);
188 seg = SS_sig(context);
190 case 4: /* ds:(si) */
191 base = SI_sig(context);
192 seg = DS_sig(context);
194 case 5: /* ds:(di) */
195 base = DI_sig(context);
196 seg = DS_sig(context);
198 case 6: /* ss:(bp) */
199 base = BP_sig(context);
200 seg = SS_sig(context);
202 case 7: /* ds:(bx) */
203 base = BX_sig(context);
204 seg = DS_sig(context);
211 if (rm == 6) /* special case: ds:(disp16) */
213 GET_VAL( &base, WORD );
214 seg = DS_sig(context);
218 case 1: /* 8-bit disp */
219 GET_VAL( &off, BYTE );
220 base += (signed char)off;
223 case 2: /* 16-bit disp */
224 GET_VAL( &off, WORD );
225 base += (signed short)off;
230 if (segprefix != -1) seg = segprefix;
232 /* Make sure the segment and offset are valid */
233 if (IS_SELECTOR_SYSTEM(seg)) return (BYTE *)(base + (index << ss));
234 if (((seg & 7) != 7) || IS_SELECTOR_FREE(seg)) return NULL;
235 if (GET_SEL_LIMIT(seg) < (base + (index << ss))) return NULL;
236 return (BYTE *)PTR_SEG_OFF_TO_LIN( seg, (base + (index << ss)) );
241 /***********************************************************************
244 * Emulate the LDS (and LES,LFS,etc.) instruction.
246 static BOOL INSTR_EmulateLDS( SIGCONTEXT *context, BYTE *instr, int long_op,
247 int long_addr, int segprefix, int *len )
250 BYTE *regmodrm = instr + 1 + (*instr == 0x0f);
251 BYTE *addr = INSTR_GetOperandAddr( context, regmodrm,
252 long_addr, segprefix, len );
254 return FALSE; /* Unable to emulate it */
255 seg = *(WORD *)(addr + (long_op ? 4 : 2));
257 if (!INSTR_ReplaceSelector( context, &seg ))
258 return FALSE; /* Unable to emulate it */
260 /* Now store the offset in the correct register */
262 switch((*regmodrm >> 3) & 7)
265 if (long_op) EAX_sig(context) = *(DWORD *)addr;
266 else AX_sig(context) = *(WORD *)addr;
269 if (long_op) ECX_sig(context) = *(DWORD *)addr;
270 else CX_sig(context) = *(WORD *)addr;
273 if (long_op) EDX_sig(context) = *(DWORD *)addr;
274 else DX_sig(context) = *(WORD *)addr;
277 if (long_op) EBX_sig(context) = *(DWORD *)addr;
278 else BX_sig(context) = *(WORD *)addr;
281 if (long_op) ESP_sig(context) = *(DWORD *)addr;
282 else SP_sig(context) = *(WORD *)addr;
285 if (long_op) EBP_sig(context) = *(DWORD *)addr;
286 else BP_sig(context) = *(WORD *)addr;
289 if (long_op) ESI_sig(context) = *(DWORD *)addr;
290 else SI_sig(context) = *(WORD *)addr;
293 if (long_op) EDI_sig(context) = *(DWORD *)addr;
294 else DI_sig(context) = *(WORD *)addr;
298 /* Store the correct segment in the segment register */
302 case 0xc4: ES_sig(context) = seg; break; /* les */
303 case 0xc5: DS_sig(context) = seg; break; /* lds */
304 case 0x0f: switch(instr[1])
306 case 0xb2: SS_sig(context) = seg; break; /* lss */
308 case 0xb4: FS_sig(context) = seg; break; /* lfs */
311 case 0xb5: GS_sig(context) = seg; break; /* lgs */
317 /* Add the opcode size to the total length */
319 *len += 1 + (*instr == 0x0f);
324 /***********************************************************************
325 * INSTR_EmulateInstruction
327 * Emulate a priviledged instruction. Returns TRUE if emulation successful.
329 BOOL INSTR_EmulateInstruction( SIGCONTEXT *context )
331 int prefix, segprefix, prefixlen, len, repX, long_op, long_addr;
335 long_op = long_addr = IS_SEL_32(context,CS_sig(context));
336 instr = (BYTE *)MK_PTR(context,CS_sig(context),EIP_sig(context));
337 if (!instr) return FALSE;
339 /* First handle any possible prefix */
341 segprefix = -1; /* no prefix */
350 segprefix = CS_sig(context);
353 segprefix = SS_sig(context);
356 segprefix = DS_sig(context);
359 segprefix = ES_sig(context);
363 segprefix = FS_sig(context);
368 segprefix = GS_sig(context);
372 long_op = !long_op; /* opcode size prefix */
375 long_addr = !long_addr; /* addr size prefix */
377 case 0xf0: /* lock */
379 case 0xf2: /* repne */
382 case 0xf3: /* repe */
386 prefix = 0; /* no more prefixes */
396 /* Now look at the actual instruction */
400 case 0x07: /* pop es */
401 case 0x17: /* pop ss */
402 case 0x1f: /* pop ds */
404 WORD seg = *(WORD *)STACK_PTR( context );
405 if (INSTR_ReplaceSelector( context, &seg ))
409 case 0x07: ES_sig(context) = seg; break;
410 case 0x17: SS_sig(context) = seg; break;
411 case 0x1f: DS_sig(context) = seg; break;
413 ADD_STACK_sig(context, long_op ? 4 : 2);
414 EIP_sig(context) += prefixlen + 1;
418 break; /* Unable to emulate it */
420 case 0x0f: /* extended instruction */
423 case 0x22: /* mov eax, crX */
426 fprintf(stderr,"mov eax,cr0 at 0x%08lx, EAX=0x%08lx\n",
427 EIP_sig(context),EAX_sig(context)
429 EIP_sig(context) += prefixlen+3;
432 break; /*fallthrough to bad instruction handling */
434 break; /*fallthrough to bad instruction handling */
435 case 0x20: /* mov crX, eax */
437 case 0xe0: /* mov cr4, eax */
438 /* CR4 register . See linux/arch/i386/mm/init.c, X86_CR4_ defs
439 * bit 0: VME Virtual Mode Exception ?
440 * bit 1: PVI Protected mode Virtual Interrupt
441 * bit 2: TSD Timestamp disable
442 * bit 3: DE Debugging extensions
443 * bit 4: PSE Page size extensions
444 * bit 5: PAE Physical address extension
445 * bit 6: MCE Machine check enable
446 * bit 7: PGE Enable global pages
447 * bit 8: PCE Enable performance counters at IPL3
449 fprintf(stderr,"mov cr4,eax at 0x%08lx\n",EIP_sig(context));
450 EAX_sig(context) = 0;
451 EIP_sig(context) += prefixlen+3;
453 case 0xc0: /* mov cr0, eax */
454 fprintf(stderr,"mov cr0,eax at 0x%08lx\n",EIP_sig(context));
455 EAX_sig(context) = 0x10; /* FIXME: set more bits ? */
456 EIP_sig(context) += prefixlen+3;
458 default: /* fallthrough to illegal instruction */
461 /* fallthrough to illegal instruction */
464 case 0xa1: /* pop fs */
466 WORD seg = *(WORD *)STACK_PTR( context );
467 if (INSTR_ReplaceSelector( context, &seg ))
469 FS_sig(context) = seg;
470 ADD_STACK_sig(context, long_op ? 4 : 2);
471 EIP_sig(context) += prefixlen + 2;
479 case 0xa9: /* pop gs */
481 WORD seg = *(WORD *)STACK_PTR( context );
482 if (INSTR_ReplaceSelector( context, &seg ))
484 GS_sig(context) = seg;
485 ADD_STACK_sig(context, long_op ? 4 : 2);
486 EIP_sig(context) += prefixlen + 2;
493 case 0xb2: /* lss addr,reg */
495 case 0xb4: /* lfs addr,reg */
498 case 0xb5: /* lgs addr,reg */
500 if (INSTR_EmulateLDS( context, instr, long_op,
501 long_addr, segprefix, &len ))
503 EIP_sig(context) += prefixlen + len;
508 break; /* Unable to emulate it */
510 case 0x6c: /* insb */
511 case 0x6d: /* insw/d */
512 case 0x6e: /* outsb */
513 case 0x6f: /* outsw/d */
515 int typ = *instr; /* Just in case it's overwritten. */
516 int outp = (typ >= 0x6e);
517 unsigned long count = repX ?
518 (long_addr ? ECX_sig(context) : CX_sig(context)) : 1;
519 int opsize = (typ & 1) ? (long_op ? 4 : 2) : 1;
520 int step = (EFL_sig(context) & 0x400) ? -opsize : +opsize;
521 int seg = outp ? DS_sig(context) : ES_sig(context); /* FIXME: is this right? */
524 /* FIXME: Check segment readable. */
527 /* FIXME: Check segment writeable. */
533 ECX_sig(context) = 0;
543 data = MK_PTR(context, seg,
544 long_addr ? ESI_sig(context) : SI_sig(context));
545 if (long_addr) ESI_sig(context) += step;
546 else SI_sig(context) += step;
550 data = MK_PTR(context, seg,
551 long_addr ? EDI_sig(context) : DI_sig(context));
552 if (long_addr) EDI_sig(context) += step;
553 else DI_sig(context) += step;
559 *((BYTE *)data) = IO_inport( DX_sig(context), 1);
560 TRACE_(io)("0x%x < %02x @ %04x:%04x\n", DX_sig(context),
561 *((BYTE *)data), CS_sig(context), IP_sig(context));
566 *((DWORD *)data) = IO_inport( DX_sig(context), 4);
567 TRACE_(io)("0x%x < %08lx @ %04x:%04x\n", DX_sig(context),
568 *((DWORD *)data), CS_sig(context), IP_sig(context));
572 *((WORD *)data) = IO_inport( DX_sig(context), 2);
573 TRACE_(io)("0x%x < %04x @ %04x:%04x\n", DX_sig(context),
574 *((WORD *)data), CS_sig(context), IP_sig(context));
578 IO_outport( DX_sig(context), 1, *((BYTE *)data));
579 TRACE_(io)("0x%x > %02x @ %04x:%04x\n", DX_sig(context),
580 *((BYTE *)data), CS_sig(context), IP_sig(context));
585 IO_outport( DX_sig(context), 4, *((DWORD *)data));
586 TRACE_(io)("0x%x > %08lx @ %04x:%04x\n", DX_sig(context),
587 *((DWORD *)data), CS_sig(context), IP_sig(context));
591 IO_outport( DX_sig(context), 2, *((WORD *)data));
592 TRACE_(io)("0x%x > %04x @ %04x:%04x\n", DX_sig(context),
593 *((WORD *)data), CS_sig(context), IP_sig(context));
598 EIP_sig(context) += prefixlen + 1;
602 case 0x8e: /* mov XX,segment_reg */
605 BYTE *addr = INSTR_GetOperandAddr(context, instr + 1,
606 long_addr, segprefix, &len );
608 break; /* Unable to emulate it */
610 if (!INSTR_ReplaceSelector( context, &seg ))
611 break; /* Unable to emulate it */
613 switch((instr[1] >> 3) & 7)
616 ES_sig(context) = seg;
617 EIP_sig(context) += prefixlen + len + 1;
622 SS_sig(context) = seg;
623 EIP_sig(context) += prefixlen + len + 1;
626 DS_sig(context) = seg;
627 EIP_sig(context) += prefixlen + len + 1;
631 FS_sig(context) = seg;
632 EIP_sig(context) += prefixlen + len + 1;
637 GS_sig(context) = seg;
638 EIP_sig(context) += prefixlen + len + 1;
646 break; /* Unable to emulate it */
648 case 0xc4: /* les addr,reg */
649 case 0xc5: /* lds addr,reg */
650 if (INSTR_EmulateLDS( context, instr, long_op,
651 long_addr, segprefix, &len ))
653 EIP_sig(context) += prefixlen + len;
656 break; /* Unable to emulate it */
658 case 0xcd: /* int <XX> */
661 ERR_(int)("int xx from 32-bit code is not supported.\n");
662 break; /* Unable to emulate it */
666 FARPROC16 addr = INT_GetPMHandler( instr[1] );
667 WORD *stack = (WORD *)STACK_PTR( context );
668 /* Push the flags and return address on the stack */
669 *(--stack) = FL_sig(context);
670 *(--stack) = CS_sig(context);
671 *(--stack) = IP_sig(context) + prefixlen + 2;
672 ADD_STACK_sig(context, -3 * sizeof(WORD));
673 /* Jump to the interrupt handler */
674 CS_sig(context) = HIWORD(addr);
675 EIP_sig(context) = LOWORD(addr);
679 case 0xcf: /* iret */
682 DWORD *stack = (DWORD *)STACK_PTR( context );
683 EIP_sig(context) = *stack++;
684 CS_sig(context) = *stack++;
685 EFL_sig(context) = *stack;
686 ADD_STACK_sig(context, 3*sizeof(DWORD)); /* Pop the return address and flags */
690 WORD *stack = (WORD *)STACK_PTR( context );
691 EIP_sig(context) = *stack++;
692 CS_sig(context) = *stack++;
693 FL_sig(context) = *stack;
694 ADD_STACK_sig(context, 3*sizeof(WORD)); /* Pop the return address and flags */
698 case 0xe4: /* inb al,XX */
699 AL_sig(context) = IO_inport( instr[1], 1 );
700 TRACE_(io)("0x%x < %02x @ %04x:%04x\n", instr[1],
701 AL_sig(context), CS_sig(context), IP_sig(context));
702 EIP_sig(context) += prefixlen + 2;
705 case 0xe5: /* in (e)ax,XX */
708 EAX_sig(context) = IO_inport( instr[1], 4 );
709 TRACE_(io)("0x%x < %08lx @ %04x:%04x\n", instr[1],
710 EAX_sig(context), CS_sig(context), IP_sig(context));
714 AX_sig(context) = IO_inport( instr[1], 2 );
715 TRACE_(io)("0x%x < %04x @ %04x:%04x\n", instr[1],
716 AX_sig(context), CS_sig(context), IP_sig(context));
718 EIP_sig(context) += prefixlen + 2;
721 case 0xe6: /* outb XX,al */
722 IO_outport( instr[1], 1, AL_sig(context) );
723 TRACE_(io)("0x%x > %02x @ %04x:%04x\n", instr[1],
724 AL_sig(context), CS_sig(context), IP_sig(context));
725 EIP_sig(context) += prefixlen + 2;
728 case 0xe7: /* out XX,(e)ax */
731 IO_outport( instr[1], 4, EAX_sig(context) );
732 TRACE_(io)("0x%x > %08lx @ %04x:%04x\n", instr[1],
733 EAX_sig(context), CS_sig(context), IP_sig(context));
737 IO_outport( instr[1], 2, AX_sig(context) );
738 TRACE_(io)("0x%x > %04x @ %04x:%04x\n", instr[1],
739 AX_sig(context), CS_sig(context), IP_sig(context));
741 EIP_sig(context) += prefixlen + 2;
744 case 0xec: /* inb al,dx */
745 AL_sig(context) = IO_inport( DX_sig(context), 1 );
746 TRACE_(io)("0x%x < %02x @ %04x:%04x\n", DX_sig(context),
747 AL_sig(context), CS_sig(context), IP_sig(context));
748 EIP_sig(context) += prefixlen + 1;
751 case 0xed: /* in (e)ax,dx */
754 EAX_sig(context) = IO_inport( DX_sig(context), 4 );
755 TRACE_(io)("0x%x < %08lx @ %04x:%04x\n", DX_sig(context),
756 EAX_sig(context), CS_sig(context), IP_sig(context));
760 AX_sig(context) = IO_inport( DX_sig(context), 2 );
761 TRACE_(io)("0x%x < %04x @ %04x:%04x\n", DX_sig(context),
762 AX_sig(context), CS_sig(context), IP_sig(context));
764 EIP_sig(context) += prefixlen + 1;
767 case 0xee: /* outb dx,al */
768 IO_outport( DX_sig(context), 1, AL_sig(context) );
769 TRACE_(io)("0x%x > %02x @ %04x:%04x\n", DX_sig(context),
770 AL_sig(context), CS_sig(context), IP_sig(context));
771 EIP_sig(context) += prefixlen + 1;
774 case 0xef: /* out dx,(e)ax */
777 IO_outport( DX_sig(context), 4, EAX_sig(context) );
778 TRACE_(io)("0x%x > %08lx @ %04x:%04x\n", DX_sig(context),
779 EAX_sig(context), CS_sig(context), IP_sig(context));
783 IO_outport( DX_sig(context), 2, AX_sig(context) );
784 TRACE_(io)("0x%x > %04x @ %04x:%04x\n", DX_sig(context),
785 AX_sig(context), CS_sig(context), IP_sig(context));
787 EIP_sig(context) += prefixlen + 1;
790 case 0xfa: /* cli, ignored */
791 EIP_sig(context) += prefixlen + 1;
794 case 0xfb: /* sti, ignored */
795 EIP_sig(context) += prefixlen + 1;
800 /* Check for Win16 __GP handler */
801 gpHandler = HasGPHandler16( PTR_SEG_OFF_TO_SEGPTR( CS_sig(context),
802 EIP_sig(context) ) );
805 WORD *stack = (WORD *)STACK_PTR( context );
806 *--stack = CS_sig(context);
807 *--stack = EIP_sig(context);
808 ADD_STACK_sig(context, -2*sizeof(WORD));
810 CS_sig(context) = SELECTOROF( gpHandler );
811 EIP_sig(context) = OFFSETOF( gpHandler );
814 return FALSE; /* Unable to emulate it */
817 #endif /* __i386__ */