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