diff --git a/reactos/boot/environ/CMakeLists.txt b/reactos/boot/environ/CMakeLists.txt index b790ab30997..e7dc32b2d8d 100644 --- a/reactos/boot/environ/CMakeLists.txt +++ b/reactos/boot/environ/CMakeLists.txt @@ -12,6 +12,8 @@ list(APPEND BOOTLIB_SOURCE lib/bootlib.c lib/misc/debug.c lib/misc/bcd.c + lib/misc/bcdopt.c + lib/misc/bootreg.c lib/misc/util.c lib/misc/image.c lib/firmware/efi/firmware.c diff --git a/reactos/boot/environ/app/bootmgr/bootmgr.c b/reactos/boot/environ/app/bootmgr/bootmgr.c index bf1e305d509..2e80c184576 100644 --- a/reactos/boot/environ/app/bootmgr/bootmgr.c +++ b/reactos/boot/environ/app/bootmgr/bootmgr.c @@ -31,8 +31,197 @@ BOOLEAN BmBootIniUsed; WCHAR BmpFileNameBuffer[128]; PWCHAR ParentFileName = L""; +BOOLEAN BmDisplayStateCached; + /* FUNCTIONS *****************************************************************/ +NTSTATUS +BmGetOptionList ( + _In_ HANDLE BcdHandle, + _In_ PGUID ObjectId, + _In_ PBL_BCD_OPTION *OptionList + ) +{ + NTSTATUS Status; + HANDLE ObjectHandle; + ULONG ElementSize, ElementCount, i, OptionsSize; + BcdElementType Type; + PBCD_ELEMENT_HEADER Header; + PBCD_ELEMENT BcdElements; + PBL_BCD_OPTION Options, Option, PreviousOption, DeviceOptions; + PBCD_DEVICE_OPTION DeviceOption; + GUID DeviceId; + PVOID DeviceData; + + /* Open the BCD object requested */ + ObjectHandle = NULL; + BcdElements = NULL; + Status = BcdOpenObject(BcdHandle, ObjectId, &ObjectHandle); + if (!NT_SUCCESS(Status)) + { + goto Quickie; + } + + /* Do the initial enumeration to get the size needed */ + ElementSize = 0; + Status = BcdEnumerateAndUnpackElements(BcdHandle, + ObjectHandle, + NULL, + &ElementSize, + &ElementCount); + if (Status != STATUS_BUFFER_TOO_SMALL) + { + /* If we got success, that doesn't make any sense */ + if (NT_SUCCESS(Status)) + { + Status = STATUS_INVALID_PARAMETER; + } + + /* Bail out */ + goto Quickie; + } + + /* Allocate a large-enough buffer */ + BcdElements = BlMmAllocateHeap(ElementSize); + if (!BcdElements) + { + Status = STATUS_NO_MEMORY; + goto Quickie; + } + + /* Now do the real enumeration to fill out the elements buffer */ + Status = BcdEnumerateAndUnpackElements(BcdHandle, + ObjectHandle, + BcdElements, + &ElementSize, + &ElementCount); + if (!NT_SUCCESS(Status)) + { + goto Quickie; + } + + /* Go through each BCD option to add the sizes up */ + OptionsSize = 0; + for (i = 0; i < ElementCount; i++) + { + OptionsSize += BcdElements[i].Header->Size + sizeof(BL_BCD_OPTION); + } + + /* Allocate the required BCD option list */ + Options = BlMmAllocateHeap(OptionsSize); + if (!Options) + { + Status = STATUS_NO_MEMORY; + goto Quickie; + } + + /* Zero it out */ + RtlZeroMemory(Options, OptionsSize); + + /* Start going through each option */ + PreviousOption = NULL; + Option = Options; + EfiPrintf(L"BCD Options found: %d\r\n", ElementCount); + for (i = 0; i < ElementCount; i++) + { + /* Read the header and type */ + Header = BcdElements[i].Header; + Type.PackedValue = Header->Type; + + /* Check if this option isn't already present */ + if (!MiscGetBootOption(Options, Type.PackedValue)) + { + /* It's a new option. Did we have an existing one? */ + if (PreviousOption) + { + /* Link it to this new one */ + PreviousOption->NextEntryOffset = (ULONG_PTR)Option - + (ULONG_PTR)Options; + } + + /* Capture the type, size, data, and offset */ + Option->Type = Type.PackedValue; + Option->DataSize = Header->Size; + RtlCopyMemory(Option + 1, BcdElements[i].Body, Header->Size); + Option->DataOffset = sizeof(BL_BCD_OPTION); + + /* Check if this was a device */ + if (Type.Format == BCD_TYPE_DEVICE) + { + /* Grab its GUID */ + DeviceOption = (PBCD_DEVICE_OPTION)(Option + 1); + DeviceId = DeviceOption->AssociatedEntry; + + /* Look up the options for that GUID */ + Status = BmGetOptionList(BcdHandle, &DeviceId, &DeviceOptions); + if (NT_SUCCESS(Status)) + { + /* Device data is after the device option */ + DeviceData = (PVOID)((ULONG_PTR)DeviceOption + Header->Size); + + /* Copy it */ + RtlCopyMemory(DeviceData, + DeviceOptions, + BlGetBootOptionListSize(DeviceOptions)); + + /* Don't need this anymore */ + BlMmFreeHeap(DeviceOptions); + + /* Write the offset of the device options */ + Option->ListOffset = (ULONG_PTR)DeviceData - + (ULONG_PTR)Option; + } + } + + /* Save the previous option and go to the next one */ + PreviousOption = Option; + Option = (PBL_BCD_OPTION)((ULONG_PTR)Option + + BlGetBootOptionSize(Option)); + } + } + + /* Return the pointer back, we've made it! */ + *OptionList = Options; + Status = STATUS_SUCCESS; + +Quickie: + /* Did we allocate a local buffer? Free it if so */ + if (BcdElements) + { + BlMmFreeHeap(BcdElements); + } + + /* Was the key open? Close it if so */ + if (ObjectHandle) + { + BiCloseKey(ObjectHandle); + } + + /* Return the option list parsing status */ + return Status; +} + +NTSTATUS +BmpUpdateApplicationOptions ( + _In_ HANDLE BcdHandle + ) +{ + NTSTATUS Status; + PBL_BCD_OPTION Options; + + /* Get the boot option list */ + Status = BmGetOptionList(BcdHandle, &BmApplicationIdentifier, &Options); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Append the options, free the local buffer, and return success */ + BlAppendBootOptions(&BlpApplicationEntry, Options); + BlMmFreeHeap(Options); + return STATUS_SUCCESS; +} + NTSTATUS BmpFwGetApplicationDirectoryPath ( _In_ PUNICODE_STRING ApplicationDirectoryPath @@ -65,39 +254,14 @@ BmpFwGetApplicationDirectoryPath ( } /* Check if we have space for one more character */ - AppPathLength = i + 1; - if (AppPathLength < i) - { - /* Nope, we'll overflow */ - AppPathLength = -1; - Status = STATUS_INTEGER_OVERFLOW; - } - else - { - /* Go ahead */ - Status = STATUS_SUCCESS; - } - - /* No overflow? */ + Status = RtlULongAdd(i, 1, &AppPathLength); if (NT_SUCCESS(Status)) { /* Check if it's safe to multiply by two */ - if ((AppPathLength * sizeof(WCHAR)) > 0xFFFFFFFF) - { - /* Nope */ - AppPathLength = -1; - Status = STATUS_INTEGER_OVERFLOW; - } - else - { - /* We're good, do the multiplication */ - Status = STATUS_SUCCESS; - AppPathLength *= sizeof(WCHAR); - } - - /* Allocate a copy for the string */ + Status = RtlULongMult(AppPathLength, sizeof(WCHAR), &AppPathLength); if (NT_SUCCESS(Status)) { + /* Allocate a copy for the string */ PathCopy = BlMmAllocateHeap(AppPathLength); if (PathCopy) { @@ -382,6 +546,38 @@ BmFatalErrorEx ( ErrorResourceId = 9002; break; + case BL_FATAL_ERROR_BCD_PARSE: + + /* File name isin parameter 1 */ + FileName = (PWCHAR)Parameter1; + + /* The NTSTATUS code is in parameter 2*/ + ErrorStatus = (NTSTATUS)Parameter2; + + /* Build the error string */ + swprintf(FormatString, + L"\nThe boot configuration file %s is invalid (%08x).\n", + FileName, + ErrorStatus); + + /* Select the resource ID message */ + ErrorResourceId = 9015; + break; + + case BL_FATAL_ERROR_GENERIC: + + /* The NTSTATUS code is in parameter 1*/ + ErrorStatus = (NTSTATUS)Parameter1; + + /* Build the error string */ + swprintf(FormatString, + L"\nThe boot manager experienced an error (%08x).\n", + ErrorStatus); + + /* Select the resource ID message */ + ErrorResourceId = 9005; + break; + default: /* The rest is not yet handled */ @@ -427,25 +623,11 @@ BmpFwGetFullPath ( ) { NTSTATUS Status; - ULONG BootDirLength, BootDirLengthWithNul; - ULONG PathLength, FullPathLength; + ULONG BootDirLength, PathLength; /* Compute the length of the directory, and add a NUL */ BootDirLength = wcslen(BootDirectory); - BootDirLengthWithNul = BootDirLength + 1; - if (BootDirLengthWithNul < BootDirLength) - { - /* This would overflow */ - BootDirLengthWithNul = -1; - Status = STATUS_INTEGER_OVERFLOW; - } - else - { - /* We have space */ - Status = STATUS_SUCCESS; - } - - /* Fail on overflow */ + Status = RtlULongAdd(BootDirLength, 1, &BootDirLength); if (!NT_SUCCESS(Status)) { goto Quickie; @@ -453,33 +635,26 @@ BmpFwGetFullPath ( /* Add the length of the file, make sure it fits */ PathLength = wcslen(FileName); - FullPathLength = PathLength + BootDirLength; - if (FullPathLength < PathLength) + Status = RtlULongAdd(PathLength, BootDirLength, &PathLength); + if (!NT_SUCCESS(Status)) { - /* Nope */ - FullPathLength = -1; - Status = STATUS_INTEGER_OVERFLOW; - } - else - { - /* All good */ - Status = STATUS_SUCCESS; + goto Quickie; } - /* Fail on overflow */ + /* Convert to bytes */ + Status = RtlULongLongToULong(PathLength * sizeof(WCHAR), &PathLength); if (!NT_SUCCESS(Status)) { goto Quickie; } /* Allocate the full path */ - FullPathLength = FullPathLength * sizeof(WCHAR); - *FullPath = BlMmAllocateHeap(FullPathLength); + *FullPath = BlMmAllocateHeap(PathLength); if (*FullPath) { /* Copy the directory followed by the file name */ - wcsncpy(*FullPath, BootDirectory, FullPathLength / sizeof(WCHAR)); - wcsncat(*FullPath, FileName, FullPathLength / sizeof(WCHAR)); + wcsncpy(*FullPath, BootDirectory, PathLength / sizeof(WCHAR)); + wcsncat(*FullPath, FileName, PathLength / sizeof(WCHAR)); } else { @@ -492,6 +667,22 @@ Quickie: return Status; } +VOID +BmCloseDataStore ( + _In_ HANDLE Handle + ) +{ + /* Check if boot.ini data needs to be freed */ + if (BmBootIniUsed) + { + EfiPrintf(L"Not handled\r\n"); + } + + /* Dereference the hive and close the key */ + BiDereferenceHive(Handle); + BiCloseKey(Handle); +} + NTSTATUS BmOpenDataStore ( _Out_ PHANDLE Handle @@ -501,7 +692,7 @@ BmOpenDataStore ( PBL_DEVICE_DESCRIPTOR BcdDevice; PWCHAR BcdPath, FullPath, PathBuffer; BOOLEAN HavePath; - ULONG PathLength, PathLengthWithNul, FullSize; + ULONG PathLength, FullSize; PVOID FinalBuffer; UNICODE_STRING BcdString; @@ -544,7 +735,7 @@ BmOpenDataStore ( if (NT_SUCCESS(Status)) { /* We don't handle custom BCDs yet */ - EfiPrintf(L"Not handled\n"); + EfiPrintf(L"Not handled: %s\r\n", BcdPath); Status = STATUS_NOT_IMPLEMENTED; goto Quickie; } @@ -579,35 +770,22 @@ BmOpenDataStore ( } /* Add a NUL to the path, make sure it'll fit */ - Status = STATUS_SUCCESS; PathLength = wcslen(PathBuffer); - PathLengthWithNul = PathLength + 1; - if (PathLengthWithNul < PathLength) + Status = RtlULongAdd(PathLength, 1, &PathLength); + if (!NT_SUCCESS(Status)) { - PathLengthWithNul = -1; - Status = STATUS_INTEGER_OVERFLOW; + goto Quickie; } - /* Bail out if it doesn't fit */ + /* Convert to bytes */ + Status = RtlULongLongToULong(PathLength * sizeof(WCHAR), &PathLength); if (!NT_SUCCESS(Status)) { goto Quickie; } /* Now add the size of the path to the device path, check if it fits */ - PathLengthWithNul = PathLengthWithNul * sizeof(WCHAR); - FullSize = PathLengthWithNul + BcdDevice->Size; - if (FullSize < BcdDevice->Size) - { - FullSize = -1; - Status = STATUS_INTEGER_OVERFLOW; - } - else - { - Status = STATUS_SUCCESS; - } - - /* Bail out if it doesn't fit */ + Status = RtlULongAdd(PathLength, BcdDevice->Size, &FullSize); if (!NT_SUCCESS(Status)) { goto Quickie; @@ -625,7 +803,7 @@ BmOpenDataStore ( RtlCopyMemory(FinalBuffer, BcdDevice, BcdDevice->Size); RtlCopyMemory((PVOID)((ULONG_PTR)FinalBuffer + BcdDevice->Size), PathBuffer, - PathLengthWithNul); + PathLength); /* Now tell the BCD engine to open the store */ BcdString.Length = FullSize; @@ -648,7 +826,11 @@ Quickie: if (!NT_SUCCESS(Status)) { /* Raise a fatal error */ - BmFatalErrorEx(1, (ULONG_PTR)PathBuffer, Status, 0, 0); + BmFatalErrorEx(BL_FATAL_ERROR_BCD_READ, + (ULONG_PTR)PathBuffer, + Status, + 0, + 0); } /* Did we get an allocated path? */ @@ -680,12 +862,13 @@ BmMain ( _In_ PBOOT_APPLICATION_PARAMETER_BLOCK BootParameters ) { - NTSTATUS Status; + NTSTATUS Status, LibraryStatus; BL_LIBRARY_PARAMETERS LibraryParameters; PBL_RETURN_ARGUMENTS ReturnArguments; BOOLEAN RebootOnError; PGUID AppIdentifier; HANDLE BcdHandle; + PBL_BCD_OPTION EarlyOptions; EfiPrintf(L"ReactOS UEFI Boot Manager Initializing...\n"); @@ -734,12 +917,73 @@ BmMain ( /* Load and initialize the boot configuration database (BCD) */ Status = BmOpenDataStore(&BcdHandle); - EfiPrintf(L"BCD Open: %lx\r\n", Status); + if (NT_SUCCESS(Status)) + { + /* Copy the boot options */ + Status = BlCopyBootOptions(BlpApplicationEntry.BcdData, &EarlyOptions); + if (NT_SUCCESS(Status)) + { + /* Update them */ + Status = BmpUpdateApplicationOptions(BcdHandle); + if (!NT_SUCCESS(Status)) + { + /* Log a fatal error */ + BmFatalErrorEx(BL_FATAL_ERROR_BCD_PARSE, + (ULONG_PTR)L"\\BCD", + Status, + 0, + 0); + } + } + } + +#ifdef _SECURE_BOOT + /* Initialize the secure boot machine policy */ + Status = BmSecureBootInitializeMachinePolicy(); + if (!NT_SUCCESS(Status)) + { + BmFatalErrorEx(BL_FATAL_ERROR_SECURE_BOOT, Status, 0, 0, 0); + } +#endif + + /* Copy the library parameters and add the re-initialization flag */ + RtlCopyMemory(&LibraryParameters, + &BlpLibraryParameters, + sizeof(LibraryParameters)); + LibraryParameters.LibraryFlags |= (BL_LIBRARY_FLAG_REINITIALIZE_ALL | + BL_LIBRARY_FLAG_REINITIALIZE); + + /* Now that we've parsed the BCD, re-initialize the library */ + LibraryStatus = BlInitializeLibrary(BootParameters, &LibraryParameters); + if (!NT_SUCCESS(LibraryStatus) && (NT_SUCCESS(Status))) + { + Status = LibraryStatus; + } /* do more stuff!! */ - EfiPrintf(L"We are A-OK!\r\n"); + EfiPrintf(L"We are A-OKer!\r\n"); EfiStall(10000000); +//Failure: + /* Check if we got here due to an internal error */ + if (BmpInternalBootError) + { + /* If XML is available, display the error */ +#if 0 + if (XmlLoaded) + { + BmDisplayDumpError(0, 0); + BmErrorPurge(); + } +#endif + + /* Don't do a fatal error -- return back to firmware */ + goto Quickie; + } + + /* Log a general fatal error once we're here */ + BmFatalErrorEx(BL_FATAL_ERROR_GENERIC, Status, 0, 0, 0); + Quickie: /* Check if we should reboot */ if ((RebootOnError) || diff --git a/reactos/boot/environ/app/bootmgr/bootmgr.h b/reactos/boot/environ/app/bootmgr/bootmgr.h index 76dea63a728..3d10382c2e0 100644 --- a/reactos/boot/environ/app/bootmgr/bootmgr.h +++ b/reactos/boot/environ/app/bootmgr/bootmgr.h @@ -49,7 +49,9 @@ typedef struct _BL_PACKED_BOOT_ERROR ULONG Size; } BL_PACKED_BOOT_ERROR, *PBL_PACKED_BOOT_ERROR; -#define BL_FATAL_ERROR_BCD_READ 0x01 +#define BL_FATAL_ERROR_BCD_READ 0x01 +#define BL_FATAL_ERROR_GENERIC 0x04 +#define BL_FATAL_ERROR_BCD_PARSE 0x07 /* FUNCTIONS *****************************************************************/ diff --git a/reactos/boot/environ/include/bcd.h b/reactos/boot/environ/include/bcd.h index f08e6ea05c3..ccd9be073f8 100644 --- a/reactos/boot/environ/include/bcd.h +++ b/reactos/boot/environ/include/bcd.h @@ -11,6 +11,21 @@ /* ENUMERATIONS **************************************************************/ +/* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa964229(v=vs.85).aspx */ + +#define BCD_CLASS_LIBRARY 0x01 +#define BCD_CLASS_APPLICATION 0x02 +#define BCD_CLASS_DEVICE 0x03 +#define BCD_CLASS_OEM 0x05 + +#define BCD_TYPE_DEVICE 0x01 +#define BCD_TYPE_STRING 0x02 +#define BCD_TYPE_OBJECT 0x03 +#define BCD_TYPE_OBJECT_LIST 0x04 +#define BCD_TYPE_INTEGER 0x05 +#define BCD_TYPE_BOOLEAN 0x06 +#define BCD_TYPE_INTEGER_LIST 0x07 + typedef enum BcdLibraryElementTypes { BcdLibraryDevice_ApplicationDevice = 0x11000001, @@ -155,9 +170,43 @@ typedef enum BcdBootMgrElementTypes BcdBootMgrBoolean_PersistBootSequence = 0x26000031 } BcdBootMgrElementTypes; - /* DATA STRUCTURES ***********************************************************/ +typedef struct +{ + union + { + ULONG PackedValue; + struct + { + ULONG SubType : 24; + ULONG Format : 4; + ULONG Class : 4; + }; + }; +} BcdElementType; + +typedef struct _BCD_ELEMENT_HEADER +{ + ULONG Version; + ULONG Type; + ULONG Size; +} BCD_ELEMENT_HEADER, *PBCD_ELEMENT_HEADER; + +typedef struct _BCD_PACKED_ELEMENT +{ + struct _BCD_PACKED_ELEMENT* NextEntry; + BcdElementType RootType; + BCD_ELEMENT_HEADER; + UCHAR Data[ANYSIZE_ARRAY]; +} BCD_PACKED_ELEMENT, *PBCD_PACKED_ELEMENT; + +typedef struct _BCD_ELEMENT +{ + PBCD_ELEMENT_HEADER Header; + PUCHAR Body; +} BCD_ELEMENT, *PBCD_ELEMENT; + typedef struct _BCD_DEVICE_OPTION { GUID AssociatedEntry; @@ -172,4 +221,35 @@ BcdOpenStoreFromFile ( _In_ PHANDLE StoreHandle ); +#define BCD_ENUMERATE_FLAG_DEEP 0x04 +#define BCD_ENUMERATE_FLAG_DEVICES 0x08 +#define BCD_ENUMERATE_FLAG_IN_ORDER 0x10 + +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 ElementCountNe + ); + +NTSTATUS +BcdOpenObject ( + _In_ HANDLE BcdHandle, + _In_ PGUID ObjectId, + _Out_ PHANDLE ObjectHandle + ); + +NTSTATUS +BcdEnumerateAndUnpackElements ( + _In_ HANDLE BcdHandle, + _In_ HANDLE ObjectHandle, + _Out_opt_ PBCD_ELEMENT Elements, + _Inout_ PULONG ElementSize, + _Out_ PULONG ElementCount + ); + #endif diff --git a/reactos/boot/environ/include/bl.h b/reactos/boot/environ/include/bl.h index aa4a608ede4..d5572cb0cc7 100644 --- a/reactos/boot/environ/include/bl.h +++ b/reactos/boot/environ/include/bl.h @@ -22,6 +22,9 @@ /* NDK Headers */ #include +/* NT SafeInt Header */ +#include + /* UEFI Headers */ #include #include @@ -52,7 +55,9 @@ #define BL_FIRMWARE_DESCRIPTOR_VERSION 2 #define BL_APPLICATION_ENTRY_FLAG_NO_GUID 0x01 +#define BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL 0x02 #define BL_APPLICATION_ENTRY_REBOOT_ON_ERROR 0x20 +#define BL_APPLICATION_ENTRY_BCD_OPTIONS_EXTERNAL 0x80 #define BL_CONTEXT_PAGING_ON 1 #define BL_CONTEXT_INTERRUPTS_ON 2 @@ -716,9 +721,9 @@ typedef struct _BL_LOCAL_DEVICE typedef struct _BL_DEVICE_DESCRIPTOR { - ULONG Size; - ULONG Flags; DEVICE_TYPE DeviceType; + ULONG Flags; + ULONG Size; ULONG Unknown; union { @@ -1424,7 +1429,18 @@ BlHtCreate ( _Out_ PULONG Id ); -/* BCD ROUTINES **************************************************************/ +/* BCD OPTION ROUTINES *******************************************************/ + +PBL_BCD_OPTION +MiscGetBootOption ( + _In_ PBL_BCD_OPTION List, + _In_ ULONG Type + ); + +ULONG +BlGetBootOptionListSize ( + _In_ PBL_BCD_OPTION BcdOption + ); ULONG BlGetBootOptionSize ( @@ -1452,6 +1468,15 @@ BlGetBootOptionBoolean ( _Out_ PBOOLEAN Value ); +NTSTATUS +BlpGetBootOptionIntegerList ( + _In_ PBL_BCD_OPTION List, + _In_ ULONG Type, + _Out_ PULONGLONG* Value, + _Out_ PULONGLONG Count, + _In_ BOOLEAN NoCopy + ); + NTSTATUS BlGetBootOptionDevice ( _In_ PBL_BCD_OPTION List, @@ -1460,6 +1485,68 @@ BlGetBootOptionDevice ( _In_opt_ PBL_BCD_OPTION* ExtraOptions ); +NTSTATUS +BlGetBootOptionGuidList ( + _In_ PBL_BCD_OPTION List, + _In_ ULONG Type, + _Out_ PGUID *Value, + _In_ PULONG Count + ); + +NTSTATUS +BlCopyBootOptions ( + _In_ PBL_BCD_OPTION OptionList, + _Out_ PBL_BCD_OPTION *CopiedOptions + ); + +NTSTATUS +BlAppendBootOptions ( + _In_ PBL_LOADED_APPLICATION_ENTRY AppEntry, + _In_ PBL_BCD_OPTION Options + ); + +/* BOOT REGISTRY ROUTINES ****************************************************/ + +VOID +BiCloseKey ( + _In_ HANDLE KeyHandle + ); + +NTSTATUS +BiOpenKey( + _In_ HANDLE ParentHandle, + _In_ PWCHAR KeyName, + _Out_ PHANDLE Handle + ); + +NTSTATUS +BiLoadHive ( + _In_ PBL_FILE_PATH_DESCRIPTOR FilePath, + _Out_ PHANDLE HiveHandle + ); + +NTSTATUS +BiGetRegistryValue ( + _In_ HANDLE KeyHandle, + _In_ PWCHAR ValueName, + _In_ PWCHAR KeyName, + _In_ ULONG Type, + _Out_ PVOID* Buffer, + _Out_ PULONG ValueLength + ); + +NTSTATUS +BiEnumerateSubKeys ( + _In_ HANDLE KeyHandle, + _Out_ PWCHAR** SubKeyList, + _Out_ PULONG SubKeyCount + ); + +VOID +BiDereferenceHive ( + _In_ HANDLE KeyHandle + ); + /* CONTEXT ROUTINES **********************************************************/ VOID @@ -1561,6 +1648,16 @@ MmFwGetMemoryMap ( _In_ ULONG Flags ); +NTSTATUS +BlpMmInitializeConstraints ( + VOID + ); + +NTSTATUS +BlMmRemoveBadMemory ( + VOID + ); + /* VIRTUAL MEMORY ROUTINES ***************************************************/ NTSTATUS diff --git a/reactos/boot/environ/lib/bootlib.c b/reactos/boot/environ/lib/bootlib.c index 43ef21361b8..5e9a6be86dd 100644 --- a/reactos/boot/environ/lib/bootlib.c +++ b/reactos/boot/environ/lib/bootlib.c @@ -86,11 +86,15 @@ InitializeLibrary ( BlpApplicationParameters = BootAppParameters; BlpLibraryParameters = *LibraryParameters; - /* Save the application entry flags */ - if (AppEntry->Flags & 2) + /* Check if the caller sent us their internal BCD options */ + if (AppEntry->Flags & BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL) { - AppEntry->Flags = (AppEntry->Flags & ~0x2) | 0x80; + /* These are external to us now, as far as we are concerned */ + AppEntry->Flags &= ~BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL; + AppEntry->Flags |= BL_APPLICATION_ENTRY_BCD_OPTIONS_EXTERNAL; } + + /* Save the application entry flags */ BlpApplicationEntry.Flags = AppEntry->Flags; /* Copy the GUID and point to the options */ @@ -363,15 +367,24 @@ BlInitializeLibrary( BlpLibraryParameters = *LibraryParameters; if (LibraryParameters->LibraryFlags & BL_LIBRARY_FLAG_REINITIALIZE_ALL) { -#if 0 - /* Initialize all the core modules again */ +#ifdef BL_TPM_SUPPORT + /* Reinitialize the TPM security enclave as BCD hash changed */ BlpSiInitialize(1); +#endif +#ifdef BL_KD_SUPPORT + /* Reinitialize the boot debugger as BCD debug options changed */ BlBdInitialize(); +#endif + + /* Reparse the bad page list now that the BCD has been reloaded */ BlMmRemoveBadMemory(); + + /* Reparse the low/high physical address limits as well */ BlpMmInitializeConstraints(); /* Redraw the graphics console as needed */ BlpDisplayInitialize(LibraryParameters->LibraryFlags); +#if 0 BlpResourceInitialize(); #endif } diff --git a/reactos/boot/environ/lib/io/display/display.c b/reactos/boot/environ/lib/io/display/display.c index 21e80fc169d..33620d4d1b9 100644 --- a/reactos/boot/environ/lib/io/display/display.c +++ b/reactos/boot/environ/lib/io/display/display.c @@ -43,7 +43,7 @@ DsppGraphicsDisabledByBcd ( VOID ) { - //EarlyPrint(L"Disabling graphics\r\n"); + EfiPrintf(L"Disabling graphics\r\n"); return FALSE; } @@ -53,10 +53,10 @@ DsppInitialize ( ) { BL_LIBRARY_PARAMETERS LibraryParameters = BlpLibraryParameters; - BOOLEAN NoGraphics;// , HighestMode; + BOOLEAN NoGraphics, HighestMode; NTSTATUS Status; PBL_DISPLAY_MODE DisplayMode; - //ULONG GraphicsResolution; + ULONGLONG GraphicsResolution; PBL_GRAPHICS_CONSOLE GraphicsConsole; PBL_TEXT_CONSOLE TextConsole, RemoteConsole; @@ -100,14 +100,9 @@ DsppInitialize ( DisplayMode = &ConsoleGraphicalResolutionList[0]; /* Check what resolution to use*/ -#if 0 Status = BlGetBootOptionInteger(BlpApplicationEntry.BcdData, BcdLibraryInteger_GraphicsResolution, &GraphicsResolution); -#else - //GraphicsResolution = 0; - Status = STATUS_NOT_FOUND; -#endif if (NT_SUCCESS(Status)) { ConsoleGraphicalResolutionListFlags |= BL_DISPLAY_GRAPHICS_FORCED_VIDEO_MODE_FLAG; @@ -116,14 +111,9 @@ DsppInitialize ( } /* Check if the highest mode should be forced */ -#if 0 Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData, BcdLibraryBoolean_GraphicsForceHighestMode, &HighestMode); -#else - //HighestMode = 0; - Status = STATUS_NOT_FOUND; -#endif if (NT_SUCCESS(Status)) { ConsoleGraphicalResolutionListFlags |= BL_DISPLAY_GRAPHICS_FORCED_HIGH_RES_MODE_FLAG; @@ -233,6 +223,7 @@ BlpDisplayInitialize ( { /* This is a reset */ Status = STATUS_NOT_IMPLEMENTED; + EfiPrintf(L"Display reset not yet implemented\r\n"); #if 0 Status = DsppReinitialize(Flags); if (NT_SUCCESS(Status)) diff --git a/reactos/boot/environ/lib/misc/bcd.c b/reactos/boot/environ/lib/misc/bcd.c index cb88bdb3ce2..31c76c5bfdf 100644 --- a/reactos/boot/environ/lib/misc/bcd.c +++ b/reactos/boot/environ/lib/misc/bcd.c @@ -13,1074 +13,1067 @@ /* FUNCTIONS *****************************************************************/ -PBL_BCD_OPTION -MiscGetBootOption ( - _In_ PBL_BCD_OPTION List, - _In_ ULONG Type +VOID +BiNotifyEnumerationError ( + _In_ HANDLE ObjectHandle, + _In_ PWCHAR ElementName, + _In_ NTSTATUS Status ) { - ULONG_PTR NextOption = 0, ListOption; - PBL_BCD_OPTION Option, FoundOption; + /* Stub for now */ + UNREFERENCED_PARAMETER(ObjectHandle); + UNREFERENCED_PARAMETER(ElementName); + UNREFERENCED_PARAMETER(Status); + EfiPrintf(L"Error in BiNotify\r\n"); +} - /* No options, bail out */ - if (!List) +ULONG +BiConvertElementFormatToValueType ( + _In_ ULONG Format + ) +{ + /* Strings and objects are strings */ + if ((Format == BCD_TYPE_STRING) || (Format == BCD_TYPE_OBJECT)) { - return NULL; + return REG_SZ; } - /* Loop while we find an option */ - FoundOption = NULL; - do + /* Object lists are arrays of strings */ + if (Format == BCD_TYPE_OBJECT_LIST) { - /* 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)) + return REG_MULTI_SZ; + } + + /* Everything else is binary */ + return REG_BINARY; +} + +NTSTATUS +BiConvertRegistryDataToElement ( + _In_ HANDLE ObjectHandle, + _In_ PVOID Data, + _In_ ULONG DataLength, + _In_ BcdElementType ElementType, + _Out_ PVOID Element, + _Out_ PULONG ElementSize + ) +{ + NTSTATUS Status; + ULONG Length, Size, ReturnedLength; + PBL_DEVICE_DESCRIPTOR Device; + BOOLEAN NullTerminate; + PBCD_DEVICE_OPTION BcdDevice, ElementDevice; + PWCHAR BcdString, ElementString; + PGUID ElementGuid; UNICODE_STRING GuidString; + PULONGLONG ElementInteger; + PUSHORT ElementWord; PBOOLEAN BcdBoolean; + + /* Assume failure */ + ReturnedLength = 0; + + /* Check what type of format we are dealing with */ + switch (ElementType.Format) + { + /* Devices -- they are in a binary format */ + case BCD_TYPE_DEVICE: + + /* First, make sure it's at least big enough for an empty descriptor */ + if (DataLength < FIELD_OFFSET(BCD_DEVICE_OPTION, + DeviceDescriptor.Unknown)) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Both the registry and BCD format are the same */ + BcdDevice = (PBCD_DEVICE_OPTION)Data; + ElementDevice = (PBCD_DEVICE_OPTION)Element; + + /* Make sure the device fits in the registry data */ + Device = &BcdDevice->DeviceDescriptor; + Size = Device->Size; + if ((Size + sizeof(BcdDevice->AssociatedEntry)) != DataLength) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Check if this is a locate device */ + if (Device->DeviceType == LocateDevice) + { + EfiPrintf(L"Locates not yet supported\r\n"); + return STATUS_NOT_SUPPORTED; + } + + /* Make sure the caller's buffer can fit the device */ + ReturnedLength = Size + sizeof(BcdDevice->AssociatedEntry); + if (ReturnedLength > *ElementSize) + { + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + + /* It'll fit -- copy it in */ + RtlCopyMemory(&ElementDevice->DeviceDescriptor, Device, Size); + ElementDevice->AssociatedEntry = BcdDevice->AssociatedEntry; + Status = STATUS_SUCCESS; + break; + + /* Strings -- they are stored as is */ + case BCD_TYPE_STRING: + + /* Make sure the string isn't empty or misaligned */ + if (!(DataLength) || (DataLength & 1)) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Both the registry and BCD format are the same */ + BcdString = (PWCHAR)Data; + ElementString = (PWCHAR)Element; + + /* We'll need as much data as the string has to offer */ + ReturnedLength = DataLength; + + /* If the string isn't NULL-terminated, do it now */ + NullTerminate = FALSE; + if (BcdString[(DataLength / sizeof(WCHAR)) - 1] != UNICODE_NULL) + { + ReturnedLength += sizeof(UNICODE_NULL); + NullTerminate = TRUE; + } + + /* Will we fit in the caller's buffer? */ + if (ReturnedLength > *ElementSize) + { + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + + /* Yep -- copy it in, and NULL-terminate if needed */ + RtlCopyMemory(Element, Data, DataLength); + if (NullTerminate) + { + ElementString[DataLength / sizeof(WCHAR)] = UNICODE_NULL; + } + + Status = STATUS_SUCCESS; + break; + + /* Objects -- they are stored as GUID Strings */ + case BCD_TYPE_OBJECT: + + /* Registry data is a string, BCD data is a GUID */ + BcdString = (PWCHAR)Data; + ElementGuid = (PGUID)Element; + + /* We need a GUID-sized buffer, does the caller have one? */ + ReturnedLength = sizeof(*ElementGuid); + if (*ElementSize < ReturnedLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + + /* Yep, copy the GUID */ + RtlInitUnicodeString(&GuidString, BcdString); + Status = RtlGUIDFromString(&GuidString, ElementGuid); + break; + + /* Object Lists -- they are stored as arrays of GUID strings */ + case BCD_TYPE_OBJECT_LIST: + + /* Assume an empty list*/ + ReturnedLength = 0; + Length = 0; + Status = STATUS_SUCCESS; + + /* Registry data is an array of strings, BCD data is array of GUIDs */ + BcdString = (PWCHAR)Data; + ElementGuid = (PGUID)Element; + + /* Loop as long as the array still has strings */ + while (*BcdString) + { + /* Don't read beyond the registry data */ + if (Length >= DataLength) + { + break; + } + + /* One more GUID -- does the caller have space? */ + ReturnedLength += sizeof(GUID); + if (ReturnedLength <= *ElementSize) + { + /* Convert and add it in */ + RtlInitUnicodeString(&GuidString, BcdString); + Status = RtlGUIDFromString(&GuidString, ElementGuid); + if (!NT_SUCCESS(Status)) + { + break; + } + + /* Move to the next GUID in the caller's buffer */ + ElementGuid++; + } + + /* Move to the next string in the registry array */ + Size = (wcslen(BcdString) * sizeof(WCHAR)) + sizeof(UNICODE_NULL); + Length += Size; + BcdString = (PWCHAR)((ULONG_PTR)BcdString + Length); + } + + /* Check if we failed anywhere */ + if (!NT_SUCCESS(Status)) + { + break; + } + + /* Check if we consumed more space than we have */ + if (ReturnedLength > *ElementSize) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + /* All good here */ + break; + + /* Integer -- stored as binary */ + case BCD_TYPE_INTEGER: + + /* BCD data is a ULONGLONG, registry data is 8 bytes binary */ + ElementInteger = (PULONGLONG)Element; + ReturnedLength = sizeof(*ElementInteger); + + /* Make sure the registry data makes sense */ + if (DataLength > ReturnedLength) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Make sure the caller has space */ + if (*ElementSize < ReturnedLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + + /* Write the integer result */ + *ElementInteger = 0; + RtlCopyMemory(ElementInteger, Data, DataLength); + Status = STATUS_SUCCESS; + break; + + /* Boolean -- stored as binary */ + case BCD_TYPE_BOOLEAN: + + /* BCD data is a BOOLEAN, registry data is 2 bytes binary */ + ElementWord = (PUSHORT)Element; + BcdBoolean = (PBOOLEAN)Data; + ReturnedLength = sizeof(ElementWord); + + /* Make sure the registry data makes sense */ + if (DataLength != sizeof(*BcdBoolean)) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Make sure the caller has space */ + if (*ElementSize < ReturnedLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + + /* Write the boolean result */ + *ElementWord = 0; + *ElementWord = *BcdBoolean != 0; + Status = STATUS_SUCCESS; + break; + + /* Integer list --stored as binary */ + case BCD_TYPE_INTEGER_LIST: + + /* BCD Data is n ULONGLONGs, registry data is n*8 bytes binary */ + ReturnedLength = DataLength; + if (!(DataLength) || (DataLength & 7)) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Make sure the caller has space */ + if (*ElementSize < ReturnedLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + + /* Write the integer list result */ + RtlCopyMemory(Element, Data, DataLength); + Status = STATUS_SUCCESS; + break; + + /* Arbitrary data */ + default: + + /* Registry data is copied binary as-is */ + ReturnedLength = DataLength; + + /* Make sure it's not empty */ + if (!DataLength) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Make sure the caller has space */ + if (*ElementSize < ReturnedLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + break; + } + + /* Write the result */ + RtlCopyMemory(Element, Data, DataLength); + Status = STATUS_SUCCESS; + break; + } + + /* If we got here due to success or space issues, write the size */ + if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) + { + *ElementSize = ReturnedLength; + } + + /* All done, return our conversion result */ + return Status; +} + +NTSTATUS +BiConvertBcdElements ( + _In_ PBCD_PACKED_ELEMENT Elements, + _Out_opt_ PBCD_ELEMENT Buffer, + _Inout_ PULONG BufferSize, + _Inout_ PULONG ElementCount + ) +{ + NTSTATUS Status; + ULONG ElementSize, AlignedElementSize, AlignedDataSize; + PBCD_ELEMENT_HEADER Header; + PVOID Data; + BOOLEAN Exists; + ULONG i, j, Count; + + /* Local variable to keep track of objects */ + Count = 0; + + /* Safely compute the element bytes needed */ + Status = RtlULongMult(*ElementCount, sizeof(BCD_ELEMENT), &ElementSize); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Safely align the element size */ + Status = RtlULongAdd(ElementSize, + sizeof(ULONG) - 1, + &AlignedElementSize); + if (!NT_SUCCESS(Status)) + { + return Status; + } + AlignedElementSize = ALIGN_DOWN(AlignedElementSize, ULONG); + + /* Do a safe version of Add2Ptr to figure out where the headers will start */ + Status = RtlULongPtrAdd((ULONG_PTR)Buffer, + AlignedElementSize, + (PULONG_PTR)&Header); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Safely compute the header bytes needed */ + Status = RtlULongMult(*ElementCount, + sizeof(BCD_ELEMENT_HEADER), + &ElementSize); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Safely align the header size */ + Status = RtlULongAdd(ElementSize, + AlignedElementSize + sizeof(ULONG) - 1, + &AlignedElementSize); + if (!NT_SUCCESS(Status)) + { + return Status; + } + AlignedElementSize = ALIGN_DOWN(AlignedElementSize, ULONG); + + /* Do a safe version of Add2Ptr */ + Status = RtlULongPtrAdd((ULONG_PTR)Buffer, + AlignedElementSize, + (PULONG_PTR)&Data); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + /* Iterate over every element */ + for (i = 0; i < *ElementCount; i++) + { + /* Safely align the element size */ + Status = RtlULongAdd(Elements->Size, + sizeof(ULONG) - 1, + &AlignedDataSize); + if (!NT_SUCCESS(Status)) + { + break; + } + AlignedDataSize = ALIGN_DOWN(AlignedDataSize, ULONG); + + /* Safely add the size of this data element */ + Status = RtlULongAdd(AlignedElementSize, + AlignedDataSize, + &AlignedElementSize); + if (!NT_SUCCESS(Status)) { - 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) + /* Do we have enough space left? */ + if (*BufferSize >= AlignedElementSize) { - /* Try to get a match in the associated option */ - Option = MiscGetBootOption((PBL_BCD_OPTION)((ULONG_PTR)Option + - ListOption), - Type); - if (Option) + /* Check if our root is an inherited object */ + Exists = FALSE; + if (Elements->RootType.PackedValue == BcdLibraryObjectList_InheritedObjects) { - /* Return it */ - FoundOption = Option; - break; + /* 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; + } + } } - } - } while (NextOption); - /* Return the option that was found, if any */ - return FoundOption; -} + /* Have we already found ourselves? */ + if (!Exists) + { + /* Nope, one more entry */ + ++Count; -/*++ - * @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; + /* Write out the unpacked object */ + Buffer->Body = Data; + Buffer->Header = Header; - /* Loop all the options*/ - do - { - /* Move to the next one */ - NextOption = (PBL_BCD_OPTION)((ULONG_PTR)BcdOption + NextOffset); + /* Fill out its header */ + Header->Size = Elements->Size; + Header->Type = Elements->Type; + Header->Version = Elements->Version; - /* Compute the size of the next one */ - Size += BlGetBootOptionSize(NextOption); + /* And copy the data */ + RtlCopyMemory(Data, Elements->Data, Header->Size); - /* Update the offset */ - NextOffset = NextOption->NextEntryOffset; - } while (NextOffset); + /* Move to the next unpacked object and header */ + ++Buffer; + ++Header; - /* 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, 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; + /* Move to the next data entry */ + Data = (PVOID)((ULONG_PTR)Data + AlignedDataSize); + } } else { - /* We're good, do the multiplication */ - Status = STATUS_SUCCESS; - CopyLength *= sizeof(WCHAR); + /* Nope, set failure code, but keep going so we can return count */ + Status = STATUS_BUFFER_TOO_SMALL; } - /* Allocate a copy for the string */ - if (NT_SUCCESS(Status)) + /* Move to the next element entry */ + Elements = Elements->NextEntry; + } + + /* 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)) + { + goto Quickie; + } + + /* Capture the object ID and convert it into a string */ + LocalGuid = *ObjectId; + Status = RtlStringFromGUID(&LocalGuid, &GuidString); + if (!NT_SUCCESS(Status)) + { + goto Quickie; + } + + /* Now open the key containing this object ID */ + Status = BiOpenKey(RootObjectHandle, GuidString.Buffer, ObjectHandle); + +Quickie: + /* Free the GUID string if we had one allocated */ + if (GuidString.Buffer) + { + RtlFreeUnicodeString(&GuidString); + } + + /* Close the root handle if it was open */ + if (RootObjectHandle) + { + BiCloseKey(RootObjectHandle); + } + + /* Return the final status */ + return Status; +} + +NTSTATUS +BiEnumerateSubElements ( + _In_ HANDLE BcdHandle, + _In_ PVOID Object, + _In_ ULONG ElementType, + _In_ ULONG Flags, + _Out_opt_ PBCD_PACKED_ELEMENT* Elements, + _Inout_ PULONG ElementSize, + _Out_ PULONG ElementCount + ) +{ + NTSTATUS Status; + PBCD_PACKED_ELEMENT Element; + HANDLE ObjectHandle; + ULONG ParsedElements, RequiredSize; + + /* Assume empty */ + *ElementCount = 0; + RequiredSize = 0; + ParsedElements = 0; + + /* Open the object */ + Status = BcdOpenObject(BcdHandle, Object, &ObjectHandle); + if (!NT_SUCCESS(Status)) + { + goto Quickie; + } + + /* Read the first entry, and the size available */ + Element = *Elements; + RequiredSize = *ElementSize; + + /* Enumerate the object into the element array */ + Status = BiEnumerateElements(BcdHandle, + ObjectHandle, + ElementType, + Flags, + Element, + &RequiredSize, + &ParsedElements); + + /* Close the handle and bail out if we couldn't enumerate */ + BiCloseKey(ObjectHandle); + if (!NT_SUCCESS(Status)) + { + goto Quickie; + } + + /* Check if the and subelements were present */ + if (ParsedElements) + { + /* Keep going until the last one */ + while (Element->NextEntry) { - StringCopy = BlMmAllocateHeap(CopyLength); - if (StringCopy) + Element = Element->NextEntry; + } + + /* Set the new buffer location to the last element */ + *Elements = Element; + } + +Quickie: + /* Return the number of sub-elements and their size */ + *ElementCount = ParsedElements; + *ElementSize = RequiredSize; + return Status; +} + +NTSTATUS +BiEnumerateSubObjectElements ( + _In_ HANDLE BcdHandle, + _Out_ PGUID SubObjectList, + _In_ ULONG SubObjectCount, + _In_ ULONG Flags, + _Out_opt_ PBCD_PACKED_ELEMENT Elements, + _Inout_ PULONG ElementSize, + _Out_ PULONG ElementCount + ) +{ + NTSTATUS Status; + ULONG SubElementCount, TotalSize, RequiredSize, CurrentSize, i; + PBCD_PACKED_ELEMENT PreviousElement; + + /* Assume empty list */ + *ElementCount = 0; + Status = STATUS_SUCCESS; + + /* Initialize variables */ + TotalSize = 0; + PreviousElement = NULL; + + /* Set the currently remaining size based on caller's input */ + CurrentSize = *ElementSize; + + /* Iterate over every subje object */ + for (i = 0; i < SubObjectCount; i++) + { + /* Set the currently remaining buffer space */ + RequiredSize = CurrentSize; + + /* Enumerate the inherited sub elements */ + Status = BiEnumerateSubElements(BcdHandle, + &SubObjectList[i], + BcdLibraryObjectList_InheritedObjects, + Flags, + &Elements, + &RequiredSize, + &SubElementCount); + if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) + { + /* Safely add the length of the sub elements */ + Status = RtlULongAdd(TotalSize, RequiredSize, &TotalSize); + if (!NT_SUCCESS(Status)) { - /* NULL-terminate it */ - RtlCopyMemory(StringCopy, - String, - CopyLength - sizeof(UNICODE_NULL)); - StringCopy[CopyLength] = UNICODE_NULL; - *Value = StringCopy; - Status = STATUS_SUCCESS; + break; + } + + /* Add the sub elements to the total */ + *ElementCount += SubElementCount; + + /* See if we have enough space*/ + if (*ElementSize >= TotalSize) + { + /* Were there any subelements? */ + if (SubElementCount) + { + /* Update to keep track of these new subelements */ + CurrentSize = *ElementSize - TotalSize; + + /* Link the subelements into the chain */ + PreviousElement = Elements; + PreviousElement->NextEntry = + (PBCD_PACKED_ELEMENT)((ULONG_PTR)Elements + TotalSize); + Elements = PreviousElement->NextEntry; + } } else { - /* No memory, fail */ - Status = STATUS_NO_MEMORY; + /* We're out of space */ + CurrentSize = 0; } } - } - - /* 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) + else if ((Status != STATUS_NOT_FOUND) && + (Status != STATUS_OBJECT_NAME_NOT_FOUND)) { - 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 -#define BI_HIVE_WRITEABLE 0x02 - -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; - -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; + /* Some other fatal error, break out */ + break; } 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)HvGetCell(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; + /* 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; } } - /* Allocate a key object */ - NewKey = BlMmAllocateHeap(sizeof(*NewKey)); - if (!NewKey) + /* Terminate the last element, if one was left */ + if (PreviousElement) { - /* Bail out if we had no memory for it */ - Status = STATUS_NO_MEMORY; - goto Quickie; + PreviousElement->NextEntry = NULL; } - /* 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) + /* Set failure code if we ran out of space */ + if (*ElementSize < TotalSize) { - BlMmFreeHeap(NameBuffer); + Status = STATUS_BUFFER_TOO_SMALL; } - /* Return status of the open operation */ + /* Return final length and status */ + *ElementSize = TotalSize; return Status; } -BOOLEAN BiHiveHashLibraryInitialized; -ULONGLONG HvSymcryptSeed; - -BOOLEAN -HvIsInPlaceBaseBlockValid ( - _In_ PHBASE_BLOCK BaseBlock +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 ) { - ULONG HiveLength, HeaderSum; - BOOLEAN Valid; + HANDLE ElementsHandle, ElementHandle; + ULONG TotalLength, RegistryElementDataLength, RemainingLength; + NTSTATUS Status; + ULONG i; + PVOID ElementData, SubObjectList, RegistryElementData; + BcdElementType ElementType; + PBCD_PACKED_ELEMENT PreviousElement; + ULONG SubElementCount, SubKeyCount, SubObjectCount, ElementDataLength; + PWCHAR ElementName; + PWCHAR* SubKeys; /* Assume failure */ - Valid = FALSE; + *ElementCount = 0; - /* 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)) + /* 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; + + /* Open the root object key's elements */ + Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle); + if (!NT_SUCCESS(Status)) { - /* Check for invalid hive size */ - HiveLength = BaseBlock->Length; - if (HiveLength) + goto Quickie; + } + + /* Enumerate all elements */ + Status = BiEnumerateSubKeys(ElementsHandle, &SubKeys, &SubKeyCount); + if (!NT_SUCCESS(Status)) + { + goto Quickie; + } + + /* Iterate over each one */ + for (i = 0; i < SubKeyCount; i++) + { + /* Open the element */ + ElementName = SubKeys[i]; + Status = BiOpenKey(ElementsHandle, ElementName, &ElementHandle); + if (!NT_SUCCESS(Status)) { - /* Check for misaligned or too large hive size */ - if (!(HiveLength & 0xFFF) && HiveLength <= 0x7FFFE000) + break; + } + + /* The name of the element is its data type */ + ElementType.PackedValue = wcstoul(SubKeys[i], NULL, 16); + if (!(ElementType.PackedValue) || (ElementType.PackedValue == -1)) + { + EfiPrintf(L"Value invald\r\n"); + BiCloseKey(ElementHandle); + ElementHandle = 0; + continue; + } + + /* Read the appropriate registry value type for this element */ + Status = BiGetRegistryValue(ElementHandle, + L"Element", + NULL, + BiConvertElementFormatToValueType( + ElementType.Format), + &RegistryElementData, + &RegistryElementDataLength); + if (!NT_SUCCESS(Status)) + { + break; + } + + /* Now figure out how much space the converted element will need */ + ElementDataLength = 0; + Status = BiConvertRegistryDataToElement(ObjectHandle, + RegistryElementData, + RegistryElementDataLength, + ElementType, + NULL, + &ElementDataLength); + if (Status != STATUS_BUFFER_TOO_SMALL) + { + break; + } + + /* Allocate a buffer big enough for the converted element */ + ElementData = BlMmAllocateHeap(ElementDataLength); + if (!ElementData) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + /* And actually convert it this time around */ + Status = BiConvertRegistryDataToElement(ObjectHandle, + RegistryElementData, + RegistryElementDataLength, + ElementType, + ElementData, + &ElementDataLength); + if (!NT_SUCCESS(Status)) + { + break; + } + + /* Safely add space for the packed element header */ + Status = RtlULongAdd(TotalLength, + FIELD_OFFSET(BCD_PACKED_ELEMENT, Data), + &TotalLength); + if (!NT_SUCCESS(Status)) + { + break; + } + + /* Safely add space for the data of the element itself */ + Status = RtlULongAdd(TotalLength, ElementDataLength, &TotalLength); + if (!NT_SUCCESS(Status)) + { + break; + } + + /* One more element */ + ++*ElementCount; + + /* See how much space we were given */ + RemainingLength = *ElementSize; + if (RemainingLength >= TotalLength) + { + /* Set the next pointer */ + Elements->NextEntry = (PBCD_PACKED_ELEMENT)((ULONG_PTR)Elements + 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)) { - /* Check for invalid header checksum */ - HeaderSum = HvpHiveHeaderChecksum(BaseBlock); - if (HeaderSum == BaseBlock->CheckSum) + /* Safely add the length of the sub elements */ + Status = RtlULongAdd(TotalLength, + ElementDataLength, + &TotalLength); + if (!NT_SUCCESS(Status)) { - /* All good */ - Valid = TRUE; + 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)Elements + + TotalLength); + Elements = PreviousElement->NextEntry; + } + } + else + { + /* We're out of space */ + ElementDataLength = 0; } } - } - } - - /* Return validity */ - return Valid; -} - -NTSTATUS -BiInitializeAndValidateHive ( - _In_ PBI_KEY_HIVE Hive - ) -{ - ULONG HiveSize; - NTSTATUS Status; - - /* Make sure the hive is at least the size of a base block */ - if (Hive->HiveSize < sizeof(HBASE_BLOCK)) - { - return STATUS_REGISTRY_CORRUPT; - } - - /* 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)) - { - return STATUS_REGISTRY_CORRUPT; - } - - /* 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)) - { - /* Cleanup volatile/old data */ - CmPrepareHive(&Hive->Hive.Hive); // CmCheckRegistry - Status = STATUS_SUCCESS; - } - - /* Return the final status */ - return Status; -} - -NTSTATUS -BiLoadHive ( - _In_ PBL_FILE_PATH_DESCRIPTOR FilePath, - _Out_ PHANDLE HiveHandle - ) -{ - 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; - - /* Initialize variables */ - DeviceId = -1; - BaseBlock = NULL; - BcdHive = NULL; - KeyObject = NULL; - LogData = NULL; - LogName = NULL; - - /* Initialize the crypto seed */ - if (!BiHiveHashLibraryInitialized) - { - HvSymcryptSeed = 0x82EF4D887A4E55C5; - BiHiveHashLibraryInitialized = TRUE; - } - - /* 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; - } - - /* 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)) - { - /* Try for RO access instead */ - HaveWriteAccess = FALSE; - Status = BlpDeviceOpen(BcdDevice, BL_DEVICE_READ_ACCESS, 0, &DeviceId); - if (!NT_SUCCESS(Status)) - { - /* No access at all -- bail out */ - goto Quickie; - } - } - - /* Now try to load the hive on disk */ - Status = BlImgLoadImageWithProgress2(DeviceId, - BlLoaderRegistry, - HiveName, - (PVOID*)&BaseBlock, - &HiveSize, - 0, - FALSE, - NULL, - NULL); - if (!NT_SUCCESS(Status)) - { - EfiPrintf(L"Hive read failure: % lx\r\n", Status); - goto Quickie; - } - - /* Allocate a hive structure */ - BcdHive = BlMmAllocateHeap(sizeof(*BcdHive)); - if (!BcdHive) - { - Status = STATUS_NO_MEMORY; - goto Quickie; - } - - /* Initialize it */ - RtlZeroMemory(BcdHive, sizeof(*BcdHive)); - BcdHive->BaseBlock = BaseBlock; - BcdHive->HiveSize = HiveSize; - if (HaveWriteAccess) - { - BcdHive->Flags |= BI_HIVE_WRITEABLE; - } - - /* Make sure the hive was at least one bin long */ - if (HiveSize < sizeof(*BaseBlock)) - { - Status = STATUS_REGISTRY_CORRUPT; - goto Quickie; - } - - /* Make sure the hive contents are at least one bin long */ - HiveLength = BaseBlock->Length; - if (BaseBlock->Length < sizeof(*BaseBlock)) - { - Status = STATUS_REGISTRY_CORRUPT; - goto Quickie; - } - - /* Validate the initial bin (the base block) */ - if (!HvIsInPlaceBaseBlockValid(BaseBlock)) - { - 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) - { - EfiPrintf(L"Log fix not implemented: %lx %lx\r\n"); - Status = STATUS_REGISTRY_CORRUPT; - goto Quickie; - } - - /* - * 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); - if (!NT_SUCCESS(Status)) - { - goto Quickie; - } - - /* Copy the current data in there */ - RtlCopyMemory(NewBaseBlock, BaseBlock, HiveSize); - - /* Free the old data */ - EfiPrintf(L"Leaking old hive buffer\r\n"); - //MmPapFreePages(BaseBlock, 1); - - /* Update our pointers */ - BaseBlock = NewBaseBlock; - HiveSize = NewHiveSize; - BcdHive->BaseBlock = BaseBlock; - BcdHive->HiveSize = HiveSize; - } - - /* Check if any log stuff needs to happen */ - if (LogData) - { - EfiPrintf(L"Log fix not implemented: %lx %lx\r\n"); - Status = STATUS_REGISTRY_CORRUPT; - goto Quickie; - } - - /* Call Hv to setup the hive library */ - Status = BiInitializeAndValidateHive(BcdHive); - if (!NT_SUCCESS(Status)) - { - goto Quickie; - } - - /* Now get the root node */ - Hive = &BcdHive->Hive.Hive; - RootNode = (PCM_KEY_NODE)HvGetCell(Hive, Hive->BaseBlock->RootCell); - if (!RootNode) - { - Status = STATUS_OBJECT_NAME_NOT_FOUND; - goto Quickie; - } - - /* 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) - { - EfiPrintf(L"No OBJECTS subkey found!\r\n"); - Status = STATUS_OBJECT_NAME_NOT_FOUND; - goto Quickie; - } - - /* 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) - { - Status = STATUS_NO_MEMORY; - goto Quickie; - } - - /* 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) - { - Status = STATUS_NO_MEMORY; - goto Quickie; - } - - /* 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) - { - BlMmFreeHeap(LogName); - } - - /* If we had logging data, free it */ - if (LogData) - { - EfiPrintf(L"Leaking log buffer\r\n"); - //MmPapFreePages(LogData, 1); - } - - /* Check if this is the failure path */ - if (!NT_SUCCESS(Status)) - { - /* 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) + else if ((Status != STATUS_NOT_FOUND) && + (Status != STATUS_OBJECT_NAME_NOT_FOUND)) { - BlMmFreeHeap(BcdHive->FilePath); + /* 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; } - /* Free the hive itself */ - BlMmFreeHeap(BcdHive); + /* Copy the elements into the list. They are arrays of GUIDs */ + RtlCopyMemory(SubObjectList, ElementData, ElementDataLength); + SubObjectCount = ElementDataLength / sizeof(GUID); } - /* Finally, free the root key object if we created one */ - if (KeyObject) + /* Free our local buffers */ + BlMmFreeHeap(ElementData); + BlMmFreeHeap(RegistryElementData); + ElementData = NULL; + RegistryElementData = NULL; + + /* Close the key */ + BiCloseKey(ElementHandle); + ElementHandle = NULL; + ElementName = NULL; + } + + /* Did we end up here with a sub object list after successful loop parsing? */ + if ((i != 0) && (i == SubKeyCount) && (SubObjectList)) + { + /* We will actually enumerate it now, at the end */ + Status = BiEnumerateSubObjectElements(BcdHandle, + SubObjectList, + SubObjectCount, + Flags, + Elements, + &RemainingLength, + &SubElementCount); + if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) { - BlMmFreeHeap(KeyObject); + /* Safely add the length of the sub elements */ + Status = RtlULongAdd(TotalLength, RemainingLength, &TotalLength); + if ((NT_SUCCESS(Status)) && (SubElementCount)) + { + /* Add the sub elements to the total */ + *ElementCount += SubElementCount; + + /* Don't touch PreviousElement anymore */ + PreviousElement = NULL; + } } } - /* Return the final status */ +Quickie: + /* Free the sub object list, if any */ + if (SubObjectList) + { + BlMmFreeHeap(SubObjectList); + } + + /* Free any local element data */ + if (ElementData) + { + BlMmFreeHeap(ElementData); + } + + /* Free any local registry data */ + if (RegistryElementData) + { + BlMmFreeHeap(RegistryElementData); + } + + /* Close the handle if still opened */ + if (ElementHandle) + { + BiCloseKey(ElementHandle); + } + + /* Terminate the last element, if any */ + if (PreviousElement) + { + PreviousElement->NextEntry = NULL; + } + + /* Close the root handle if still opened */ + if (ElementsHandle) + { + BiCloseKey(ElementsHandle); + } + + /* Set failure code if out of space */ + if (*ElementSize < TotalLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + + /* Other errors will send a notification error */ + if (!(NT_SUCCESS(Status)) && (Status != STATUS_BUFFER_TOO_SMALL)) + { + BiNotifyEnumerationError(ObjectHandle, ElementName, Status); + } + + /* Finally free the subkeys array */ + if (SubKeys) + { + BlMmFreeHeap(SubKeys); + } + + /* And return the required, final length and status */ + *ElementSize = TotalLength; return Status; } @@ -1119,10 +1112,91 @@ BiAddStoreFromFile ( 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 +1229,7 @@ BcdOpenStoreFromFile ( if (NT_SUCCESS(Status)) { /* Return the handle on success */ - *StoreHandle = LocalHandle; + *BcdHandle = LocalHandle; } /* Free the descriptor and return the status */ diff --git a/reactos/boot/environ/lib/misc/bcdopt.c b/reactos/boot/environ/lib/misc/bcdopt.c new file mode 100644 index 00000000000..5a8ce226735 --- /dev/null +++ b/reactos/boot/environ/lib/misc/bcdopt.c @@ -0,0 +1,647 @@ +/* + * 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 +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 +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(&NewOptions[OptionsSize], 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 += OptionsSize; + 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; +} + diff --git a/reactos/boot/environ/lib/misc/bootreg.c b/reactos/boot/environ/lib/misc/bootreg.c new file mode 100644 index 00000000000..278ca1c4f4d --- /dev/null +++ b/reactos/boot/environ/lib/misc/bootreg.c @@ -0,0 +1,889 @@ +/* + * COPYRIGHT: See COPYING.ARM in the top level directory + * PROJECT: ReactOS UEFI Boot Library + * FILE: boot/environ/lib/misc/bootreg.c + * PURPOSE: Boot Library Boot Registry Wrapper for CMLIB + * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org) + */ + +/* INCLUDES ******************************************************************/ + +#include "bl.h" +#include + +/* DEFINITIONS ***************************************************************/ + +#define BI_FLUSH_HIVE 0x01 +#define BI_HIVE_WRITEABLE 0x02 + +/* DATA STRUCTURES ***********************************************************/ + +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; + +typedef struct _BI_KEY_OBJECT +{ + PBI_KEY_HIVE KeyHive; + PCM_KEY_NODE KeyNode; + HCELL_INDEX KeyCell; + PWCHAR KeyName; +} BI_KEY_OBJECT, *PBI_KEY_OBJECT; + +/* GLOBALS *******************************************************************/ + +BOOLEAN BiHiveHashLibraryInitialized; +ULONGLONG HvSymcryptSeed; + +/* FUNCTIONS *****************************************************************/ + +BOOLEAN +HvIsInPlaceBaseBlockValid ( + _In_ PHBASE_BLOCK BaseBlock + ) +{ + ULONG HiveLength, HeaderSum; + BOOLEAN Valid; + + /* 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)) + { + /* Check for invalid hive size */ + HiveLength = BaseBlock->Length; + if (HiveLength) + { + /* Check for misaligned or too large hive size */ + if (!(HiveLength & 0xFFF) && HiveLength <= 0x7FFFE000) + { + /* Check for invalid header checksum */ + HeaderSum = HvpHiveHeaderChecksum(BaseBlock); + if (HeaderSum == BaseBlock->CheckSum) + { + /* All good */ + Valid = TRUE; + } + } + } + } + + /* Return validity */ + return Valid; +} + +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); +} + +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)HvGetCell(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 +BiInitializeAndValidateHive ( + _In_ PBI_KEY_HIVE Hive + ) +{ + ULONG HiveSize; + NTSTATUS Status; + + /* Make sure the hive is at least the size of a base block */ + if (Hive->HiveSize < sizeof(HBASE_BLOCK)) + { + return STATUS_REGISTRY_CORRUPT; + } + + /* 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)) + { + return STATUS_REGISTRY_CORRUPT; + } + + /* 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)) + { + /* Cleanup volatile/old data */ + CmPrepareHive(&Hive->Hive.Hive); // CmCheckRegistry + Status = STATUS_SUCCESS; + } + + /* Return the final status */ + return Status; +} + +NTSTATUS +BiLoadHive ( + _In_ PBL_FILE_PATH_DESCRIPTOR FilePath, + _Out_ PHANDLE HiveHandle + ) +{ + 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; + + /* Initialize variables */ + DeviceId = -1; + BaseBlock = NULL; + BcdHive = NULL; + KeyObject = NULL; + LogData = NULL; + LogName = NULL; + + /* Initialize the crypto seed */ + if (!BiHiveHashLibraryInitialized) + { + HvSymcryptSeed = 0x82EF4D887A4E55C5; + BiHiveHashLibraryInitialized = TRUE; + } + + /* 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; + } + + /* 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)) + { + /* Try for RO access instead */ + HaveWriteAccess = FALSE; + Status = BlpDeviceOpen(BcdDevice, BL_DEVICE_READ_ACCESS, 0, &DeviceId); + if (!NT_SUCCESS(Status)) + { + /* No access at all -- bail out */ + goto Quickie; + } + } + + /* Now try to load the hive on disk */ + Status = BlImgLoadImageWithProgress2(DeviceId, + BlLoaderRegistry, + HiveName, + (PVOID*)&BaseBlock, + &HiveSize, + 0, + FALSE, + NULL, + NULL); + if (!NT_SUCCESS(Status)) + { + EfiPrintf(L"Hive read failure: % lx\r\n", Status); + goto Quickie; + } + + /* Allocate a hive structure */ + BcdHive = BlMmAllocateHeap(sizeof(*BcdHive)); + if (!BcdHive) + { + Status = STATUS_NO_MEMORY; + goto Quickie; + } + + /* Initialize it */ + RtlZeroMemory(BcdHive, sizeof(*BcdHive)); + BcdHive->BaseBlock = BaseBlock; + BcdHive->HiveSize = HiveSize; + if (HaveWriteAccess) + { + BcdHive->Flags |= BI_HIVE_WRITEABLE; + } + + /* Make sure the hive was at least one bin long */ + if (HiveSize < sizeof(*BaseBlock)) + { + Status = STATUS_REGISTRY_CORRUPT; + goto Quickie; + } + + /* Make sure the hive contents are at least one bin long */ + HiveLength = BaseBlock->Length; + if (BaseBlock->Length < sizeof(*BaseBlock)) + { + Status = STATUS_REGISTRY_CORRUPT; + goto Quickie; + } + + /* Validate the initial bin (the base block) */ + if (!HvIsInPlaceBaseBlockValid(BaseBlock)) + { + 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) + { + EfiPrintf(L"Log fix not implemented: %lx %lx\r\n"); + Status = STATUS_REGISTRY_CORRUPT; + goto Quickie; + } + + /* + * 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); + if (!NT_SUCCESS(Status)) + { + goto Quickie; + } + + /* Copy the current data in there */ + RtlCopyMemory(NewBaseBlock, BaseBlock, HiveSize); + + /* Free the old data */ + EfiPrintf(L"Leaking old hive buffer\r\n"); + //MmPapFreePages(BaseBlock, 1); + + /* Update our pointers */ + BaseBlock = NewBaseBlock; + HiveSize = NewHiveSize; + BcdHive->BaseBlock = BaseBlock; + BcdHive->HiveSize = HiveSize; + } + + /* Check if any log stuff needs to happen */ + if (LogData) + { + EfiPrintf(L"Log fix not implemented: %lx %lx\r\n"); + Status = STATUS_REGISTRY_CORRUPT; + goto Quickie; + } + + /* Call Hv to setup the hive library */ + Status = BiInitializeAndValidateHive(BcdHive); + if (!NT_SUCCESS(Status)) + { + goto Quickie; + } + + /* Now get the root node */ + Hive = &BcdHive->Hive.Hive; + RootNode = (PCM_KEY_NODE)HvGetCell(Hive, Hive->BaseBlock->RootCell); + if (!RootNode) + { + Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto Quickie; + } + + /* 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) + { + EfiPrintf(L"No OBJECTS subkey found!\r\n"); + Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto Quickie; + } + + /* 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) + { + Status = STATUS_NO_MEMORY; + goto Quickie; + } + + /* 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) + { + Status = STATUS_NO_MEMORY; + goto Quickie; + } + + /* 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) + { + BlMmFreeHeap(LogName); + } + + /* If we had logging data, free it */ + if (LogData) + { + EfiPrintf(L"Leaking log buffer\r\n"); + //MmPapFreePages(LogData, 1); + } + + /* Check if this is the failure path */ + if (!NT_SUCCESS(Status)) + { + /* 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); + } + + /* Finally, free the root key object if we created one */ + if (KeyObject) + { + BlMmFreeHeap(KeyObject); + } + } + + /* Return the final status */ + return Status; +} + +NTSTATUS +BiGetRegistryValue ( + _In_ HANDLE KeyHandle, + _In_ PWCHAR ValueName, + _In_ PWCHAR KeyName, + _In_ ULONG Type, + _Out_ PVOID* Buffer, + _Out_ PULONG ValueLength + ) +{ + PCM_KEY_NODE KeyNode; + PHHIVE KeyHive; + UNICODE_STRING ValueString; + PBI_KEY_OBJECT KeyObject; + PCM_KEY_VALUE KeyValue; + PVOID ValueCopy; + ULONG Size; + HCELL_INDEX CellIndex; + PCELL_DATA ValueData; + + /* Get the key object, node,and hive */ + KeyObject = (PBI_KEY_OBJECT)KeyHandle; + KeyNode = KeyObject->KeyNode; + KeyHive = &KeyObject->KeyHive->Hive.Hive; + + /* Find the value cell index in the list of values */ + RtlInitUnicodeString(&ValueString, ValueName); + CmpFindNameInList(KeyHive, + &KeyNode->ValueList, + &ValueString, + NULL, + &CellIndex); + if (CellIndex == HCELL_NIL) + { + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + /* Get the cell data for it */ + KeyValue = (PCM_KEY_VALUE)HvGetCell(KeyHive, CellIndex); + if (!KeyValue) + { + return STATUS_REGISTRY_CORRUPT; + } + + /* Make sure the type matches */ + if (KeyValue->Type != Type) + { + return STATUS_OBJECT_TYPE_MISMATCH; + } + + /* Now get the data cell */ + ValueData = CmpValueToData(KeyHive, KeyValue, &Size); + + /* Make a copy of it */ + ValueCopy = BlMmAllocateHeap(Size); + if (!ValueCopy) + { + return STATUS_NO_MEMORY; + } + + /* Copy it in the buffer, and return it and its size */ + RtlCopyMemory(ValueCopy, ValueData, Size); + *Buffer = ValueCopy; + *ValueLength = Size; + return STATUS_SUCCESS; +} + +NTSTATUS +BiEnumerateSubKeys ( + _In_ HANDLE KeyHandle, + _Out_ PWCHAR** SubKeyList, + _Out_ PULONG SubKeyCount + ) +{ + PCM_KEY_NODE KeyNode, Node; + PBI_KEY_OBJECT KeyObject; + ULONG KeyCount; + ULONG NameLength, NewTotalNameLength, FinalLength, TotalNameLength; + PHHIVE Hive; + PWCHAR KeyName, NameEnd; + HCELL_INDEX CellIndex; + PWCHAR* SubKeys; + NTSTATUS Status; + ULONG i; + + /* Get the key object, node, and hive */ + KeyObject = (PBI_KEY_OBJECT)KeyHandle; + KeyNode = KeyObject->KeyNode; + Hive = &KeyObject->KeyHive->Hive.Hive; + + /* Assume it's empty */ + *SubKeyList = 0; + *SubKeyCount = 0; + + /* Initialize locals */ + KeyCount = 0; + SubKeys = 0; + TotalNameLength = 0; + + /* Find the first subkey cell index */ + CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, KeyCount); + while (CellIndex != HCELL_NIL) + { + /* Move to the next one */ + KeyCount++; + + /* Get the cell data for it */ + Node = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex); + if (!Node) + { + return STATUS_REGISTRY_CORRUPT; + } + + /* Check if the value is compressed */ + if (Node->Flags & KEY_COMP_NAME) + { + /* Get the compressed name size */ + NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength); + } + else + { + /* Get the real size */ + NameLength = Node->NameLength; + } + + /* Add up the new length, protecting against overflow */ + NewTotalNameLength = TotalNameLength + NameLength + sizeof(UNICODE_NULL); + if (NewTotalNameLength < TotalNameLength) + { + Status = STATUS_NAME_TOO_LONG; + goto Quickie; + } + + /* We're good, use the new length */ + TotalNameLength = NewTotalNameLength; + + /* Find the next subkey cell index */ + CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, KeyCount); + } + + /* Were there no keys? We're done, if so */ + if (!KeyCount) + { + return STATUS_SUCCESS; + } + + /* Safely compute the size of the array needed */ + Status = RtlULongLongToULong(sizeof(PWCHAR) * KeyCount, &FinalLength); + if (!NT_SUCCESS(Status)) + { + goto Quickie; + } + + /* Safely add that to the name length */ + Status = RtlULongAdd(TotalNameLength, FinalLength, &FinalLength); + if (!NT_SUCCESS(Status)) + { + goto Quickie; + } + + /* Allocate an array big enough for the names and pointers */ + SubKeys = BlMmAllocateHeap(FinalLength); + if (!SubKeys) + { + Status = STATUS_NO_MEMORY; + goto Quickie; + } + + /* Go over each key again */ + NameEnd = (PWCHAR)&SubKeys[KeyCount]; + for (i = 0; i < KeyCount; i++) + { + /* Get the cell index for this subkey */ + CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, i); + if (CellIndex == HCELL_NIL) + { + break; + } + + /* Get the cell data for it */ + Node = HvGetCell(Hive, CellIndex); + if (!Node) + { + Status = STATUS_REGISTRY_CORRUPT; + goto Quickie; + } + + /* Check if the value is compressed */ + KeyName = Node->Name; + if (Node->Flags & KEY_COMP_NAME) + { + /* Get the compressed name size */ + NameLength = CmpCompressedNameSize(KeyName, Node->NameLength); + CmpCopyCompressedName(NameEnd, NameLength, KeyName, Node->NameLength); + } + else + { + /* Get the real size */ + NameLength = Node->NameLength; + RtlCopyMemory(NameEnd, KeyName, NameLength); + } + + /* Move the name buffer to the next spot, and NULL-terminate */ + SubKeys[i] = NameEnd; + NameEnd += (NameLength / sizeof(WCHAR)); + *NameEnd = UNICODE_NULL; + + /* Keep going */ + NameEnd++; + } + + /* Check if the subkeys were empty */ + if (i == 0) + { + /* They disappeared in the middle of enumeration */ + Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto Quickie; + } + + /* Return the count and the array of names */ + *SubKeyList = SubKeys; + *SubKeyCount = i; + SubKeys = NULL; + Status = STATUS_SUCCESS; + +Quickie: + /* On the failure path, free the subkeys if any exist */ + if (SubKeys) + { + BlMmFreeHeap(SubKeys); + } + + /* All done, return the result */ + return Status; +} + diff --git a/reactos/boot/environ/lib/mm/mm.c b/reactos/boot/environ/lib/mm/mm.c index d7519fc4d40..bc679a1d800 100644 --- a/reactos/boot/environ/lib/mm/mm.c +++ b/reactos/boot/environ/lib/mm/mm.c @@ -9,6 +9,7 @@ /* INCLUDES ******************************************************************/ #include "bl.h" +#include "bcd.h" /* DATA VARIABLES ************************************************************/ @@ -38,7 +39,35 @@ BlMmRemoveBadMemory ( VOID ) { - /* FIXME: Read BCD option to see what bad memory to remove */ + BOOLEAN AllowBad; + NTSTATUS Status; + PULONGLONG BadPages; + ULONGLONG BadPageCount; + + /* First check if bad memory access is allowed */ + AllowBad = FALSE; + Status = BlGetBootOptionBoolean(BlpApplicationEntry.BcdData, + BcdLibraryBoolean_AllowBadMemoryAccess, + &AllowBad); + if ((NT_SUCCESS(Status)) && (AllowBad)) + { + /* No point checking the list if it is */ + return STATUS_SUCCESS; + } + + /* Otherwise, check if there's a persisted bad page list */ + Status = BlpGetBootOptionIntegerList(BlpApplicationEntry.BcdData, + BcdLibraryIntegerList_BadMemoryList, + &BadPages, + &BadPageCount, + TRUE); + if (NT_SUCCESS(Status)) + { + EfiPrintf(L"Persistent bad page list not supported\r\n"); + return STATUS_NOT_IMPLEMENTED; + } + + /* All done here */ return STATUS_SUCCESS; } diff --git a/reactos/boot/environ/lib/mm/pagealloc.c b/reactos/boot/environ/lib/mm/pagealloc.c index 55314c102c8..2b682a1d8b1 100644 --- a/reactos/boot/environ/lib/mm/pagealloc.c +++ b/reactos/boot/environ/lib/mm/pagealloc.c @@ -9,7 +9,7 @@ /* INCLUDES ******************************************************************/ #include "bl.h" - +#include "bcd.h" typedef struct _BL_PA_REQUEST { @@ -50,7 +50,30 @@ BlpMmInitializeConstraints ( VOID ) { - /* FIXME: Read BCD option 'avoidlowmemory' and 'truncatememory' */ + NTSTATUS Status; + ULONGLONG LowestAddressValid, HighestAddressValid; + + /* Check for LOWMEM */ + Status = BlGetBootOptionInteger(BlpApplicationEntry.BcdData, + BcdLibraryInteger_AvoidLowPhysicalMemory, + &LowestAddressValid); + if (NT_SUCCESS(Status)) + { + EfiPrintf(L"/LOWMEM not supported\r\n"); + return STATUS_NOT_IMPLEMENTED; + } + + /* Check for MAXMEM */ + Status = BlGetBootOptionInteger(BlpApplicationEntry.BcdData, + BcdLibraryInteger_TruncatePhysicalMemory, + &HighestAddressValid); + if (NT_SUCCESS(Status)) + { + EfiPrintf(L"/MAXMEM not supported\r\n"); + return STATUS_NOT_IMPLEMENTED; + } + + /* Return back to the caller */ return STATUS_SUCCESS; }