[BOOTLIB]
[reactos.git] / reactos / boot / environ / lib / misc / bcd.c
index cb88bdb..12a94ae 100644 (file)
 
 /* FUNCTIONS *****************************************************************/
 
-PBL_BCD_OPTION
-MiscGetBootOption (
-    _In_ PBL_BCD_OPTION List,
-    _In_ ULONG Type
-    )
-{
-    ULONG_PTR NextOption = 0, ListOption;
-    PBL_BCD_OPTION Option, FoundOption;
-
-    /* No options, bail out */
-    if (!List)
-    {
-        return NULL;
-    }
-
-    /* Loop while we find an option */
-    FoundOption = NULL;
-    do
-    {
-        /* 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))
-        {
-            FoundOption = Option;
-            break;
-        }
-
-        /* Store the offset of the next option */
-        NextOption = Option->NextEntryOffset;
-
-        /* Failed to match. Check for list options */
-        ListOption = Option->ListOffset;
-        if (ListOption)
-        {
-            /* Try to get a match in the associated option */
-            Option = MiscGetBootOption((PBL_BCD_OPTION)((ULONG_PTR)Option +
-                                       ListOption),
-                                       Type);
-            if (Option)
-            {
-                /* Return it */
-                FoundOption = Option;
-                break;
-            }
-        }
-    } while (NextOption);
-
-    /* Return the option that was found, if any */
-    return FoundOption;
-}
-
-/*++
- * @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
+VOID
+BiNotifyEnumerationError (
+    _In_ HANDLE ObjectHandle, 
+    _In_ PWCHAR ElementName,
+    _In_ NTSTATUS Status
     )
 {
-    ULONG Size = 0, NextOffset = 0;
-    PBL_BCD_OPTION NextOption;
-
-    /* Loop all the options*/
-    do
-    {
-        /* Move to the next one */
-        NextOption = (PBL_BCD_OPTION)((ULONG_PTR)BcdOption + NextOffset);
-
-        /* Compute the size of the next one */
-        Size += BlGetBootOptionSize(NextOption);
-
-        /* Update the offset */
-        NextOffset = NextOption->NextEntryOffset;
-    } while (NextOffset);
-
-    /* Return final computed size */
-    return Size;
+    /* 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);
 }
 
-/*++
- * @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
+BiConvertElementFormatToValueType (
+    _In_ ULONG Format
     )
 {
-    ULONG Size, Offset;
-
-    /* Check if there's any data */
-    if (BcdOption->DataOffset)
+    /* Strings and objects are strings */
+    if ((Format == BCD_TYPE_STRING) || (Format == BCD_TYPE_OBJECT))
     {
-        /* Add the size of the data */
-        Size = BcdOption->DataOffset + BcdOption->DataSize;
-    }
-    else
-    {
-        /* No data, just the structure itself */
-        Size = sizeof(*BcdOption);
+        return REG_SZ;
     }
 
-    /* Any associated options? */
-    Offset = BcdOption->ListOffset;
-    if (Offset)
+    /* Object lists are arrays of strings */
+    if (Format == BCD_TYPE_OBJECT_LIST)
     {
-        /* Go get those too */
-        Size += BlGetBootOptionListSize((PVOID)((ULONG_PTR)BcdOption + Offset));
+        return REG_MULTI_SZ;
     }
 
-    /* Return the final size */
-    return Size;
+    /* Everything else is binary */
+    return REG_BINARY;
 }
 
 NTSTATUS
-BlGetBootOptionString (
-    _In_ PBL_BCD_OPTION List,
-    _In_ ULONG Type,
-    _Out_ PWCHAR* Value
+BiConvertRegistryDataToElement (
+    _In_ HANDLE ObjectHandle,
+    _In_ PVOID Data,
+    _In_ ULONG DataLength,
+    _In_ BcdElementType ElementType,
+    _Out_ PVOID Element,
+    _Out_ PULONG ElementSize
     )
 {
     NTSTATUS Status;
-    PBL_BCD_OPTION Option;
-    PWCHAR String, StringCopy;
-    ULONG StringLength, CopyLength;
-    //PGUID AppIdentifier;
+    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;
 
-    /* Make sure this is a BCD_STRING */
-    if ((Type & 0xF000000) != 0x2000000)
-    {
-        return STATUS_INVALID_PARAMETER;
-    }
+    /* Assume failure */
+    ReturnedLength = 0;
 
-    /* Return the data */
-    Option = MiscGetBootOption(List, Type);
-    if (Option)
-    {
-        /* Extract the string */
-        String = (PWCHAR)((ULONG_PTR)Option + Option->DataOffset);
-        Status = STATUS_SUCCESS;
-    }
-    else
+    /* Check what type of format we are dealing with */
+    switch (ElementType.Format)
     {
-        /* No string is present */
-        String = NULL;
-        Status = STATUS_NOT_FOUND;
-    }
+        /* Devices -- they are in a binary format */
+        case BCD_TYPE_DEVICE:
 
-    /* Compute the data size */
-    StringLength = Option->DataSize / sizeof(WCHAR);
-
-#ifdef _SECURE_BOOT_
-    /* Filter out SecureBoot Options */
-    AppIdentifier = BlGetApplicationIdentifier();
-    Status = BlpBootOptionCallbackString(AppIdentifier, Type, String, StringLength, &String, &StringLength);
-#else
-#endif
-    /* Check if we have space for one more character */
-    CopyLength = StringLength + 1;
-    if (CopyLength < StringLength)
-    {
-        /* Nope, we'll overflow */
-        CopyLength = -1;
-        Status = STATUS_INTEGER_OVERFLOW;
-    }
+            /* 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;
+            }
 
-    /* No overflow? */
-    if (NT_SUCCESS(Status))
-    {
-        /* Check if it's safe to multiply by two */
-        if ((CopyLength * sizeof(WCHAR)) > 0xFFFFFFFF)
-        {
-            /* Nope */
-            CopyLength = -1;
-            Status = STATUS_INTEGER_OVERFLOW;
-        }
-        else
-        {
-            /* We're good, do the multiplication */
-            Status = STATUS_SUCCESS;
-            CopyLength *= sizeof(WCHAR);
-        }
+            /* Both the registry and BCD format are the same */
+            BcdDevice = (PBCD_DEVICE_OPTION)Data;
+            ElementDevice = (PBCD_DEVICE_OPTION)Element;
 
-        /* Allocate a copy for the string */
-        if (NT_SUCCESS(Status))
-        {
-            StringCopy = BlMmAllocateHeap(CopyLength);
-            if (StringCopy)
+            /* Make sure the device fits in the registry data */
+            Device = &BcdDevice->DeviceDescriptor;
+            Size = Device->Size;
+            if ((Size + sizeof(BcdDevice->AssociatedEntry)) != DataLength)
             {
-                /* NULL-terminate it */
-                RtlCopyMemory(StringCopy,
-                              String,
-                              CopyLength - sizeof(UNICODE_NULL));
-                StringCopy[CopyLength] = UNICODE_NULL;
-                *Value = StringCopy;
-                Status = STATUS_SUCCESS;
+                return STATUS_OBJECT_TYPE_MISMATCH;
             }
-            else
+
+            /* Check if this is a locate device */
+            if (Device->DeviceType == LocateDevice)
             {
-                /* No memory, fail */
-                Status = STATUS_NO_MEMORY;
+                EfiPrintf(L"Locates not yet supported\r\n");
+                return STATUS_NOT_SUPPORTED;
             }
-        }
-    }
 
-    /* All done */
-    return Status;
-}
+            /* Make sure the caller's buffer can fit the device */
+            ReturnedLength = Size + sizeof(BcdDevice->AssociatedEntry);
+            if (ReturnedLength > *ElementSize)
+            {
+                Status = STATUS_BUFFER_TOO_SMALL;
+                break;
+            }
 
-NTSTATUS
-BlGetBootOptionDevice (
-    _In_ PBL_BCD_OPTION List,
-    _In_ ULONG Type,
-    _Out_ PBL_DEVICE_DESCRIPTOR* Value,
-    _In_opt_ PBL_BCD_OPTION* ExtraOptions
-    )
-{
-    NTSTATUS Status;
-    PBL_BCD_OPTION Option, ListData, ListCopy, SecureListData;
-    PBCD_DEVICE_OPTION BcdDevice;
-    ULONG DeviceSize, ListOffset, ListSize;
-    PBL_DEVICE_DESCRIPTOR DeviceDescriptor, SecureDescriptor;
-    //PGUID AppIdentifier;
-
-    /* Make sure this is a BCD_DEVICE */
-    if ((Type & 0xF000000) != 0x1000000)
-    {
-        return STATUS_INVALID_PARAMETER;
-    }
+            /* It'll fit -- copy it in */
+            RtlCopyMemory(&ElementDevice->DeviceDescriptor, Device, Size);
+            ElementDevice->AssociatedEntry = BcdDevice->AssociatedEntry;
+            Status = STATUS_SUCCESS;
+            break;
 
-    /* Return the data */
-    Option = MiscGetBootOption(List, Type);
-    if (!Option)
-    {
-        /* Set failure if no data exists */
-        Status = STATUS_NOT_FOUND;
-    }
-    else
-    {
-        /* Otherwise, read the size of the BCD device encoded */
-        BcdDevice = (PBCD_DEVICE_OPTION)((ULONG_PTR)Option + Option->DataOffset);
-        DeviceSize = BcdDevice->DeviceDescriptor.Size;
+        /* Strings -- they are stored as is */
+        case BCD_TYPE_STRING:
 
-        /* Allocate a buffer to copy it into */
-        DeviceDescriptor = BlMmAllocateHeap(DeviceSize);
-        if (!DeviceDescriptor)
-        {
-            return STATUS_NO_MEMORY;
-        }
+            /* Make sure the string isn't empty or misaligned */
+            if (!(DataLength) || (DataLength & 1))
+            {
+                return STATUS_OBJECT_TYPE_MISMATCH;
+            }
 
-        /* Copy it into that buffer */
-        RtlCopyMemory(DeviceDescriptor, &BcdDevice->DeviceDescriptor, DeviceSize);
-        Status = STATUS_SUCCESS;
-    }
+            /* Both the registry and BCD format are the same */
+            BcdString = (PWCHAR)Data;
+            ElementString = (PWCHAR)Element;
 
-    /* Check if extra options were requested */
-    if (ExtraOptions)
-    {
-        /* See where they are */
-        ListOffset = Option->ListOffset;
-        if (ListOffset)
-        {
-            /* See how big they are */
-            ListData = (PBL_BCD_OPTION)((ULONG_PTR)Option + ListOffset);
-            ListSize = BlGetBootOptionListSize(ListData);
+            /* We'll need as much data as the string has to offer */
+            ReturnedLength = DataLength;
 
-            /* Allocate a buffer to hold them into */
-            ListCopy = BlMmAllocateHeap(ListSize);
-            if (!ListCopy)
+            /* If the string isn't NULL-terminated, do it now */
+            NullTerminate = FALSE;
+            if (BcdString[(DataLength / sizeof(WCHAR)) - 1] != UNICODE_NULL)
             {
-                Status = STATUS_NO_MEMORY;
-                goto Quickie;
+                ReturnedLength += sizeof(UNICODE_NULL);
+                NullTerminate = TRUE;
             }
 
-            /* Copy them in there */
-            RtlCopyMemory(ListCopy, ListData, ListSize);
-        }
-    }
-
-#ifdef _SECURE_BOOT_
-    /* Filter out SecureBoot Options */
-    AppIdentifier = BlGetApplicationIdentifier();
-    if (BlpBootOptionCallbacks)
-    {
-        DeviceCallback = BlpBootOptionCallbacks->Device;
-        if (DeviceCallback)
-        {
-            Status = DeviceCallback(BlpBootOptionCallbackCookie,
-                                    Status,
-                                    0,
-                                    AppIdentifier,
-                                    Type,
-                                    &SecureDescriptor,
-                                    PtrOptionData);
-        }
-    }
-#else
-    /* No secure boot, so the secure descriptors are the standard ones */
-    SecureDescriptor = DeviceDescriptor;
-    SecureListData = ListCopy;
-#endif
+            /* Will we fit in the caller's buffer? */
+            if (ReturnedLength > *ElementSize)
+            {
+                Status = STATUS_BUFFER_TOO_SMALL;
+                break;
+            }
 
-    /* Check if the data was read correctly */
-    if (NT_SUCCESS(Status))
-    {
-        /* Check if we had a new descriptor after filtering */
-        if (SecureDescriptor != DeviceDescriptor)
-        {
-            /* Yep -- if we had an old one, free it */
-            if (DeviceDescriptor)
+            /* Yep -- copy it in, and NULL-terminate if needed */
+            RtlCopyMemory(Element, Data, DataLength);
+            if (NullTerminate)
             {
-                BlMmFreeHeap(DeviceDescriptor);
+                ElementString[DataLength / sizeof(WCHAR)] = UNICODE_NULL;
             }
-        }
 
-        /* Check if we had a new list after filtering */
-        if (SecureListData != ListCopy)
-        {
-            /* Yep -- if we had an old list, free it */
-            if (ListCopy)
+            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)
             {
-                BlMmFreeHeap(ListCopy);
+                Status = STATUS_BUFFER_TOO_SMALL;
+                break;
             }
-        }
+            
+            /* Yep, copy the GUID */
+            RtlInitUnicodeString(&GuidString, BcdString);
+            Status = RtlGUIDFromString(&GuidString, ElementGuid);
+            break;
 
-        /* Finally, check if the caller wanted extra options */
-        if (ExtraOptions)
-        {
-            /* Yep -- so pass the caller our copy */
-            *ExtraOptions = ListCopy;
-            ListCopy = NULL;
-        }
+        /* Object Lists -- they are stored as arrays of GUID strings */
+        case BCD_TYPE_OBJECT_LIST:
 
-        /* Caller always wants data back, so pass them our copy */
-        *Value = DeviceDescriptor;
-        DeviceDescriptor = NULL;
-    }
+            /* Assume an empty list*/
+            ReturnedLength = 0;
+            Length = 0;
+            Status = STATUS_SUCCESS;
 
-Quickie:
-    /* On the failure path, if these buffers are active, we should free them */
-    if (ListCopy)
-    {
-        BlMmFreeHeap(ListCopy);
-    }
-    if (DeviceDescriptor)
-    {
-        BlMmFreeHeap(DeviceDescriptor);
-    }
+            /* Registry data is an array of strings, BCD data is array of GUIDs */
+            BcdString = (PWCHAR)Data;
+            ElementGuid = (PGUID)Element;
 
-    /* All done */
-    return Status;
-}
+            /* Loop as long as the array still has strings */
+            while (*BcdString)
+            {
+                /* Don't read beyond the registry data */
+                if (Length >= DataLength)
+                {
+                    break;
+                }
 
-NTSTATUS
-BlGetBootOptionInteger (
-    _In_ PBL_BCD_OPTION List,
-    _In_ ULONG Type,
-    _Out_ PULONGLONG Value
-    )
-{
-    NTSTATUS Status;
-    PBL_BCD_OPTION Option;
-    //PGUID AppIdentifier;
+                /* 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++;
+                }
 
-    /* Make sure this is a BCD_INTEGER */
-    if ((Type & 0xF000000) != 0x5000000)
-    {
-        return STATUS_INVALID_PARAMETER;
-    }
+                /* 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);
+            }
 
-    /* Return the data */
-    Option = MiscGetBootOption(List, Type);
-    if (Option)
-    {
-        *Value = *(PULONGLONG)((ULONG_PTR)Option + Option->DataOffset);
-    }
+            /* Check if we failed anywhere */
+            if (!NT_SUCCESS(Status))
+            {
+                break;
+            }
 
-#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
-    return Status;
-}
+            /* Check if we consumed more space than we have */
+            if (ReturnedLength > *ElementSize)
+            {
+                Status = STATUS_BUFFER_TOO_SMALL;
+            }
 
-NTSTATUS
-BlGetBootOptionBoolean (
-    _In_ PBL_BCD_OPTION List,
-    _In_ ULONG Type,
-    _Out_ PBOOLEAN Value
-    )
-{
-    NTSTATUS Status;
-    PBL_BCD_OPTION Option;
-    //PGUID AppIdentifier;
+            /* All good here */
+            break;
 
-    /* Make sure this is a BCD_BOOLEAN */
-    if ((Type & 0xF000000) != 0x6000000)
-    {
-        return STATUS_INVALID_PARAMETER;
-    }
+        /* Integer -- stored as binary */
+        case BCD_TYPE_INTEGER:
 
-    /* Return the data */
-    Option = MiscGetBootOption(List, Type);
-    if (Option)
-    {
-        *Value = *(PBOOLEAN)((ULONG_PTR)Option + Option->DataOffset);
-    }
+            /* BCD data is a ULONGLONG, registry data is 8 bytes binary */
+            ElementInteger = (PULONGLONG)Element;
+            ReturnedLength = sizeof(*ElementInteger);
 
-#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 Status;
-}
+            /* Make sure the registry data makes sense */
+            if (DataLength > ReturnedLength)
+            {
+                return STATUS_OBJECT_TYPE_MISMATCH;
+            }
 
-#define BI_FLUSH_HIVE       0x01
-#define BI_HIVE_WRITEABLE   0x02
+            /* Make sure the caller has space */
+            if (*ElementSize < ReturnedLength)
+            {
+                Status = STATUS_BUFFER_TOO_SMALL;
+                break;
+            }
 
-typedef struct _BI_KEY_HIVE
-{
-    PHBASE_BLOCK BaseBlock;
-    ULONG HiveSize;
-    PBL_FILE_PATH_DESCRIPTOR FilePath;
-    CMHIVE Hive;
-    LONG ReferenceCount;
-    ULONG Flags;
-    PCM_KEY_NODE RootNode;
-} BI_KEY_HIVE, *PBI_KEY_HIVE;
+            /* Write the integer result */
+            *ElementInteger = 0;
+            RtlCopyMemory(ElementInteger, Data, DataLength);
+            Status = STATUS_SUCCESS;
+            break;
 
-typedef struct _BI_KEY_OBJECT
-{
-    PBI_KEY_HIVE KeyHive;
-    PCM_KEY_NODE KeyNode;
-    HCELL_INDEX KeyCell;
-    PWCHAR KeyName;
-} BI_KEY_OBJECT, *PBI_KEY_OBJECT;
-
-PVOID
-NTAPI
-CmpAllocate (
-    _In_ SIZE_T Size,
-    _In_ BOOLEAN Paged,
-    _In_ ULONG Tag
-    )
-{
-    UNREFERENCED_PARAMETER(Paged);
-    UNREFERENCED_PARAMETER(Tag);
+        /* Boolean -- stored as binary */
+        case BCD_TYPE_BOOLEAN:
 
-    /* Call the heap allocator */
-    return BlMmAllocateHeap(Size);
-}
+            /* BCD data is a BOOLEAN, registry data is 2 bytes binary */
+            ElementWord = (PUSHORT)Element;
+            BcdBoolean = (PBOOLEAN)Data;
+            ReturnedLength = sizeof(ElementWord);
 
-VOID
-NTAPI
-CmpFree (
-    _In_ PVOID Ptr,
-    _In_ ULONG Quota
-    )
-{
-    UNREFERENCED_PARAMETER(Quota);
+            /* Make sure the registry data makes sense */
+            if (DataLength != sizeof(*BcdBoolean))
+            {
+                return STATUS_OBJECT_TYPE_MISMATCH;
+            }
 
-    /* Call the heap allocator */
-    BlMmFreeHeap(Ptr);
-}
+            /* Make sure the caller has space */
+            if (*ElementSize < ReturnedLength)
+            {
+                Status = STATUS_BUFFER_TOO_SMALL;
+                break;
+            }
 
-FORCEINLINE
-VOID
-BiDereferenceHive (
-    _In_ HANDLE KeyHandle
-    )
-{
-    PBI_KEY_OBJECT KeyObject;
+            /* Write the boolean result */
+            *ElementWord = 0;
+            *ElementWord = *BcdBoolean != 0;
+            Status = STATUS_SUCCESS;
+            break;
 
-    /* Get the key object */
-    KeyObject = (PBI_KEY_OBJECT)KeyHandle;
+        /* Integer list --stored as binary */
+        case BCD_TYPE_INTEGER_LIST:
 
-    /* Drop a reference on the parent hive */
-    --KeyObject->KeyHive->ReferenceCount;
-}
+            /* BCD Data is n ULONGLONGs, registry data is n*8 bytes binary */
+            ReturnedLength = DataLength;
+            if (!(DataLength) || (DataLength & 7))
+            {
+                return STATUS_OBJECT_TYPE_MISMATCH;
+            }
 
-VOID
-BiFlushHive (
-    _In_ HANDLE KeyHandle
-    )
-{
-    /* Not yet implemented */
-    EfiPrintf(L"NO reg flush\r\n");
-    return;
-}
+            /* Make sure the caller has space */
+            if (*ElementSize < ReturnedLength)
+            {
+                Status = STATUS_BUFFER_TOO_SMALL;
+                break;
+            }
 
-VOID
-BiCloseKey (
-    _In_ HANDLE KeyHandle
-    )
-{
-    PBI_KEY_HIVE KeyHive;
-    PBI_KEY_OBJECT KeyObject;
+            /* Write the integer list result */
+            RtlCopyMemory(Element, Data, DataLength);
+            Status = STATUS_SUCCESS;
+            break;
 
-    /* Get the key object and hive */
-    KeyObject = (PBI_KEY_OBJECT)KeyHandle;
-    KeyHive = KeyObject->KeyHive;
+        /* Arbitrary data */
+        default:
 
-    /* Check if we have a hive, or name, or key node */
-    if ((KeyHive) || (KeyObject->KeyNode) || (KeyObject->KeyName))
-    {
-        /* Drop a reference, see if it's the last one */
-        BiDereferenceHive(KeyHandle);
-        if (!KeyHive->ReferenceCount)
-        {
-            /* Check if we should flush it */
-            if (KeyHive->Flags & BI_FLUSH_HIVE)
+            /* Registry data is copied binary as-is */
+            ReturnedLength = DataLength;
+
+            /* Make sure it's not empty */
+            if (!DataLength)
             {
-                BiFlushHive(KeyHandle);
+                return STATUS_OBJECT_TYPE_MISMATCH;
             }
 
-            /* Unmap the hive */
-            //MmPapFreePages(KeyHive->ImageBase, 1);
-            EfiPrintf(L"Leaking hive memory\r\n");
+            /* Make sure the caller has space */
+            if (*ElementSize < ReturnedLength)
+            {
+                Status = STATUS_BUFFER_TOO_SMALL;
+                break;
+            }
 
-            /* Free the hive and hive path */
-            BlMmFreeHeap(KeyHive->FilePath);
-            BlMmFreeHeap(KeyHive);
-        }
+            /* Write the result */
+            RtlCopyMemory(Element, Data, DataLength);
+            Status = STATUS_SUCCESS;
+            break;
+    }
 
-        /* Check if a key name is present */
-        if (KeyObject->KeyName)
-        {
-            /* Free it */
-            BlMmFreeHeap(KeyObject->KeyName);
-        }
+    /* If we got here due to success or space issues, write the size */
+    if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL))
+    {
+        *ElementSize = ReturnedLength;
     }
 
-    /* Free the object */
-    BlMmFreeHeap(KeyObject);
+    /* All done, return our conversion result */
+    return Status;
 }
 
 NTSTATUS
-BiOpenKey(
-    _In_ HANDLE ParentHandle,
-    _In_ PWCHAR KeyName,
-    _Out_ PHANDLE Handle
+BiConvertBcdElements (
+    _In_ PBCD_PACKED_ELEMENT Elements,
+    _Out_opt_ PBCD_ELEMENT Buffer,
+    _Inout_ PULONG BufferSize, 
+    _Inout_ PULONG ElementCount
     )
 {
-    PBI_KEY_OBJECT ParentKey, NewKey;
-    PBI_KEY_HIVE ParentHive;
     NTSTATUS Status;
-    ULONG NameLength, SubNameLength, NameBytes;
-    PWCHAR NameStart, NameBuffer;
-    UNICODE_STRING KeyString;
-    HCELL_INDEX KeyCell;
-    PHHIVE Hive;
-    PCM_KEY_NODE ParentNode;
+    ULONG ElementSize, AlignedElementSize, AlignedDataSize;
+    PBCD_ELEMENT_HEADER Header;
+    PVOID Data;
+    BOOLEAN Exists;
+    ULONG i, j, Count;
 
-    /* Convert from a handle to our key object */
-    ParentKey = (PBI_KEY_OBJECT)ParentHandle;
+    /* Local variable to keep track of objects */
+    Count = 0;
 
-    /* Extract the hive and node information */
-    ParentHive = ParentKey->KeyHive;
-    ParentNode = ParentKey->KeyNode;
-    Hive = &ParentKey->KeyHive->Hive.Hive;
+    /* Safely compute the element bytes needed */
+    Status = RtlULongMult(*ElementCount, sizeof(BCD_ELEMENT), &ElementSize);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
 
-    /* Initialize variables */
-    KeyCell = HCELL_NIL;
-    Status = STATUS_SUCCESS;
-    NameBuffer = NULL;
+    /* Safely align the element size */
+    Status = RtlULongAdd(ElementSize,
+                         sizeof(ULONG) - 1,
+                         &AlignedElementSize);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+    AlignedElementSize = ALIGN_DOWN(AlignedElementSize, ULONG);
 
-    /* Loop as long as there's still portions of the key name in play */
-    NameLength = wcslen(KeyName);
-    while (NameLength)
+    /* 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))
     {
-        /* Find the first path separator */
-        NameStart = wcschr(KeyName, OBJ_NAME_PATH_SEPARATOR);
-        if (NameStart)
-        {
-            /* Look only at the key before the separator */
-            SubNameLength = NameStart - KeyName;
-            ++NameStart;
-        }
-        else
-        {
-            /* No path separator, this is the final leaf key */
-            SubNameLength = NameLength;
-        }
+        return Status;
+    }
 
-        /* Free the name buffer from the previous pass if needed */
-        if (NameBuffer)
-        {
-            BlMmFreeHeap(NameBuffer);
-        }
+    /* Safely compute the header bytes needed */
+    Status = RtlULongMult(*ElementCount,
+                          sizeof(BCD_ELEMENT_HEADER),
+                          &ElementSize);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
 
-        /* Allocate a buffer to hold the name of this specific subkey only */
-        NameBytes = SubNameLength * sizeof(WCHAR);
-        NameBuffer = BlMmAllocateHeap(NameBytes + sizeof(UNICODE_NULL));
-        if (!NameBuffer)
-        {
-            Status = STATUS_NO_MEMORY;
-            goto Quickie;
-        }
+    /* Safely align the header size */
+    Status = RtlULongAdd(ElementSize,
+                         AlignedElementSize + sizeof(ULONG) - 1,
+                         &AlignedElementSize);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+    AlignedElementSize = ALIGN_DOWN(AlignedElementSize, ULONG);
 
-        /* Copy and null-terminate the name of the subkey */
-        RtlCopyMemory(NameBuffer, KeyName, NameBytes);
-        NameBuffer[SubNameLength] = UNICODE_NULL;
+    /* Do a safe version of Add2Ptr */
+    Status = RtlULongPtrAdd((ULONG_PTR)Buffer,
+                            AlignedElementSize,
+                            (PULONG_PTR)&Data);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
 
-        /* Convert it into a UNICODE_STRING and try to find it */
-        RtlInitUnicodeString(&KeyString, NameBuffer);
-        KeyCell = CmpFindSubKeyByName(Hive, ParentNode, &KeyString);
-        if (KeyCell == HCELL_NIL)
+    /* 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))
         {
-            Status = STATUS_OBJECT_NAME_NOT_FOUND;
-            goto Quickie;
+            break;
         }
+        AlignedDataSize = ALIGN_DOWN(AlignedDataSize, ULONG);
 
-        /* We found it -- get the key node out of it */
-        ParentNode = (PCM_KEY_NODE)HvGetCell(Hive, KeyCell);
-        if (!ParentNode)
+        /* Safely add the size of this data element */
+        Status = RtlULongAdd(AlignedElementSize,
+                             AlignedDataSize,
+                             &AlignedElementSize);
+        if (!NT_SUCCESS(Status))
         {
-            Status = STATUS_REGISTRY_CORRUPT;
-            goto Quickie;
+            break;
         }
 
-        /* Update the key name to the next remaining path element */
-        KeyName = NameStart;
-        if (NameStart)
+        /* Do we have enough space left? */
+        if (*BufferSize >= AlignedElementSize)
         {
-            /* Update the length to the remainder of the path */
-            NameLength += -1 - SubNameLength;
+            /* 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;
+                    }
+                }
+            }
+
+            /* Have we already found ourselves? */
+            if (!Exists)
+            {
+                /* 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
         {
-            /* There's nothing left, this was the leaf key */
-            NameLength = 0;
+            /* 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;
     }
 
-    /* Allocate a key object */
-    NewKey = BlMmAllocateHeap(sizeof(*NewKey));
-    if (!NewKey)
+    /* Return the new final buffer size and count */
+    *BufferSize = AlignedElementSize;
+    *ElementCount = Count;
+    return Status;
+}
+
+NTSTATUS
+BcdOpenObject (
+    _In_ HANDLE BcdHandle,
+    _In_ PGUID ObjectId,
+    _Out_ PHANDLE ObjectHandle
+    )
+{
+    NTSTATUS Status;
+    GUID LocalGuid;
+    UNICODE_STRING GuidString;
+    HANDLE RootObjectHandle;
+
+    /* Assume failure */
+    *ObjectHandle = NULL;
+
+    /* Initialize GUID string */
+    GuidString.Buffer = NULL;
+
+    /* Open the root "Objects" handle */
+    RootObjectHandle = NULL;
+    Status = BiOpenKey(BcdHandle, L"Objects", &RootObjectHandle);
+    if (!NT_SUCCESS(Status))
     {
-        /* Bail out if we had no memory for it */
-        Status = STATUS_NO_MEMORY;
         goto Quickie;
     }
 
-    /* Fill out the key object data */
-    NewKey->KeyNode = ParentNode;
-    NewKey->KeyHive = ParentHive;
-    NewKey->KeyName = NameBuffer;
-    NewKey->KeyCell = KeyCell;
-
-    /* Add a reference to the hive */
-    ++ParentHive->ReferenceCount;
+    /* Capture the object ID and convert it into a string */
+    LocalGuid = *ObjectId;
+    Status = RtlStringFromGUID(&LocalGuid, &GuidString);
+    if (!NT_SUCCESS(Status))
+    {
+        goto Quickie;
+    }
 
-    /* Return the object back to the caller */
-    *Handle = NewKey;
+    /* Now open the key containing this object ID */
+    Status = BiOpenKey(RootObjectHandle, GuidString.Buffer, ObjectHandle);
 
 Quickie:
-    /* If we had a name buffer, free it */
-    if (NameBuffer)
+    /* Free the GUID string if we had one allocated */
+    if (GuidString.Buffer)
+    {
+        RtlFreeUnicodeString(&GuidString);
+    }
+
+    /* Close the root handle if it was open */
+    if (RootObjectHandle)
     {
-        BlMmFreeHeap(NameBuffer);
+        BiCloseKey(RootObjectHandle);
     }
 
-    /* Return status of the open operation */
+    /* Return the final status */
     return Status;
 }
 
-BOOLEAN BiHiveHashLibraryInitialized;
-ULONGLONG HvSymcryptSeed;
-
-BOOLEAN
-HvIsInPlaceBaseBlockValid (
-    _In_ PHBASE_BLOCK BaseBlock
+NTSTATUS
+BcdDeleteElement (
+    _In_ HANDLE ObjectHandle,
+    _In_ ULONG Type
     )
 {
-    ULONG HiveLength, HeaderSum;
-    BOOLEAN Valid;
+    NTSTATUS Status;
+    HANDLE ElementsHandle, ElementHandle;
+    WCHAR TypeString[22];
 
-    /* Assume failure */
-    Valid = FALSE;
-
-    /* Check for incorrect signature, type, version, or format */
-    if ((BaseBlock->Signature == 'fger') &&
-        (BaseBlock->Type == 0) &&
-        (BaseBlock->Major <= 1) &&
-        (BaseBlock->Minor <= 5) &&
-        (BaseBlock->Minor >= 3) &&
-        (BaseBlock->Format == 1))
+    /* Open the elements key */
+    Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle);
+    if (NT_SUCCESS(Status))
     {
-        /* Check for invalid hive size */
-        HiveLength = BaseBlock->Length;
-        if (HiveLength)
+        /* Convert the element ID into a string */
+        if (!_ultow(Type, TypeString, 16))
         {
-            /* Check for misaligned or too large hive size */
-            if (!(HiveLength & 0xFFF) && HiveLength <= 0x7FFFE000)
+            /* Failed to do so */
+            Status = STATUS_UNSUCCESSFUL;
+        }
+        else
+        {
+            /* Open the element specifically */
+            Status = BiOpenKey(ElementsHandle, TypeString, &ElementHandle);
+            if (NT_SUCCESS(Status))
             {
-                /* Check for invalid header checksum */
-                HeaderSum = HvpHiveHeaderChecksum(BaseBlock);
-                if (HeaderSum == BaseBlock->CheckSum)
+                /* Delete it */
+                Status = BiDeleteKey(ElementHandle);
+                if (NT_SUCCESS(Status))
                 {
-                    /* All good */
-                    Valid = TRUE;
+                    /* 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);
+            }
         }
     }
 
-    /* Return validity */
-    return Valid;
+    /* Check if we should close the elements handle */
+    if (ElementsHandle)
+    {
+        /* Do it */
+        BiCloseKey(ElementsHandle);
+    }
+
+    /* Return whatever the result was */
+    return Status;
 }
 
 NTSTATUS
-BiInitializeAndValidateHive (
-    _In_ PBI_KEY_HIVE Hive
+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
     )
 {
-    ULONG HiveSize;
-    NTSTATUS Status;
-
-    /* Make sure the hive is at least the size of a base block */
-    if (Hive->HiveSize < sizeof(HBASE_BLOCK))
+    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))
     {
-        return STATUS_REGISTRY_CORRUPT;
+        goto Quickie;
     }
 
-    /* Make sure that the base block accurately describes the size of the hive */
-    HiveSize = Hive->BaseBlock->Length + sizeof(HBASE_BLOCK);
-    if ((HiveSize < sizeof(HBASE_BLOCK)) || (HiveSize > Hive->HiveSize))
+    /* 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))
     {
-        return STATUS_REGISTRY_CORRUPT;
+        goto Quickie;
     }
 
-    /* Initialize a flat memory hive */
-    RtlZeroMemory(&Hive->Hive, sizeof(Hive->Hive));
-    Status = HvInitialize(&Hive->Hive.Hive,
-                          HINIT_FLAT,
-                          0,
-                          0,
-                          Hive->BaseBlock,
-                          CmpAllocate,
-                          CmpFree,
-                          NULL,
-                          NULL,
-                          NULL,
-                          NULL,
-                          0,
-                          NULL);
-    if (NT_SUCCESS(Status))
+    /* Check if the and subelements were present  */
+    if (ParsedElements)
     {
-        /* Cleanup volatile/old data */
-        CmPrepareHive(&Hive->Hive.Hive); // CmCheckRegistry 
-        Status = STATUS_SUCCESS;
+        /* Keep going until the last one */
+        while (Element->NextEntry)
+        {
+            Element = Element->NextEntry;
+        }
+
+        /* Set the new buffer location to the last element */
+        *Elements = Element;
     }
 
-    /* Return the final status */
+Quickie:
+    /* Return the number of sub-elements and their size */
+    *ElementCount = ParsedElements;
+    *ElementSize = RequiredSize;
     return Status;
 }
 
 NTSTATUS
-BiLoadHive (
-    _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
-    _Out_ PHANDLE HiveHandle
+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
     )
 {
-    ULONG DeviceId;
-    PHBASE_BLOCK BaseBlock, NewBaseBlock;
-    PBI_KEY_OBJECT KeyObject;
-    PBI_KEY_HIVE BcdHive;
-    PBL_DEVICE_DESCRIPTOR BcdDevice;
-    ULONG PathLength, DeviceLength, HiveSize, HiveLength, NewHiveSize;
-    PWCHAR HiveName, LogName;
-    BOOLEAN HaveWriteAccess;
     NTSTATUS Status;
-    PVOID LogData;
-    PHHIVE Hive;
-    UNICODE_STRING KeyString;
-    PCM_KEY_NODE RootNode;
-    HCELL_INDEX CellIndex;
+    ULONG SubElementCount, TotalSize, RequiredSize, CurrentSize, i;
+    PBCD_PACKED_ELEMENT PreviousElement;
+    /* Assume empty list */
+    *ElementCount = 0;
+    Status = STATUS_SUCCESS;
 
     /* Initialize variables */
-    DeviceId = -1;
-    BaseBlock = NULL;
-    BcdHive = NULL;
-    KeyObject = NULL;
-    LogData = NULL;
-    LogName = NULL;
-
-    /* Initialize the crypto seed */
-    if (!BiHiveHashLibraryInitialized)
-    {
-        HvSymcryptSeed = 0x82EF4D887A4E55C5;
-        BiHiveHashLibraryInitialized = TRUE;
-    }
+    TotalSize = 0;
+    PreviousElement = NULL;
 
-    /* Extract and validate the input path */
-    BcdDevice = (PBL_DEVICE_DESCRIPTOR)&FilePath->Path;
-    PathLength = FilePath->Length;
-    DeviceLength = BcdDevice->Size;
-    HiveName = (PWCHAR)((ULONG_PTR)BcdDevice + BcdDevice->Size);
-    if (PathLength <= DeviceLength)
-    {
-        /* Doesn't make sense, bail out */
-        Status = STATUS_INVALID_PARAMETER;
-        goto Quickie;
-    }
+    /* Set the currently remaining size based on caller's input */
+    CurrentSize = *ElementSize;
 
-    /* Attempt to open the underlying device for RW access */
-    HaveWriteAccess = TRUE;
-    Status = BlpDeviceOpen(BcdDevice,
-                           BL_DEVICE_READ_ACCESS | BL_DEVICE_WRITE_ACCESS,
-                           0,
-                           &DeviceId);
-    if (!NT_SUCCESS(Status))
+    /* Iterate over every subje object */
+    for (i = 0; i < SubObjectCount; i++)
     {
-        /* Try for RO access instead */
-        HaveWriteAccess = FALSE;
-        Status = BlpDeviceOpen(BcdDevice, BL_DEVICE_READ_ACCESS, 0, &DeviceId);
-        if (!NT_SUCCESS(Status))
+        /* 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))
+            {
+                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
+            {
+                /* 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
         {
-            /* No access at all -- bail out */
-            goto Quickie;
+            /* 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;
         }
     }
 
-    /* Now try to load the hive on disk */
-    Status = BlImgLoadImageWithProgress2(DeviceId,
-                                         BlLoaderRegistry,
-                                         HiveName,
-                                         (PVOID*)&BaseBlock,
-                                         &HiveSize,
-                                         0,
-                                         FALSE,
-                                         NULL,
-                                         NULL);
-    if (!NT_SUCCESS(Status))
+    /* Terminate the last element, if one was left */
+    if (PreviousElement)
     {
-        EfiPrintf(L"Hive read failure: % lx\r\n", Status);
-        goto Quickie;
+        PreviousElement->NextEntry = NULL;
     }
 
-    /* Allocate a hive structure */
-    BcdHive = BlMmAllocateHeap(sizeof(*BcdHive));
-    if (!BcdHive)
+    /* Set failure code if we ran out of space */
+    if (*ElementSize < TotalSize)
     {
-        Status = STATUS_NO_MEMORY;
-        goto Quickie;
+        Status = STATUS_BUFFER_TOO_SMALL;
     }
 
-    /* Initialize it */
-    RtlZeroMemory(BcdHive, sizeof(*BcdHive));
-    BcdHive->BaseBlock = BaseBlock;
-    BcdHive->HiveSize = HiveSize;
-    if (HaveWriteAccess)
-    {
-        BcdHive->Flags |= BI_HIVE_WRITEABLE;
-    }
+    /* Return final length and status */
+    *ElementSize = TotalSize;
+    return Status;
+}
 
-    /* Make sure the hive was at least one bin long */
-    if (HiveSize < sizeof(*BaseBlock))
-    {
-        Status = STATUS_REGISTRY_CORRUPT;
-        goto Quickie;
-    }
+NTSTATUS
+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;
+    ULONG i;
+    PVOID ElementData, SubObjectList, RegistryElementData;
+    BcdElementType ElementType;
+    PBCD_PACKED_ELEMENT PreviousElement, ElementsStart;
+    ULONG SubElementCount, SubKeyCount, SubObjectCount, ElementDataLength;
+    PWCHAR ElementName;
+    PWCHAR* SubKeys;
 
-    /* Make sure the hive contents are at least one bin long */
-    HiveLength = BaseBlock->Length;
-    if (BaseBlock->Length < sizeof(*BaseBlock))
+    /* 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;
+
+    /* Open the root object key's elements */
+    Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle);
+    if (!NT_SUCCESS(Status))
     {
-        Status = STATUS_REGISTRY_CORRUPT;
         goto Quickie;
     }
 
-    /* Validate the initial bin (the base block) */
-    if (!HvIsInPlaceBaseBlockValid(BaseBlock))
+    /* Enumerate all elements */
+    Status = BiEnumerateSubKeys(ElementsHandle, &SubKeys, &SubKeyCount);
+    if (!NT_SUCCESS(Status))
     {
-        EfiPrintf(L"Recovery not implemented\r\n");
-        Status = STATUS_REGISTRY_CORRUPT;
         goto Quickie;
     }
 
-    /* Check if there's log recovery that needs to happen */
-    if (BaseBlock->Sequence1 != BaseBlock->Sequence2)
+    /* Iterate over each one */
+    for (i = 0; i < SubKeyCount; i++)
     {
-        EfiPrintf(L"Log fix not implemented: %lx %lx\r\n");
-        Status = STATUS_REGISTRY_CORRUPT;
-        goto Quickie;
-    }
+        /* 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;
+        }
 
-    /*
-     * Check if the whole hive doesn't fit in the buffer.
-     * Note: HiveLength does not include the size of the baseblock itself
-     */
-    if (HiveSize < (HiveLength + sizeof(*BaseBlock)))
-    {
-        EfiPrintf(L"Need bigger hive buffer path\r\n");
-
-        /* Allocate a slightly bigger buffer */
-        NewHiveSize = HiveLength + sizeof(*BaseBlock);
-        Status = MmPapAllocatePagesInRange((PVOID*)&NewBaseBlock,
-                                           BlLoaderRegistry,
-                                           NewHiveSize >> PAGE_SHIFT,
-                                           0,
-                                           0,
-                                           NULL,
-                                           0);
+        /* 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))
         {
-            goto Quickie;
+            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 current data in there */
-        RtlCopyMemory(NewBaseBlock, BaseBlock, HiveSize);
+            /* Copy the elements into the list. They are arrays of GUIDs */
+            RtlCopyMemory(SubObjectList, ElementData, ElementDataLength);
+            SubObjectCount = ElementDataLength / sizeof(GUID);
+        }
 
-        /* Free the old data */
-        EfiPrintf(L"Leaking old hive buffer\r\n");
-        //MmPapFreePages(BaseBlock, 1);
+        /* Free our local buffers */
+        BlMmFreeHeap(ElementData);
+        BlMmFreeHeap(RegistryElementData);
+        ElementData = NULL;
+        RegistryElementData = NULL;
 
-        /* Update our pointers */
-        BaseBlock = NewBaseBlock;
-        HiveSize = NewHiveSize;
-        BcdHive->BaseBlock = BaseBlock;
-        BcdHive->HiveSize = HiveSize;
+        /* Close the key */
+        BiCloseKey(ElementHandle);
+        ElementHandle = NULL;
+        ElementName = NULL;
     }
 
-    /* Check if any log stuff needs to happen */
-    if (LogData)
+    /* Did we end up here with a sub object list after successful loop parsing? */
+    if ((i != 0) && (i == SubKeyCount) && (SubObjectList))
     {
-        EfiPrintf(L"Log fix not implemented: %lx %lx\r\n");
-        Status = STATUS_REGISTRY_CORRUPT;
-        goto Quickie;
+        /* 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;
+            }
+        }
     }
 
-    /* Call Hv to setup the hive library */
-    Status = BiInitializeAndValidateHive(BcdHive);
-    if (!NT_SUCCESS(Status))
+Quickie:
+    /* Free the sub object list, if any */
+    if (SubObjectList)
     {
-        goto Quickie;
+        BlMmFreeHeap(SubObjectList);
     }
 
-    /* Now get the root node */
-    Hive = &BcdHive->Hive.Hive;
-    RootNode = (PCM_KEY_NODE)HvGetCell(Hive, Hive->BaseBlock->RootCell);
-    if (!RootNode)
+    /* Free any local element data */
+    if (ElementData)
     {
-        Status = STATUS_OBJECT_NAME_NOT_FOUND;
-        goto Quickie;
+        BlMmFreeHeap(ElementData);
     }
 
-    /* Find the Objects subkey under it to see if it's a real BCD hive */
-    RtlInitUnicodeString(&KeyString, L"Objects");
-    CellIndex = CmpFindSubKeyByName(Hive, RootNode, &KeyString);
-    if (CellIndex == HCELL_NIL)
+    /* Free any local registry data */
+    if (RegistryElementData)
     {
-        EfiPrintf(L"No OBJECTS subkey found!\r\n");
-        Status = STATUS_OBJECT_NAME_NOT_FOUND;
-        goto Quickie;
+        BlMmFreeHeap(RegistryElementData);
     }
 
-    /* This is a valid BCD hive, store its root node here */
-    BcdHive->RootNode = RootNode;
-
-    /* Allocate a copy of the file path */
-    BcdHive->FilePath = BlMmAllocateHeap(FilePath->Length);
-    if (!BcdHive->FilePath)
+    /* Close the handle if still opened */
+    if (ElementHandle)
     {
-        Status = STATUS_NO_MEMORY;
-        goto Quickie;
+        BiCloseKey(ElementHandle);
     }
 
-    /* Make a copy of it */
-    RtlCopyMemory(BcdHive->FilePath, FilePath, FilePath->Length);
-
-    /* Create a key object to describe the rot */
-    KeyObject = BlMmAllocateHeap(sizeof(*KeyObject));
-    if (!KeyObject)
+    /* Terminate the last element, if any */
+    if (PreviousElement)
     {
-        Status = STATUS_NO_MEMORY;
-        goto Quickie;
+        PreviousElement->NextEntry = NULL;
     }
 
-    /* Fill out the details */
-    KeyObject->KeyNode = RootNode;
-    KeyObject->KeyHive = BcdHive;
-    KeyObject->KeyName = NULL;
-    KeyObject->KeyCell = Hive->BaseBlock->RootCell;
-
-    /* One reference for the key object, plus one lifetime reference */
-    BcdHive->ReferenceCount = 2;
-
-    /* This is the hive handle */
-    *HiveHandle  = KeyObject;
-
-    /* We're all good */
-    Status = STATUS_SUCCESS;
-
-Quickie:
-    /* If we had a log name, free it */
-    if (LogName)
+    /* Close the root handle if still opened */
+    if (ElementsHandle)
     {
-        BlMmFreeHeap(LogName);
+        BiCloseKey(ElementsHandle);
     }
 
-    /* If we had logging data, free it */
-    if (LogData)
+    /* Set  failure code if out of space */
+    if (*ElementSize < TotalLength)
     {
-        EfiPrintf(L"Leaking log buffer\r\n");
-        //MmPapFreePages(LogData, 1);
+        Status = STATUS_BUFFER_TOO_SMALL;
     }
 
-    /* Check if this is the failure path */
-    if (!NT_SUCCESS(Status))
+    /* Other errors will send a notification error */
+    if (!(NT_SUCCESS(Status)) && (Status != STATUS_BUFFER_TOO_SMALL))
     {
-        /* If we mapped the hive, free it */
-        if (BaseBlock)
-        {
-            EfiPrintf(L"Leaking base block on failure\r\n");
-            //MmPapFreePages(BaseBlock, 1u);
-        }
-
-        /* If we opened the device, close it */
-        if (DeviceId != -1)
-        {
-            BlDeviceClose(DeviceId);
-        }
-
-        /* Did we create a hive object? */
-        if (BcdHive)
-        {
-            /* Free the file path if we made a copy of it */
-            if (BcdHive->FilePath)
-            {
-                BlMmFreeHeap(BcdHive->FilePath);
-            }
-
-            /* Free the hive itself */
-            BlMmFreeHeap(BcdHive);
-        }
+        BiNotifyEnumerationError(ObjectHandle, ElementName, Status);
+    }
 
-        /* Finally, free the root key object if we created one */
-        if (KeyObject)
-        {
-            BlMmFreeHeap(KeyObject);
-        }
+    /* Finally free the subkeys array */
+    if (SubKeys)
+    {
+        BlMmFreeHeap(SubKeys);
     }
 
-    /* Return the final status */
+    /* And return the required, final length and status */
+    *ElementSize = TotalLength;
     return Status;
 }
 
@@ -1119,10 +1175,154 @@ BiAddStoreFromFile (
     return Status;
 }
 
+NTSTATUS
+BiGetObjectDescription (
+    _In_ HANDLE ObjectHandle,
+    _Out_ PBCD_OBJECT_DESCRIPTION Description
+    )
+{
+    NTSTATUS Status;
+    HANDLE DescriptionHandle;
+    PULONG Data;
+    ULONG Length;
+
+    /* Initialize locals */
+    Data = NULL;
+    DescriptionHandle = NULL;
+
+    /* Open the description key */
+    Status = BiOpenKey(ObjectHandle, L"Description", &DescriptionHandle);
+    if (NT_SUCCESS(Status))
+    {
+        /* It exists */
+        Description->Valid = TRUE;
+
+        /* 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);
+    }
+
+    /* Did we have data allocated? */
+    if (Data)
+    {
+        /* Free it */
+        BlMmFreeHeap(Data);
+    }
+
+    /* Return back to caller */
+    return Status;
+}
+
+NTSTATUS
+BcdEnumerateAndUnpackElements (
+    _In_ HANDLE BcdHandle,
+    _In_ HANDLE ObjectHandle,
+    _Out_opt_ PBCD_ELEMENT Elements,
+    _Inout_ PULONG ElementSize,
+    _Out_ PULONG ElementCount
+    )
+{
+    PVOID LocalElements;
+    NTSTATUS Status;
+    ULONG LocalElementCount, LocalElementSize;
+
+    /* Make sure required parameters are there */
+    if (!(ElementSize) || !(ElementCount) || ((Elements) && (!*ElementSize)))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* 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)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* 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))
+    {
+        return Status;
+    }
+
+    /* 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 StoreHandle
+    _In_ PHANDLE BcdHandle
     )
 {
     ULONG Length;
@@ -1155,7 +1355,7 @@ BcdOpenStoreFromFile (
     if (NT_SUCCESS(Status))
     {
         /* Return the handle on success */
-        *StoreHandle = LocalHandle;
+        *BcdHandle = LocalHandle;
     }
 
     /* Free the descriptor and return the status */