/* * COPYRIGHT: See COPYING.ARM in the top level directory * PROJECT: ReactOS UEFI Boot Library * FILE: boot/environ/lib/misc/bcdopt.c * PURPOSE: Boot Library BCD Option Parsing Routines * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org) */ /* INCLUDES ******************************************************************/ #include "bl.h" #include /* 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 ) { 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); } /* 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 BlGetBootOptionString ( _In_ PBL_BCD_OPTION List, _In_ ULONG Type, _Out_ PWCHAR* Value ) { NTSTATUS Status; PBL_BCD_OPTION Option; PWCHAR String, StringCopy; ULONG StringLength; BcdElementType ElementType; //PGUID AppIdentifier; /* Make sure this is a BCD_STRING */ ElementType.PackedValue = Type; if (ElementType.Format != BCD_TYPE_STRING) { 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 /* Make sure we have a valid, non-filtered string */ if (NT_SUCCESS(Status)) { /* Check if we have space for one more character */ Status = RtlULongAdd(StringLength, 1, &StringLength); if (NT_SUCCESS(Status)) { /* Check if it's safe to multiply by two */ Status = RtlULongMult(StringLength, sizeof(WCHAR), &StringLength); if (NT_SUCCESS(Status)) { /* Allocate a copy for the string */ StringCopy = BlMmAllocateHeap(StringLength); if (StringCopy) { /* NULL-terminate it */ RtlCopyMemory(StringCopy, String, StringLength - sizeof(UNICODE_NULL)); StringCopy[StringLength] = UNICODE_NULL; *Value = StringCopy; Status = STATUS_SUCCESS; } else { /* No memory, fail */ Status = STATUS_NO_MEMORY; } } } } /* All done */ return Status; } NTSTATUS BlGetBootOptionGuid ( _In_ PBL_BCD_OPTION List, _In_ ULONG Type, _Out_ PGUID Value ) { NTSTATUS Status; PBL_BCD_OPTION Option; PGUID Guid; BcdElementType ElementType; /* Make sure this is a BCD_TYPE_OBJECT */ ElementType.PackedValue = Type; if (ElementType.Format != BCD_TYPE_OBJECT) { return STATUS_INVALID_PARAMETER; } /* Return the data */ Option = MiscGetBootOption(List, Type); if (!Option) { /* Set failure if no data exists */ Status = STATUS_NOT_FOUND; } else { /* Copy the GUID */ Guid = (PGUID)((ULONG_PTR)Option + Option->DataOffset); RtlCopyMemory(Value, Guid, Option->DataSize); Status = STATUS_SUCCESS; } /* All good */ return Status; } NTSTATUS BlGetBootOptionGuidList ( _In_ PBL_BCD_OPTION List, _In_ ULONG Type, _Out_ PGUID *Value, _In_ PULONG Count ) { NTSTATUS Status; PBL_BCD_OPTION Option; PGUID GuidCopy, Guid; ULONG GuidCount; BcdElementType ElementType; /* Make sure this is a BCD_TYPE_OBJECT_LIST */ ElementType.PackedValue = Type; if (ElementType.Format != BCD_TYPE_OBJECT_LIST) { return STATUS_INVALID_PARAMETER; } /* Return the data */ Option = MiscGetBootOption(List, Type); if (!Option) { /* Set failure if no data exists */ Status = STATUS_NOT_FOUND; } else { /* Get the GUIDs and allocate a copy for them */ Guid = (PGUID)((ULONG_PTR)Option + Option->DataOffset); GuidCopy = BlMmAllocateHeap(Option->DataSize); if (GuidCopy) { /* Copy the GUIDs */ RtlCopyMemory(GuidCopy, Guid, Option->DataSize); /* Return the number of GUIDs and the start of the array */ GuidCount = Option->DataSize / sizeof(GUID); *Value = GuidCopy; *Count = GuidCount; Status = STATUS_SUCCESS; } else { /* No memory for the copy */ Status = STATUS_NO_MEMORY; } } /* All good */ 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; BcdElementType ElementType; /* Make sure this is a BCD_TYPE_DEVICE */ ElementType.PackedValue = Type; if (ElementType.Format != BCD_TYPE_DEVICE) { 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; BcdElementType ElementType; /* Make sure this is a BCD_TYPE_INTEGER */ ElementType.PackedValue = Type; if (ElementType.Format != BCD_TYPE_INTEGER) { 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; BcdElementType ElementType; /* Make sure this is a BCD_TYPE_BOOLEAN */ ElementType.PackedValue = Type; if (ElementType.Format != BCD_TYPE_BOOLEAN) { 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; } NTSTATUS BlpGetBootOptionIntegerList ( _In_ PBL_BCD_OPTION List, _In_ ULONG Type, _Out_ PULONGLONG* Value, _Out_ PULONGLONG Count, _In_ BOOLEAN NoCopy ) { PBL_BCD_OPTION Option; BcdElementType ElementType; PULONGLONG ValueCopy; /* Make sure this is a BCD_TYPE_INTEGER_LIST */ ElementType.PackedValue = Type; if (ElementType.Format != BCD_TYPE_INTEGER_LIST) { return STATUS_INVALID_PARAMETER; } /* Return the data */ Option = MiscGetBootOption(List, Type); if (!Option) { return STATUS_NOT_FOUND; } /* Check if a copy should be made of it */ if (NoCopy) { /* Nope, return the raw value */ *Value = (PULONGLONG)((ULONG_PTR)Option + Option->DataOffset); } else { /* Allocate a buffer for the copy */ ValueCopy = BlMmAllocateHeap(Option->DataSize); if (!ValueCopy) { return STATUS_NO_MEMORY; } /* Copy the data in */ RtlCopyMemory(ValueCopy, (PVOID)((ULONG_PTR)Option + Option->DataOffset), Option->DataSize); /* Return our copy */ *Value = ValueCopy; } /* Return count and success */ *Count = Option->DataSize / sizeof(ULONGLONG); return STATUS_SUCCESS; } NTSTATUS BlCopyBootOptions ( _In_ PBL_BCD_OPTION OptionList, _Out_ PBL_BCD_OPTION *CopiedOptions ) { NTSTATUS Status; ULONG OptionSize; PBL_BCD_OPTION Options; /* Assume no options */ Status = STATUS_SUCCESS; *CopiedOptions = NULL; /* Get the size of the list and allocate a copy for it */ OptionSize = BlGetBootOptionListSize(OptionList); Options = BlMmAllocateHeap(OptionSize); if (!Options) { return STATUS_NO_MEMORY; } /* Make the copy and return it to the caller */ RtlCopyMemory(Options, OptionList, OptionSize); *CopiedOptions = Options; return Status; } NTSTATUS BlAppendBootOptionBoolean ( _In_ PBL_LOADED_APPLICATION_ENTRY AppEntry, _In_ ULONG OptionId, _In_ BOOLEAN Value ) { NTSTATUS Status; PBL_BCD_OPTION Option; /* Allocate space for the entry -- remember BOOLEANs are USHORTs in BCD */ Option = BlMmAllocateHeap(sizeof(*Option) + sizeof(USHORT)); if (!Option) { return STATUS_NO_MEMORY; } /* Initialize it and set the boolean to TRUE */ RtlZeroMemory(Option, sizeof(*Option) + sizeof(USHORT)); Option->DataSize = sizeof(USHORT); Option->Type = OptionId; Option->DataOffset = sizeof(*Option); *(PBOOLEAN)(Option + 1) = Value; /* Append it */ Status = BlAppendBootOptions(AppEntry, Option); /* We're all done, free our initial option */ BlMmFreeHeap(Option); return Status; } NTSTATUS BlAppendBootOptionInteger ( _In_ PBL_LOADED_APPLICATION_ENTRY AppEntry, _In_ ULONG OptionId, _In_ ULONGLONG Value ) { NTSTATUS Status; PBL_BCD_OPTION Option; /* Allocate space for the entry */ Option = BlMmAllocateHeap(sizeof(*Option) + sizeof(Value)); if (!Option) { return STATUS_NO_MEMORY; } /* Initialize it and set the integer to the given value */ RtlZeroMemory(Option, sizeof(*Option) + sizeof(Value)); Option->DataSize = sizeof(Value); Option->Type = OptionId; Option->DataOffset = sizeof(*Option); *(PULONGLONG)(Option + 1) = Value; /* Append it */ Status = BlAppendBootOptions(AppEntry, Option); /* We're all done, free our initial option */ BlMmFreeHeap(Option); return Status; } NTSTATUS BlAppendBootOptionString ( _In_ PBL_LOADED_APPLICATION_ENTRY AppEntry, _In_ ULONG OptionId, _In_ PWCHAR OptionString ) { NTSTATUS Status; ULONG StringSize; PBL_BCD_OPTION Option; /* Get the length in bytes */ Status = RtlULongLongToULong(wcslen(OptionString) * sizeof(WCHAR), &StringSize); if (!NT_SUCCESS(Status)) { return Status; } /* Add a NULL-terminator */ Status = RtlULongAdd(StringSize, sizeof(UNICODE_NULL), &StringSize); if (!NT_SUCCESS(Status)) { return Status; } /* Allocate space for the entry */ Option = BlMmAllocateHeap(sizeof(*Option) + StringSize); if (!Option) { return STATUS_NO_MEMORY; } /* Initialize it and copy the string value */ RtlZeroMemory(Option, sizeof(*Option) + StringSize); Option->DataSize = StringSize; Option->Type = OptionId; Option->DataOffset = sizeof(*Option); wcsncpy((PWCHAR)Option + 1, OptionString, StringSize / sizeof(WCHAR)); /* Append it */ Status = BlAppendBootOptions(AppEntry, Option); /* We're all done, free our initial option */ BlMmFreeHeap(Option); return Status; } NTSTATUS BlAppendBootOptions ( _In_ PBL_LOADED_APPLICATION_ENTRY AppEntry, _In_ PBL_BCD_OPTION Options ) { ULONG OptionsSize, CurrentSize; PBL_BCD_OPTION NewOptions, CurrentOptions, NextOption; NTSTATUS Status; ULONG CurrentOffset; /* Get the current options */ CurrentOptions = AppEntry->BcdData; /* Calculate the size of the current, and the appended options */ CurrentSize = BlGetBootOptionListSize(CurrentOptions); OptionsSize = BlGetBootOptionListSize(Options); /* Allocate a buffer for the concatenated (new) options */ NewOptions = BlMmAllocateHeap(CurrentSize + OptionsSize); if (!NewOptions) { return STATUS_NO_MEMORY; } /* Copy the old options, and the ones to be added */ RtlCopyMemory(NewOptions, CurrentOptions, CurrentSize); RtlCopyMemory((PVOID)((ULONG_PTR)NewOptions + CurrentSize), Options, OptionsSize); /* We made it! */ Status = STATUS_SUCCESS; /* Scan through to the last option in the list */ CurrentOffset = 0; do { NextOption = (PBL_BCD_OPTION)((ULONG_PTR)NewOptions + CurrentOffset); CurrentOffset = NextOption->NextEntryOffset; } while (CurrentOffset); /* Every other option now has to have its offset adjusted */ do { NextOption->NextEntryOffset += CurrentSize; NextOption = (PBL_BCD_OPTION)((ULONG_PTR)NewOptions + NextOption->NextEntryOffset); } while (NextOption->NextEntryOffset); /* If we already had internal options, free them */ if (AppEntry->Flags & BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL) { BlMmFreeHeap(AppEntry->BcdData); } /* Write the new pointer */ AppEntry->BcdData = NewOptions; /* Options are now internal, not external */ AppEntry->Flags &= ~BL_APPLICATION_ENTRY_BCD_OPTIONS_EXTERNAL; AppEntry->Flags |= BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL; return Status; } VOID BlRemoveBootOption ( _In_ PBL_BCD_OPTION List, _In_ ULONG Type ) { PBL_BCD_OPTION Option; /* Keep going until the option is gone */ while (1) { /* Get the BCD option */ Option = MiscGetBootOption(List, Type); if (!Option) { break; } /* Pretend it's empty */ Option->Empty = TRUE; } } NTSTATUS BlReplaceBootOptions ( _In_ PBL_LOADED_APPLICATION_ENTRY AppEntry, _In_ PBL_BCD_OPTION OldOptions ) { NTSTATUS Status; ULONG OptionSize; PBL_BCD_OPTION NewOptions; /* Make sure there's something to replace with */ if (!OldOptions) { return STATUS_INVALID_PARAMETER; } /* Check if we already had allocated internal options */ if (AppEntry->Flags & BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL) { /* Free them */ BlMmFreeHeap(AppEntry->BcdData); } /* Reset option flags */ AppEntry->Flags &= ~(BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL | BL_APPLICATION_ENTRY_BCD_OPTIONS_EXTERNAL); /* Reset the options and set success for now */ Status = STATUS_SUCCESS; AppEntry->BcdData = NULL; /* Get the size of the new list of options */ OptionSize = BlGetBootOptionListSize(OldOptions); /* Allocate a copy of the new list */ NewOptions = BlMmAllocateHeap(OptionSize); if (!NewOptions) { return STATUS_NO_MEMORY; } /* Copy it in */ RtlCopyMemory(NewOptions, OldOptions, OptionSize); /* Set it as the new set of options and return */ AppEntry->Flags |= BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL; AppEntry->BcdData = NewOptions; return Status; }