1 /*
2 * PROJECT: EFI Windows Loader
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: boot/freeldr/freeldr/windows/wlregistry.c
5 * PURPOSE: Registry support functions
6 * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org)
7 */
8
9 /* INCLUDES ***************************************************************/
10
11 #include <freeldr.h>
12 #include "winldr.h"
13 #include "registry.h"
14 #include <internal/cmboot.h>
15
16 #include <debug.h>
17 DBG_DEFAULT_CHANNEL(WINDOWS);
18
19 // The only global var here, used to mark mem pages as NLS in WinLdrSetupMemoryLayout()
20 ULONG TotalNLSSize = 0;
21
22 static BOOLEAN
23 WinLdrGetNLSNames(
24 _In_ HKEY ControlSet,
25 _Inout_ PUNICODE_STRING AnsiFileName,
26 _Inout_ PUNICODE_STRING OemFileName,
27 _Inout_ PUNICODE_STRING LangFileName, // CaseTable
28 _Inout_ PUNICODE_STRING OemHalFileName);
29
30 static BOOLEAN
31 WinLdrScanRegistry(
32 IN OUT PLIST_ENTRY BootDriverListHead);
33
34 typedef enum _BAD_HIVE_REASON
35 {
36 GoodHive = 1,
37 CorruptHive,
38 NoHive,
39 NoHiveAlloc
40 } BAD_HIVE_REASON, *PBAD_HIVE_REASON;
41
42 /* FUNCTIONS **************************************************************/
43
44 static BOOLEAN
WinLdrLoadSystemHive(_Inout_ PLOADER_PARAMETER_BLOCK LoaderBlock,_In_ PCSTR DirectoryPath,_In_ PCSTR HiveName,_Out_ PBAD_HIVE_REASON Reason)45 WinLdrLoadSystemHive(
46 _Inout_ PLOADER_PARAMETER_BLOCK LoaderBlock,
47 _In_ PCSTR DirectoryPath,
48 _In_ PCSTR HiveName,
49 _Out_ PBAD_HIVE_REASON Reason)
50 {
51 ULONG FileId;
52 CHAR FullHiveName[MAX_PATH];
53 ARC_STATUS Status;
54 FILEINFORMATION FileInfo;
55 ULONG HiveFileSize;
56 PVOID HiveDataPhysical;
57 PVOID HiveDataVirtual;
58 ULONG BytesRead;
59
60 /* Do not setup any bad reason for now */
61 *Reason = GoodHive;
62
63 /* Concatenate path and filename to get the full name */
64 RtlStringCbCopyA(FullHiveName, sizeof(FullHiveName), DirectoryPath);
65 RtlStringCbCatA(FullHiveName, sizeof(FullHiveName), HiveName);
66
67 NtLdrOutputLoadMsg(FullHiveName, NULL);
68 Status = ArcOpen(FullHiveName, OpenReadOnly, &FileId);
69 if (Status != ESUCCESS)
70 {
71 WARN("Error while opening '%s', Status: %u\n", FullHiveName, Status);
72 *Reason = NoHive;
73 return FALSE;
74 }
75
76 /* Get the file length */
77 Status = ArcGetFileInformation(FileId, &FileInfo);
78 if (Status != ESUCCESS)
79 {
80 WARN("Hive file has 0 size!\n");
81 *Reason = CorruptHive;
82 ArcClose(FileId);
83 return FALSE;
84 }
85 HiveFileSize = FileInfo.EndingAddress.LowPart;
86
87 /* Round up the size to page boundary and alloc memory */
88 HiveDataPhysical = MmAllocateMemoryWithType(
89 MM_SIZE_TO_PAGES(HiveFileSize + MM_PAGE_SIZE - 1) << MM_PAGE_SHIFT,
90 LoaderRegistryData);
91
92 if (HiveDataPhysical == NULL)
93 {
94 WARN("Could not alloc memory for hive!\n");
95 *Reason = NoHiveAlloc;
96 ArcClose(FileId);
97 return FALSE;
98 }
99
100 /* Convert address to virtual */
101 HiveDataVirtual = PaToVa(HiveDataPhysical);
102
103 /* Fill LoaderBlock's entries */
104 LoaderBlock->RegistryLength = HiveFileSize;
105 LoaderBlock->RegistryBase = HiveDataVirtual;
106
107 /* Finally read from file to the memory */
108 Status = ArcRead(FileId, HiveDataPhysical, HiveFileSize, &BytesRead);
109 if (Status != ESUCCESS)
110 {
111 WARN("Error while reading '%s', Status: %u\n", FullHiveName, Status);
112 *Reason = CorruptHive;
113 ArcClose(FileId);
114 return FALSE;
115 }
116
117 // FIXME: HACK: Get the boot filesystem driver name now...
118 BootFileSystem = FsGetServiceName(FileId);
119
120 ArcClose(FileId);
121 return TRUE;
122 }
123
124 BOOLEAN
WinLdrInitSystemHive(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,IN PCSTR SystemRoot,IN BOOLEAN Setup)125 WinLdrInitSystemHive(
126 IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
127 IN PCSTR SystemRoot,
128 IN BOOLEAN Setup)
129 {
130 CHAR SearchPath[1024];
131 PVOID ChunkBase;
132 PCSTR HiveName;
133 BOOLEAN Success;
134 BAD_HIVE_REASON Reason;
135
136 /* Load the corresponding text-mode setup system hive or the standard hive */
137 if (Setup)
138 {
139 RtlStringCbCopyA(SearchPath, sizeof(SearchPath), SystemRoot);
140 HiveName = "SETUPREG.HIV";
141 }
142 else
143 {
144 RtlStringCbCopyA(SearchPath, sizeof(SearchPath), SystemRoot);
145 RtlStringCbCatA(SearchPath, sizeof(SearchPath), "system32\\config\\");
146 HiveName = "SYSTEM";
147 }
148
149 TRACE("WinLdrInitSystemHive: loading hive %s%s\n", SearchPath, HiveName);
150 Success = WinLdrLoadSystemHive(LoaderBlock, SearchPath, HiveName, &Reason);
151 if (!Success)
152 {
153 /* Check whether the SYSTEM hive does not exist or is too corrupt to be read */
154 if (Reason == CorruptHive || Reason == NoHive)
155 {
156 /* Try loading the alternate hive, the main hive should be recovered later */
157 goto LoadAlternateHive;
158 }
159
160 /* We are failing for other reason, bail out */
161 UiMessageBox("Could not load %s hive!", HiveName);
162 return FALSE;
163 }
164
165 /* Import what was loaded */
166 Success = RegImportBinaryHive(VaToPa(LoaderBlock->RegistryBase), LoaderBlock->RegistryLength, SearchPath, FALSE);
167 if (!Success)
168 {
169 /*
170 * Importing of the SYSTEM hive failed. The scenarios that would
171 * have made this possible are the following:
172 *
173 * 1. The primary hive is corrupt beyond repair (such as when
174 * core FS structures are total toast);
175 *
176 * 2. Repairing the hive could with a LOG could not recover it
177 * to the fullest. This is the case when the hive and LOG have
178 * diverged too much, or are mismatched, or the containing healthy
179 * data in the LOG was not marked as dirty that could be copied
180 * into the primary hive;
181 *
182 * 3. LOG is bad (e.g. corrupt dirty vector);
183 *
184 * 4. LOG does not physically exist on the backing storage.
185 *
186 * 5. SYSTEM hive does not physically exist or it is a 0 bytes file
187 * (the latter case still counts as corruption).
188 *
189 * With the hope to boot the system, load the mirror counterpart
190 * of the main hive, the alternate. The kernel should be able to recover
191 * the main hive later on as soon as it starts writing to it.
192 */
193 LoadAlternateHive:
194 HiveName = "SYSTEM.ALT";
195 Success = WinLdrLoadSystemHive(LoaderBlock, SearchPath, HiveName, &Reason);
196 if (!Success)
197 {
198 UiMessageBox("Could not load %s hive!", HiveName);
199 return FALSE;
200 }
201
202 /* Retry importing it again */
203 Success = RegImportBinaryHive(VaToPa(LoaderBlock->RegistryBase), LoaderBlock->RegistryLength, SearchPath, TRUE);
204 if (!Success)
205 {
206 UiMessageBox("Importing binary hive failed!");
207 return FALSE;
208 }
209
210 /*
211 * Acknowledge the kernel we recovered the SYSTEM hive
212 * on our side by loading the alternate variant of the hive.
213 */
214 WARN("SYSTEM hive does not exist or is corrupt and SYSTEM.ALT has been loaded!\n");
215 ChunkBase = VaToPa(LoaderBlock->RegistryBase);
216 ((PHBASE_BLOCK)ChunkBase)->BootRecover = HBOOT_BOOT_RECOVERED_BY_ALTERNATE_HIVE;
217 }
218
219 // FIXME: Load SYSTEM.SAV if GUI setup installation is still in progress
220
221 /* Initialize the 'CurrentControlSet' link */
222 if (!RegInitCurrentControlSet(FALSE))
223 {
224 UiMessageBox("Initializing CurrentControlSet link failed!");
225 return FALSE;
226 }
227
228 return TRUE;
229 }
230
WinLdrScanSystemHive(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,IN PCSTR SystemRoot)231 BOOLEAN WinLdrScanSystemHive(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
232 IN PCSTR SystemRoot)
233 {
234 BOOLEAN Success;
235 DECLARE_UNICODE_STRING_SIZE(AnsiFileName, MAX_PATH);
236 DECLARE_UNICODE_STRING_SIZE(OemFileName, MAX_PATH);
237 DECLARE_UNICODE_STRING_SIZE(LangFileName, MAX_PATH); // CaseTable
238 DECLARE_UNICODE_STRING_SIZE(OemHalFileName, MAX_PATH);
239 CHAR SearchPath[1024];
240
241 /* Scan registry and prepare boot drivers list */
242 Success = WinLdrScanRegistry(&LoaderBlock->BootDriverListHead);
243 if (!Success)
244 {
245 UiMessageBox("Failed to load boot drivers!");
246 return FALSE;
247 }
248
249 /* Get names of NLS files */
250 Success = WinLdrGetNLSNames(CurrentControlSetKey,
251 &AnsiFileName,
252 &OemFileName,
253 &LangFileName,
254 &OemHalFileName);
255 if (!Success)
256 {
257 UiMessageBox("Getting NLS names from registry failed!");
258 return FALSE;
259 }
260
261 TRACE("NLS data: '%wZ' '%wZ' '%wZ' '%wZ'\n",
262 &AnsiFileName, &OemFileName, &LangFileName, &OemHalFileName);
263
264 /* Load NLS data */
265 RtlStringCbCopyA(SearchPath, sizeof(SearchPath), SystemRoot);
266 RtlStringCbCatA(SearchPath, sizeof(SearchPath), "system32\\");
267 Success = WinLdrLoadNLSData(LoaderBlock,
268 SearchPath,
269 &AnsiFileName,
270 &OemFileName,
271 &LangFileName,
272 &OemHalFileName);
273 TRACE("NLS data loading %s\n", Success ? "successful" : "failed");
274
275 return TRUE;
276 }
277
278
279 /* PRIVATE FUNCTIONS ******************************************************/
280
281 // Queries registry for those three file names
282 static BOOLEAN
WinLdrGetNLSNames(_In_ HKEY ControlSet,_Inout_ PUNICODE_STRING AnsiFileName,_Inout_ PUNICODE_STRING OemFileName,_Inout_ PUNICODE_STRING LangFileName,_Inout_ PUNICODE_STRING OemHalFileName)283 WinLdrGetNLSNames(
284 _In_ HKEY ControlSet,
285 _Inout_ PUNICODE_STRING AnsiFileName,
286 _Inout_ PUNICODE_STRING OemFileName,
287 _Inout_ PUNICODE_STRING LangFileName, // CaseTable
288 _Inout_ PUNICODE_STRING OemHalFileName)
289 {
290 LONG rc;
291 HKEY hKey;
292 ULONG BufferSize;
293 WCHAR szIdBuffer[80];
294
295 /* Open the CodePage key */
296 rc = RegOpenKey(ControlSet, L"Control\\NLS\\CodePage", &hKey);
297 if (rc != ERROR_SUCCESS)
298 {
299 //TRACE("Couldn't open CodePage registry key\n");
300 return FALSE;
301 }
302
303 /* Get ANSI codepage file */
304 BufferSize = sizeof(szIdBuffer);
305 rc = RegQueryValue(hKey, L"ACP", NULL, (PUCHAR)szIdBuffer, &BufferSize);
306 if (rc != ERROR_SUCCESS)
307 {
308 //TRACE("Couldn't get ACP NLS setting\n");
309 goto Quit;
310 }
311
312 BufferSize = AnsiFileName->MaximumLength;
313 rc = RegQueryValue(hKey, szIdBuffer, NULL,
314 (PUCHAR)AnsiFileName->Buffer, &BufferSize);
315 if (rc != ERROR_SUCCESS)
316 {
317 //TRACE("ACP NLS Setting exists, but isn't readable\n");
318 //goto Quit;
319 AnsiFileName->Length = 0;
320 RtlAppendUnicodeToString(AnsiFileName, L"c_1252.nls"); // HACK: ReactOS bug CORE-6105
321 }
322 else
323 {
324 AnsiFileName->Length = (USHORT)BufferSize - sizeof(UNICODE_NULL);
325 }
326
327 /* Get OEM codepage file */
328 BufferSize = sizeof(szIdBuffer);
329 rc = RegQueryValue(hKey, L"OEMCP", NULL, (PUCHAR)szIdBuffer, &BufferSize);
330 if (rc != ERROR_SUCCESS)
331 {
332 //TRACE("Couldn't get OEMCP NLS setting\n");
333 goto Quit;
334 }
335
336 BufferSize = OemFileName->MaximumLength;
337 rc = RegQueryValue(hKey, szIdBuffer, NULL,
338 (PUCHAR)OemFileName->Buffer, &BufferSize);
339 if (rc != ERROR_SUCCESS)
340 {
341 //TRACE("OEMCP NLS setting exists, but isn't readable\n");
342 //goto Quit;
343 OemFileName->Length = 0;
344 RtlAppendUnicodeToString(OemFileName, L"c_437.nls"); // HACK: ReactOS bug CORE-6105
345 }
346 else
347 {
348 OemFileName->Length = (USHORT)BufferSize - sizeof(UNICODE_NULL);
349 }
350
351 /* Get OEM HAL font file */
352 BufferSize = OemHalFileName->MaximumLength;
353 rc = RegQueryValue(hKey, L"OEMHAL", NULL,
354 (PUCHAR)OemHalFileName->Buffer, &BufferSize);
355 if (rc != ERROR_SUCCESS)
356 {
357 //TRACE("Couldn't get OEMHAL NLS setting\n");
358 //goto Quit;
359 RtlInitEmptyUnicodeString(OemHalFileName, NULL, 0);
360 }
361 else
362 {
363 OemHalFileName->Length = (USHORT)BufferSize - sizeof(UNICODE_NULL);
364 }
365
366 RegCloseKey(hKey);
367
368 /* Open the Language key */
369 rc = RegOpenKey(ControlSet, L"Control\\NLS\\Language", &hKey);
370 if (rc != ERROR_SUCCESS)
371 {
372 //TRACE("Couldn't open Language registry key\n");
373 return FALSE;
374 }
375
376 /* Get the Unicode case table file */
377 BufferSize = sizeof(szIdBuffer);
378 rc = RegQueryValue(hKey, L"Default", NULL, (PUCHAR)szIdBuffer, &BufferSize);
379 if (rc != ERROR_SUCCESS)
380 {
381 //TRACE("Couldn't get Language Default setting\n");
382 goto Quit;
383 }
384
385 BufferSize = LangFileName->MaximumLength;
386 rc = RegQueryValue(hKey, szIdBuffer, NULL,
387 (PUCHAR)LangFileName->Buffer, &BufferSize);
388 if (rc != ERROR_SUCCESS)
389 {
390 //TRACE("Language Default setting exists, but isn't readable\n");
391 //goto Quit;
392 LangFileName->Length = 0;
393 RtlAppendUnicodeToString(LangFileName, L"l_intl.nls");
394 }
395 else
396 {
397 LangFileName->Length = (USHORT)BufferSize - sizeof(UNICODE_NULL);
398 }
399
400 Quit:
401 RegCloseKey(hKey);
402 return (rc == ERROR_SUCCESS);
403 }
404
405 BOOLEAN
WinLdrLoadNLSData(_Inout_ PLOADER_PARAMETER_BLOCK LoaderBlock,_In_ PCSTR DirectoryPath,_In_ PCUNICODE_STRING AnsiFileName,_In_ PCUNICODE_STRING OemFileName,_In_ PCUNICODE_STRING LangFileName,_In_ PCUNICODE_STRING OemHalFileName)406 WinLdrLoadNLSData(
407 _Inout_ PLOADER_PARAMETER_BLOCK LoaderBlock,
408 _In_ PCSTR DirectoryPath,
409 _In_ PCUNICODE_STRING AnsiFileName,
410 _In_ PCUNICODE_STRING OemFileName,
411 _In_ PCUNICODE_STRING LangFileName, // CaseTable
412 _In_ PCUNICODE_STRING OemHalFileName)
413 {
414 ARC_STATUS Status;
415 FILEINFORMATION FileInfo;
416 ULONG AnsiFileId = -1, OemFileId = -1, LangFileId = -1;
417 ULONG AnsiFileSize, OemFileSize, LangFileSize;
418 ULONG TotalSize;
419 ULONG BytesRead;
420 PVOID NlsDataBase, NlsVirtual;
421 BOOLEAN AnsiEqualsOem = FALSE;
422 CHAR FileName[MAX_PATH];
423
424 /* There may be a case, where OEM and ANSI pages coincide */
425 if (RtlCompareUnicodeString(AnsiFileName, OemFileName, TRUE) == 0)
426 AnsiEqualsOem = TRUE;
427
428 /* Open file with ANSI and store its size */
429 RtlStringCbPrintfA(FileName, sizeof(FileName), "%s%wZ",
430 DirectoryPath, AnsiFileName);
431 Status = ArcOpen(FileName, OpenReadOnly, &AnsiFileId);
432 if (Status != ESUCCESS)
433 {
434 WARN("Error while opening '%s', Status: %u\n", FileName, Status);
435 goto Quit;
436 }
437
438 Status = ArcGetFileInformation(AnsiFileId, &FileInfo);
439 if (Status != ESUCCESS)
440 goto Quit;
441 AnsiFileSize = FileInfo.EndingAddress.LowPart;
442 TRACE("AnsiFileSize: %d\n", AnsiFileSize);
443
444 /* Open OEM file and store its length */
445 if (AnsiEqualsOem)
446 {
447 OemFileSize = 0;
448 }
449 else
450 {
451 RtlStringCbPrintfA(FileName, sizeof(FileName), "%s%wZ",
452 DirectoryPath, OemFileName);
453 Status = ArcOpen(FileName, OpenReadOnly, &OemFileId);
454 if (Status != ESUCCESS)
455 {
456 WARN("Error while opening '%s', Status: %u\n", FileName, Status);
457 goto Quit;
458 }
459
460 Status = ArcGetFileInformation(OemFileId, &FileInfo);
461 if (Status != ESUCCESS)
462 goto Quit;
463 OemFileSize = FileInfo.EndingAddress.LowPart;
464 }
465 TRACE("OemFileSize: %d\n", OemFileSize);
466
467 /* Finally open the language codepage file and store its length */
468 RtlStringCbPrintfA(FileName, sizeof(FileName), "%s%wZ",
469 DirectoryPath, LangFileName);
470 Status = ArcOpen(FileName, OpenReadOnly, &LangFileId);
471 if (Status != ESUCCESS)
472 {
473 WARN("Error while opening '%s', Status: %u\n", FileName, Status);
474 goto Quit;
475 }
476
477 Status = ArcGetFileInformation(LangFileId, &FileInfo);
478 if (Status != ESUCCESS)
479 goto Quit;
480 LangFileSize = FileInfo.EndingAddress.LowPart;
481 TRACE("LangFileSize: %d\n", LangFileSize);
482
483 //
484 // TODO: The OEMHAL file.
485 //
486
487 /* Sum up all three length, having in mind that every one of them
488 must start at a page boundary => thus round up each file to a page */
489 TotalSize = MM_SIZE_TO_PAGES(AnsiFileSize) +
490 MM_SIZE_TO_PAGES(OemFileSize) +
491 MM_SIZE_TO_PAGES(LangFileSize);
492
493 /* Store it for later marking the pages as NlsData type */
494 TotalNLSSize = TotalSize;
495
496 NlsDataBase = MmAllocateMemoryWithType(TotalSize*MM_PAGE_SIZE, LoaderNlsData);
497 if (NlsDataBase == NULL)
498 goto Quit;
499
500 NlsVirtual = PaToVa(NlsDataBase);
501 LoaderBlock->NlsData->AnsiCodePageData = NlsVirtual;
502
503 LoaderBlock->NlsData->OemCodePageData =
504 (PVOID)((ULONG_PTR)NlsVirtual +
505 (MM_SIZE_TO_PAGES(AnsiFileSize) << MM_PAGE_SHIFT));
506
507 LoaderBlock->NlsData->UnicodeCodePageData =
508 (PVOID)((ULONG_PTR)NlsVirtual +
509 (MM_SIZE_TO_PAGES(AnsiFileSize) << MM_PAGE_SHIFT) +
510 (MM_SIZE_TO_PAGES(OemFileSize) << MM_PAGE_SHIFT));
511
512 /* ANSI and OEM data are the same - just set pointers to the same area */
513 if (AnsiEqualsOem)
514 LoaderBlock->NlsData->OemCodePageData = LoaderBlock->NlsData->AnsiCodePageData;
515
516 /* Now actually read the data into memory, starting with the ANSI file */
517 RtlStringCbPrintfA(FileName, sizeof(FileName), "%s%wZ",
518 DirectoryPath, AnsiFileName);
519 NtLdrOutputLoadMsg(FileName, NULL);
520 Status = ArcRead(AnsiFileId,
521 VaToPa(LoaderBlock->NlsData->AnsiCodePageData),
522 AnsiFileSize, &BytesRead);
523 if (Status != ESUCCESS)
524 {
525 WARN("Error while reading '%s', Status: %u\n", FileName, Status);
526 goto Quit;
527 }
528
529 /* OEM now, if it isn't the same as the ANSI one */
530 if (!AnsiEqualsOem)
531 {
532 RtlStringCbPrintfA(FileName, sizeof(FileName), "%s%wZ",
533 DirectoryPath, OemFileName);
534 NtLdrOutputLoadMsg(FileName, NULL);
535 Status = ArcRead(OemFileId,
536 VaToPa(LoaderBlock->NlsData->OemCodePageData),
537 OemFileSize, &BytesRead);
538 if (Status != ESUCCESS)
539 {
540 WARN("Error while reading '%s', Status: %u\n", FileName, Status);
541 goto Quit;
542 }
543 }
544
545 /* Finally the language file */
546 RtlStringCbPrintfA(FileName, sizeof(FileName), "%s%wZ",
547 DirectoryPath, LangFileName);
548 NtLdrOutputLoadMsg(FileName, NULL);
549 Status = ArcRead(LangFileId,
550 VaToPa(LoaderBlock->NlsData->UnicodeCodePageData),
551 LangFileSize, &BytesRead);
552 if (Status != ESUCCESS)
553 {
554 WARN("Error while reading '%s', Status: %u\n", FileName, Status);
555 goto Quit;
556 }
557
558 //
559 // THIS IS a HACK and should be replaced by actually loading the OEMHAL file!
560 //
561 LoaderBlock->OemFontFile = VaToPa(LoaderBlock->NlsData->UnicodeCodePageData);
562
563 /* Convert NlsTables address to VA */
564 LoaderBlock->NlsData = PaToVa(LoaderBlock->NlsData);
565
566 Quit:
567 if (LangFileId != -1)
568 ArcClose(LangFileId);
569 if (OemFileId != -1)
570 ArcClose(OemFileId);
571 if (AnsiFileId != -1)
572 ArcClose(AnsiFileId);
573
574 if (Status != ESUCCESS)
575 UiMessageBox("Error reading NLS file %s", FileName);
576
577 return (Status == ESUCCESS);
578 }
579
580 static BOOLEAN
WinLdrScanRegistry(IN OUT PLIST_ENTRY BootDriverListHead)581 WinLdrScanRegistry(
582 IN OUT PLIST_ENTRY BootDriverListHead)
583 {
584 BOOLEAN Success;
585
586 /* Find all boot drivers */
587 Success = CmpFindDrivers(SystemHive,
588 HKEY_TO_HCI(CurrentControlSetKey),
589 BootLoad,
590 BootFileSystem,
591 BootDriverListHead);
592 if (!Success)
593 goto Quit;
594
595 /* Sort by group/tag */
596 Success = CmpSortDriverList(SystemHive,
597 HKEY_TO_HCI(CurrentControlSetKey),
598 BootDriverListHead);
599 if (!Success)
600 goto Quit;
601
602 /* Remove circular dependencies (cycles) and sort */
603 Success = CmpResolveDriverDependencies(BootDriverListHead);
604 if (!Success)
605 goto Quit;
606
607 Quit:
608 /* In case of failure, free the boot driver list */
609 if (!Success)
610 CmpFreeDriverList(SystemHive, BootDriverListHead);
611
612 return Success;
613 }
614
615 /**
616 * @brief
617 * Inserts the specified driver entry into the driver list, or updates
618 * an existing entry with new ImagePath, ErrorControl, Group and Tag values.
619 *
620 * @param[in,out] DriverListHead
621 * The driver list where to insert the driver entry.
622 *
623 * @param[in] InsertAtHead
624 * Whether to insert the driver at the head (TRUE) or at the tail (FALSE)
625 * of the driver list.
626 *
627 * @param[in] DriverName
628 * The driver's name.
629 *
630 * @param[in] ImagePath
631 * Optional path the the driver's image. If none is specified,
632 * a default path is constructed out of the driver's name.
633 *
634 * @param[in] GroupName
635 * Optional driver group name.
636 *
637 * @param[in] ErrorControl
638 * @param[in] Tag
639 * The ErrorControl and group Tag values for the driver.
640 *
641 * @return
642 * TRUE if the driver has been inserted into the list or updated, FALSE if not.
643 **/
644 BOOLEAN
WinLdrAddDriverToList(_Inout_ PLIST_ENTRY DriverListHead,_In_ BOOLEAN InsertAtHead,_In_ PCWSTR DriverName,_In_opt_ PCWSTR ImagePath,_In_opt_ PCWSTR GroupName,_In_ ULONG ErrorControl,_In_ ULONG Tag)645 WinLdrAddDriverToList(
646 _Inout_ PLIST_ENTRY DriverListHead,
647 _In_ BOOLEAN InsertAtHead,
648 _In_ PCWSTR DriverName,
649 _In_opt_ PCWSTR ImagePath,
650 _In_opt_ PCWSTR GroupName,
651 _In_ ULONG ErrorControl,
652 _In_ ULONG Tag)
653 {
654 PBOOT_DRIVER_NODE DriverNode;
655 PBOOT_DRIVER_LIST_ENTRY DriverEntry;
656 BOOLEAN AlreadyInserted;
657 USHORT PathLength;
658 UNICODE_STRING DriverNameU;
659 UNICODE_STRING RegistryPath;
660 UNICODE_STRING FilePath = {0};
661 UNICODE_STRING RegistryString = {0};
662 UNICODE_STRING GroupString = {0};
663
664 /* Check whether the driver is already in the list */
665 RtlInitUnicodeString(&DriverNameU, DriverName);
666 AlreadyInserted = CmpIsDriverInList(DriverListHead,
667 &DriverNameU,
668 &DriverNode);
669 if (AlreadyInserted)
670 {
671 /* If so, we have obtained its node */
672 ASSERT(DriverNode);
673 DriverEntry = &DriverNode->ListEntry;
674 }
675 else
676 {
677 /* Allocate a driver node and initialize it */
678 DriverNode = CmpAllocate(sizeof(BOOT_DRIVER_NODE), FALSE, TAG_CM);
679 if (!DriverNode)
680 return FALSE;
681
682 RtlZeroMemory(DriverNode, sizeof(BOOT_DRIVER_NODE));
683 DriverEntry = &DriverNode->ListEntry;
684
685 /* Driver Name */
686 RtlInitEmptyUnicodeString(&DriverNode->Name,
687 CmpAllocate(DriverNameU.Length, FALSE, TAG_CM),
688 DriverNameU.Length);
689 if (!DriverNode->Name.Buffer)
690 goto Failure;
691
692 if (!NT_SUCCESS(RtlAppendUnicodeStringToString(&DriverNode->Name, &DriverNameU)))
693 goto Failure;
694 }
695
696 /* Check whether we have a valid ImagePath. If not, we need
697 * to build it like "System32\\Drivers\\blah.sys" */
698 if (ImagePath && *ImagePath)
699 {
700 /* Just copy ImagePath to the corresponding field in the structure */
701 PathLength = (USHORT)(wcslen(ImagePath)) * sizeof(WCHAR);
702 RtlInitEmptyUnicodeString(&FilePath,
703 CmpAllocate(PathLength, FALSE, TAG_WLDR_NAME),
704 PathLength);
705 if (!FilePath.Buffer)
706 goto Failure;
707
708 if (!NT_SUCCESS(RtlAppendUnicodeToString(&FilePath, ImagePath)))
709 goto Failure;
710 }
711 else
712 {
713 /* We have to construct ImagePath ourselves */
714 PathLength = DriverNode->Name.Length + sizeof(L"system32\\drivers\\.sys");
715 RtlInitEmptyUnicodeString(&FilePath,
716 CmpAllocate(PathLength, FALSE, TAG_WLDR_NAME),
717 PathLength);
718 if (!FilePath.Buffer)
719 goto Failure;
720
721 if (!NT_SUCCESS(RtlAppendUnicodeToString(&FilePath, L"system32\\drivers\\")) ||
722 !NT_SUCCESS(RtlAppendUnicodeStringToString(&FilePath, &DriverNode->Name)) ||
723 !NT_SUCCESS(RtlAppendUnicodeToString(&FilePath, L".sys")))
724 {
725 goto Failure;
726 }
727 }
728
729 /* Registry path */
730 RtlInitUnicodeString(&RegistryPath,
731 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
732 PathLength = RegistryPath.Length + DriverNode->Name.Length;
733 RtlInitEmptyUnicodeString(&RegistryString,
734 CmpAllocate(PathLength, FALSE, TAG_WLDR_NAME),
735 PathLength);
736 if (!RegistryString.Buffer)
737 goto Failure;
738
739 if (!NT_SUCCESS(RtlAppendUnicodeStringToString(&RegistryString, &RegistryPath)) ||
740 !NT_SUCCESS(RtlAppendUnicodeStringToString(&RegistryString, &DriverNode->Name)))
741 {
742 goto Failure;
743 }
744
745 /* Group */
746 if (GroupName && *GroupName)
747 {
748 /*
749 * NOTE: Here we can use our own allocator as we alone maintain the
750 * group string. This is different from the other allocated strings,
751 * where we instead need to use the same (hive) allocator as the
752 * one used by CmpAddDriverToList(), for interoperability purposes.
753 */
754 RtlCreateUnicodeString(&GroupString, GroupName);
755 if (!GroupString.Buffer)
756 goto Failure;
757 }
758 else
759 {
760 RtlInitEmptyUnicodeString(&GroupString, NULL, 0);
761 }
762
763 /* Set or replace the driver node's file path */
764 if (DriverEntry->FilePath.Buffer)
765 {
766 CmpFree(DriverEntry->FilePath.Buffer,
767 DriverEntry->FilePath.MaximumLength);
768 }
769 DriverEntry->FilePath = FilePath;
770 FilePath.Buffer = NULL;
771
772 /* Set or replace the driver node's registry path */
773 if (DriverEntry->RegistryPath.Buffer)
774 {
775 CmpFree(DriverEntry->RegistryPath.Buffer,
776 DriverEntry->RegistryPath.MaximumLength);
777 }
778 DriverEntry->RegistryPath = RegistryString;
779 RegistryString.Buffer = NULL;
780
781 /* Set or replace the driver node's group */
782 if (DriverNode->Group.Buffer)
783 {
784 /*
785 * If the buffer is inside the registry hive's memory, this means that
786 * it has been set by CmpAddDriverToList() to point to some data within
787 * the hive; thus we should not free the buffer but just replace it.
788 * Otherwise, this is a buffer previously allocated by ourselves, that
789 * we can free.
790 *
791 * NOTE: This function does not have an explicit LoaderBlock input
792 * parameter pointer, since it does not need it, except for this
793 * very place. So instead, use the global WinLdrSystemBlock pointer.
794 */
795 PLOADER_PARAMETER_BLOCK LoaderBlock =
796 (WinLdrSystemBlock ? &WinLdrSystemBlock->LoaderBlock : NULL);
797
798 if (!LoaderBlock || !LoaderBlock->RegistryBase || !LoaderBlock->RegistryLength ||
799 ((ULONG_PTR)DriverNode->Group.Buffer <
800 (ULONG_PTR)VaToPa(LoaderBlock->RegistryBase)) ||
801 ((ULONG_PTR)DriverNode->Group.Buffer >=
802 (ULONG_PTR)VaToPa(LoaderBlock->RegistryBase) + LoaderBlock->RegistryLength))
803 {
804 RtlFreeUnicodeString(&DriverNode->Group);
805 }
806 }
807 DriverNode->Group = GroupString;
808 GroupString.Buffer = NULL;
809
810 /* ErrorControl and Tag */
811 DriverNode->ErrorControl = ErrorControl;
812 DriverNode->Tag = Tag;
813
814 /* Insert the entry into the list if it does not exist there already */
815 if (!AlreadyInserted)
816 {
817 if (InsertAtHead)
818 InsertHeadList(DriverListHead, &DriverEntry->Link);
819 else
820 InsertTailList(DriverListHead, &DriverEntry->Link);
821 }
822
823 return TRUE;
824
825 Failure:
826 if (GroupString.Buffer)
827 RtlFreeUnicodeString(&GroupString);
828 if (RegistryString.Buffer)
829 CmpFree(RegistryString.Buffer, RegistryString.MaximumLength);
830 if (FilePath.Buffer)
831 CmpFree(FilePath.Buffer, FilePath.MaximumLength);
832
833 /* If it does not exist in the list already, free the allocated
834 * driver node, otherwise keep the original one in place. */
835 if (!AlreadyInserted)
836 {
837 if (DriverEntry->RegistryPath.Buffer)
838 {
839 CmpFree(DriverEntry->RegistryPath.Buffer,
840 DriverEntry->RegistryPath.MaximumLength);
841 }
842 if (DriverEntry->FilePath.Buffer)
843 {
844 CmpFree(DriverEntry->FilePath.Buffer,
845 DriverEntry->FilePath.MaximumLength);
846 }
847 if (DriverNode->Name.Buffer)
848 {
849 CmpFree(DriverNode->Name.Buffer,
850 DriverNode->Name.MaximumLength);
851 }
852 CmpFree(DriverNode, sizeof(BOOT_DRIVER_NODE));
853 }
854
855 return FALSE;
856 }
857