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