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