2 * Windows NT kernel wrapper for KQEMU
4 * Copyright (C) 2005 Filip Navara
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 #include <ddk/ntddk.h>
27 typedef unsigned char uint8_t;
28 typedef unsigned short uint16_t;
29 typedef unsigned int uint32_t;
30 typedef unsigned long long uint64_t;
33 #include "kqemu-kernel.h"
35 /* XXX: make it dynamic according to available RAM */
36 #define MAX_LOCKED_PAGES (16386 / 4)
38 struct kqemu_instance {
39 struct kqemu_state *state;
43 FAST_MUTEX instance_lock;
44 struct kqemu_instance *active_instance;
46 /* lock the page at virtual address 'user_addr' and return its
47 page index. Return -1 if error */
48 struct kqemu_user_page *CDECL kqemu_lock_user_page(unsigned long *ppage_index,
49 unsigned long user_addr)
52 PPFN_NUMBER mdl_pages;
54 if (user_addr & 0xfff) {
55 DbgPrint("kqemu: unaligned user memory\n");
59 mdl = ExAllocatePool(NonPagedPool, sizeof(MDL) + sizeof(PFN_NUMBER));
61 DbgPrint("kqemu: Not enough memory for MDL structure\n");
64 mdl_pages = (PPFN_NUMBER)(mdl + 1);
66 MmInitializeMdl(mdl, user_addr, PAGE_SIZE);
67 /* XXX: Protect with SEH. */
68 MmProbeAndLockPages(mdl, KernelMode, IoModifyAccess);
69 *ppage_index = mdl_pages[0];
70 return (struct kqemu_user_page *)mdl;
73 void CDECL kqemu_unlock_user_page(struct kqemu_user_page *page)
75 PMDL mdl = (PMDL)page;
81 struct kqemu_page *CDECL kqemu_alloc_zeroed_page(unsigned long *ppage_index)
86 ptr = MmAllocateNonCachedMemory(PAGE_SIZE);
89 RtlZeroMemory(ptr, PAGE_SIZE);
90 pa = MmGetPhysicalAddress(ptr);
91 *ppage_index = (unsigned long)(pa.QuadPart >> PAGE_SHIFT);
92 return (struct kqemu_page *)ptr;
95 void CDECL kqemu_free_page(struct kqemu_page *page)
101 MmFreeNonCachedMemory(ptr, PAGE_SIZE);
104 void * CDECL kqemu_page_kaddr(struct kqemu_page *page)
110 void * CDECL kqemu_vmalloc(unsigned int size)
114 ptr = ExAllocatePoolWithTag(NonPagedPool, size, TAG('K','Q','M','U'));
117 RtlZeroMemory(ptr, size);
121 void CDECL kqemu_vfree(void *ptr)
128 unsigned long CDECL kqemu_vmalloc_to_phys(const void *vaddr)
132 pa = MmGetPhysicalAddress((void *)vaddr);
133 return (unsigned long)(pa.QuadPart >> PAGE_SHIFT);
136 /* Map a IO area in the kernel address space and return its
137 address. Return NULL if error or not implemented. */
138 void * CDECL kqemu_io_map(unsigned long page_index, unsigned int size)
143 pa.QuadPart = page_index << PAGE_SHIFT;
144 return MmMapIoSpace(pa, size, MmNonCached);
146 /* XXX: mingw32 tools too old */
151 /* Unmap the IO area */
152 void CDECL kqemu_io_unmap(void *ptr, unsigned int size)
154 return MmUnmapIoSpace(ptr, size);
157 /* return TRUE if a signal is pending (i.e. the guest must stop
159 int CDECL kqemu_schedule(void)
161 return active_instance->current_irp->Cancel;
164 void CDECL kqemu_log(const char *fmt, ...)
170 _vsnprintf(log_buf, sizeof(log_buf), fmt, ap);
171 DbgPrint("kqemu: %s", log_buf);
176 KQemuCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
178 PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
179 struct kqemu_instance *State;
181 State = kqemu_vmalloc(sizeof(struct kqemu_instance));
184 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
185 Irp->IoStatus.Information = 0;
186 IoCompleteRequest(Irp, IO_NO_INCREMENT);
187 return STATUS_INSUFFICIENT_RESOURCES;
190 IrpStack->FileObject->FsContext = State;
192 Irp->IoStatus.Status = STATUS_SUCCESS;
193 Irp->IoStatus.Information = 0;
194 IoCompleteRequest(Irp, IO_NO_INCREMENT);
196 return STATUS_SUCCESS;
200 KQemuClose(PDEVICE_OBJECT DeviceObject, PIRP Irp)
202 PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
203 struct kqemu_instance *State = IrpStack->FileObject->FsContext;
206 kqemu_delete(State->state);
211 Irp->IoStatus.Status = STATUS_SUCCESS;
212 Irp->IoStatus.Information = 0;
213 IoCompleteRequest(Irp, IO_NO_INCREMENT);
215 return STATUS_SUCCESS;
219 KQemuDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
221 PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
222 struct kqemu_instance *State = IrpStack->FileObject->FsContext;
226 Irp->IoStatus.Information = 0;
228 switch (IrpStack->Parameters.DeviceIoControl.IoControlCode)
232 Status = STATUS_INVALID_PARAMETER;
235 if (IrpStack->Parameters.DeviceIoControl.InputBufferLength <
236 sizeof(struct kqemu_init))
238 Status = STATUS_INVALID_PARAMETER;
241 State->state = kqemu_init((struct kqemu_init *)Irp->AssociatedIrp.SystemBuffer,
244 Status = STATUS_INSUFFICIENT_RESOURCES;
247 Status = STATUS_SUCCESS;
252 struct kqemu_cpu_state *ctx;
255 Status = STATUS_INVALID_PARAMETER;
258 if (IrpStack->Parameters.DeviceIoControl.InputBufferLength <
260 IrpStack->Parameters.DeviceIoControl.OutputBufferLength <
263 Status = STATUS_INVALID_PARAMETER;
267 ExAcquireFastMutex(&instance_lock);
268 active_instance = State;
269 State->current_irp = Irp;
271 ctx = kqemu_get_cpu_state(State->state);
273 RtlCopyMemory(ctx, Irp->AssociatedIrp.SystemBuffer,
275 ret = kqemu_exec(State->state);
276 RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, ctx, sizeof(*ctx));
278 ExReleaseFastMutex(&instance_lock);
280 Irp->IoStatus.Information = sizeof(*ctx);
281 Status = STATUS_SUCCESS;
285 case KQEMU_GET_VERSION:
286 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength <
289 Status = STATUS_INVALID_PARAMETER;
293 *((int *)Irp->AssociatedIrp.SystemBuffer) = KQEMU_VERSION;
294 Irp->IoStatus.Information = sizeof(int);
295 Status = STATUS_SUCCESS;
299 Status = STATUS_INVALID_PARAMETER;
303 Irp->IoStatus.Status = Status;
304 IoCompleteRequest(Irp, IO_NO_INCREMENT);
310 KQemuUnload(PDRIVER_OBJECT DriverObject)
312 UNICODE_STRING SymlinkName;
314 RtlInitUnicodeString(&SymlinkName, L"\\??\\kqemu");
315 IoDeleteSymbolicLink(&SymlinkName);
316 IoDeleteDevice(DriverObject->DeviceObject);
320 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
322 PDEVICE_OBJECT DeviceObject;
323 UNICODE_STRING DeviceName;
324 UNICODE_STRING SymlinkName;
327 DbgPrint("QEMU Accelerator Module version %d.%d.%d\n",
328 (KQEMU_VERSION >> 16),
329 (KQEMU_VERSION >> 8) & 0xff,
330 (KQEMU_VERSION) & 0xff);
332 MmLockPagableCodeSection(DriverEntry);
334 ExInitializeFastMutex(&instance_lock);
336 DriverObject->MajorFunction[IRP_MJ_CREATE] = KQemuCreate;
337 DriverObject->MajorFunction[IRP_MJ_CLOSE] = KQemuClose;
338 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KQemuDeviceControl;
339 DriverObject->DriverUnload = KQemuUnload;
341 RtlInitUnicodeString(&DeviceName, L"\\Device\\kqemu");
342 RtlInitUnicodeString(&SymlinkName, L"\\??\\kqemu");
344 Status = IoCreateDevice(DriverObject, 0,
345 &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE,
347 if (!NT_SUCCESS(Status))
352 /* Create the dos device link */
353 Status = IoCreateSymbolicLink(&SymlinkName, &DeviceName);
354 if (!NT_SUCCESS(Status))
356 IoDeleteDevice(DeviceObject);
360 return STATUS_SUCCESS;