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/>.
31 #include "winvblock.h"
32 #include "wv_stdlib.h"
33 #include "wv_string.h"
47 PDRIVER_OBJECT driver__obj_ptr = NULL;
50 static void * driver__state_handle_;
51 static winvblock__bool driver__started_ = FALSE;
52 static PDEVICE_OBJECT driver__bus_fdo_ = NULL;
53 static KSPIN_LOCK driver__bus_fdo_lock_;
54 /* Contains TXTSETUP.SIF/BOOT.INI-style OsLoadOptions parameters. */
55 static LPWSTR driver__os_load_opts_ = NULL;
57 /* Forward declarations. */
58 static driver__dispatch_func driver__dispatch_not_supported_;
59 static driver__dispatch_func driver__dispatch_power_;
60 static driver__dispatch_func driver__dispatch_create_close_;
61 static driver__dispatch_func driver__dispatch_;
62 static void STDCALL driver__unload_(IN PDRIVER_OBJECT);
64 static LPWSTR STDCALL get_opt(IN LPWSTR opt_name) {
65 LPWSTR our_opts, the_opt;
66 WCHAR our_sig[] = L"WINVBLOCK=";
67 /* To produce constant integer expressions. */
69 our_sig_len_bytes = sizeof ( our_sig ) - sizeof ( WCHAR ),
70 our_sig_len = our_sig_len_bytes / sizeof ( WCHAR )
72 size_t opt_name_len, opt_name_len_bytes;
74 if (!driver__os_load_opts_ || !opt_name)
77 /* Find /WINVBLOCK= options. */
78 our_opts = driver__os_load_opts_;
79 while (*our_opts != L'\0') {
80 if (!wv_memcmpeq(our_opts, our_sig, our_sig_len_bytes)) {
84 our_opts += our_sig_len;
88 /* Search for the specific option. */
90 opt_name_len = wcslen(opt_name);
91 opt_name_len_bytes = opt_name_len * sizeof (WCHAR);
92 while (*the_opt != L'\0' && *the_opt != L' ') {
93 if (!wv_memcmpeq(the_opt, opt_name, opt_name_len_bytes)) {
94 while (*the_opt != L'\0' && *the_opt != L' ' && *the_opt != L',')
98 the_opt += opt_name_len;
102 if (*the_opt == L'\0' || *the_opt == L' ')
105 /* Next should come "=". */
106 if (*the_opt != L'=')
110 * And finally our option's value. The caller needs
111 * to worry about looking past the end of the option.
114 if (*the_opt == L'\0' || *the_opt == L' ')
119 static NTSTATUS STDCALL driver__attach_fdo_(
120 IN PDRIVER_OBJECT DriverObject,
121 IN PDEVICE_OBJECT PhysicalDeviceObject
126 struct bus__type * bus;
127 PUNICODE_STRING dev_name = NULL;
128 PDEVICE_OBJECT fdo = NULL;
129 struct device__type * dev_ptr;
132 /* Do we alreay have our main bus? */
133 if (driver__bus_fdo_) {
134 DBG("Already have the main bus. Refusing.\n");
135 status = STATUS_NOT_SUPPORTED;
136 goto err_already_established;
138 /* Create the bus. */
141 DBG("bus__create() failed for the main bus.\n");
142 status = STATUS_INSUFFICIENT_RESOURCES;
145 /* In booting, he has a name. His name is WinVBlock. */
146 RtlInitUnicodeString(
148 L"\\Device\\" winvblock__literal_w
150 RtlInitUnicodeString(
152 L"\\DosDevices\\" winvblock__literal_w
155 /* Create the bus FDO. */
156 status = IoCreateDevice(
158 sizeof (driver__dev_ext),
160 FILE_DEVICE_CONTROLLER,
161 FILE_DEVICE_SECURE_OPEN,
165 if (!NT_SUCCESS(status)) {
166 DBG("IoCreateDevice() failed!\n");
169 /* DosDevice symlink. */
170 status = IoCreateSymbolicLink(
174 if (!NT_SUCCESS(status)) {
175 DBG("IoCreateSymbolicLink() failed!\n");
176 goto err_dos_symlink;
178 /* Set associations for the bus, device, FDO, PDO. */
179 device__set(fdo, bus->device);
180 bus->device->Self = fdo;
181 bus->PhysicalDeviceObject = PhysicalDeviceObject;
182 fdo->Flags |= DO_DIRECT_IO; /* FIXME? */
183 fdo->Flags |= DO_POWER_INRUSH; /* FIXME? */
184 /* Attach the FDO to the PDO. */
185 bus->LowerDeviceObject = IoAttachDeviceToDeviceStack(
189 if (bus->LowerDeviceObject == NULL) {
190 status = STATUS_NO_SUCH_DEVICE;
191 DBG("IoAttachDeviceToDeviceStack() failed!\n");
195 KeAcquireSpinLock(&driver__bus_fdo_lock_, &irql);
196 if (driver__bus_fdo_) {
197 KeReleaseSpinLock(&driver__bus_fdo_lock_, irql);
198 DBG("Beaten to it!\n");
199 status = STATUS_NOT_SUPPORTED;
200 goto err_race_failed;
202 fdo->Flags &= ~DO_DEVICE_INITIALIZING;
204 bus->device->State = Started;
206 driver__bus_fdo_ = fdo;
207 KeReleaseSpinLock(&driver__bus_fdo_lock_, irql);
209 return STATUS_SUCCESS;
215 IoDeleteSymbolicLink(&bus->dos_dev_name);
221 device__free(bus->device);
224 err_already_established:
226 DBG("Exit with failure\n");
230 /* Create the root-enumerated, main bus device. */
231 NTSTATUS STDCALL driver__create_bus_(void) {
232 struct bus__type * bus;
234 PDEVICE_OBJECT bus_pdo = NULL;
236 /* Create the PDO. */
237 IoReportDetectedDevice(
239 InterfaceTypeUndefined,
247 if (bus_pdo == NULL) {
248 DBG("IoReportDetectedDevice() went wrong! Exiting.\n");
249 status = STATUS_UNSUCCESSFUL;
252 /* Attach FDO to PDO. */
253 status = driver__attach_fdo_(driver__obj_ptr, bus_pdo);
254 if (!NT_SUCCESS(status)) {
255 DBG("driver__attach_fdo_() went wrong!\n");
258 /* PDO created, FDO attached. All done. */
259 return STATUS_SUCCESS;
263 IoDeleteDevice(bus_pdo);
270 * Note the exception to the function naming convention.
271 * TODO: See if a Makefile change is good enough.
273 NTSTATUS STDCALL DriverEntry(
274 IN PDRIVER_OBJECT DriverObject,
275 IN PUNICODE_STRING RegistryPath
281 if (driver__obj_ptr) {
282 DBG("Re-entry not allowed!\n");
283 return STATUS_NOT_SUPPORTED;
285 driver__obj_ptr = DriverObject;
286 if (driver__started_)
287 return STATUS_SUCCESS;
289 status = registry__note_os_load_opts(&driver__os_load_opts_);
290 if (!NT_SUCCESS(status))
291 return Error("registry__note_driver__os_load_opts", status);
293 driver__state_handle_ = NULL;
294 KeInitializeSpinLock(&driver__bus_fdo_lock_);
296 if ((driver__state_handle_ = PoRegisterSystemState(
300 DBG("Could not set system state to ES_CONTINUOUS!!\n");
303 * Set up IRP MajorFunction function table for devices
304 * this driver handles.
306 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
307 DriverObject->MajorFunction[i] = driver__dispatch_not_supported_;
308 DriverObject->MajorFunction[IRP_MJ_PNP] = driver__dispatch_;
309 DriverObject->MajorFunction[IRP_MJ_POWER] = driver__dispatch_power_;
310 DriverObject->MajorFunction[IRP_MJ_CREATE] = driver__dispatch_create_close_;
311 DriverObject->MajorFunction[IRP_MJ_CLOSE] = driver__dispatch_create_close_;
312 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = driver__dispatch_;
313 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = driver__dispatch_;
314 DriverObject->MajorFunction[IRP_MJ_SCSI] = driver__dispatch_;
315 /* Set the driver Unload callback. */
316 DriverObject->DriverUnload = driver__unload_;
317 /* Set the driver AddDevice callback. */
318 DriverObject->DriverExtension->AddDevice = driver__attach_fdo_;
319 /* Initialize various modules. */
320 disk__init(); /* TODO: Check for error. */
321 filedisk__init(); /* TODO: Check for error. */
322 ramdisk__init(); /* TODO: Check for error. */
325 * Always create the root-enumerated, main bus device.
326 * This is required in order to boot from a WinVBlock disk.
328 status = driver__create_bus_();
329 if(!NT_SUCCESS(status))
332 driver__started_ = TRUE;
334 return STATUS_SUCCESS;
338 driver__unload_(DriverObject);
339 DBG("Exit due to failure\n");
343 static NTSTATUS STDCALL driver__dispatch_not_supported_(
344 IN PDEVICE_OBJECT dev_obj,
347 irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
348 IoCompleteRequest(irp, IO_NO_INCREMENT);
349 return irp->IoStatus.Status;
352 /* IRP is not understood. */
353 extern winvblock__lib_func NTSTATUS STDCALL driver__not_supported(
354 IN PDEVICE_OBJECT dev_obj,
356 IN PIO_STACK_LOCATION stack,
357 IN struct device__type * dev_ptr,
358 OUT winvblock__bool_ptr completion_ptr
360 NTSTATUS status = STATUS_NOT_SUPPORTED;
362 irp->IoStatus.Status = status;
363 IoCompleteRequest(irp, IO_NO_INCREMENT);
364 *completion_ptr = TRUE;
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 /* device__get() checks for a NULL dev_obj */
396 struct device__type * dev = device__get(dev_obj);
399 Debug_IrpStart(dev_obj, irp);
401 /* Check that the device exists. */
402 if (!dev || dev->state == device__state_deleted) {
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->irp_mj && dev->irp_mj->power)
409 return dev->irp_mj->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 /* device__get() checks for a NULL dev_obj */
420 struct device__type * dev = device__get(dev_obj);
423 Debug_IrpStart(dev_obj, irp);
425 /* Check that the device exists. */
426 if (!dev || dev->state == device__state_deleted)
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 static NTSTATUS STDCALL driver__dispatch_(
433 IN PDEVICE_OBJECT DeviceObject,
437 struct device__type * dev_ptr;
440 Debug_IrpStart(DeviceObject, Irp);
442 dev_ptr = device__get(DeviceObject);
444 /* Check for a deleted device. */
445 if (dev_ptr->state == device__state_deleted) {
446 Irp->IoStatus.Information = 0;
447 Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
448 IoCompleteRequest(Irp, IO_NO_INCREMENT);
450 Debug_IrpEnd ( Irp, STATUS_NO_SUCH_DEVICE );
452 return STATUS_NO_SUCH_DEVICE;
455 /* Enqueue the IRP for threaded devices, or process immediately. */
456 if (dev_ptr->thread) {
457 IoMarkIrpPending(Irp);
458 ExInterlockedInsertTailList(
460 /* Where IRPs can be linked. */
461 &Irp->Tail.Overlay.ListEntry,
462 &dev_ptr->irp_list_lock
464 KeSetEvent(&dev_ptr->thread_wakeup, 0, FALSE);
465 status = STATUS_PENDING;
467 status = dev_ptr->dispatch(DeviceObject, Irp);
472 /* Place-holder while implementing a dispatch routine per device class. */
473 winvblock__lib_func NTSTATUS STDCALL driver__default_dispatch(
474 IN PDEVICE_OBJECT dev,
478 winvblock__bool completion = FALSE;
479 static const irp__handling handling_table[] = {
481 * Major, minor, any major?, any minor?, handler
482 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
483 * Note that the fall-through case must come FIRST!
484 * Why? It sets completion to true, so others won't be called.
486 { 0, 0, TRUE, TRUE, driver__not_supported },
489 status = irp__process(
492 IoGetCurrentIrpStackLocation(irp),
496 /* Fall through to some driver defaults, if needed. */
497 if (status == STATUS_NOT_SUPPORTED && !completion) {
498 status = irp__process_with_table(
502 sizeof handling_table,
507 if (status != STATUS_PENDING)
508 Debug_IrpEnd(irp, status);
514 static void STDCALL driver__unload_(IN PDRIVER_OBJECT DriverObject) {
515 UNICODE_STRING DosDeviceName;
517 DBG("Unloading...\n");
518 if (driver__state_handle_ != NULL)
519 PoUnregisterSystemState(driver__state_handle_);
520 RtlInitUnicodeString(
522 L"\\DosDevices\\" winvblock__literal_w
524 IoDeleteSymbolicLink(&DosDeviceName);
525 driver__bus_fdo_ = NULL;
526 wv_free(driver__os_load_opts_);
527 driver__started_ = FALSE;
531 winvblock__lib_func void STDCALL Driver_CompletePendingIrp(IN PIRP Irp) {
533 Debug_IrpEnd(Irp, Irp->IoStatus.Status);
535 IoCompleteRequest(Irp, IO_NO_INCREMENT);
538 /* Note the exception to the function naming convention. */
539 winvblock__lib_func NTSTATUS STDCALL Error(
543 DBG("%s: 0x%08x\n", Message, Status);
548 * Get a pointer to the driver bus device.
550 * @ret A pointer to the driver bus, or NULL.
552 winvblock__lib_func struct bus__type * driver__bus(void) {
553 if (!driver__bus_fdo_) {
554 DBG("No driver bus device!\n");
557 return bus__get(device__get(driver__bus_fdo_));