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 [EMAIL PROTECTED]:\winnt\system32\drivers}. Then do: [EMAIL PROTECTED] -regedit kqemu.reg [EMAIL PROTECTED] 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 <[EMAIL PROTECTED]> + + * 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 <[EMAIL PROTECTED]> * 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"
_______________________________________________ Qemu-devel mailing list Qemu-devel@nongnu.org http://lists.nongnu.org/mailman/listinfo/qemu-devel