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