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 driver__obj_ptr = NULL;
54 static void * driver__state_handle_;
55 static winvblock__bool driver__started_ = FALSE;
56 static PDEVICE_OBJECT driver__bus_fdo_ = NULL;
57 static KSPIN_LOCK driver__bus_fdo_lock_;
58 static UNICODE_STRING WvBusName_ = {
59 sizeof WV_M_BUS_NAME_,
60 sizeof WV_M_BUS_NAME_,
63 static UNICODE_STRING WvBusDosname_ = {
64 sizeof WV_M_BUS_DOSNAME_,
65 sizeof WV_M_BUS_DOSNAME_,
68 /* Contains TXTSETUP.SIF/BOOT.INI-style OsLoadOptions parameters. */
69 static LPWSTR driver__os_load_opts_ = NULL;
71 /* Forward declarations. */
72 static driver__dispatch_func driver__dispatch_not_supported_;
73 static driver__dispatch_func driver__dispatch_power_;
74 static driver__dispatch_func driver__dispatch_create_close_;
75 static driver__dispatch_func driver__dispatch_sys_ctl_;
76 static driver__dispatch_func driver__dispatch_dev_ctl_;
77 static driver__dispatch_func driver__dispatch_scsi_;
78 static driver__dispatch_func driver__dispatch_pnp_;
79 static void STDCALL driver__unload_(IN PDRIVER_OBJECT);
81 static LPWSTR STDCALL get_opt(IN LPWSTR opt_name) {
82 LPWSTR our_opts, the_opt;
83 WCHAR our_sig[] = L"WINVBLOCK=";
84 /* To produce constant integer expressions. */
86 our_sig_len_bytes = sizeof ( our_sig ) - sizeof ( WCHAR ),
87 our_sig_len = our_sig_len_bytes / sizeof ( WCHAR )
89 size_t opt_name_len, opt_name_len_bytes;
91 if (!driver__os_load_opts_ || !opt_name)
94 /* Find /WINVBLOCK= options. */
95 our_opts = driver__os_load_opts_;
96 while (*our_opts != L'\0') {
97 if (!wv_memcmpeq(our_opts, our_sig, our_sig_len_bytes)) {
101 our_opts += our_sig_len;
105 /* Search for the specific option. */
107 opt_name_len = wcslen(opt_name);
108 opt_name_len_bytes = opt_name_len * sizeof (WCHAR);
109 while (*the_opt != L'\0' && *the_opt != L' ') {
110 if (!wv_memcmpeq(the_opt, opt_name, opt_name_len_bytes)) {
111 while (*the_opt != L'\0' && *the_opt != L' ' && *the_opt != L',')
115 the_opt += opt_name_len;
119 if (*the_opt == L'\0' || *the_opt == L' ')
122 /* Next should come "=". */
123 if (*the_opt != L'=')
127 * And finally our option's value. The caller needs
128 * to worry about looking past the end of the option.
131 if (*the_opt == L'\0' || *the_opt == L' ')
136 static NTSTATUS STDCALL driver__attach_fdo_(
137 IN PDRIVER_OBJECT DriverObject,
138 IN PDEVICE_OBJECT PhysicalDeviceObject
144 PUNICODE_STRING dev_name = NULL;
145 PDEVICE_OBJECT fdo = NULL;
148 /* Do we alreay have our main bus? */
149 if (driver__bus_fdo_) {
150 DBG("Already have the main bus. Refusing.\n");
151 status = STATUS_NOT_SUPPORTED;
152 goto err_already_established;
154 /* Create the bus. */
157 DBG("WvBusCreate() failed for the main bus.\n");
158 status = STATUS_INSUFFICIENT_RESOURCES;
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(
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, &bus->Dev);
186 bus->Dev.Self = bus->Fdo = fdo;
187 bus->Dev.IsBus = TRUE;
188 bus->PhysicalDeviceObject = PhysicalDeviceObject;
189 fdo->Flags |= DO_DIRECT_IO; /* FIXME? */
190 fdo->Flags |= DO_POWER_INRUSH; /* FIXME? */
191 /* Attach the FDO to the PDO. */
192 bus->LowerDeviceObject = IoAttachDeviceToDeviceStack(
196 if (bus->LowerDeviceObject == NULL) {
197 status = STATUS_NO_SUCH_DEVICE;
198 DBG("IoAttachDeviceToDeviceStack() failed!\n");
201 status = WvBusStartThread(bus);
202 if (!NT_SUCCESS(status)) {
203 DBG("Couldn't start bus thread!\n");
207 KeAcquireSpinLock(&driver__bus_fdo_lock_, &irql);
208 if (driver__bus_fdo_) {
209 KeReleaseSpinLock(&driver__bus_fdo_lock_, irql);
210 DBG("Beaten to it!\n");
211 status = STATUS_NOT_SUPPORTED;
212 goto err_race_failed;
214 fdo->Flags &= ~DO_DEVICE_INITIALIZING;
216 bus->device->State = Started;
218 driver__bus_fdo_ = fdo;
219 KeReleaseSpinLock(&driver__bus_fdo_lock_, irql);
221 return STATUS_SUCCESS;
229 IoDeleteSymbolicLink(&WvBusDosname_);
235 WvDevFree(&bus->Dev);
238 err_already_established:
240 DBG("Exit with failure\n");
244 /* Create the root-enumerated, main bus device. */
245 NTSTATUS STDCALL driver__create_bus_(void) {
248 PDEVICE_OBJECT bus_pdo = NULL;
250 /* Create the PDO. */
251 IoReportDetectedDevice(
253 InterfaceTypeUndefined,
261 if (bus_pdo == NULL) {
262 DBG("IoReportDetectedDevice() went wrong! Exiting.\n");
263 status = STATUS_UNSUCCESSFUL;
266 /* Attach FDO to PDO. */
267 status = driver__attach_fdo_(driver__obj_ptr, bus_pdo);
268 if (!NT_SUCCESS(status)) {
269 DBG("driver__attach_fdo_() went wrong!\n");
272 /* PDO created, FDO attached. All done. */
273 return STATUS_SUCCESS;
277 IoDeleteDevice(bus_pdo);
284 * Note the exception to the function naming convention.
285 * TODO: See if a Makefile change is good enough.
287 NTSTATUS STDCALL DriverEntry(
288 IN PDRIVER_OBJECT DriverObject,
289 IN PUNICODE_STRING RegistryPath
295 if (driver__obj_ptr) {
296 DBG("Re-entry not allowed!\n");
297 return STATUS_NOT_SUPPORTED;
299 driver__obj_ptr = DriverObject;
300 if (driver__started_)
301 return STATUS_SUCCESS;
303 status = registry__note_os_load_opts(&driver__os_load_opts_);
304 if (!NT_SUCCESS(status))
305 return Error("registry__note_driver__os_load_opts", status);
307 driver__state_handle_ = NULL;
308 KeInitializeSpinLock(&driver__bus_fdo_lock_);
310 if ((driver__state_handle_ = PoRegisterSystemState(
314 DBG("Could not set system state to ES_CONTINUOUS!!\n");
317 * Set up IRP MajorFunction function table for devices
318 * this driver handles.
320 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
321 DriverObject->MajorFunction[i] = driver__dispatch_not_supported_;
322 DriverObject->MajorFunction[IRP_MJ_PNP] = driver__dispatch_pnp_;
323 DriverObject->MajorFunction[IRP_MJ_POWER] = driver__dispatch_power_;
324 DriverObject->MajorFunction[IRP_MJ_CREATE] = driver__dispatch_create_close_;
325 DriverObject->MajorFunction[IRP_MJ_CLOSE] = driver__dispatch_create_close_;
326 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
327 driver__dispatch_sys_ctl_;
328 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
329 driver__dispatch_dev_ctl_;
330 DriverObject->MajorFunction[IRP_MJ_SCSI] = driver__dispatch_scsi_;
331 /* Set the driver Unload callback. */
332 DriverObject->DriverUnload = driver__unload_;
333 /* Set the driver AddDevice callback. */
334 DriverObject->DriverExtension->AddDevice = driver__attach_fdo_;
335 /* Initialize various modules. */
336 disk__module_init(); /* TODO: Check for error. */
337 filedisk__module_init(); /* TODO: Check for error. */
338 ramdisk__module_init(); /* TODO: Check for error. */
341 * Always create the root-enumerated, main bus device.
342 * This is required in order to boot from a WinVBlock disk.
344 status = driver__create_bus_();
345 if(!NT_SUCCESS(status))
348 driver__started_ = TRUE;
350 return STATUS_SUCCESS;
354 driver__unload_(DriverObject);
355 DBG("Exit due to failure\n");
359 static NTSTATUS STDCALL driver__dispatch_not_supported_(
360 IN PDEVICE_OBJECT dev_obj,
363 irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
364 IoCompleteRequest(irp, IO_NO_INCREMENT);
365 return irp->IoStatus.Status;
369 * Common IRP completion routine.
371 * @v irp Points to the IRP to complete.
372 * @v info Number of bytes returned for the IRP, or 0.
373 * @v status Status for the IRP to complete.
374 * @ret NTSTATUS Returns the status value, as passed.
376 winvblock__lib_func NTSTATUS STDCALL driver__complete_irp(
381 irp->IoStatus.Information = info;
382 irp->IoStatus.Status = status;
383 IoCompleteRequest(irp, IO_NO_INCREMENT);
385 Debug_IrpEnd(irp, status);
390 /* Handle a power IRP. */
391 static NTSTATUS driver__dispatch_power_(
392 IN PDEVICE_OBJECT dev_obj,
395 /* WvDevFromDevObj() checks for a NULL dev_obj */
396 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
399 Debug_IrpStart(dev_obj, irp);
401 /* Check that the device exists. */
402 if (!dev || dev->State == WvDevStateDeleted) {
403 /* Even if it doesn't, a power IRP is important! */
404 PoStartNextPowerIrp(irp);
405 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
407 /* Call the particular device's power handler. */
408 if (dev->IrpMj && dev->IrpMj->Power)
409 return dev->IrpMj->Power(dev, irp);
410 /* Otherwise, we don't support the IRP. */
411 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
414 /* Handle an IRP_MJ_CREATE or IRP_MJ_CLOSE IRP. */
415 static NTSTATUS driver__dispatch_create_close_(
416 IN PDEVICE_OBJECT dev_obj,
419 /* WvDevFromDevObj() checks for a NULL dev_obj */
420 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
423 Debug_IrpStart(dev_obj, irp);
425 /* Check that the device exists. */
426 if (!dev || dev->State == WvDevStateDeleted)
427 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
428 /* Always succeed with nothing to do. */
429 return driver__complete_irp(irp, 0, STATUS_SUCCESS);
432 /* Handle an IRP_MJ_SYSTEM_CONTROL IRP. */
433 static NTSTATUS driver__dispatch_sys_ctl_(
434 IN PDEVICE_OBJECT dev_obj,
437 /* WvDevFromDevObj() checks for a NULL dev_obj */
438 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
441 Debug_IrpStart(dev_obj, irp);
443 /* Check that the device exists. */
444 if (!dev || dev->State == WvDevStateDeleted)
445 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
446 /* Call the particular device's power handler. */
447 if (dev->IrpMj && dev->IrpMj->SysCtl)
448 return dev->IrpMj->SysCtl(dev, irp);
449 /* Otherwise, we don't support the IRP. */
450 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
453 /* Handle an IRP_MJ_DEVICE_CONTROL IRP. */
454 static NTSTATUS driver__dispatch_dev_ctl_(
455 IN PDEVICE_OBJECT dev_obj,
458 /* WvDevFromDevObj() checks for a NULL dev_obj */
459 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
460 PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
463 Debug_IrpStart(dev_obj, irp);
465 /* Check that the device exists. */
466 if (!dev || dev->State == WvDevStateDeleted)
467 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
468 /* Call the particular device's power handler. */
469 if (dev->IrpMj && dev->IrpMj->DevCtl) {
470 return dev->IrpMj->DevCtl(
473 io_stack_loc->Parameters.DeviceIoControl.IoControlCode
476 /* Otherwise, we don't support the IRP. */
477 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
480 /* Handle an IRP_MJ_SCSI IRP. */
481 static NTSTATUS driver__dispatch_scsi_(
482 IN PDEVICE_OBJECT dev_obj,
485 /* WvDevFromDevObj() checks for a NULL dev_obj */
486 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
487 PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
490 Debug_IrpStart(dev_obj, irp);
492 /* Check that the device exists. */
493 if (!dev || dev->State == WvDevStateDeleted)
494 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
495 /* Call the particular device's power handler. */
496 if (dev->IrpMj && dev->IrpMj->Scsi) {
497 return dev->IrpMj->Scsi(
500 io_stack_loc->Parameters.Scsi.Srb->Function
503 /* Otherwise, we don't support the IRP. */
504 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
507 /* Handle an IRP_MJ_PNP IRP. */
508 static NTSTATUS driver__dispatch_pnp_(
509 IN PDEVICE_OBJECT dev_obj,
512 /* WvDevFromDevObj() checks for a NULL dev_obj */
513 WV_SP_DEV_T dev = WvDevFromDevObj(dev_obj);
514 PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
517 Debug_IrpStart(dev_obj, irp);
519 /* Check that the device exists. */
520 if (!dev || dev->State == WvDevStateDeleted)
521 return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
522 /* Call the particular device's power handler. */
523 if (dev->IrpMj && dev->IrpMj->Pnp) {
524 return dev->IrpMj->Pnp(
527 io_stack_loc->MinorFunction
530 /* Otherwise, we don't support the IRP. */
531 return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
534 static void STDCALL driver__unload_(IN PDRIVER_OBJECT DriverObject) {
535 DBG("Unloading...\n");
536 if (driver__state_handle_ != NULL)
537 PoUnregisterSystemState(driver__state_handle_);
538 IoDeleteSymbolicLink(&WvBusDosname_);
539 driver__bus_fdo_ = NULL;
540 wv_free(driver__os_load_opts_);
541 driver__started_ = FALSE;
545 winvblock__lib_func void STDCALL Driver_CompletePendingIrp(IN PIRP Irp) {
547 Debug_IrpEnd(Irp, Irp->IoStatus.Status);
549 IoCompleteRequest(Irp, IO_NO_INCREMENT);
552 /* Note the exception to the function naming convention. */
553 winvblock__lib_func NTSTATUS STDCALL Error(
557 DBG("%s: 0x%08x\n", Message, Status);
562 * Get a pointer to the driver bus device.
564 * @ret A pointer to the driver bus, or NULL.
566 winvblock__lib_func WV_SP_BUS_T driver__bus(void) {
567 if (!driver__bus_fdo_) {
568 DBG("No driver bus device!\n");
571 return WvBusFromDev(WvDevFromDevObj(driver__bus_fdo_));