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