X-Git-Url: https://git.reactos.org/?p=reactos.git;a=blobdiff_plain;f=reactos%2Fboot%2Fenviron%2Flib%2Fmisc%2Fbcd.c;h=12a94ae8210c2f27dd8aedc26e7bbfff3fe2108a;hp=58eed43173d1be97188da57ea856e0f0df90eb3b;hb=380484c985b9b4a457d20e1ff88d8ba3107835be;hpb=be0ecbd74e1476ed9f3599d8316f07e3f9c1e13b;ds=sidebyside diff --git a/reactos/boot/environ/lib/misc/bcd.c b/reactos/boot/environ/lib/misc/bcd.c index 58eed43173d..12a94ae8210 100644 --- a/reactos/boot/environ/lib/misc/bcd.c +++ b/reactos/boot/environ/lib/misc/bcd.c @@ -9,302 +9,1357 @@ /* INCLUDES ******************************************************************/ #include "bl.h" +#include /* FUNCTIONS *****************************************************************/ -PBL_BCD_OPTION -MiscGetBootOption ( - _In_ PBL_BCD_OPTION List, - _In_ ULONG Type +VOID +BiNotifyEnumerationError ( + _In_ HANDLE ObjectHandle, + _In_ PWCHAR ElementName, + _In_ NTSTATUS Status ) { - ULONG_PTR NextOption = 0, ListOption; - PBL_BCD_OPTION Option; + /* Stub for now */ + UNREFERENCED_PARAMETER(ObjectHandle); + UNREFERENCED_PARAMETER(ElementName); + UNREFERENCED_PARAMETER(Status); + EfiPrintf(L"Error in BiNotify: %lx for element %s\r\n", Status, ElementName); +} - /* No options, bail out */ - if (!List) +ULONG +BiConvertElementFormatToValueType ( + _In_ ULONG Format + ) +{ + /* Strings and objects are strings */ + if ((Format == BCD_TYPE_STRING) || (Format == BCD_TYPE_OBJECT)) { - return NULL; + return REG_SZ; } - /* Loop while we find an option */ - while (TRUE) + /* Object lists are arrays of strings */ + if (Format == BCD_TYPE_OBJECT_LIST) + { + return REG_MULTI_SZ; + } + + /* Everything else is binary */ + return REG_BINARY; +} + +NTSTATUS +BiConvertRegistryDataToElement ( + _In_ HANDLE ObjectHandle, + _In_ PVOID Data, + _In_ ULONG DataLength, + _In_ BcdElementType ElementType, + _Out_ PVOID Element, + _Out_ PULONG ElementSize + ) +{ + NTSTATUS Status; + ULONG Length, Size, ReturnedLength; + PBL_DEVICE_DESCRIPTOR Device; + BOOLEAN NullTerminate; + PBCD_DEVICE_OPTION BcdDevice, ElementDevice; + PWCHAR BcdString, ElementString; + PGUID ElementGuid; UNICODE_STRING GuidString; + PULONGLONG ElementInteger; + PUSHORT ElementWord; PBOOLEAN BcdBoolean; + + /* Assume failure */ + ReturnedLength = 0; + + /* Check what type of format we are dealing with */ + switch (ElementType.Format) { - /* Get the next option and see if it matches the type */ - Option = (PBL_BCD_OPTION)((ULONG_PTR)List + NextOption); - if ((Option->Type == Type) && !(Option->Empty)) + /* Devices -- they are in a binary format */ + case BCD_TYPE_DEVICE: + + /* First, make sure it's at least big enough for an empty descriptor */ + if (DataLength < FIELD_OFFSET(BCD_DEVICE_OPTION, + DeviceDescriptor.Unknown)) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Both the registry and BCD format are the same */ + BcdDevice = (PBCD_DEVICE_OPTION)Data; + ElementDevice = (PBCD_DEVICE_OPTION)Element; + + /* Make sure the device fits in the registry data */ + Device = &BcdDevice->DeviceDescriptor; + Size = Device->Size; + if ((Size + sizeof(BcdDevice->AssociatedEntry)) != DataLength) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Check if this is a locate device */ + if (Device->DeviceType == LocateDevice) + { + EfiPrintf(L"Locates not yet supported\r\n"); + return STATUS_NOT_SUPPORTED; + } + + /* Make sure the caller's buffer can fit the device */ + ReturnedLength = Size + sizeof(BcdDevice->AssociatedEntry); + if (ReturnedLength > *ElementSize) + { + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + + /* It'll fit -- copy it in */ + RtlCopyMemory(&ElementDevice->DeviceDescriptor, Device, Size); + ElementDevice->AssociatedEntry = BcdDevice->AssociatedEntry; + Status = STATUS_SUCCESS; + break; + + /* Strings -- they are stored as is */ + case BCD_TYPE_STRING: + + /* Make sure the string isn't empty or misaligned */ + if (!(DataLength) || (DataLength & 1)) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Both the registry and BCD format are the same */ + BcdString = (PWCHAR)Data; + ElementString = (PWCHAR)Element; + + /* We'll need as much data as the string has to offer */ + ReturnedLength = DataLength; + + /* If the string isn't NULL-terminated, do it now */ + NullTerminate = FALSE; + if (BcdString[(DataLength / sizeof(WCHAR)) - 1] != UNICODE_NULL) + { + ReturnedLength += sizeof(UNICODE_NULL); + NullTerminate = TRUE; + } + + /* Will we fit in the caller's buffer? */ + if (ReturnedLength > *ElementSize) + { + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + + /* Yep -- copy it in, and NULL-terminate if needed */ + RtlCopyMemory(Element, Data, DataLength); + if (NullTerminate) + { + ElementString[DataLength / sizeof(WCHAR)] = UNICODE_NULL; + } + + Status = STATUS_SUCCESS; + break; + + /* Objects -- they are stored as GUID Strings */ + case BCD_TYPE_OBJECT: + + /* Registry data is a string, BCD data is a GUID */ + BcdString = (PWCHAR)Data; + ElementGuid = (PGUID)Element; + + /* We need a GUID-sized buffer, does the caller have one? */ + ReturnedLength = sizeof(*ElementGuid); + if (*ElementSize < ReturnedLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + + /* Yep, copy the GUID */ + RtlInitUnicodeString(&GuidString, BcdString); + Status = RtlGUIDFromString(&GuidString, ElementGuid); + break; + + /* Object Lists -- they are stored as arrays of GUID strings */ + case BCD_TYPE_OBJECT_LIST: + + /* Assume an empty list*/ + ReturnedLength = 0; + Length = 0; + Status = STATUS_SUCCESS; + + /* Registry data is an array of strings, BCD data is array of GUIDs */ + BcdString = (PWCHAR)Data; + ElementGuid = (PGUID)Element; + + /* Loop as long as the array still has strings */ + while (*BcdString) + { + /* Don't read beyond the registry data */ + if (Length >= DataLength) + { + break; + } + + /* One more GUID -- does the caller have space? */ + ReturnedLength += sizeof(GUID); + if (ReturnedLength <= *ElementSize) + { + /* Convert and add it in */ + RtlInitUnicodeString(&GuidString, BcdString); + Status = RtlGUIDFromString(&GuidString, ElementGuid); + if (!NT_SUCCESS(Status)) + { + break; + } + + /* Move to the next GUID in the caller's buffer */ + ElementGuid++; + } + + /* Move to the next string in the registry array */ + Size = (wcslen(BcdString) * sizeof(WCHAR)) + sizeof(UNICODE_NULL); + Length += Size; + BcdString = (PWCHAR)((ULONG_PTR)BcdString + Length); + } + + /* Check if we failed anywhere */ + if (!NT_SUCCESS(Status)) + { + break; + } + + /* Check if we consumed more space than we have */ + if (ReturnedLength > *ElementSize) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + /* All good here */ + break; + + /* Integer -- stored as binary */ + case BCD_TYPE_INTEGER: + + /* BCD data is a ULONGLONG, registry data is 8 bytes binary */ + ElementInteger = (PULONGLONG)Element; + ReturnedLength = sizeof(*ElementInteger); + + /* Make sure the registry data makes sense */ + if (DataLength > ReturnedLength) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Make sure the caller has space */ + if (*ElementSize < ReturnedLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + + /* Write the integer result */ + *ElementInteger = 0; + RtlCopyMemory(ElementInteger, Data, DataLength); + Status = STATUS_SUCCESS; + break; + + /* Boolean -- stored as binary */ + case BCD_TYPE_BOOLEAN: + + /* BCD data is a BOOLEAN, registry data is 2 bytes binary */ + ElementWord = (PUSHORT)Element; + BcdBoolean = (PBOOLEAN)Data; + ReturnedLength = sizeof(ElementWord); + + /* Make sure the registry data makes sense */ + if (DataLength != sizeof(*BcdBoolean)) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Make sure the caller has space */ + if (*ElementSize < ReturnedLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + + /* Write the boolean result */ + *ElementWord = 0; + *ElementWord = *BcdBoolean != 0; + Status = STATUS_SUCCESS; + break; + + /* Integer list --stored as binary */ + case BCD_TYPE_INTEGER_LIST: + + /* BCD Data is n ULONGLONGs, registry data is n*8 bytes binary */ + ReturnedLength = DataLength; + if (!(DataLength) || (DataLength & 7)) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Make sure the caller has space */ + if (*ElementSize < ReturnedLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + + /* Write the integer list result */ + RtlCopyMemory(Element, Data, DataLength); + Status = STATUS_SUCCESS; + break; + + /* Arbitrary data */ + default: + + /* Registry data is copied binary as-is */ + ReturnedLength = DataLength; + + /* Make sure it's not empty */ + if (!DataLength) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Make sure the caller has space */ + if (*ElementSize < ReturnedLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + + /* Write the result */ + RtlCopyMemory(Element, Data, DataLength); + Status = STATUS_SUCCESS; + break; + } + + /* If we got here due to success or space issues, write the size */ + if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) + { + *ElementSize = ReturnedLength; + } + + /* All done, return our conversion result */ + return Status; +} + +NTSTATUS +BiConvertBcdElements ( + _In_ PBCD_PACKED_ELEMENT Elements, + _Out_opt_ PBCD_ELEMENT Buffer, + _Inout_ PULONG BufferSize, + _Inout_ PULONG ElementCount + ) +{ + NTSTATUS Status; + ULONG ElementSize, AlignedElementSize, AlignedDataSize; + PBCD_ELEMENT_HEADER Header; + PVOID Data; + BOOLEAN Exists; + ULONG i, j, Count; + + /* Local variable to keep track of objects */ + Count = 0; + + /* Safely compute the element bytes needed */ + Status = RtlULongMult(*ElementCount, sizeof(BCD_ELEMENT), &ElementSize); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Safely align the element size */ + Status = RtlULongAdd(ElementSize, + sizeof(ULONG) - 1, + &AlignedElementSize); + if (!NT_SUCCESS(Status)) + { + return Status; + } + AlignedElementSize = ALIGN_DOWN(AlignedElementSize, ULONG); + + /* Do a safe version of Add2Ptr to figure out where the headers will start */ + Status = RtlULongPtrAdd((ULONG_PTR)Buffer, + AlignedElementSize, + (PULONG_PTR)&Header); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Safely compute the header bytes needed */ + Status = RtlULongMult(*ElementCount, + sizeof(BCD_ELEMENT_HEADER), + &ElementSize); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Safely align the header size */ + Status = RtlULongAdd(ElementSize, + AlignedElementSize + sizeof(ULONG) - 1, + &AlignedElementSize); + if (!NT_SUCCESS(Status)) + { + return Status; + } + AlignedElementSize = ALIGN_DOWN(AlignedElementSize, ULONG); + + /* Do a safe version of Add2Ptr */ + Status = RtlULongPtrAdd((ULONG_PTR)Buffer, + AlignedElementSize, + (PULONG_PTR)&Data); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Iterate over every element */ + for (i = 0; i < *ElementCount; i++) + { + /* Safely align the element size */ + Status = RtlULongAdd(Elements->Size, + sizeof(ULONG) - 1, + &AlignedDataSize); + if (!NT_SUCCESS(Status)) { break; } + AlignedDataSize = ALIGN_DOWN(AlignedDataSize, ULONG); - /* Store the offset of the next option */ - NextOption = Option->NextEntryOffset; + /* Safely add the size of this data element */ + Status = RtlULongAdd(AlignedElementSize, + AlignedDataSize, + &AlignedElementSize); + if (!NT_SUCCESS(Status)) + { + break; + } - /* Failed to match. Check for list options */ - ListOption = Option->ListOffset; - if (ListOption) + /* Do we have enough space left? */ + if (*BufferSize >= AlignedElementSize) { - /* Try to get a match in the associated option */ - Option = MiscGetBootOption((PBL_BCD_OPTION)((ULONG_PTR)Option + - ListOption), - Type); + /* Check if our root is an inherited object */ + Exists = FALSE; + if (Elements->RootType.PackedValue == BcdLibraryObjectList_InheritedObjects) + { + /* Yes, scan for us in the current buffer */ + for (j = 0; j < Count; j++) + { + /* Do we already exist? */ + while (Buffer[j].Header->Type == Elements->RootType.PackedValue) + { + /* Yep */ + Exists = TRUE; + break; + } + } + } - /* Found one, return it */ - if (Option) + /* Have we already found ourselves? */ + if (!Exists) { - return Option; + /* Nope, one more entry */ + ++Count; + + /* Write out the unpacked object */ + Buffer->Body = Data; + Buffer->Header = Header; + + /* Fill out its header */ + Header->Size = Elements->Size; + Header->Type = Elements->Type; + Header->Version = Elements->Version; + + /* And copy the data */ + RtlCopyMemory(Data, Elements->Data, Header->Size); + + /* Move to the next unpacked object and header */ + ++Buffer; + ++Header; + + /* Move to the next data entry */ + Data = (PVOID)((ULONG_PTR)Data + AlignedDataSize); } } + else + { + /* Nope, set failure code, but keep going so we can return count */ + Status = STATUS_BUFFER_TOO_SMALL; + } + + /* Move to the next element entry */ + Elements = Elements->NextEntry; } - /* We found the option, return it */ - return Option; + /* Return the new final buffer size and count */ + *BufferSize = AlignedElementSize; + *ElementCount = Count; + return Status; } NTSTATUS -BlGetBootOptionString ( - _In_ PBL_BCD_OPTION List, - _In_ ULONG Type, - _Out_ PWCHAR* Value +BcdOpenObject ( + _In_ HANDLE BcdHandle, + _In_ PGUID ObjectId, + _Out_ PHANDLE ObjectHandle ) { NTSTATUS Status; - PBL_BCD_OPTION Option; - PWCHAR String, StringCopy; - ULONG StringLength, CopyLength; - //PGUID AppIdentifier; + GUID LocalGuid; + UNICODE_STRING GuidString; + HANDLE RootObjectHandle; - /* Make sure this is a BCD_STRING */ - if ((Type & 0xF000000) != 0x2000000) - { - return STATUS_INVALID_PARAMETER; - } + /* Assume failure */ + *ObjectHandle = NULL; + + /* Initialize GUID string */ + GuidString.Buffer = NULL; - /* Return the data */ - Option = MiscGetBootOption(List, Type); - if (Option) + /* Open the root "Objects" handle */ + RootObjectHandle = NULL; + Status = BiOpenKey(BcdHandle, L"Objects", &RootObjectHandle); + if (!NT_SUCCESS(Status)) { - /* Extract the string */ - String = (PWCHAR)((ULONG_PTR)Option + Option->DataOffset); + goto Quickie; } - else + + /* Capture the object ID and convert it into a string */ + LocalGuid = *ObjectId; + Status = RtlStringFromGUID(&LocalGuid, &GuidString); + if (!NT_SUCCESS(Status)) { - /* No string is present */ - String = NULL; + goto Quickie; } - /* Compute the data size */ - StringLength = Option->DataSize / sizeof(WCHAR); + /* Now open the key containing this object ID */ + Status = BiOpenKey(RootObjectHandle, GuidString.Buffer, ObjectHandle); -#ifdef _SECURE_BOOT_ - /* Filter out SecureBoot Options */ - AppIdentifier = BlGetApplicationIdentifier(); - Status = BlpBootOptionCallbackString(AppIdentifier, Type, String, StringLength, &String, &StringLength); -#else - Status = STATUS_SUCCESS; -#endif - - /* Check if we have space for one more character */ - CopyLength = StringLength + 1; - if (CopyLength < StringLength) +Quickie: + /* Free the GUID string if we had one allocated */ + if (GuidString.Buffer) { - /* Nope, we'll overflow */ - CopyLength = -1; - Status = STATUS_INTEGER_OVERFLOW; + RtlFreeUnicodeString(&GuidString); } - else + + /* Close the root handle if it was open */ + if (RootObjectHandle) { - /* Go ahead */ - Status = STATUS_SUCCESS; + BiCloseKey(RootObjectHandle); } - /* No overflow? */ + /* Return the final status */ + return Status; +} + +NTSTATUS +BcdDeleteElement ( + _In_ HANDLE ObjectHandle, + _In_ ULONG Type + ) +{ + NTSTATUS Status; + HANDLE ElementsHandle, ElementHandle; + WCHAR TypeString[22]; + + /* Open the elements key */ + Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle); if (NT_SUCCESS(Status)) { - /* Check if it's safe to multiply by two */ - if ((CopyLength * sizeof(WCHAR)) > 0xFFFFFFFF) + /* Convert the element ID into a string */ + if (!_ultow(Type, TypeString, 16)) { - /* Nope */ - CopyLength = -1; - Status = STATUS_INTEGER_OVERFLOW; + /* Failed to do so */ + Status = STATUS_UNSUCCESSFUL; } else { - /* We're good, do the multiplication */ - Status = STATUS_SUCCESS; - CopyLength *= sizeof(WCHAR); + /* Open the element specifically */ + Status = BiOpenKey(ElementsHandle, TypeString, &ElementHandle); + if (NT_SUCCESS(Status)) + { + /* Delete it */ + Status = BiDeleteKey(ElementHandle); + if (NT_SUCCESS(Status)) + { + /* No point in closing the handle anymore */ + ElementHandle = NULL; + } + } + else + { + /* The element doesn't exist */ + Status = STATUS_NOT_FOUND; + } + + /* Check if we should close the key */ + if (ElementHandle) + { + /* Do it */ + BiCloseKey(ElementHandle); + } } + } - /* Allocate a copy for the string */ - if (NT_SUCCESS(Status)) + /* Check if we should close the elements handle */ + if (ElementsHandle) + { + /* Do it */ + BiCloseKey(ElementsHandle); + } + + /* Return whatever the result was */ + return Status; +} + +NTSTATUS +BiEnumerateSubElements ( + _In_ HANDLE BcdHandle, + _In_ PVOID Object, + _In_ ULONG ElementType, + _In_ ULONG Flags, + _Out_opt_ PBCD_PACKED_ELEMENT* Elements, + _Inout_ PULONG ElementSize, + _Out_ PULONG ElementCount + ) +{ + NTSTATUS Status; + PBCD_PACKED_ELEMENT Element; + HANDLE ObjectHandle; + ULONG ParsedElements, RequiredSize; + + /* Assume empty */ + *ElementCount = 0; + RequiredSize = 0; + ParsedElements = 0; + + /* Open the object */ + Status = BcdOpenObject(BcdHandle, Object, &ObjectHandle); + if (!NT_SUCCESS(Status)) + { + goto Quickie; + } + + /* Read the first entry, and the size available */ + Element = *Elements; + RequiredSize = *ElementSize; + + /* Enumerate the object into the element array */ + Status = BiEnumerateElements(BcdHandle, + ObjectHandle, + ElementType, + Flags, + Element, + &RequiredSize, + &ParsedElements); + + /* Close the handle and bail out if we couldn't enumerate */ + BiCloseKey(ObjectHandle); + if (!NT_SUCCESS(Status)) + { + goto Quickie; + } + + /* Check if the and subelements were present */ + if (ParsedElements) + { + /* Keep going until the last one */ + while (Element->NextEntry) { - StringCopy = BlMmAllocateHeap(CopyLength); - if (StringCopy) + Element = Element->NextEntry; + } + + /* Set the new buffer location to the last element */ + *Elements = Element; + } + +Quickie: + /* Return the number of sub-elements and their size */ + *ElementCount = ParsedElements; + *ElementSize = RequiredSize; + return Status; +} + +NTSTATUS +BiEnumerateSubObjectElements ( + _In_ HANDLE BcdHandle, + _Out_ PGUID SubObjectList, + _In_ ULONG SubObjectCount, + _In_ ULONG Flags, + _Out_opt_ PBCD_PACKED_ELEMENT Elements, + _Inout_ PULONG ElementSize, + _Out_ PULONG ElementCount + ) +{ + NTSTATUS Status; + ULONG SubElementCount, TotalSize, RequiredSize, CurrentSize, i; + PBCD_PACKED_ELEMENT PreviousElement; + + /* Assume empty list */ + *ElementCount = 0; + Status = STATUS_SUCCESS; + + /* Initialize variables */ + TotalSize = 0; + PreviousElement = NULL; + + /* Set the currently remaining size based on caller's input */ + CurrentSize = *ElementSize; + + /* Iterate over every subje object */ + for (i = 0; i < SubObjectCount; i++) + { + /* Set the currently remaining buffer space */ + RequiredSize = CurrentSize; + + /* Enumerate the inherited sub elements */ + Status = BiEnumerateSubElements(BcdHandle, + &SubObjectList[i], + BcdLibraryObjectList_InheritedObjects, + Flags, + &Elements, + &RequiredSize, + &SubElementCount); + if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) + { + /* Safely add the length of the sub elements */ + Status = RtlULongAdd(TotalSize, RequiredSize, &TotalSize); + if (!NT_SUCCESS(Status)) { - /* NULL-terminate it */ - RtlCopyMemory(StringCopy, - String, - CopyLength - sizeof(UNICODE_NULL)); - StringCopy[CopyLength] = UNICODE_NULL; - *Value = StringCopy; - Status = STATUS_SUCCESS; + break; + } + + /* Add the sub elements to the total */ + *ElementCount += SubElementCount; + + /* See if we have enough space*/ + if (*ElementSize >= TotalSize) + { + /* Were there any subelements? */ + if (SubElementCount) + { + /* Update to keep track of these new subelements */ + CurrentSize = *ElementSize - TotalSize; + + /* Link the subelements into the chain */ + PreviousElement = Elements; + PreviousElement->NextEntry = + (PBCD_PACKED_ELEMENT)((ULONG_PTR)Elements + TotalSize); + Elements = PreviousElement->NextEntry; + } } else { - /* No memory, fail */ - Status = STATUS_NO_MEMORY; + /* We're out of space */ + CurrentSize = 0; } } + else if ((Status != STATUS_NOT_FOUND) && + (Status != STATUS_OBJECT_NAME_NOT_FOUND)) + { + /* Some other fatal error, break out */ + break; + } + else + { + /* The sub element was not found, print a warning but keep going */ + BlStatusPrint(L"Ignoring missing BCD inherit object: {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", + (&SubObjectList[i])->Data1, + (&SubObjectList[i])->Data2, + (&SubObjectList[i])->Data3, + (&SubObjectList[i])->Data4[0], + (&SubObjectList[i])->Data4[1], + (&SubObjectList[i])->Data4[2], + (&SubObjectList[i])->Data4[3], + (&SubObjectList[i])->Data4[4], + (&SubObjectList[i])->Data4[5], + (&SubObjectList[i])->Data4[6], + (&SubObjectList[i])->Data4[7], + (&SubObjectList[i])->Data4[8]); + Status = STATUS_SUCCESS; + } + } + + /* Terminate the last element, if one was left */ + if (PreviousElement) + { + PreviousElement->NextEntry = NULL; + } + + /* Set failure code if we ran out of space */ + if (*ElementSize < TotalSize) + { + Status = STATUS_BUFFER_TOO_SMALL; } - /* All done */ + /* Return final length and status */ + *ElementSize = TotalSize; return Status; } NTSTATUS -BlGetBootOptionInteger ( - _In_ PBL_BCD_OPTION List, - _In_ ULONG Type, - _Out_ PULONGLONG Value +BiEnumerateElements ( + _In_ HANDLE BcdHandle, + _In_ HANDLE ObjectHandle, + _In_ ULONG RootElementType, + _In_ ULONG Flags, + _Out_opt_ PBCD_PACKED_ELEMENT Elements, + _Inout_ PULONG ElementSize, + _Out_ PULONG ElementCount ) { + HANDLE ElementsHandle, ElementHandle; + ULONG TotalLength, RegistryElementDataLength, RemainingLength; NTSTATUS Status; - PBL_BCD_OPTION Option; - //PGUID AppIdentifier; + ULONG i; + PVOID ElementData, SubObjectList, RegistryElementData; + BcdElementType ElementType; + PBCD_PACKED_ELEMENT PreviousElement, ElementsStart; + ULONG SubElementCount, SubKeyCount, SubObjectCount, ElementDataLength; + PWCHAR ElementName; + PWCHAR* SubKeys; + + /* Assume failure */ + *ElementCount = 0; + + /* Initialize all locals that are checked at the end*/ + SubKeys = NULL; + ElementsHandle = NULL; + ElementHandle = NULL; + ElementData = NULL; + RegistryElementData = NULL; + PreviousElement = NULL; + ElementName = NULL; + SubObjectList = NULL; + TotalLength = 0; + ElementDataLength = 0; + SubObjectCount = 0; + RemainingLength = 0; + ElementsStart = Elements; - /* Make sure this is a BCD_INTEGER */ - if ((Type & 0xF000000) != 0x5000000) + /* Open the root object key's elements */ + Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle); + if (!NT_SUCCESS(Status)) { - return STATUS_INVALID_PARAMETER; + goto Quickie; + } + + /* Enumerate all elements */ + Status = BiEnumerateSubKeys(ElementsHandle, &SubKeys, &SubKeyCount); + if (!NT_SUCCESS(Status)) + { + goto Quickie; + } + + /* Iterate over each one */ + for (i = 0; i < SubKeyCount; i++) + { + /* Open the element */ + ElementName = SubKeys[i]; + Status = BiOpenKey(ElementsHandle, ElementName, &ElementHandle); + if (!NT_SUCCESS(Status)) + { + EfiPrintf(L"ELEMENT ERROR: %lx\r\n", Status); + EfiStall(100000); + break; + } + + /* The name of the element is its data type */ + ElementType.PackedValue = wcstoul(SubKeys[i], NULL, 16); + if (!(ElementType.PackedValue) || (ElementType.PackedValue == -1)) + { + EfiPrintf(L"Value invalid\r\n"); + BiCloseKey(ElementHandle); + ElementHandle = NULL; + continue; + } + + /* Read the appropriate registry value type for this element */ + Status = BiGetRegistryValue(ElementHandle, + L"Element", + BiConvertElementFormatToValueType( + ElementType.Format), + &RegistryElementData, + &RegistryElementDataLength); + if (!NT_SUCCESS(Status)) + { + EfiPrintf(L"Element invalid\r\n"); + break; + } + + /* Now figure out how much space the converted element will need */ + ElementDataLength = 0; + Status = BiConvertRegistryDataToElement(ObjectHandle, + RegistryElementData, + RegistryElementDataLength, + ElementType, + NULL, + &ElementDataLength); + if (Status != STATUS_BUFFER_TOO_SMALL) + { + break; + } + + /* Allocate a buffer big enough for the converted element */ + ElementData = BlMmAllocateHeap(ElementDataLength); + if (!ElementData) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + /* And actually convert it this time around */ + Status = BiConvertRegistryDataToElement(ObjectHandle, + RegistryElementData, + RegistryElementDataLength, + ElementType, + ElementData, + &ElementDataLength); + if (!NT_SUCCESS(Status)) + { + break; + } + + /* Safely add space for the packed element header */ + Status = RtlULongAdd(TotalLength, + FIELD_OFFSET(BCD_PACKED_ELEMENT, Data), + &TotalLength); + if (!NT_SUCCESS(Status)) + { + break; + } + + /* Safely add space for the data of the element itself */ + Status = RtlULongAdd(TotalLength, ElementDataLength, &TotalLength); + if (!NT_SUCCESS(Status)) + { + break; + } + + /* One more element */ + ++*ElementCount; + + /* See how much space we were given */ + RemainingLength = *ElementSize; + if (RemainingLength >= TotalLength) + { + /* Set the next pointer */ + Elements->NextEntry = (PBCD_PACKED_ELEMENT)((ULONG_PTR)ElementsStart + TotalLength); + + /* Fill this one out */ + Elements->RootType.PackedValue = RootElementType; + Elements->Version = 1; + Elements->Type = ElementType.PackedValue; + Elements->Size = ElementDataLength; + + /* Add the data */ + RtlCopyMemory(Elements->Data, ElementData, ElementDataLength); + RemainingLength -= TotalLength; + + /* Move to the next element on the next pass */ + PreviousElement = Elements; + Elements = Elements->NextEntry; + } + else + { + /* We're out of space */ + RemainingLength = 0; + } + + /* Are we enumerating devices, and is this a device? */ + if ((Flags & BCD_ENUMERATE_FLAG_DEVICES) && + (ElementType.Format == BCD_TYPE_DEVICE)) + { + /* Yep, so go inside to enumerate it */ + Status = BiEnumerateSubElements(BcdHandle, + ElementData, + ElementType.PackedValue, + Flags, + &Elements, + &ElementDataLength, + &SubElementCount); + if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) + { + /* Safely add the length of the sub elements */ + Status = RtlULongAdd(TotalLength, + ElementDataLength, + &TotalLength); + if (!NT_SUCCESS(Status)) + { + break; + } + + /* Add the sub elements to the total */ + *ElementCount += SubElementCount; + + /* See if we have enough space*/ + if (*ElementSize >= TotalLength) + { + /* Were there any subelements? */ + if (SubElementCount) + { + /* Update to keep track of these new subelements */ + ElementDataLength = *ElementSize - TotalLength; + + /* Link the subelements into the chain */ + PreviousElement = Elements; + PreviousElement->NextEntry = + (PBCD_PACKED_ELEMENT)((ULONG_PTR)ElementsStart + + TotalLength); + Elements = PreviousElement->NextEntry; + } + } + else + { + /* We're out of space */ + ElementDataLength = 0; + } + } + else if ((Status != STATUS_NOT_FOUND) && + (Status != STATUS_OBJECT_NAME_NOT_FOUND)) + { + /* Fatal error trying to read the data, so fail */ + break; + } + } + else if ((Flags & BCD_ENUMERATE_FLAG_DEEP) && + (ElementType.PackedValue == BcdLibraryObjectList_InheritedObjects)) + { + /* Inherited objects are requsted, so allocate a buffer for them */ + SubObjectList = BlMmAllocateHeap(ElementDataLength); + if (!SubObjectList) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + /* Copy the elements into the list. They are arrays of GUIDs */ + RtlCopyMemory(SubObjectList, ElementData, ElementDataLength); + SubObjectCount = ElementDataLength / sizeof(GUID); + } + + /* Free our local buffers */ + BlMmFreeHeap(ElementData); + BlMmFreeHeap(RegistryElementData); + ElementData = NULL; + RegistryElementData = NULL; + + /* Close the key */ + BiCloseKey(ElementHandle); + ElementHandle = NULL; + ElementName = NULL; + } + + /* Did we end up here with a sub object list after successful loop parsing? */ + if ((i != 0) && (i == SubKeyCount) && (SubObjectList)) + { + /* We will actually enumerate it now, at the end */ + Status = BiEnumerateSubObjectElements(BcdHandle, + SubObjectList, + SubObjectCount, + Flags, + Elements, + &RemainingLength, + &SubElementCount); + if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) + { + /* Safely add the length of the sub elements */ + Status = RtlULongAdd(TotalLength, RemainingLength, &TotalLength); + if ((NT_SUCCESS(Status)) && (SubElementCount)) + { + /* Add the sub elements to the total */ + *ElementCount += SubElementCount; + + /* Don't touch PreviousElement anymore */ + PreviousElement = NULL; + } + } } - /* Return the data */ - Option = MiscGetBootOption(List, Type); - if (Option) +Quickie: + /* Free the sub object list, if any */ + if (SubObjectList) { - *Value = *(PULONGLONG)((ULONG_PTR)Option + Option->DataOffset); + BlMmFreeHeap(SubObjectList); } -#ifdef _SECURE_BOOT_ - /* Filter out SecureBoot Options */ - AppIdentifier = BlGetApplicationIdentifier(); - Status = BlpBootOptionCallbackULongLong(AppIdentifier, Type, Value); -#else - /* Option found */ - Status = Option ? STATUS_SUCCESS : STATUS_NOT_FOUND; -#endif + /* Free any local element data */ + if (ElementData) + { + BlMmFreeHeap(ElementData); + } + + /* Free any local registry data */ + if (RegistryElementData) + { + BlMmFreeHeap(RegistryElementData); + } + + /* Close the handle if still opened */ + if (ElementHandle) + { + BiCloseKey(ElementHandle); + } + + /* Terminate the last element, if any */ + if (PreviousElement) + { + PreviousElement->NextEntry = NULL; + } + + /* Close the root handle if still opened */ + if (ElementsHandle) + { + BiCloseKey(ElementsHandle); + } + + /* Set failure code if out of space */ + if (*ElementSize < TotalLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + /* Other errors will send a notification error */ + if (!(NT_SUCCESS(Status)) && (Status != STATUS_BUFFER_TOO_SMALL)) + { + BiNotifyEnumerationError(ObjectHandle, ElementName, Status); + } + + /* Finally free the subkeys array */ + if (SubKeys) + { + BlMmFreeHeap(SubKeys); + } + + /* And return the required, final length and status */ + *ElementSize = TotalLength; return Status; } NTSTATUS -BlGetBootOptionBoolean ( - _In_ PBL_BCD_OPTION List, - _In_ ULONG Type, - _Out_ PBOOLEAN Value +BiAddStoreFromFile ( + _In_ PBL_FILE_PATH_DESCRIPTOR FilePath, + _Out_ PHANDLE StoreHandle ) { NTSTATUS Status; - PBL_BCD_OPTION Option; - //PGUID AppIdentifier; + HANDLE HiveHandle, KeyHandle; - /* Make sure this is a BCD_BOOLEAN */ - if ((Type & 0xF000000) != 0x6000000) + /* Load the specified hive */ + Status = BiLoadHive(FilePath, &HiveHandle); + if (!NT_SUCCESS(Status)) { - return STATUS_INVALID_PARAMETER; + return Status; } - /* Return the data */ - Option = MiscGetBootOption(List, Type); - if (Option) + /* Open the description key to make sure this is really a BCD */ + Status = BiOpenKey(HiveHandle, L"Description", &KeyHandle); + if (NT_SUCCESS(Status)) + { + /* It is -- close the key as we don't need it */ + BiCloseKey(KeyHandle); + *StoreHandle = HiveHandle; + } + else { - *Value = *(PBOOLEAN)((ULONG_PTR)Option + Option->DataOffset); + /* Failure, drop a reference on the hive and close the key */ + BiDereferenceHive(HiveHandle); + BiCloseKey(HiveHandle); } -#ifdef _SECURE_BOOT_ - /* Filter out SecureBoot Options */ - AppIdentifier = BlGetApplicationIdentifier(); - Status = BlpBootOptionCallbackBoolean(AppIdentifier, Type, Value); -#else - /* Option found */ - Status = Option ? STATUS_SUCCESS : STATUS_NOT_FOUND; -#endif + /* Return the status */ return Status; } -/*++ - * @name BlGetBootOptionListSize - * - * The BlGetBootOptionListSize routine - * - * @param BcdOption - * UEFI Image Handle for the current loaded application. - * - * @return Size of the BCD option - * - *--*/ -ULONG -BlGetBootOptionListSize ( - _In_ PBL_BCD_OPTION BcdOption +NTSTATUS +BiGetObjectDescription ( + _In_ HANDLE ObjectHandle, + _Out_ PBCD_OBJECT_DESCRIPTION Description ) { - ULONG Size = 0, NextOffset = 0; - PBL_BCD_OPTION NextOption; + NTSTATUS Status; + HANDLE DescriptionHandle; + PULONG Data; + ULONG Length; + + /* Initialize locals */ + Data = NULL; + DescriptionHandle = NULL; - /* Loop all the options*/ - do + /* Open the description key */ + Status = BiOpenKey(ObjectHandle, L"Description", &DescriptionHandle); + if (NT_SUCCESS(Status)) { - /* Move to the next one */ - NextOption = (PBL_BCD_OPTION)((ULONG_PTR)BcdOption + NextOffset); + /* It exists */ + Description->Valid = TRUE; - /* Compute the size of the next one */ - Size += BlGetBootOptionSize(NextOption); + /* Read the type */ + Length = 0; + Status = BiGetRegistryValue(DescriptionHandle, + L"Type", + REG_DWORD, + (PVOID*)&Data, + &Length); + if (NT_SUCCESS(Status)) + { + /* Make sure it's the length we expected it to be */ + if (Length == sizeof(Data)) + { + /* Return the type that is stored there */ + Description->Type = *Data; + } + else + { + /* Invalid type value */ + Status = STATUS_OBJECT_TYPE_MISMATCH; + } + } + } + + /* Did we have a handle open? */ + if (DescriptionHandle) + { + /* Close it */ + BiCloseKey(DescriptionHandle); + } - /* Update the offset */ - NextOffset = NextOption->NextEntryOffset; - } while (NextOffset != 0); + /* Did we have data allocated? */ + if (Data) + { + /* Free it */ + BlMmFreeHeap(Data); + } - /* Return final computed size */ - return Size; + /* Return back to caller */ + return Status; } -/*++ - * @name BlGetBootOptionSize - * - * The BlGetBootOptionSize routine - * - * @param BcdOption - * UEFI Image Handle for the current loaded application. - * - * @return Size of the BCD option - * - *--*/ -ULONG -BlGetBootOptionSize ( - _In_ PBL_BCD_OPTION BcdOption +NTSTATUS +BcdEnumerateAndUnpackElements ( + _In_ HANDLE BcdHandle, + _In_ HANDLE ObjectHandle, + _Out_opt_ PBCD_ELEMENT Elements, + _Inout_ PULONG ElementSize, + _Out_ PULONG ElementCount ) { - ULONG Size, Offset; + PVOID LocalElements; + NTSTATUS Status; + ULONG LocalElementCount, LocalElementSize; - /* Check if there's any data */ - if (BcdOption->DataOffset != 0) + /* Make sure required parameters are there */ + if (!(ElementSize) || !(ElementCount) || ((Elements) && (!*ElementSize))) { - /* Add the size of the data */ - Size = BcdOption->DataOffset + BcdOption->DataSize; + return STATUS_INVALID_PARAMETER; } - else + + /* Set initial count to zero */ + *ElementCount = 0; + + /* Do the initial enumeration to figure out the size required */ + LocalElementSize = 0; + LocalElementCount = 0; + Status = BiEnumerateElements(BcdHandle, + ObjectHandle, + 0, + BCD_ENUMERATE_FLAG_IN_ORDER | + BCD_ENUMERATE_FLAG_DEVICES | + BCD_ENUMERATE_FLAG_DEEP, + NULL, + &LocalElementSize, + &LocalElementCount); + if (Status != STATUS_BUFFER_TOO_SMALL) + { + return Status; + } + + /* Now allocate a buffer large enough to hold them */ + LocalElements = BlMmAllocateHeap(LocalElementSize); + if (!LocalElements) { - /* No data, just the structure itself */ - Size = sizeof(*BcdOption); + return STATUS_INSUFFICIENT_RESOURCES; } - /* Any associated options? */ - Offset = BcdOption->ListOffset; - if (Offset != 0) + /* Zero out the array and do the real enumeration this time around */ + RtlZeroMemory(LocalElements, LocalElementSize); + Status = BiEnumerateElements(BcdHandle, + ObjectHandle, + 0, + BCD_ENUMERATE_FLAG_IN_ORDER | + BCD_ENUMERATE_FLAG_DEVICES | + BCD_ENUMERATE_FLAG_DEEP, + LocalElements, + &LocalElementSize, + &LocalElementCount); + if (!NT_SUCCESS(Status)) { - /* Go get those too */ - Size += BlGetBootOptionListSize((PVOID)((ULONG_PTR)BcdOption + Offset)); + return Status; } - /* Return the final size */ - return Size; + /* Now we know the real count */ + *ElementCount = LocalElementCount; + + /* Now unpack the data */ + Status = BiConvertBcdElements(LocalElements, + Elements, + ElementSize, + &LocalElementCount); + if (NT_SUCCESS(Status)) + { + /* Not all elements may have been converted */ + *ElementCount = LocalElementCount; + } + + /* Free the local (unpacked) buffer and return status */ + BlMmFreeHeap(LocalElements); + return Status; } + +NTSTATUS +BcdOpenStoreFromFile ( + _In_ PUNICODE_STRING FileName, + _In_ PHANDLE BcdHandle + ) +{ + ULONG Length; + PBL_FILE_PATH_DESCRIPTOR FilePath; + NTSTATUS Status; + HANDLE LocalHandle; + + /* Assume failure */ + LocalHandle = NULL; + + /* Allocate a path descriptor */ + Length = FileName->Length + sizeof(*FilePath); + FilePath = BlMmAllocateHeap(Length); + if (!FilePath) + { + return STATUS_NO_MEMORY; + } + + /* Initialize it */ + FilePath->Version = 1; + FilePath->PathType = InternalPath; + FilePath->Length = Length; + + /* Copy the name and NULL-terminate it */ + RtlCopyMemory(FilePath->Path, FileName->Buffer, Length); + FilePath->Path[Length / sizeof(WCHAR)] = UNICODE_NULL; + + /* Open the BCD */ + Status = BiAddStoreFromFile(FilePath, &LocalHandle); + if (NT_SUCCESS(Status)) + { + /* Return the handle on success */ + *BcdHandle = LocalHandle; + } + + /* Free the descriptor and return the status */ + BlMmFreeHeap(FilePath); + return Status; +} +