1 /* 2 * COPYRIGHT: See COPYING.ARM in the top level directory 3 * PROJECT: ReactOS UEFI Boot Library 4 * FILE: boot/environ/lib/misc/bcd.c 5 * PURPOSE: Boot Library BCD Routines 6 * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org) 7 */ 8 9 /* INCLUDES ******************************************************************/ 10 11 #include "bl.h" 12 #include <bcd.h> 13 14 /* FUNCTIONS *****************************************************************/ 15 16 VOID 17 BiNotifyEnumerationError ( 18 _In_ HANDLE ObjectHandle, 19 _In_ PWCHAR ElementName, 20 _In_ NTSTATUS Status 21 ) 22 { 23 /* Stub for now */ 24 UNREFERENCED_PARAMETER(ObjectHandle); 25 UNREFERENCED_PARAMETER(ElementName); 26 UNREFERENCED_PARAMETER(Status); 27 EfiPrintf(L"Error in BiNotify: %lx for element %s\r\n", Status, ElementName); 28 } 29 30 ULONG 31 BiConvertElementFormatToValueType ( 32 _In_ ULONG Format 33 ) 34 { 35 /* Strings and objects are strings */ 36 if ((Format == BCD_TYPE_STRING) || (Format == BCD_TYPE_OBJECT)) 37 { 38 return REG_SZ; 39 } 40 41 /* Object lists are arrays of strings */ 42 if (Format == BCD_TYPE_OBJECT_LIST) 43 { 44 return REG_MULTI_SZ; 45 } 46 47 /* Everything else is binary */ 48 return REG_BINARY; 49 } 50 51 NTSTATUS 52 BiConvertRegistryDataToElement ( 53 _In_ HANDLE ObjectHandle, 54 _In_ PVOID Data, 55 _In_ ULONG DataLength, 56 _In_ BcdElementType ElementType, 57 _Out_ PVOID Element, 58 _Out_ PULONG ElementSize 59 ) 60 { 61 NTSTATUS Status; 62 ULONG Length, Size, ReturnedLength; 63 PBL_DEVICE_DESCRIPTOR Device; 64 BOOLEAN NullTerminate; 65 PBCD_DEVICE_OPTION BcdDevice, ElementDevice; 66 PWCHAR BcdString, ElementString; 67 PGUID ElementGuid; UNICODE_STRING GuidString; 68 PULONGLONG ElementInteger; 69 PUSHORT ElementWord; PBOOLEAN BcdBoolean; 70 71 /* Assume failure */ 72 ReturnedLength = 0; 73 74 /* Check what type of format we are dealing with */ 75 switch (ElementType.Format) 76 { 77 /* Devices -- they are in a binary format */ 78 case BCD_TYPE_DEVICE: 79 80 /* First, make sure it's at least big enough for an empty descriptor */ 81 if (DataLength < FIELD_OFFSET(BCD_DEVICE_OPTION, 82 DeviceDescriptor.Unknown)) 83 { 84 return STATUS_OBJECT_TYPE_MISMATCH; 85 } 86 87 /* Both the registry and BCD format are the same */ 88 BcdDevice = (PBCD_DEVICE_OPTION)Data; 89 ElementDevice = (PBCD_DEVICE_OPTION)Element; 90 91 /* Make sure the device fits in the registry data */ 92 Device = &BcdDevice->DeviceDescriptor; 93 Size = Device->Size; 94 if ((Size + sizeof(BcdDevice->AssociatedEntry)) != DataLength) 95 { 96 return STATUS_OBJECT_TYPE_MISMATCH; 97 } 98 99 /* Check if this is a locate device */ 100 if (Device->DeviceType == LocateDevice) 101 { 102 EfiPrintf(L"Locates not yet supported\r\n"); 103 return STATUS_NOT_SUPPORTED; 104 } 105 106 /* Make sure the caller's buffer can fit the device */ 107 ReturnedLength = Size + sizeof(BcdDevice->AssociatedEntry); 108 if (ReturnedLength > *ElementSize) 109 { 110 Status = STATUS_BUFFER_TOO_SMALL; 111 break; 112 } 113 114 /* It'll fit -- copy it in */ 115 RtlCopyMemory(&ElementDevice->DeviceDescriptor, Device, Size); 116 ElementDevice->AssociatedEntry = BcdDevice->AssociatedEntry; 117 Status = STATUS_SUCCESS; 118 break; 119 120 /* Strings -- they are stored as is */ 121 case BCD_TYPE_STRING: 122 123 /* Make sure the string isn't empty or misaligned */ 124 if (!(DataLength) || (DataLength & 1)) 125 { 126 return STATUS_OBJECT_TYPE_MISMATCH; 127 } 128 129 /* Both the registry and BCD format are the same */ 130 BcdString = (PWCHAR)Data; 131 ElementString = (PWCHAR)Element; 132 133 /* We'll need as much data as the string has to offer */ 134 ReturnedLength = DataLength; 135 136 /* If the string isn't NULL-terminated, do it now */ 137 NullTerminate = FALSE; 138 if (BcdString[(DataLength / sizeof(WCHAR)) - 1] != UNICODE_NULL) 139 { 140 ReturnedLength += sizeof(UNICODE_NULL); 141 NullTerminate = TRUE; 142 } 143 144 /* Will we fit in the caller's buffer? */ 145 if (ReturnedLength > *ElementSize) 146 { 147 Status = STATUS_BUFFER_TOO_SMALL; 148 break; 149 } 150 151 /* Yep -- copy it in, and NULL-terminate if needed */ 152 RtlCopyMemory(Element, Data, DataLength); 153 if (NullTerminate) 154 { 155 ElementString[DataLength / sizeof(WCHAR)] = UNICODE_NULL; 156 } 157 158 Status = STATUS_SUCCESS; 159 break; 160 161 /* Objects -- they are stored as GUID Strings */ 162 case BCD_TYPE_OBJECT: 163 164 /* Registry data is a string, BCD data is a GUID */ 165 BcdString = (PWCHAR)Data; 166 ElementGuid = (PGUID)Element; 167 168 /* We need a GUID-sized buffer, does the caller have one? */ 169 ReturnedLength = sizeof(*ElementGuid); 170 if (*ElementSize < ReturnedLength) 171 { 172 Status = STATUS_BUFFER_TOO_SMALL; 173 break; 174 } 175 176 /* Yep, copy the GUID */ 177 RtlInitUnicodeString(&GuidString, BcdString); 178 Status = RtlGUIDFromString(&GuidString, ElementGuid); 179 break; 180 181 /* Object Lists -- they are stored as arrays of GUID strings */ 182 case BCD_TYPE_OBJECT_LIST: 183 184 /* Assume an empty list*/ 185 ReturnedLength = 0; 186 Length = 0; 187 Status = STATUS_SUCCESS; 188 189 /* Registry data is an array of strings, BCD data is array of GUIDs */ 190 BcdString = (PWCHAR)Data; 191 ElementGuid = (PGUID)Element; 192 193 /* Loop as long as the array still has strings */ 194 while (*BcdString) 195 { 196 /* Don't read beyond the registry data */ 197 if (Length >= DataLength) 198 { 199 break; 200 } 201 202 /* One more GUID -- does the caller have space? */ 203 ReturnedLength += sizeof(GUID); 204 if (ReturnedLength <= *ElementSize) 205 { 206 /* Convert and add it in */ 207 RtlInitUnicodeString(&GuidString, BcdString); 208 Status = RtlGUIDFromString(&GuidString, ElementGuid); 209 if (!NT_SUCCESS(Status)) 210 { 211 break; 212 } 213 214 /* Move to the next GUID in the caller's buffer */ 215 ElementGuid++; 216 } 217 218 /* Move to the next string in the registry array */ 219 Size = (wcslen(BcdString) * sizeof(WCHAR)) + sizeof(UNICODE_NULL); 220 Length += Size; 221 BcdString = (PWCHAR)((ULONG_PTR)BcdString + Length); 222 } 223 224 /* Check if we failed anywhere */ 225 if (!NT_SUCCESS(Status)) 226 { 227 break; 228 } 229 230 /* Check if we consumed more space than we have */ 231 if (ReturnedLength > *ElementSize) 232 { 233 Status = STATUS_BUFFER_TOO_SMALL; 234 } 235 236 /* All good here */ 237 break; 238 239 /* Integer -- stored as binary */ 240 case BCD_TYPE_INTEGER: 241 242 /* BCD data is a ULONGLONG, registry data is 8 bytes binary */ 243 ElementInteger = (PULONGLONG)Element; 244 ReturnedLength = sizeof(*ElementInteger); 245 246 /* Make sure the registry data makes sense */ 247 if (DataLength > ReturnedLength) 248 { 249 return STATUS_OBJECT_TYPE_MISMATCH; 250 } 251 252 /* Make sure the caller has space */ 253 if (*ElementSize < ReturnedLength) 254 { 255 Status = STATUS_BUFFER_TOO_SMALL; 256 break; 257 } 258 259 /* Write the integer result */ 260 *ElementInteger = 0; 261 RtlCopyMemory(ElementInteger, Data, DataLength); 262 Status = STATUS_SUCCESS; 263 break; 264 265 /* Boolean -- stored as binary */ 266 case BCD_TYPE_BOOLEAN: 267 268 /* BCD data is a BOOLEAN, registry data is 2 bytes binary */ 269 ElementWord = (PUSHORT)Element; 270 BcdBoolean = (PBOOLEAN)Data; 271 ReturnedLength = sizeof(ElementWord); 272 273 /* Make sure the registry data makes sense */ 274 if (DataLength != sizeof(*BcdBoolean)) 275 { 276 return STATUS_OBJECT_TYPE_MISMATCH; 277 } 278 279 /* Make sure the caller has space */ 280 if (*ElementSize < ReturnedLength) 281 { 282 Status = STATUS_BUFFER_TOO_SMALL; 283 break; 284 } 285 286 /* Write the boolean result */ 287 *ElementWord = 0; 288 *ElementWord = *BcdBoolean != 0; 289 Status = STATUS_SUCCESS; 290 break; 291 292 /* Integer list --stored as binary */ 293 case BCD_TYPE_INTEGER_LIST: 294 295 /* BCD Data is n ULONGLONGs, registry data is n*8 bytes binary */ 296 ReturnedLength = DataLength; 297 if (!(DataLength) || (DataLength & 7)) 298 { 299 return STATUS_OBJECT_TYPE_MISMATCH; 300 } 301 302 /* Make sure the caller has space */ 303 if (*ElementSize < ReturnedLength) 304 { 305 Status = STATUS_BUFFER_TOO_SMALL; 306 break; 307 } 308 309 /* Write the integer list result */ 310 RtlCopyMemory(Element, Data, DataLength); 311 Status = STATUS_SUCCESS; 312 break; 313 314 /* Arbitrary data */ 315 default: 316 317 /* Registry data is copied binary as-is */ 318 ReturnedLength = DataLength; 319 320 /* Make sure it's not empty */ 321 if (!DataLength) 322 { 323 return STATUS_OBJECT_TYPE_MISMATCH; 324 } 325 326 /* Make sure the caller has space */ 327 if (*ElementSize < ReturnedLength) 328 { 329 Status = STATUS_BUFFER_TOO_SMALL; 330 break; 331 } 332 333 /* Write the result */ 334 RtlCopyMemory(Element, Data, DataLength); 335 Status = STATUS_SUCCESS; 336 break; 337 } 338 339 /* If we got here due to success or space issues, write the size */ 340 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) 341 { 342 *ElementSize = ReturnedLength; 343 } 344 345 /* All done, return our conversion result */ 346 return Status; 347 } 348 349 NTSTATUS 350 BiConvertBcdElements ( 351 _In_ PBCD_PACKED_ELEMENT Elements, 352 _Out_opt_ PBCD_ELEMENT Buffer, 353 _Inout_ PULONG BufferSize, 354 _Inout_ PULONG ElementCount 355 ) 356 { 357 NTSTATUS Status; 358 ULONG ElementSize, AlignedElementSize, AlignedDataSize; 359 PBCD_ELEMENT_HEADER Header; 360 PVOID Data; 361 BOOLEAN Exists; 362 ULONG i, j, Count; 363 364 /* Local variable to keep track of objects */ 365 Count = 0; 366 367 /* Safely compute the element bytes needed */ 368 Status = RtlULongMult(*ElementCount, sizeof(BCD_ELEMENT), &ElementSize); 369 if (!NT_SUCCESS(Status)) 370 { 371 return Status; 372 } 373 374 /* Safely align the element size */ 375 Status = RtlULongAdd(ElementSize, 376 sizeof(ULONG) - 1, 377 &AlignedElementSize); 378 if (!NT_SUCCESS(Status)) 379 { 380 return Status; 381 } 382 AlignedElementSize = ALIGN_DOWN(AlignedElementSize, ULONG); 383 384 /* Do a safe version of Add2Ptr to figure out where the headers will start */ 385 Status = RtlULongPtrAdd((ULONG_PTR)Buffer, 386 AlignedElementSize, 387 (PULONG_PTR)&Header); 388 if (!NT_SUCCESS(Status)) 389 { 390 return Status; 391 } 392 393 /* Safely compute the header bytes needed */ 394 Status = RtlULongMult(*ElementCount, 395 sizeof(BCD_ELEMENT_HEADER), 396 &ElementSize); 397 if (!NT_SUCCESS(Status)) 398 { 399 return Status; 400 } 401 402 /* Safely align the header size */ 403 Status = RtlULongAdd(ElementSize, 404 AlignedElementSize + sizeof(ULONG) - 1, 405 &AlignedElementSize); 406 if (!NT_SUCCESS(Status)) 407 { 408 return Status; 409 } 410 AlignedElementSize = ALIGN_DOWN(AlignedElementSize, ULONG); 411 412 /* Do a safe version of Add2Ptr */ 413 Status = RtlULongPtrAdd((ULONG_PTR)Buffer, 414 AlignedElementSize, 415 (PULONG_PTR)&Data); 416 if (!NT_SUCCESS(Status)) 417 { 418 return Status; 419 } 420 421 /* Iterate over every element */ 422 for (i = 0; i < *ElementCount; i++) 423 { 424 /* Safely align the element size */ 425 Status = RtlULongAdd(Elements->Size, 426 sizeof(ULONG) - 1, 427 &AlignedDataSize); 428 if (!NT_SUCCESS(Status)) 429 { 430 break; 431 } 432 AlignedDataSize = ALIGN_DOWN(AlignedDataSize, ULONG); 433 434 /* Safely add the size of this data element */ 435 Status = RtlULongAdd(AlignedElementSize, 436 AlignedDataSize, 437 &AlignedElementSize); 438 if (!NT_SUCCESS(Status)) 439 { 440 break; 441 } 442 443 /* Do we have enough space left? */ 444 if (*BufferSize >= AlignedElementSize) 445 { 446 /* Check if our root is an inherited object */ 447 Exists = FALSE; 448 if (Elements->RootType.PackedValue == BcdLibraryObjectList_InheritedObjects) 449 { 450 /* Yes, scan for us in the current buffer */ 451 for (j = 0; j < Count; j++) 452 { 453 /* Do we already exist? */ 454 while (Buffer[j].Header->Type == Elements->RootType.PackedValue) 455 { 456 /* Yep */ 457 Exists = TRUE; 458 break; 459 } 460 } 461 } 462 463 /* Have we already found ourselves? */ 464 if (!Exists) 465 { 466 /* Nope, one more entry */ 467 ++Count; 468 469 /* Write out the unpacked object */ 470 Buffer->Body = Data; 471 Buffer->Header = Header; 472 473 /* Fill out its header */ 474 Header->Size = Elements->Size; 475 Header->Type = Elements->Type; 476 Header->Version = Elements->Version; 477 478 /* And copy the data */ 479 RtlCopyMemory(Data, Elements->Data, Header->Size); 480 481 /* Move to the next unpacked object and header */ 482 ++Buffer; 483 ++Header; 484 485 /* Move to the next data entry */ 486 Data = (PVOID)((ULONG_PTR)Data + AlignedDataSize); 487 } 488 } 489 else 490 { 491 /* Nope, set failure code, but keep going so we can return count */ 492 Status = STATUS_BUFFER_TOO_SMALL; 493 } 494 495 /* Move to the next element entry */ 496 Elements = Elements->NextEntry; 497 } 498 499 /* Return the new final buffer size and count */ 500 *BufferSize = AlignedElementSize; 501 *ElementCount = Count; 502 return Status; 503 } 504 505 NTSTATUS 506 BcdOpenObject ( 507 _In_ HANDLE BcdHandle, 508 _In_ PGUID ObjectId, 509 _Out_ PHANDLE ObjectHandle 510 ) 511 { 512 NTSTATUS Status; 513 GUID LocalGuid; 514 UNICODE_STRING GuidString; 515 HANDLE RootObjectHandle; 516 517 /* Assume failure */ 518 *ObjectHandle = NULL; 519 520 /* Initialize GUID string */ 521 GuidString.Buffer = NULL; 522 523 /* Open the root "Objects" handle */ 524 RootObjectHandle = NULL; 525 Status = BiOpenKey(BcdHandle, L"Objects", &RootObjectHandle); 526 if (!NT_SUCCESS(Status)) 527 { 528 goto Quickie; 529 } 530 531 /* Capture the object ID and convert it into a string */ 532 LocalGuid = *ObjectId; 533 Status = RtlStringFromGUID(&LocalGuid, &GuidString); 534 if (!NT_SUCCESS(Status)) 535 { 536 goto Quickie; 537 } 538 539 /* Now open the key containing this object ID */ 540 Status = BiOpenKey(RootObjectHandle, GuidString.Buffer, ObjectHandle); 541 542 Quickie: 543 /* Free the GUID string if we had one allocated */ 544 if (GuidString.Buffer) 545 { 546 RtlFreeUnicodeString(&GuidString); 547 } 548 549 /* Close the root handle if it was open */ 550 if (RootObjectHandle) 551 { 552 BiCloseKey(RootObjectHandle); 553 } 554 555 /* Return the final status */ 556 return Status; 557 } 558 559 NTSTATUS 560 BcdDeleteElement ( 561 _In_ HANDLE ObjectHandle, 562 _In_ ULONG Type 563 ) 564 { 565 NTSTATUS Status; 566 HANDLE ElementsHandle, ElementHandle; 567 WCHAR TypeString[22]; 568 569 /* Open the elements key */ 570 Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle); 571 if (NT_SUCCESS(Status)) 572 { 573 /* Convert the element ID into a string */ 574 if (!_ultow(Type, TypeString, 16)) 575 { 576 /* Failed to do so */ 577 Status = STATUS_UNSUCCESSFUL; 578 } 579 else 580 { 581 /* Open the element specifically */ 582 Status = BiOpenKey(ElementsHandle, TypeString, &ElementHandle); 583 if (NT_SUCCESS(Status)) 584 { 585 /* Delete it */ 586 Status = BiDeleteKey(ElementHandle); 587 if (NT_SUCCESS(Status)) 588 { 589 /* No point in closing the handle anymore */ 590 ElementHandle = NULL; 591 } 592 } 593 else 594 { 595 /* The element doesn't exist */ 596 Status = STATUS_NOT_FOUND; 597 } 598 599 /* Check if we should close the key */ 600 if (ElementHandle) 601 { 602 /* Do it */ 603 BiCloseKey(ElementHandle); 604 } 605 } 606 } 607 608 /* Check if we should close the elements handle */ 609 if (ElementsHandle) 610 { 611 /* Do it */ 612 BiCloseKey(ElementsHandle); 613 } 614 615 /* Return whatever the result was */ 616 return Status; 617 } 618 619 NTSTATUS 620 BiEnumerateSubElements ( 621 _In_ HANDLE BcdHandle, 622 _In_ PVOID Object, 623 _In_ ULONG ElementType, 624 _In_ ULONG Flags, 625 _Out_opt_ PBCD_PACKED_ELEMENT* Elements, 626 _Inout_ PULONG ElementSize, 627 _Out_ PULONG ElementCount 628 ) 629 { 630 NTSTATUS Status; 631 PBCD_PACKED_ELEMENT Element; 632 HANDLE ObjectHandle; 633 ULONG ParsedElements, RequiredSize; 634 635 /* Assume empty */ 636 *ElementCount = 0; 637 RequiredSize = 0; 638 ParsedElements = 0; 639 640 /* Open the object */ 641 Status = BcdOpenObject(BcdHandle, Object, &ObjectHandle); 642 if (!NT_SUCCESS(Status)) 643 { 644 goto Quickie; 645 } 646 647 /* Read the first entry, and the size available */ 648 Element = *Elements; 649 RequiredSize = *ElementSize; 650 651 /* Enumerate the object into the element array */ 652 Status = BiEnumerateElements(BcdHandle, 653 ObjectHandle, 654 ElementType, 655 Flags, 656 Element, 657 &RequiredSize, 658 &ParsedElements); 659 660 /* Close the handle and bail out if we couldn't enumerate */ 661 BiCloseKey(ObjectHandle); 662 if (!NT_SUCCESS(Status)) 663 { 664 goto Quickie; 665 } 666 667 /* Check if the and subelements were present */ 668 if (ParsedElements) 669 { 670 /* Keep going until the last one */ 671 while (Element->NextEntry) 672 { 673 Element = Element->NextEntry; 674 } 675 676 /* Set the new buffer location to the last element */ 677 *Elements = Element; 678 } 679 680 Quickie: 681 /* Return the number of sub-elements and their size */ 682 *ElementCount = ParsedElements; 683 *ElementSize = RequiredSize; 684 return Status; 685 } 686 687 NTSTATUS 688 BiEnumerateSubObjectElements ( 689 _In_ HANDLE BcdHandle, 690 _Out_ PGUID SubObjectList, 691 _In_ ULONG SubObjectCount, 692 _In_ ULONG Flags, 693 _Out_opt_ PBCD_PACKED_ELEMENT Elements, 694 _Inout_ PULONG ElementSize, 695 _Out_ PULONG ElementCount 696 ) 697 { 698 NTSTATUS Status; 699 ULONG SubElementCount, TotalSize, RequiredSize, CurrentSize, i; 700 PBCD_PACKED_ELEMENT PreviousElement; 701 702 /* Assume empty list */ 703 *ElementCount = 0; 704 Status = STATUS_SUCCESS; 705 706 /* Initialize variables */ 707 TotalSize = 0; 708 PreviousElement = NULL; 709 710 /* Set the currently remaining size based on caller's input */ 711 CurrentSize = *ElementSize; 712 713 /* Iterate over every subje object */ 714 for (i = 0; i < SubObjectCount; i++) 715 { 716 /* Set the currently remaining buffer space */ 717 RequiredSize = CurrentSize; 718 719 /* Enumerate the inherited sub elements */ 720 Status = BiEnumerateSubElements(BcdHandle, 721 &SubObjectList[i], 722 BcdLibraryObjectList_InheritedObjects, 723 Flags, 724 &Elements, 725 &RequiredSize, 726 &SubElementCount); 727 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) 728 { 729 /* Safely add the length of the sub elements */ 730 Status = RtlULongAdd(TotalSize, RequiredSize, &TotalSize); 731 if (!NT_SUCCESS(Status)) 732 { 733 break; 734 } 735 736 /* Add the sub elements to the total */ 737 *ElementCount += SubElementCount; 738 739 /* See if we have enough space*/ 740 if (*ElementSize >= TotalSize) 741 { 742 /* Were there any subelements? */ 743 if (SubElementCount) 744 { 745 /* Update to keep track of these new subelements */ 746 CurrentSize = *ElementSize - TotalSize; 747 748 /* Link the subelements into the chain */ 749 PreviousElement = Elements; 750 PreviousElement->NextEntry = 751 (PBCD_PACKED_ELEMENT)((ULONG_PTR)Elements + TotalSize); 752 Elements = PreviousElement->NextEntry; 753 } 754 } 755 else 756 { 757 /* We're out of space */ 758 CurrentSize = 0; 759 } 760 } 761 else if ((Status != STATUS_NOT_FOUND) && 762 (Status != STATUS_OBJECT_NAME_NOT_FOUND)) 763 { 764 /* Some other fatal error, break out */ 765 break; 766 } 767 else 768 { 769 /* The sub element was not found, print a warning but keep going */ 770 BlStatusPrint(L"Ignoring missing BCD inherit object: {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", 771 (&SubObjectList[i])->Data1, 772 (&SubObjectList[i])->Data2, 773 (&SubObjectList[i])->Data3, 774 (&SubObjectList[i])->Data4[0], 775 (&SubObjectList[i])->Data4[1], 776 (&SubObjectList[i])->Data4[2], 777 (&SubObjectList[i])->Data4[3], 778 (&SubObjectList[i])->Data4[4], 779 (&SubObjectList[i])->Data4[5], 780 (&SubObjectList[i])->Data4[6], 781 (&SubObjectList[i])->Data4[7]); 782 Status = STATUS_SUCCESS; 783 } 784 } 785 786 /* Terminate the last element, if one was left */ 787 if (PreviousElement) 788 { 789 PreviousElement->NextEntry = NULL; 790 } 791 792 /* Set failure code if we ran out of space */ 793 if (*ElementSize < TotalSize) 794 { 795 Status = STATUS_BUFFER_TOO_SMALL; 796 } 797 798 /* Return final length and status */ 799 *ElementSize = TotalSize; 800 return Status; 801 } 802 803 NTSTATUS 804 BiEnumerateElements ( 805 _In_ HANDLE BcdHandle, 806 _In_ HANDLE ObjectHandle, 807 _In_ ULONG RootElementType, 808 _In_ ULONG Flags, 809 _Out_opt_ PBCD_PACKED_ELEMENT Elements, 810 _Inout_ PULONG ElementSize, 811 _Out_ PULONG ElementCount 812 ) 813 { 814 HANDLE ElementsHandle, ElementHandle; 815 ULONG TotalLength, RegistryElementDataLength, RemainingLength; 816 NTSTATUS Status; 817 ULONG i; 818 PVOID ElementData, SubObjectList, RegistryElementData; 819 BcdElementType ElementType; 820 PBCD_PACKED_ELEMENT PreviousElement, ElementsStart; 821 ULONG SubElementCount, SubKeyCount, SubObjectCount, ElementDataLength; 822 PWCHAR ElementName; 823 PWCHAR* SubKeys; 824 825 /* Assume failure */ 826 *ElementCount = 0; 827 828 /* Initialize all locals that are checked at the end*/ 829 SubKeys = NULL; 830 ElementsHandle = NULL; 831 ElementHandle = NULL; 832 ElementData = NULL; 833 RegistryElementData = NULL; 834 PreviousElement = NULL; 835 ElementName = NULL; 836 SubObjectList = NULL; 837 TotalLength = 0; 838 ElementDataLength = 0; 839 SubObjectCount = 0; 840 RemainingLength = 0; 841 ElementsStart = Elements; 842 843 /* Open the root object key's elements */ 844 Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle); 845 if (!NT_SUCCESS(Status)) 846 { 847 goto Quickie; 848 } 849 850 /* Enumerate all elements */ 851 Status = BiEnumerateSubKeys(ElementsHandle, &SubKeys, &SubKeyCount); 852 if (!NT_SUCCESS(Status)) 853 { 854 goto Quickie; 855 } 856 857 /* Iterate over each one */ 858 for (i = 0; i < SubKeyCount; i++) 859 { 860 /* Open the element */ 861 ElementName = SubKeys[i]; 862 Status = BiOpenKey(ElementsHandle, ElementName, &ElementHandle); 863 if (!NT_SUCCESS(Status)) 864 { 865 EfiPrintf(L"ELEMENT ERROR: %lx\r\n", Status); 866 EfiStall(100000); 867 break; 868 } 869 870 /* The name of the element is its data type */ 871 ElementType.PackedValue = wcstoul(SubKeys[i], NULL, 16); 872 if (!(ElementType.PackedValue) || (ElementType.PackedValue == -1)) 873 { 874 EfiPrintf(L"Value invalid\r\n"); 875 BiCloseKey(ElementHandle); 876 ElementHandle = NULL; 877 continue; 878 } 879 880 /* Read the appropriate registry value type for this element */ 881 Status = BiGetRegistryValue(ElementHandle, 882 L"Element", 883 BiConvertElementFormatToValueType( 884 ElementType.Format), 885 &RegistryElementData, 886 &RegistryElementDataLength); 887 if (!NT_SUCCESS(Status)) 888 { 889 EfiPrintf(L"Element invalid\r\n"); 890 break; 891 } 892 893 /* Now figure out how much space the converted element will need */ 894 ElementDataLength = 0; 895 Status = BiConvertRegistryDataToElement(ObjectHandle, 896 RegistryElementData, 897 RegistryElementDataLength, 898 ElementType, 899 NULL, 900 &ElementDataLength); 901 if (Status != STATUS_BUFFER_TOO_SMALL) 902 { 903 break; 904 } 905 906 /* Allocate a buffer big enough for the converted element */ 907 ElementData = BlMmAllocateHeap(ElementDataLength); 908 if (!ElementData) 909 { 910 Status = STATUS_INSUFFICIENT_RESOURCES; 911 break; 912 } 913 914 /* And actually convert it this time around */ 915 Status = BiConvertRegistryDataToElement(ObjectHandle, 916 RegistryElementData, 917 RegistryElementDataLength, 918 ElementType, 919 ElementData, 920 &ElementDataLength); 921 if (!NT_SUCCESS(Status)) 922 { 923 break; 924 } 925 926 /* Safely add space for the packed element header */ 927 Status = RtlULongAdd(TotalLength, 928 FIELD_OFFSET(BCD_PACKED_ELEMENT, Data), 929 &TotalLength); 930 if (!NT_SUCCESS(Status)) 931 { 932 break; 933 } 934 935 /* Safely add space for the data of the element itself */ 936 Status = RtlULongAdd(TotalLength, ElementDataLength, &TotalLength); 937 if (!NT_SUCCESS(Status)) 938 { 939 break; 940 } 941 942 /* One more element */ 943 ++*ElementCount; 944 945 /* See how much space we were given */ 946 RemainingLength = *ElementSize; 947 if (RemainingLength >= TotalLength) 948 { 949 /* Set the next pointer */ 950 Elements->NextEntry = (PBCD_PACKED_ELEMENT)((ULONG_PTR)ElementsStart + TotalLength); 951 952 /* Fill this one out */ 953 Elements->RootType.PackedValue = RootElementType; 954 Elements->Version = 1; 955 Elements->Type = ElementType.PackedValue; 956 Elements->Size = ElementDataLength; 957 958 /* Add the data */ 959 RtlCopyMemory(Elements->Data, ElementData, ElementDataLength); 960 RemainingLength -= TotalLength; 961 962 /* Move to the next element on the next pass */ 963 PreviousElement = Elements; 964 Elements = Elements->NextEntry; 965 } 966 else 967 { 968 /* We're out of space */ 969 RemainingLength = 0; 970 } 971 972 /* Are we enumerating devices, and is this a device? */ 973 if ((Flags & BCD_ENUMERATE_FLAG_DEVICES) && 974 (ElementType.Format == BCD_TYPE_DEVICE)) 975 { 976 /* Yep, so go inside to enumerate it */ 977 Status = BiEnumerateSubElements(BcdHandle, 978 ElementData, 979 ElementType.PackedValue, 980 Flags, 981 &Elements, 982 &ElementDataLength, 983 &SubElementCount); 984 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) 985 { 986 /* Safely add the length of the sub elements */ 987 Status = RtlULongAdd(TotalLength, 988 ElementDataLength, 989 &TotalLength); 990 if (!NT_SUCCESS(Status)) 991 { 992 break; 993 } 994 995 /* Add the sub elements to the total */ 996 *ElementCount += SubElementCount; 997 998 /* See if we have enough space*/ 999 if (*ElementSize >= TotalLength) 1000 { 1001 /* Were there any subelements? */ 1002 if (SubElementCount) 1003 { 1004 /* Update to keep track of these new subelements */ 1005 ElementDataLength = *ElementSize - TotalLength; 1006 1007 /* Link the subelements into the chain */ 1008 PreviousElement = Elements; 1009 PreviousElement->NextEntry = 1010 (PBCD_PACKED_ELEMENT)((ULONG_PTR)ElementsStart + 1011 TotalLength); 1012 Elements = PreviousElement->NextEntry; 1013 } 1014 } 1015 else 1016 { 1017 /* We're out of space */ 1018 ElementDataLength = 0; 1019 } 1020 } 1021 else if ((Status != STATUS_NOT_FOUND) && 1022 (Status != STATUS_OBJECT_NAME_NOT_FOUND)) 1023 { 1024 /* Fatal error trying to read the data, so fail */ 1025 break; 1026 } 1027 } 1028 else if ((Flags & BCD_ENUMERATE_FLAG_DEEP) && 1029 (ElementType.PackedValue == BcdLibraryObjectList_InheritedObjects)) 1030 { 1031 /* Inherited objects are requested, so allocate a buffer for them */ 1032 SubObjectList = BlMmAllocateHeap(ElementDataLength); 1033 if (!SubObjectList) 1034 { 1035 Status = STATUS_INSUFFICIENT_RESOURCES; 1036 break; 1037 } 1038 1039 /* Copy the elements into the list. They are arrays of GUIDs */ 1040 RtlCopyMemory(SubObjectList, ElementData, ElementDataLength); 1041 SubObjectCount = ElementDataLength / sizeof(GUID); 1042 } 1043 1044 /* Free our local buffers */ 1045 BlMmFreeHeap(ElementData); 1046 BlMmFreeHeap(RegistryElementData); 1047 ElementData = NULL; 1048 RegistryElementData = NULL; 1049 1050 /* Close the key */ 1051 BiCloseKey(ElementHandle); 1052 ElementHandle = NULL; 1053 ElementName = NULL; 1054 } 1055 1056 /* Did we end up here with a sub object list after successful loop parsing? */ 1057 if ((i != 0) && (i == SubKeyCount) && (SubObjectList)) 1058 { 1059 /* We will actually enumerate it now, at the end */ 1060 Status = BiEnumerateSubObjectElements(BcdHandle, 1061 SubObjectList, 1062 SubObjectCount, 1063 Flags, 1064 Elements, 1065 &RemainingLength, 1066 &SubElementCount); 1067 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) 1068 { 1069 /* Safely add the length of the sub elements */ 1070 Status = RtlULongAdd(TotalLength, RemainingLength, &TotalLength); 1071 if ((NT_SUCCESS(Status)) && (SubElementCount)) 1072 { 1073 /* Add the sub elements to the total */ 1074 *ElementCount += SubElementCount; 1075 1076 /* Don't touch PreviousElement anymore */ 1077 PreviousElement = NULL; 1078 } 1079 } 1080 } 1081 1082 Quickie: 1083 /* Free the sub object list, if any */ 1084 if (SubObjectList) 1085 { 1086 BlMmFreeHeap(SubObjectList); 1087 } 1088 1089 /* Free any local element data */ 1090 if (ElementData) 1091 { 1092 BlMmFreeHeap(ElementData); 1093 } 1094 1095 /* Free any local registry data */ 1096 if (RegistryElementData) 1097 { 1098 BlMmFreeHeap(RegistryElementData); 1099 } 1100 1101 /* Close the handle if still opened */ 1102 if (ElementHandle) 1103 { 1104 BiCloseKey(ElementHandle); 1105 } 1106 1107 /* Terminate the last element, if any */ 1108 if (PreviousElement) 1109 { 1110 PreviousElement->NextEntry = NULL; 1111 } 1112 1113 /* Close the root handle if still opened */ 1114 if (ElementsHandle) 1115 { 1116 BiCloseKey(ElementsHandle); 1117 } 1118 1119 /* Set failure code if out of space */ 1120 if (*ElementSize < TotalLength) 1121 { 1122 Status = STATUS_BUFFER_TOO_SMALL; 1123 } 1124 1125 /* Other errors will send a notification error */ 1126 if (!(NT_SUCCESS(Status)) && (Status != STATUS_BUFFER_TOO_SMALL)) 1127 { 1128 BiNotifyEnumerationError(ObjectHandle, ElementName, Status); 1129 } 1130 1131 /* Finally free the subkeys array */ 1132 if (SubKeys) 1133 { 1134 BlMmFreeHeap(SubKeys); 1135 } 1136 1137 /* And return the required, final length and status */ 1138 *ElementSize = TotalLength; 1139 return Status; 1140 } 1141 1142 NTSTATUS 1143 BiAddStoreFromFile ( 1144 _In_ PBL_FILE_PATH_DESCRIPTOR FilePath, 1145 _Out_ PHANDLE StoreHandle 1146 ) 1147 { 1148 NTSTATUS Status; 1149 HANDLE HiveHandle, KeyHandle; 1150 1151 /* Load the specified hive */ 1152 Status = BiLoadHive(FilePath, &HiveHandle); 1153 if (!NT_SUCCESS(Status)) 1154 { 1155 return Status; 1156 } 1157 1158 /* Open the description key to make sure this is really a BCD */ 1159 Status = BiOpenKey(HiveHandle, L"Description", &KeyHandle); 1160 if (NT_SUCCESS(Status)) 1161 { 1162 /* It is -- close the key as we don't need it */ 1163 BiCloseKey(KeyHandle); 1164 *StoreHandle = HiveHandle; 1165 } 1166 else 1167 { 1168 /* Failure, drop a reference on the hive and close the key */ 1169 BiDereferenceHive(HiveHandle); 1170 BiCloseKey(HiveHandle); 1171 } 1172 1173 /* Return the status */ 1174 return Status; 1175 } 1176 1177 NTSTATUS 1178 BiGetObjectDescription ( 1179 _In_ HANDLE ObjectHandle, 1180 _Out_ PBCD_OBJECT_DESCRIPTION Description 1181 ) 1182 { 1183 NTSTATUS Status; 1184 HANDLE DescriptionHandle; 1185 PULONG Data; 1186 ULONG Length; 1187 1188 /* Initialize locals */ 1189 Data = NULL; 1190 DescriptionHandle = NULL; 1191 1192 /* Open the description key */ 1193 Status = BiOpenKey(ObjectHandle, L"Description", &DescriptionHandle); 1194 if (NT_SUCCESS(Status)) 1195 { 1196 /* It exists */ 1197 Description->Valid = TRUE; 1198 1199 /* Read the type */ 1200 Length = 0; 1201 Status = BiGetRegistryValue(DescriptionHandle, 1202 L"Type", 1203 REG_DWORD, 1204 (PVOID*)&Data, 1205 &Length); 1206 if (NT_SUCCESS(Status)) 1207 { 1208 /* Make sure it's the length we expected it to be */ 1209 if (Length == sizeof(Data)) 1210 { 1211 /* Return the type that is stored there */ 1212 Description->Type = *Data; 1213 } 1214 else 1215 { 1216 /* Invalid type value */ 1217 Status = STATUS_OBJECT_TYPE_MISMATCH; 1218 } 1219 } 1220 } 1221 1222 /* Did we have a handle open? */ 1223 if (DescriptionHandle) 1224 { 1225 /* Close it */ 1226 BiCloseKey(DescriptionHandle); 1227 } 1228 1229 /* Did we have data allocated? */ 1230 if (Data) 1231 { 1232 /* Free it */ 1233 BlMmFreeHeap(Data); 1234 } 1235 1236 /* Return back to caller */ 1237 return Status; 1238 } 1239 1240 NTSTATUS 1241 BcdEnumerateAndUnpackElements ( 1242 _In_ HANDLE BcdHandle, 1243 _In_ HANDLE ObjectHandle, 1244 _Out_opt_ PBCD_ELEMENT Elements, 1245 _Inout_ PULONG ElementSize, 1246 _Out_ PULONG ElementCount 1247 ) 1248 { 1249 PVOID LocalElements; 1250 NTSTATUS Status; 1251 ULONG LocalElementCount, LocalElementSize; 1252 1253 /* Make sure required parameters are there */ 1254 if (!(ElementSize) || !(ElementCount) || ((Elements) && (!*ElementSize))) 1255 { 1256 return STATUS_INVALID_PARAMETER; 1257 } 1258 1259 /* Set initial count to zero */ 1260 *ElementCount = 0; 1261 1262 /* Do the initial enumeration to figure out the size required */ 1263 LocalElementSize = 0; 1264 LocalElementCount = 0; 1265 Status = BiEnumerateElements(BcdHandle, 1266 ObjectHandle, 1267 0, 1268 BCD_ENUMERATE_FLAG_IN_ORDER | 1269 BCD_ENUMERATE_FLAG_DEVICES | 1270 BCD_ENUMERATE_FLAG_DEEP, 1271 NULL, 1272 &LocalElementSize, 1273 &LocalElementCount); 1274 if (Status != STATUS_BUFFER_TOO_SMALL) 1275 { 1276 return Status; 1277 } 1278 1279 /* Now allocate a buffer large enough to hold them */ 1280 LocalElements = BlMmAllocateHeap(LocalElementSize); 1281 if (!LocalElements) 1282 { 1283 return STATUS_INSUFFICIENT_RESOURCES; 1284 } 1285 1286 /* Zero out the array and do the real enumeration this time around */ 1287 RtlZeroMemory(LocalElements, LocalElementSize); 1288 Status = BiEnumerateElements(BcdHandle, 1289 ObjectHandle, 1290 0, 1291 BCD_ENUMERATE_FLAG_IN_ORDER | 1292 BCD_ENUMERATE_FLAG_DEVICES | 1293 BCD_ENUMERATE_FLAG_DEEP, 1294 LocalElements, 1295 &LocalElementSize, 1296 &LocalElementCount); 1297 if (!NT_SUCCESS(Status)) 1298 { 1299 return Status; 1300 } 1301 1302 /* Now we know the real count */ 1303 *ElementCount = LocalElementCount; 1304 1305 /* Now unpack the data */ 1306 Status = BiConvertBcdElements(LocalElements, 1307 Elements, 1308 ElementSize, 1309 &LocalElementCount); 1310 if (NT_SUCCESS(Status)) 1311 { 1312 /* Not all elements may have been converted */ 1313 *ElementCount = LocalElementCount; 1314 } 1315 1316 /* Free the local (unpacked) buffer and return status */ 1317 BlMmFreeHeap(LocalElements); 1318 return Status; 1319 } 1320 1321 NTSTATUS 1322 BcdOpenStoreFromFile ( 1323 _In_ PUNICODE_STRING FileName, 1324 _In_ PHANDLE BcdHandle 1325 ) 1326 { 1327 ULONG Length; 1328 PBL_FILE_PATH_DESCRIPTOR FilePath; 1329 NTSTATUS Status; 1330 HANDLE LocalHandle; 1331 1332 /* Assume failure */ 1333 LocalHandle = NULL; 1334 1335 /* Allocate a path descriptor */ 1336 Length = FileName->Length + sizeof(*FilePath); 1337 FilePath = BlMmAllocateHeap(Length); 1338 if (!FilePath) 1339 { 1340 return STATUS_NO_MEMORY; 1341 } 1342 1343 /* Initialize it */ 1344 FilePath->Version = 1; 1345 FilePath->PathType = InternalPath; 1346 FilePath->Length = Length; 1347 1348 /* Copy the name and NULL-terminate it */ 1349 RtlCopyMemory(FilePath->Path, FileName->Buffer, Length); 1350 FilePath->Path[Length / sizeof(WCHAR)] = UNICODE_NULL; 1351 1352 /* Open the BCD */ 1353 Status = BiAddStoreFromFile(FilePath, &LocalHandle); 1354 if (NT_SUCCESS(Status)) 1355 { 1356 /* Return the handle on success */ 1357 *BcdHandle = LocalHandle; 1358 } 1359 1360 /* Free the descriptor and return the status */ 1361 BlMmFreeHeap(FilePath); 1362 return Status; 1363 } 1364 1365