[BOOTLIB]:
[reactos.git] / reactos / boot / environ / lib / misc / bcd.c
index 58eed43..d62ff7c 100644 (file)
@@ -9,6 +9,7 @@
 /* INCLUDES ******************************************************************/
 
 #include "bl.h"
+#include <bcd.h>
 
 /* FUNCTIONS *****************************************************************/
 
@@ -19,7 +20,7 @@ MiscGetBootOption (
     )
 {
     ULONG_PTR NextOption = 0, ListOption;
-    PBL_BCD_OPTION Option;
+    PBL_BCD_OPTION Option, FoundOption;
 
     /* No options, bail out */
     if (!List)
@@ -28,12 +29,14 @@ MiscGetBootOption (
     }
 
     /* Loop while we find an option */
-    while (TRUE)
+    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;
         }
 
@@ -48,17 +51,95 @@ MiscGetBootOption (
             Option = MiscGetBootOption((PBL_BCD_OPTION)((ULONG_PTR)Option +
                                        ListOption),
                                        Type);
-
-            /* Found one, return it */
             if (Option)
             {
-                return 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
+    )
+{
+    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;
+}
+
+/*++
+ * @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
+    )
+{
+    ULONG Size, Offset;
+
+    /* Check if there's any data */
+    if (BcdOption->DataOffset)
+    {
+        /* Add the size of the data */
+        Size = BcdOption->DataOffset + BcdOption->DataSize;
+    }
+    else
+    {
+        /* No data, just the structure itself */
+        Size = sizeof(*BcdOption);
     }
 
-    /* We found the option, return it */
-    return Option;
+    /* Any associated options? */
+    Offset = BcdOption->ListOffset;
+    if (Offset)
+    {
+        /* Go get those too */
+        Size += BlGetBootOptionListSize((PVOID)((ULONG_PTR)BcdOption + Offset));
+    }
+
+    /* Return the final size */
+    return Size;
 }
 
 NTSTATUS
@@ -86,11 +167,13 @@ BlGetBootOptionString (
     {
         /* Extract the string */
         String = (PWCHAR)((ULONG_PTR)Option + Option->DataOffset);
+        Status = STATUS_SUCCESS;
     }
     else
     {
         /* No string is present */
         String = NULL;
+        Status = STATUS_NOT_FOUND;
     }
 
     /* Compute the data size */
@@ -101,9 +184,7 @@ BlGetBootOptionString (
     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)
@@ -112,11 +193,6 @@ BlGetBootOptionString (
         CopyLength = -1;
         Status = STATUS_INTEGER_OVERFLOW;
     }
-    else
-    {
-        /* Go ahead */
-        Status = STATUS_SUCCESS;
-    }
 
     /* No overflow? */
     if (NT_SUCCESS(Status))
@@ -161,6 +237,150 @@ BlGetBootOptionString (
     return Status;
 }
 
+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;
+    }
+
+    /* 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;
+
+        /* Allocate a buffer to copy it into */
+        DeviceDescriptor = BlMmAllocateHeap(DeviceSize);
+        if (!DeviceDescriptor)
+        {
+            return STATUS_NO_MEMORY;
+        }
+
+        /* Copy it into that buffer */
+        RtlCopyMemory(DeviceDescriptor, &BcdDevice->DeviceDescriptor, DeviceSize);
+        Status = STATUS_SUCCESS;
+    }
+
+    /* 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);
+
+            /* Allocate a buffer to hold them into */
+            ListCopy = BlMmAllocateHeap(ListSize);
+            if (!ListCopy)
+            {
+                Status = STATUS_NO_MEMORY;
+                goto Quickie;
+            }
+
+            /* 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
+
+    /* 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)
+            {
+                BlMmFreeHeap(DeviceDescriptor);
+            }
+        }
+
+        /* Check if we had a new list after filtering */
+        if (SecureListData != ListCopy)
+        {
+            /* Yep -- if we had an old list, free it */
+            if (ListCopy)
+            {
+                BlMmFreeHeap(ListCopy);
+            }
+        }
+
+        /* Finally, check if the caller wanted extra options */
+        if (ExtraOptions)
+        {
+            /* Yep -- so pass the caller our copy */
+            *ExtraOptions = ListCopy;
+            ListCopy = NULL;
+        }
+
+        /* Caller always wants data back, so pass them our copy */
+        *Value = DeviceDescriptor;
+        DeviceDescriptor = NULL;
+    }
+
+Quickie:
+    /* On the failure path, if these buffers are active, we should free them */
+    if (ListCopy)
+    {
+        BlMmFreeHeap(ListCopy);
+    }
+    if (DeviceDescriptor)
+    {
+        BlMmFreeHeap(DeviceDescriptor);
+    }
+
+    /* All done */
+    return Status;
+}
+
 NTSTATUS
 BlGetBootOptionInteger (
     _In_ PBL_BCD_OPTION List,
@@ -231,80 +451,343 @@ BlGetBootOptionBoolean (
     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
+#define BI_FLUSH_HIVE   0x01
+
+typedef struct _BI_KEY_HIVE
+{
+    PVOID ImageBase;
+    PBL_FILE_PATH_DESCRIPTOR FilePath;
+    CMHIVE Hive;
+    LONG ReferenceCount;
+    ULONG Flags;
+} BI_KEY_HIVE, *PBI_KEY_HIVE;
+
+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
     )
 {
-    ULONG Size = 0, NextOffset = 0;
-    PBL_BCD_OPTION NextOption;
+    UNREFERENCED_PARAMETER(Paged);
+    UNREFERENCED_PARAMETER(Tag);
 
-    /* Loop all the options*/
-    do
+    /* Call the heap allocator */
+    return BlMmAllocateHeap(Size);
+}
+
+VOID
+NTAPI
+CmpFree (
+    _In_ PVOID Ptr,
+    _In_ ULONG Quota
+    )
+{
+    UNREFERENCED_PARAMETER(Quota);
+
+    /* Call the heap allocator */
+    BlMmFreeHeap(Ptr);
+}
+
+FORCEINLINE
+VOID
+BiDereferenceHive (
+    _In_ HANDLE KeyHandle
+    )
+{
+    PBI_KEY_OBJECT KeyObject;
+
+    /* Get the key object */
+    KeyObject = (PBI_KEY_OBJECT)KeyHandle;
+
+    /* Drop a reference on the parent hive */
+    --KeyObject->KeyHive->ReferenceCount;
+}
+
+VOID
+BiFlushHive (
+    _In_ HANDLE KeyHandle
+    )
+{
+    /* Not yet implemented */
+    EfiPrintf(L"NO reg flush\r\n");
+    return;
+}
+
+VOID
+BiCloseKey (
+    _In_ HANDLE KeyHandle
+    )
+{
+    PBI_KEY_HIVE KeyHive;
+    PBI_KEY_OBJECT KeyObject;
+
+    /* Get the key object and hive */
+    KeyObject = (PBI_KEY_OBJECT)KeyHandle;
+    KeyHive = KeyObject->KeyHive;
+
+    /* Check if we have a hive, or name, or key node */
+    if ((KeyHive) || (KeyObject->KeyNode) || (KeyObject->KeyName))
     {
-        /* Move to the next one */
-        NextOption = (PBL_BCD_OPTION)((ULONG_PTR)BcdOption + NextOffset);
+        /* 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)
+            {
+                BiFlushHive(KeyHandle);
+            }
 
-        /* Compute the size of the next one */
-        Size += BlGetBootOptionSize(NextOption);
+            /* Unmap the hive */
+            //MmPapFreePages(KeyHive->ImageBase, 1);
+            EfiPrintf(L"Leaking hive memory\r\n");
 
-        /* Update the offset */
-        NextOffset = NextOption->NextEntryOffset;
-    } while (NextOffset != 0);
+            /* Free the hive and hive path */
+            BlMmFreeHeap(KeyHive->FilePath);
+            BlMmFreeHeap(KeyHive);
+        }
 
-    /* Return final computed size */
-    return Size;
+        /* Check if a key name is present */
+        if (KeyObject->KeyName)
+        {
+            /* Free it */
+            BlMmFreeHeap(KeyObject->KeyName);
+        }
+    }
+
+    /* Free the object */
+    BlMmFreeHeap(KeyObject);
 }
 
-/*++
- * @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
+BiOpenKey(
+    _In_ HANDLE ParentHandle,
+    _In_ PWCHAR KeyName,
+    _Out_ PHANDLE Handle
     )
 {
-    ULONG Size, Offset;
+    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;
+
+    /* Convert from a handle to our key object */
+    ParentKey = (PBI_KEY_OBJECT)ParentHandle;
+
+    /* Extract the hive and node information */
+    ParentHive = ParentKey->KeyHive;
+    ParentNode = ParentKey->KeyNode;
+    Hive = &ParentKey->KeyHive->Hive.Hive;
+
+    /* Initialize variables */
+    KeyCell = HCELL_NIL;
+    Status = STATUS_SUCCESS;
+    NameBuffer = NULL;
 
-    /* Check if there's any data */
-    if (BcdOption->DataOffset != 0)
+    /* Loop as long as there's still portions of the key name in play */
+    NameLength = wcslen(KeyName);
+    while (NameLength)
     {
-        /* Add the size of the data */
-        Size = BcdOption->DataOffset + BcdOption->DataSize;
+        /* 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;
+        }
+
+        /* Free the name buffer from the previous pass if needed */
+        if (NameBuffer)
+        {
+            BlMmFreeHeap(NameBuffer);
+        }
+
+        /* 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;
+        }
+
+        /* Copy and null-terminate the name of the subkey */
+        RtlCopyMemory(NameBuffer, KeyName, NameBytes);
+        NameBuffer[SubNameLength] = UNICODE_NULL;
+
+        /* Convert it into a UNICODE_STRING and try to find it */
+        RtlInitUnicodeString(&KeyString, NameBuffer);
+        KeyCell = CmpFindSubKeyByName(Hive, ParentNode, &KeyString);
+        if (KeyCell == HCELL_NIL)
+        {
+            Status = STATUS_OBJECT_NAME_NOT_FOUND;
+            goto Quickie;
+        }
+
+        /* We found it -- get the key node out of it */
+        ParentNode = (PCM_KEY_NODE)Hive->GetCellRoutine(Hive, KeyCell);
+        if (!ParentNode)
+        {
+            Status = STATUS_REGISTRY_CORRUPT;
+            goto Quickie;
+        }
+
+        /* Update the key name to the next remaining path element */
+        KeyName = NameStart;
+        if (NameStart)
+        {
+            /* Update the length to the remainder of the path */
+            NameLength += -1 - SubNameLength;
+        }
+        else
+        {
+            /* There's nothing left, this was the leaf key */
+            NameLength = 0;
+        }
+    }
+
+    /* Allocate a key object */
+    NewKey = BlMmAllocateHeap(sizeof(*NewKey));
+    if (!NewKey)
+    {
+        /* 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;
+
+    /* Return the object back to the caller */
+    *Handle = NewKey;
+
+Quickie:
+    /* If we had a name buffer, free it */
+    if (NameBuffer)
+    {
+        BlMmFreeHeap(NameBuffer);
+    }
+
+    /* Return status of the open operation */
+    return Status;
+}
+
+NTSTATUS
+BiLoadHive (
+    _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
+    _Out_ PHANDLE HiveHandle
+    )
+{
+    /* This is TODO */
+    EfiPrintf(L"Loading a hive is not yet implemented\r\n");
+    *HiveHandle = NULL;
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS
+BiAddStoreFromFile (
+    _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
+    _Out_ PHANDLE StoreHandle
+    )
+{
+    NTSTATUS Status;
+    HANDLE HiveHandle, KeyHandle;
+
+    /* Load the specified hive */
+    Status = BiLoadHive(FilePath, &HiveHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* 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
     {
-        /* No data, just the structure itself */
-        Size = sizeof(*BcdOption);
+        /* Failure, drop a reference on the hive and close the key */
+        BiDereferenceHive(HiveHandle);
+        BiCloseKey(HiveHandle);
     }
 
-    /* Any associated options? */
-    Offset = BcdOption->ListOffset;
-    if (Offset != 0)
+    /* Return the status */
+    return Status;
+}
+
+NTSTATUS
+BcdOpenStoreFromFile (
+    _In_ PUNICODE_STRING FileName,
+    _In_ PHANDLE StoreHandle
+    )
+{
+    ULONG Length;
+    PBL_FILE_PATH_DESCRIPTOR FilePath;
+    NTSTATUS Status;
+    HANDLE LocalHandle;
+
+    /* Assume failure */
+    LocalHandle = NULL;
+    EfiPrintf(L"Opening BCD store: %wZ\n", FileName);
+
+    /* Allocate a path descriptor */
+    Length = FileName->Length + sizeof(*FilePath);
+    FilePath = BlMmAllocateHeap(Length);
+    if (!FilePath)
     {
-        /* Go get those too */
-        Size += BlGetBootOptionListSize((PVOID)((ULONG_PTR)BcdOption + Offset));
+        return STATUS_NO_MEMORY;
     }
 
-    /* Return the final size */
-    return Size;
+    /* 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 */
+        *StoreHandle = LocalHandle;
+    }
+
+    /* Free the descriptor and return the status */
+    BlMmFreeHeap(FilePath);
+    return Status;
 }
+