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