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