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