From 45b2c868bb31d0d962d329474ce64463fdf1a6e2 Mon Sep 17 00:00:00 2001
From: Alexandre Julliard <julliard@winehq.org>
Date: Mon, 18 Sep 2000 02:15:07 +0000
Subject: [PATCH] Added preliminary support for switching to vm86 mode with
 proper exception handling.

---
 dlls/ntdll/signal_i386.c | 289 +++++++++++++++++++++++++++++++--------
 include/wine/exception.h |  10 ++
 2 files changed, 245 insertions(+), 54 deletions(-)

diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index d9997e64470..2cb33a0b175 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -25,6 +25,10 @@
 # endif
 #endif
 
+#ifdef HAVE_SYS_VM86_H
+# include <sys/vm86.h>
+#endif
+
 #include "selectors.h"
 
 /***********************************************************************
@@ -104,6 +108,22 @@ static inline int wine_sigaltstack( const struct sigaltstack *new,
 }
 #endif
 
+static inline int wine_vm86plus( int func, struct vm86plus_struct *ptr )
+{
+    int res;
+    __asm__ __volatile__( "pushl %%fs\n\t"
+                          "pushl %%ebx\n\t"
+                          "movl %2,%%ebx\n\t"
+                          "int $0x80\n\t"
+                          "popl %%ebx\n\t"
+                          "popl %%fs"
+                          : "=a" (res)
+                          : "0" (166 /*SYS_vm86*/), "g" (func), "c" (ptr) );
+    if (res >= 0) return res;
+    errno = -res;
+    return -1;
+}
+
 #endif  /* linux */
 
 #ifdef BSDI
@@ -332,7 +352,9 @@ static inline void handler_init( CONTEXT *context, const SIGCONTEXT *sigcontext
 #endif
     context->SegFs = fs;
     /* now restore a proper %fs for the fault handler */
-    if (!IS_SELECTOR_SYSTEM(CS_sig(sigcontext))) fs = SYSLEVEL_Win16CurrentTeb;
+    if ((EFL_sig(sigcontext) & 0x00020000) ||  /* vm86 mode */
+        !IS_SELECTOR_SYSTEM(CS_sig(sigcontext)))  /* 16-bit mode */
+        fs = SYSLEVEL_Win16CurrentTeb;
     if (!fs) fs = SYSLEVEL_EmergencyTeb;
     __set_fs(fs);
 }
@@ -351,6 +373,34 @@ static inline int get_trap_code( const SIGCONTEXT *sigcontext )
 #endif
 }
 
+/***********************************************************************
+ *           get_error_code
+ *
+ * Get the error code for a signal.
+ */
+static inline int get_error_code( const SIGCONTEXT *sigcontext )
+{
+#ifdef ERROR_sig
+    return ERROR_sig(sigcontext);
+#else
+    return 0;
+#endif
+}
+
+/***********************************************************************
+ *           get_cr2_value
+ *
+ * Get the CR2 value for a signal.
+ */
+static inline void *get_cr2_value( const SIGCONTEXT *sigcontext )
+{
+#ifdef CR2_sig
+    return (void *)CR2_sig(sigcontext);
+#else
+    return NULL;
+#endif
+}
+
 /***********************************************************************
  *           save_context
  *
@@ -484,31 +534,27 @@ static inline DWORD get_fpu_code( const CONTEXT *context )
 
 
 /**********************************************************************
- *		segv_handler
+ *		do_segv
  *
- * Handler for SIGSEGV and related errors.
+ * Implementation of SIGSEGV handler.
  */
-static HANDLER_DEF(segv_handler)
+static void do_segv( CONTEXT *context, int trap_code, void *cr2, int err_code )
 {
     EXCEPTION_RECORD rec;
-    CONTEXT context;
     DWORD page_fault_code = EXCEPTION_ACCESS_VIOLATION;
 
-    handler_init( &context, HANDLER_CONTEXT );
-
 #ifdef CR2_sig
     /* we want the page-fault case to be fast */
-    if (get_trap_code(HANDLER_CONTEXT) == T_PAGEFLT)
-        if (!(page_fault_code = VIRTUAL_HandleFault( (LPVOID)CR2_sig(HANDLER_CONTEXT) ))) return;
+    if (trap_code == T_PAGEFLT)
+        if (!(page_fault_code = VIRTUAL_HandleFault( cr2 ))) return;
 #endif
 
-    save_context( &context, HANDLER_CONTEXT );
     rec.ExceptionRecord  = NULL;
     rec.ExceptionFlags   = EXCEPTION_CONTINUABLE;
-    rec.ExceptionAddress = (LPVOID)context.Eip;
+    rec.ExceptionAddress = (LPVOID)context->Eip;
     rec.NumberParameters = 0;
 
-    switch(get_trap_code(HANDLER_CONTEXT))
+    switch(trap_code)
     {
     case T_OFLOW:   /* Overflow exception */
         rec.ExceptionCode = EXCEPTION_INT_OVERFLOW;
@@ -525,33 +571,30 @@ static HANDLER_DEF(segv_handler)
     case T_SEGNPFLT:  /* Segment not present exception */
     case T_PROTFLT:   /* General protection fault */
     case T_UNKNOWN:   /* Unknown fault code */
-        if (INSTR_EmulateInstruction( &context )) goto restore;
+        if (INSTR_EmulateInstruction( context )) return;
         rec.ExceptionCode = EXCEPTION_PRIV_INSTRUCTION;
         break;
     case T_PAGEFLT:  /* Page fault */
 #ifdef CR2_sig
         rec.NumberParameters = 2;
-#ifdef ERROR_sig
-        rec.ExceptionInformation[0] = (ERROR_sig(HANDLER_CONTEXT) & 2) != 0;
-#else
+        rec.ExceptionInformation[0] = (err_code & 2) != 0;
         rec.ExceptionInformation[0] = 0;
-#endif /* ERROR_sig */
-        rec.ExceptionInformation[1] = CR2_sig(HANDLER_CONTEXT);
+        rec.ExceptionInformation[1] = (DWORD)cr2;
 #endif /* CR2_sig */
         rec.ExceptionCode = page_fault_code;
         break;
     case T_ALIGNFLT:  /* Alignment check exception */
         /* FIXME: pass through exception handler first? */
-    	if (context.EFlags & 0x00040000)
+        if (context->EFlags & 0x00040000)
         {
             /* Disable AC flag, return */
-            context.EFlags &= ~0x00040000;
-            goto restore;
- 	}
+            context->EFlags &= ~0x00040000;
+            return;
+        }
         rec.ExceptionCode = EXCEPTION_DATATYPE_MISALIGNMENT;
         break;
     default:
-        ERR( "Got unexpected trap %d\n", get_trap_code(HANDLER_CONTEXT) );
+        ERR( "Got unexpected trap %d\n", trap_code );
         /* fall through */
     case T_NMI:       /* NMI interrupt */
     case T_DNA:       /* Device not available exception */
@@ -565,64 +608,51 @@ static HANDLER_DEF(segv_handler)
         rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
         break;
     }
-
-    EXC_RtlRaiseException( &rec, &context );
- restore:
-    restore_context( &context, HANDLER_CONTEXT );
+    EXC_RtlRaiseException( &rec, context );
 }
 
 
 /**********************************************************************
- *		trap_handler
+ *		do_trap
  *
- * Handler for SIGTRAP.
+ * Implementation of SIGTRAP handler.
  */
-static HANDLER_DEF(trap_handler)
+static void do_trap( CONTEXT *context, int trap_code )
 {
     EXCEPTION_RECORD rec;
-    CONTEXT context;
-
-    handler_init( &context, HANDLER_CONTEXT );
 
-    save_context( &context, HANDLER_CONTEXT );
     rec.ExceptionFlags   = EXCEPTION_CONTINUABLE;
     rec.ExceptionRecord  = NULL;
-    rec.ExceptionAddress = (LPVOID)context.Eip;
+    rec.ExceptionAddress = (LPVOID)context->Eip;
     rec.NumberParameters = 0;
 
-    switch(get_trap_code(HANDLER_CONTEXT))
+    switch(trap_code)
     {
     case T_TRCTRAP:  /* Single-step exception */
         rec.ExceptionCode = EXCEPTION_SINGLE_STEP;
-        context.EFlags &= ~0x100;  /* clear single-step flag */
+        context->EFlags &= ~0x100;  /* clear single-step flag */
         break;
     case T_BPTFLT:   /* Breakpoint exception */
-        rec.ExceptionAddress = (char *) rec.ExceptionAddress - 1;  /* back up over the int3 instruction */
+        rec.ExceptionAddress = (char *)rec.ExceptionAddress - 1;  /* back up over the int3 instruction */
         /* fall through */
     default:
         rec.ExceptionCode = EXCEPTION_BREAKPOINT;
         break;
     }
-    EXC_RtlRaiseException( &rec, &context );
-    restore_context( &context, HANDLER_CONTEXT );
+    EXC_RtlRaiseException( &rec, context );
 }
 
 
 /**********************************************************************
- *		fpe_handler
+ *		do_fpe
  *
- * Handler for SIGFPE.
+ * Implementation of SIGFPE handler
  */
-static HANDLER_DEF(fpe_handler)
+static void do_fpe( CONTEXT *context, int trap_code )
 {
     EXCEPTION_RECORD rec;
-    CONTEXT context;
 
-    handler_init( &context, HANDLER_CONTEXT );
-
-    save_fpu( &context, HANDLER_CONTEXT );
-
-    switch(get_trap_code(HANDLER_CONTEXT))
+    switch(trap_code)
     {
     case T_DIVIDE:   /* Division by zero exception */
         rec.ExceptionCode = EXCEPTION_INT_DIVIDE_BY_ZERO;
@@ -632,19 +662,64 @@ static HANDLER_DEF(fpe_handler)
         break;
     case T_ARITHTRAP:  /* Floating point exception */
     case T_UNKNOWN:    /* Unknown fault code */
-        rec.ExceptionCode = get_fpu_code( &context );
+        rec.ExceptionCode = get_fpu_code( context );
         break;
     default:
-        ERR( "Got unexpected trap %d\n", get_trap_code(HANDLER_CONTEXT) );
+        ERR( "Got unexpected trap %d\n", trap_code );
         rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION;
         break;
     }
-    save_context( &context, HANDLER_CONTEXT );
     rec.ExceptionFlags   = EXCEPTION_CONTINUABLE;
     rec.ExceptionRecord  = NULL;
-    rec.ExceptionAddress = (LPVOID)context.Eip;
+    rec.ExceptionAddress = (LPVOID)context->Eip;
     rec.NumberParameters = 0;
-    EXC_RtlRaiseException( &rec, &context );
+    EXC_RtlRaiseException( &rec, context );
+}
+
+
+/**********************************************************************
+ *		segv_handler
+ *
+ * Handler for SIGSEGV and related errors.
+ */
+static HANDLER_DEF(segv_handler)
+{
+    CONTEXT context;
+    handler_init( &context, HANDLER_CONTEXT );
+    save_context( &context, HANDLER_CONTEXT );
+    do_segv( &context, get_trap_code(HANDLER_CONTEXT),
+             get_cr2_value(HANDLER_CONTEXT), get_error_code(HANDLER_CONTEXT) );
+    restore_context( &context, HANDLER_CONTEXT );
+}
+
+
+/**********************************************************************
+ *		trap_handler
+ *
+ * Handler for SIGTRAP.
+ */
+static HANDLER_DEF(trap_handler)
+{
+    CONTEXT context;
+    handler_init( &context, HANDLER_CONTEXT );
+    save_context( &context, HANDLER_CONTEXT );
+    do_trap( &context, get_trap_code(HANDLER_CONTEXT) );
+    restore_context( &context, HANDLER_CONTEXT );
+}
+
+
+/**********************************************************************
+ *		fpe_handler
+ *
+ * Handler for SIGFPE.
+ */
+static HANDLER_DEF(fpe_handler)
+{
+    CONTEXT context;
+    handler_init( &context, HANDLER_CONTEXT );
+    save_fpu( &context, HANDLER_CONTEXT );
+    save_context( &context, HANDLER_CONTEXT );
+    do_fpe( &context, get_trap_code(HANDLER_CONTEXT) );
     restore_context( &context, HANDLER_CONTEXT );
     restore_fpu( &context, HANDLER_CONTEXT );
 }
@@ -754,6 +829,112 @@ BOOL SIGNAL_Init(void)
     return FALSE;
 }
 
+
+#ifdef linux
+/**********************************************************************
+ *		__wine_enter_vm86
+ *
+ * Enter vm86 mode with the specified register context.
+ */
+void __wine_enter_vm86( CONTEXT *context )
+{
+    EXCEPTION_RECORD rec;
+    int res;
+    struct vm86plus_struct vm86;
+
+    for (;;)
+    {
+        vm86.regs.eax    = context->Eax;
+        vm86.regs.ebx    = context->Ebx;
+        vm86.regs.ecx    = context->Ecx;
+        vm86.regs.edx    = context->Edx;
+        vm86.regs.esi    = context->Esi;
+        vm86.regs.edi    = context->Edi;
+        vm86.regs.esp    = context->Esp;
+        vm86.regs.ebp    = context->Ebp;
+        vm86.regs.eip    = context->Eip;
+        vm86.regs.cs     = context->SegCs;
+        vm86.regs.ds     = context->SegDs;
+        vm86.regs.es     = context->SegEs;
+        vm86.regs.fs     = context->SegFs;
+        vm86.regs.gs     = context->SegGs;
+        vm86.regs.eflags = context->EFlags;
+        if (vm86.regs.eflags & IF_MASK) vm86.regs.eflags |= VIF_MASK;
+
+        res = wine_vm86plus( VM86_ENTER, &vm86 );
+
+        context->Eax    = vm86.regs.eax;
+        context->Ebx    = vm86.regs.ebx;
+        context->Ecx    = vm86.regs.ecx;
+        context->Edx    = vm86.regs.edx;
+        context->Esi    = vm86.regs.esi;
+        context->Edi    = vm86.regs.edi;
+        context->Esp    = vm86.regs.esp;
+        context->Ebp    = vm86.regs.ebp;
+        context->Eip    = vm86.regs.eip;
+        context->SegCs  = vm86.regs.cs;
+        context->SegDs  = vm86.regs.ds;
+        context->SegEs  = vm86.regs.es;
+        context->SegFs  = vm86.regs.fs;
+        context->SegGs  = vm86.regs.gs;
+        context->EFlags = vm86.regs.eflags;
+
+        switch(VM86_TYPE(res))
+        {
+        case VM86_SIGNAL: /* return due to signal */
+            switch(VM86_ARG(res))
+            {
+            case SIGILL:
+            case SIGSEGV:
+#ifdef SIGBUS
+            case SIGBUS:
+#endif
+                do_segv( context, T_UNKNOWN, 0, 0 );
+                continue;
+            case SIGTRAP:
+                do_trap( context, T_UNKNOWN  );
+                continue;
+            case SIGFPE:
+                do_fpe( context, T_UNKNOWN );
+                continue;
+            }
+            rec.ExceptionCode = EXCEPTION_VM86_SIGNAL;
+            break;
+        case VM86_UNKNOWN: /* unhandled GP fault - IO-instruction or similar */
+            do_segv( context, T_PROTFLT, 0, 0 );
+            continue;
+        case VM86_TRAP: /* return due to DOS-debugger request */
+            do_trap( context, T_UNKNOWN  );
+            continue;
+        case VM86_INTx: /* int3/int x instruction (ARG = x) */
+            rec.ExceptionCode = EXCEPTION_VM86_INTx;
+            break;
+        case VM86_STI: /* sti/popf/iret instruction enabled virtual interrupts */
+            rec.ExceptionCode = EXCEPTION_VM86_STI;
+            break;
+        case VM86_PICRETURN: /* return due to pending PIC request */
+            rec.ExceptionCode = EXCEPTION_VM86_PICRETURN;
+            break;
+        default:
+            ERR( "unhandled result from vm86 mode %x\n", res );
+            continue;
+        }
+        rec.ExceptionFlags          = EXCEPTION_CONTINUABLE;
+        rec.ExceptionRecord         = NULL;
+        rec.ExceptionAddress        = (LPVOID)context->Eip;
+        rec.NumberParameters        = 1;
+        rec.ExceptionInformation[0] = VM86_ARG(res);
+        EXC_RtlRaiseException( &rec, context );
+    }
+}
+
+#else /* linux */
+void __wine_enter_vm86( CONTEXT *context )
+{
+    MESSAGE("vm86 mode not supported on this platform\n");
+}
+#endif /* linux */
+
 /**********************************************************************
  *		DbgBreakPoint   (NTDLL)
  */
diff --git a/include/wine/exception.h b/include/wine/exception.h
index 712d3dc0a0f..78352f4f4d8 100644
--- a/include/wine/exception.h
+++ b/include/wine/exception.h
@@ -165,6 +165,16 @@ static inline EXCEPTION_FRAME * WINE_UNUSED __wine_pop_frame( EXCEPTION_FRAME *f
 #endif
 }
 
+
+/* Wine-specific exceptions codes */
+
+/* unhandled return status from vm86 mode */
+#define EXCEPTION_VM86_SIGNAL     0x80000100
+#define EXCEPTION_VM86_INTx       0x80000101
+#define EXCEPTION_VM86_STI        0x80000102
+#define EXCEPTION_VM86_PICRETURN  0x80000103
+
+
 #ifdef __WINE__
 extern void WINAPI EXC_RtlRaiseException( PEXCEPTION_RECORD, PCONTEXT );
 extern BOOL SIGNAL_Init(void);
-- 
GitLab