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