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/
6 * This file is part of WinVBlock, derived from WinAoE.
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.
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.
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/>.
32 #include "winvblock.h"
33 #include "wv_stdlib.h"
34 #include "wv_string.h"
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)
51 PDRIVER_OBJECT WvDriverObj = NULL;
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_,
63 static UNICODE_STRING WvDriverBusDosname_ = {
64 sizeof WV_M_BUS_DOSNAME_,
65 sizeof WV_M_BUS_DOSNAME_,
69 static WV_S_BUS_T WvDriverBus_ = {0};
70 /* Contains TXTSETUP.SIF/BOOT.INI-style OsLoadOptions parameters. */
71 static LPWSTR WvDriverOsLoadOpts_ = NULL;
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_CTL WvDriverBusDevCtl_;
84 static WV_F_DEV_DISPATCH WvDriverBusPower_;
85 static WV_F_DEV_PNP WvDriverBusPnp_;
87 static LPWSTR STDCALL get_opt(IN LPWSTR opt_name) {
88 LPWSTR our_opts, the_opt;
89 WCHAR our_sig[] = L"WINVBLOCK=";
90 /* To produce constant integer expressions. */
92 our_sig_len_bytes = sizeof ( our_sig ) - sizeof ( WCHAR ),
93 our_sig_len = our_sig_len_bytes / sizeof ( WCHAR )
95 size_t opt_name_len, opt_name_len_bytes;
97 if (!WvDriverOsLoadOpts_ || !opt_name)
100 /* Find /WINVBLOCK= options. */
101 our_opts = WvDriverOsLoadOpts_;
102 while (*our_opts != L'\0') {
103 if (!wv_memcmpeq(our_opts, our_sig, our_sig_len_bytes)) {
107 our_opts += our_sig_len;
111 /* Search for the specific option. */
113 opt_name_len = wcslen(opt_name);
114 opt_name_len_bytes = opt_name_len * sizeof (WCHAR);
115 while (*the_opt != L'\0' && *the_opt != L' ') {
116 if (!wv_memcmpeq(the_opt, opt_name, opt_name_len_bytes)) {
117 while (*the_opt != L'\0' && *the_opt != L' ' && *the_opt != L',')
121 the_opt += opt_name_len;
125 if (*the_opt == L'\0' || *the_opt == L' ')
128 /* Next should come "=". */
129 if (*the_opt != L'=')
133 * And finally our option's value. The caller needs
134 * to worry about looking past the end of the option.
137 if (*the_opt == L'\0' || *the_opt == L' ')
142 static NTSTATUS STDCALL driver__attach_fdo_(
143 IN PDRIVER_OBJECT DriverObject,
144 IN PDEVICE_OBJECT PhysicalDeviceObject
149 PUNICODE_STRING dev_name = NULL;
150 PDEVICE_OBJECT fdo = NULL;
153 /* Do we alreay have our main bus? */
154 if (WvDriverBusFdo_) {
155 DBG("Already have the main bus. Refusing.\n");
156 status = STATUS_NOT_SUPPORTED;
157 goto err_already_established;
159 /* Initialize the bus. */
160 WvBusInit(&WvDriverBus_);
161 /* Create the bus FDO. */
162 status = IoCreateDevice(
164 sizeof (driver__dev_ext),
166 FILE_DEVICE_CONTROLLER,
167 FILE_DEVICE_SECURE_OPEN,
171 if (!NT_SUCCESS(status)) {
172 DBG("IoCreateDevice() failed!\n");
175 /* DosDevice symlink. */
176 status = IoCreateSymbolicLink(
177 &WvDriverBusDosname_,
180 if (!NT_SUCCESS(status)) {
181 DBG("IoCreateSymbolicLink() failed!\n");
182 goto err_dos_symlink;
184 /* Set associations for the bus, device, FDO, PDO. */
185 WvDevForDevObj(fdo, &WvDriverBus_.Dev);
186 WvDriverBus_.Dev.Self = WvDriverBus_.Fdo = fdo;
187 WvDriverBus_.Dev.IsBus = TRUE;
188 WvDriverBus_.Dev.IrpMj->SysCtl = WvDriverBusSysCtl_;
189 WvDriverBus_.Dev.IrpMj->DevCtl = WvDriverBusDevCtl_;
190 WvDriverBus_.Dev.IrpMj->Power = WvDriverBusPower_;
191 WvDriverBus_.Dev.IrpMj->Pnp = WvDriverBusPnp_;
192 WvDriverBus_.PhysicalDeviceObject = PhysicalDeviceObject;
193 fdo->Flags |= DO_DIRECT_IO; /* FIXME? */
194 fdo->Flags |= DO_POWER_INRUSH; /* FIXME? */
195 /* Attach the FDO to the PDO. */
196 WvDriverBus_.LowerDeviceObject = IoAttachDeviceToDeviceStack(
200 if (WvDriverBus_.LowerDeviceObject == NULL) {
201 status = STATUS_NO_SUCH_DEVICE;
202 DBG("IoAttachDeviceToDeviceStack() failed!\n");
205 status = WvBusStartThread(&WvDriverBus_);
206 if (!NT_SUCCESS(status)) {
207 DBG("Couldn't start bus thread!\n");
211 KeAcquireSpinLock(&WvDriverBusFdoLock_, &irql);
212 if (WvDriverBusFdo_) {
213 KeReleaseSpinLock(&WvDriverBusFdoLock_, irql);
214 DBG("Beaten to it!\n");
215 status = STATUS_NOT_SUPPORTED;
216 goto err_race_failed;
218 fdo->Flags &= ~DO_DEVICE_INITIALIZING;
220 WvDriverBus_.Dev.State = Started;
222 WvDriverBusFdo_ = fdo;
223 KeReleaseSpinLock(&WvDriverBusFdoLock_, irql);
225 return STATUS_SUCCESS;
233 IoDeleteSymbolicLink(&WvDriverBusDosname_);
239 err_already_established:
241 DBG("Exit with failure\n");
245 /* Create the root-enumerated, main bus device. */
246 NTSTATUS STDCALL driver__create_bus_(void) {
249 PDEVICE_OBJECT bus_pdo = NULL;
251 /* Create the PDO. */
252 IoReportDetectedDevice(
254 InterfaceTypeUndefined,
262 if (bus_pdo == NULL) {
263 DBG("IoReportDetectedDevice() went wrong! Exiting.\n");
264 status = STATUS_UNSUCCESSFUL;
267 /* Attach FDO to PDO. */
268 status = driver__attach_fdo_(WvDriverObj, bus_pdo);
269 if (!NT_SUCCESS(status)) {
270 DBG("driver__attach_fdo_() went wrong!\n");
273 /* PDO created, FDO attached. All done. */
274 return STATUS_SUCCESS;
278 IoDeleteDevice(bus_pdo);
285 * Note the exception to the function naming convention.
286 * TODO: See if a Makefile change is good enough.
288 NTSTATUS STDCALL DriverEntry(
289 IN PDRIVER_OBJECT DriverObject,
290 IN PUNICODE_STRING RegistryPath
297 DBG("Re-entry not allowed!\n");
298 return STATUS_NOT_SUPPORTED;
300 WvDriverObj = DriverObject;
301 if (WvDriverStarted_)
302 return STATUS_SUCCESS;
304 status = registry__note_os_load_opts(&WvDriverOsLoadOpts_);
305 if (!NT_SUCCESS(status))
306 return Error("registry__note_driver__os_load_opts", status);
308 WvDriverStateHandle_ = NULL;
309 KeInitializeSpinLock(&WvDriverBusFdoLock_);
311 if ((WvDriverStateHandle_ = PoRegisterSystemState(
315 DBG("Could not set system state to ES_CONTINUOUS!!\n");
318 * Set up IRP MajorFunction function table for devices
319 * this driver handles.
321 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
322 DriverObject->MajorFunction[i] = driver__dispatch_not_supported_;
323 DriverObject->MajorFunction[IRP_MJ_PNP] = driver__dispatch_pnp_;
324 DriverObject->MajorFunction[IRP_MJ_POWER] = driver__dispatch_power_;
325 DriverObject->MajorFunction[IRP_MJ_CREATE] = driver__dispatch_create_close_;
326 DriverObject->MajorFunction[IRP_MJ_CLOSE] = driver__dispatch_create_close_;
327 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
328 driver__dispatch_sys_ctl_;
329 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
330 driver__dispatch_dev_ctl_;
331 DriverObject->MajorFunction[IRP_MJ_SCSI] = driver__dispatch_scsi_;
332 /* Set the driver Unload callback. */
333 DriverObject->DriverUnload = driver__unload_;
334 /* Set the driver AddDevice callback. */
335 DriverObject->DriverExtension->AddDevice = driver__attach_fdo_;
336 /* Initialize various modules. */
337 disk__module_init(); /* TODO: Check for error. */
338 filedisk__module_init(); /* TODO: Check for error. */
339 ramdisk__module_init(); /* TODO: Check for error. */
342 * Always create the root-enumerated, main bus device.
343 * This is required in order to boot from a WinVBlock disk.
345 status = driver__create_bus_();
346 if(!NT_SUCCESS(status))
349 WvDriverStarted_ = TRUE;
351 return STATUS_SUCCESS;
355 driver__unload_(DriverObject);
356 DBG("Exit due to failure\n");
360 static NTSTATUS STDCALL driver__dispatch_not_supported_(
361 IN PDEVICE_OBJECT dev_obj,
364 irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
365 IoCompleteRequest(irp, IO_NO_INCREMENT);
366 return irp->IoStatus.Status;
370 * Common IRP completion routine.
372 * @v irp Points to the IRP to complete.
373 * @v info Number of bytes returned for the IRP, or 0.
374 * @v status Status for the IRP to complete.
375 * @ret NTSTATUS Returns the status value, as passed.
377 winvblock__lib_func NTSTATUS STDCALL driver__complete_irp(
382 irp->IoStatus.Information = info;
383 irp->IoStatus.Status = status;
384 IoCompleteRequest(irp, IO_NO_INCREMENT);
386 Debug_IrpEnd(irp, status);
391 /* Handle a power IRP. */
392 static NTSTATUS driver__dispatch_power_(
393 IN PDEVICE_OBJECT dev_obj,
396 /* WvDevFromDevObj() checks for a NULL dev_obj */
397 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
400 Debug_IrpStart(dev_obj, irp);
402 /* Check that the device exists. */
403 if (!dev || dev->State == WvDevStateDeleted) {
404 /* Even if it doesn't, a power IRP is important! */
405 PoStartNextPowerIrp(irp);
406 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
408 /* Call the particular device's power handler. */
409 if (dev->IrpMj && dev->IrpMj->Power)
410 return dev->IrpMj->Power(dev, irp);
411 /* Otherwise, we don't support the IRP. */
412 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
415 /* Handle an IRP_MJ_CREATE or IRP_MJ_CLOSE IRP. */
416 static NTSTATUS driver__dispatch_create_close_(
417 IN PDEVICE_OBJECT dev_obj,
420 /* WvDevFromDevObj() checks for a NULL dev_obj */
421 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
424 Debug_IrpStart(dev_obj, irp);
426 /* Check that the device exists. */
427 if (!dev || dev->State == WvDevStateDeleted)
428 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
429 /* Always succeed with nothing to do. */
430 return driver__complete_irp(irp, 0, STATUS_SUCCESS);
433 /* Handle an IRP_MJ_SYSTEM_CONTROL IRP. */
434 static NTSTATUS driver__dispatch_sys_ctl_(
435 IN PDEVICE_OBJECT dev_obj,
438 /* WvDevFromDevObj() checks for a NULL dev_obj */
439 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
442 Debug_IrpStart(dev_obj, irp);
444 /* Check that the device exists. */
445 if (!dev || dev->State == WvDevStateDeleted)
446 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
447 /* Call the particular device's power handler. */
448 if (dev->IrpMj && dev->IrpMj->SysCtl)
449 return dev->IrpMj->SysCtl(dev, irp);
450 /* Otherwise, we don't support the IRP. */
451 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
454 /* Handle an IRP_MJ_DEVICE_CONTROL IRP. */
455 static NTSTATUS driver__dispatch_dev_ctl_(
456 IN PDEVICE_OBJECT dev_obj,
459 /* WvDevFromDevObj() checks for a NULL dev_obj */
460 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
461 PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
464 Debug_IrpStart(dev_obj, irp);
466 /* Check that the device exists. */
467 if (!dev || dev->State == WvDevStateDeleted)
468 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
469 /* Call the particular device's power handler. */
470 if (dev->IrpMj && dev->IrpMj->DevCtl) {
471 return dev->IrpMj->DevCtl(
474 io_stack_loc->Parameters.DeviceIoControl.IoControlCode
477 /* Otherwise, we don't support the IRP. */
478 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
481 /* Handle an IRP_MJ_SCSI IRP. */
482 static NTSTATUS driver__dispatch_scsi_(
483 IN PDEVICE_OBJECT dev_obj,
486 /* WvDevFromDevObj() checks for a NULL dev_obj */
487 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
488 PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
491 Debug_IrpStart(dev_obj, irp);
493 /* Check that the device exists. */
494 if (!dev || dev->State == WvDevStateDeleted)
495 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
496 /* Call the particular device's power handler. */
497 if (dev->IrpMj && dev->IrpMj->Scsi) {
498 return dev->IrpMj->Scsi(
501 io_stack_loc->Parameters.Scsi.Srb->Function
504 /* Otherwise, we don't support the IRP. */
505 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
508 /* Handle an IRP_MJ_PNP IRP. */
509 static NTSTATUS driver__dispatch_pnp_(
510 IN PDEVICE_OBJECT dev_obj,
513 /* WvDevFromDevObj() checks for a NULL dev_obj */
514 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
515 PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
518 Debug_IrpStart(dev_obj, irp);
520 /* Check that the device exists. */
521 if (!dev || dev->State == WvDevStateDeleted)
522 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
523 /* Call the particular device's power handler. */
524 if (dev->IrpMj && dev->IrpMj->Pnp) {
525 return dev->IrpMj->Pnp(
528 io_stack_loc->MinorFunction
531 /* Otherwise, we don't support the IRP. */
532 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
535 static void STDCALL driver__unload_(IN PDRIVER_OBJECT DriverObject) {
536 DBG("Unloading...\n");
537 if (WvDriverStateHandle_ != NULL)
538 PoUnregisterSystemState(WvDriverStateHandle_);
539 IoDeleteSymbolicLink(&WvDriverBusDosname_);
540 WvDriverBusFdo_ = NULL;
541 wv_free(WvDriverOsLoadOpts_);
542 WvDriverStarted_ = FALSE;
546 winvblock__lib_func void STDCALL WvDriverCompletePendingIrp(IN PIRP Irp) {
548 Debug_IrpEnd(Irp, Irp->IoStatus.Status);
550 IoCompleteRequest(Irp, IO_NO_INCREMENT);
553 /* Note the exception to the function naming convention. */
554 winvblock__lib_func NTSTATUS STDCALL Error(
558 DBG("%s: 0x%08x\n", Message, Status);
563 * Get a pointer to the driver bus device.
565 * @ret A pointer to the driver bus, or NULL.
567 winvblock__lib_func WV_SP_BUS_T driver__bus(void) {
568 if (!WvDriverBusFdo_) {
569 DBG("No driver bus device!\n");
572 return WvBusFromDev(WvDevFromDevObj(WvDriverBusFdo_));
575 /* Pass an IRP_MJ_SYSTEM_CONTROL IRP to the bus. */
576 static NTSTATUS STDCALL WvDriverBusSysCtl_(IN WV_SP_DEV_T dev, IN PIRP irp) {
577 WV_SP_BUS_T bus = WvBusFromDev(dev);
579 return WvBusSysCtl(bus, irp);
582 /* Pass a power IRP to the bus. */
583 static NTSTATUS STDCALL WvDriverBusPower_(IN WV_SP_DEV_T dev, IN PIRP irp) {
584 WV_SP_BUS_T bus = WvBusFromDev(dev);
586 return WvBusPower(bus, irp);
589 /* Pass an IRP_MJ_PNP to the bus. */
590 static NTSTATUS STDCALL WvDriverBusPnp_(
595 WV_SP_BUS_T bus = WvBusFromDev(dev);
597 return WvBusPnp(bus, irp, code);
601 * Add a child node to the bus.
603 * @v Dev Points to the child device to add.
604 * @ret TRUE for success, FALSE for failure.
606 winvblock__lib_func winvblock__bool STDCALL WvDriverBusAddDev(
607 IN OUT WV_SP_DEV_T Dev
609 /* The new node's device object. */
610 PDEVICE_OBJECT dev_obj;
613 if (!WvDriverBusFdo_ || !Dev) {
614 DBG("No bus or no device!\n");
617 /* Create the child device. */
618 dev_obj = WvDevCreatePdo(Dev);
620 DBG("PDO creation failed!\n");
623 /* Create a node. TODO: Put the node somewhere better. */
624 Dev->BusNode = wv_malloc(sizeof *(Dev->BusNode));
626 DBG("Couldn't allocate node storage!\n");
627 IoDeleteDevice(dev_obj);
630 WvBusInitNode(Dev->BusNode, dev_obj);
631 /* Associate the parent bus. */
632 Dev->Parent = WvDriverBus_.Dev.Self;
634 * Initialize the device. For disks, this routine is responsible for
635 * determining the disk's geometry appropriately for AoE/RAM/file disks.
638 dev_obj->Flags &= ~DO_DEVICE_INITIALIZING;
639 /* Add the new PDO device to the bus' list of children. */
640 WvBusAddNode(&WvDriverBus_, Dev->BusNode);
646 static NTSTATUS STDCALL WvDriverBusDevCtlDiskDetach_(
650 winvblock__uint8_ptr buffer = irp->AssociatedIrp.SystemBuffer;
651 winvblock__uint32 disk_num = *(winvblock__uint32_ptr) buffer;
652 WV_SP_DEV_T dev_walker;
653 WV_SP_DISK_T disk_walker = NULL, prev_disk_walker;
656 DBG("Request to detach disk: %d\n", disk_num);
657 bus = WvBusFromDev(dev);
658 dev_walker = bus->first_child;
659 if (dev_walker != NULL)
660 disk_walker = disk__get_ptr(dev_walker);
661 prev_disk_walker = disk_walker;
662 while ((disk_walker != NULL) && (dev_walker->DevNum != disk_num)) {
663 prev_disk_walker = disk_walker;
664 dev_walker = dev_walker->next_sibling_ptr;
665 if (dev_walker != NULL)
666 disk_walker = disk__get_ptr(dev_walker);
668 if (disk_walker != NULL) {
669 if (disk_walker->Dev->Boot) {
670 DBG("Cannot unmount a boot drive.\n");
671 irp->IoStatus.Information = 0;
672 return STATUS_INVALID_DEVICE_REQUEST;
674 DBG("Deleting disk %d\n", dev_walker->DevNum);
675 if (disk_walker == disk__get_ptr(bus->first_child))
676 bus->first_child = dev_walker->next_sibling_ptr;
678 prev_disk_walker->Dev->next_sibling_ptr =
679 dev_walker->next_sibling_ptr;
681 disk_walker->Unmount = TRUE;
682 dev_walker->next_sibling_ptr = NULL;
683 if (bus->PhysicalDeviceObject != NULL)
684 IoInvalidateDeviceRelations(bus->PhysicalDeviceObject, BusRelations);
687 irp->IoStatus.Information = 0;
688 return STATUS_SUCCESS;
691 NTSTATUS STDCALL WvDriverBusDevCtl_(
694 IN ULONG POINTER_ALIGNMENT code
699 case IOCTL_FILE_ATTACH:
700 status = filedisk__attach(dev, irp);
703 case IOCTL_FILE_DETACH:
704 status = WvDriverBusDevCtlDiskDetach_(dev, irp);
708 irp->IoStatus.Information = 0;
709 status = STATUS_INVALID_DEVICE_REQUEST;
712 irp->IoStatus.Status = status;
713 IoCompleteRequest(irp, IO_NO_INCREMENT);