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