[driver] Rename driver__obj_ptr to WvDriverObj
[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 #include <scsi.h>
31
32 #include "winvblock.h"
33 #include "wv_stdlib.h"
34 #include "wv_string.h"
35 #include "portable.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 /* Names for the main bus. */
47 #define WV_M_BUS_NAME_ (L"\\Device\\" winvblock__literal_w)
48 #define WV_M_BUS_DOSNAME_ (L"\\DosDevices\\" winvblock__literal_w)
49
50 /* Exported. */
51 PDRIVER_OBJECT WvDriverObj = NULL;
52
53 /* Globals. */
54 static void * driver__state_handle_;
55 static winvblock__bool driver__started_ = FALSE;
56 static PDEVICE_OBJECT driver__bus_fdo_ = NULL;
57 static KSPIN_LOCK driver__bus_fdo_lock_;
58 static UNICODE_STRING WvBusName_ = {
59     sizeof WV_M_BUS_NAME_,
60     sizeof WV_M_BUS_NAME_,
61     WV_M_BUS_NAME_
62   };
63 static UNICODE_STRING WvBusDosname_ = {
64     sizeof WV_M_BUS_DOSNAME_,
65     sizeof WV_M_BUS_DOSNAME_,
66     WV_M_BUS_DOSNAME_
67   };
68 /* Contains TXTSETUP.SIF/BOOT.INI-style OsLoadOptions parameters. */
69 static LPWSTR driver__os_load_opts_ = NULL;
70
71 /* Forward declarations. */
72 static driver__dispatch_func driver__dispatch_not_supported_;
73 static driver__dispatch_func driver__dispatch_power_;
74 static driver__dispatch_func driver__dispatch_create_close_;
75 static driver__dispatch_func driver__dispatch_sys_ctl_;
76 static driver__dispatch_func driver__dispatch_dev_ctl_;
77 static driver__dispatch_func driver__dispatch_scsi_;
78 static driver__dispatch_func driver__dispatch_pnp_;
79 static void STDCALL driver__unload_(IN PDRIVER_OBJECT);
80
81 static LPWSTR STDCALL get_opt(IN LPWSTR opt_name) {
82     LPWSTR our_opts, the_opt;
83     WCHAR our_sig[] = L"WINVBLOCK=";
84     /* To produce constant integer expressions. */
85     enum {
86         our_sig_len_bytes = sizeof ( our_sig ) - sizeof ( WCHAR ),
87         our_sig_len = our_sig_len_bytes / sizeof ( WCHAR )
88       };
89     size_t opt_name_len, opt_name_len_bytes;
90
91     if (!driver__os_load_opts_ || !opt_name)
92       return NULL;
93
94     /* Find /WINVBLOCK= options. */
95     our_opts = driver__os_load_opts_;
96     while (*our_opts != L'\0') {
97         if (!wv_memcmpeq(our_opts, our_sig, our_sig_len_bytes)) {
98             our_opts++;
99             continue;
100           }
101         our_opts += our_sig_len;
102         break;
103       }
104
105     /* Search for the specific option. */
106     the_opt = our_opts;
107     opt_name_len = wcslen(opt_name);
108     opt_name_len_bytes = opt_name_len * sizeof (WCHAR);
109     while (*the_opt != L'\0' && *the_opt != L' ') {
110         if (!wv_memcmpeq(the_opt, opt_name, opt_name_len_bytes)) {
111             while (*the_opt != L'\0' && *the_opt != L' ' && *the_opt != L',')
112               the_opt++;
113             continue;
114           }
115         the_opt += opt_name_len;
116         break;
117       }
118
119     if (*the_opt == L'\0' || *the_opt == L' ')
120       return NULL;
121
122     /* Next should come "=". */
123     if (*the_opt != L'=')
124       return NULL;
125
126     /*
127      * And finally our option's value.  The caller needs
128      * to worry about looking past the end of the option.
129      */
130     the_opt++;
131     if (*the_opt == L'\0' || *the_opt == L' ')
132       return NULL;
133     return the_opt;
134   }
135
136 static NTSTATUS STDCALL driver__attach_fdo_(
137     IN PDRIVER_OBJECT DriverObject,
138     IN PDEVICE_OBJECT PhysicalDeviceObject
139   ) {
140     KIRQL irql;
141     NTSTATUS status;
142     PLIST_ENTRY walker;
143     WV_SP_BUS_T bus;
144     PUNICODE_STRING dev_name = NULL;
145     PDEVICE_OBJECT fdo = NULL;
146
147     DBG("Entry\n");
148     /* Do we alreay have our main bus? */
149     if (driver__bus_fdo_) {
150         DBG("Already have the main bus.  Refusing.\n");
151         status = STATUS_NOT_SUPPORTED;
152         goto err_already_established;
153       }
154     /* Create the bus. */
155     bus = WvBusCreate();
156     if (!bus) {
157         DBG("WvBusCreate() failed for the main bus.\n");
158         status = STATUS_INSUFFICIENT_RESOURCES;
159         goto err_bus;
160       }
161     /* Create the bus FDO. */
162     status = IoCreateDevice(
163         DriverObject,
164         sizeof (driver__dev_ext),
165         &WvBusName_,
166         FILE_DEVICE_CONTROLLER,
167         FILE_DEVICE_SECURE_OPEN,
168         FALSE,
169         &fdo
170       );
171     if (!NT_SUCCESS(status)) {
172         DBG("IoCreateDevice() failed!\n");
173         goto err_fdo;
174       }
175     /* DosDevice symlink. */
176     status = IoCreateSymbolicLink(
177         &WvBusDosname_,
178         &WvBusName_
179       );
180     if (!NT_SUCCESS(status)) {
181         DBG("IoCreateSymbolicLink() failed!\n");
182         goto err_dos_symlink;
183       }
184     /* Set associations for the bus, device, FDO, PDO. */
185     WvDevForDevObj(fdo, &bus->Dev);
186     bus->Dev.Self = bus->Fdo = fdo;
187     bus->Dev.IsBus = TRUE;
188     bus->PhysicalDeviceObject = PhysicalDeviceObject;
189     fdo->Flags |= DO_DIRECT_IO;         /* FIXME? */
190     fdo->Flags |= DO_POWER_INRUSH;      /* FIXME? */
191     /* Attach the FDO to the PDO. */
192     bus->LowerDeviceObject = IoAttachDeviceToDeviceStack(
193         fdo,
194         PhysicalDeviceObject
195       );
196     if (bus->LowerDeviceObject == NULL) {
197         status = STATUS_NO_SUCH_DEVICE;
198         DBG("IoAttachDeviceToDeviceStack() failed!\n");
199         goto err_attach;
200       }
201     status = WvBusStartThread(bus);
202     if (!NT_SUCCESS(status)) {
203         DBG("Couldn't start bus thread!\n");
204         goto err_thread;
205       }
206     /* Ok! */
207     KeAcquireSpinLock(&driver__bus_fdo_lock_, &irql);
208     if (driver__bus_fdo_) {
209         KeReleaseSpinLock(&driver__bus_fdo_lock_, irql);
210         DBG("Beaten to it!\n");
211         status = STATUS_NOT_SUPPORTED;
212         goto err_race_failed;
213       }
214     fdo->Flags &= ~DO_DEVICE_INITIALIZING;
215     #ifdef RIS
216     bus->device->State = Started;
217     #endif
218     driver__bus_fdo_ = fdo;
219     KeReleaseSpinLock(&driver__bus_fdo_lock_, irql);
220     DBG("Exit\n");
221     return STATUS_SUCCESS;
222
223     err_race_failed:
224
225     err_thread:
226
227     err_attach:
228
229     IoDeleteSymbolicLink(&WvBusDosname_);
230     err_dos_symlink:
231
232     IoDeleteDevice(fdo);
233     err_fdo:
234
235     WvDevFree(&bus->Dev);
236     err_bus:
237
238     err_already_established:
239
240     DBG("Exit with failure\n");
241     return status;
242   }
243
244 /* Create the root-enumerated, main bus device. */
245 NTSTATUS STDCALL driver__create_bus_(void) {
246     WV_SP_BUS_T bus;
247     NTSTATUS status;
248     PDEVICE_OBJECT bus_pdo = NULL;
249
250     /* Create the PDO. */
251     IoReportDetectedDevice(
252         WvDriverObj,
253         InterfaceTypeUndefined,
254         -1,
255         -1,
256         NULL,
257         NULL,
258         FALSE,
259         &bus_pdo
260       );
261     if (bus_pdo == NULL) {
262         DBG("IoReportDetectedDevice() went wrong!  Exiting.\n");
263         status = STATUS_UNSUCCESSFUL;
264         goto err_driver_bus;
265       }
266     /* Attach FDO to PDO. */
267     status = driver__attach_fdo_(WvDriverObj, bus_pdo);
268     if (!NT_SUCCESS(status)) {
269         DBG("driver__attach_fdo_() went wrong!\n");
270         goto err_add_dev;
271       }
272     /* PDO created, FDO attached.  All done. */
273     return STATUS_SUCCESS;
274
275     err_add_dev:
276
277     IoDeleteDevice(bus_pdo);
278     err_driver_bus:
279
280     return status;
281   }
282
283 /*
284  * Note the exception to the function naming convention.
285  * TODO: See if a Makefile change is good enough.
286  */
287 NTSTATUS STDCALL DriverEntry(
288     IN PDRIVER_OBJECT DriverObject,
289     IN PUNICODE_STRING RegistryPath
290   ) {
291     NTSTATUS status;
292     int i;
293
294     DBG("Entry\n");
295     if (WvDriverObj) {
296         DBG("Re-entry not allowed!\n");
297         return STATUS_NOT_SUPPORTED;
298       }
299     WvDriverObj = DriverObject;
300     if (driver__started_)
301       return STATUS_SUCCESS;
302     Debug_Initialize();
303     status = registry__note_os_load_opts(&driver__os_load_opts_);
304     if (!NT_SUCCESS(status))
305       return Error("registry__note_driver__os_load_opts", status);
306
307     driver__state_handle_ = NULL;
308     KeInitializeSpinLock(&driver__bus_fdo_lock_);
309
310     if ((driver__state_handle_ = PoRegisterSystemState(
311         NULL,
312         ES_CONTINUOUS
313       )) == NULL) {
314         DBG("Could not set system state to ES_CONTINUOUS!!\n");
315       }
316     /*
317      * Set up IRP MajorFunction function table for devices
318      * this driver handles.
319      */
320     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
321       DriverObject->MajorFunction[i] = driver__dispatch_not_supported_;
322     DriverObject->MajorFunction[IRP_MJ_PNP] = driver__dispatch_pnp_;
323     DriverObject->MajorFunction[IRP_MJ_POWER] = driver__dispatch_power_;
324     DriverObject->MajorFunction[IRP_MJ_CREATE] = driver__dispatch_create_close_;
325     DriverObject->MajorFunction[IRP_MJ_CLOSE] = driver__dispatch_create_close_;
326     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
327       driver__dispatch_sys_ctl_;
328     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
329       driver__dispatch_dev_ctl_;
330     DriverObject->MajorFunction[IRP_MJ_SCSI] = driver__dispatch_scsi_;
331     /* Set the driver Unload callback. */
332     DriverObject->DriverUnload = driver__unload_;
333     /* Set the driver AddDevice callback. */
334     DriverObject->DriverExtension->AddDevice = driver__attach_fdo_;
335     /* Initialize various modules. */
336     disk__module_init();        /* TODO: Check for error. */
337     filedisk__module_init();    /* TODO: Check for error. */
338     ramdisk__module_init();     /* TODO: Check for error. */
339
340     /*
341      * Always create the root-enumerated, main bus device.
342      * This is required in order to boot from a WinVBlock disk.
343      */
344     status = driver__create_bus_();
345     if(!NT_SUCCESS(status))
346       goto err_bus;
347
348     driver__started_ = TRUE;
349     DBG("Exit\n");
350     return STATUS_SUCCESS;
351
352     err_bus:
353
354     driver__unload_(DriverObject);
355     DBG("Exit due to failure\n");
356     return status;
357   }
358
359 static NTSTATUS STDCALL driver__dispatch_not_supported_(
360     IN PDEVICE_OBJECT dev_obj,
361     IN PIRP irp
362   ) {
363     irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
364     IoCompleteRequest(irp, IO_NO_INCREMENT);
365     return irp->IoStatus.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     /* WvDevFromDevObj() checks for a NULL dev_obj */
396     WV_SP_DEV_T dev = WvDevFromDevObj(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 == WvDevStateDeleted) {
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->IrpMj && dev->IrpMj->Power)
409       return dev->IrpMj->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     /* WvDevFromDevObj() checks for a NULL dev_obj */
420     WV_SP_DEV_T dev = WvDevFromDevObj(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 == WvDevStateDeleted)
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 /* Handle an IRP_MJ_SYSTEM_CONTROL IRP. */
433 static NTSTATUS driver__dispatch_sys_ctl_(
434     IN PDEVICE_OBJECT dev_obj,
435     IN PIRP irp
436   ) {
437     /* WvDevFromDevObj() checks for a NULL dev_obj */
438     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
439
440     #ifdef DEBUGIRPS
441     Debug_IrpStart(dev_obj, irp);
442     #endif
443     /* Check that the device exists. */
444     if (!dev || dev->State == WvDevStateDeleted)
445       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
446     /* Call the particular device's power handler. */
447     if (dev->IrpMj && dev->IrpMj->SysCtl)
448       return dev->IrpMj->SysCtl(dev, irp);
449     /* Otherwise, we don't support the IRP. */
450     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
451   }
452
453 /* Handle an IRP_MJ_DEVICE_CONTROL IRP. */
454 static NTSTATUS driver__dispatch_dev_ctl_(
455     IN PDEVICE_OBJECT dev_obj,
456     IN PIRP irp
457   ) {
458     /* WvDevFromDevObj() checks for a NULL dev_obj */
459     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
460     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
461
462     #ifdef DEBUGIRPS
463     Debug_IrpStart(dev_obj, irp);
464     #endif
465     /* Check that the device exists. */
466     if (!dev || dev->State == WvDevStateDeleted)
467       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
468     /* Call the particular device's power handler. */
469     if (dev->IrpMj && dev->IrpMj->DevCtl) {
470         return dev->IrpMj->DevCtl(
471             dev,
472             irp,
473             io_stack_loc->Parameters.DeviceIoControl.IoControlCode
474           );
475       }
476     /* Otherwise, we don't support the IRP. */
477     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
478   }
479
480 /* Handle an IRP_MJ_SCSI IRP. */
481 static NTSTATUS driver__dispatch_scsi_(
482     IN PDEVICE_OBJECT dev_obj,
483     IN PIRP irp
484   ) {
485     /* WvDevFromDevObj() checks for a NULL dev_obj */
486     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
487     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
488
489     #ifdef DEBUGIRPS
490     Debug_IrpStart(dev_obj, irp);
491     #endif
492     /* Check that the device exists. */
493     if (!dev || dev->State == WvDevStateDeleted)
494       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
495     /* Call the particular device's power handler. */
496     if (dev->IrpMj && dev->IrpMj->Scsi) {
497         return dev->IrpMj->Scsi(
498             dev,
499             irp,
500             io_stack_loc->Parameters.Scsi.Srb->Function
501           );
502       }
503     /* Otherwise, we don't support the IRP. */
504     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
505   }
506
507 /* Handle an IRP_MJ_PNP IRP. */
508 static NTSTATUS driver__dispatch_pnp_(
509     IN PDEVICE_OBJECT dev_obj,
510     IN PIRP irp
511   ) {
512     /* WvDevFromDevObj() checks for a NULL dev_obj */
513     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
514     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
515
516     #ifdef DEBUGIRPS
517     Debug_IrpStart(dev_obj, irp);
518     #endif
519     /* Check that the device exists. */
520     if (!dev || dev->State == WvDevStateDeleted)
521       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
522     /* Call the particular device's power handler. */
523     if (dev->IrpMj && dev->IrpMj->Pnp) {
524         return dev->IrpMj->Pnp(
525             dev,
526             irp,
527             io_stack_loc->MinorFunction
528           );
529       }
530     /* Otherwise, we don't support the IRP. */
531     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
532   }
533
534 static void STDCALL driver__unload_(IN PDRIVER_OBJECT DriverObject) {
535     DBG("Unloading...\n");
536     if (driver__state_handle_ != NULL)
537       PoUnregisterSystemState(driver__state_handle_);
538     IoDeleteSymbolicLink(&WvBusDosname_);
539     driver__bus_fdo_ = NULL;
540     wv_free(driver__os_load_opts_);
541     driver__started_ = FALSE;
542     DBG("Done\n");
543   }
544
545 winvblock__lib_func void STDCALL Driver_CompletePendingIrp(IN PIRP Irp) {
546     #ifdef DEBUGIRPS
547     Debug_IrpEnd(Irp, Irp->IoStatus.Status);
548     #endif
549     IoCompleteRequest(Irp, IO_NO_INCREMENT);
550   }
551
552 /* Note the exception to the function naming convention. */
553 winvblock__lib_func NTSTATUS STDCALL Error(
554     IN PCHAR Message,
555     IN NTSTATUS Status
556   ) {
557     DBG("%s: 0x%08x\n", Message, Status);
558     return Status;
559   }
560
561 /**
562  * Get a pointer to the driver bus device.
563  *
564  * @ret         A pointer to the driver bus, or NULL.
565  */
566 winvblock__lib_func WV_SP_BUS_T driver__bus(void) {
567     if (!driver__bus_fdo_) {
568         DBG("No driver bus device!\n");
569         return NULL;
570       }
571     return WvBusFromDev(WvDevFromDevObj(driver__bus_fdo_));
572   }