[project] Don't use mini IRP handling for IRP_MJ_CREATE/CLOSE
[people/sha0/winvblock.git] / src / winvblock / driver.c
1 /**
2  * Copyright (C) 2009-2010, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  * Copyright 2006-2008, V.
4  * For WinAoE contact information, see http://winaoe.org/
5  *
6  * This file is part of WinVBlock, derived from WinAoE.
7  *
8  * WinVBlock is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * WinVBlock is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with WinVBlock.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /**
23  * @file
24  *
25  * Driver specifics.
26  */
27
28 #include <stdio.h>
29 #include <ntddk.h>
30
31 #include "winvblock.h"
32 #include "wv_stdlib.h"
33 #include "wv_string.h"
34 #include "portable.h"
35 #include "irp.h"
36 #include "driver.h"
37 #include "device.h"
38 #include "disk.h"
39 #include "registry.h"
40 #include "mount.h"
41 #include "bus.h"
42 #include "filedisk.h"
43 #include "ramdisk.h"
44 #include "debug.h"
45
46 /* Exported. */
47 PDRIVER_OBJECT driver__obj_ptr = NULL;
48
49 /* Globals. */
50 static void * driver__state_handle_;
51 static winvblock__bool driver__started_ = FALSE;
52 static PDEVICE_OBJECT driver__bus_fdo_ = NULL;
53 static KSPIN_LOCK driver__bus_fdo_lock_;
54 /* Contains TXTSETUP.SIF/BOOT.INI-style OsLoadOptions parameters. */
55 static LPWSTR driver__os_load_opts_ = NULL;
56
57 /* Forward declarations. */
58 static driver__dispatch_func driver__dispatch_not_supported_;
59 static driver__dispatch_func driver__dispatch_power_;
60 static driver__dispatch_func driver__dispatch_create_close_;
61 static driver__dispatch_func driver__dispatch_;
62 static void STDCALL driver__unload_(IN PDRIVER_OBJECT);
63
64 static LPWSTR STDCALL get_opt(IN LPWSTR opt_name) {
65     LPWSTR our_opts, the_opt;
66     WCHAR our_sig[] = L"WINVBLOCK=";
67     /* To produce constant integer expressions. */
68     enum {
69         our_sig_len_bytes = sizeof ( our_sig ) - sizeof ( WCHAR ),
70         our_sig_len = our_sig_len_bytes / sizeof ( WCHAR )
71       };
72     size_t opt_name_len, opt_name_len_bytes;
73
74     if (!driver__os_load_opts_ || !opt_name)
75       return NULL;
76
77     /* Find /WINVBLOCK= options. */
78     our_opts = driver__os_load_opts_;
79     while (*our_opts != L'\0') {
80         if (!wv_memcmpeq(our_opts, our_sig, our_sig_len_bytes)) {
81             our_opts++;
82             continue;
83           }
84         our_opts += our_sig_len;
85         break;
86       }
87
88     /* Search for the specific option. */
89     the_opt = our_opts;
90     opt_name_len = wcslen(opt_name);
91     opt_name_len_bytes = opt_name_len * sizeof (WCHAR);
92     while (*the_opt != L'\0' && *the_opt != L' ') {
93         if (!wv_memcmpeq(the_opt, opt_name, opt_name_len_bytes)) {
94             while (*the_opt != L'\0' && *the_opt != L' ' && *the_opt != L',')
95               the_opt++;
96             continue;
97           }
98         the_opt += opt_name_len;
99         break;
100       }
101
102     if (*the_opt == L'\0' || *the_opt == L' ')
103       return NULL;
104
105     /* Next should come "=". */
106     if (*the_opt != L'=')
107       return NULL;
108
109     /*
110      * And finally our option's value.  The caller needs
111      * to worry about looking past the end of the option.
112      */
113     the_opt++;
114     if (*the_opt == L'\0' || *the_opt == L' ')
115       return NULL;
116     return the_opt;
117   }
118
119 static NTSTATUS STDCALL driver__attach_fdo_(
120     IN PDRIVER_OBJECT DriverObject,
121     IN PDEVICE_OBJECT PhysicalDeviceObject
122   ) {
123     KIRQL irql;
124     NTSTATUS status;
125     PLIST_ENTRY walker;
126     struct bus__type * bus;
127     PUNICODE_STRING dev_name = NULL;
128     PDEVICE_OBJECT fdo = NULL;
129     struct device__type * dev_ptr;
130
131     DBG("Entry\n");
132     /* Do we alreay have our main bus? */
133     if (driver__bus_fdo_) {
134         DBG("Already have the main bus.  Refusing.\n");
135         status = STATUS_NOT_SUPPORTED;
136         goto err_already_established;
137       }
138     /* Create the bus. */
139     bus = bus__create();
140     if (!bus) {
141         DBG("bus__create() failed for the main bus.\n");
142         status = STATUS_INSUFFICIENT_RESOURCES;
143         goto err_bus;
144       }
145     /* In booting, he has a name.  His name is WinVBlock. */
146     RtlInitUnicodeString(
147         &bus->dev_name,
148         L"\\Device\\" winvblock__literal_w
149       );
150     RtlInitUnicodeString(
151         &bus->dos_dev_name,
152         L"\\DosDevices\\" winvblock__literal_w
153       );
154     bus->named = TRUE;
155     /* Create the bus FDO. */
156     status = IoCreateDevice(
157         DriverObject,
158         sizeof (driver__dev_ext),
159         &bus->dev_name,
160         FILE_DEVICE_CONTROLLER,
161         FILE_DEVICE_SECURE_OPEN,
162         FALSE,
163         &fdo
164       );
165     if (!NT_SUCCESS(status)) {
166         DBG("IoCreateDevice() failed!\n");
167         goto err_fdo;
168       }
169     /* DosDevice symlink. */
170     status = IoCreateSymbolicLink(
171         &bus->dos_dev_name,
172         &bus->dev_name
173       );
174     if (!NT_SUCCESS(status)) {
175         DBG("IoCreateSymbolicLink() failed!\n");
176         goto err_dos_symlink;
177       }
178     /* Set associations for the bus, device, FDO, PDO. */
179     device__set(fdo, bus->device);
180     bus->device->Self = fdo;
181     bus->PhysicalDeviceObject = PhysicalDeviceObject;
182     fdo->Flags |= DO_DIRECT_IO;         /* FIXME? */
183     fdo->Flags |= DO_POWER_INRUSH;      /* FIXME? */
184     /* Attach the FDO to the PDO. */
185     bus->LowerDeviceObject = IoAttachDeviceToDeviceStack(
186         fdo,
187         PhysicalDeviceObject
188       );
189     if (bus->LowerDeviceObject == NULL) {
190         status = STATUS_NO_SUCH_DEVICE;
191         DBG("IoAttachDeviceToDeviceStack() failed!\n");
192         goto err_attach;
193       }
194     /* Ok! */
195     KeAcquireSpinLock(&driver__bus_fdo_lock_, &irql);
196     if (driver__bus_fdo_) {
197         KeReleaseSpinLock(&driver__bus_fdo_lock_, irql);
198         DBG("Beaten to it!\n");
199         status = STATUS_NOT_SUPPORTED;
200         goto err_race_failed;
201       }
202     fdo->Flags &= ~DO_DEVICE_INITIALIZING;
203     #ifdef RIS
204     bus->device->State = Started;
205     #endif
206     driver__bus_fdo_ = fdo;
207     KeReleaseSpinLock(&driver__bus_fdo_lock_, irql);
208     DBG("Exit\n");
209     return STATUS_SUCCESS;
210
211     err_race_failed:
212
213     err_attach:
214
215     IoDeleteSymbolicLink(&bus->dos_dev_name);
216     err_dos_symlink:
217
218     IoDeleteDevice(fdo);
219     err_fdo:
220
221     device__free(bus->device);
222     err_bus:
223
224     err_already_established:
225
226     DBG("Exit with failure\n");
227     return status;
228   }
229
230 /* Create the root-enumerated, main bus device. */
231 NTSTATUS STDCALL driver__create_bus_(void) {
232     struct bus__type * bus;
233     NTSTATUS status;
234     PDEVICE_OBJECT bus_pdo = NULL;
235
236     /* Create the PDO. */
237     IoReportDetectedDevice(
238         driver__obj_ptr,
239         InterfaceTypeUndefined,
240         -1,
241         -1,
242         NULL,
243         NULL,
244         FALSE,
245         &bus_pdo
246       );
247     if (bus_pdo == NULL) {
248         DBG("IoReportDetectedDevice() went wrong!  Exiting.\n");
249         status = STATUS_UNSUCCESSFUL;
250         goto err_driver_bus;
251       }
252     /* Attach FDO to PDO. */
253     status = driver__attach_fdo_(driver__obj_ptr, bus_pdo);
254     if (!NT_SUCCESS(status)) {
255         DBG("driver__attach_fdo_() went wrong!\n");
256         goto err_add_dev;
257       }
258     /* PDO created, FDO attached.  All done. */
259     return STATUS_SUCCESS;
260
261     err_add_dev:
262
263     IoDeleteDevice(bus_pdo);
264     err_driver_bus:
265
266     return status;
267   }
268
269 /*
270  * Note the exception to the function naming convention.
271  * TODO: See if a Makefile change is good enough.
272  */
273 NTSTATUS STDCALL DriverEntry(
274     IN PDRIVER_OBJECT DriverObject,
275     IN PUNICODE_STRING RegistryPath
276   ) {
277     NTSTATUS status;
278     int i;
279
280     DBG("Entry\n");
281     if (driver__obj_ptr) {
282         DBG("Re-entry not allowed!\n");
283         return STATUS_NOT_SUPPORTED;
284       }
285     driver__obj_ptr = DriverObject;
286     if (driver__started_)
287       return STATUS_SUCCESS;
288     Debug_Initialize();
289     status = registry__note_os_load_opts(&driver__os_load_opts_);
290     if (!NT_SUCCESS(status))
291       return Error("registry__note_driver__os_load_opts", status);
292
293     driver__state_handle_ = NULL;
294     KeInitializeSpinLock(&driver__bus_fdo_lock_);
295
296     if ((driver__state_handle_ = PoRegisterSystemState(
297         NULL,
298         ES_CONTINUOUS
299       )) == NULL) {
300         DBG("Could not set system state to ES_CONTINUOUS!!\n");
301       }
302     /*
303      * Set up IRP MajorFunction function table for devices
304      * this driver handles.
305      */
306     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
307       DriverObject->MajorFunction[i] = driver__dispatch_not_supported_;
308     DriverObject->MajorFunction[IRP_MJ_PNP] = driver__dispatch_;
309     DriverObject->MajorFunction[IRP_MJ_POWER] = driver__dispatch_power_;
310     DriverObject->MajorFunction[IRP_MJ_CREATE] = driver__dispatch_create_close_;
311     DriverObject->MajorFunction[IRP_MJ_CLOSE] = driver__dispatch_create_close_;
312     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = driver__dispatch_;
313     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = driver__dispatch_;
314     DriverObject->MajorFunction[IRP_MJ_SCSI] = driver__dispatch_;
315     /* Set the driver Unload callback. */
316     DriverObject->DriverUnload = driver__unload_;
317     /* Set the driver AddDevice callback. */
318     DriverObject->DriverExtension->AddDevice = driver__attach_fdo_;
319     /* Initialize various modules. */
320     disk__init();               /* TODO: Check for error. */
321     filedisk__init();           /* TODO: Check for error. */
322     ramdisk__init();            /* TODO: Check for error. */
323
324     /*
325      * Always create the root-enumerated, main bus device.
326      * This is required in order to boot from a WinVBlock disk.
327      */
328     status = driver__create_bus_();
329     if(!NT_SUCCESS(status))
330       goto err_bus;
331
332     driver__started_ = TRUE;
333     DBG("Exit\n");
334     return STATUS_SUCCESS;
335
336     err_bus:
337
338     driver__unload_(DriverObject);
339     DBG("Exit due to failure\n");
340     return status;
341   }
342
343 static NTSTATUS STDCALL driver__dispatch_not_supported_(
344     IN PDEVICE_OBJECT dev_obj,
345     IN PIRP irp
346   ) {
347     irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
348     IoCompleteRequest(irp, IO_NO_INCREMENT);
349     return irp->IoStatus.Status;
350   }
351
352 /* IRP is not understood. */
353 extern winvblock__lib_func NTSTATUS STDCALL driver__not_supported(
354     IN PDEVICE_OBJECT dev_obj,
355     IN PIRP irp,
356     IN PIO_STACK_LOCATION stack,
357     IN struct device__type * dev_ptr,
358     OUT winvblock__bool_ptr completion_ptr
359   ) {
360     NTSTATUS status = STATUS_NOT_SUPPORTED;
361
362     irp->IoStatus.Status = status;
363     IoCompleteRequest(irp, IO_NO_INCREMENT);
364     *completion_ptr = TRUE;
365     return status;
366   }
367
368 /**
369  * Common IRP completion routine.
370  *
371  * @v irp               Points to the IRP to complete.
372  * @v info              Number of bytes returned for the IRP, or 0.
373  * @v status            Status for the IRP to complete.
374  * @ret NTSTATUS        Returns the status value, as passed.
375  */
376 winvblock__lib_func NTSTATUS STDCALL driver__complete_irp(
377     IN PIRP irp,
378     IN ULONG_PTR info,
379     IN NTSTATUS status
380   ) {
381     irp->IoStatus.Information = info;
382     irp->IoStatus.Status = status;
383     IoCompleteRequest(irp, IO_NO_INCREMENT);
384     #ifdef DEBUGIRPS
385     Debug_IrpEnd(irp, status);
386     #endif
387     return status;
388   }
389
390 /* Handle a power IRP. */
391 static NTSTATUS driver__dispatch_power_(
392     IN PDEVICE_OBJECT dev_obj,
393     IN PIRP irp
394   ) {
395     /* device__get() checks for a NULL dev_obj */
396     struct device__type * dev = device__get(dev_obj);
397
398     #ifdef DEBUGIRPS
399     Debug_IrpStart(dev_obj, irp);
400     #endif
401     /* Check that the device exists. */
402     if (!dev || dev->state == device__state_deleted) {
403         /* Even if it doesn't, a power IRP is important! */
404         PoStartNextPowerIrp(irp);
405         return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
406       }
407     /* Call the particular device's power handler. */
408     if (dev->irp_mj && dev->irp_mj->power)            
409       return dev->irp_mj->power(dev, irp);
410     /* Otherwise, we don't support the IRP. */
411     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
412   }
413
414 /* Handle an IRP_MJ_CREATE or IRP_MJ_CLOSE IRP. */
415 static NTSTATUS driver__dispatch_create_close_(
416     IN PDEVICE_OBJECT dev_obj,
417     IN PIRP irp
418   ) {
419     /* device__get() checks for a NULL dev_obj */
420     struct device__type * dev = device__get(dev_obj);
421
422     #ifdef DEBUGIRPS
423     Debug_IrpStart(dev_obj, irp);
424     #endif
425     /* Check that the device exists. */
426     if (!dev || dev->state == device__state_deleted)
427       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
428     /* Always succeed with nothing to do. */
429     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
430   }
431
432 static NTSTATUS STDCALL driver__dispatch_(
433     IN PDEVICE_OBJECT DeviceObject,
434     IN PIRP Irp
435   ) {
436     NTSTATUS status;
437     struct device__type * dev_ptr;
438
439     #ifdef DEBUGIRPS
440     Debug_IrpStart(DeviceObject, Irp);
441     #endif
442     dev_ptr = device__get(DeviceObject);
443
444     /* Check for a deleted device. */
445     if (dev_ptr->state == device__state_deleted) {
446         Irp->IoStatus.Information = 0;
447         Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
448         IoCompleteRequest(Irp, IO_NO_INCREMENT);
449         #ifdef DEBUGIRPS
450         Debug_IrpEnd ( Irp, STATUS_NO_SUCH_DEVICE );
451         #endif
452         return STATUS_NO_SUCH_DEVICE;
453       }
454
455     /* Enqueue the IRP for threaded devices, or process immediately. */
456     if (dev_ptr->thread) {
457         IoMarkIrpPending(Irp);
458         ExInterlockedInsertTailList(
459             &dev_ptr->irp_list,
460             /* Where IRPs can be linked. */
461             &Irp->Tail.Overlay.ListEntry,
462             &dev_ptr->irp_list_lock
463           );
464         KeSetEvent(&dev_ptr->thread_wakeup, 0, FALSE);
465         status = STATUS_PENDING;
466       } else
467       status = dev_ptr->dispatch(DeviceObject, Irp);
468
469     return status;
470   }
471
472 /* Place-holder while implementing a dispatch routine per device class. */
473 winvblock__lib_func NTSTATUS STDCALL driver__default_dispatch(
474     IN PDEVICE_OBJECT dev,
475     IN PIRP irp
476   ) {
477     NTSTATUS status;
478     winvblock__bool completion = FALSE;
479     static const irp__handling handling_table[] = {
480         /*
481          * Major, minor, any major?, any minor?, handler
482          * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
483          * Note that the fall-through case must come FIRST!
484          * Why? It sets completion to true, so others won't be called.
485          */
486         {             0, 0,  TRUE, TRUE, driver__not_supported },
487       };
488
489     status = irp__process(
490         dev,
491         irp,
492         IoGetCurrentIrpStackLocation(irp),
493         device__get(dev),
494         &completion
495       );
496     /* Fall through to some driver defaults, if needed. */
497     if (status == STATUS_NOT_SUPPORTED && !completion) {
498         status = irp__process_with_table(
499             dev,
500             irp,
501             handling_table,
502             sizeof handling_table,
503             &completion
504           );
505       }
506     #ifdef DEBUGIRPS
507     if (status != STATUS_PENDING)
508       Debug_IrpEnd(irp, status);
509     #endif
510
511     return status;
512   }
513
514 static void STDCALL driver__unload_(IN PDRIVER_OBJECT DriverObject) {
515     UNICODE_STRING DosDeviceName;
516
517     DBG("Unloading...\n");
518     if (driver__state_handle_ != NULL)
519       PoUnregisterSystemState(driver__state_handle_);
520     RtlInitUnicodeString(
521         &DosDeviceName,
522         L"\\DosDevices\\" winvblock__literal_w
523       );
524     IoDeleteSymbolicLink(&DosDeviceName);
525     driver__bus_fdo_ = NULL;
526     wv_free(driver__os_load_opts_);
527     driver__started_ = FALSE;
528     DBG("Done\n");
529   }
530
531 winvblock__lib_func void STDCALL Driver_CompletePendingIrp(IN PIRP Irp) {
532     #ifdef DEBUGIRPS
533     Debug_IrpEnd(Irp, Irp->IoStatus.Status);
534     #endif
535     IoCompleteRequest(Irp, IO_NO_INCREMENT);
536   }
537
538 /* Note the exception to the function naming convention. */
539 winvblock__lib_func NTSTATUS STDCALL Error(
540     IN PCHAR Message,
541     IN NTSTATUS Status
542   ) {
543     DBG("%s: 0x%08x\n", Message, Status);
544     return Status;
545   }
546
547 /**
548  * Get a pointer to the driver bus device.
549  *
550  * @ret         A pointer to the driver bus, or NULL.
551  */
552 winvblock__lib_func struct bus__type * driver__bus(void) {
553     if (!driver__bus_fdo_) {
554         DBG("No driver bus device!\n");
555         return NULL;
556       }
557     return bus__get(device__get(driver__bus_fdo_));
558   }