47d617a61a9cd25712a6422a6ca434cf92dc07eb
[people/sha0/winvblock.git] / src / winvblock / driver.c
1 /**
2  * Copyright (C) 2009-2011, 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 "portable.h"
33 #include "winvblock.h"
34 #include "wv_stdlib.h"
35 #include "wv_string.h"
36 #include "irp.h"
37 #include "driver.h"
38 #include "bus.h"
39 #include "device.h"
40 #include "disk.h"
41 #include "registry.h"
42 #include "mount.h"
43 #include "filedisk.h"
44 #include "ramdisk.h"
45 #include "debug.h"
46
47 /* From bus.c */
48 extern WVL_S_BUS_T WvBus;
49 extern WV_S_DEV_T WvBusDev;
50 extern UNICODE_STRING WvBusName;
51 extern UNICODE_STRING WvBusDosname;
52 extern PETHREAD WvBusThread;
53 extern WV_F_DEV_DISPATCH WvBusSysCtl;
54 extern WV_F_DEV_CTL WvBusDevCtl;
55 extern WV_F_DEV_DISPATCH WvBusPower;
56 extern WV_F_DEV_PNP WvBusPnp;
57 extern WVL_F_BUS_PNP WvBusPnpQueryDevText;
58 extern NTSTATUS STDCALL WvBusEstablish(IN PUNICODE_STRING);
59
60 /* Exported. */
61 PDRIVER_OBJECT WvDriverObj = NULL;
62
63 /* Globals. */
64 static PVOID WvDriverStateHandle;
65 static BOOLEAN WvDriverStarted = FALSE;
66 /* Contains TXTSETUP.SIF/BOOT.INI-style OsLoadOptions parameters. */
67 static LPWSTR WvOsLoadOpts = NULL;
68
69 /* Forward declarations. */
70 static DRIVER_DISPATCH WvIrpNotSupported;
71 static __drv_dispatchType(IRP_MJ_POWER) DRIVER_DISPATCH WvIrpPower;
72 static
73   __drv_dispatchType(IRP_MJ_CREATE)
74   __drv_dispatchType(IRP_MJ_CLOSE)
75   DRIVER_DISPATCH WvIrpCreateClose;
76 static __drv_dispatchType(IRP_MJ_SYSTEM_CONTROL)
77   DRIVER_DISPATCH WvIrpSysCtl;
78 static __drv_dispatchType(IRP_MJ_DEVICE_CONTROL)
79   DRIVER_DISPATCH WvIrpDevCtl;
80 static __drv_dispatchType(IRP_MJ_SCSI) DRIVER_DISPATCH WvIrpScsi;
81 static __drv_dispatchType(IRP_MJ_PNP) DRIVER_DISPATCH WvIrpPnp;
82 static DRIVER_UNLOAD WvUnload;
83
84 static LPWSTR STDCALL WvGetOpt(IN LPWSTR opt_name) {
85     LPWSTR our_opts, the_opt;
86     WCHAR our_sig[] = L"WINVBLOCK=";
87     /* To produce constant integer expressions. */
88     enum {
89         our_sig_len_bytes = sizeof ( our_sig ) - sizeof ( WCHAR ),
90         our_sig_len = our_sig_len_bytes / sizeof ( WCHAR )
91       };
92     size_t opt_name_len, opt_name_len_bytes;
93
94     if (!WvOsLoadOpts || !opt_name)
95       return NULL;
96
97     /* Find /WINVBLOCK= options. */
98     our_opts = WvOsLoadOpts;
99     while (*our_opts != L'\0') {
100         if (!wv_memcmpeq(our_opts, our_sig, our_sig_len_bytes)) {
101             our_opts++;
102             continue;
103           }
104         our_opts += our_sig_len;
105         break;
106       }
107
108     /* Search for the specific option. */
109     the_opt = our_opts;
110     opt_name_len = wcslen(opt_name);
111     opt_name_len_bytes = opt_name_len * sizeof (WCHAR);
112     while (*the_opt != L'\0' && *the_opt != L' ') {
113         if (!wv_memcmpeq(the_opt, opt_name, opt_name_len_bytes)) {
114             while (*the_opt != L'\0' && *the_opt != L' ' && *the_opt != L',')
115               the_opt++;
116             continue;
117           }
118         the_opt += opt_name_len;
119         break;
120       }
121
122     if (*the_opt == L'\0' || *the_opt == L' ')
123       return NULL;
124
125     /* Next should come "=". */
126     if (*the_opt != L'=')
127       return NULL;
128
129     /*
130      * And finally our option's value.  The caller needs
131      * to worry about looking past the end of the option.
132      */
133     the_opt++;
134     if (*the_opt == L'\0' || *the_opt == L' ')
135       return NULL;
136     return the_opt;
137   }
138
139 static NTSTATUS STDCALL WvAttachFdo(
140     IN PDRIVER_OBJECT DriverObject,
141     IN PDEVICE_OBJECT Pdo
142   ) {
143     NTSTATUS status;
144     PLIST_ENTRY walker;
145     PDEVICE_OBJECT fdo = NULL;
146     static WV_S_DEV_IRP_MJ irp_mj = {
147         WvBusPower,
148         WvBusSysCtl,
149         WvBusDevCtl,
150         (WV_FP_DEV_SCSI) 0,
151         WvBusPnp,
152       };
153
154     DBG("Entry\n");
155     /* Do we alreay have our main bus? */
156     if (WvBus.Fdo) {
157         DBG("Already have the main bus.  Refusing.\n");
158         status = STATUS_NOT_SUPPORTED;
159         goto err_already_established;
160       }
161     /* Initialize the bus. */
162     WvlBusInit(&WvBus);
163     WvDevInit(&WvBusDev);
164     /* Create the bus FDO. */
165     status = IoCreateDevice(
166         DriverObject,
167         sizeof (WV_S_DEV_EXT),
168         &WvBusName,
169         FILE_DEVICE_CONTROLLER,
170         FILE_DEVICE_SECURE_OPEN,
171         FALSE,
172         &fdo
173       );
174     if (!NT_SUCCESS(status)) {
175         DBG("IoCreateDevice() failed!\n");
176         goto err_fdo;
177       }
178     /* DosDevice symlink. */
179     status = IoCreateSymbolicLink(
180         &WvBusDosname,
181         &WvBusName
182       );
183     if (!NT_SUCCESS(status)) {
184         DBG("IoCreateSymbolicLink() failed!\n");
185         goto err_dos_symlink;
186       }
187     /* Set associations for the bus, device, FDO, PDO. */
188     WvDevForDevObj(fdo, &WvBusDev);
189     WvBusDev.Self = WvBus.Fdo = fdo;
190     WvBusDev.IsBus = TRUE;
191     WvBusDev.IrpMj = &irp_mj;
192     WvBus.QueryDevText = WvBusPnpQueryDevText;
193     WvBus.Pdo = Pdo;
194     fdo->Flags |= DO_DIRECT_IO;         /* FIXME? */
195     fdo->Flags |= DO_POWER_INRUSH;      /* FIXME? */
196     /* Attach the FDO to the PDO. */
197     WvBus.LowerDeviceObject = IoAttachDeviceToDeviceStack(
198         fdo,
199         Pdo
200       );
201     if (WvBus.LowerDeviceObject == NULL) {
202         status = STATUS_NO_SUCH_DEVICE;
203         DBG("IoAttachDeviceToDeviceStack() failed!\n");
204         goto err_attach;
205       }
206     status = WvlBusStartThread(&WvBus, &WvBusThread);
207     if (!NT_SUCCESS(status)) {
208         DBG("Couldn't start bus thread!\n");
209         goto err_thread;
210       }
211     /* Ok! */
212     fdo->Flags &= ~DO_DEVICE_INITIALIZING;
213     #ifdef RIS
214     WvBus.Dev.State = Started;
215     #endif
216     DBG("Exit\n");
217     return STATUS_SUCCESS;
218
219     err_thread:
220
221     err_attach:
222
223     IoDeleteSymbolicLink(&WvBusDosname);
224     err_dos_symlink:
225
226     IoDeleteDevice(fdo);
227     err_fdo:
228
229     err_already_established:
230
231     DBG("Exit with failure\n");
232     return status;
233   }
234
235 /*
236  * Note the exception to the function naming convention.
237  * TODO: See if a Makefile change is good enough.
238  */
239 NTSTATUS STDCALL DriverEntry(
240     IN PDRIVER_OBJECT DriverObject,
241     IN PUNICODE_STRING RegistryPath
242   ) {
243     NTSTATUS status;
244     int i;
245
246     DBG("Entry\n");
247     if (WvDriverObj) {
248         DBG("Re-entry not allowed!\n");
249         return STATUS_NOT_SUPPORTED;
250       }
251     WvDriverObj = DriverObject;
252     if (WvDriverStarted)
253       return STATUS_SUCCESS;
254     Debug_Initialize();
255     status = WvlRegNoteOsLoadOpts(&WvOsLoadOpts);
256     if (!NT_SUCCESS(status))
257       return WvlError("WvlRegNoteOsLoadOpts", status);
258
259     WvDriverStateHandle = NULL;
260
261     if ((WvDriverStateHandle = PoRegisterSystemState(
262         NULL,
263         ES_CONTINUOUS
264       )) == NULL) {
265         DBG("Could not set system state to ES_CONTINUOUS!!\n");
266       }
267     /*
268      * Set up IRP MajorFunction function table for devices
269      * this driver handles.
270      */
271     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
272       DriverObject->MajorFunction[i] = WvIrpNotSupported;
273     DriverObject->MajorFunction[IRP_MJ_PNP] = WvIrpPnp;
274     DriverObject->MajorFunction[IRP_MJ_POWER] = WvIrpPower;
275     DriverObject->MajorFunction[IRP_MJ_CREATE] = WvIrpCreateClose;
276     DriverObject->MajorFunction[IRP_MJ_CLOSE] = WvIrpCreateClose;
277     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
278       WvIrpSysCtl;
279     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
280       WvIrpDevCtl;
281     DriverObject->MajorFunction[IRP_MJ_SCSI] = WvIrpScsi;
282     /* Set the driver Unload callback. */
283     DriverObject->DriverUnload = WvUnload;
284     /* Set the driver AddDevice callback. */
285     DriverObject->DriverExtension->AddDevice = WvAttachFdo;
286     /* Initialize various modules. */
287     disk__module_init();        /* TODO: Check for error. */
288     filedisk__module_init();    /* TODO: Check for error. */
289     ramdisk__module_init();     /* TODO: Check for error. */
290
291     /* Establish the bus PDO. */
292     status = WvBusEstablish(RegistryPath);
293     if(!NT_SUCCESS(status))
294       goto err_bus;
295
296     WvDriverStarted = TRUE;
297     DBG("Exit\n");
298     return STATUS_SUCCESS;
299
300     err_bus:
301
302     WvUnload(DriverObject);
303     DBG("Exit due to failure\n");
304     return status;
305   }
306
307 static NTSTATUS STDCALL WvIrpNotSupported(
308     IN PDEVICE_OBJECT dev_obj,
309     IN PIRP irp
310   ) {
311     irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
312     IoCompleteRequest(irp, IO_NO_INCREMENT);
313     return irp->IoStatus.Status;
314   }
315
316 /* Handle a power IRP. */
317 static NTSTATUS WvIrpPower(
318     IN PDEVICE_OBJECT dev_obj,
319     IN PIRP irp
320   ) {
321     /* WvDevFromDevObj() checks for a NULL dev_obj */
322     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
323
324     #ifdef DEBUGIRPS
325     Debug_IrpStart(dev_obj, irp);
326     #endif
327     /* Check that the device exists. */
328     if (!dev || dev->State == WvDevStateDeleted) {
329         /* Even if it doesn't, a power IRP is important! */
330         PoStartNextPowerIrp(irp);
331         return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
332       }
333     /* Call the particular device's power handler. */
334     if (dev->IrpMj && dev->IrpMj->Power)
335       return dev->IrpMj->Power(dev, irp);
336     /* Otherwise, we don't support the IRP. */
337     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
338   }
339
340 /* Handle an IRP_MJ_CREATE or IRP_MJ_CLOSE IRP. */
341 static NTSTATUS WvIrpCreateClose(
342     IN PDEVICE_OBJECT dev_obj,
343     IN PIRP irp
344   ) {
345     /* WvDevFromDevObj() checks for a NULL dev_obj */
346     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
347
348     #ifdef DEBUGIRPS
349     Debug_IrpStart(dev_obj, irp);
350     #endif
351     /* Check that the device exists. */
352     if (!dev || dev->State == WvDevStateDeleted)
353       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
354     /* Always succeed with nothing to do. */
355     return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
356   }
357
358 /* Handle an IRP_MJ_SYSTEM_CONTROL IRP. */
359 static NTSTATUS WvIrpSysCtl(
360     IN PDEVICE_OBJECT dev_obj,
361     IN PIRP irp
362   ) {
363     /* WvDevFromDevObj() checks for a NULL dev_obj */
364     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
365
366     #ifdef DEBUGIRPS
367     Debug_IrpStart(dev_obj, irp);
368     #endif
369     /* Check that the device exists. */
370     if (!dev || dev->State == WvDevStateDeleted)
371       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
372     /* Call the particular device's power handler. */
373     if (dev->IrpMj && dev->IrpMj->SysCtl)
374       return dev->IrpMj->SysCtl(dev, irp);
375     /* Otherwise, we don't support the IRP. */
376     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
377   }
378
379 /* Handle an IRP_MJ_DEVICE_CONTROL IRP. */
380 static NTSTATUS WvIrpDevCtl(
381     IN PDEVICE_OBJECT dev_obj,
382     IN PIRP irp
383   ) {
384     /* WvDevFromDevObj() checks for a NULL dev_obj */
385     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
386     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
387
388     #ifdef DEBUGIRPS
389     Debug_IrpStart(dev_obj, irp);
390     #endif
391     /* Check that the device exists. */
392     if (!dev || dev->State == WvDevStateDeleted)
393       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
394     /* Call the particular device's power handler. */
395     if (dev->IrpMj && dev->IrpMj->DevCtl) {
396         return dev->IrpMj->DevCtl(
397             dev,
398             irp,
399             io_stack_loc->Parameters.DeviceIoControl.IoControlCode
400           );
401       }
402     /* Otherwise, we don't support the IRP. */
403     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
404   }
405
406 /* Handle an IRP_MJ_SCSI IRP. */
407 static NTSTATUS WvIrpScsi(
408     IN PDEVICE_OBJECT dev_obj,
409     IN PIRP irp
410   ) {
411     /* WvDevFromDevObj() checks for a NULL dev_obj */
412     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
413     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
414
415     #ifdef DEBUGIRPS
416     Debug_IrpStart(dev_obj, irp);
417     #endif
418     /* Check that the device exists. */
419     if (!dev || dev->State == WvDevStateDeleted)
420       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
421     /* Call the particular device's power handler. */
422     if (dev->IrpMj && dev->IrpMj->Scsi) {
423         return dev->IrpMj->Scsi(
424             dev,
425             irp,
426             io_stack_loc->Parameters.Scsi.Srb->Function
427           );
428       }
429     /* Otherwise, we don't support the IRP. */
430     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
431   }
432
433 /* Handle an IRP_MJ_PNP IRP. */
434 static NTSTATUS WvIrpPnp(
435     IN PDEVICE_OBJECT dev_obj,
436     IN PIRP irp
437   ) {
438     /* WvDevFromDevObj() checks for a NULL dev_obj */
439     WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
440     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
441     NTSTATUS status;
442
443     #ifdef DEBUGIRPS
444     Debug_IrpStart(dev_obj, irp);
445     #endif
446     /* Check that the device exists. */
447     if (!dev || dev->State == WvDevStateDeleted)
448       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
449     /* Call the particular device's power handler. */
450     if (dev->IrpMj && dev->IrpMj->Pnp) {
451         status = dev->IrpMj->Pnp(
452             dev,
453             irp,
454             io_stack_loc->MinorFunction
455           );
456         if (dev->IsBus) {
457             dev->OldState = WvBus.OldState;
458             dev->State = WvBus.State;
459           }
460         return status;
461       }
462     /* Otherwise, we don't support the IRP. */
463     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
464   }
465
466 static VOID STDCALL WvUnload(IN PDRIVER_OBJECT DriverObject) {
467     DBG("Unloading...\n");
468     WvBus.Stop = TRUE;
469     KeSetEvent(&WvBus.ThreadSignal, 0, FALSE);
470     if (WvBusThread) {
471         KeWaitForSingleObject(
472             WvBusThread,
473             Executive,
474             KernelMode,
475             FALSE,
476             NULL
477           );
478         ObDereferenceObject(WvBusThread);
479       }
480     if (WvDriverStateHandle != NULL)
481       PoUnregisterSystemState(WvDriverStateHandle);
482     IoDeleteSymbolicLink(&WvBusDosname);
483     WvBus.Fdo = NULL;
484     wv_free(WvOsLoadOpts);
485     WvDriverStarted = FALSE;
486     DBG("Done\n");
487   }
488
489 NTSTATUS STDCALL WvDriverGetDevCapabilities(
490     IN PDEVICE_OBJECT DevObj,
491     IN PDEVICE_CAPABILITIES DevCapabilities
492   ) {
493     IO_STATUS_BLOCK io_status;
494     KEVENT pnp_event;
495     NTSTATUS status;
496     PDEVICE_OBJECT target_obj;
497     PIO_STACK_LOCATION io_stack_loc;
498     PIRP pnp_irp;
499
500     RtlZeroMemory(DevCapabilities, sizeof *DevCapabilities);
501     DevCapabilities->Size = sizeof *DevCapabilities;
502     DevCapabilities->Version = 1;
503     DevCapabilities->Address = -1;
504     DevCapabilities->UINumber = -1;
505
506     KeInitializeEvent(&pnp_event, NotificationEvent, FALSE);
507     target_obj = IoGetAttachedDeviceReference(DevObj);
508     pnp_irp = IoBuildSynchronousFsdRequest(
509         IRP_MJ_PNP,
510         target_obj,
511         NULL,
512         0,
513         NULL,
514         &pnp_event,
515         &io_status
516       );
517     if (pnp_irp == NULL) {
518         status = STATUS_INSUFFICIENT_RESOURCES;
519       } else {
520         pnp_irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
521         io_stack_loc = IoGetNextIrpStackLocation(pnp_irp);
522         RtlZeroMemory(io_stack_loc, sizeof *io_stack_loc);
523         io_stack_loc->MajorFunction = IRP_MJ_PNP;
524         io_stack_loc->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
525         io_stack_loc->Parameters.DeviceCapabilities.Capabilities =
526           DevCapabilities;
527         status = IoCallDriver(target_obj, pnp_irp);
528         if (status == STATUS_PENDING) {
529             KeWaitForSingleObject(
530                 &pnp_event,
531                 Executive,
532                 KernelMode,
533                 FALSE,
534                 NULL
535               );
536             status = io_status.Status;
537           }
538       }
539     ObDereferenceObject(target_obj);
540     return status;
541   }