0.8.0-alt1
[qemu] / kqemu / kqemu-win32.c
1 /*
2  * Windows NT kernel wrapper for KQEMU
3  *
4  * Copyright (C) 2005 Filip Navara
5  *
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:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
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
22  * THE SOFTWARE.
23  */
24 #include <stddef.h>
25 #include <ddk/ntddk.h>
26
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;
31
32 #undef CDECL
33 #include "kqemu-kernel.h"
34
35 /* XXX: make it dynamic according to available RAM */
36 #define MAX_LOCKED_PAGES (16386 / 4)
37
38 struct kqemu_instance {
39     struct kqemu_state *state;
40     PIRP current_irp;
41 };
42
43 FAST_MUTEX instance_lock;
44 struct kqemu_instance *active_instance;
45
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)
50 {
51     PMDL mdl;
52     PPFN_NUMBER mdl_pages;
53
54     if (user_addr & 0xfff) {
55         DbgPrint("kqemu: unaligned user memory\n");
56         return NULL;
57     }
58
59     mdl = ExAllocatePool(NonPagedPool, sizeof(MDL) + sizeof(PFN_NUMBER));
60     if (mdl == NULL) {
61         DbgPrint("kqemu: Not enough memory for MDL structure\n");
62         return NULL;
63     }
64     mdl_pages = (PPFN_NUMBER)(mdl + 1);
65
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;
71 }
72
73 void CDECL kqemu_unlock_user_page(struct kqemu_user_page *page)
74 {
75     PMDL mdl = (PMDL)page;
76
77     MmUnlockPages(mdl);
78     ExFreePool(mdl);
79 }
80
81 struct kqemu_page *CDECL kqemu_alloc_zeroed_page(unsigned long *ppage_index)
82 {
83     void *ptr;
84     LARGE_INTEGER pa;
85
86     ptr = MmAllocateNonCachedMemory(PAGE_SIZE);
87     if (!ptr)
88         return NULL;
89     RtlZeroMemory(ptr, PAGE_SIZE);
90     pa = MmGetPhysicalAddress(ptr);
91     *ppage_index = (unsigned long)(pa.QuadPart >> PAGE_SHIFT);
92     return (struct kqemu_page *)ptr;
93 }
94
95 void CDECL kqemu_free_page(struct kqemu_page *page)
96 {
97     void *ptr = page;
98
99     if (!ptr)
100         return;
101     MmFreeNonCachedMemory(ptr, PAGE_SIZE);
102 }
103
104 void * CDECL kqemu_page_kaddr(struct kqemu_page *page)
105 {
106     void *ptr = page;
107     return ptr;
108 }
109
110 void * CDECL kqemu_vmalloc(unsigned int size)
111 {
112     void * ptr;
113
114     ptr = ExAllocatePoolWithTag(NonPagedPool, size, TAG('K','Q','M','U'));
115     if (!ptr)
116         return NULL;
117     RtlZeroMemory(ptr, size);
118     return ptr;
119 }
120
121 void CDECL kqemu_vfree(void *ptr)
122 {
123     if (!ptr)
124         return;
125     ExFreePool(ptr);
126 }
127
128 unsigned long CDECL kqemu_vmalloc_to_phys(const void *vaddr)
129 {
130     LARGE_INTEGER pa;
131
132     pa = MmGetPhysicalAddress((void *)vaddr);
133     return (unsigned long)(pa.QuadPart >> PAGE_SHIFT);
134 }
135
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)
139 {
140 #if 1
141     PHYSICAL_ADDRESS pa;
142     
143     pa.QuadPart = page_index << PAGE_SHIFT;
144     return MmMapIoSpace(pa, size, MmNonCached);
145 #else
146     /* XXX: mingw32 tools too old */
147     return NULL;
148 #endif
149 }
150
151 /* Unmap the IO area */
152 void CDECL kqemu_io_unmap(void *ptr, unsigned int size)
153 {
154     return MmUnmapIoSpace(ptr, size);
155 }
156
157 /* return TRUE if a signal is pending (i.e. the guest must stop
158    execution) */
159 int CDECL kqemu_schedule(void)
160 {
161     return active_instance->current_irp->Cancel;
162 }
163
164 void CDECL kqemu_log(const char *fmt, ...)
165 {
166     char log_buf[1024];
167     va_list ap;
168
169     va_start(ap, fmt);
170     _vsnprintf(log_buf, sizeof(log_buf), fmt, ap);
171     DbgPrint("kqemu: %s", log_buf);
172     va_end(ap);
173 }
174
175 NTSTATUS STDCALL
176 KQemuCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
177 {
178     PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
179     struct kqemu_instance *State;
180
181     State = kqemu_vmalloc(sizeof(struct kqemu_instance));
182     if (State == NULL)
183     {
184         Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
185         Irp->IoStatus.Information = 0;
186         IoCompleteRequest(Irp, IO_NO_INCREMENT);
187         return STATUS_INSUFFICIENT_RESOURCES;
188     }
189
190     IrpStack->FileObject->FsContext = State;
191     
192     Irp->IoStatus.Status = STATUS_SUCCESS;
193     Irp->IoStatus.Information = 0;
194     IoCompleteRequest(Irp, IO_NO_INCREMENT);
195
196     return STATUS_SUCCESS;
197 }
198
199 NTSTATUS STDCALL
200 KQemuClose(PDEVICE_OBJECT DeviceObject, PIRP Irp)
201 {
202     PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
203     struct kqemu_instance *State = IrpStack->FileObject->FsContext;
204
205     if (State->state) {
206         kqemu_delete(State->state);
207         State->state = NULL;
208     }
209     kqemu_vfree(State);
210
211     Irp->IoStatus.Status = STATUS_SUCCESS;
212     Irp->IoStatus.Information = 0;
213     IoCompleteRequest(Irp, IO_NO_INCREMENT);
214
215     return STATUS_SUCCESS;
216 }
217
218 NTSTATUS STDCALL
219 KQemuDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
220 {
221     PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
222     struct kqemu_instance *State = IrpStack->FileObject->FsContext;
223     NTSTATUS Status;
224     int ret;
225
226     Irp->IoStatus.Information = 0;
227
228     switch (IrpStack->Parameters.DeviceIoControl.IoControlCode)
229     {
230         case KQEMU_INIT:
231             if (State->state) {
232                 Status = STATUS_INVALID_PARAMETER;
233                 break;
234             }
235             if (IrpStack->Parameters.DeviceIoControl.InputBufferLength <
236                 sizeof(struct kqemu_init)) 
237             {
238                 Status = STATUS_INVALID_PARAMETER;
239                 break;
240             }
241             State->state = kqemu_init((struct kqemu_init *)Irp->AssociatedIrp.SystemBuffer,
242                                       MAX_LOCKED_PAGES);
243             if (!State->state) {
244                 Status = STATUS_INSUFFICIENT_RESOURCES;
245                 break;
246             } 
247             Status = STATUS_SUCCESS;
248             break;
249
250         case KQEMU_EXEC:
251             {
252                 struct kqemu_cpu_state *ctx;
253
254                 if (!State->state) {
255                     Status = STATUS_INVALID_PARAMETER;
256                     break;
257                 }
258                 if (IrpStack->Parameters.DeviceIoControl.InputBufferLength <
259                     sizeof(*ctx) ||
260                     IrpStack->Parameters.DeviceIoControl.OutputBufferLength <
261                     sizeof(*ctx))
262                 {
263                     Status = STATUS_INVALID_PARAMETER;
264                     break;
265                 }
266                 
267                 ExAcquireFastMutex(&instance_lock);
268                 active_instance = State;
269                 State->current_irp = Irp;
270
271                 ctx = kqemu_get_cpu_state(State->state);
272                 
273                 RtlCopyMemory(ctx, Irp->AssociatedIrp.SystemBuffer, 
274                               sizeof(*ctx));
275                 ret = kqemu_exec(State->state);
276                 RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, ctx, sizeof(*ctx));
277
278                 ExReleaseFastMutex(&instance_lock);
279                 
280                 Irp->IoStatus.Information = sizeof(*ctx);
281                 Status = STATUS_SUCCESS;
282             }
283             break;
284     
285         case KQEMU_GET_VERSION:
286             if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength <
287                 sizeof(int)) 
288             {
289                 Status = STATUS_INVALID_PARAMETER;
290                 break;
291             }
292
293             *((int *)Irp->AssociatedIrp.SystemBuffer) = KQEMU_VERSION;
294             Irp->IoStatus.Information = sizeof(int);
295             Status = STATUS_SUCCESS;
296             break;
297
298         default:
299             Status = STATUS_INVALID_PARAMETER;
300             break;
301     }
302
303     Irp->IoStatus.Status = Status;
304     IoCompleteRequest(Irp, IO_NO_INCREMENT);
305
306     return Status;
307 }
308
309 VOID STDCALL
310 KQemuUnload(PDRIVER_OBJECT DriverObject)
311 {
312     UNICODE_STRING SymlinkName;
313
314     RtlInitUnicodeString(&SymlinkName, L"\\??\\kqemu");
315     IoDeleteSymbolicLink(&SymlinkName);
316     IoDeleteDevice(DriverObject->DeviceObject);
317 }
318
319 NTSTATUS STDCALL 
320 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
321 {
322     PDEVICE_OBJECT DeviceObject;
323     UNICODE_STRING DeviceName;
324     UNICODE_STRING SymlinkName;
325     NTSTATUS Status;
326
327     DbgPrint("QEMU Accelerator Module version %d.%d.%d\n",
328              (KQEMU_VERSION >> 16),
329              (KQEMU_VERSION >> 8) & 0xff,
330              (KQEMU_VERSION) & 0xff);
331
332     MmLockPagableCodeSection(DriverEntry);
333
334     ExInitializeFastMutex(&instance_lock);
335
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;
340
341     RtlInitUnicodeString(&DeviceName, L"\\Device\\kqemu");
342     RtlInitUnicodeString(&SymlinkName, L"\\??\\kqemu");
343
344     Status = IoCreateDevice(DriverObject, 0,
345                             &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE,
346                             &DeviceObject);
347     if (!NT_SUCCESS(Status))
348     {
349         return Status;
350     }
351
352     /* Create the dos device link */
353     Status = IoCreateSymbolicLink(&SymlinkName, &DeviceName);
354     if (!NT_SUCCESS(Status))
355     {
356         IoDeleteDevice(DeviceObject);
357         return Status;
358     }
359
360     return STATUS_SUCCESS;
361 }