From 472184e5801de5d1fb92d275d9c0c7e840c9a0bf Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Tue, 11 Nov 2014 03:11:33 +0100
Subject: ntdll: Implement emulation of SIDT instruction when using Exagear.

---
 configure.ac             |   8 ++
 dlls/ntdll/signal_i386.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 231 insertions(+)

diff --git a/configure.ac b/configure.ac
index 1e6bba3..43bf0db 100644
--- a/configure.ac
+++ b/configure.ac
@@ -32,6 +32,7 @@ AC_ARG_ENABLE(win16, AS_HELP_STRING([--disable-win16],[do not include Win16 supp
 AC_ARG_ENABLE(win64, AS_HELP_STRING([--enable-win64],[build a Win64 emulator on AMD64 (won't run Win32 binaries)]))
 AC_ARG_ENABLE(tests, AS_HELP_STRING([--disable-tests],[do not build the regression tests]))
 AC_ARG_ENABLE(maintainer-mode, AS_HELP_STRING([--enable-maintainer-mode],[enable maintainer-specific build rules]))
+AC_ARG_ENABLE(exagear-compat, AS_HELP_STRING([--enable-exagear-compat],[use workarounds for known problems in the Exagear emulator]))
 
 AC_ARG_WITH(alsa,      AS_HELP_STRING([--without-alsa],[do not use the Alsa sound support]),
             [if test "x$withval" = "xno"; then ac_cv_header_sys_asoundlib_h=no; ac_cv_header_alsa_asoundlib_h=no; fi])
@@ -364,6 +365,13 @@ WINE_WARNING_WITH(gettext,[test "$MSGFMT" = false],
                   [gettext tools not found (or too old), translations won't be built.],
                   [enable_po])
 
+dnl **** Enable Exagear workarounds ****
+
+if test "x$enable_exagear_compat" = "xyes"
+then
+  AC_DEFINE(EXAGEAR_COMPAT, 1, [Define if you want to enable Exagear emulator workarounds])
+fi
+
 dnl **** Check for some libraries ****
 
 dnl Check for -li386 for NetBSD and OpenBSD
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index ee8855a..4269329 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -96,6 +96,14 @@ typedef struct
     BYTE Reserved4[96];
 } XMM_SAVE_AREA32;
 
+#include "pshpack1.h"
+struct idtr
+{
+    WORD  limit;
+    BYTE *base;
+};
+#include "poppack.h"
+
 /***********************************************************************
  * signal context platform-specific definitions
  */
@@ -1898,6 +1906,213 @@ static inline DWORD get_fpu_code( const CONTEXT *context )
 }
 
 
+#ifdef EXAGEAR_COMPAT
+
+/***********************************************************************
+ *           INSTR_GetOperandAddr
+ *
+ * Return the address of an instruction operand (from the mod/rm byte).
+ */
+static BYTE *INSTR_GetOperandAddr( CONTEXT *context, const BYTE *instr,
+                                   int long_addr, int segprefix, int *len )
+{
+    int mod, rm, base = 0, index = 0, ss = 0, off;
+
+#define GET_VAL(val,type) \
+    { *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); }
+
+    *len = 0;
+    GET_VAL( &mod, BYTE );
+    rm = mod & 7;
+    mod >>= 6;
+
+    if (mod == 3)
+    {
+        switch(rm)
+        {
+        case 0: return (BYTE *)&context->Eax;
+        case 1: return (BYTE *)&context->Ecx;
+        case 2: return (BYTE *)&context->Edx;
+        case 3: return (BYTE *)&context->Ebx;
+        case 4: return (BYTE *)&context->Esp;
+        case 5: return (BYTE *)&context->Ebp;
+        case 6: return (BYTE *)&context->Esi;
+        case 7: return (BYTE *)&context->Edi;
+        }
+    }
+
+    if (long_addr)
+    {
+        if (rm == 4)
+        {
+            BYTE sib;
+            GET_VAL( &sib, BYTE );
+            rm = sib & 7;
+            ss = sib >> 6;
+            switch((sib >> 3) & 7)
+            {
+            case 0: index = context->Eax; break;
+            case 1: index = context->Ecx; break;
+            case 2: index = context->Edx; break;
+            case 3: index = context->Ebx; break;
+            case 4: index = 0; break;
+            case 5: index = context->Ebp; break;
+            case 6: index = context->Esi; break;
+            case 7: index = context->Edi; break;
+            }
+        }
+
+        switch(rm)
+        {
+        case 0: base = context->Eax; break;
+        case 1: base = context->Ecx; break;
+        case 2: base = context->Edx; break;
+        case 3: base = context->Ebx; break;
+        case 4: base = context->Esp; break;
+        case 5: base = context->Ebp; break;
+        case 6: base = context->Esi; break;
+        case 7: base = context->Edi; break;
+        }
+        switch (mod)
+        {
+        case 0:
+            if (rm == 5)  /* special case: ds:(disp32) */
+            {
+                GET_VAL( &base, DWORD );
+            }
+            break;
+
+        case 1:  /* 8-bit disp */
+            GET_VAL( &off, BYTE );
+            base += (signed char)off;
+            break;
+
+        case 2:  /* 32-bit disp */
+            GET_VAL( &off, DWORD );
+            base += (signed long)off;
+            break;
+        }
+    }
+    else  /* short address */
+    {
+        switch(rm)
+        {
+        case 0:  /* ds:(bx,si) */
+            base = LOWORD(context->Ebx) + LOWORD(context->Esi);
+            break;
+        case 1:  /* ds:(bx,di) */
+            base = LOWORD(context->Ebx) + LOWORD(context->Edi);
+            break;
+        case 2:  /* ss:(bp,si) */
+            base = LOWORD(context->Ebp) + LOWORD(context->Esi);
+            break;
+        case 3:  /* ss:(bp,di) */
+            base = LOWORD(context->Ebp) + LOWORD(context->Edi);
+            break;
+        case 4:  /* ds:(si) */
+            base = LOWORD(context->Esi);
+            break;
+        case 5:  /* ds:(di) */
+            base = LOWORD(context->Edi);
+            break;
+        case 6:  /* ss:(bp) */
+            base = LOWORD(context->Ebp);
+            break;
+        case 7:  /* ds:(bx) */
+            base = LOWORD(context->Ebx);
+            break;
+        }
+
+        switch(mod)
+        {
+        case 0:
+            if (rm == 6)  /* special case: ds:(disp16) */
+            {
+                GET_VAL( &base, WORD );
+            }
+            break;
+
+        case 1:  /* 8-bit disp */
+            GET_VAL( &off, BYTE );
+            base += (signed char)off;
+            break;
+
+        case 2:  /* 16-bit disp */
+            GET_VAL( &off, WORD );
+            base += (signed short)off;
+            break;
+        }
+        base &= 0xffff;
+    }
+    /* FIXME: we assume that all segments have a base of 0 */
+    return (BYTE *)(base + (index << ss));
+#undef GET_VAL
+}
+
+
+/***********************************************************************
+ *           check_invalid_instr
+ *
+ * Support for instructions not implemented by Exagear.
+ */
+static inline BOOL check_invalid_instr( CONTEXT *context )
+{
+    const BYTE *instr;
+    unsigned int prefix_count = 0;
+    int len, long_addr = 1;
+
+    if (!wine_ldt_is_system( context->SegCs )) return FALSE;
+    instr = (BYTE *)context->Eip;
+
+    for (;;) switch (*instr)
+    {
+    /* instruction prefixes */
+    case 0x2e:  /* %cs: */
+    case 0x36:  /* %ss: */
+    case 0x3e:  /* %ds: */
+    case 0x26:  /* %es: */
+    case 0x64:  /* %fs: */
+    case 0x65:  /* %gs: */
+    case 0x66:  /* opcode size */
+    case 0x67:  /* addr size */
+    case 0xf0:  /* lock */
+    case 0xf2:  /* repne */
+    case 0xf3:  /* repe */
+        if (++prefix_count >= 15) return FALSE;
+        if (*instr == 0x67) long_addr = !long_addr; /* addr size */
+        instr++;
+        continue;
+    case 0x0f: /* extended instruction */
+        switch (instr[1])
+        {
+        case 0x01:
+            if (((instr[2] >> 3) & 7) == 1) /* sidt m */
+            {
+                struct idtr ret;
+                BYTE *addr;
+
+                if ((instr[2] >> 6) == 3) return FALSE; /* loading to register not allowed */
+                addr = INSTR_GetOperandAddr( context, instr + 2, long_addr, 0, &len );
+
+                /* fake IDT structure */
+                ret.limit = 0xfff;
+                ret.base  = (void *)0xff000000;
+                memcpy(addr, &ret, sizeof(ret));
+
+                context->Eip += prefix_count + len + 2;
+                return TRUE;
+            }
+            break;
+        }
+        return FALSE;
+    default:
+        return FALSE;
+    }
+}
+
+#endif /* EXAGEAR_COMPAT */
+
+
 /**********************************************************************
  *		raise_segv_exception
  */
@@ -1907,6 +2122,14 @@ static void WINAPI raise_segv_exception( EXCEPTION_RECORD *rec, CONTEXT *context
 
     switch(rec->ExceptionCode)
     {
+#ifdef EXAGEAR_COMPAT
+    case EXCEPTION_ILLEGAL_INSTRUCTION:
+        {
+            if (check_invalid_instr( context ))
+                goto done;
+        }
+        break;
+#endif
     case EXCEPTION_ACCESS_VIOLATION:
         if (rec->NumberParameters == 2)
         {
-- 
2.7.1

