qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [patch] QVM86 / KQEMU for Windows updates


From: Filip Navara
Subject: [Qemu-devel] [patch] QVM86 / KQEMU for Windows updates
Date: Thu, 28 Jul 2005 13:23:41 +0200
User-agent: Mozilla Thunderbird 0.9 (Windows/20041103)

Changes:
- Implement the "scheduling" functions.
- Fix a bug which cause the driver to fail unloading on Win2K machines.
- Attempt to fix QVM86 on machines with NX support.
- Installation script.
Index: exec-all.h
===================================================================
RCS file: /cvsroot/qemu/qemu/exec-all.h,v
retrieving revision 1.34
diff -u -p -r1.34 exec-all.h
--- exec-all.h  24 Jul 2005 14:14:53 -0000      1.34
+++ exec-all.h  27 Jul 2005 20:53:43 -0000
@@ -607,6 +607,7 @@ int kqemu_init(CPUState *env);
 int kqemu_cpu_exec(CPUState *env);
 void kqemu_flush_page(CPUState *env, target_ulong addr);
 void kqemu_flush(CPUState *env, int global);
+void kqemu_cpu_interrupt(CPUState *env);
 
 static inline int kqemu_is_ok(CPUState *env)
 {
Index: kqemu.c
===================================================================
RCS file: /cvsroot/qemu/qemu/kqemu.c,v
retrieving revision 1.4
diff -u -p -r1.4 kqemu.c
--- kqemu.c     24 Apr 2005 18:03:37 -0000      1.4
+++ kqemu.c     27 Jul 2005 20:53:24 -0000
@@ -456,11 +456,14 @@ int kqemu_cpu_exec(CPUState *env)
     }
 
 #ifdef _WIN32
-    DeviceIoControl(kqemu_fd, KQEMU_EXEC,
-                   kenv, sizeof(struct kqemu_cpu_state),
-                   kenv, sizeof(struct kqemu_cpu_state),
-                   &temp, NULL);
-    ret = kenv->retval;
+    if (DeviceIoControl(kqemu_fd, KQEMU_EXEC,
+                        kenv, sizeof(struct kqemu_cpu_state),
+                        kenv, sizeof(struct kqemu_cpu_state),
+                        &temp, NULL)) {
+        ret = kenv->retval;
+    } else {
+        ret = -1;
+    }
 #else
 #if KQEMU_VERSION >= 0x010100
     ioctl(kqemu_fd, KQEMU_EXEC, kenv);
@@ -544,6 +547,15 @@ int kqemu_cpu_exec(CPUState *env)
         exit(1);
     }
     return 0;
+}
+
+void kqemu_cpu_interrupt(CPUState *env)
+{
+#if defined(_WIN32) && KQEMU_VERSION >= 0x010101
+    /* cancelling the I/O request causes KQEMU to finish executing the 
+       current block and successfully returning. */
+    CancelIo(kqemu_fd);
+#endif
 }
 
 #endif
Index: vl.c
===================================================================
RCS file: /cvsroot/qemu/qemu/vl.c,v
retrieving revision 1.133
diff -u -p -r1.133 vl.c
--- vl.c        24 Jul 2005 18:44:55 -0000      1.133
+++ vl.c        27 Jul 2005 20:54:26 -0000
@@ -875,6 +875,9 @@ static void host_alarm_handler(int host_
                            qemu_get_clock(rt_clock))) {
         /* stop the cpu because a timer occured */
         cpu_interrupt(global_env, CPU_INTERRUPT_EXIT);
+#ifdef USE_KQEMU
+        kqemu_cpu_interrupt(global_env);
+#endif
     }
 }
 
--- kqemu/kqemu-doc.html        Sun Jul 24 20:02:14 2005
+++ kqemu/kqemu-doc.html        Wed Jul 27 23:20:28 2005
@@ -173,16 +173,7 @@ the option <CODE>major=N</CODE> to set a
 <H2><A NAME="SEC5" HREF="kqemu-doc.html#TOC5">2.3 QEMU Accelerator 
Installation for Windows</A></H2>
 
 <P>
-Copy the kqemu driver <TT>`kqemu.sys'</TT> to
-<TT>`c:\winnt\system32\drivers'</TT>.  Then do:
-
-<PRE>
-regedit kqemu.reg 
-</PRE>
-
-<P>
-Now kqemu is installed and you must restart your system.
-
+Right click on <TT>`kqemu.inf'</TT> in Explorer and choose Install.
 
 <P>
 In order to start kqemu, you must do:
--- kqemu/kqemu-doc.texi        Sun Jul 24 20:02:12 2005
+++ kqemu/kqemu-doc.texi        Wed Jul 27 23:19:38 2005
@@ -112,13 +112,7 @@ the option @code{major=N} to set an alte
 
 @section QEMU Accelerator Installation for Windows
 
-Copy the kqemu driver @file{kqemu.sys} to
address@hidden:\winnt\system32\drivers}.  Then do:
address@hidden
-regedit kqemu.reg 
address@hidden example
-
-Now kqemu is installed and you must restart your system.
+Right click on @file{kqemu.inf} in Explorer and choose Install.
 
 In order to start kqemu, you must do:
 @example
--- kqemu/kqemu-win32.c Sun Jul 24 19:39:55 2005
+++ kqemu/kqemu-win32.c Thu Jul 28 13:09:45 2005
@@ -36,6 +36,14 @@ typedef unsigned long long uint64_t;
 /* XXX: make it dynamic according to available RAM */
 #define MAX_LOCKED_PAGES (16386 / 4)
 
+struct kqemu_instance {
+    struct kqemu_state *state;
+    PIRP current_irp;
+};
+
+FAST_MUTEX instance_lock;
+struct kqemu_instance *active_instance;
+
 /* lock the page at virtual address 'user_addr' and return its
    page index. Return -1 if error */
 struct kqemu_user_page *CDECL kqemu_lock_user_page(unsigned long *ppage_index,
@@ -57,6 +65,7 @@ struct kqemu_user_page *CDECL kqemu_lock
     mdl_pages = (PPFN_NUMBER)(mdl + 1);
 
     MmInitializeMdl(mdl, user_addr, PAGE_SIZE);
+    /* XXX: Protect with SEH. */
     MmProbeAndLockPages(mdl, KernelMode, IoModifyAccess);
     *ppage_index = mdl_pages[0];
     return (struct kqemu_user_page *)mdl;
@@ -150,19 +159,19 @@ void CDECL kqemu_io_unmap(void *ptr, uns
    execution) */
 int CDECL kqemu_schedule(void)
 {
-    /* XXX: do it */
-    return TRUE;
+    return active_instance->current_irp->Cancel;
 }
 
 void CDECL kqemu_log(const char *fmt, ...)
 {
-    /* XXX: format parameters */
-    DbgPrint("%s", fmt);
-}
+    char log_buf[1024];
+    va_list ap;
 
-struct kqemu_instance {
-    struct kqemu_state *state;
-};
+    va_start(ap, fmt);
+    _vsnprintf(log_buf, sizeof(log_buf), fmt, ap);
+    DbgPrint("kqemu: %s", log_buf);
+    va_end(ap);
+}
 
 NTSTATUS STDCALL
 KQemuCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
@@ -256,12 +265,19 @@ KQemuDeviceControl(PDEVICE_OBJECT Device
                     break;
                 }
                 
+                ExAcquireFastMutex(&instance_lock);
+                active_instance = State;
+                State->current_irp = Irp;
+
                 ctx = kqemu_get_cpu_state(State->state);
                 
                 RtlCopyMemory(ctx, Irp->AssociatedIrp.SystemBuffer, 
                               sizeof(*ctx));
                 ret = kqemu_exec(State->state);
                 RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, ctx, 
sizeof(*ctx));
+
+                ExReleaseFastMutex(&instance_lock);
+                
                 Irp->IoStatus.Information = sizeof(*ctx);
                 Status = STATUS_SUCCESS;
             }
@@ -294,6 +310,10 @@ KQemuDeviceControl(PDEVICE_OBJECT Device
 VOID STDCALL
 KQemuUnload(PDRIVER_OBJECT DriverObject)
 {
+    UNICODE_STRING SymlinkName;
+
+    RtlInitUnicodeString(&SymlinkName, L"\\??\\kqemu");
+    IoDeleteSymbolicLink(&SymlinkName);
     IoDeleteDevice(DriverObject->DeviceObject);
 }
 
@@ -312,6 +332,8 @@ DriverEntry(PDRIVER_OBJECT DriverObject,
 
     MmLockPagableCodeSection(DriverEntry);
 
+    ExInitializeFastMutex(&instance_lock);
+
     DriverObject->MajorFunction[IRP_MJ_CREATE] = KQemuCreate;
     DriverObject->MajorFunction[IRP_MJ_CLOSE] = KQemuClose;
     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KQemuDeviceControl;
@@ -324,10 +346,17 @@ DriverEntry(PDRIVER_OBJECT DriverObject,
                             &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE,
                             &DeviceObject);
     if (!NT_SUCCESS(Status))
+    {
         return Status;
+    }
 
     /* Create the dos device link */
-    IoCreateSymbolicLink(&SymlinkName, &DeviceName);
+    Status = IoCreateSymbolicLink(&SymlinkName, &DeviceName);
+    if (!NT_SUCCESS(Status))
+    {
+        IoDeleteDevice(DeviceObject);
+        return Status;
+    }
 
     return STATUS_SUCCESS;
 }
--- kqemu/kqemu.h       Sun Jul 24 19:38:27 2005
+++ kqemu/kqemu.h       Wed Jul 27 23:07:48 2005
@@ -1,7 +1,7 @@
 #ifndef KQEMU_H
 #define KQEMU_H
 
-#define KQEMU_VERSION 0x010100
+#define KQEMU_VERSION 0x010101
 
 struct kqemu_segment_cache {
     uint32_t selector;
Index: ChangeLog
===================================================================
RCS file: /cvsroot/qvm86/qvm86/ChangeLog,v
retrieving revision 1.20
diff -u -p -r1.20 ChangeLog
--- ChangeLog   18 Jun 2005 22:59:47 -0000      1.20
+++ ChangeLog   28 Jul 2005 11:14:05 -0000
@@ -1,3 +1,11 @@
+2005-07-28  Filip Navara  <address@hidden>
+
+       * qvm86-win32.c (KQemuUnload): Correctly delete symlink on
+       driver unload.
+       (host_do_sched): Implement.
+       (host_alloc_pages, host_free_pages): Experimental fix for machines
+       with NX support.
+
 2005-06-18  Paul Brook  <address@hidden>
 
        * qvm86-host.c (rdtsc): Move inside #ifdef QVM86_PROFILE.
@@ -6,7 +14,7 @@
        (qvm86_clear_gdt_entries): New function.
        (qvm86_setup_monitor_idt): Return immediately if nothing to do.
        (qvm86_shadow_fault): Update comments.
-       (qvm86_handle_exception): Remove debugging code.  Contuinue execution
+       (qvm86_handle_exception): Remove debugging code.  Continue execution
        after a hardware interrupt when possible.  Use new gdt clearing code.
        * qvm86-linux.c (host_do_sched): New function.
        * qvm86-win32.c (host_do_sched): New function.
Index: README
===================================================================
RCS file: /cvsroot/qvm86/qvm86/README,v
retrieving revision 1.5
diff -u -p -r1.5 README
--- README      10 Apr 2005 10:48:14 -0000      1.5
+++ README      28 Jul 2005 11:11:12 -0000
@@ -26,11 +26,11 @@ can find it.  You need to manually load 
 QVM86 uses a dynamic misc device minor.  If you are using devfs or udev then
 /dev/qvm86 should be created automatically when you load the module.
 
-On Windows hosts you need to copy qvm86.sys to windows/system32/drivers, apply
-qvm86.reg and do "net start qvm86".  Windows host support is extremely
-experimental.  The windows code has been built with the mingw32 toolchains.
-It is possible to compile on cygwin, but some hacking of makefiles is
-required to ensure -mno-cygwin is used.
+On Windows hosts you right click on qvm86.inf and choose Install and then
+do "net start qvm86".  Windows host support is extremely experimental. 
+The windows code has been built with the mingw32 toolchains. It is possible
+to compile on cygwin, but some hacking of makefiles is required to ensure
+-mno-cygwin is used.
 
 WARNING: QVM86 is still in the early stages of development.
 It may crash your machine or cause data corruption.  Do NOT use it on any
Index: kqemu.h
===================================================================
RCS file: /cvsroot/qvm86/qvm86/kqemu.h,v
retrieving revision 1.3
diff -u -p -r1.3 kqemu.h
--- kqemu.h     9 Apr 2005 23:32:54 -0000       1.3
+++ kqemu.h     28 Jul 2005 11:13:21 -0000
@@ -1,7 +1,11 @@
 #ifndef KQEMU_H
 #define KQEMU_H
 
+#ifdef _WIN32
+#define KQEMU_VERSION 0x010101
+#else
 #define KQEMU_VERSION 0x010000
+#endif
 
 struct kqemu_segment_cache {
     uint32_t selector;
Index: qvm86-host.c
===================================================================
RCS file: /cvsroot/qvm86/qvm86/qvm86-host.c,v
retrieving revision 1.11
diff -u -p -r1.11 qvm86-host.c
--- qvm86-host.c        18 Jun 2005 22:59:47 -0000      1.11
+++ qvm86-host.c        22 Jul 2005 12:47:42 -0000
@@ -832,7 +832,7 @@ qvm86_handle_exception (qvm86_state * qs
       soft_int (vector);
       /* Return to userspace if there are signals pending, otherwise
          resume guest execution.  */
-      if (host_do_sched())
+      if (host_do_sched(qs))
        return 2;
       else
        return 0;
Index: qvm86-linux.c
===================================================================
RCS file: /cvsroot/qvm86/qvm86/qvm86-linux.c,v
retrieving revision 1.5
diff -u -p -r1.5 qvm86-linux.c
--- qvm86-linux.c       18 Jun 2005 22:59:47 -0000      1.5
+++ qvm86-linux.c       22 Jul 2005 12:47:35 -0000
@@ -67,7 +67,7 @@ MODULE_LICENSE("GPL");
 /* Give other tasks a chance to run.  Returns nonzero if this task has
    signals pending.  */
 int
-host_do_sched()
+host_do_sched (qvm86_state *qs)
 {
   if (need_resched())
       schedule();
Index: qvm86-win32.c
===================================================================
RCS file: /cvsroot/qvm86/qvm86/qvm86-win32.c,v
retrieving revision 1.4
diff -u -p -r1.4 qvm86-win32.c
--- qvm86-win32.c       18 Jun 2005 22:59:47 -0000      1.4
+++ qvm86-win32.c       28 Jul 2005 11:07:24 -0000
@@ -22,15 +22,38 @@
 
 #include "qvm86.h"
 
-/* Give other tasks a chance to run.  Returns nonzero if this task has
-   signals pending.  */
 int
-host_do_sched()
+host_do_sched(qvm86_state *State)
 {
-  /* See the linux implementation for how this should work.  I've no idea
-     what the equivalent windows code is.  Always returning 1 is safe but
-     probably not optimal.  */
-  return 1;
+    PIRP Irp = State->host_opaque;
+
+    /*
+     * The Linux version of this routines does two things:
+     * 1. Reschedules if other task is pending.
+     * 2. Returns 1 if some signal pending, 0 otherwise.
+     *
+     * There is no need to do anything on Windows NT to manually
+     * reschedule threads since they can be scheduled at any time
+     * by the OS even if they're currently in kernel mode (unless
+     * we're running at IRQL >= DISPATCH_LEVEL of course, which is
+     * not our case).
+     *
+     * As for point 2. we're in kind of uneasy situation since there
+     * is no direct equivalent of POSIX signals on Windows. The two
+     * closest things would be APCs and SEH, neither of which make
+     * sense in this context. To implement this functionality I decided
+     * to take another road ... the ultimate effect of returning 1
+     * from this routine is that the control is passed back to the OS
+     * and the I/O request packet (IRP) is completed. There are only
+     * cases of interruption that we really need to handle. The first one
+     * is thread termination which has the effect of marking all IRPs
+     * as cancelled and waiting for them to be completed, so all we do
+     * here is to return 1 if the IRP was marked as cancelled. The
+     * second one is when QEMU timer is fired. We use a helper code in
+     * the main program to cancel the IRP in that case.
+     */
+
+    return Irp->Cancel;
 }
 
 void *
@@ -50,7 +73,18 @@ host_alloc_pages(int count)
 {
     char * ptr;
 
-    ptr = MmAllocateNonCachedMemory(PAGE_SIZE * count);
+    /*
+     * Implementation note: We don't use MmAllocateNonCachedMemory here
+     * since it returns pages with the NX bit set. All Microsoft
+     * documentation seems to suggest that memory allocated from
+     * non-paged pool shouldn't have the bit set, but so far I have
+     * no proof if it's true or not. If it isn't, we would have to
+     * resort to much more invasive and dangerous techniques such as
+     * allocation user mode pages with NtAllocateVirtualMemory.
+     */
+
+    ptr = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE * count,
+                                TAG('Q','V','M','P'));
     if (!ptr)
         return NULL;
     RtlZeroMemory(ptr, PAGE_SIZE * count);
@@ -70,7 +104,7 @@ host_free_pages(void *ptr, int count)
 {
     if (!ptr)
         return;
-    MmFreeNonCachedMemory(ptr, PAGE_SIZE * count);
+    ExFreePool(ptr);
 }
 
 unsigned long
@@ -105,6 +139,7 @@ host_map_user(unsigned long useraddr, qv
     mdl_pages = (PPFN_NUMBER)(mdl + 1);
 
     MmInitializeMdl(mdl, useraddr, PAGE_SIZE);
+    /* XXX: Protect with SEH. */
     MmProbeAndLockPages(mdl, KernelMode, IoModifyAccess);
 
     map->host = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority);
@@ -219,12 +254,26 @@ KQemuDeviceControl(PDEVICE_OBJECT Device
                     break;
                 }
             
+                /*
+                 * We allow only one KQEMU_EXEC request at a time and use
+                 * the host_opaque pointer to store the IRP location. It
+                 * is later used in host_do_sched.
+                 */
+                if (InterlockedCompareExchangePointer(&State->host_opaque,
+                                                      Irp, NULL) != NULL)
+                {
+                    Status = STATUS_DEVICE_NOT_READY;
+                    break;
+                }
+
                 RtlCopyMemory(&s, Irp->AssociatedIrp.SystemBuffer, sizeof(s));
                 ret = qvm86_exec(State, &s);
                 s.retval = ret;
                 RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &s, sizeof(s));
                 Irp->IoStatus.Information = sizeof(s);
                 Status = STATUS_SUCCESS;
+
+                InterlockedExchangePointer(&State->host_opaque, NULL);
             }
             break;
     
@@ -255,6 +304,10 @@ KQemuDeviceControl(PDEVICE_OBJECT Device
 VOID STDCALL
 KQemuUnload(PDRIVER_OBJECT DriverObject)
 {
+    UNICODE_STRING SymlinkName;
+
+    RtlInitUnicodeString(&SymlinkName, L"\\??\\qvm86");
+    IoDeleteSymbolicLink(&SymlinkName);
     IoDeleteDevice(DriverObject->DeviceObject);
 }
 
@@ -288,7 +341,12 @@ DriverEntry(PDRIVER_OBJECT DriverObject,
         return Status;
 
     /* Create the dos device link */
-    IoCreateSymbolicLink(&SymlinkName, &DeviceName);
+    Status = IoCreateSymbolicLink(&SymlinkName, &DeviceName);
+    if (!NT_SUCCESS(Status))
+    {
+        IoDeleteDevice(DeviceObject);
+        return Status;
+    }
 
     return STATUS_SUCCESS;
 }
Index: qvm86.h
===================================================================
RCS file: /cvsroot/qvm86/qvm86/qvm86.h,v
retrieving revision 1.4
diff -u -p -r1.4 qvm86.h
--- qvm86.h     18 Jun 2005 22:59:47 -0000      1.4
+++ qvm86.h     22 Jul 2005 12:47:23 -0000
@@ -244,6 +244,9 @@ typedef struct
   /* This will be nonzero if a fatal error occured.  Usually due to
      something not yet implemented.  Ideally this would never happen.  */
   int die;
+
+  /* This pointer is exclusively for use by specific host code. */
+  void *host_opaque;
 } qvm86_state;
 
 void qvm86_init (qvm86_state * qs, struct kqemu_init * init);
@@ -252,7 +255,7 @@ void qvm86_init_device (qvm86_state *);
 void qvm86_cleanup_device (qvm86_state *);
 /* Give other tasks a chance to run.  Returns nonzero if this task has
    signals pending.  */
-int host_do_sched(void);
+int host_do_sched (qvm86_state *);
 /* Allocate a contiguous block of physical memory.  The returned block must
    be suitable for use with host_phys_addr.  */
 void * host_alloc_pages (int);
Index: qvm86.reg
===================================================================
RCS file: /cvsroot/qvm86/qvm86/qvm86.reg,v
retrieving revision 1.1
diff -u -p -r1.1 qvm86.reg
--- qvm86.reg   9 Apr 2005 23:32:54 -0000       1.1
+++ /dev/null   28 Jul 2005 11:10:07 -0000
@@ -1,7 +0,0 @@
-REGEDIT4

-

-[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\qvm86]

-"Type"=dword:00000001

-"Start"=dword:00000003

-"ErrorControl"=dword:00000001

-"DisplayName"="qvm86"


reply via email to

[Prev in Thread] Current Thread [Next in Thread]