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_;
84 static LPWSTR STDCALL get_opt(IN LPWSTR opt_name) {
85 LPWSTR our_opts, the_opt;
86 WCHAR our_sig[] = L"WINVBLOCK=";
87 /* To produce constant integer expressions. */
89 our_sig_len_bytes = sizeof ( our_sig ) - sizeof ( WCHAR ),
90 our_sig_len = our_sig_len_bytes / sizeof ( WCHAR )
92 size_t opt_name_len, opt_name_len_bytes;
94 if (!WvDriverOsLoadOpts_ || !opt_name)
97 /* Find /WINVBLOCK= options. */
98 our_opts = WvDriverOsLoadOpts_;
99 while (*our_opts != L'\0') {
100 if (!wv_memcmpeq(our_opts, our_sig, our_sig_len_bytes)) {
104 our_opts += our_sig_len;
108 /* Search for the specific option. */
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',')
118 the_opt += opt_name_len;
122 if (*the_opt == L'\0' || *the_opt == L' ')
125 /* Next should come "=". */
126 if (*the_opt != L'=')
130 * And finally our option's value. The caller needs
131 * to worry about looking past the end of the option.
134 if (*the_opt == L'\0' || *the_opt == L' ')
139 static NTSTATUS STDCALL driver__attach_fdo_(
140 IN PDRIVER_OBJECT DriverObject,
141 IN PDEVICE_OBJECT PhysicalDeviceObject
146 PUNICODE_STRING dev_name = NULL;
147 PDEVICE_OBJECT fdo = NULL;
150 /* Do we alreay have our main bus? */
151 if (WvDriverBusFdo_) {
152 DBG("Already have the main bus. Refusing.\n");
153 status = STATUS_NOT_SUPPORTED;
154 goto err_already_established;
156 /* Initialize the bus. */
157 WvBusInit(&WvDriverBus_);
158 /* Create the bus FDO. */
159 status = IoCreateDevice(
161 sizeof (driver__dev_ext),
163 FILE_DEVICE_CONTROLLER,
164 FILE_DEVICE_SECURE_OPEN,
168 if (!NT_SUCCESS(status)) {
169 DBG("IoCreateDevice() failed!\n");
172 /* DosDevice symlink. */
173 status = IoCreateSymbolicLink(
174 &WvDriverBusDosname_,
177 if (!NT_SUCCESS(status)) {
178 DBG("IoCreateSymbolicLink() failed!\n");
179 goto err_dos_symlink;
181 /* Set associations for the bus, device, FDO, PDO. */
182 WvDevForDevObj(fdo, &WvDriverBus_.Dev);
183 WvDriverBus_.Dev.Self = WvDriverBus_.Fdo = fdo;
184 WvDriverBus_.Dev.IsBus = TRUE;
185 WvDriverBus_.Dev.IrpMj->SysCtl = WvDriverBusSysCtl_;
186 WvDriverBus_.PhysicalDeviceObject = PhysicalDeviceObject;
187 fdo->Flags |= DO_DIRECT_IO; /* FIXME? */
188 fdo->Flags |= DO_POWER_INRUSH; /* FIXME? */
189 /* Attach the FDO to the PDO. */
190 WvDriverBus_.LowerDeviceObject = IoAttachDeviceToDeviceStack(
194 if (WvDriverBus_.LowerDeviceObject == NULL) {
195 status = STATUS_NO_SUCH_DEVICE;
196 DBG("IoAttachDeviceToDeviceStack() failed!\n");
199 status = WvBusStartThread(&WvDriverBus_);
200 if (!NT_SUCCESS(status)) {
201 DBG("Couldn't start bus thread!\n");
205 KeAcquireSpinLock(&WvDriverBusFdoLock_, &irql);
206 if (WvDriverBusFdo_) {
207 KeReleaseSpinLock(&WvDriverBusFdoLock_, irql);
208 DBG("Beaten to it!\n");
209 status = STATUS_NOT_SUPPORTED;
210 goto err_race_failed;
212 fdo->Flags &= ~DO_DEVICE_INITIALIZING;
214 WvDriverBus_.Dev.State = Started;
216 WvDriverBusFdo_ = fdo;
217 KeReleaseSpinLock(&WvDriverBusFdoLock_, irql);
219 return STATUS_SUCCESS;
227 IoDeleteSymbolicLink(&WvDriverBusDosname_);
233 err_already_established:
235 DBG("Exit with failure\n");
239 /* Create the root-enumerated, main bus device. */
240 NTSTATUS STDCALL driver__create_bus_(void) {
243 PDEVICE_OBJECT bus_pdo = NULL;
245 /* Create the PDO. */
246 IoReportDetectedDevice(
248 InterfaceTypeUndefined,
256 if (bus_pdo == NULL) {
257 DBG("IoReportDetectedDevice() went wrong! Exiting.\n");
258 status = STATUS_UNSUCCESSFUL;
261 /* Attach FDO to PDO. */
262 status = driver__attach_fdo_(WvDriverObj, bus_pdo);
263 if (!NT_SUCCESS(status)) {
264 DBG("driver__attach_fdo_() went wrong!\n");
267 /* PDO created, FDO attached. All done. */
268 return STATUS_SUCCESS;
272 IoDeleteDevice(bus_pdo);
279 * Note the exception to the function naming convention.
280 * TODO: See if a Makefile change is good enough.
282 NTSTATUS STDCALL DriverEntry(
283 IN PDRIVER_OBJECT DriverObject,
284 IN PUNICODE_STRING RegistryPath
291 DBG("Re-entry not allowed!\n");
292 return STATUS_NOT_SUPPORTED;
294 WvDriverObj = DriverObject;
295 if (WvDriverStarted_)
296 return STATUS_SUCCESS;
298 status = registry__note_os_load_opts(&WvDriverOsLoadOpts_);
299 if (!NT_SUCCESS(status))
300 return Error("registry__note_driver__os_load_opts", status);
302 WvDriverStateHandle_ = NULL;
303 KeInitializeSpinLock(&WvDriverBusFdoLock_);
305 if ((WvDriverStateHandle_ = PoRegisterSystemState(
309 DBG("Could not set system state to ES_CONTINUOUS!!\n");
312 * Set up IRP MajorFunction function table for devices
313 * this driver handles.
315 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
316 DriverObject->MajorFunction[i] = driver__dispatch_not_supported_;
317 DriverObject->MajorFunction[IRP_MJ_PNP] = driver__dispatch_pnp_;
318 DriverObject->MajorFunction[IRP_MJ_POWER] = driver__dispatch_power_;
319 DriverObject->MajorFunction[IRP_MJ_CREATE] = driver__dispatch_create_close_;
320 DriverObject->MajorFunction[IRP_MJ_CLOSE] = driver__dispatch_create_close_;
321 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
322 driver__dispatch_sys_ctl_;
323 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
324 driver__dispatch_dev_ctl_;
325 DriverObject->MajorFunction[IRP_MJ_SCSI] = driver__dispatch_scsi_;
326 /* Set the driver Unload callback. */
327 DriverObject->DriverUnload = driver__unload_;
328 /* Set the driver AddDevice callback. */
329 DriverObject->DriverExtension->AddDevice = driver__attach_fdo_;
330 /* Initialize various modules. */
331 disk__module_init(); /* TODO: Check for error. */
332 filedisk__module_init(); /* TODO: Check for error. */
333 ramdisk__module_init(); /* TODO: Check for error. */
336 * Always create the root-enumerated, main bus device.
337 * This is required in order to boot from a WinVBlock disk.
339 status = driver__create_bus_();
340 if(!NT_SUCCESS(status))
343 WvDriverStarted_ = TRUE;
345 return STATUS_SUCCESS;
349 driver__unload_(DriverObject);
350 DBG("Exit due to failure\n");
354 static NTSTATUS STDCALL driver__dispatch_not_supported_(
355 IN PDEVICE_OBJECT dev_obj,
358 irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
359 IoCompleteRequest(irp, IO_NO_INCREMENT);
360 return irp->IoStatus.Status;
364 * Common IRP completion routine.
366 * @v irp Points to the IRP to complete.
367 * @v info Number of bytes returned for the IRP, or 0.
368 * @v status Status for the IRP to complete.
369 * @ret NTSTATUS Returns the status value, as passed.
371 winvblock__lib_func NTSTATUS STDCALL driver__complete_irp(
376 irp->IoStatus.Information = info;
377 irp->IoStatus.Status = status;
378 IoCompleteRequest(irp, IO_NO_INCREMENT);
380 Debug_IrpEnd(irp, status);
385 /* Handle a power IRP. */
386 static NTSTATUS driver__dispatch_power_(
387 IN PDEVICE_OBJECT dev_obj,
390 /* WvDevFromDevObj() checks for a NULL dev_obj */
391 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
394 Debug_IrpStart(dev_obj, irp);
396 /* Check that the device exists. */
397 if (!dev || dev->State == WvDevStateDeleted) {
398 /* Even if it doesn't, a power IRP is important! */
399 PoStartNextPowerIrp(irp);
400 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
402 /* Call the particular device's power handler. */
403 if (dev->IrpMj && dev->IrpMj->Power)
404 return dev->IrpMj->Power(dev, irp);
405 /* Otherwise, we don't support the IRP. */
406 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
409 /* Handle an IRP_MJ_CREATE or IRP_MJ_CLOSE IRP. */
410 static NTSTATUS driver__dispatch_create_close_(
411 IN PDEVICE_OBJECT dev_obj,
414 /* WvDevFromDevObj() checks for a NULL dev_obj */
415 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
418 Debug_IrpStart(dev_obj, irp);
420 /* Check that the device exists. */
421 if (!dev || dev->State == WvDevStateDeleted)
422 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
423 /* Always succeed with nothing to do. */
424 return driver__complete_irp(irp, 0, STATUS_SUCCESS);
427 /* Handle an IRP_MJ_SYSTEM_CONTROL IRP. */
428 static NTSTATUS driver__dispatch_sys_ctl_(
429 IN PDEVICE_OBJECT dev_obj,
432 /* WvDevFromDevObj() checks for a NULL dev_obj */
433 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
436 Debug_IrpStart(dev_obj, irp);
438 /* Check that the device exists. */
439 if (!dev || dev->State == WvDevStateDeleted)
440 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
441 /* Call the particular device's power handler. */
442 if (dev->IrpMj && dev->IrpMj->SysCtl)
443 return dev->IrpMj->SysCtl(dev, irp);
444 /* Otherwise, we don't support the IRP. */
445 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
448 /* Handle an IRP_MJ_DEVICE_CONTROL IRP. */
449 static NTSTATUS driver__dispatch_dev_ctl_(
450 IN PDEVICE_OBJECT dev_obj,
453 /* WvDevFromDevObj() checks for a NULL dev_obj */
454 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
455 PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
458 Debug_IrpStart(dev_obj, irp);
460 /* Check that the device exists. */
461 if (!dev || dev->State == WvDevStateDeleted)
462 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
463 /* Call the particular device's power handler. */
464 if (dev->IrpMj && dev->IrpMj->DevCtl) {
465 return dev->IrpMj->DevCtl(
468 io_stack_loc->Parameters.DeviceIoControl.IoControlCode
471 /* Otherwise, we don't support the IRP. */
472 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
475 /* Handle an IRP_MJ_SCSI IRP. */
476 static NTSTATUS driver__dispatch_scsi_(
477 IN PDEVICE_OBJECT dev_obj,
480 /* WvDevFromDevObj() checks for a NULL dev_obj */
481 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
482 PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
485 Debug_IrpStart(dev_obj, irp);
487 /* Check that the device exists. */
488 if (!dev || dev->State == WvDevStateDeleted)
489 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
490 /* Call the particular device's power handler. */
491 if (dev->IrpMj && dev->IrpMj->Scsi) {
492 return dev->IrpMj->Scsi(
495 io_stack_loc->Parameters.Scsi.Srb->Function
498 /* Otherwise, we don't support the IRP. */
499 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
502 /* Handle an IRP_MJ_PNP IRP. */
503 static NTSTATUS driver__dispatch_pnp_(
504 IN PDEVICE_OBJECT dev_obj,
507 /* WvDevFromDevObj() checks for a NULL dev_obj */
508 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
509 PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
512 Debug_IrpStart(dev_obj, irp);
514 /* Check that the device exists. */
515 if (!dev || dev->State == WvDevStateDeleted)
516 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
517 /* Call the particular device's power handler. */
518 if (dev->IrpMj && dev->IrpMj->Pnp) {
519 return dev->IrpMj->Pnp(
522 io_stack_loc->MinorFunction
525 /* Otherwise, we don't support the IRP. */
526 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
529 static void STDCALL driver__unload_(IN PDRIVER_OBJECT DriverObject) {
530 DBG("Unloading...\n");
531 if (WvDriverStateHandle_ != NULL)
532 PoUnregisterSystemState(WvDriverStateHandle_);
533 IoDeleteSymbolicLink(&WvDriverBusDosname_);
534 WvDriverBusFdo_ = NULL;
535 wv_free(WvDriverOsLoadOpts_);
536 WvDriverStarted_ = FALSE;
540 winvblock__lib_func void STDCALL WvDriverCompletePendingIrp(IN PIRP Irp) {
542 Debug_IrpEnd(Irp, Irp->IoStatus.Status);
544 IoCompleteRequest(Irp, IO_NO_INCREMENT);
547 /* Note the exception to the function naming convention. */
548 winvblock__lib_func NTSTATUS STDCALL Error(
552 DBG("%s: 0x%08x\n", Message, Status);
557 * Get a pointer to the driver bus device.
559 * @ret A pointer to the driver bus, or NULL.
561 winvblock__lib_func WV_SP_BUS_T driver__bus(void) {
562 if (!WvDriverBusFdo_) {
563 DBG("No driver bus device!\n");
566 return WvBusFromDev(WvDevFromDevObj(WvDriverBusFdo_));
569 /* Pass an IRP_MJ_SYSTEM_CONTROL IRP to the bus. */
570 static NTSTATUS STDCALL WvDriverBusSysCtl_(IN WV_SP_DEV_T dev, IN PIRP irp) {
571 WV_SP_BUS_T bus = WvBusFromDev(dev);
573 return WvBusSysCtl(bus, irp);