[BOOTLIB]:
[reactos.git] / reactos / boot / environ / lib / misc / bcd.c
index 903f1f1..d62ff7c 100644 (file)
@@ -9,9 +9,61 @@
 /* INCLUDES ******************************************************************/
 
 #include "bl.h"
+#include <bcd.h>
 
 /* 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
  *
@@ -42,7 +94,7 @@ BlGetBootOptionListSize (
 
         /* Update the offset */
         NextOffset = NextOption->NextEntryOffset;
-    } while (NextOffset != 0);
+    } while (NextOffset);
 
     /* Return final computed size */
     return Size;
@@ -67,7 +119,7 @@ BlGetBootOptionSize (
     ULONG Size, Offset;
 
     /* Check if there's any data */
-    if (BcdOption->DataOffset != 0)
+    if (BcdOption->DataOffset)
     {
         /* Add the size of the data */
         Size = BcdOption->DataOffset + BcdOption->DataSize;
@@ -80,7 +132,7 @@ BlGetBootOptionSize (
 
     /* Any associated options? */
     Offset = BcdOption->ListOffset;
-    if (Offset != 0)
+    if (Offset)
     {
         /* Go get those too */
         Size += BlGetBootOptionListSize((PVOID)((ULONG_PTR)BcdOption + Offset));
@@ -89,3 +141,653 @@ BlGetBootOptionSize (
     /* Return the final size */
     return Size;
 }
+
+NTSTATUS
+BlGetBootOptionString (
+    _In_ PBL_BCD_OPTION List,
+    _In_ ULONG Type,
+    _Out_ PWCHAR* Value
+    )
+{
+    NTSTATUS Status;
+    PBL_BCD_OPTION Option;
+    PWCHAR String, StringCopy;
+    ULONG StringLength, CopyLength;
+    //PGUID AppIdentifier;
+
+    /* Make sure this is a BCD_STRING */
+    if ((Type & 0xF000000) != 0x2000000)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Return the data */
+    Option = MiscGetBootOption(List, Type);
+    if (Option)
+    {
+        /* 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 */
+    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;
+    }
+
+    /* 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);
+        }
+
+        /* Allocate a copy for the string */
+        if (NT_SUCCESS(Status))
+        {
+            StringCopy = BlMmAllocateHeap(CopyLength);
+            if (StringCopy)
+            {
+                /* NULL-terminate it */
+                RtlCopyMemory(StringCopy,
+                              String,
+                              CopyLength - sizeof(UNICODE_NULL));
+                StringCopy[CopyLength] = UNICODE_NULL;
+                *Value = StringCopy;
+                Status = STATUS_SUCCESS;
+            }
+            else
+            {
+                /* No memory, fail */
+                Status = STATUS_NO_MEMORY;
+            }
+        }
+    }
+
+    /* All done */
+    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,
+    _In_ ULONG Type,
+    _Out_ PULONGLONG Value
+    )
+{
+    NTSTATUS Status;
+    PBL_BCD_OPTION Option;
+    //PGUID AppIdentifier;
+
+    /* Make sure this is a BCD_INTEGER */
+    if ((Type & 0xF000000) != 0x5000000)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Return the data */
+    Option = MiscGetBootOption(List, Type);
+    if (Option)
+    {
+        *Value = *(PULONGLONG)((ULONG_PTR)Option + Option->DataOffset);
+    }
+
+#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;
+}
+
+NTSTATUS
+BlGetBootOptionBoolean (
+    _In_ PBL_BCD_OPTION List,
+    _In_ ULONG Type,
+    _Out_ PBOOLEAN Value
+    )
+{
+    NTSTATUS Status;
+    PBL_BCD_OPTION Option;
+    //PGUID AppIdentifier;
+
+    /* Make sure this is a BCD_BOOLEAN */
+    if ((Type & 0xF000000) != 0x6000000)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Return the data */
+    Option = MiscGetBootOption(List, Type);
+    if (Option)
+    {
+        *Value = *(PBOOLEAN)((ULONG_PTR)Option + Option->DataOffset);
+    }
+
+#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;
+}
+
+#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
+    )
+{
+    UNREFERENCED_PARAMETER(Paged);
+    UNREFERENCED_PARAMETER(Tag);
+
+    /* 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))
+    {
+        /* 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);
+            }
+
+            /* Unmap the hive */
+            //MmPapFreePages(KeyHive->ImageBase, 1);
+            EfiPrintf(L"Leaking hive memory\r\n");
+
+            /* Free the hive and hive path */
+            BlMmFreeHeap(KeyHive->FilePath);
+            BlMmFreeHeap(KeyHive);
+        }
+
+        /* Check if a key name is present */
+        if (KeyObject->KeyName)
+        {
+            /* Free it */
+            BlMmFreeHeap(KeyObject->KeyName);
+        }
+    }
+
+    /* Free the object */
+    BlMmFreeHeap(KeyObject);
+}
+
+NTSTATUS
+BiOpenKey(
+    _In_ HANDLE ParentHandle,
+    _In_ PWCHAR KeyName,
+    _Out_ PHANDLE Handle
+    )
+{
+    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;
+
+    /* Loop as long as there's still portions of the key name in play */
+    NameLength = wcslen(KeyName);
+    while (NameLength)
+    {
+        /* 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
+    {
+        /* Failure, drop a reference on the hive and close the key */
+        BiDereferenceHive(HiveHandle);
+        BiCloseKey(HiveHandle);
+    }
+
+    /* 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)
+    {
+        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 */
+        *StoreHandle = LocalHandle;
+    }
+
+    /* Free the descriptor and return the status */
+    BlMmFreeHeap(FilePath);
+    return Status;
+}
+