1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: BSD - See COPYING.ARM in the top level directory 4 * PURPOSE: Configuration Manager - Boot Initialization 5 * COPYRIGHT: Copyright 2007 Alex Ionescu (alex.ionescu@reactos.org) 6 * Copyright 2010 ReactOS Portable Systems Group 7 * Copyright 2022 Hermès Bélusca-Maïto 8 * 9 * NOTE: This module is shared by both the kernel and the bootloader. 10 */ 11 12 /* INCLUDES *******************************************************************/ 13 14 #include <ntoskrnl.h> 15 16 #define NDEBUG 17 #include <debug.h> 18 19 #ifdef _BLDR_ 20 21 #undef CODE_SEG 22 #define CODE_SEG(...) 23 24 #include <ntstrsafe.h> 25 #include <cmlib.h> 26 #include "internal/cmboot.h" 27 28 // HACK: This is part of non-NT-compatible SafeBoot support in kernel. 29 ULONG InitSafeBootMode = 0; 30 31 DBG_DEFAULT_CHANNEL(REGISTRY); 32 #define CMTRACE(x, fmt, ...) TRACE(fmt, ##__VA_ARGS__) // DPRINT 33 34 #endif /* _BLDR_ */ 35 36 37 /* DEFINES ********************************************************************/ 38 39 #define CM_BOOT_DEBUG 0x20 40 41 #define IS_NULL_TERMINATED(Buffer, Size) \ 42 (((Size) >= sizeof(WCHAR)) && ((Buffer)[(Size) / sizeof(WCHAR) - 1] == UNICODE_NULL)) 43 44 45 /* FUNCTIONS ******************************************************************/ 46 47 // HACK: This is part of non-NT-compatible SafeBoot support in kernel. 48 extern ULONG InitSafeBootMode; 49 50 CODE_SEG("INIT") 51 static 52 BOOLEAN 53 CmpIsSafe( 54 _In_ PHHIVE Hive, 55 _In_ HCELL_INDEX SafeBootCell, 56 _In_ HCELL_INDEX DriverCell); 57 58 /** 59 * @brief 60 * Finds the corresponding "HKLM\SYSTEM\ControlSetXXX" system control set 61 * registry key, according to the "Current", "Default", or "LastKnownGood" 62 * values in the "HKLM\SYSTEM\Select" registry key. 63 * 64 * @param[in] SystemHive 65 * The SYSTEM hive. 66 * 67 * @param[in] RootCell 68 * The root cell of the SYSTEM hive. 69 * 70 * @param[in] SelectKeyName 71 * The control set to check for: either "Current", "Default", or 72 * "LastKnownGood", the value of which selects the corresponding 73 * "HKLM\SYSTEM\ControlSetXXX" control set registry key. 74 * 75 * @param[out] AutoSelect 76 * Value of the "AutoSelect" registry value (unused). 77 * 78 * @return 79 * The control set registry key's hive cell (if found), or HCELL_NIL. 80 **/ 81 CODE_SEG("INIT") 82 HCELL_INDEX 83 NTAPI 84 CmpFindControlSet( 85 _In_ PHHIVE SystemHive, 86 _In_ HCELL_INDEX RootCell, 87 _In_ PCUNICODE_STRING SelectKeyName, 88 _Out_ PBOOLEAN AutoSelect) 89 { 90 UNICODE_STRING Name; 91 PCM_KEY_NODE Node; 92 HCELL_INDEX SelectCell, AutoSelectCell, SelectValueCell, ControlSetCell; 93 HCELL_INDEX CurrentValueCell; 94 PCM_KEY_VALUE Value; 95 ULONG Length; 96 NTSTATUS Status; 97 PULONG CurrentData; 98 PULONG ControlSetId; 99 WCHAR Buffer[128]; 100 101 /* Sanity check: We shouldn't need to release any acquired cells */ 102 ASSERT(SystemHive->ReleaseCellRoutine == NULL); 103 104 /* Get the Select key */ 105 RtlInitUnicodeString(&Name, L"select"); 106 Node = (PCM_KEY_NODE)HvGetCell(SystemHive, RootCell); 107 if (!Node) return HCELL_NIL; 108 SelectCell = CmpFindSubKeyByName(SystemHive, Node, &Name); 109 if (SelectCell == HCELL_NIL) return HCELL_NIL; 110 111 /* Get AutoSelect value */ 112 RtlInitUnicodeString(&Name, L"AutoSelect"); 113 Node = (PCM_KEY_NODE)HvGetCell(SystemHive, SelectCell); 114 if (!Node) return HCELL_NIL; 115 AutoSelectCell = CmpFindValueByName(SystemHive, Node, &Name); 116 if (AutoSelectCell == HCELL_NIL) 117 { 118 /* Assume TRUE if the value is missing */ 119 *AutoSelect = TRUE; 120 } 121 else 122 { 123 /* Read the value */ 124 Value = (PCM_KEY_VALUE)HvGetCell(SystemHive, AutoSelectCell); 125 if (!Value) return HCELL_NIL; 126 // if (Value->Type != REG_DWORD) return HCELL_NIL; 127 128 /* Convert it to a boolean */ 129 CurrentData = (PULONG)CmpValueToData(SystemHive, Value, &Length); 130 if (!CurrentData) return HCELL_NIL; 131 // if (Length < sizeof(ULONG)) return HCELL_NIL; 132 133 *AutoSelect = *(PBOOLEAN)CurrentData; 134 } 135 136 /* Now find the control set being looked up */ 137 Node = (PCM_KEY_NODE)HvGetCell(SystemHive, SelectCell); 138 if (!Node) return HCELL_NIL; 139 SelectValueCell = CmpFindValueByName(SystemHive, Node, SelectKeyName); 140 if (SelectValueCell == HCELL_NIL) return HCELL_NIL; 141 142 /* Read the value (corresponding to the CCS ID) */ 143 Value = (PCM_KEY_VALUE)HvGetCell(SystemHive, SelectValueCell); 144 if (!Value) return HCELL_NIL; 145 if (Value->Type != REG_DWORD) return HCELL_NIL; 146 ControlSetId = (PULONG)CmpValueToData(SystemHive, Value, &Length); 147 if (!ControlSetId) return HCELL_NIL; 148 if (Length < sizeof(ULONG)) return HCELL_NIL; 149 150 /* Now build the CCS's Name */ 151 Status = RtlStringCbPrintfW(Buffer, sizeof(Buffer), 152 L"ControlSet%03lu", *ControlSetId); 153 if (!NT_SUCCESS(Status)) return HCELL_NIL; 154 /* RtlStringCbPrintfW ensures the buffer to be NULL-terminated */ 155 RtlInitUnicodeString(&Name, Buffer); 156 157 /* Now open it */ 158 Node = (PCM_KEY_NODE)HvGetCell(SystemHive, RootCell); 159 if (!Node) return HCELL_NIL; 160 ControlSetCell = CmpFindSubKeyByName(SystemHive, Node, &Name); 161 if (ControlSetCell == HCELL_NIL) return HCELL_NIL; 162 163 /* Get the value of the "Current" CCS */ 164 RtlInitUnicodeString(&Name, L"Current"); 165 Node = (PCM_KEY_NODE)HvGetCell(SystemHive, SelectCell); 166 if (!Node) return HCELL_NIL; 167 CurrentValueCell = CmpFindValueByName(SystemHive, Node, &Name); 168 169 /* Make sure it exists */ 170 if (CurrentValueCell != HCELL_NIL) 171 { 172 /* Get the current value and make sure it's a ULONG */ 173 Value = (PCM_KEY_VALUE)HvGetCell(SystemHive, CurrentValueCell); 174 if (!Value) return HCELL_NIL; 175 if (Value->Type == REG_DWORD) 176 { 177 /* Get the data and update it */ 178 CurrentData = (PULONG)CmpValueToData(SystemHive, Value, &Length); 179 if (!CurrentData) return HCELL_NIL; 180 if (Length < sizeof(ULONG)) return HCELL_NIL; 181 182 *CurrentData = *ControlSetId; 183 } 184 } 185 186 /* Return the CCS cell */ 187 return ControlSetCell; 188 } 189 190 /** 191 * @brief 192 * Finds the index of the driver's "Tag" value 193 * in its corresponding group ordering list. 194 * 195 * @param[in] Hive 196 * The SYSTEM hive. 197 * 198 * @param[in] TagCell 199 * The driver's "Tag" registry value's hive cell. 200 * 201 * @param[in] GroupOrderCell 202 * The hive cell of the "Control\GroupOrderList" registry key 203 * inside the currently selected control set. 204 * 205 * @param[in] GroupName 206 * The driver's group name. 207 * 208 * @return 209 * The corresponding tag index, or -1 (last position), 210 * or -2 (next-to-last position). 211 **/ 212 CODE_SEG("INIT") 213 static 214 ULONG 215 CmpFindTagIndex( 216 _In_ PHHIVE Hive, 217 _In_ HCELL_INDEX TagCell, 218 _In_ HCELL_INDEX GroupOrderCell, 219 _In_ PCUNICODE_STRING GroupName) 220 { 221 PCM_KEY_VALUE TagValue, Value; 222 PCM_KEY_NODE Node; 223 HCELL_INDEX OrderCell; 224 PULONG DriverTag, TagOrder; 225 ULONG CurrentTag, Length; 226 BOOLEAN BufferAllocated; 227 228 /* Sanity check: We shouldn't need to release any acquired cells */ 229 ASSERT(Hive->ReleaseCellRoutine == NULL); 230 231 /* Get the tag */ 232 Value = (PCM_KEY_VALUE)HvGetCell(Hive, TagCell); 233 if (!Value) return -2; 234 if (Value->Type != REG_DWORD) return -2; 235 DriverTag = (PULONG)CmpValueToData(Hive, Value, &Length); 236 if (!DriverTag) return -2; 237 if (Length < sizeof(ULONG)) return -2; 238 239 /* Get the order array */ 240 Node = (PCM_KEY_NODE)HvGetCell(Hive, GroupOrderCell); 241 if (!Node) return -2; 242 OrderCell = CmpFindValueByName(Hive, Node, GroupName); 243 if (OrderCell == HCELL_NIL) return -2; 244 245 /* And read it */ 246 TagValue = (PCM_KEY_VALUE)HvGetCell(Hive, OrderCell); 247 if (!TagValue) return -2; 248 if (!CmpGetValueData(Hive, 249 TagValue, 250 &Length, 251 (PVOID*)&TagOrder, 252 &BufferAllocated, 253 &OrderCell) 254 || !TagOrder) 255 { 256 return -2; 257 } 258 259 /* Parse each tag */ 260 for (CurrentTag = 1; CurrentTag <= TagOrder[0]; CurrentTag++) 261 { 262 /* Find a match */ 263 if (TagOrder[CurrentTag] == *DriverTag) 264 { 265 /* Found it -- return the tag */ 266 if (BufferAllocated) Hive->Free(TagOrder, Length); 267 return CurrentTag; 268 } 269 } 270 271 /* No matches, so assume next to last ordering */ 272 if (BufferAllocated) Hive->Free(TagOrder, Length); 273 return -2; 274 } 275 276 #ifdef _BLDR_ 277 278 /** 279 * @brief 280 * Checks whether the specified named driver is already in the driver list. 281 * Optionally returns its corresponding driver node. 282 * 283 * @remarks Used in bootloader only. 284 * 285 * @param[in] DriverListHead 286 * The driver list. 287 * 288 * @param[in] DriverName 289 * The name of the driver to search for. 290 * 291 * @param[out] FoundDriver 292 * Optional pointer that receives in output the address of the 293 * matching driver node, if any, or NULL if none has been found. 294 * 295 * @return 296 * TRUE if the driver has been found, FALSE if not. 297 **/ 298 CODE_SEG("INIT") 299 BOOLEAN 300 NTAPI 301 CmpIsDriverInList( 302 _In_ PLIST_ENTRY DriverListHead, 303 _In_ PCUNICODE_STRING DriverName, 304 _Out_opt_ PBOOT_DRIVER_NODE* FoundDriver) 305 { 306 PLIST_ENTRY Entry; 307 PBOOT_DRIVER_NODE DriverNode; 308 309 for (Entry = DriverListHead->Flink; 310 Entry != DriverListHead; 311 Entry = Entry->Flink) 312 { 313 DriverNode = CONTAINING_RECORD(Entry, 314 BOOT_DRIVER_NODE, 315 ListEntry.Link); 316 317 if (RtlEqualUnicodeString(&DriverNode->Name, 318 DriverName, 319 TRUE)) 320 { 321 /* The driver node has been found */ 322 if (FoundDriver) 323 *FoundDriver = DriverNode; 324 return TRUE; 325 } 326 } 327 328 /* None has been found */ 329 if (FoundDriver) 330 *FoundDriver = NULL; 331 return FALSE; 332 } 333 334 #endif /* _BLDR_ */ 335 336 /** 337 * @brief 338 * Inserts the specified driver entry into the driver list. 339 * 340 * @param[in] Hive 341 * The SYSTEM hive. 342 * 343 * @param[in] DriverCell 344 * The registry key's hive cell of the driver to be added, inside 345 * the "Services" sub-key of the currently selected control set. 346 * 347 * @param[in] GroupOrderCell 348 * The hive cell of the "Control\GroupOrderList" registry key 349 * inside the currently selected control set. 350 * 351 * @param[in] RegistryPath 352 * Constant UNICODE_STRING pointing to 353 * "\\Registry\\Machine\\System\\CurrentControlSet\\Services\\". 354 * 355 * @param[in,out] DriverListHead 356 * The driver list where to insert the driver entry. 357 * 358 * @return 359 * TRUE if the driver has been inserted into the list, FALSE if not. 360 **/ 361 CODE_SEG("INIT") 362 BOOLEAN 363 NTAPI 364 CmpAddDriverToList( 365 _In_ PHHIVE Hive, 366 _In_ HCELL_INDEX DriverCell, 367 _In_ HCELL_INDEX GroupOrderCell, 368 _In_ PCUNICODE_STRING RegistryPath, 369 _Inout_ PLIST_ENTRY DriverListHead) 370 { 371 PBOOT_DRIVER_NODE DriverNode; 372 PBOOT_DRIVER_LIST_ENTRY DriverEntry; 373 PCM_KEY_NODE Node; 374 PCM_KEY_VALUE Value; 375 ULONG Length; 376 USHORT NameLength; 377 HCELL_INDEX ValueCell, TagCell; 378 PUNICODE_STRING FilePath, RegistryString; 379 UNICODE_STRING Name; 380 PULONG ErrorControl; 381 PWCHAR Buffer; 382 383 /* Sanity check: We shouldn't need to release any acquired cells */ 384 ASSERT(Hive->ReleaseCellRoutine == NULL); 385 386 /* Allocate a driver node and initialize it */ 387 DriverNode = Hive->Allocate(sizeof(BOOT_DRIVER_NODE), FALSE, TAG_CM); 388 if (!DriverNode) 389 return FALSE; 390 391 RtlZeroMemory(DriverNode, sizeof(BOOT_DRIVER_NODE)); 392 DriverEntry = &DriverNode->ListEntry; 393 394 /* Get the driver cell */ 395 Node = (PCM_KEY_NODE)HvGetCell(Hive, DriverCell); 396 if (!Node) 397 goto Failure; 398 399 /* Get the name from the cell */ 400 NameLength = (Node->Flags & KEY_COMP_NAME) ? 401 CmpCompressedNameSize(Node->Name, Node->NameLength) : 402 Node->NameLength; 403 if (NameLength < sizeof(WCHAR)) 404 goto Failure; 405 406 /* Now allocate the buffer for it and copy the name */ 407 RtlInitEmptyUnicodeString(&DriverNode->Name, 408 Hive->Allocate(NameLength, FALSE, TAG_CM), 409 NameLength); 410 if (!DriverNode->Name.Buffer) 411 goto Failure; 412 413 DriverNode->Name.Length = NameLength; 414 if (Node->Flags & KEY_COMP_NAME) 415 { 416 /* Compressed name */ 417 CmpCopyCompressedName(DriverNode->Name.Buffer, 418 DriverNode->Name.Length, 419 Node->Name, 420 Node->NameLength); 421 } 422 else 423 { 424 /* Normal name */ 425 RtlCopyMemory(DriverNode->Name.Buffer, Node->Name, Node->NameLength); 426 } 427 428 /* Now find the image path */ 429 RtlInitUnicodeString(&Name, L"ImagePath"); 430 ValueCell = CmpFindValueByName(Hive, Node, &Name); 431 if (ValueCell == HCELL_NIL) 432 { 433 /* Could not find it, so assume the drivers path */ 434 Length = sizeof(L"System32\\Drivers\\") + NameLength + sizeof(L".sys"); 435 436 /* Allocate the path name */ 437 FilePath = &DriverEntry->FilePath; 438 RtlInitEmptyUnicodeString(FilePath, 439 Hive->Allocate(Length, FALSE, TAG_CM), 440 (USHORT)Length); 441 if (!FilePath->Buffer) 442 goto Failure; 443 444 /* Write the path name */ 445 if (!NT_SUCCESS(RtlAppendUnicodeToString(FilePath, L"System32\\Drivers\\")) || 446 !NT_SUCCESS(RtlAppendUnicodeStringToString(FilePath, &DriverNode->Name)) || 447 !NT_SUCCESS(RtlAppendUnicodeToString(FilePath, L".sys"))) 448 { 449 goto Failure; 450 } 451 } 452 else 453 { 454 /* Path name exists, so grab it */ 455 Value = (PCM_KEY_VALUE)HvGetCell(Hive, ValueCell); 456 if (!Value) 457 goto Failure; 458 if ((Value->Type != REG_SZ) && (Value->Type != REG_EXPAND_SZ)) 459 goto Failure; 460 Buffer = (PWCHAR)CmpValueToData(Hive, Value, &Length); 461 if (!Buffer) 462 goto Failure; 463 if (IS_NULL_TERMINATED(Buffer, Length)) 464 Length -= sizeof(UNICODE_NULL); 465 if (Length < sizeof(WCHAR)) 466 goto Failure; 467 468 /* Allocate and setup the path name */ 469 FilePath = &DriverEntry->FilePath; 470 RtlInitEmptyUnicodeString(FilePath, 471 Hive->Allocate(Length, FALSE, TAG_CM), 472 (USHORT)Length); 473 if (!FilePath->Buffer) 474 goto Failure; 475 476 /* Transfer the data */ 477 RtlCopyMemory(FilePath->Buffer, Buffer, Length); 478 FilePath->Length = (USHORT)Length; 479 } 480 481 /* Now build the registry path */ 482 RegistryString = &DriverEntry->RegistryPath; 483 Length = RegistryPath->Length + NameLength; 484 RtlInitEmptyUnicodeString(RegistryString, 485 Hive->Allocate(Length, FALSE, TAG_CM), 486 (USHORT)Length); 487 if (!RegistryString->Buffer) 488 goto Failure; 489 490 /* Add the driver name to it */ 491 if (!NT_SUCCESS(RtlAppendUnicodeStringToString(RegistryString, RegistryPath)) || 492 !NT_SUCCESS(RtlAppendUnicodeStringToString(RegistryString, &DriverNode->Name))) 493 { 494 goto Failure; 495 } 496 497 /* The entry is done, add it */ 498 InsertHeadList(DriverListHead, &DriverEntry->Link); 499 500 /* Now find error control settings */ 501 RtlInitUnicodeString(&Name, L"ErrorControl"); 502 ValueCell = CmpFindValueByName(Hive, Node, &Name); 503 if (ValueCell == HCELL_NIL) 504 { 505 /* Could not find it, so assume default */ 506 DriverNode->ErrorControl = NormalError; 507 } 508 else 509 { 510 /* Otherwise, read whatever the data says */ 511 Value = (PCM_KEY_VALUE)HvGetCell(Hive, ValueCell); 512 if (!Value) 513 goto Failure; 514 if (Value->Type != REG_DWORD) 515 goto Failure; 516 ErrorControl = (PULONG)CmpValueToData(Hive, Value, &Length); 517 if (!ErrorControl) 518 goto Failure; 519 if (Length < sizeof(ULONG)) 520 goto Failure; 521 522 DriverNode->ErrorControl = *ErrorControl; 523 } 524 525 /* Next, get the group cell */ 526 RtlInitUnicodeString(&Name, L"group"); 527 ValueCell = CmpFindValueByName(Hive, Node, &Name); 528 if (ValueCell == HCELL_NIL) 529 { 530 /* Could not find it, so set an empty string */ 531 RtlInitEmptyUnicodeString(&DriverNode->Group, NULL, 0); 532 } 533 else 534 { 535 /* Found it, read the group value */ 536 Value = (PCM_KEY_VALUE)HvGetCell(Hive, ValueCell); 537 if (!Value) 538 goto Failure; 539 if (Value->Type != REG_SZ) // REG_EXPAND_SZ not really allowed there. 540 goto Failure; 541 542 /* Copy it into the node */ 543 Buffer = (PWCHAR)CmpValueToData(Hive, Value, &Length); 544 if (!Buffer) 545 goto Failure; 546 if (IS_NULL_TERMINATED(Buffer, Length)) 547 Length -= sizeof(UNICODE_NULL); 548 549 DriverNode->Group.Buffer = Buffer; 550 DriverNode->Group.Length = (USHORT)Length; 551 DriverNode->Group.MaximumLength = DriverNode->Group.Length; 552 } 553 554 /* Finally, find the tag */ 555 RtlInitUnicodeString(&Name, L"Tag"); 556 TagCell = CmpFindValueByName(Hive, Node, &Name); 557 if (TagCell == HCELL_NIL) 558 { 559 /* No tag, so load last */ 560 DriverNode->Tag = -1; 561 } 562 else 563 { 564 /* Otherwise, decode it based on tag order */ 565 DriverNode->Tag = CmpFindTagIndex(Hive, 566 TagCell, 567 GroupOrderCell, 568 &DriverNode->Group); 569 } 570 571 CMTRACE(CM_BOOT_DEBUG, "Adding boot driver: '%wZ', '%wZ'\n", 572 &DriverNode->Name, &DriverEntry->FilePath); 573 574 /* All done! */ 575 return TRUE; 576 577 Failure: 578 if (DriverEntry->RegistryPath.Buffer) 579 { 580 Hive->Free(DriverEntry->RegistryPath.Buffer, 581 DriverEntry->RegistryPath.MaximumLength); 582 } 583 if (DriverEntry->FilePath.Buffer) 584 { 585 Hive->Free(DriverEntry->FilePath.Buffer, 586 DriverEntry->FilePath.MaximumLength); 587 } 588 if (DriverNode->Name.Buffer) 589 { 590 Hive->Free(DriverNode->Name.Buffer, 591 DriverNode->Name.MaximumLength); 592 } 593 Hive->Free(DriverNode, sizeof(BOOT_DRIVER_NODE)); 594 595 return FALSE; 596 } 597 598 /** 599 * @brief 600 * Checks whether the specified driver has the expected load type. 601 * 602 * @param[in] Hive 603 * The SYSTEM hive. 604 * 605 * @param[in] DriverCell 606 * The registry key's hive cell of the driver, inside the 607 * "Services" sub-key of the currently selected control set. 608 * 609 * @param[in] LoadType 610 * The load type the driver should match. 611 * 612 * @return 613 * TRUE if the driver's load type matches, FALSE if not. 614 **/ 615 CODE_SEG("INIT") 616 static 617 BOOLEAN 618 CmpIsLoadType( 619 _In_ PHHIVE Hive, 620 _In_ HCELL_INDEX Cell, 621 _In_ SERVICE_LOAD_TYPE LoadType) 622 { 623 PCM_KEY_NODE Node; 624 PCM_KEY_VALUE Value; 625 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"Start"); 626 HCELL_INDEX ValueCell; 627 ULONG Length; 628 PULONG Data; 629 630 /* Sanity check: We shouldn't need to release any acquired cells */ 631 ASSERT(Hive->ReleaseCellRoutine == NULL); 632 633 /* Open the start cell */ 634 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 635 if (!Node) return FALSE; 636 ValueCell = CmpFindValueByName(Hive, Node, &Name); 637 if (ValueCell == HCELL_NIL) return FALSE; 638 639 /* Read the start value */ 640 Value = (PCM_KEY_VALUE)HvGetCell(Hive, ValueCell); 641 if (!Value) return FALSE; 642 if (Value->Type != REG_DWORD) return FALSE; 643 Data = (PULONG)CmpValueToData(Hive, Value, &Length); 644 if (!Data) return FALSE; 645 if (Length < sizeof(ULONG)) return FALSE; 646 647 /* Return if the type matches */ 648 return (*Data == LoadType); 649 } 650 651 /** 652 * @brief 653 * Enumerates all drivers within the given control set and load type, 654 * present in the "Services" sub-key, and inserts them into the driver list. 655 * 656 * @param[in] Hive 657 * The SYSTEM hive. 658 * 659 * @param[in] ControlSet 660 * The control set registry key's hive cell. 661 * 662 * @param[in] LoadType 663 * The load type the driver should match. 664 * 665 * @param[in] BootFileSystem 666 * Optional name of the boot file system, for which to insert 667 * its corresponding driver. 668 * 669 * @param[in,out] DriverListHead 670 * The driver list where to insert the enumerated drivers. 671 * 672 * @return 673 * TRUE if the drivers have been successfully enumerated and inserted, 674 * FALSE if not. 675 **/ 676 CODE_SEG("INIT") 677 BOOLEAN 678 NTAPI 679 CmpFindDrivers( 680 _In_ PHHIVE Hive, 681 _In_ HCELL_INDEX ControlSet, 682 _In_ SERVICE_LOAD_TYPE LoadType, 683 _In_opt_ PCWSTR BootFileSystem, 684 _Inout_ PLIST_ENTRY DriverListHead) 685 { 686 HCELL_INDEX ServicesCell, ControlCell, GroupOrderCell, DriverCell; 687 HCELL_INDEX SafeBootCell = HCELL_NIL; 688 ULONG i; 689 UNICODE_STRING Name; 690 UNICODE_STRING KeyPath; 691 PCM_KEY_NODE ControlNode, ServicesNode, Node; 692 PBOOT_DRIVER_NODE FsNode; 693 694 /* Sanity check: We shouldn't need to release any acquired cells */ 695 ASSERT(Hive->ReleaseCellRoutine == NULL); 696 697 /* Open the control set key */ 698 ControlNode = (PCM_KEY_NODE)HvGetCell(Hive, ControlSet); 699 if (!ControlNode) return FALSE; 700 701 /* Get services cell */ 702 RtlInitUnicodeString(&Name, L"Services"); 703 ServicesCell = CmpFindSubKeyByName(Hive, ControlNode, &Name); 704 if (ServicesCell == HCELL_NIL) return FALSE; 705 706 /* Open services key */ 707 ServicesNode = (PCM_KEY_NODE)HvGetCell(Hive, ServicesCell); 708 if (!ServicesNode) return FALSE; 709 710 /* Get control cell */ 711 RtlInitUnicodeString(&Name, L"Control"); 712 ControlCell = CmpFindSubKeyByName(Hive, ControlNode, &Name); 713 if (ControlCell == HCELL_NIL) return FALSE; 714 715 /* Get the group order cell and read it */ 716 Node = (PCM_KEY_NODE)HvGetCell(Hive, ControlCell); 717 if (!Node) return FALSE; 718 RtlInitUnicodeString(&Name, L"GroupOrderList"); 719 GroupOrderCell = CmpFindSubKeyByName(Hive, Node, &Name); 720 if (GroupOrderCell == HCELL_NIL) return FALSE; 721 722 /* Get Safe Boot cell */ 723 if (InitSafeBootMode) 724 { 725 /* Open the Safe Boot key */ 726 RtlInitUnicodeString(&Name, L"SafeBoot"); 727 Node = (PCM_KEY_NODE)HvGetCell(Hive, ControlCell); 728 if (!Node) return FALSE; 729 SafeBootCell = CmpFindSubKeyByName(Hive, Node, &Name); 730 if (SafeBootCell == HCELL_NIL) return FALSE; 731 732 /* Open the correct start key (depending on the mode) */ 733 Node = (PCM_KEY_NODE)HvGetCell(Hive, SafeBootCell); 734 if (!Node) return FALSE; 735 switch (InitSafeBootMode) 736 { 737 /* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */ 738 case 1: 739 case 3: RtlInitUnicodeString(&Name, L"Minimal"); break; 740 case 2: RtlInitUnicodeString(&Name, L"Network"); break; 741 default: return FALSE; 742 } 743 SafeBootCell = CmpFindSubKeyByName(Hive, Node, &Name); 744 if (SafeBootCell == HCELL_NIL) return FALSE; 745 } 746 747 /* Build the root registry path */ 748 RtlInitUnicodeString(&KeyPath, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"); 749 750 /* Enumerate each sub-key */ 751 i = 0; 752 DriverCell = CmpFindSubKeyByNumber(Hive, ServicesNode, i); 753 while (DriverCell != HCELL_NIL) 754 { 755 /* Make sure it's a driver of this start type AND is "safe" to load */ 756 if (CmpIsLoadType(Hive, DriverCell, LoadType) && 757 CmpIsSafe(Hive, SafeBootCell, DriverCell)) 758 { 759 /* Add it to the list */ 760 if (!CmpAddDriverToList(Hive, 761 DriverCell, 762 GroupOrderCell, 763 &KeyPath, 764 DriverListHead)) 765 { 766 CMTRACE(CM_BOOT_DEBUG, " Failed to add boot driver\n"); 767 } 768 } 769 770 /* Go to the next sub-key */ 771 DriverCell = CmpFindSubKeyByNumber(Hive, ServicesNode, ++i); 772 } 773 774 /* Check if we have a boot file system */ 775 if (BootFileSystem) 776 { 777 /* Find it */ 778 RtlInitUnicodeString(&Name, BootFileSystem); 779 DriverCell = CmpFindSubKeyByName(Hive, ServicesNode, &Name); 780 if (DriverCell != HCELL_NIL) 781 { 782 CMTRACE(CM_BOOT_DEBUG, "Adding Boot FileSystem '%S'\n", 783 BootFileSystem); 784 785 /* Always add it to the list */ 786 if (!CmpAddDriverToList(Hive, 787 DriverCell, 788 GroupOrderCell, 789 &KeyPath, 790 DriverListHead)) 791 { 792 CMTRACE(CM_BOOT_DEBUG, " Failed to add boot driver\n"); 793 } 794 else 795 { 796 /* Mark it as critical so it always loads */ 797 FsNode = CONTAINING_RECORD(DriverListHead->Flink, 798 BOOT_DRIVER_NODE, 799 ListEntry.Link); 800 FsNode->ErrorControl = SERVICE_ERROR_CRITICAL; 801 } 802 } 803 } 804 805 /* We're done! */ 806 return TRUE; 807 } 808 809 /** 810 * @brief 811 * Performs the driver list sorting, according to the ordering list. 812 * 813 * @param[in] Hive 814 * The SYSTEM hive. 815 * 816 * @param[in] ControlSet 817 * The control set registry key's hive cell. 818 * 819 * @param[in,out] DriverListHead 820 * The driver list to sort. 821 * 822 * @return 823 * TRUE if sorting has been successfully done, FALSE if not. 824 **/ 825 CODE_SEG("INIT") 826 static 827 BOOLEAN 828 CmpDoSort( 829 _Inout_ PLIST_ENTRY DriverListHead, 830 _In_ PCUNICODE_STRING OrderList) 831 { 832 PWCHAR Current, End = NULL; 833 UNICODE_STRING GroupName; 834 PLIST_ENTRY NextEntry; 835 PBOOT_DRIVER_NODE CurrentNode; 836 837 /* We're going from end to start, so get to the last group and keep going */ 838 Current = &OrderList->Buffer[OrderList->Length / sizeof(WCHAR)]; 839 while (Current > OrderList->Buffer) 840 { 841 /* Scan the current string */ 842 do 843 { 844 if (*Current == UNICODE_NULL) End = Current; 845 } while ((*(--Current - 1) != UNICODE_NULL) && (Current != OrderList->Buffer)); 846 847 /* This is our cleaned up string for this specific group */ 848 ASSERT(End != NULL); 849 GroupName.Length = (USHORT)(End - Current) * sizeof(WCHAR); 850 GroupName.MaximumLength = GroupName.Length; 851 GroupName.Buffer = Current; 852 853 /* Now loop the driver list */ 854 NextEntry = DriverListHead->Flink; 855 while (NextEntry != DriverListHead) 856 { 857 /* Get this node */ 858 CurrentNode = CONTAINING_RECORD(NextEntry, 859 BOOT_DRIVER_NODE, 860 ListEntry.Link); 861 862 /* Get the next entry now since we'll do a relink */ 863 NextEntry = NextEntry->Flink; 864 865 /* Is there a group name and does it match the current group? */ 866 if (CurrentNode->Group.Buffer && 867 RtlEqualUnicodeString(&GroupName, &CurrentNode->Group, TRUE)) 868 { 869 /* Remove from this location and re-link in the new one */ 870 RemoveEntryList(&CurrentNode->ListEntry.Link); 871 InsertHeadList(DriverListHead, &CurrentNode->ListEntry.Link); 872 } 873 } 874 875 /* Move on */ 876 --Current; 877 } 878 879 /* All done */ 880 return TRUE; 881 } 882 883 /** 884 * @brief 885 * Sorts the driver list, according to the drivers' group load ordering. 886 * 887 * @param[in] Hive 888 * The SYSTEM hive. 889 * 890 * @param[in] ControlSet 891 * The control set registry key's hive cell. 892 * 893 * @param[in,out] DriverListHead 894 * The driver list to sort. 895 * 896 * @return 897 * TRUE if sorting has been successfully done, FALSE if not. 898 **/ 899 CODE_SEG("INIT") 900 BOOLEAN 901 NTAPI 902 CmpSortDriverList( 903 _In_ PHHIVE Hive, 904 _In_ HCELL_INDEX ControlSet, 905 _Inout_ PLIST_ENTRY DriverListHead) 906 { 907 PCM_KEY_NODE Node; 908 PCM_KEY_VALUE ListValue; 909 HCELL_INDEX ControlCell, GroupOrder, ListCell; 910 UNICODE_STRING Name, OrderList; 911 ULONG Length; 912 913 /* Sanity check: We shouldn't need to release any acquired cells */ 914 ASSERT(Hive->ReleaseCellRoutine == NULL); 915 916 /* Open the control key */ 917 Node = (PCM_KEY_NODE)HvGetCell(Hive, ControlSet); 918 if (!Node) return FALSE; 919 RtlInitUnicodeString(&Name, L"Control"); 920 ControlCell = CmpFindSubKeyByName(Hive, Node, &Name); 921 if (ControlCell == HCELL_NIL) return FALSE; 922 923 /* Open the service group order */ 924 Node = (PCM_KEY_NODE)HvGetCell(Hive, ControlCell); 925 if (!Node) return FALSE; 926 RtlInitUnicodeString(&Name, L"ServiceGroupOrder"); 927 GroupOrder = CmpFindSubKeyByName(Hive, Node, &Name); 928 if (GroupOrder == HCELL_NIL) return FALSE; 929 930 /* Open the list key */ 931 Node = (PCM_KEY_NODE)HvGetCell(Hive, GroupOrder); 932 if (!Node) return FALSE; 933 RtlInitUnicodeString(&Name, L"list"); 934 ListCell = CmpFindValueByName(Hive, Node, &Name); 935 if (ListCell == HCELL_NIL) return FALSE; 936 937 /* Read the actual list */ 938 ListValue = (PCM_KEY_VALUE)HvGetCell(Hive, ListCell); 939 if (!ListValue) return FALSE; 940 if (ListValue->Type != REG_MULTI_SZ) return FALSE; 941 942 /* Copy it into a buffer */ 943 OrderList.Buffer = (PWCHAR)CmpValueToData(Hive, ListValue, &Length); 944 if (!OrderList.Buffer) return FALSE; 945 if (!IS_NULL_TERMINATED(OrderList.Buffer, Length)) return FALSE; 946 OrderList.Length = (USHORT)Length - sizeof(UNICODE_NULL); 947 OrderList.MaximumLength = OrderList.Length; 948 949 /* And start the sort algorithm */ 950 return CmpDoSort(DriverListHead, &OrderList); 951 } 952 953 CODE_SEG("INIT") 954 static 955 BOOLEAN 956 CmpOrderGroup( 957 _In_ PBOOT_DRIVER_NODE StartNode, 958 _In_ PBOOT_DRIVER_NODE EndNode) 959 { 960 PBOOT_DRIVER_NODE CurrentNode, PreviousNode; 961 PLIST_ENTRY ListEntry; 962 963 /* Base case, nothing to do */ 964 if (StartNode == EndNode) return TRUE; 965 966 /* Loop the nodes */ 967 CurrentNode = StartNode; 968 do 969 { 970 /* Save this as the previous node */ 971 PreviousNode = CurrentNode; 972 973 /* And move to the next one */ 974 ListEntry = CurrentNode->ListEntry.Link.Flink; 975 CurrentNode = CONTAINING_RECORD(ListEntry, 976 BOOT_DRIVER_NODE, 977 ListEntry.Link); 978 979 /* Check if the previous driver had a bigger tag */ 980 if (PreviousNode->Tag > CurrentNode->Tag) 981 { 982 /* Check if we need to update the tail */ 983 if (CurrentNode == EndNode) 984 { 985 /* Update the tail */ 986 ListEntry = CurrentNode->ListEntry.Link.Blink; 987 EndNode = CONTAINING_RECORD(ListEntry, 988 BOOT_DRIVER_NODE, 989 ListEntry.Link); 990 } 991 992 /* Remove this driver since we need to move it */ 993 RemoveEntryList(&CurrentNode->ListEntry.Link); 994 995 /* Keep looping until we find a driver with a lower tag than ours */ 996 while ((PreviousNode->Tag > CurrentNode->Tag) && (PreviousNode != StartNode)) 997 { 998 /* We'll be re-inserted at this spot */ 999 ListEntry = PreviousNode->ListEntry.Link.Blink; 1000 PreviousNode = CONTAINING_RECORD(ListEntry, 1001 BOOT_DRIVER_NODE, 1002 ListEntry.Link); 1003 } 1004 1005 /* Do the insert in the new location */ 1006 InsertTailList(&PreviousNode->ListEntry.Link, &CurrentNode->ListEntry.Link); 1007 1008 /* Update the head, if needed */ 1009 if (PreviousNode == StartNode) StartNode = CurrentNode; 1010 } 1011 } while (CurrentNode != EndNode); 1012 1013 /* All done */ 1014 return TRUE; 1015 } 1016 1017 /** 1018 * @brief 1019 * Removes potential circular dependencies (cycles) and sorts the driver list. 1020 * 1021 * @param[in,out] DriverListHead 1022 * The driver list to sort. 1023 * 1024 * @return 1025 * Always TRUE. 1026 **/ 1027 CODE_SEG("INIT") 1028 BOOLEAN 1029 NTAPI 1030 CmpResolveDriverDependencies( 1031 _Inout_ PLIST_ENTRY DriverListHead) 1032 { 1033 PLIST_ENTRY NextEntry; 1034 PBOOT_DRIVER_NODE StartNode, EndNode, CurrentNode; 1035 1036 /* Loop the list */ 1037 NextEntry = DriverListHead->Flink; 1038 while (NextEntry != DriverListHead) 1039 { 1040 /* Find the first entry */ 1041 StartNode = CONTAINING_RECORD(NextEntry, 1042 BOOT_DRIVER_NODE, 1043 ListEntry.Link); 1044 do 1045 { 1046 /* Find the last entry */ 1047 EndNode = CONTAINING_RECORD(NextEntry, 1048 BOOT_DRIVER_NODE, 1049 ListEntry.Link); 1050 1051 /* Get the next entry */ 1052 NextEntry = NextEntry->Flink; 1053 CurrentNode = CONTAINING_RECORD(NextEntry, 1054 BOOT_DRIVER_NODE, 1055 ListEntry.Link); 1056 1057 /* If the next entry is back to the top, break out */ 1058 if (NextEntry == DriverListHead) break; 1059 1060 /* Otherwise, check if this entry is equal */ 1061 if (!RtlEqualUnicodeString(&StartNode->Group, 1062 &CurrentNode->Group, 1063 TRUE)) 1064 { 1065 /* It is, so we've detected a cycle, break out */ 1066 break; 1067 } 1068 } while (NextEntry != DriverListHead); 1069 1070 /* Now we have the correct start and end pointers, so do the sort */ 1071 CmpOrderGroup(StartNode, EndNode); 1072 } 1073 1074 /* We're done */ 1075 return TRUE; 1076 } 1077 1078 CODE_SEG("INIT") 1079 static 1080 BOOLEAN 1081 CmpIsSafe( 1082 _In_ PHHIVE Hive, 1083 _In_ HCELL_INDEX SafeBootCell, 1084 _In_ HCELL_INDEX DriverCell) 1085 { 1086 PCM_KEY_NODE SafeBootNode; 1087 PCM_KEY_NODE DriverNode; 1088 PCM_KEY_VALUE KeyValue; 1089 HCELL_INDEX CellIndex; 1090 ULONG Length; 1091 UNICODE_STRING Name; 1092 PWCHAR Buffer; 1093 1094 /* Sanity check: We shouldn't need to release any acquired cells */ 1095 ASSERT(Hive->ReleaseCellRoutine == NULL); 1096 1097 /* Driver key node (mandatory) */ 1098 ASSERT(DriverCell != HCELL_NIL); 1099 DriverNode = (PCM_KEY_NODE)HvGetCell(Hive, DriverCell); 1100 if (!DriverNode) return FALSE; 1101 1102 /* Safe boot key node (optional but return TRUE if not present) */ 1103 if (SafeBootCell == HCELL_NIL) return TRUE; 1104 SafeBootNode = (PCM_KEY_NODE)HvGetCell(Hive, SafeBootCell); 1105 if (!SafeBootNode) return FALSE; 1106 1107 /* Search by the name from the group */ 1108 RtlInitUnicodeString(&Name, L"Group"); 1109 CellIndex = CmpFindValueByName(Hive, DriverNode, &Name); 1110 if (CellIndex != HCELL_NIL) 1111 { 1112 KeyValue = (PCM_KEY_VALUE)HvGetCell(Hive, CellIndex); 1113 if (!KeyValue) return FALSE; 1114 1115 if (KeyValue->Type == REG_SZ) // REG_EXPAND_SZ not really allowed there. 1116 { 1117 /* Compose the search 'key' */ 1118 Buffer = (PWCHAR)CmpValueToData(Hive, KeyValue, &Length); 1119 if (!Buffer) 1120 return FALSE; 1121 if (IS_NULL_TERMINATED(Buffer, Length)) 1122 Length -= sizeof(UNICODE_NULL); 1123 1124 Name.Buffer = Buffer; 1125 Name.Length = (USHORT)Length; 1126 Name.MaximumLength = Name.Length; 1127 1128 /* Search for corresponding key in the Safe Boot key */ 1129 CellIndex = CmpFindSubKeyByName(Hive, SafeBootNode, &Name); 1130 if (CellIndex != HCELL_NIL) return TRUE; 1131 } 1132 } 1133 1134 /* Group has not been found - find driver name */ 1135 Length = (DriverNode->Flags & KEY_COMP_NAME) ? 1136 CmpCompressedNameSize(DriverNode->Name, DriverNode->NameLength) : 1137 DriverNode->NameLength; 1138 if (Length < sizeof(WCHAR)) 1139 return FALSE; 1140 1141 /* Now allocate the buffer for it and copy the name */ 1142 RtlInitEmptyUnicodeString(&Name, 1143 Hive->Allocate(Length, FALSE, TAG_CM), 1144 (USHORT)Length); 1145 if (!Name.Buffer) 1146 return FALSE; 1147 1148 Name.Length = (USHORT)Length; 1149 if (DriverNode->Flags & KEY_COMP_NAME) 1150 { 1151 /* Compressed name */ 1152 CmpCopyCompressedName(Name.Buffer, 1153 Name.Length, 1154 DriverNode->Name, 1155 DriverNode->NameLength); 1156 } 1157 else 1158 { 1159 /* Normal name */ 1160 RtlCopyMemory(Name.Buffer, DriverNode->Name, DriverNode->NameLength); 1161 } 1162 1163 CellIndex = CmpFindSubKeyByName(Hive, SafeBootNode, &Name); 1164 Hive->Free(Name.Buffer, Name.MaximumLength); 1165 if (CellIndex != HCELL_NIL) return TRUE; 1166 1167 /* Not group or driver name - search by image name */ 1168 RtlInitUnicodeString(&Name, L"ImagePath"); 1169 CellIndex = CmpFindValueByName(Hive, DriverNode, &Name); 1170 if (CellIndex != HCELL_NIL) 1171 { 1172 KeyValue = (PCM_KEY_VALUE)HvGetCell(Hive, CellIndex); 1173 if (!KeyValue) return FALSE; 1174 1175 if ((KeyValue->Type == REG_SZ) || (KeyValue->Type == REG_EXPAND_SZ)) 1176 { 1177 /* Compose the search 'key' */ 1178 Buffer = (PWCHAR)CmpValueToData(Hive, KeyValue, &Length); 1179 if (!Buffer) return FALSE; 1180 if (Length < sizeof(WCHAR)) return FALSE; 1181 1182 /* Get the base image file name */ 1183 // FIXME: wcsrchr() may fail if Buffer is *not* NULL-terminated! 1184 Name.Buffer = wcsrchr(Buffer, OBJ_NAME_PATH_SEPARATOR); 1185 if (!Name.Buffer) return FALSE; 1186 ++Name.Buffer; 1187 1188 /* Length of the base name must be >=1 WCHAR */ 1189 if (((ULONG_PTR)Name.Buffer - (ULONG_PTR)Buffer) >= Length) 1190 return FALSE; 1191 Length -= ((ULONG_PTR)Name.Buffer - (ULONG_PTR)Buffer); 1192 if (IS_NULL_TERMINATED(Name.Buffer, Length)) 1193 Length -= sizeof(UNICODE_NULL); 1194 if (Length < sizeof(WCHAR)) return FALSE; 1195 1196 Name.Length = (USHORT)Length; 1197 Name.MaximumLength = Name.Length; 1198 1199 /* Search for corresponding key in the Safe Boot key */ 1200 CellIndex = CmpFindSubKeyByName(Hive, SafeBootNode, &Name); 1201 if (CellIndex != HCELL_NIL) return TRUE; 1202 } 1203 } 1204 1205 /* Nothing found - nothing else to search */ 1206 return FALSE; 1207 } 1208 1209 /** 1210 * @brief 1211 * Empties the driver list and frees all allocated driver nodes in it. 1212 * 1213 * @param[in] Hive 1214 * The SYSTEM hive (used only for the Hive->Free() memory deallocator). 1215 * 1216 * @param[in,out] DriverListHead 1217 * The driver list to free. 1218 * 1219 * @return None 1220 **/ 1221 CODE_SEG("INIT") 1222 VOID 1223 NTAPI 1224 CmpFreeDriverList( 1225 _In_ PHHIVE Hive, 1226 _Inout_ PLIST_ENTRY DriverListHead) 1227 { 1228 PLIST_ENTRY Entry; 1229 PBOOT_DRIVER_NODE DriverNode; 1230 1231 /* Loop through the list and remove each driver node */ 1232 while (!IsListEmpty(DriverListHead)) 1233 { 1234 /* Get the driver node */ 1235 Entry = RemoveHeadList(DriverListHead); 1236 DriverNode = CONTAINING_RECORD(Entry, 1237 BOOT_DRIVER_NODE, 1238 ListEntry.Link); 1239 1240 /* Free any allocated string buffers, then the node */ 1241 if (DriverNode->ListEntry.RegistryPath.Buffer) 1242 { 1243 Hive->Free(DriverNode->ListEntry.RegistryPath.Buffer, 1244 DriverNode->ListEntry.RegistryPath.MaximumLength); 1245 } 1246 if (DriverNode->ListEntry.FilePath.Buffer) 1247 { 1248 Hive->Free(DriverNode->ListEntry.FilePath.Buffer, 1249 DriverNode->ListEntry.FilePath.MaximumLength); 1250 } 1251 if (DriverNode->Name.Buffer) 1252 { 1253 Hive->Free(DriverNode->Name.Buffer, 1254 DriverNode->Name.MaximumLength); 1255 } 1256 Hive->Free(DriverNode, sizeof(BOOT_DRIVER_NODE)); 1257 } 1258 } 1259 1260 /* EOF */ 1261