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;
30 typedef unsigned int uint32_t;
31 typedef unsigned long long uint64_t;
34 #include "kqemu-kernel.h"
36 /* XXX: make it dynamic according to available RAM */
37 #define MAX_LOCKED_PAGES (16386 / 4)
39 struct kqemu_instance
{
40 struct kqemu_state
*state
;
44 FAST_MUTEX instance_lock
;
45 struct kqemu_instance
*active_instance
;
46 struct kqemu_global_state
*kqemu_gs
;
48 /* lock the page at virtual address 'user_addr' and return its
49 page index. Return -1 if error */
50 struct kqemu_user_page
*CDECL
kqemu_lock_user_page(unsigned long *ppage_index
,
51 unsigned long user_addr
)
54 PPFN_NUMBER mdl_pages
;
56 if (user_addr
& 0xfff) {
57 DbgPrint("kqemu: unaligned user memory\n");
61 mdl
= ExAllocatePool(NonPagedPool
, sizeof(MDL
) + sizeof(PFN_NUMBER
));
63 DbgPrint("kqemu: Not enough memory for MDL structure\n");
66 mdl_pages
= (PPFN_NUMBER
)(mdl
+ 1);
68 MmInitializeMdl(mdl
, user_addr
, PAGE_SIZE
);
69 /* XXX: Protect with SEH. */
70 MmProbeAndLockPages(mdl
, KernelMode
, IoModifyAccess
);
71 *ppage_index
= mdl_pages
[0];
72 return (struct kqemu_user_page
*)mdl
;
75 void CDECL
kqemu_unlock_user_page(struct kqemu_user_page
*page
)
77 PMDL mdl
= (PMDL
)page
;
83 struct kqemu_page
*CDECL
kqemu_alloc_zeroed_page(unsigned long *ppage_index
)
88 ptr
= MmAllocateNonCachedMemory(PAGE_SIZE
);
91 RtlZeroMemory(ptr
, PAGE_SIZE
);
92 pa
= MmGetPhysicalAddress(ptr
);
93 *ppage_index
= (unsigned long)(pa
.QuadPart
>> PAGE_SHIFT
);
94 return (struct kqemu_page
*)ptr
;
97 void CDECL
kqemu_free_page(struct kqemu_page
*page
)
103 MmFreeNonCachedMemory(ptr
, PAGE_SIZE
);
106 void * CDECL
kqemu_page_kaddr(struct kqemu_page
*page
)
112 void * CDECL
kqemu_vmalloc(unsigned int size
)
116 ptr
= ExAllocatePoolWithTag(NonPagedPool
, size
, TAG('K','Q','M','U'));
119 RtlZeroMemory(ptr
, size
);
123 void CDECL
kqemu_vfree(void *ptr
)
130 unsigned long CDECL
kqemu_vmalloc_to_phys(const void *vaddr
)
134 pa
= MmGetPhysicalAddress((void *)vaddr
);
135 return (unsigned long)(pa
.QuadPart
>> PAGE_SHIFT
);
138 /* Map a IO area in the kernel address space and return its
139 address. Return NULL if error or not implemented. */
140 void * CDECL
kqemu_io_map(unsigned long page_index
, unsigned int size
)
145 pa
.QuadPart
= page_index
<< PAGE_SHIFT
;
146 return MmMapIoSpace(pa
, size
, MmNonCached
);
148 /* XXX: mingw32 tools too old */
153 /* Unmap the IO area */
154 void CDECL
kqemu_io_unmap(void *ptr
, unsigned int size
)
156 return MmUnmapIoSpace(ptr
, size
);
159 /* return TRUE if a signal is pending (i.e. the guest must stop
161 int CDECL
kqemu_schedule(void)
164 return active_instance
->current_irp
->Cancel
;
166 /* XXX: temporary "fix" to correct the CancelIO() problem. A
167 proper solution may be to add a new KQEMU_INTERRUPT ioctl. */
172 void CDECL
kqemu_log(const char *fmt
, ...)
178 _vsnprintf(log_buf
, sizeof(log_buf
), fmt
, ap
);
179 DbgPrint("kqemu: %s", log_buf
);
184 KQemuCreate(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
186 PIO_STACK_LOCATION IrpStack
= IoGetCurrentIrpStackLocation(Irp
);
187 struct kqemu_instance
*State
;
189 State
= kqemu_vmalloc(sizeof(struct kqemu_instance
));
192 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
193 Irp
->IoStatus
.Information
= 0;
194 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
195 return STATUS_INSUFFICIENT_RESOURCES
;
198 IrpStack
->FileObject
->FsContext
= State
;
200 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
201 Irp
->IoStatus
.Information
= 0;
202 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
204 return STATUS_SUCCESS
;
208 KQemuClose(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
210 PIO_STACK_LOCATION IrpStack
= IoGetCurrentIrpStackLocation(Irp
);
211 struct kqemu_instance
*State
= IrpStack
->FileObject
->FsContext
;
214 kqemu_delete(State
->state
);
219 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
220 Irp
->IoStatus
.Information
= 0;
221 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
223 return STATUS_SUCCESS
;
227 KQemuDeviceControl(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
229 PIO_STACK_LOCATION IrpStack
= IoGetCurrentIrpStackLocation(Irp
);
230 struct kqemu_instance
*State
= IrpStack
->FileObject
->FsContext
;
234 Irp
->IoStatus
.Information
= 0;
236 switch (IrpStack
->Parameters
.DeviceIoControl
.IoControlCode
)
240 Status
= STATUS_INVALID_PARAMETER
;
243 if (IrpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
244 sizeof(struct kqemu_init
))
246 Status
= STATUS_INVALID_PARAMETER
;
249 State
->state
= kqemu_init((struct kqemu_init
*)Irp
->AssociatedIrp
.SystemBuffer
,
252 Status
= STATUS_INSUFFICIENT_RESOURCES
;
255 Status
= STATUS_SUCCESS
;
260 struct kqemu_cpu_state
*ctx
;
263 Status
= STATUS_INVALID_PARAMETER
;
266 if (IrpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
268 IrpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
271 Status
= STATUS_INVALID_PARAMETER
;
275 ExAcquireFastMutex(&instance_lock
);
276 active_instance
= State
;
277 State
->current_irp
= Irp
;
279 ctx
= kqemu_get_cpu_state(State
->state
);
281 RtlCopyMemory(ctx
, Irp
->AssociatedIrp
.SystemBuffer
,
283 ret
= kqemu_exec(State
->state
);
284 RtlCopyMemory(Irp
->AssociatedIrp
.SystemBuffer
, ctx
, sizeof(*ctx
));
286 ExReleaseFastMutex(&instance_lock
);
288 Irp
->IoStatus
.Information
= sizeof(*ctx
);
289 Status
= STATUS_SUCCESS
;
293 case KQEMU_SET_PHYS_MEM
:
295 struct kqemu_phys_mem
*kphys_mem
;
298 Status
= STATUS_INVALID_PARAMETER
;
301 if (IrpStack
->Parameters
.DeviceIoControl
.InputBufferLength
<
302 sizeof(struct kqemu_phys_mem
))
304 Status
= STATUS_INVALID_PARAMETER
;
308 ExAcquireFastMutex(&instance_lock
);
310 kphys_mem
= (struct kqemu_phys_mem
*)Irp
->AssociatedIrp
.SystemBuffer
;
311 ret
= kqemu_set_phys_mem(State
->state
, kphys_mem
);
313 ExReleaseFastMutex(&instance_lock
);
316 Status
= STATUS_SUCCESS
;
318 Status
= STATUS_INVALID_PARAMETER
;
322 case KQEMU_GET_VERSION
:
323 if (IrpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
<
326 Status
= STATUS_INVALID_PARAMETER
;
330 *((int *)Irp
->AssociatedIrp
.SystemBuffer
) = KQEMU_VERSION
;
331 Irp
->IoStatus
.Information
= sizeof(int);
332 Status
= STATUS_SUCCESS
;
336 Status
= STATUS_INVALID_PARAMETER
;
340 Irp
->IoStatus
.Status
= Status
;
341 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
347 KQemuUnload(PDRIVER_OBJECT DriverObject
)
349 UNICODE_STRING SymlinkName
;
351 RtlInitUnicodeString(&SymlinkName
, L
"\\??\\kqemu");
352 IoDeleteSymbolicLink(&SymlinkName
);
353 IoDeleteDevice(DriverObject
->DeviceObject
);
355 kqemu_global_delete(kqemu_gs
);
361 DriverEntry(PDRIVER_OBJECT DriverObject
, PUNICODE_STRING RegistryPath
)
363 PDEVICE_OBJECT DeviceObject
;
364 UNICODE_STRING DeviceName
;
365 UNICODE_STRING SymlinkName
;
368 DbgPrint("QEMU Accelerator Module version %d.%d.%d\n",
369 (KQEMU_VERSION
>> 16),
370 (KQEMU_VERSION
>> 8) & 0xff,
371 (KQEMU_VERSION
) & 0xff);
373 MmLockPagableCodeSection(DriverEntry
);
375 ExInitializeFastMutex(&instance_lock
);
377 kqemu_gs
= kqemu_global_init(MAX_LOCKED_PAGES
);
379 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = KQemuCreate
;
380 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = KQemuClose
;
381 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = KQemuDeviceControl
;
382 DriverObject
->DriverUnload
= KQemuUnload
;
384 RtlInitUnicodeString(&DeviceName
, L
"\\Device\\kqemu");
385 RtlInitUnicodeString(&SymlinkName
, L
"\\??\\kqemu");
387 Status
= IoCreateDevice(DriverObject
, 0,
388 &DeviceName
, FILE_DEVICE_UNKNOWN
, 0, FALSE
,
390 if (!NT_SUCCESS(Status
))
395 /* Create the dos device link */
396 Status
= IoCreateSymbolicLink(&SymlinkName
, &DeviceName
);
397 if (!NT_SUCCESS(Status
))
399 IoDeleteDevice(DeviceObject
);
403 return STATUS_SUCCESS
;