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