[NTOS]: Implement Configuration Manager routines for building a driver list, sorting...
[reactos.git] / reactos / ntoskrnl / io / iomgr / driver.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/iomgr/driver.c
5 * PURPOSE: Driver Object Management
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Filip Navara (navaraf@reactos.org)
8 * Hervé Poussineau (hpoussin@reactos.org)
9 */
10
11 /* INCLUDES *******************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16
17 /* GLOBALS ********************************************************************/
18
19 LIST_ENTRY DriverReinitListHead;
20 KSPIN_LOCK DriverReinitListLock;
21 PLIST_ENTRY DriverReinitTailEntry;
22
23 PLIST_ENTRY DriverBootReinitTailEntry;
24 LIST_ENTRY DriverBootReinitListHead;
25 KSPIN_LOCK DriverBootReinitListLock;
26
27 UNICODE_STRING IopHardwareDatabaseKey =
28 RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DESCRIPTION\\SYSTEM");
29
30 POBJECT_TYPE IoDriverObjectType = NULL;
31
32 #define TAG_RTLREGISTRY 'vrqR'
33
34 extern BOOLEAN ExpInTextModeSetup;
35 extern BOOLEAN PnpSystemInit;
36
37 USHORT IopGroupIndex;
38 PLIST_ENTRY IopGroupTable;
39
40 /* PRIVATE FUNCTIONS **********************************************************/
41
42 NTSTATUS NTAPI
43 IopInvalidDeviceRequest(
44 PDEVICE_OBJECT DeviceObject,
45 PIRP Irp)
46 {
47 Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
48 Irp->IoStatus.Information = 0;
49 IoCompleteRequest(Irp, IO_NO_INCREMENT);
50 return STATUS_INVALID_DEVICE_REQUEST;
51 }
52
53 VOID
54 NTAPI
55 IopDeleteDriver(IN PVOID ObjectBody)
56 {
57 PDRIVER_OBJECT DriverObject = ObjectBody;
58 PIO_CLIENT_EXTENSION DriverExtension, NextDriverExtension;
59 PAGED_CODE();
60
61 /* Get the extension and loop them */
62 DriverExtension = IoGetDrvObjExtension(DriverObject)->
63 ClientDriverExtension;
64 while (DriverExtension)
65 {
66 /* Get the next one */
67 NextDriverExtension = DriverExtension->NextExtension;
68 ExFreePoolWithTag(DriverExtension, TAG_DRIVER_EXTENSION);
69
70 /* Move on */
71 DriverExtension = NextDriverExtension;
72 }
73
74 /* Check if the driver image is still loaded */
75 if (DriverObject->DriverSection)
76 {
77 /* Unload it */
78 MmUnloadSystemImage(DriverObject->DriverSection);
79 }
80
81 /* Check if it has a name */
82 if (DriverObject->DriverName.Buffer)
83 {
84 /* Free it */
85 ExFreePool(DriverObject->DriverName.Buffer);
86 }
87
88 #if 0 /* See a bit of hack in IopCreateDriver */
89 /* Check if it has a service key name */
90 if (DriverObject->DriverExtension->ServiceKeyName.Buffer)
91 {
92 /* Free it */
93 ExFreePool(DriverObject->DriverExtension->ServiceKeyName.Buffer);
94 }
95 #endif
96 }
97
98 NTSTATUS FASTCALL
99 IopGetDriverObject(
100 PDRIVER_OBJECT *DriverObject,
101 PUNICODE_STRING ServiceName,
102 BOOLEAN FileSystem)
103 {
104 PDRIVER_OBJECT Object;
105 WCHAR NameBuffer[MAX_PATH];
106 UNICODE_STRING DriverName;
107 NTSTATUS Status;
108
109 DPRINT("IopGetDriverObject(%p '%wZ' %x)\n",
110 DriverObject, ServiceName, FileSystem);
111
112 *DriverObject = NULL;
113
114 /* Create ModuleName string */
115 if (ServiceName == NULL || ServiceName->Buffer == NULL)
116 /* We don't know which DriverObject we have to open */
117 return STATUS_INVALID_PARAMETER_2;
118
119 DriverName.Buffer = NameBuffer;
120 DriverName.Length = 0;
121 DriverName.MaximumLength = sizeof(NameBuffer);
122
123 if (FileSystem == TRUE)
124 RtlAppendUnicodeToString(&DriverName, FILESYSTEM_ROOT_NAME);
125 else
126 RtlAppendUnicodeToString(&DriverName, DRIVER_ROOT_NAME);
127 RtlAppendUnicodeStringToString(&DriverName, ServiceName);
128
129 DPRINT("Driver name: '%wZ'\n", &DriverName);
130
131 /* Open driver object */
132 Status = ObReferenceObjectByName(
133 &DriverName,
134 OBJ_OPENIF | OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, /* Attributes */
135 NULL, /* PassedAccessState */
136 0, /* DesiredAccess */
137 IoDriverObjectType,
138 KernelMode,
139 NULL, /* ParseContext */
140 (PVOID*)&Object);
141
142 if (!NT_SUCCESS(Status))
143 {
144 DPRINT("Failed to reference driver object, status=0x%08x\n", Status);
145 return Status;
146 }
147
148 *DriverObject = Object;
149
150 DPRINT("Driver Object: %p\n", Object);
151
152 return STATUS_SUCCESS;
153 }
154
155 /*
156 * RETURNS
157 * TRUE if String2 contains String1 as a suffix.
158 */
159 BOOLEAN
160 NTAPI
161 IopSuffixUnicodeString(
162 IN PCUNICODE_STRING String1,
163 IN PCUNICODE_STRING String2)
164 {
165 PWCHAR pc1;
166 PWCHAR pc2;
167 ULONG Length;
168
169 if (String2->Length < String1->Length)
170 return FALSE;
171
172 Length = String1->Length / 2;
173 pc1 = String1->Buffer;
174 pc2 = &String2->Buffer[String2->Length / sizeof(WCHAR) - Length];
175
176 if (pc1 && pc2)
177 {
178 while (Length--)
179 {
180 if( *pc1++ != *pc2++ )
181 return FALSE;
182 }
183 return TRUE;
184 }
185 return FALSE;
186 }
187
188 /*
189 * IopDisplayLoadingMessage
190 *
191 * Display 'Loading XXX...' message.
192 */
193
194 VOID
195 FASTCALL
196 INIT_FUNCTION
197 IopDisplayLoadingMessage(PUNICODE_STRING ServiceName)
198 {
199 CHAR TextBuffer[256];
200 UNICODE_STRING DotSys = RTL_CONSTANT_STRING(L".SYS");
201
202 if (ExpInTextModeSetup) return;
203 if (!KeLoaderBlock) return;
204 RtlUpcaseUnicodeString(ServiceName, ServiceName, FALSE);
205 snprintf(TextBuffer, sizeof(TextBuffer),
206 "%s%sSystem32\\Drivers\\%wZ%s\n",
207 KeLoaderBlock->ArcBootDeviceName,
208 KeLoaderBlock->NtBootPathName,
209 ServiceName,
210 IopSuffixUnicodeString(&DotSys, ServiceName) ? "" : ".SYS");
211 HalDisplayString(TextBuffer);
212 }
213
214 /*
215 * IopNormalizeImagePath
216 *
217 * Normalize an image path to contain complete path.
218 *
219 * Parameters
220 * ImagePath
221 * The input path and on exit the result path. ImagePath.Buffer
222 * must be allocated by ExAllocatePool on input. Caller is responsible
223 * for freeing the buffer when it's no longer needed.
224 *
225 * ServiceName
226 * Name of the service that ImagePath belongs to.
227 *
228 * Return Value
229 * Status
230 *
231 * Remarks
232 * The input image path isn't freed on error.
233 */
234
235 NTSTATUS FASTCALL
236 IopNormalizeImagePath(
237 IN OUT PUNICODE_STRING ImagePath,
238 IN PUNICODE_STRING ServiceName)
239 {
240 UNICODE_STRING InputImagePath;
241
242 RtlCopyMemory(
243 &InputImagePath,
244 ImagePath,
245 sizeof(UNICODE_STRING));
246
247 if (InputImagePath.Length == 0)
248 {
249 ImagePath->Length = 0;
250 ImagePath->MaximumLength =
251 (33 * sizeof(WCHAR)) + ServiceName->Length + sizeof(UNICODE_NULL);
252 ImagePath->Buffer = ExAllocatePool(NonPagedPool, ImagePath->MaximumLength);
253 if (ImagePath->Buffer == NULL)
254 return STATUS_NO_MEMORY;
255
256 RtlAppendUnicodeToString(ImagePath, L"\\SystemRoot\\system32\\drivers\\");
257 RtlAppendUnicodeStringToString(ImagePath, ServiceName);
258 RtlAppendUnicodeToString(ImagePath, L".sys");
259 } else
260 if (InputImagePath.Buffer[0] != L'\\')
261 {
262 ImagePath->Length = 0;
263 ImagePath->MaximumLength =
264 12 * sizeof(WCHAR) + InputImagePath.Length + sizeof(UNICODE_NULL);
265 ImagePath->Buffer = ExAllocatePool(NonPagedPool, ImagePath->MaximumLength);
266 if (ImagePath->Buffer == NULL)
267 return STATUS_NO_MEMORY;
268
269 RtlAppendUnicodeToString(ImagePath, L"\\SystemRoot\\");
270 RtlAppendUnicodeStringToString(ImagePath, &InputImagePath);
271
272 /* Free caller's string */
273 ExFreePoolWithTag(InputImagePath.Buffer, TAG_RTLREGISTRY);
274 }
275
276 return STATUS_SUCCESS;
277 }
278
279 /*
280 * IopLoadServiceModule
281 *
282 * Load a module specified by registry settings for service.
283 *
284 * Parameters
285 * ServiceName
286 * Name of the service to load.
287 *
288 * Return Value
289 * Status
290 */
291
292 NTSTATUS FASTCALL
293 IopLoadServiceModule(
294 IN PUNICODE_STRING ServiceName,
295 OUT PLDR_DATA_TABLE_ENTRY *ModuleObject)
296 {
297 RTL_QUERY_REGISTRY_TABLE QueryTable[3];
298 ULONG ServiceStart;
299 UNICODE_STRING ServiceImagePath, CCSName;
300 NTSTATUS Status;
301 HANDLE CCSKey, ServiceKey;
302 PVOID BaseAddress;
303
304 DPRINT("IopLoadServiceModule(%wZ, 0x%p)\n", ServiceName, ModuleObject);
305
306 /* FIXME: This check may be removed once the bug is fixed */
307 if (ServiceName->Buffer == NULL)
308 {
309 DPRINT1("If you see this, please report to Fireball or hpoussin!\n");
310 return STATUS_UNSUCCESSFUL;
311 }
312
313 /* Open CurrentControlSet */
314 RtlInitUnicodeString(&CCSName,
315 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services");
316 Status = IopOpenRegistryKeyEx(&CCSKey, NULL, &CCSName, KEY_READ);
317 if (!NT_SUCCESS(Status))
318 {
319 DPRINT1("ZwOpenKey() failed with Status %08X\n", Status);
320 return Status;
321 }
322
323 /* Open service key */
324 Status = IopOpenRegistryKeyEx(&ServiceKey, CCSKey, ServiceName, KEY_READ);
325 if (!NT_SUCCESS(Status))
326 {
327 DPRINT1("ZwOpenKey() failed with Status %08X\n", Status);
328 ZwClose(CCSKey);
329 return Status;
330 }
331
332 /*
333 * Get information about the service.
334 */
335
336 RtlZeroMemory(QueryTable, sizeof(QueryTable));
337
338 RtlInitUnicodeString(&ServiceImagePath, NULL);
339
340 QueryTable[0].Name = L"Start";
341 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
342 QueryTable[0].EntryContext = &ServiceStart;
343
344 QueryTable[1].Name = L"ImagePath";
345 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
346 QueryTable[1].EntryContext = &ServiceImagePath;
347
348 Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
349 (PWSTR)ServiceKey, QueryTable, NULL, NULL);
350
351 ZwClose(ServiceKey);
352 ZwClose(CCSKey);
353
354 if (!NT_SUCCESS(Status))
355 {
356 DPRINT1("RtlQueryRegistryValues() failed (Status %x)\n", Status);
357 return Status;
358 }
359
360 /*
361 * Normalize the image path for all later processing.
362 */
363
364 Status = IopNormalizeImagePath(&ServiceImagePath, ServiceName);
365
366 if (!NT_SUCCESS(Status))
367 {
368 DPRINT("IopNormalizeImagePath() failed (Status %x)\n", Status);
369 return Status;
370 }
371
372 /*
373 * Case for disabled drivers
374 */
375
376 if (ServiceStart >= 4)
377 {
378 /* FIXME: Check if it is the right status code */
379 Status = STATUS_PLUGPLAY_NO_DEVICE;
380 }
381 else
382 {
383 DPRINT("Loading module\n");
384 Status = MmLoadSystemImage(&ServiceImagePath, NULL, NULL, 0, (PVOID)ModuleObject, &BaseAddress);
385 }
386
387 ExFreePool(ServiceImagePath.Buffer);
388
389 /*
390 * Now check if the module was loaded successfully.
391 */
392
393 if (!NT_SUCCESS(Status))
394 {
395 DPRINT("Module loading failed (Status %x)\n", Status);
396 }
397
398 DPRINT("Module loading (Status %x)\n", Status);
399
400 return Status;
401 }
402
403 /*
404 * IopInitializeDriverModule
405 *
406 * Initalize a loaded driver.
407 *
408 * Parameters
409 * DeviceNode
410 * Pointer to device node.
411 *
412 * ModuleObject
413 * Module object representing the driver. It can be retrieve by
414 * IopLoadServiceModule.
415 *
416 * ServiceName
417 * Name of the service (as in registry).
418 *
419 * FileSystemDriver
420 * Set to TRUE for file system drivers.
421 *
422 * DriverObject
423 * On successful return this contains the driver object representing
424 * the loaded driver.
425 */
426
427 NTSTATUS FASTCALL
428 IopInitializeDriverModule(
429 IN PDEVICE_NODE DeviceNode,
430 IN PLDR_DATA_TABLE_ENTRY ModuleObject,
431 IN PUNICODE_STRING ServiceName,
432 IN BOOLEAN FileSystemDriver,
433 OUT PDRIVER_OBJECT *DriverObject)
434 {
435 const WCHAR ServicesKeyName[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\";
436 WCHAR NameBuffer[MAX_PATH];
437 UNICODE_STRING DriverName;
438 UNICODE_STRING RegistryKey;
439 PDRIVER_INITIALIZE DriverEntry;
440 PDRIVER_OBJECT Driver;
441 PDEVICE_OBJECT DeviceObject;
442 NTSTATUS Status;
443
444 DriverEntry = ModuleObject->EntryPoint;
445
446 if (ServiceName != NULL && ServiceName->Length != 0)
447 {
448 RegistryKey.Length = 0;
449 RegistryKey.MaximumLength = sizeof(ServicesKeyName) + ServiceName->Length;
450 RegistryKey.Buffer = ExAllocatePool(PagedPool, RegistryKey.MaximumLength);
451 if (RegistryKey.Buffer == NULL)
452 {
453 return STATUS_INSUFFICIENT_RESOURCES;
454 }
455 RtlAppendUnicodeToString(&RegistryKey, ServicesKeyName);
456 RtlAppendUnicodeStringToString(&RegistryKey, ServiceName);
457 }
458 else
459 {
460 RtlInitUnicodeString(&RegistryKey, NULL);
461 }
462
463 /* Create ModuleName string */
464 if (ServiceName && ServiceName->Length > 0)
465 {
466 if (FileSystemDriver == TRUE)
467 wcscpy(NameBuffer, FILESYSTEM_ROOT_NAME);
468 else
469 wcscpy(NameBuffer, DRIVER_ROOT_NAME);
470
471 RtlInitUnicodeString(&DriverName, NameBuffer);
472 DriverName.MaximumLength = sizeof(NameBuffer);
473
474 RtlAppendUnicodeStringToString(&DriverName, ServiceName);
475
476 DPRINT("Driver name: '%wZ'\n", &DriverName);
477 }
478 else
479 DriverName.Length = 0;
480
481 Status = IopCreateDriver(
482 DriverName.Length > 0 ? &DriverName : NULL,
483 DriverEntry,
484 &RegistryKey,
485 ModuleObject->DllBase,
486 ModuleObject->SizeOfImage,
487 &Driver);
488 RtlFreeUnicodeString(&RegistryKey);
489
490 *DriverObject = Driver;
491 if (!NT_SUCCESS(Status))
492 {
493 DPRINT("IopCreateDriver() failed (Status 0x%08lx)\n", Status);
494 return Status;
495 }
496
497 /* Set the driver as initialized */
498 Driver->Flags |= DRVO_INITIALIZED;
499 DeviceObject = Driver->DeviceObject;
500 while (DeviceObject)
501 {
502 /* Set every device as initialized too */
503 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
504 DeviceObject = DeviceObject->NextDevice;
505 }
506
507 if (PnpSystemInit) IopReinitializeDrivers();
508
509 return STATUS_SUCCESS;
510 }
511
512 /*
513 * IopAttachFilterDriversCallback
514 *
515 * Internal routine used by IopAttachFilterDrivers.
516 */
517
518 NTSTATUS NTAPI
519 IopAttachFilterDriversCallback(
520 PWSTR ValueName,
521 ULONG ValueType,
522 PVOID ValueData,
523 ULONG ValueLength,
524 PVOID Context,
525 PVOID EntryContext)
526 {
527 PDEVICE_NODE DeviceNode = Context;
528 UNICODE_STRING ServiceName;
529 PWCHAR Filters;
530 PLDR_DATA_TABLE_ENTRY ModuleObject;
531 PDRIVER_OBJECT DriverObject;
532 NTSTATUS Status;
533
534 for (Filters = ValueData;
535 ((ULONG_PTR)Filters - (ULONG_PTR)ValueData) < ValueLength &&
536 *Filters != 0;
537 Filters += (ServiceName.Length / sizeof(WCHAR)) + 1)
538 {
539 DPRINT("Filter Driver: %S (%wZ)\n", Filters, &DeviceNode->InstancePath);
540 ServiceName.Buffer = Filters;
541 ServiceName.MaximumLength =
542 ServiceName.Length = wcslen(Filters) * sizeof(WCHAR);
543
544 /* Load and initialize the filter driver */
545 Status = IopLoadServiceModule(&ServiceName, &ModuleObject);
546 if (Status != STATUS_IMAGE_ALREADY_LOADED)
547 {
548 if (!NT_SUCCESS(Status))
549 continue;
550
551 Status = IopInitializeDriverModule(DeviceNode, ModuleObject, &ServiceName,
552 FALSE, &DriverObject);
553 if (!NT_SUCCESS(Status))
554 continue;
555 }
556 else
557 {
558 /* get existing DriverObject pointer */
559 Status = IopGetDriverObject(
560 &DriverObject,
561 &ServiceName,
562 FALSE);
563 if (!NT_SUCCESS(Status))
564 {
565 DPRINT1("IopGetDriverObject() returned status 0x%08x!\n", Status);
566 continue;
567 }
568 }
569
570 Status = IopInitializeDevice(DeviceNode, DriverObject);
571 if (!NT_SUCCESS(Status))
572 continue;
573 }
574
575 return STATUS_SUCCESS;
576 }
577
578 /*
579 * IopAttachFilterDrivers
580 *
581 * Load filter drivers for specified device node.
582 *
583 * Parameters
584 * Lower
585 * Set to TRUE for loading lower level filters or FALSE for upper
586 * level filters.
587 */
588
589 NTSTATUS FASTCALL
590 IopAttachFilterDrivers(
591 PDEVICE_NODE DeviceNode,
592 BOOLEAN Lower)
593 {
594 RTL_QUERY_REGISTRY_TABLE QueryTable[2] = { { NULL, 0, NULL, NULL, 0, NULL, 0 }, };
595 UNICODE_STRING Class;
596 WCHAR ClassBuffer[40];
597 UNICODE_STRING EnumRoot = RTL_CONSTANT_STRING(ENUM_ROOT);
598 HANDLE EnumRootKey, SubKey;
599 NTSTATUS Status;
600
601 /* Open enumeration root key */
602 Status = IopOpenRegistryKeyEx(&EnumRootKey, NULL,
603 &EnumRoot, KEY_READ);
604 if (!NT_SUCCESS(Status))
605 {
606 DPRINT1("ZwOpenKey() failed with Status %08X\n", Status);
607 return Status;
608 }
609
610 /* Open subkey */
611 Status = IopOpenRegistryKeyEx(&SubKey, EnumRootKey,
612 &DeviceNode->InstancePath, KEY_READ);
613 if (!NT_SUCCESS(Status))
614 {
615 DPRINT1("ZwOpenKey() failed with Status %08X\n", Status);
616 ZwClose(EnumRootKey);
617 return Status;
618 }
619
620 /*
621 * First load the device filters
622 */
623 QueryTable[0].QueryRoutine = IopAttachFilterDriversCallback;
624 if (Lower)
625 QueryTable[0].Name = L"LowerFilters";
626 else
627 QueryTable[0].Name = L"UpperFilters";
628 QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
629
630 RtlQueryRegistryValues(
631 RTL_REGISTRY_HANDLE,
632 (PWSTR)SubKey,
633 QueryTable,
634 DeviceNode,
635 NULL);
636
637 /*
638 * Now get the class GUID
639 */
640 Class.Length = 0;
641 Class.MaximumLength = 40 * sizeof(WCHAR);
642 Class.Buffer = ClassBuffer;
643 QueryTable[0].QueryRoutine = NULL;
644 QueryTable[0].Name = L"ClassGUID";
645 QueryTable[0].EntryContext = &Class;
646 QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_DIRECT;
647
648 Status = RtlQueryRegistryValues(
649 RTL_REGISTRY_HANDLE,
650 (PWSTR)SubKey,
651 QueryTable,
652 DeviceNode,
653 NULL);
654
655 /* Close handles */
656 ZwClose(SubKey);
657 ZwClose(EnumRootKey);
658
659 /*
660 * Load the class filter driver
661 */
662 if (NT_SUCCESS(Status))
663 {
664 UNICODE_STRING ControlClass = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class");
665
666 Status = IopOpenRegistryKeyEx(&EnumRootKey, NULL,
667 &ControlClass, KEY_READ);
668 if (!NT_SUCCESS(Status))
669 {
670 DPRINT1("ZwOpenKey() failed with Status %08X\n", Status);
671 return Status;
672 }
673
674 /* Open subkey */
675 Status = IopOpenRegistryKeyEx(&SubKey, EnumRootKey,
676 &Class, KEY_READ);
677 if (!NT_SUCCESS(Status))
678 {
679 DPRINT1("ZwOpenKey() failed with Status %08X\n", Status);
680 ZwClose(EnumRootKey);
681 return Status;
682 }
683
684 QueryTable[0].QueryRoutine = IopAttachFilterDriversCallback;
685 if (Lower)
686 QueryTable[0].Name = L"LowerFilters";
687 else
688 QueryTable[0].Name = L"UpperFilters";
689 QueryTable[0].EntryContext = NULL;
690 QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
691
692 RtlQueryRegistryValues(
693 RTL_REGISTRY_HANDLE,
694 (PWSTR)SubKey,
695 QueryTable,
696 DeviceNode,
697 NULL);
698
699 /* Clean up */
700 ZwClose(SubKey);
701 ZwClose(EnumRootKey);
702 }
703
704 return STATUS_SUCCESS;
705 }
706
707 NTSTATUS
708 NTAPI
709 MiResolveImageReferences(IN PVOID ImageBase,
710 IN PUNICODE_STRING ImageFileDirectory,
711 IN PUNICODE_STRING NamePrefix OPTIONAL,
712 OUT PCHAR *MissingApi,
713 OUT PWCHAR *MissingDriver,
714 OUT PLOAD_IMPORTS *LoadImports);
715
716 //
717 // Used for images already loaded (boot drivers)
718 //
719 NTSTATUS
720 NTAPI
721 LdrProcessDriverModule(PLDR_DATA_TABLE_ENTRY LdrEntry,
722 PUNICODE_STRING FileName,
723 PLDR_DATA_TABLE_ENTRY *ModuleObject)
724 {
725 NTSTATUS Status;
726 PLDR_DATA_TABLE_ENTRY NewEntry;
727 UNICODE_STRING BaseName, BaseDirectory;
728 PLOAD_IMPORTS LoadedImports = (PVOID)-2;
729 PCHAR MissingApiName, Buffer;
730 PWCHAR MissingDriverName;
731 PVOID DriverBase = LdrEntry->DllBase;
732
733 /* Allocate a buffer we'll use for names */
734 Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG_LDR_WSTR);
735 if (!Buffer)
736 {
737 /* Fail */
738 return STATUS_INSUFFICIENT_RESOURCES;
739 }
740
741 /* Check for a separator */
742 if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
743 {
744 PWCHAR p;
745 ULONG BaseLength;
746
747 /* Loop the path until we get to the base name */
748 p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
749 while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;
750
751 /* Get the length */
752 BaseLength = (ULONG)(&FileName->Buffer[FileName->Length / sizeof(WCHAR)] - p);
753 BaseLength *= sizeof(WCHAR);
754
755 /* Setup the string */
756 BaseName.Length = (USHORT)BaseLength;
757 BaseName.Buffer = p;
758 }
759 else
760 {
761 /* Otherwise, we already have a base name */
762 BaseName.Length = FileName->Length;
763 BaseName.Buffer = FileName->Buffer;
764 }
765
766 /* Setup the maximum length */
767 BaseName.MaximumLength = BaseName.Length;
768
769 /* Now compute the base directory */
770 BaseDirectory = *FileName;
771 BaseDirectory.Length -= BaseName.Length;
772 BaseDirectory.MaximumLength = BaseDirectory.Length;
773
774 NewEntry = LdrEntry;
775
776 /* Resolve imports */
777 MissingApiName = Buffer;
778 Status = MiResolveImageReferences(DriverBase,
779 &BaseDirectory,
780 NULL,
781 &MissingApiName,
782 &MissingDriverName,
783 &LoadedImports);
784 if (!NT_SUCCESS(Status)) return Status;
785
786 /* Return */
787 *ModuleObject = LdrEntry;
788 return STATUS_SUCCESS;
789 }
790
791 /*
792 * IopInitializeBuiltinDriver
793 *
794 * Initialize a driver that is already loaded in memory.
795 */
796
797 NTSTATUS
798 NTAPI
799 IopInitializeBuiltinDriver(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
800 {
801 PDEVICE_NODE DeviceNode;
802 PDRIVER_OBJECT DriverObject;
803 NTSTATUS Status;
804 PWCHAR FileNameWithoutPath;
805 LPWSTR FileExtension;
806 PUNICODE_STRING ModuleName = &LdrEntry->BaseDllName;
807 UNICODE_STRING ServiceName;
808
809 /*
810 * Display 'Loading XXX...' message
811 */
812 IopDisplayLoadingMessage(ModuleName);
813 InbvIndicateProgress();
814
815 /*
816 * Generate filename without path (not needed by freeldr)
817 */
818 FileNameWithoutPath = wcsrchr(ModuleName->Buffer, L'\\');
819 if (FileNameWithoutPath == NULL)
820 {
821 FileNameWithoutPath = ModuleName->Buffer;
822 }
823 else
824 {
825 FileNameWithoutPath++;
826 }
827
828 /*
829 * Strip the file extension from ServiceName
830 */
831 RtlCreateUnicodeString(&ServiceName, FileNameWithoutPath);
832 FileExtension = wcsrchr(ServiceName.Buffer, '.');
833 if (FileExtension != NULL)
834 {
835 ServiceName.Length -= wcslen(FileExtension) * sizeof(WCHAR);
836 FileExtension[0] = 0;
837 }
838
839 /*
840 * Determine the right device object
841 */
842 /* Use IopRootDeviceNode for now */
843 Status = IopCreateDeviceNode(IopRootDeviceNode, NULL, &ServiceName, &DeviceNode);
844 if (!NT_SUCCESS(Status))
845 {
846 DPRINT1("Driver '%wZ' load failed, status (%x)\n", ModuleName, Status);
847 return(Status);
848 }
849 DeviceNode->ServiceName = ServiceName;
850
851 /*
852 * Initialize the driver
853 */
854 Status = IopInitializeDriverModule(DeviceNode, LdrEntry,
855 &DeviceNode->ServiceName, FALSE, &DriverObject);
856
857 if (!NT_SUCCESS(Status))
858 {
859 IopFreeDeviceNode(DeviceNode);
860 return Status;
861 }
862
863 Status = IopInitializeDevice(DeviceNode, DriverObject);
864 if (NT_SUCCESS(Status))
865 {
866 Status = IopStartDevice(DeviceNode);
867 }
868
869 return Status;
870 }
871
872 /*
873 * IopInitializeBootDrivers
874 *
875 * Initialize boot drivers and free memory for boot files.
876 *
877 * Parameters
878 * None
879 *
880 * Return Value
881 * None
882 */
883 VOID
884 FASTCALL
885 IopInitializeBootDrivers(VOID)
886 {
887 PLIST_ENTRY ListHead, NextEntry, NextEntry2;
888 PLDR_DATA_TABLE_ENTRY LdrEntry;
889 PDEVICE_NODE DeviceNode;
890 PDRIVER_OBJECT DriverObject;
891 LDR_DATA_TABLE_ENTRY ModuleObject;
892 NTSTATUS Status;
893 UNICODE_STRING DriverName;
894 ULONG i, Index;
895 PDRIVER_INFORMATION DriverInfo, DriverInfoTag;
896 HANDLE KeyHandle;
897 PBOOT_DRIVER_LIST_ENTRY BootEntry;
898 DPRINT("IopInitializeBootDrivers()\n");
899
900 /* Use IopRootDeviceNode for now */
901 Status = IopCreateDeviceNode(IopRootDeviceNode, NULL, NULL, &DeviceNode);
902 if (!NT_SUCCESS(Status)) return;
903
904 /* Setup the module object for the RAW FS Driver */
905 ModuleObject.DllBase = NULL;
906 ModuleObject.SizeOfImage = 0;
907 ModuleObject.EntryPoint = RawFsDriverEntry;
908 RtlInitUnicodeString(&DriverName, L"RAW");
909
910 /* Initialize it */
911 Status = IopInitializeDriverModule(DeviceNode,
912 &ModuleObject,
913 &DriverName,
914 TRUE,
915 &DriverObject);
916 if (!NT_SUCCESS(Status))
917 {
918 /* Fail */
919 IopFreeDeviceNode(DeviceNode);
920 return;
921 }
922
923 /* Now initialize the associated device */
924 Status = IopInitializeDevice(DeviceNode, DriverObject);
925 if (!NT_SUCCESS(Status))
926 {
927 /* Fail */
928 IopFreeDeviceNode(DeviceNode);
929 return;
930 }
931
932 /* Start it up */
933 Status = IopStartDevice(DeviceNode);
934 if (!NT_SUCCESS(Status))
935 {
936 /* Fail */
937 IopFreeDeviceNode(DeviceNode);
938 return;
939 }
940
941 /* Get highest group order index */
942 IopGroupIndex = PpInitGetGroupOrderIndex(NULL);
943 if (IopGroupIndex == 0xFFFF) ASSERT(FALSE);
944
945 /* Allocate the group table */
946 IopGroupTable = ExAllocatePoolWithTag(PagedPool,
947 IopGroupIndex * sizeof(LIST_ENTRY),
948 TAG_IO);
949 if (IopGroupTable == NULL) ASSERT(FALSE);
950
951 /* Initialize the group table lists */
952 for (i = 0; i < IopGroupIndex; i++) InitializeListHead(&IopGroupTable[i]);
953
954 /* Loop the boot modules */
955 ListHead = &KeLoaderBlock->LoadOrderListHead;
956 NextEntry = ListHead->Flink;
957 while (ListHead != NextEntry)
958 {
959 /* Get the entry */
960 LdrEntry = CONTAINING_RECORD(NextEntry,
961 LDR_DATA_TABLE_ENTRY,
962 InLoadOrderLinks);
963
964 /* Check if the DLL needs to be initialized */
965 if (LdrEntry->Flags & LDRP_DRIVER_DEPENDENT_DLL)
966 {
967 /* Call its entrypoint */
968 MmCallDllInitialize(LdrEntry, NULL);
969 }
970
971 /* Go to the next driver */
972 NextEntry = NextEntry->Flink;
973 }
974
975 /* Loop the boot drivers */
976 ListHead = &KeLoaderBlock->BootDriverListHead;
977 NextEntry = ListHead->Flink;
978 while (ListHead != NextEntry)
979 {
980 /* Get the entry */
981 BootEntry = CONTAINING_RECORD(NextEntry,
982 BOOT_DRIVER_LIST_ENTRY,
983 Link);
984
985 /* Get the driver loader entry */
986 LdrEntry = BootEntry->LdrEntry;
987
988 /* Allocate our internal accounting structure */
989 DriverInfo = ExAllocatePoolWithTag(PagedPool,
990 sizeof(DRIVER_INFORMATION),
991 TAG_IO);
992 if (DriverInfo)
993 {
994 /* Zero it and initialize it */
995 RtlZeroMemory(DriverInfo, sizeof(DRIVER_INFORMATION));
996 InitializeListHead(&DriverInfo->Link);
997 DriverInfo->DataTableEntry = BootEntry;
998
999 /* Open the registry key */
1000 Status = IopOpenRegistryKeyEx(&KeyHandle,
1001 NULL,
1002 &BootEntry->RegistryPath,
1003 KEY_READ);
1004 if ((NT_SUCCESS(Status)) || /* ReactOS HACK for SETUPLDR */
1005 ((KeLoaderBlock->SetupLdrBlock) && (KeyHandle = (PVOID)1)))
1006 {
1007 /* Save the handle */
1008 DriverInfo->ServiceHandle = KeyHandle;
1009
1010 /* Get the group oder index */
1011 Index = PpInitGetGroupOrderIndex(KeyHandle);
1012
1013 /* Get the tag position */
1014 DriverInfo->TagPosition = PipGetDriverTagPriority(KeyHandle);
1015
1016 /* Insert it into the list, at the right place */
1017 ASSERT(Index < IopGroupIndex);
1018 NextEntry2 = IopGroupTable[Index].Flink;
1019 while (NextEntry2 != &IopGroupTable[Index])
1020 {
1021 /* Get the driver info */
1022 DriverInfoTag = CONTAINING_RECORD(NextEntry2,
1023 DRIVER_INFORMATION,
1024 Link);
1025
1026 /* Check if we found the right tag position */
1027 if (DriverInfoTag->TagPosition > DriverInfo->TagPosition)
1028 {
1029 /* We're done */
1030 break;
1031 }
1032
1033 /* Next entry */
1034 NextEntry2 = NextEntry2->Flink;
1035 }
1036
1037 /* Insert us right before the next entry */
1038 NextEntry2 = NextEntry2->Blink;
1039 InsertHeadList(NextEntry2, &DriverInfo->Link);
1040 }
1041 }
1042
1043 /* Go to the next driver */
1044 NextEntry = NextEntry->Flink;
1045 }
1046
1047 /* Loop each group index */
1048 for (i = 0; i < IopGroupIndex; i++)
1049 {
1050 /* Loop each group table */
1051 NextEntry = IopGroupTable[i].Flink;
1052 while (NextEntry != &IopGroupTable[i])
1053 {
1054 /* Get the entry */
1055 DriverInfo = CONTAINING_RECORD(NextEntry,
1056 DRIVER_INFORMATION,
1057 Link);
1058
1059 /* Get the driver loader entry */
1060 LdrEntry = DriverInfo->DataTableEntry->LdrEntry;
1061
1062 /* Initialize it */
1063 IopInitializeBuiltinDriver(LdrEntry);
1064
1065 /* Next entry */
1066 NextEntry = NextEntry->Flink;
1067 }
1068 }
1069
1070 /* In old ROS, the loader list became empty after this point. Simulate. */
1071 InitializeListHead(&KeLoaderBlock->LoadOrderListHead);
1072 }
1073
1074 VOID
1075 FASTCALL
1076 IopInitializeSystemDrivers(VOID)
1077 {
1078 PUNICODE_STRING *DriverList, *SavedList;
1079
1080 /* No system drivers on the boot cd */
1081 if (KeLoaderBlock->SetupLdrBlock) return;
1082
1083 /* Get the driver list */
1084 SavedList = DriverList = CmGetSystemDriverList();
1085 ASSERT(DriverList);
1086
1087 /* Loop it */
1088 while (*DriverList)
1089 {
1090 /* Load the driver */
1091 ZwLoadDriver(*DriverList);
1092
1093 /* Free the entry */
1094 RtlFreeUnicodeString(*DriverList);
1095 ExFreePool(*DriverList);
1096
1097 /* Next entry */
1098 InbvIndicateProgress();
1099 DriverList++;
1100 }
1101
1102 /* Free the list */
1103 ExFreePool(SavedList);
1104 }
1105
1106 /*
1107 * IopUnloadDriver
1108 *
1109 * Unloads a device driver.
1110 *
1111 * Parameters
1112 * DriverServiceName
1113 * Name of the service to unload (registry key).
1114 *
1115 * UnloadPnpDrivers
1116 * Whether to unload Plug & Plug or only legacy drivers. If this
1117 * parameter is set to FALSE, the routine will unload only legacy
1118 * drivers.
1119 *
1120 * Return Value
1121 * Status
1122 *
1123 * To do
1124 * Guard the whole function by SEH.
1125 */
1126
1127 NTSTATUS NTAPI
1128 IopUnloadDriver(PUNICODE_STRING DriverServiceName, BOOLEAN UnloadPnpDrivers)
1129 {
1130 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
1131 UNICODE_STRING ImagePath;
1132 UNICODE_STRING ServiceName;
1133 UNICODE_STRING ObjectName;
1134 PDRIVER_OBJECT DriverObject;
1135 PDEVICE_OBJECT DeviceObject;
1136 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1137 LOAD_UNLOAD_PARAMS LoadParams;
1138 NTSTATUS Status;
1139 LPWSTR Start;
1140 BOOLEAN SafeToUnload = TRUE;
1141
1142 DPRINT("IopUnloadDriver('%wZ', %d)\n", DriverServiceName, UnloadPnpDrivers);
1143
1144 PAGED_CODE();
1145
1146 /*
1147 * Get the service name from the registry key name
1148 */
1149
1150 Start = wcsrchr(DriverServiceName->Buffer, L'\\');
1151 if (Start == NULL)
1152 Start = DriverServiceName->Buffer;
1153 else
1154 Start++;
1155
1156 RtlInitUnicodeString(&ServiceName, Start);
1157
1158 /*
1159 * Construct the driver object name
1160 */
1161
1162 ObjectName.Length = (wcslen(Start) + 8) * sizeof(WCHAR);
1163 ObjectName.MaximumLength = ObjectName.Length + sizeof(WCHAR);
1164 ObjectName.Buffer = ExAllocatePool(PagedPool, ObjectName.MaximumLength);
1165 if (!ObjectName.Buffer) return STATUS_INSUFFICIENT_RESOURCES;
1166 wcscpy(ObjectName.Buffer, L"\\Driver\\");
1167 memcpy(ObjectName.Buffer + 8, Start, ObjectName.Length - 8 * sizeof(WCHAR));
1168 ObjectName.Buffer[ObjectName.Length/sizeof(WCHAR)] = 0;
1169
1170 /*
1171 * Find the driver object
1172 */
1173 Status = ObReferenceObjectByName(&ObjectName,
1174 0,
1175 0,
1176 0,
1177 IoDriverObjectType,
1178 KernelMode,
1179 0,
1180 (PVOID*)&DriverObject);
1181
1182 /*
1183 * Free the buffer for driver object name
1184 */
1185 ExFreePool(ObjectName.Buffer);
1186
1187 if (!NT_SUCCESS(Status))
1188 {
1189 DPRINT1("Can't locate driver object for %wZ\n", &ObjectName);
1190 return Status;
1191 }
1192
1193 /* Check that driver is not already unloading */
1194 if (DriverObject->Flags & DRVO_UNLOAD_INVOKED)
1195 {
1196 DPRINT1("Driver deletion pending\n");
1197 ObDereferenceObject(DriverObject);
1198 return STATUS_DELETE_PENDING;
1199 }
1200
1201 /*
1202 * Get path of service...
1203 */
1204
1205 RtlZeroMemory(QueryTable, sizeof(QueryTable));
1206
1207 RtlInitUnicodeString(&ImagePath, NULL);
1208
1209 QueryTable[0].Name = L"ImagePath";
1210 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
1211 QueryTable[0].EntryContext = &ImagePath;
1212
1213 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
1214 DriverServiceName->Buffer, QueryTable, NULL, NULL);
1215
1216 if (!NT_SUCCESS(Status))
1217 {
1218 DPRINT1("RtlQueryRegistryValues() failed (Status %x)\n", Status);
1219 ObDereferenceObject(DriverObject);
1220 return Status;
1221 }
1222
1223 /*
1224 * Normalize the image path for all later processing.
1225 */
1226
1227 Status = IopNormalizeImagePath(&ImagePath, &ServiceName);
1228
1229 if (!NT_SUCCESS(Status))
1230 {
1231 DPRINT1("IopNormalizeImagePath() failed (Status %x)\n", Status);
1232 ObDereferenceObject(DriverObject);
1233 return Status;
1234 }
1235
1236 /*
1237 * Free the service path
1238 */
1239
1240 ExFreePool(ImagePath.Buffer);
1241
1242 /*
1243 * Unload the module and release the references to the device object
1244 */
1245
1246 /* Call the load/unload routine, depending on current process */
1247 if (DriverObject->DriverUnload && DriverObject->DriverSection)
1248 {
1249 /* Loop through each device object of the driver
1250 and set DOE_UNLOAD_PENDING flag */
1251 DeviceObject = DriverObject->DeviceObject;
1252 while (DeviceObject)
1253 {
1254 /* Set the unload pending flag for the device */
1255 DeviceExtension = IoGetDevObjExtension(DeviceObject);
1256 DeviceExtension->ExtensionFlags |= DOE_UNLOAD_PENDING;
1257
1258 /* Make sure there are no attached devices or no reference counts */
1259 if ((DeviceObject->ReferenceCount) || (DeviceObject->AttachedDevice))
1260 {
1261 /* Not safe to unload */
1262 DPRINT1("Drivers device object is referenced or has attached devices\n");
1263
1264 SafeToUnload = FALSE;
1265 }
1266
1267 DeviceObject = DeviceObject->NextDevice;
1268 }
1269
1270 /* If not safe to unload, then return success */
1271 if (!SafeToUnload)
1272 {
1273 ObDereferenceObject(DriverObject);
1274 return STATUS_SUCCESS;
1275 }
1276
1277 /* Set the unload invoked flag */
1278 DriverObject->Flags |= DRVO_UNLOAD_INVOKED;
1279
1280 if (PsGetCurrentProcess() == PsInitialSystemProcess)
1281 {
1282 /* Just call right away */
1283 (*DriverObject->DriverUnload)(DriverObject);
1284 }
1285 else
1286 {
1287 /* Load/Unload must be called from system process */
1288
1289 /* Prepare parameters block */
1290 LoadParams.DriverObject = DriverObject;
1291 KeInitializeEvent(&LoadParams.Event, NotificationEvent, FALSE);
1292
1293 ExInitializeWorkItem(&LoadParams.WorkItem,
1294 (PWORKER_THREAD_ROUTINE)IopLoadUnloadDriver,
1295 (PVOID)&LoadParams);
1296
1297 /* Queue it */
1298 ExQueueWorkItem(&LoadParams.WorkItem, DelayedWorkQueue);
1299
1300 /* And wait when it completes */
1301 KeWaitForSingleObject(&LoadParams.Event, UserRequest, KernelMode,
1302 FALSE, NULL);
1303 }
1304
1305 /* Mark the driver object temporary, so it could be deleted later */
1306 ObMakeTemporaryObject(DriverObject);
1307
1308 /* Dereference it 2 times */
1309 ObDereferenceObject(DriverObject);
1310 ObDereferenceObject(DriverObject);
1311
1312 return STATUS_SUCCESS;
1313 }
1314 else
1315 {
1316 /* Dereference one time (refd inside this function) */
1317 ObDereferenceObject(DriverObject);
1318
1319 /* Return unloading failure */
1320 return STATUS_INVALID_DEVICE_REQUEST;
1321 }
1322 }
1323
1324 VOID
1325 NTAPI
1326 IopReinitializeDrivers(VOID)
1327 {
1328 PDRIVER_REINIT_ITEM ReinitItem;
1329 PLIST_ENTRY Entry;
1330
1331 /* Get the first entry and start looping */
1332 Entry = ExInterlockedRemoveHeadList(&DriverReinitListHead,
1333 &DriverReinitListLock);
1334 while (Entry)
1335 {
1336 /* Get the item*/
1337 ReinitItem = CONTAINING_RECORD(Entry, DRIVER_REINIT_ITEM, ItemEntry);
1338
1339 /* Increment reinitialization counter */
1340 ReinitItem->DriverObject->DriverExtension->Count++;
1341
1342 /* Remove the device object flag */
1343 ReinitItem->DriverObject->Flags &= ~DRVO_REINIT_REGISTERED;
1344
1345 /* Call the routine */
1346 ReinitItem->ReinitRoutine(ReinitItem->DriverObject,
1347 ReinitItem->Context,
1348 ReinitItem->DriverObject->
1349 DriverExtension->Count);
1350
1351 /* Free the entry */
1352 ExFreePool(Entry);
1353
1354 /* Move to the next one */
1355 Entry = ExInterlockedRemoveHeadList(&DriverReinitListHead,
1356 &DriverReinitListLock);
1357 }
1358 }
1359
1360 VOID
1361 NTAPI
1362 IopReinitializeBootDrivers(VOID)
1363 {
1364 PDRIVER_REINIT_ITEM ReinitItem;
1365 PLIST_ENTRY Entry;
1366
1367 /* Get the first entry and start looping */
1368 Entry = ExInterlockedRemoveHeadList(&DriverBootReinitListHead,
1369 &DriverBootReinitListLock);
1370 while (Entry)
1371 {
1372 /* Get the item*/
1373 ReinitItem = CONTAINING_RECORD(Entry, DRIVER_REINIT_ITEM, ItemEntry);
1374
1375 /* Increment reinitialization counter */
1376 ReinitItem->DriverObject->DriverExtension->Count++;
1377
1378 /* Remove the device object flag */
1379 ReinitItem->DriverObject->Flags &= ~DRVO_BOOTREINIT_REGISTERED;
1380
1381 /* Call the routine */
1382 ReinitItem->ReinitRoutine(ReinitItem->DriverObject,
1383 ReinitItem->Context,
1384 ReinitItem->DriverObject->
1385 DriverExtension->Count);
1386
1387 /* Free the entry */
1388 ExFreePool(Entry);
1389
1390 /* Move to the next one */
1391 Entry = ExInterlockedRemoveHeadList(&DriverBootReinitListHead,
1392 &DriverBootReinitListLock);
1393 }
1394 }
1395
1396 NTSTATUS
1397 NTAPI
1398 IopCreateDriver(IN PUNICODE_STRING DriverName OPTIONAL,
1399 IN PDRIVER_INITIALIZE InitializationFunction,
1400 IN PUNICODE_STRING RegistryPath,
1401 IN PVOID DllBase,
1402 IN ULONG SizeOfImage,
1403 OUT PDRIVER_OBJECT *pDriverObject)
1404 {
1405 WCHAR NameBuffer[100];
1406 USHORT NameLength;
1407 UNICODE_STRING LocalDriverName;
1408 NTSTATUS Status;
1409 OBJECT_ATTRIBUTES ObjectAttributes;
1410 ULONG ObjectSize;
1411 PDRIVER_OBJECT DriverObject;
1412 UNICODE_STRING ServiceKeyName;
1413 HANDLE hDriver;
1414 ULONG i, RetryCount = 0;
1415
1416 try_again:
1417 /* First, create a unique name for the driver if we don't have one */
1418 if (!DriverName)
1419 {
1420 /* Create a random name and set up the string*/
1421 NameLength = (USHORT)swprintf(NameBuffer,
1422 L"\\Driver\\%08u",
1423 KeTickCount);
1424 LocalDriverName.Length = NameLength * sizeof(WCHAR);
1425 LocalDriverName.MaximumLength = LocalDriverName.Length + sizeof(UNICODE_NULL);
1426 LocalDriverName.Buffer = NameBuffer;
1427 }
1428 else
1429 {
1430 /* So we can avoid another code path, use a local var */
1431 LocalDriverName = *DriverName;
1432 }
1433
1434 /* Initialize the Attributes */
1435 ObjectSize = sizeof(DRIVER_OBJECT) + sizeof(EXTENDED_DRIVER_EXTENSION);
1436 InitializeObjectAttributes(&ObjectAttributes,
1437 &LocalDriverName,
1438 OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
1439 NULL,
1440 NULL);
1441
1442 /* Create the Object */
1443 Status = ObCreateObject(KernelMode,
1444 IoDriverObjectType,
1445 &ObjectAttributes,
1446 KernelMode,
1447 NULL,
1448 ObjectSize,
1449 0,
1450 0,
1451 (PVOID*)&DriverObject);
1452 if (!NT_SUCCESS(Status)) return Status;
1453
1454 DPRINT("IopCreateDriver(): created DO %p\n", DriverObject);
1455
1456 /* Set up the Object */
1457 RtlZeroMemory(DriverObject, ObjectSize);
1458 DriverObject->Type = IO_TYPE_DRIVER;
1459 DriverObject->Size = sizeof(DRIVER_OBJECT);
1460 DriverObject->Flags = DRVO_LEGACY_DRIVER;//DRVO_BUILTIN_DRIVER;
1461 DriverObject->DriverExtension = (PDRIVER_EXTENSION)(DriverObject + 1);
1462 DriverObject->DriverExtension->DriverObject = DriverObject;
1463 DriverObject->DriverInit = InitializationFunction;
1464
1465 /* Loop all Major Functions */
1466 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
1467 {
1468 /* Invalidate each function */
1469 DriverObject->MajorFunction[i] = IopInvalidDeviceRequest;
1470 }
1471
1472 /* Set up the service key name buffer */
1473 ServiceKeyName.Buffer = ExAllocatePoolWithTag(PagedPool,
1474 LocalDriverName.Length +
1475 sizeof(WCHAR),
1476 TAG_IO);
1477 if (!ServiceKeyName.Buffer)
1478 {
1479 /* Fail */
1480 ObMakeTemporaryObject(DriverObject);
1481 ObDereferenceObject(DriverObject);
1482 return STATUS_INSUFFICIENT_RESOURCES;
1483 }
1484
1485 /* Fill out the key data and copy the buffer */
1486 ServiceKeyName.Length = LocalDriverName.Length;
1487 ServiceKeyName.MaximumLength = LocalDriverName.MaximumLength;
1488 RtlCopyMemory(ServiceKeyName.Buffer,
1489 LocalDriverName.Buffer,
1490 LocalDriverName.Length);
1491
1492 /* Null-terminate it and set it */
1493 ServiceKeyName.Buffer[ServiceKeyName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1494 DriverObject->DriverExtension->ServiceKeyName = ServiceKeyName;
1495
1496 /* Also store it in the Driver Object. This is a bit of a hack. */
1497 RtlCopyMemory(&DriverObject->DriverName,
1498 &ServiceKeyName,
1499 sizeof(UNICODE_STRING));
1500
1501 /* Add the Object and get its handle */
1502 Status = ObInsertObject(DriverObject,
1503 NULL,
1504 FILE_READ_DATA,
1505 0,
1506 NULL,
1507 &hDriver);
1508
1509 /* Eliminate small possibility when this function is called more than
1510 once in a row, and KeTickCount doesn't get enough time to change */
1511 if (!DriverName && (Status == STATUS_OBJECT_NAME_COLLISION) && (RetryCount < 100))
1512 {
1513 RetryCount++;
1514 goto try_again;
1515 }
1516
1517 if (!NT_SUCCESS(Status)) return Status;
1518
1519 /* Now reference it */
1520 Status = ObReferenceObjectByHandle(hDriver,
1521 0,
1522 IoDriverObjectType,
1523 KernelMode,
1524 (PVOID*)&DriverObject,
1525 NULL);
1526 if (!NT_SUCCESS(Status))
1527 {
1528 /* Fail */
1529 ObMakeTemporaryObject(DriverObject);
1530 ObDereferenceObject(DriverObject);
1531 return Status;
1532 }
1533
1534 /* Close the extra handle */
1535 ZwClose(hDriver);
1536
1537 DriverObject->HardwareDatabase = &IopHardwareDatabaseKey;
1538 DriverObject->DriverStart = DllBase;
1539 DriverObject->DriverSize = SizeOfImage;
1540
1541 /* Finally, call its init function */
1542 DPRINT("RegistryKey: %wZ\n", RegistryPath);
1543 DPRINT("Calling driver entrypoint at %p\n", InitializationFunction);
1544 Status = (*InitializationFunction)(DriverObject, RegistryPath);
1545 if (!NT_SUCCESS(Status))
1546 {
1547 /* If it didn't work, then kill the object */
1548 DPRINT1("'%wZ' initialization failed, status (0x%08lx)\n", DriverName, Status);
1549 ObMakeTemporaryObject(DriverObject);
1550 ObDereferenceObject(DriverObject);
1551 }
1552 else
1553 {
1554 /* Returns to caller the object */
1555 *pDriverObject = DriverObject;
1556 }
1557
1558 /* Loop all Major Functions */
1559 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
1560 {
1561 /*
1562 * Make sure the driver didn't set any dispatch entry point to NULL!
1563 * Doing so is illegal; drivers shouldn't touch entry points they
1564 * do not implement.
1565 */
1566 ASSERT(DriverObject->MajorFunction[i] != NULL);
1567
1568 /* Check if it did so anyway */
1569 if (!DriverObject->MajorFunction[i])
1570 {
1571 /* Fix it up */
1572 DriverObject->MajorFunction[i] = IopInvalidDeviceRequest;
1573 }
1574 }
1575
1576 /* Return the Status */
1577 return Status;
1578 }
1579
1580 /* PUBLIC FUNCTIONS ***********************************************************/
1581
1582 /*
1583 * @implemented
1584 */
1585 NTSTATUS
1586 NTAPI
1587 IoCreateDriver(IN PUNICODE_STRING DriverName OPTIONAL,
1588 IN PDRIVER_INITIALIZE InitializationFunction)
1589 {
1590 PDRIVER_OBJECT DriverObject;
1591 return IopCreateDriver(DriverName, InitializationFunction, NULL, 0, 0, &DriverObject);
1592 }
1593
1594 /*
1595 * @implemented
1596 */
1597 VOID
1598 NTAPI
1599 IoDeleteDriver(IN PDRIVER_OBJECT DriverObject)
1600 {
1601 /* Simply dereference the Object */
1602 ObDereferenceObject(DriverObject);
1603 }
1604
1605 /*
1606 * @implemented
1607 */
1608 VOID
1609 NTAPI
1610 IoRegisterBootDriverReinitialization(IN PDRIVER_OBJECT DriverObject,
1611 IN PDRIVER_REINITIALIZE ReinitRoutine,
1612 IN PVOID Context)
1613 {
1614 PDRIVER_REINIT_ITEM ReinitItem;
1615
1616 /* Allocate the entry */
1617 ReinitItem = ExAllocatePoolWithTag(NonPagedPool,
1618 sizeof(DRIVER_REINIT_ITEM),
1619 TAG_REINIT);
1620 if (!ReinitItem) return;
1621
1622 /* Fill it out */
1623 ReinitItem->DriverObject = DriverObject;
1624 ReinitItem->ReinitRoutine = ReinitRoutine;
1625 ReinitItem->Context = Context;
1626
1627 /* Set the Driver Object flag and insert the entry into the list */
1628 DriverObject->Flags |= DRVO_BOOTREINIT_REGISTERED;
1629 ExInterlockedInsertTailList(&DriverBootReinitListHead,
1630 &ReinitItem->ItemEntry,
1631 &DriverBootReinitListLock);
1632 }
1633
1634 /*
1635 * @implemented
1636 */
1637 VOID
1638 NTAPI
1639 IoRegisterDriverReinitialization(IN PDRIVER_OBJECT DriverObject,
1640 IN PDRIVER_REINITIALIZE ReinitRoutine,
1641 IN PVOID Context)
1642 {
1643 PDRIVER_REINIT_ITEM ReinitItem;
1644
1645 /* Allocate the entry */
1646 ReinitItem = ExAllocatePoolWithTag(NonPagedPool,
1647 sizeof(DRIVER_REINIT_ITEM),
1648 TAG_REINIT);
1649 if (!ReinitItem) return;
1650
1651 /* Fill it out */
1652 ReinitItem->DriverObject = DriverObject;
1653 ReinitItem->ReinitRoutine = ReinitRoutine;
1654 ReinitItem->Context = Context;
1655
1656 /* Set the Driver Object flag and insert the entry into the list */
1657 DriverObject->Flags |= DRVO_REINIT_REGISTERED;
1658 ExInterlockedInsertTailList(&DriverReinitListHead,
1659 &ReinitItem->ItemEntry,
1660 &DriverReinitListLock);
1661 }
1662
1663 /*
1664 * @implemented
1665 */
1666 NTSTATUS
1667 NTAPI
1668 IoAllocateDriverObjectExtension(IN PDRIVER_OBJECT DriverObject,
1669 IN PVOID ClientIdentificationAddress,
1670 IN ULONG DriverObjectExtensionSize,
1671 OUT PVOID *DriverObjectExtension)
1672 {
1673 KIRQL OldIrql;
1674 PIO_CLIENT_EXTENSION DriverExtensions, NewDriverExtension;
1675 BOOLEAN Inserted = FALSE;
1676
1677 /* Assume failure */
1678 *DriverObjectExtension = NULL;
1679
1680 /* Allocate the extension */
1681 NewDriverExtension = ExAllocatePoolWithTag(NonPagedPool,
1682 sizeof(IO_CLIENT_EXTENSION) +
1683 DriverObjectExtensionSize,
1684 TAG_DRIVER_EXTENSION);
1685 if (!NewDriverExtension) return STATUS_INSUFFICIENT_RESOURCES;
1686
1687 /* Clear the extension for teh caller */
1688 RtlZeroMemory(NewDriverExtension,
1689 sizeof(IO_CLIENT_EXTENSION) + DriverObjectExtensionSize);
1690
1691 /* Acqure lock */
1692 OldIrql = KeRaiseIrqlToDpcLevel();
1693
1694 /* Fill out the extension */
1695 NewDriverExtension->ClientIdentificationAddress = ClientIdentificationAddress;
1696
1697 /* Loop the current extensions */
1698 DriverExtensions = IoGetDrvObjExtension(DriverObject)->
1699 ClientDriverExtension;
1700 while (DriverExtensions)
1701 {
1702 /* Check if the identifier matches */
1703 if (DriverExtensions->ClientIdentificationAddress ==
1704 ClientIdentificationAddress)
1705 {
1706 /* We have a collision, break out */
1707 break;
1708 }
1709
1710 /* Go to the next one */
1711 DriverExtensions = DriverExtensions->NextExtension;
1712 }
1713
1714 /* Check if we didn't collide */
1715 if (!DriverExtensions)
1716 {
1717 /* Link this one in */
1718 NewDriverExtension->NextExtension =
1719 IoGetDrvObjExtension(DriverObject)->ClientDriverExtension;
1720 IoGetDrvObjExtension(DriverObject)->ClientDriverExtension =
1721 NewDriverExtension;
1722 Inserted = TRUE;
1723 }
1724
1725 /* Release the lock */
1726 KeLowerIrql(OldIrql);
1727
1728 /* Check if insertion failed */
1729 if (!Inserted)
1730 {
1731 /* Free the entry and fail */
1732 ExFreePool(NewDriverExtension);
1733 return STATUS_OBJECT_NAME_COLLISION;
1734 }
1735
1736 /* Otherwise, return the pointer */
1737 *DriverObjectExtension = NewDriverExtension + 1;
1738 return STATUS_SUCCESS;
1739 }
1740
1741 /*
1742 * @implemented
1743 */
1744 PVOID
1745 NTAPI
1746 IoGetDriverObjectExtension(IN PDRIVER_OBJECT DriverObject,
1747 IN PVOID ClientIdentificationAddress)
1748 {
1749 KIRQL OldIrql;
1750 PIO_CLIENT_EXTENSION DriverExtensions;
1751
1752 /* Acquire lock */
1753 OldIrql = KeRaiseIrqlToDpcLevel();
1754
1755 /* Loop the list until we find the right one */
1756 DriverExtensions = IoGetDrvObjExtension(DriverObject)->ClientDriverExtension;
1757 while (DriverExtensions)
1758 {
1759 /* Check for a match */
1760 if (DriverExtensions->ClientIdentificationAddress ==
1761 ClientIdentificationAddress)
1762 {
1763 /* Break out */
1764 break;
1765 }
1766
1767 /* Keep looping */
1768 DriverExtensions = DriverExtensions->NextExtension;
1769 }
1770
1771 /* Release lock */
1772 KeLowerIrql(OldIrql);
1773
1774 /* Return nothing or the extension */
1775 if (!DriverExtensions) return NULL;
1776 return DriverExtensions + 1;
1777 }
1778
1779 VOID NTAPI
1780 IopLoadUnloadDriver(PLOAD_UNLOAD_PARAMS LoadParams)
1781 {
1782 RTL_QUERY_REGISTRY_TABLE QueryTable[3];
1783 UNICODE_STRING ImagePath;
1784 UNICODE_STRING ServiceName;
1785 NTSTATUS Status;
1786 ULONG Type;
1787 PDEVICE_NODE DeviceNode;
1788 PDRIVER_OBJECT DriverObject;
1789 PLDR_DATA_TABLE_ENTRY ModuleObject;
1790 PVOID BaseAddress;
1791 WCHAR *cur;
1792
1793 /* Check if it's an unload request */
1794 if (LoadParams->DriverObject)
1795 {
1796 (*LoadParams->DriverObject->DriverUnload)(LoadParams->DriverObject);
1797
1798 /* Return success and signal the event */
1799 LoadParams->Status = STATUS_SUCCESS;
1800 (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
1801 return;
1802 }
1803
1804 RtlInitUnicodeString(&ImagePath, NULL);
1805
1806 /*
1807 * Get the service name from the registry key name.
1808 */
1809 ASSERT(LoadParams->ServiceName->Length >= sizeof(WCHAR));
1810
1811 ServiceName = *LoadParams->ServiceName;
1812 cur = LoadParams->ServiceName->Buffer +
1813 (LoadParams->ServiceName->Length / sizeof(WCHAR)) - 1;
1814 while (LoadParams->ServiceName->Buffer != cur)
1815 {
1816 if(*cur == L'\\')
1817 {
1818 ServiceName.Buffer = cur + 1;
1819 ServiceName.Length = LoadParams->ServiceName->Length -
1820 (USHORT)((ULONG_PTR)ServiceName.Buffer -
1821 (ULONG_PTR)LoadParams->ServiceName->Buffer);
1822 break;
1823 }
1824 cur--;
1825 }
1826
1827 IopDisplayLoadingMessage(&ServiceName);
1828
1829 /*
1830 * Get service type.
1831 */
1832
1833 RtlZeroMemory(&QueryTable, sizeof(QueryTable));
1834
1835 RtlInitUnicodeString(&ImagePath, NULL);
1836
1837 QueryTable[0].Name = L"Type";
1838 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
1839 QueryTable[0].EntryContext = &Type;
1840
1841 QueryTable[1].Name = L"ImagePath";
1842 QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
1843 QueryTable[1].EntryContext = &ImagePath;
1844
1845 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
1846 LoadParams->ServiceName->Buffer, QueryTable, NULL, NULL);
1847
1848 if (!NT_SUCCESS(Status))
1849 {
1850 DPRINT("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
1851 if (ImagePath.Buffer)
1852 ExFreePool(ImagePath.Buffer);
1853 LoadParams->Status = Status;
1854 (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
1855 return;
1856 }
1857
1858 /*
1859 * Normalize the image path for all later processing.
1860 */
1861
1862 Status = IopNormalizeImagePath(&ImagePath, &ServiceName);
1863
1864 if (!NT_SUCCESS(Status))
1865 {
1866 DPRINT("IopNormalizeImagePath() failed (Status %x)\n", Status);
1867 LoadParams->Status = Status;
1868 (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
1869 return;
1870 }
1871
1872 DPRINT("FullImagePath: '%wZ'\n", &ImagePath);
1873 DPRINT("Type: %lx\n", Type);
1874
1875 /*
1876 * Create device node
1877 */
1878
1879 /* Use IopRootDeviceNode for now */
1880 Status = IopCreateDeviceNode(IopRootDeviceNode, NULL, &ServiceName, &DeviceNode);
1881
1882 if (!NT_SUCCESS(Status))
1883 {
1884 DPRINT("IopCreateDeviceNode() failed (Status %lx)\n", Status);
1885 LoadParams->Status = Status;
1886 (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
1887 return;
1888 }
1889
1890 /* Get existing DriverObject pointer (in case the driver has
1891 already been loaded and initialized) */
1892 Status = IopGetDriverObject(
1893 &DriverObject,
1894 &ServiceName,
1895 (Type == 2 /* SERVICE_FILE_SYSTEM_DRIVER */ ||
1896 Type == 8 /* SERVICE_RECOGNIZER_DRIVER */));
1897
1898 if (!NT_SUCCESS(Status))
1899 {
1900 /*
1901 * Load the driver module
1902 */
1903
1904 Status = MmLoadSystemImage(&ImagePath, NULL, NULL, 0, (PVOID)&ModuleObject, &BaseAddress);
1905 if (!NT_SUCCESS(Status) && Status != STATUS_IMAGE_ALREADY_LOADED)
1906 {
1907 DPRINT("MmLoadSystemImage() failed (Status %lx)\n", Status);
1908 IopFreeDeviceNode(DeviceNode);
1909 LoadParams->Status = Status;
1910 (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
1911 return;
1912 }
1913
1914 /*
1915 * Set a service name for the device node
1916 */
1917
1918 RtlCreateUnicodeString(&DeviceNode->ServiceName, ServiceName.Buffer);
1919
1920 /*
1921 * Initialize the driver module if it's loaded for the first time
1922 */
1923 if (Status != STATUS_IMAGE_ALREADY_LOADED)
1924 {
1925 Status = IopInitializeDriverModule(
1926 DeviceNode,
1927 ModuleObject,
1928 &DeviceNode->ServiceName,
1929 (Type == 2 /* SERVICE_FILE_SYSTEM_DRIVER */ ||
1930 Type == 8 /* SERVICE_RECOGNIZER_DRIVER */),
1931 &DriverObject);
1932
1933 if (!NT_SUCCESS(Status))
1934 {
1935 DPRINT("IopInitializeDriver() failed (Status %lx)\n", Status);
1936 MmUnloadSystemImage(ModuleObject);
1937 IopFreeDeviceNode(DeviceNode);
1938 LoadParams->Status = Status;
1939 (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
1940 return;
1941 }
1942 }
1943
1944 /* Store its DriverSection, so that it could be unloaded */
1945 DriverObject->DriverSection = ModuleObject;
1946
1947 /* Initialize and start device */
1948 IopInitializeDevice(DeviceNode, DriverObject);
1949 Status = IopStartDevice(DeviceNode);
1950 }
1951 else
1952 {
1953 DPRINT("DriverObject already exist in ObjectManager\n");
1954
1955 /* IopGetDriverObject references the DriverObject, so dereference it */
1956 ObDereferenceObject(DriverObject);
1957
1958 /* Free device node since driver loading failed */
1959 IopFreeDeviceNode(DeviceNode);
1960 }
1961
1962 /* Pass status to the caller and signal the event */
1963 LoadParams->Status = Status;
1964 (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
1965 }
1966
1967 /*
1968 * NtLoadDriver
1969 *
1970 * Loads a device driver.
1971 *
1972 * Parameters
1973 * DriverServiceName
1974 * Name of the service to load (registry key).
1975 *
1976 * Return Value
1977 * Status
1978 *
1979 * Status
1980 * implemented
1981 */
1982 NTSTATUS NTAPI
1983 NtLoadDriver(IN PUNICODE_STRING DriverServiceName)
1984 {
1985 UNICODE_STRING CapturedDriverServiceName = { 0, 0, NULL };
1986 KPROCESSOR_MODE PreviousMode;
1987 LOAD_UNLOAD_PARAMS LoadParams;
1988 NTSTATUS Status;
1989
1990 PAGED_CODE();
1991
1992 PreviousMode = KeGetPreviousMode();
1993
1994 /*
1995 * Check security privileges
1996 */
1997
1998 /* FIXME: Uncomment when privileges will be correctly implemented. */
1999 #if 0
2000 if (!SeSinglePrivilegeCheck(SeLoadDriverPrivilege, PreviousMode))
2001 {
2002 DPRINT("Privilege not held\n");
2003 return STATUS_PRIVILEGE_NOT_HELD;
2004 }
2005 #endif
2006
2007 Status = ProbeAndCaptureUnicodeString(&CapturedDriverServiceName,
2008 PreviousMode,
2009 DriverServiceName);
2010 if (!NT_SUCCESS(Status))
2011 {
2012 return Status;
2013 }
2014
2015 DPRINT("NtLoadDriver('%wZ')\n", &CapturedDriverServiceName);
2016
2017 LoadParams.ServiceName = &CapturedDriverServiceName;
2018 LoadParams.DriverObject = NULL;
2019 KeInitializeEvent(&LoadParams.Event, NotificationEvent, FALSE);
2020
2021 /* Call the load/unload routine, depending on current process */
2022 if (PsGetCurrentProcess() == PsInitialSystemProcess)
2023 {
2024 /* Just call right away */
2025 IopLoadUnloadDriver(&LoadParams);
2026 }
2027 else
2028 {
2029 /* Load/Unload must be called from system process */
2030 ExInitializeWorkItem(&LoadParams.WorkItem,
2031 (PWORKER_THREAD_ROUTINE)IopLoadUnloadDriver,
2032 (PVOID)&LoadParams);
2033
2034 /* Queue it */
2035 ExQueueWorkItem(&LoadParams.WorkItem, DelayedWorkQueue);
2036
2037 /* And wait when it completes */
2038 KeWaitForSingleObject(&LoadParams.Event, UserRequest, KernelMode,
2039 FALSE, NULL);
2040 }
2041
2042 ReleaseCapturedUnicodeString(&CapturedDriverServiceName,
2043 PreviousMode);
2044
2045 return LoadParams.Status;
2046 }
2047
2048 /*
2049 * NtUnloadDriver
2050 *
2051 * Unloads a legacy device driver.
2052 *
2053 * Parameters
2054 * DriverServiceName
2055 * Name of the service to unload (registry key).
2056 *
2057 * Return Value
2058 * Status
2059 *
2060 * Status
2061 * implemented
2062 */
2063
2064 NTSTATUS NTAPI
2065 NtUnloadDriver(IN PUNICODE_STRING DriverServiceName)
2066 {
2067 return IopUnloadDriver(DriverServiceName, FALSE);
2068 }
2069
2070 /* EOF */