another vm86 patch

Ove Kaaven ovek at arcticnet.no
Mon Jun 18 10:32:53 CDT 2001


Here's some more of that DOS code I was working on a few months back; this
patch makes that __wine_enter_vm86 function usable for pretty much
everything I tried it on. It became a bit messy, but when combining
signal-safety, thread-safety, and high performance expectations with the
somewhat flawed vm86 syscall, what can be expected...

The idea here is that reception of the signals SIGALRM or SIGUSR2 at any
time will *always* force a EXCEPTION_VM86_STI, as soon as possible. If
vm86 is running, it's thrown immediately (from inside the signal handler),
if interrupts are enabled. Otherwise, it's marked pending and thrown from
inside __wine_enter_vm86 when appropriate.

This way, I can both accommodate a relatively high speed timer in the vm86
thread, and also a separate thread that runs a message loop and sends the
vm86 thread a signal (SIGUSR2) when it got something, like we used to do
with dosmod. Except that the new model improved the performance an order
of magnitude, of course...

Unfortunately, full-screen DOS apps won't work with my revised
dlls/winedos code in the current Wine, because of assertion failures in
server/console.c line 194 when making the console complex (creating an
xterm), probably since my winedos message loop is always waiting for
console input, so I don't think I can submit the patch that finally gets
rid of dosmod in favor of __wine_enter_vm86 yet, unless Alexandre can deal
with the assertion failure?

Log:
Extended __wine_enter_vm86 to handle pending interrupts.

Index: include/thread.h
===================================================================
RCS file: /home/wine/wine/include/thread.h,v
retrieving revision 1.53
diff -u -r1.53 thread.h
--- include/thread.h	2001/05/18 23:21:22	1.53
+++ include/thread.h	2001/06/15 20:32:20
@@ -102,10 +102,14 @@
     void        *pthread_data;   /* --3 220 Data for pthread emulation */
     struct async_private *pending_list;   /* --3 224 list of pending async operations */
     void        *driver_data;    /* --3 228 Graphics driver private data */
+    DWORD        alarms;         /* --3 22c Data for vm86 mode */
+    DWORD        vm86_pending;   /* --3 230 Data for vm86 mode */
+    void        *vm86_ptr;       /* --3 234 Data for vm86 mode */
+    void        *vm86_ctx;       /* --3 238 Data for vm86 mode */
     /* here is plenty space for wine specific fields (don't forget to change pad6!!) */
 
     /* the following are nt specific fields */
-    DWORD        pad6[627];                  /* --n 22c */
+    DWORD        pad6[623];                  /* --n 22c */
     UNICODE_STRING StaticUnicodeString;      /* -2- bf8 used by advapi32 */
     USHORT       StaticUnicodeBuffer[261];   /* -2- c00 used by advapi32 */
     DWORD        pad7;                       /* --n e0c */
Index: dlls/ntdll/signal_i386.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/signal_i386.c,v
retrieving revision 1.24
diff -u -r1.24 signal_i386.c
--- dlls/ntdll/signal_i386.c	2001/06/15 19:43:15	1.24
+++ dlls/ntdll/signal_i386.c	2001/06/18 14:40:44
@@ -774,6 +774,62 @@
 
 
 /**********************************************************************
+ *		set_vm86_pend
+ *
+ * Handler for SIGUSR2, which we use to set the vm86 pending flag.
+ */
+static void set_vm86_pend( CONTEXT *context )
+{
+    EXCEPTION_RECORD rec;
+    TEB *teb = NtCurrentTeb();
+
+    rec.ExceptionCode           = EXCEPTION_VM86_STI;
+    rec.ExceptionFlags          = EXCEPTION_CONTINUABLE;
+    rec.ExceptionRecord         = NULL;
+    rec.NumberParameters        = 1;
+    rec.ExceptionInformation[0] = 0;
+
+    /* __wine_enter_vm86() merges the vm86_pending flag in safely */
+    teb->vm86_pending |= VIP_MASK;
+    /* see if we were in VM86 mode */
+    if (context->EFlags & 0x00020000)
+    {
+        /* seems so, also set flag in signal context */
+        if (context->EFlags & VIP_MASK) return;
+        context->EFlags |= VIP_MASK;
+        if (context->EFlags & VIF_MASK) {
+            /* VIF is set, throw exception */
+            teb->vm86_pending = 0;
+            rec.ExceptionAddress = (LPVOID)context->Eip;
+            EXC_RtlRaiseException( &rec, context );
+        }
+    }
+#ifdef linux
+    else if (teb->vm86_ptr)
+    {
+        /* not in VM86, but possibly setting up for it */
+        struct vm86plus_struct *vm86 = (struct vm86plus_struct*)(teb->vm86_ptr);
+        if (vm86->regs.eflags & VIP_MASK) return;
+        vm86->regs.eflags |= VIP_MASK;
+        if (vm86->regs.eflags & VIF_MASK) {
+            /* VIF is set, throw exception */
+            CONTEXT vcontext;
+            teb->vm86_pending = 0;
+            save_vm86_context( &vcontext, vm86 );
+            rec.ExceptionAddress = (LPVOID)vcontext.Eip;
+            EXC_RtlRaiseException( &rec, &vcontext );
+            restore_vm86_context( &vcontext, vm86 );
+            if (teb->vm86_ctx) {
+                /* must also save here */
+                *(CONTEXT*)(teb->vm86_ctx) = vcontext;
+            }
+        }
+    }
+#endif /* linux */
+}
+
+
+/**********************************************************************
  *		segv_handler
  *
  * Handler for SIGSEGV and related errors.
@@ -839,6 +895,41 @@
 }
 
 
+/**********************************************************************
+ *		alrm_handler
+ *
+ * Handler for SIGALRM.
+ * Increases the alarm counter and sets the vm86 pending flag.
+ */
+static HANDLER_DEF(alrm_handler)
+{
+    CONTEXT context;
+
+    save_context( &context, HANDLER_CONTEXT );
+    NtCurrentTeb()->alarms++;
+    set_vm86_pend( &context );
+    restore_context( &context, HANDLER_CONTEXT );
+}
+
+
+/**********************************************************************
+ *		usr2_handler
+ *
+ * Handler for SIGUSR2.
+ * We use it to signal that the running __wine_enter_vm86() should
+ * immediately set VIP_MASK, causing pending events to be handled
+ * as early as possible.
+ */
+static HANDLER_DEF(usr2_handler)
+{
+    CONTEXT context;
+
+    save_context( &context, HANDLER_CONTEXT );
+    set_vm86_pend( &context );
+    restore_context( &context, HANDLER_CONTEXT );
+}
+
+
 /***********************************************************************
  *           set_handler
  *
@@ -919,6 +1010,8 @@
 #ifdef SIGTRAP
     if (set_handler( SIGTRAP, have_sigaltstack, (void (*)())trap_handler ) == -1) goto error;
 #endif
+    if (set_handler( SIGALRM, have_sigaltstack, (void (*)())alrm_handler ) == -1) goto error;
+    if (set_handler( SIGUSR2, have_sigaltstack, (void (*)())usr2_handler ) == -1) goto error;
     return TRUE;
 
  error:
@@ -936,6 +1029,7 @@
 void __wine_enter_vm86( CONTEXT *context )
 {
     EXCEPTION_RECORD rec;
+    TEB *teb = NtCurrentTeb();
     int res;
     struct vm86plus_struct vm86;
 
@@ -943,6 +1037,22 @@
     for (;;)
     {
         restore_vm86_context( context, &vm86 );
+        /* Linux doesn't preserve pending flag (VIP_MASK) on return,
+         * so save it on entry, just in case */
+        teb->vm86_pending |= (context->EFlags & VIP_MASK);
+        /* Work around race conditions with signal handler
+         * (avoiding sigprocmask for performance reasons) */
+        teb->vm86_ptr = &vm86;
+        vm86.regs.eflags |= teb->vm86_pending;
+        /* Check for VIF|VIP here, since vm86_enter doesn't */
+        if ((vm86.regs.eflags & (VIF_MASK|VIP_MASK)) == (VIF_MASK|VIP_MASK)) {
+            teb->vm86_ptr = NULL;
+            teb->vm86_pending = 0;
+            context->EFlags |= VIP_MASK;
+            rec.ExceptionCode = EXCEPTION_VM86_STI;
+            rec.ExceptionInformation[0] = 0;
+            goto cancel_vm86;
+        }
 
         do
         {
@@ -954,7 +1064,11 @@
             }
         } while (VM86_TYPE(res) == VM86_SIGNAL);
 
+        teb->vm86_ctx = context;
         save_vm86_context( context, &vm86 );
+        teb->vm86_ptr = NULL;
+        teb->vm86_ctx = NULL;
+        context->EFlags |= teb->vm86_pending;
 
         switch(VM86_TYPE(res))
         {
@@ -962,12 +1076,13 @@
             do_segv( context, T_PROTFLT, 0, 0 );
             continue;
         case VM86_TRAP: /* return due to DOS-debugger request */
-            do_trap( context, VM86_ARG(res)  );
+            do_trap( context, VM86_ARG(res) );
             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 */
+            teb->vm86_pending = 0;
             rec.ExceptionCode = EXCEPTION_VM86_STI;
             break;
         case VM86_PICRETURN: /* return due to pending PIC request */
@@ -977,11 +1092,12 @@
             ERR( "unhandled result from vm86 mode %x\n", res );
             continue;
         }
+        rec.ExceptionInformation[0] = VM86_ARG(res);
+cancel_vm86:
         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 );
     }
 }





More information about the wine-patches mailing list