From 089711c394fae9efcb0a8ada1b2f6ceb1fca18ab Mon Sep 17 00:00:00 2001 From: Mike Beaton Date: Tue, 3 Sep 2024 09:10:06 +0100 Subject: [PATCH 1/4] OpenNetworkBoot: Import initial unmodified EDK 2 files --- Platform/OpenNetworkBoot/BmBoot.c | 2664 ++++++++++++++++++ Platform/OpenNetworkBoot/BmBootDescription.c | 911 ++++++ Platform/OpenNetworkBoot/TlsAuthConfigImpl.c | 1562 ++++++++++ 3 files changed, 5137 insertions(+) create mode 100644 Platform/OpenNetworkBoot/BmBoot.c create mode 100644 Platform/OpenNetworkBoot/BmBootDescription.c create mode 100644 Platform/OpenNetworkBoot/TlsAuthConfigImpl.c diff --git a/Platform/OpenNetworkBoot/BmBoot.c b/Platform/OpenNetworkBoot/BmBoot.c new file mode 100644 index 00000000000..d3aebc9262b --- /dev/null +++ b/Platform/OpenNetworkBoot/BmBoot.c @@ -0,0 +1,2664 @@ +/** @file + Library functions which relates with booting. + +Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +Copyright (c) 2011 - 2021, Intel Corporation. All rights reserved.
+(C) Copyright 2015-2021 Hewlett Packard Enterprise Development LP
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "InternalBm.h" + +EFI_RAM_DISK_PROTOCOL *mRamDisk = NULL; + +EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION mBmRefreshLegacyBootOption = NULL; +EFI_BOOT_MANAGER_LEGACY_BOOT mBmLegacyBoot = NULL; + +/// +/// This GUID is used for an EFI Variable that stores the front device pathes +/// for a partial device path that starts with the HD node. +/// +EFI_GUID mBmHardDriveBootVariableGuid = { + 0xfab7e9e1, 0x39dd, 0x4f2b, { 0x84, 0x08, 0xe2, 0x0e, 0x90, 0x6c, 0xb6, 0xde } +}; +EFI_GUID mBmAutoCreateBootOptionGuid = { + 0x8108ac4e, 0x9f11, 0x4d59, { 0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2 } +}; + +/** + + End Perf entry of BDS + + @param Event The triggered event. + @param Context Context for this event. + +**/ +VOID +EFIAPI +BmEndOfBdsPerfCode ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Record the performance data for End of BDS + // + PERF_CROSSMODULE_END ("BDS"); + + return; +} + +/** + The function registers the legacy boot support capabilities. + + @param RefreshLegacyBootOption The function pointer to create all the legacy boot options. + @param LegacyBoot The function pointer to boot the legacy boot option. +**/ +VOID +EFIAPI +EfiBootManagerRegisterLegacyBootSupport ( + EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION RefreshLegacyBootOption, + EFI_BOOT_MANAGER_LEGACY_BOOT LegacyBoot + ) +{ + mBmRefreshLegacyBootOption = RefreshLegacyBootOption; + mBmLegacyBoot = LegacyBoot; +} + +/** + Return TRUE when the boot option is auto-created instead of manually added. + + @param BootOption Pointer to the boot option to check. + + @retval TRUE The boot option is auto-created. + @retval FALSE The boot option is manually added. +**/ +BOOLEAN +BmIsAutoCreateBootOption ( + EFI_BOOT_MANAGER_LOAD_OPTION *BootOption + ) +{ + if ((BootOption->OptionalDataSize == sizeof (EFI_GUID)) && + CompareGuid ((EFI_GUID *)BootOption->OptionalData, &mBmAutoCreateBootOptionGuid) + ) + { + return TRUE; + } else { + return FALSE; + } +} + +/** + Find the boot option in the NV storage and return the option number. + + @param OptionToFind Boot option to be checked. + + @return The option number of the found boot option. + +**/ +UINTN +BmFindBootOptionInVariable ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *OptionToFind + ) +{ + EFI_STATUS Status; + EFI_BOOT_MANAGER_LOAD_OPTION BootOption; + UINTN OptionNumber; + CHAR16 OptionName[BM_OPTION_NAME_LEN]; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINTN BootOptionCount; + UINTN Index; + + OptionNumber = LoadOptionNumberUnassigned; + + // + // Try to match the variable exactly if the option number is assigned + // + if (OptionToFind->OptionNumber != LoadOptionNumberUnassigned) { + UnicodeSPrint ( + OptionName, + sizeof (OptionName), + L"%s%04x", + mBmLoadOptionName[OptionToFind->OptionType], + OptionToFind->OptionNumber + ); + Status = EfiBootManagerVariableToLoadOption (OptionName, &BootOption); + + if (!EFI_ERROR (Status)) { + ASSERT (OptionToFind->OptionNumber == BootOption.OptionNumber); + if ((OptionToFind->Attributes == BootOption.Attributes) && + (StrCmp (OptionToFind->Description, BootOption.Description) == 0) && + (CompareMem (OptionToFind->FilePath, BootOption.FilePath, GetDevicePathSize (OptionToFind->FilePath)) == 0) && + (OptionToFind->OptionalDataSize == BootOption.OptionalDataSize) && + (CompareMem (OptionToFind->OptionalData, BootOption.OptionalData, OptionToFind->OptionalDataSize) == 0) + ) + { + OptionNumber = OptionToFind->OptionNumber; + } + + EfiBootManagerFreeLoadOption (&BootOption); + } + } + + // + // The option number assigned is either incorrect or unassigned. + // + if (OptionNumber == LoadOptionNumberUnassigned) { + BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); + + Index = EfiBootManagerFindLoadOption (OptionToFind, BootOptions, BootOptionCount); + if (Index != -1) { + OptionNumber = BootOptions[Index].OptionNumber; + } + + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); + } + + return OptionNumber; +} + +/** + Return the correct FV file path. + FV address may change across reboot. This routine promises the FV file device path is right. + + @param FilePath The Memory Mapped Device Path to get the file buffer. + + @return The updated FV Device Path pointint to the file. +**/ +EFI_DEVICE_PATH_PROTOCOL * +BmAdjustFvFilePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *FvFileNode; + EFI_HANDLE FvHandle; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + UINTN FvHandleCount; + EFI_HANDLE *FvHandles; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + EFI_DEVICE_PATH_PROTOCOL *FullPath; + + // + // Get the file buffer by using the exactly FilePath. + // + FvFileNode = FilePath; + Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &FvFileNode, &FvHandle); + if (!EFI_ERROR (Status)) { + return DuplicateDevicePath (FilePath); + } + + // + // Only wide match other FVs if it's a memory mapped FV file path. + // + if ((DevicePathType (FilePath) != HARDWARE_DEVICE_PATH) || (DevicePathSubType (FilePath) != HW_MEMMAP_DP)) { + return NULL; + } + + FvFileNode = NextDevicePathNode (FilePath); + + // + // Firstly find the FV file in current FV + // + gBS->HandleProtocol ( + gImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); + NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), FvFileNode); + FullPath = BmAdjustFvFilePath (NewDevicePath); + FreePool (NewDevicePath); + if (FullPath != NULL) { + return FullPath; + } + + // + // Secondly find the FV file in all other FVs + // + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareVolume2ProtocolGuid, + NULL, + &FvHandleCount, + &FvHandles + ); + for (Index = 0; Index < FvHandleCount; Index++) { + if (FvHandles[Index] == LoadedImage->DeviceHandle) { + // + // Skip current FV, it was handed in first step. + // + continue; + } + + NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandles[Index]), FvFileNode); + FullPath = BmAdjustFvFilePath (NewDevicePath); + FreePool (NewDevicePath); + if (FullPath != NULL) { + break; + } + } + + if (FvHandles != NULL) { + FreePool (FvHandles); + } + + return FullPath; +} + +/** + Check if it's a Device Path pointing to FV file. + + The function doesn't garentee the device path points to existing FV file. + + @param DevicePath Input device path. + + @retval TRUE The device path is a FV File Device Path. + @retval FALSE The device path is NOT a FV File Device Path. +**/ +BOOLEAN +BmIsFvFilePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_DEVICE_PATH_PROTOCOL *Node; + + Node = DevicePath; + Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &Handle); + if (!EFI_ERROR (Status)) { + return TRUE; + } + + if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) && (DevicePathSubType (DevicePath) == HW_MEMMAP_DP)) { + DevicePath = NextDevicePathNode (DevicePath); + if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) && (DevicePathSubType (DevicePath) == MEDIA_PIWG_FW_FILE_DP)) { + return IsDevicePathEnd (NextDevicePathNode (DevicePath)); + } + } + + return FALSE; +} + +/** + Check whether a USB device match the specified USB Class device path. This + function follows "Load Option Processing" behavior in UEFI specification. + + @param UsbIo USB I/O protocol associated with the USB device. + @param UsbClass The USB Class device path to match. + + @retval TRUE The USB device match the USB Class device path. + @retval FALSE The USB device does not match the USB Class device path. + +**/ +BOOLEAN +BmMatchUsbClass ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN USB_CLASS_DEVICE_PATH *UsbClass + ) +{ + EFI_STATUS Status; + EFI_USB_DEVICE_DESCRIPTOR DevDesc; + EFI_USB_INTERFACE_DESCRIPTOR IfDesc; + UINT8 DeviceClass; + UINT8 DeviceSubClass; + UINT8 DeviceProtocol; + + if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) || + (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)) + { + return FALSE; + } + + // + // Check Vendor Id and Product Id. + // + Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); + if (EFI_ERROR (Status)) { + return FALSE; + } + + if ((UsbClass->VendorId != 0xffff) && + (UsbClass->VendorId != DevDesc.IdVendor)) + { + return FALSE; + } + + if ((UsbClass->ProductId != 0xffff) && + (UsbClass->ProductId != DevDesc.IdProduct)) + { + return FALSE; + } + + DeviceClass = DevDesc.DeviceClass; + DeviceSubClass = DevDesc.DeviceSubClass; + DeviceProtocol = DevDesc.DeviceProtocol; + if (DeviceClass == 0) { + // + // If Class in Device Descriptor is set to 0, use the Class, SubClass and + // Protocol in Interface Descriptor instead. + // + Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc); + if (EFI_ERROR (Status)) { + return FALSE; + } + + DeviceClass = IfDesc.InterfaceClass; + DeviceSubClass = IfDesc.InterfaceSubClass; + DeviceProtocol = IfDesc.InterfaceProtocol; + } + + // + // Check Class, SubClass and Protocol. + // + if ((UsbClass->DeviceClass != 0xff) && + (UsbClass->DeviceClass != DeviceClass)) + { + return FALSE; + } + + if ((UsbClass->DeviceSubClass != 0xff) && + (UsbClass->DeviceSubClass != DeviceSubClass)) + { + return FALSE; + } + + if ((UsbClass->DeviceProtocol != 0xff) && + (UsbClass->DeviceProtocol != DeviceProtocol)) + { + return FALSE; + } + + return TRUE; +} + +/** + Check whether a USB device match the specified USB WWID device path. This + function follows "Load Option Processing" behavior in UEFI specification. + + @param UsbIo USB I/O protocol associated with the USB device. + @param UsbWwid The USB WWID device path to match. + + @retval TRUE The USB device match the USB WWID device path. + @retval FALSE The USB device does not match the USB WWID device path. + +**/ +BOOLEAN +BmMatchUsbWwid ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN USB_WWID_DEVICE_PATH *UsbWwid + ) +{ + EFI_STATUS Status; + EFI_USB_DEVICE_DESCRIPTOR DevDesc; + EFI_USB_INTERFACE_DESCRIPTOR IfDesc; + UINT16 *LangIdTable; + UINT16 TableSize; + UINT16 Index; + CHAR16 *CompareStr; + UINTN CompareLen; + CHAR16 *SerialNumberStr; + UINTN Length; + + if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) || + (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP)) + { + return FALSE; + } + + // + // Check Vendor Id and Product Id. + // + Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); + if (EFI_ERROR (Status)) { + return FALSE; + } + + if ((DevDesc.IdVendor != UsbWwid->VendorId) || + (DevDesc.IdProduct != UsbWwid->ProductId)) + { + return FALSE; + } + + // + // Check Interface Number. + // + Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc); + if (EFI_ERROR (Status)) { + return FALSE; + } + + if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) { + return FALSE; + } + + // + // Check Serial Number. + // + if (DevDesc.StrSerialNumber == 0) { + return FALSE; + } + + // + // Get all supported languages. + // + TableSize = 0; + LangIdTable = NULL; + Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize); + if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) { + return FALSE; + } + + // + // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters. + // + CompareStr = (CHAR16 *)(UINTN)(UsbWwid + 1); + CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16); + if (CompareStr[CompareLen - 1] == L'\0') { + CompareLen--; + } + + // + // Compare serial number in each supported language. + // + for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) { + SerialNumberStr = NULL; + Status = UsbIo->UsbGetStringDescriptor ( + UsbIo, + LangIdTable[Index], + DevDesc.StrSerialNumber, + &SerialNumberStr + ); + if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) { + continue; + } + + Length = StrLen (SerialNumberStr); + if ((Length >= CompareLen) && + (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) + { + FreePool (SerialNumberStr); + return TRUE; + } + + FreePool (SerialNumberStr); + } + + return FALSE; +} + +/** + Find a USB device which match the specified short-form device path start with + USB Class or USB WWID device path. If ParentDevicePath is NULL, this function + will search in all USB devices of the platform. If ParentDevicePath is not NULL, + this function will only search in its child devices. + + @param DevicePath The device path that contains USB Class or USB WWID device path. + @param ParentDevicePathSize The length of the device path before the USB Class or + USB WWID device path. + @param UsbIoHandleCount A pointer to the count of the returned USB IO handles. + + @retval NULL The matched USB IO handles cannot be found. + @retval other The matched USB IO handles. + +**/ +EFI_HANDLE * +BmFindUsbDevice ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN UINTN ParentDevicePathSize, + OUT UINTN *UsbIoHandleCount + ) +{ + EFI_STATUS Status; + EFI_HANDLE *UsbIoHandles; + EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath; + EFI_USB_IO_PROTOCOL *UsbIo; + UINTN Index; + BOOLEAN Matched; + + ASSERT (UsbIoHandleCount != NULL); + + // + // Get all UsbIo Handles. + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiUsbIoProtocolGuid, + NULL, + UsbIoHandleCount, + &UsbIoHandles + ); + if (EFI_ERROR (Status)) { + *UsbIoHandleCount = 0; + UsbIoHandles = NULL; + } + + for (Index = 0; Index < *UsbIoHandleCount; ) { + // + // Get the Usb IO interface. + // + Status = gBS->HandleProtocol ( + UsbIoHandles[Index], + &gEfiUsbIoProtocolGuid, + (VOID **)&UsbIo + ); + UsbIoDevicePath = DevicePathFromHandle (UsbIoHandles[Index]); + Matched = FALSE; + if (!EFI_ERROR (Status) && (UsbIoDevicePath != NULL)) { + // + // Compare starting part of UsbIoHandle's device path with ParentDevicePath. + // + if (CompareMem (UsbIoDevicePath, DevicePath, ParentDevicePathSize) == 0) { + if (BmMatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *)((UINTN)DevicePath + ParentDevicePathSize)) || + BmMatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *)((UINTN)DevicePath + ParentDevicePathSize))) + { + Matched = TRUE; + } + } + } + + if (!Matched) { + (*UsbIoHandleCount)--; + CopyMem (&UsbIoHandles[Index], &UsbIoHandles[Index + 1], (*UsbIoHandleCount - Index) * sizeof (EFI_HANDLE)); + } else { + Index++; + } + } + + return UsbIoHandles; +} + +/** + Expand USB Class or USB WWID device path node to be full device path of a USB + device in platform. + + This function support following 4 cases: + 1) Boot Option device path starts with a USB Class or USB WWID device path, + and there is no Media FilePath device path in the end. + In this case, it will follow Removable Media Boot Behavior. + 2) Boot Option device path starts with a USB Class or USB WWID device path, + and ended with Media FilePath device path. + 3) Boot Option device path starts with a full device path to a USB Host Controller, + contains a USB Class or USB WWID device path node, while not ended with Media + FilePath device path. In this case, it will follow Removable Media Boot Behavior. + 4) Boot Option device path starts with a full device path to a USB Host Controller, + contains a USB Class or USB WWID device path node, and ended with Media + FilePath device path. + + @param FilePath The device path pointing to a load option. + It could be a short-form device path. + @param FullPath The full path returned by the routine in last call. + Set to NULL in first call. + @param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer. + + @return The next possible full path pointing to the load option. + Caller is responsible to free the memory. +**/ +EFI_DEVICE_PATH_PROTOCOL * +BmExpandUsbDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN EFI_DEVICE_PATH_PROTOCOL *FullPath, + IN EFI_DEVICE_PATH_PROTOCOL *ShortformNode + ) +{ + UINTN ParentDevicePathSize; + EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath; + EFI_DEVICE_PATH_PROTOCOL *NextFullPath; + EFI_HANDLE *Handles; + UINTN HandleCount; + UINTN Index; + BOOLEAN GetNext; + + NextFullPath = NULL; + GetNext = (BOOLEAN)(FullPath == NULL); + ParentDevicePathSize = (UINTN)ShortformNode - (UINTN)FilePath; + RemainingDevicePath = NextDevicePathNode (ShortformNode); + Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount); + + for (Index = 0; Index < HandleCount; Index++) { + FilePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath); + if (FilePath == NULL) { + // + // Out of memory. + // + continue; + } + + NextFullPath = BmGetNextLoadOptionDevicePath (FilePath, NULL); + FreePool (FilePath); + if (NextFullPath == NULL) { + // + // No BlockIo or SimpleFileSystem under FilePath. + // + continue; + } + + if (GetNext) { + break; + } else { + GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0); + FreePool (NextFullPath); + NextFullPath = NULL; + } + } + + if (Handles != NULL) { + FreePool (Handles); + } + + return NextFullPath; +} + +/** + Expand File-path device path node to be full device path in platform. + + @param FilePath The device path pointing to a load option. + It could be a short-form device path. + @param FullPath The full path returned by the routine in last call. + Set to NULL in first call. + + @return The next possible full path pointing to the load option. + Caller is responsible to free the memory. +**/ +EFI_DEVICE_PATH_PROTOCOL * +BmExpandFileDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN EFI_DEVICE_PATH_PROTOCOL *FullPath + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN HandleCount; + EFI_HANDLE *Handles; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + UINTN MediaType; + EFI_DEVICE_PATH_PROTOCOL *NextFullPath; + BOOLEAN GetNext; + + EfiBootManagerConnectAll (); + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandleCount, &Handles); + if (EFI_ERROR (Status)) { + HandleCount = 0; + Handles = NULL; + } + + GetNext = (BOOLEAN)(FullPath == NULL); + NextFullPath = NULL; + // + // Enumerate all removable media devices followed by all fixed media devices, + // followed by media devices which don't layer on block io. + // + for (MediaType = 0; MediaType < 3; MediaType++) { + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol (Handles[Index], &gEfiBlockIoProtocolGuid, (VOID *)&BlockIo); + if (EFI_ERROR (Status)) { + BlockIo = NULL; + } + + if (((MediaType == 0) && (BlockIo != NULL) && BlockIo->Media->RemovableMedia) || + ((MediaType == 1) && (BlockIo != NULL) && !BlockIo->Media->RemovableMedia) || + ((MediaType == 2) && (BlockIo == NULL)) + ) + { + NextFullPath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), FilePath); + if (GetNext) { + break; + } else { + GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0); + FreePool (NextFullPath); + NextFullPath = NULL; + } + } + } + + if (NextFullPath != NULL) { + break; + } + } + + if (Handles != NULL) { + FreePool (Handles); + } + + return NextFullPath; +} + +/** + Expand URI device path node to be full device path in platform. + + @param FilePath The device path pointing to a load option. + It could be a short-form device path. + @param FullPath The full path returned by the routine in last call. + Set to NULL in first call. + + @return The next possible full path pointing to the load option. + Caller is responsible to free the memory. +**/ +EFI_DEVICE_PATH_PROTOCOL * +BmExpandUriDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN EFI_DEVICE_PATH_PROTOCOL *FullPath + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN HandleCount; + EFI_HANDLE *Handles; + EFI_DEVICE_PATH_PROTOCOL *NextFullPath; + EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath; + BOOLEAN GetNext; + + EfiBootManagerConnectAll (); + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &HandleCount, &Handles); + if (EFI_ERROR (Status)) { + HandleCount = 0; + Handles = NULL; + } + + NextFullPath = NULL; + GetNext = (BOOLEAN)(FullPath == NULL); + for (Index = 0; Index < HandleCount; Index++) { + NextFullPath = BmExpandLoadFile (Handles[Index], FilePath); + + if (NextFullPath == NULL) { + continue; + } + + if (GetNext) { + break; + } else { + GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0); + // + // Free the resource occupied by the RAM disk. + // + RamDiskDevicePath = BmGetRamDiskDevicePath (NextFullPath); + if (RamDiskDevicePath != NULL) { + BmDestroyRamDisk (RamDiskDevicePath); + FreePool (RamDiskDevicePath); + } + + FreePool (NextFullPath); + NextFullPath = NULL; + } + } + + if (Handles != NULL) { + FreePool (Handles); + } + + return NextFullPath; +} + +/** + Save the partition DevicePath to the CachedDevicePath as the first instance. + + @param CachedDevicePath The device path cache. + @param DevicePath The partition device path to be cached. +**/ +VOID +BmCachePartitionDevicePath ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **CachedDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + UINTN Count; + + if (BmMatchDevicePaths (*CachedDevicePath, DevicePath)) { + TempDevicePath = *CachedDevicePath; + *CachedDevicePath = BmDelPartMatchInstance (*CachedDevicePath, DevicePath); + FreePool (TempDevicePath); + } + + if (*CachedDevicePath == NULL) { + *CachedDevicePath = DuplicateDevicePath (DevicePath); + return; + } + + TempDevicePath = *CachedDevicePath; + *CachedDevicePath = AppendDevicePathInstance (DevicePath, *CachedDevicePath); + if (TempDevicePath != NULL) { + FreePool (TempDevicePath); + } + + // + // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller + // If the user try to boot many OS in different HDs or partitions, in theory, the 'HDDP' variable maybe become larger and larger. + // + Count = 0; + TempDevicePath = *CachedDevicePath; + while (!IsDevicePathEnd (TempDevicePath)) { + TempDevicePath = NextDevicePathNode (TempDevicePath); + // + // Parse one instance + // + while (!IsDevicePathEndType (TempDevicePath)) { + TempDevicePath = NextDevicePathNode (TempDevicePath); + } + + Count++; + // + // If the CachedDevicePath variable contain too much instance, only remain 12 instances. + // + if (Count == 12) { + SetDevicePathEndNode (TempDevicePath); + break; + } + } +} + +/** + Expand a device path that starts with a hard drive media device path node to be a + full device path that includes the full hardware path to the device. We need + to do this so it can be booted. As an optimization the front match (the part point + to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable + so a connect all is not required on every boot. All successful history device path + which point to partition node (the front part) will be saved. + + @param FilePath The device path pointing to a load option. + It could be a short-form device path. + + @return The full device path pointing to the load option. +**/ +EFI_DEVICE_PATH_PROTOCOL * +BmExpandPartitionDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath + ) +{ + EFI_STATUS Status; + UINTN BlockIoHandleCount; + EFI_HANDLE *BlockIoBuffer; + EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath; + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath; + EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + EFI_DEVICE_PATH_PROTOCOL *FullPath; + UINTN CachedDevicePathSize; + BOOLEAN NeedAdjust; + EFI_DEVICE_PATH_PROTOCOL *Instance; + UINTN Size; + BOOLEAN MatchFound; + BOOLEAN ConnectAllAttempted; + + // + // Check if there is prestore 'HDDP' variable. + // If exist, search the front path which point to partition node in the variable instants. + // If fail to find or 'HDDP' not exist, reconnect all and search in all system + // + GetVariable2 (L"HDDP", &mBmHardDriveBootVariableGuid, (VOID **)&CachedDevicePath, &CachedDevicePathSize); + + // + // Delete the invalid 'HDDP' variable. + // + if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) { + FreePool (CachedDevicePath); + CachedDevicePath = NULL; + Status = gRT->SetVariable ( + L"HDDP", + &mBmHardDriveBootVariableGuid, + 0, + 0, + NULL + ); + ASSERT_EFI_ERROR (Status); + } + + FullPath = NULL; + if (CachedDevicePath != NULL) { + TempNewDevicePath = CachedDevicePath; + NeedAdjust = FALSE; + do { + // + // Check every instance of the variable + // First, check whether the instance contain the partition node, which is needed for distinguishing multi + // partial partition boot option. Second, check whether the instance could be connected. + // + Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size); + if (BmMatchPartitionDevicePathNode (Instance, (HARDDRIVE_DEVICE_PATH *)FilePath)) { + // + // Connect the device path instance, the device path point to hard drive media device path node + // e.g. ACPI() /PCI()/ATA()/Partition() + // + Status = EfiBootManagerConnectDevicePath (Instance, NULL); + if (!EFI_ERROR (Status)) { + TempDevicePath = AppendDevicePath (Instance, NextDevicePathNode (FilePath)); + // + // TempDevicePath = ACPI()/PCI()/ATA()/Partition() + // or = ACPI()/PCI()/ATA()/Partition()/.../A.EFI + // + // When TempDevicePath = ACPI()/PCI()/ATA()/Partition(), + // it may expand to two potienal full paths (nested partition, rarely happen): + // 1. ACPI()/PCI()/ATA()/Partition()/Partition(A1)/EFI/BootX64.EFI + // 2. ACPI()/PCI()/ATA()/Partition()/Partition(A2)/EFI/BootX64.EFI + // For simplicity, only #1 is returned. + // + FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL); + FreePool (TempDevicePath); + + if (FullPath != NULL) { + // + // Adjust the 'HDDP' instances sequence if the matched one is not first one. + // + if (NeedAdjust) { + BmCachePartitionDevicePath (&CachedDevicePath, Instance); + // + // Save the matching Device Path so we don't need to do a connect all next time + // Failing to save only impacts performance next time expanding the short-form device path + // + Status = gRT->SetVariable ( + L"HDDP", + &mBmHardDriveBootVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + GetDevicePathSize (CachedDevicePath), + CachedDevicePath + ); + } + + FreePool (Instance); + FreePool (CachedDevicePath); + return FullPath; + } + } + } + + // + // Come here means the first instance is not matched + // + NeedAdjust = TRUE; + FreePool (Instance); + } while (TempNewDevicePath != NULL); + } + + // + // If we get here we fail to find or 'HDDP' not exist, and now we need + // to search all devices in the system for a matched partition + // + BlockIoBuffer = NULL; + MatchFound = FALSE; + ConnectAllAttempted = FALSE; + do { + if (BlockIoBuffer != NULL) { + FreePool (BlockIoBuffer); + } + + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer); + if (EFI_ERROR (Status)) { + BlockIoHandleCount = 0; + BlockIoBuffer = NULL; + } + + // + // Loop through all the device handles that support the BLOCK_IO Protocol + // + for (Index = 0; Index < BlockIoHandleCount; Index++) { + BlockIoDevicePath = DevicePathFromHandle (BlockIoBuffer[Index]); + if (BlockIoDevicePath == NULL) { + continue; + } + + if (BmMatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *)FilePath)) { + // + // Find the matched partition device path + // + TempDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (FilePath)); + FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL); + FreePool (TempDevicePath); + + if (FullPath != NULL) { + BmCachePartitionDevicePath (&CachedDevicePath, BlockIoDevicePath); + + // + // Save the matching Device Path so we don't need to do a connect all next time + // Failing to save only impacts performance next time expanding the short-form device path + // + Status = gRT->SetVariable ( + L"HDDP", + &mBmHardDriveBootVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + GetDevicePathSize (CachedDevicePath), + CachedDevicePath + ); + MatchFound = TRUE; + break; + } + } + } + + // + // If we found a matching BLOCK_IO handle or we've already + // tried a ConnectAll, we are done searching. + // + if (MatchFound || ConnectAllAttempted) { + break; + } + + EfiBootManagerConnectAll (); + ConnectAllAttempted = TRUE; + } while (1); + + if (CachedDevicePath != NULL) { + FreePool (CachedDevicePath); + } + + if (BlockIoBuffer != NULL) { + FreePool (BlockIoBuffer); + } + + return FullPath; +} + +/** + Expand the media device path which points to a BlockIo or SimpleFileSystem instance + by appending EFI_REMOVABLE_MEDIA_FILE_NAME. + + @param DevicePath The media device path pointing to a BlockIo or SimpleFileSystem instance. + @param FullPath The full path returned by the routine in last call. + Set to NULL in first call. + + @return The next possible full path pointing to the load option. + Caller is responsible to free the memory. +**/ +EFI_DEVICE_PATH_PROTOCOL * +BmExpandMediaDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN EFI_DEVICE_PATH_PROTOCOL *FullPath + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + VOID *Buffer; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + EFI_DEVICE_PATH_PROTOCOL *NextFullPath; + UINTN Size; + UINTN TempSize; + EFI_HANDLE *SimpleFileSystemHandles; + UINTN NumberSimpleFileSystemHandles; + UINTN Index; + BOOLEAN GetNext; + + GetNext = (BOOLEAN)(FullPath == NULL); + // + // Check whether the device is connected + // + TempDevicePath = DevicePath; + Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle); + if (!EFI_ERROR (Status)) { + ASSERT (IsDevicePathEnd (TempDevicePath)); + + NextFullPath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME); + // + // For device path pointing to simple file system, it only expands to one full path. + // + if (GetNext) { + return NextFullPath; + } else { + FreePool (NextFullPath); + return NULL; + } + } + + Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle); + ASSERT_EFI_ERROR (Status); + + // + // For device boot option only pointing to the removable device handle, + // should make sure all its children handles (its child partion or media handles) + // are created and connected. + // + gBS->ConnectController (Handle, NULL, NULL, TRUE); + + // + // Issue a dummy read to the device to check for media change. + // When the removable media is changed, any Block IO read/write will + // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is + // returned. After the Block IO protocol is reinstalled, subsequent + // Block IO read/write will success. + // + Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return NULL; + } + + Buffer = AllocatePool (BlockIo->Media->BlockSize); + if (Buffer != NULL) { + BlockIo->ReadBlocks ( + BlockIo, + BlockIo->Media->MediaId, + 0, + BlockIo->Media->BlockSize, + Buffer + ); + FreePool (Buffer); + } + + // + // Detect the the default boot file from removable Media + // + NextFullPath = NULL; + Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH; + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleFileSystemProtocolGuid, + NULL, + &NumberSimpleFileSystemHandles, + &SimpleFileSystemHandles + ); + for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) { + // + // Get the device path size of SimpleFileSystem handle + // + TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]); + TempSize = GetDevicePathSize (TempDevicePath) - END_DEVICE_PATH_LENGTH; + // + // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path + // + if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) { + NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME); + if (GetNext) { + break; + } else { + GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0); + FreePool (NextFullPath); + NextFullPath = NULL; + } + } + } + + if (SimpleFileSystemHandles != NULL) { + FreePool (SimpleFileSystemHandles); + } + + return NextFullPath; +} + +/** + Check whether Left and Right are the same without matching the specific + device path data in IP device path and URI device path node. + + @retval TRUE Left and Right are the same. + @retval FALSE Left and Right are the different. +**/ +BOOLEAN +BmMatchHttpBootDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *Left, + IN EFI_DEVICE_PATH_PROTOCOL *Right + ) +{ + for ( ; !IsDevicePathEnd (Left) && !IsDevicePathEnd (Right) + ; Left = NextDevicePathNode (Left), Right = NextDevicePathNode (Right) + ) + { + if (CompareMem (Left, Right, DevicePathNodeLength (Left)) != 0) { + if ((DevicePathType (Left) != MESSAGING_DEVICE_PATH) || (DevicePathType (Right) != MESSAGING_DEVICE_PATH)) { + return FALSE; + } + + if (DevicePathSubType (Left) == MSG_DNS_DP) { + Left = NextDevicePathNode (Left); + } + + if (DevicePathSubType (Right) == MSG_DNS_DP) { + Right = NextDevicePathNode (Right); + } + + if (((DevicePathSubType (Left) != MSG_IPv4_DP) || (DevicePathSubType (Right) != MSG_IPv4_DP)) && + ((DevicePathSubType (Left) != MSG_IPv6_DP) || (DevicePathSubType (Right) != MSG_IPv6_DP)) && + ((DevicePathSubType (Left) != MSG_URI_DP) || (DevicePathSubType (Right) != MSG_URI_DP)) + ) + { + return FALSE; + } + } + } + + return (BOOLEAN)(IsDevicePathEnd (Left) && IsDevicePathEnd (Right)); +} + +/** + Get the file buffer from the file system produced by Load File instance. + + @param LoadFileHandle The handle of LoadFile instance. + @param RamDiskHandle Return the RAM Disk handle. + + @return The next possible full path pointing to the load option. + Caller is responsible to free the memory. +**/ +EFI_DEVICE_PATH_PROTOCOL * +BmExpandNetworkFileSystem ( + IN EFI_HANDLE LoadFileHandle, + OUT EFI_HANDLE *RamDiskHandle + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_HANDLE *Handles; + UINTN HandleCount; + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *Node; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiBlockIoProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + if (EFI_ERROR (Status)) { + Handles = NULL; + HandleCount = 0; + } + + Handle = NULL; + for (Index = 0; Index < HandleCount; Index++) { + Node = DevicePathFromHandle (Handles[Index]); + Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle); + if (!EFI_ERROR (Status) && + (Handle == LoadFileHandle) && + (DevicePathType (Node) == MEDIA_DEVICE_PATH) && (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)) + { + // + // Find the BlockIo instance populated from the LoadFile. + // + Handle = Handles[Index]; + break; + } + } + + if (Handles != NULL) { + FreePool (Handles); + } + + if (Index == HandleCount) { + Handle = NULL; + } + + *RamDiskHandle = Handle; + + if (Handle != NULL) { + // + // Re-use BmExpandMediaDevicePath() to get the full device path of load option. + // But assume only one SimpleFileSystem can be found under the BlockIo. + // + return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), NULL); + } else { + return NULL; + } +} + +/** + Return the RAM Disk device path created by LoadFile. + + @param FilePath The source file path. + + @return Callee-to-free RAM Disk device path +**/ +EFI_DEVICE_PATH_PROTOCOL * +BmGetRamDiskDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath; + EFI_DEVICE_PATH_PROTOCOL *Node; + EFI_HANDLE Handle; + + Node = FilePath; + Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle); + if (!EFI_ERROR (Status) && + (DevicePathType (Node) == MEDIA_DEVICE_PATH) && + (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP) + ) + { + // + // Construct the device path pointing to RAM Disk + // + Node = NextDevicePathNode (Node); + RamDiskDevicePath = DuplicateDevicePath (FilePath); + ASSERT (RamDiskDevicePath != NULL); + SetDevicePathEndNode ((VOID *)((UINTN)RamDiskDevicePath + ((UINTN)Node - (UINTN)FilePath))); + return RamDiskDevicePath; + } + + return NULL; +} + +/** + Return the buffer and buffer size occupied by the RAM Disk. + + @param RamDiskDevicePath RAM Disk device path. + @param RamDiskSizeInPages Return RAM Disk size in pages. + + @retval RAM Disk buffer. +**/ +VOID * +BmGetRamDiskMemoryInfo ( + IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath, + OUT UINTN *RamDiskSizeInPages + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + UINT64 StartingAddr; + UINT64 EndingAddr; + + ASSERT (RamDiskDevicePath != NULL); + + *RamDiskSizeInPages = 0; + + // + // Get the buffer occupied by RAM Disk. + // + Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &RamDiskDevicePath, &Handle); + ASSERT_EFI_ERROR (Status); + ASSERT ( + (DevicePathType (RamDiskDevicePath) == MEDIA_DEVICE_PATH) && + (DevicePathSubType (RamDiskDevicePath) == MEDIA_RAM_DISK_DP) + ); + StartingAddr = ReadUnaligned64 ((UINT64 *)((MEDIA_RAM_DISK_DEVICE_PATH *)RamDiskDevicePath)->StartingAddr); + EndingAddr = ReadUnaligned64 ((UINT64 *)((MEDIA_RAM_DISK_DEVICE_PATH *)RamDiskDevicePath)->EndingAddr); + *RamDiskSizeInPages = EFI_SIZE_TO_PAGES ((UINTN)(EndingAddr - StartingAddr + 1)); + return (VOID *)(UINTN)StartingAddr; +} + +/** + Destroy the RAM Disk. + + The destroy operation includes to call RamDisk.Unregister to + unregister the RAM DISK from RAM DISK driver, free the memory + allocated for the RAM Disk. + + @param RamDiskDevicePath RAM Disk device path. +**/ +VOID +BmDestroyRamDisk ( + IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath + ) +{ + EFI_STATUS Status; + VOID *RamDiskBuffer; + UINTN RamDiskSizeInPages; + + ASSERT (RamDiskDevicePath != NULL); + + RamDiskBuffer = BmGetRamDiskMemoryInfo (RamDiskDevicePath, &RamDiskSizeInPages); + + // + // Destroy RAM Disk. + // + if (mRamDisk == NULL) { + Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID *)&mRamDisk); + ASSERT_EFI_ERROR (Status); + } + + Status = mRamDisk->Unregister (RamDiskDevicePath); + ASSERT_EFI_ERROR (Status); + FreePages (RamDiskBuffer, RamDiskSizeInPages); +} + +/** + Get the file buffer from the specified Load File instance. + + @param LoadFileHandle The specified Load File instance. + @param FilePath The file path which will pass to LoadFile(). + + @return The full device path pointing to the load option buffer. +**/ +EFI_DEVICE_PATH_PROTOCOL * +BmExpandLoadFile ( + IN EFI_HANDLE LoadFileHandle, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath + ) +{ + EFI_STATUS Status; + EFI_LOAD_FILE_PROTOCOL *LoadFile; + VOID *FileBuffer; + EFI_HANDLE RamDiskHandle; + UINTN BufferSize; + EFI_DEVICE_PATH_PROTOCOL *FullPath; + + Status = gBS->OpenProtocol ( + LoadFileHandle, + &gEfiLoadFileProtocolGuid, + (VOID **)&LoadFile, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + ASSERT_EFI_ERROR (Status); + + FileBuffer = NULL; + BufferSize = 0; + Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer); + if ((Status != EFI_WARN_FILE_SYSTEM) && (Status != EFI_BUFFER_TOO_SMALL)) { + return NULL; + } + + if (Status == EFI_BUFFER_TOO_SMALL) { + // + // The load option buffer is directly returned by LoadFile. + // + return DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle)); + } + + // + // The load option resides in a RAM disk. + // + FileBuffer = AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize)); + if (FileBuffer == NULL) { + DEBUG_CODE_BEGIN (); + EFI_DEVICE_PATH *LoadFilePath; + CHAR16 *LoadFileText; + CHAR16 *FileText; + + LoadFilePath = DevicePathFromHandle (LoadFileHandle); + if (LoadFilePath == NULL) { + LoadFileText = NULL; + } else { + LoadFileText = ConvertDevicePathToText (LoadFilePath, FALSE, FALSE); + } + + FileText = ConvertDevicePathToText (FilePath, FALSE, FALSE); + + DEBUG (( + DEBUG_ERROR, + "%a:%a: failed to allocate reserved pages: " + "BufferSize=%Lu LoadFile=\"%s\" FilePath=\"%s\"\n", + gEfiCallerBaseName, + __func__, + (UINT64)BufferSize, + LoadFileText, + FileText + )); + + if (FileText != NULL) { + FreePool (FileText); + } + + if (LoadFileText != NULL) { + FreePool (LoadFileText); + } + + DEBUG_CODE_END (); + return NULL; + } + + Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer); + if (EFI_ERROR (Status)) { + FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize)); + return NULL; + } + + FullPath = BmExpandNetworkFileSystem (LoadFileHandle, &RamDiskHandle); + if (FullPath == NULL) { + // + // Free the memory occupied by the RAM disk if there is no BlockIo or SimpleFileSystem instance. + // + BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle)); + } + + return FullPath; +} + +/** + Return the full device path pointing to the load option. + + FilePath may: + 1. Exactly matches to a LoadFile instance. + 2. Cannot match to any LoadFile instance. Wide match is required. + In either case, the routine may return: + 1. A copy of FilePath when FilePath matches to a LoadFile instance and + the LoadFile returns a load option buffer. + 2. A new device path with IP and URI information updated when wide match + happens. + 3. A new device path pointing to a load option in RAM disk. + In either case, only one full device path is returned for a specified + FilePath. + + @param FilePath The media device path pointing to a LoadFile instance. + + @return The load option buffer. +**/ +EFI_DEVICE_PATH_PROTOCOL * +BmExpandLoadFiles ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_HANDLE *Handles; + UINTN HandleCount; + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *Node; + + // + // Get file buffer from load file instance. + // + Node = FilePath; + Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle); + if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) { + // + // When wide match happens, pass full device path to LoadFile (), + // otherwise, pass remaining device path to LoadFile (). + // + FilePath = Node; + } else { + Handle = NULL; + // + // Use wide match algorithm to find one when + // cannot find a LoadFile instance to exactly match the FilePath + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiLoadFileProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + if (EFI_ERROR (Status)) { + Handles = NULL; + HandleCount = 0; + } + + for (Index = 0; Index < HandleCount; Index++) { + if (BmMatchHttpBootDevicePath (DevicePathFromHandle (Handles[Index]), FilePath)) { + Handle = Handles[Index]; + break; + } + } + + if (Handles != NULL) { + FreePool (Handles); + } + } + + if (Handle == NULL) { + return NULL; + } + + return BmExpandLoadFile (Handle, FilePath); +} + +/** + Get the load option by its device path. + + @param FilePath The device path pointing to a load option. + It could be a short-form device path. + @param FullPath Return the full device path of the load option after + short-form device path expanding. + Caller is responsible to free it. + @param FileSize Return the load option size. + + @return The load option buffer. Caller is responsible to free the memory. +**/ +VOID * +EFIAPI +EfiBootManagerGetLoadOptionBuffer ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, + OUT UINTN *FileSize + ) +{ + *FullPath = NULL; + + EfiBootManagerConnectDevicePath (FilePath, NULL); + return BmGetNextLoadOptionBuffer (LoadOptionTypeMax, FilePath, FullPath, FileSize); +} + +/** + Get the next possible full path pointing to the load option. + The routine doesn't guarantee the returned full path points to an existing + file, and it also doesn't guarantee the existing file is a valid load option. + BmGetNextLoadOptionBuffer() guarantees. + + @param FilePath The device path pointing to a load option. + It could be a short-form device path. + @param FullPath The full path returned by the routine in last call. + Set to NULL in first call. + + @return The next possible full path pointing to the load option. + Caller is responsible to free the memory. +**/ +EFI_DEVICE_PATH_PROTOCOL * +BmGetNextLoadOptionDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN EFI_DEVICE_PATH_PROTOCOL *FullPath + ) +{ + EFI_HANDLE Handle; + EFI_DEVICE_PATH_PROTOCOL *Node; + EFI_STATUS Status; + + ASSERT (FilePath != NULL); + + // + // Boot from media device by adding a default file name \EFI\BOOT\BOOT{machine type short-name}.EFI + // + Node = FilePath; + Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle); + if (EFI_ERROR (Status)) { + Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &Node, &Handle); + } + + if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) { + return BmExpandMediaDevicePath (FilePath, FullPath); + } + + // + // Expand the short-form device path to full device path + // + if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) && + (DevicePathSubType (FilePath) == MEDIA_HARDDRIVE_DP)) + { + // + // Expand the Harddrive device path + // + if (FullPath == NULL) { + return BmExpandPartitionDevicePath (FilePath); + } else { + return NULL; + } + } else if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) && + (DevicePathSubType (FilePath) == MEDIA_FILEPATH_DP)) + { + // + // Expand the File-path device path + // + return BmExpandFileDevicePath (FilePath, FullPath); + } else if ((DevicePathType (FilePath) == MESSAGING_DEVICE_PATH) && + (DevicePathSubType (FilePath) == MSG_URI_DP)) + { + // + // Expand the URI device path + // + return BmExpandUriDevicePath (FilePath, FullPath); + } else { + Node = FilePath; + Status = gBS->LocateDevicePath (&gEfiUsbIoProtocolGuid, &Node, &Handle); + if (EFI_ERROR (Status)) { + // + // Only expand the USB WWID/Class device path + // when FilePath doesn't point to a physical UsbIo controller. + // Otherwise, infinite recursion will happen. + // + for (Node = FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) { + if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) && + ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) || (DevicePathSubType (Node) == MSG_USB_WWID_DP))) + { + break; + } + } + + // + // Expand the USB WWID/Class device path + // + if (!IsDevicePathEnd (Node)) { + if (FilePath == Node) { + // + // Boot Option device path starts with USB Class or USB WWID device path. + // For Boot Option device path which doesn't begin with the USB Class or + // USB WWID device path, it's not needed to connect again here. + // + BmConnectUsbShortFormDevicePath (FilePath); + } + + return BmExpandUsbDevicePath (FilePath, FullPath, Node); + } + } + } + + // + // For the below cases, FilePath only expands to one Full path. + // So just handle the case when FullPath == NULL. + // + if (FullPath != NULL) { + return NULL; + } + + // + // Load option resides in FV. + // + if (BmIsFvFilePath (FilePath)) { + return BmAdjustFvFilePath (FilePath); + } + + // + // Load option resides in Simple File System. + // + Node = FilePath; + Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle); + if (!EFI_ERROR (Status)) { + return DuplicateDevicePath (FilePath); + } + + // + // Last chance to try: Load option may be loaded through LoadFile. + // + return BmExpandLoadFiles (FilePath); +} + +/** + Check if it's a Device Path pointing to BootManagerMenu. + + @param DevicePath Input device path. + + @retval TRUE The device path is BootManagerMenu File Device Path. + @retval FALSE The device path is NOT BootManagerMenu File Device Path. +**/ +BOOLEAN +BmIsBootManagerMenuFilePath ( + EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_HANDLE FvHandle; + VOID *NameGuid; + EFI_STATUS Status; + + Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &DevicePath, &FvHandle); + if (!EFI_ERROR (Status)) { + NameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)DevicePath); + if (NameGuid != NULL) { + return CompareGuid (NameGuid, PcdGetPtr (PcdBootManagerMenuFile)); + } + } + + return FALSE; +} + +/** + Report status code with EFI_RETURN_STATUS_EXTENDED_DATA about LoadImage() or + StartImage() failure. + + @param[in] ErrorCode An Error Code in the Software Class, DXE Boot + Service Driver Subclass. ErrorCode will be used to + compose the Value parameter for status code + reporting. Must be one of + EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR and + EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED. + + @param[in] FailureStatus The failure status returned by the boot service + that should be reported. +**/ +VOID +BmReportLoadFailure ( + IN UINT32 ErrorCode, + IN EFI_STATUS FailureStatus + ) +{ + EFI_RETURN_STATUS_EXTENDED_DATA ExtendedData; + + if (!ReportErrorCodeEnabled ()) { + return; + } + + ASSERT ( + (ErrorCode == EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR) || + (ErrorCode == EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED) + ); + + ZeroMem (&ExtendedData, sizeof (ExtendedData)); + ExtendedData.ReturnStatus = FailureStatus; + + REPORT_STATUS_CODE_EX ( + (EFI_ERROR_CODE | EFI_ERROR_MINOR), + (EFI_SOFTWARE_DXE_BS_DRIVER | ErrorCode), + 0, + NULL, + NULL, + &ExtendedData.DataHeader + 1, + sizeof (ExtendedData) - sizeof (ExtendedData.DataHeader) + ); +} + +/** + Attempt to boot the EFI boot option. This routine sets L"BootCurent" and + also signals the EFI ready to boot event. If the device path for the option + starts with a BBS device path a legacy boot is attempted via the registered + gLegacyBoot function. Short form device paths are also supported via this + rountine. A device path starting with MEDIA_HARDDRIVE_DP, MSG_USB_WWID_DP, + MSG_USB_CLASS_DP gets expaned out to find the first device that matches. + If the BootOption Device Path fails the removable media boot algorithm + is attempted (\EFI\BOOTIA32.EFI, \EFI\BOOTX64.EFI,... only one file type + is tried per processor type) + + @param BootOption Boot Option to try and boot. + On return, BootOption->Status contains the boot status. + EFI_SUCCESS BootOption was booted + EFI_UNSUPPORTED A BBS device path was found with no valid callback + registered via EfiBootManagerInitialize(). + EFI_NOT_FOUND The BootOption was not found on the system + !EFI_SUCCESS BootOption failed with this error status + +**/ +VOID +EFIAPI +EfiBootManagerBoot ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption + ) +{ + EFI_STATUS Status; + EFI_HANDLE ImageHandle; + EFI_LOADED_IMAGE_PROTOCOL *ImageInfo; + UINT16 Uint16; + UINTN OptionNumber; + UINTN OriginalOptionNumber; + EFI_DEVICE_PATH_PROTOCOL *FilePath; + EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath; + VOID *FileBuffer; + UINTN FileSize; + EFI_BOOT_LOGO_PROTOCOL *BootLogo; + EFI_EVENT LegacyBootEvent; + + if (BootOption == NULL) { + return; + } + + if ((BootOption->FilePath == NULL) || (BootOption->OptionType != LoadOptionTypeBoot)) { + BootOption->Status = EFI_INVALID_PARAMETER; + return; + } + + // + // 1. Create Boot#### for a temporary boot if there is no match Boot#### (i.e. a boot by selected a EFI Shell using "Boot From File") + // + OptionNumber = BmFindBootOptionInVariable (BootOption); + if (OptionNumber == LoadOptionNumberUnassigned) { + Status = BmGetFreeOptionNumber (LoadOptionTypeBoot, &Uint16); + if (!EFI_ERROR (Status)) { + // + // Save the BootOption->OptionNumber to restore later + // + OptionNumber = Uint16; + OriginalOptionNumber = BootOption->OptionNumber; + BootOption->OptionNumber = OptionNumber; + Status = EfiBootManagerLoadOptionToVariable (BootOption); + BootOption->OptionNumber = OriginalOptionNumber; + } + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status)); + BootOption->Status = Status; + return; + } + } + + // + // 2. Set BootCurrent + // + Uint16 = (UINT16)OptionNumber; + BmSetVariableAndReportStatusCodeOnError ( + L"BootCurrent", + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof (UINT16), + &Uint16 + ); + + // + // 3. Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute + // the boot option. + // + if (BmIsBootManagerMenuFilePath (BootOption->FilePath)) { + DEBUG ((DEBUG_INFO, "[Bds] Booting Boot Manager Menu.\n")); + BmStopHotkeyService (NULL, NULL); + } else { + EfiSignalEventReadyToBoot (); + // + // Report Status Code to indicate ReadyToBoot was signalled + // + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT)); + // + // 4. Repair system through DriverHealth protocol + // + BmRepairAllControllers (0); + } + + PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32)OptionNumber); + + // + // 5. Adjust the different type memory page number just before booting + // and save the updated info into the variable for next boot to use + // + BmSetMemoryTypeInformationVariable ( + (BOOLEAN)((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT) + ); + + // + // 6. Load EFI boot option to ImageHandle + // + DEBUG_CODE_BEGIN (); + if (BootOption->Description == NULL) { + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting from unknown device path\n")); + } else { + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting %s\n", BootOption->Description)); + } + + DEBUG_CODE_END (); + + ImageHandle = NULL; + RamDiskDevicePath = NULL; + if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) { + Status = EFI_NOT_FOUND; + FilePath = NULL; + EfiBootManagerConnectDevicePath (BootOption->FilePath, NULL); + FileBuffer = BmGetNextLoadOptionBuffer (LoadOptionTypeBoot, BootOption->FilePath, &FilePath, &FileSize); + if (FileBuffer != NULL) { + RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath); + + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad)); + Status = gBS->LoadImage ( + TRUE, + gImageHandle, + FilePath, + FileBuffer, + FileSize, + &ImageHandle + ); + } + + if (FileBuffer != NULL) { + FreePool (FileBuffer); + } + + if (FilePath != NULL) { + FreePool (FilePath); + } + + if (EFI_ERROR (Status)) { + // + // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created + // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now. + // If the caller doesn't have the option to defer the execution of an image, we should + // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak. + // + if (Status == EFI_SECURITY_VIOLATION) { + gBS->UnloadImage (ImageHandle); + } + + // + // Destroy the RAM disk + // + if (RamDiskDevicePath != NULL) { + BmDestroyRamDisk (RamDiskDevicePath); + FreePool (RamDiskDevicePath); + } + + // + // Report Status Code with the failure status to indicate that the failure to load boot option + // + BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR, Status); + BootOption->Status = Status; + return; + } + } + + // + // Check to see if we should legacy BOOT. If yes then do the legacy boot + // Write boot to OS performance data for Legacy boot + // + if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) { + if (mBmLegacyBoot != NULL) { + // + // Write boot to OS performance data for legacy boot. + // + PERF_CODE ( + // + // Create an event to be signalled when Legacy Boot occurs to write performance data. + // + Status = EfiCreateEventLegacyBootEx ( + TPL_NOTIFY, + BmEndOfBdsPerfCode, + NULL, + &LegacyBootEvent + ); + ASSERT_EFI_ERROR (Status); + ); + + mBmLegacyBoot (BootOption); + } else { + BootOption->Status = EFI_UNSUPPORTED; + } + + PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32)OptionNumber); + return; + } + + // + // Provide the image with its load options + // + Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&ImageInfo); + ASSERT_EFI_ERROR (Status); + + if (!BmIsAutoCreateBootOption (BootOption)) { + ImageInfo->LoadOptionsSize = BootOption->OptionalDataSize; + ImageInfo->LoadOptions = BootOption->OptionalData; + } + + // + // Clean to NULL because the image is loaded directly from the firmwares boot manager. + // + ImageInfo->ParentHandle = NULL; + + // + // Before calling the image, enable the Watchdog Timer for 5 minutes period + // + gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL); + + // + // Write boot to OS performance data for UEFI boot + // + PERF_CODE ( + BmEndOfBdsPerfCode (NULL, NULL); + ); + + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart)); + + Status = gBS->StartImage (ImageHandle, &BootOption->ExitDataSize, &BootOption->ExitData); + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status)); + BootOption->Status = Status; + + // + // Destroy the RAM disk + // + if (RamDiskDevicePath != NULL) { + BmDestroyRamDisk (RamDiskDevicePath); + FreePool (RamDiskDevicePath); + } + + if (EFI_ERROR (Status)) { + // + // Report Status Code with the failure status to indicate that boot failure + // + BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED, Status); + } + + PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32)OptionNumber); + + // + // Clear the Watchdog Timer after the image returns + // + gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL); + + // + // Set Logo status invalid after trying one boot option + // + BootLogo = NULL; + Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **)&BootLogo); + if (!EFI_ERROR (Status) && (BootLogo != NULL)) { + Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0); + ASSERT_EFI_ERROR (Status); + } + + // + // Clear Boot Current + // + Status = gRT->SetVariable ( + L"BootCurrent", + &gEfiGlobalVariableGuid, + 0, + 0, + NULL + ); + // + // Deleting variable with current variable implementation shouldn't fail. + // When BootXXXX (e.g.: BootManagerMenu) boots BootYYYY, exiting BootYYYY causes BootCurrent deleted, + // exiting BootXXXX causes deleting BootCurrent returns EFI_NOT_FOUND. + // + ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND); +} + +/** + Check whether there is a instance in BlockIoDevicePath, which contain multi device path + instances, has the same partition node with HardDriveDevicePath device path + + @param BlockIoDevicePath Multi device path instances which need to check + @param HardDriveDevicePath A device path which starts with a hard drive media + device path. + + @retval TRUE There is a matched device path instance. + @retval FALSE There is no matched device path instance. + +**/ +BOOLEAN +BmMatchPartitionDevicePathNode ( + IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath, + IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath + ) +{ + HARDDRIVE_DEVICE_PATH *Node; + + if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) { + return FALSE; + } + + // + // Match all the partition device path nodes including the nested partition nodes + // + while (!IsDevicePathEnd (BlockIoDevicePath)) { + if ((DevicePathType (BlockIoDevicePath) == MEDIA_DEVICE_PATH) && + (DevicePathSubType (BlockIoDevicePath) == MEDIA_HARDDRIVE_DP) + ) + { + // + // See if the harddrive device path in blockio matches the orig Hard Drive Node + // + Node = (HARDDRIVE_DEVICE_PATH *)BlockIoDevicePath; + + // + // Match Signature and PartitionNumber. + // Unused bytes in Signature are initiaized with zeros. + // + if ((Node->PartitionNumber == HardDriveDevicePath->PartitionNumber) && + (Node->MBRType == HardDriveDevicePath->MBRType) && + (Node->SignatureType == HardDriveDevicePath->SignatureType) && + (CompareMem (Node->Signature, HardDriveDevicePath->Signature, sizeof (Node->Signature)) == 0)) + { + return TRUE; + } + } + + BlockIoDevicePath = NextDevicePathNode (BlockIoDevicePath); + } + + return FALSE; +} + +/** + Emuerate all possible bootable medias in the following order: + 1. Removable BlockIo - The boot option only points to the removable media + device, like USB key, DVD, Floppy etc. + 2. Fixed BlockIo - The boot option only points to a Fixed blockIo device, + like HardDisk. + 3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting + SimpleFileSystem Protocol, but not supporting BlockIo + protocol. + 4. LoadFile - The boot option points to the media supporting + LoadFile protocol. + Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior + + @param BootOptionCount Return the boot option count which has been found. + + @retval Pointer to the boot option array. +**/ +EFI_BOOT_MANAGER_LOAD_OPTION * +BmEnumerateBootOptions ( + UINTN *BootOptionCount + ) +{ + EFI_STATUS Status; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINTN HandleCount; + EFI_HANDLE *Handles; + EFI_BLOCK_IO_PROTOCOL *BlkIo; + UINTN Removable; + UINTN Index; + CHAR16 *Description; + + ASSERT (BootOptionCount != NULL); + + *BootOptionCount = 0; + BootOptions = NULL; + + // + // Parse removable block io followed by fixed block io + // + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiBlockIoProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + + for (Removable = 0; Removable < 2; Removable++) { + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + Handles[Index], + &gEfiBlockIoProtocolGuid, + (VOID **)&BlkIo + ); + if (EFI_ERROR (Status)) { + continue; + } + + // + // Skip the logical partitions + // + if (BlkIo->Media->LogicalPartition) { + continue; + } + + // + // Skip the fixed block io then the removable block io + // + if (BlkIo->Media->RemovableMedia == ((Removable == 0) ? FALSE : TRUE)) { + continue; + } + + // + // Skip removable media if not present + // + if ((BlkIo->Media->RemovableMedia == TRUE) && + (BlkIo->Media->MediaPresent == FALSE)) + { + continue; + } + + Description = BmGetBootDescription (Handles[Index]); + BootOptions = ReallocatePool ( + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), + BootOptions + ); + ASSERT (BootOptions != NULL); + + Status = EfiBootManagerInitializeLoadOption ( + &BootOptions[(*BootOptionCount)++], + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + LOAD_OPTION_ACTIVE, + Description, + DevicePathFromHandle (Handles[Index]), + NULL, + 0 + ); + ASSERT_EFI_ERROR (Status); + + FreePool (Description); + } + } + + if (HandleCount != 0) { + FreePool (Handles); + } + + // + // Parse simple file system not based on block io + // + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleFileSystemProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + Handles[Index], + &gEfiBlockIoProtocolGuid, + (VOID **)&BlkIo + ); + if (!EFI_ERROR (Status)) { + // + // Skip if the file system handle supports a BlkIo protocol, which we've handled in above + // + continue; + } + + Description = BmGetBootDescription (Handles[Index]); + BootOptions = ReallocatePool ( + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), + BootOptions + ); + ASSERT (BootOptions != NULL); + + Status = EfiBootManagerInitializeLoadOption ( + &BootOptions[(*BootOptionCount)++], + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + LOAD_OPTION_ACTIVE, + Description, + DevicePathFromHandle (Handles[Index]), + NULL, + 0 + ); + ASSERT_EFI_ERROR (Status); + FreePool (Description); + } + + if (HandleCount != 0) { + FreePool (Handles); + } + + // + // Parse load file protocol + // + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiLoadFileProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + for (Index = 0; Index < HandleCount; Index++) { + // + // Ignore BootManagerMenu. its boot option will be created by EfiBootManagerGetBootManagerMenu(). + // + if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) { + continue; + } + + Description = BmGetBootDescription (Handles[Index]); + BootOptions = ReallocatePool ( + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), + BootOptions + ); + ASSERT (BootOptions != NULL); + + Status = EfiBootManagerInitializeLoadOption ( + &BootOptions[(*BootOptionCount)++], + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + LOAD_OPTION_ACTIVE, + Description, + DevicePathFromHandle (Handles[Index]), + NULL, + 0 + ); + ASSERT_EFI_ERROR (Status); + FreePool (Description); + } + + if (HandleCount != 0) { + FreePool (Handles); + } + + BmMakeBootOptionDescriptionUnique (BootOptions, *BootOptionCount); + return BootOptions; +} + +/** + The function enumerates all boot options, creates them and registers them in the BootOrder variable. +**/ +VOID +EFIAPI +EfiBootManagerRefreshAllBootOption ( + VOID + ) +{ + EFI_STATUS Status; + EFI_BOOT_MANAGER_LOAD_OPTION *NvBootOptions; + UINTN NvBootOptionCount; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINTN BootOptionCount; + EFI_BOOT_MANAGER_LOAD_OPTION *UpdatedBootOptions; + UINTN UpdatedBootOptionCount; + UINTN Index; + EDKII_PLATFORM_BOOT_MANAGER_PROTOCOL *PlatformBootManager; + + // + // Optionally refresh the legacy boot option + // + if (mBmRefreshLegacyBootOption != NULL) { + mBmRefreshLegacyBootOption (); + } + + BootOptions = BmEnumerateBootOptions (&BootOptionCount); + + // + // Mark the boot option as added by BDS by setting OptionalData to a special GUID + // + for (Index = 0; Index < BootOptionCount; Index++) { + BootOptions[Index].OptionalData = AllocateCopyPool (sizeof (EFI_GUID), &mBmAutoCreateBootOptionGuid); + BootOptions[Index].OptionalDataSize = sizeof (EFI_GUID); + } + + // + // Locate Platform Boot Options Protocol + // + Status = gBS->LocateProtocol ( + &gEdkiiPlatformBootManagerProtocolGuid, + NULL, + (VOID **)&PlatformBootManager + ); + if (!EFI_ERROR (Status)) { + // + // If found, call platform specific refresh to all auto enumerated and NV + // boot options. + // + Status = PlatformBootManager->RefreshAllBootOptions ( + (CONST EFI_BOOT_MANAGER_LOAD_OPTION *)BootOptions, + (CONST UINTN)BootOptionCount, + &UpdatedBootOptions, + &UpdatedBootOptionCount + ); + if (!EFI_ERROR (Status)) { + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); + BootOptions = UpdatedBootOptions; + BootOptionCount = UpdatedBootOptionCount; + } + } + + NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot); + + // + // Remove invalid EFI boot options from NV + // + for (Index = 0; Index < NvBootOptionCount; Index++) { + if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) || + (DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP) + ) && BmIsAutoCreateBootOption (&NvBootOptions[Index]) + ) + { + // + // Only check those added by BDS + // so that the boot options added by end-user or OS installer won't be deleted + // + if (EfiBootManagerFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == -1) { + Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot); + // + // Deleting variable with current variable implementation shouldn't fail. + // + ASSERT_EFI_ERROR (Status); + } + } + } + + // + // Add new EFI boot options to NV + // + for (Index = 0; Index < BootOptionCount; Index++) { + if (EfiBootManagerFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == -1) { + EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN)-1); + // + // Try best to add the boot options so continue upon failure. + // + } + } + + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); + EfiBootManagerFreeLoadOptions (NvBootOptions, NvBootOptionCount); +} + +/** + This function is called to get or create the boot option for the Boot Manager Menu. + + The Boot Manager Menu is shown after successfully booting a boot option. + This function will first try to search the BootManagerMenuFile is in the same FV as + the module links to this library. If fails, it will search in all FVs. + + @param BootOption Return the boot option of the Boot Manager Menu + + @retval EFI_SUCCESS Successfully register the Boot Manager Menu. + @retval EFI_NOT_FOUND The Boot Manager Menu cannot be found. + @retval others Return status of gRT->SetVariable (). BootOption still points + to the Boot Manager Menu even the Status is not EFI_SUCCESS + and EFI_NOT_FOUND. +**/ +EFI_STATUS +BmRegisterBootManagerMenu ( + OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption + ) +{ + EFI_STATUS Status; + CHAR16 *Description; + UINTN DescriptionLength; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINTN HandleCount; + EFI_HANDLE *Handles; + UINTN Index; + + DevicePath = NULL; + Description = NULL; + // + // Try to find BootManagerMenu from LoadFile protocol + // + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiLoadFileProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + for (Index = 0; Index < HandleCount; Index++) { + if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) { + DevicePath = DuplicateDevicePath (DevicePathFromHandle (Handles[Index])); + Description = BmGetBootDescription (Handles[Index]); + break; + } + } + + if (HandleCount != 0) { + FreePool (Handles); + } + + if (DevicePath == NULL) { + Status = GetFileDevicePathFromAnyFv ( + PcdGetPtr (PcdBootManagerMenuFile), + EFI_SECTION_PE32, + 0, + &DevicePath + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "[Bds]BootManagerMenu FFS section can not be found, skip its boot option registration\n")); + return EFI_NOT_FOUND; + } + + ASSERT (DevicePath != NULL); + // + // Get BootManagerMenu application's description from EFI User Interface Section. + // + Status = GetSectionFromAnyFv ( + PcdGetPtr (PcdBootManagerMenuFile), + EFI_SECTION_USER_INTERFACE, + 0, + (VOID **)&Description, + &DescriptionLength + ); + if (EFI_ERROR (Status)) { + Description = NULL; + } + } + + Status = EfiBootManagerInitializeLoadOption ( + BootOption, + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN, + (Description != NULL) ? Description : L"Boot Manager Menu", + DevicePath, + NULL, + 0 + ); + ASSERT_EFI_ERROR (Status); + FreePool (DevicePath); + if (Description != NULL) { + FreePool (Description); + } + + DEBUG_CODE ( + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINTN BootOptionCount; + + BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); + ASSERT (EfiBootManagerFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1); + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); + ); + + return EfiBootManagerAddLoadOptionVariable (BootOption, (UINTN)-1); +} + +/** + Return the boot option corresponding to the Boot Manager Menu. + It may automatically create one if the boot option hasn't been created yet. + + @param BootOption Return the Boot Manager Menu. + + @retval EFI_SUCCESS The Boot Manager Menu is successfully returned. + @retval EFI_NOT_FOUND The Boot Manager Menu cannot be found. + @retval others Return status of gRT->SetVariable (). BootOption still points + to the Boot Manager Menu even the Status is not EFI_SUCCESS + and EFI_NOT_FOUND. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerGetBootManagerMenu ( + EFI_BOOT_MANAGER_LOAD_OPTION *BootOption + ) +{ + EFI_STATUS Status; + UINTN BootOptionCount; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINTN Index; + + BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); + + for (Index = 0; Index < BootOptionCount; Index++) { + if (BmIsBootManagerMenuFilePath (BootOptions[Index].FilePath)) { + Status = EfiBootManagerInitializeLoadOption ( + BootOption, + BootOptions[Index].OptionNumber, + BootOptions[Index].OptionType, + BootOptions[Index].Attributes, + BootOptions[Index].Description, + BootOptions[Index].FilePath, + BootOptions[Index].OptionalData, + BootOptions[Index].OptionalDataSize + ); + ASSERT_EFI_ERROR (Status); + break; + } + } + + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); + + // + // Automatically create the Boot#### for Boot Manager Menu when not found. + // + if (Index == BootOptionCount) { + return BmRegisterBootManagerMenu (BootOption); + } else { + return EFI_SUCCESS; + } +} + +/** + Get the next possible full path pointing to the load option. + The routine doesn't guarantee the returned full path points to an existing + file, and it also doesn't guarantee the existing file is a valid load option. + BmGetNextLoadOptionBuffer() guarantees. + + @param FilePath The device path pointing to a load option. + It could be a short-form device path. + @param FullPath The full path returned by the routine in last call. + Set to NULL in first call. + + @return The next possible full path pointing to the load option. + Caller is responsible to free the memory. +**/ +EFI_DEVICE_PATH_PROTOCOL * +EFIAPI +EfiBootManagerGetNextLoadOptionDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN EFI_DEVICE_PATH_PROTOCOL *FullPath + ) +{ + return BmGetNextLoadOptionDevicePath (FilePath, FullPath); +} diff --git a/Platform/OpenNetworkBoot/BmBootDescription.c b/Platform/OpenNetworkBoot/BmBootDescription.c new file mode 100644 index 00000000000..480f0931e0c --- /dev/null +++ b/Platform/OpenNetworkBoot/BmBootDescription.c @@ -0,0 +1,911 @@ +/** @file + Library functions which relate with boot option description. + +Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+(C) Copyright 2015 Hewlett Packard Enterprise Development LP
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "InternalBm.h" + +#define VENDOR_IDENTIFICATION_OFFSET 3 +#define VENDOR_IDENTIFICATION_LENGTH 8 +#define PRODUCT_IDENTIFICATION_OFFSET 11 +#define PRODUCT_IDENTIFICATION_LENGTH 16 + +CONST UINT16 mBmUsbLangId = 0x0409; // English +CHAR16 mBmUefiPrefix[] = L"UEFI "; + +LIST_ENTRY mPlatformBootDescriptionHandlers = INITIALIZE_LIST_HEAD_VARIABLE (mPlatformBootDescriptionHandlers); + +/** + For a bootable Device path, return its boot type. + + @param DevicePath The bootable device Path to check + + @retval AcpiFloppyBoot If given device path contains ACPI_DEVICE_PATH type device path node + which HID is floppy device. + @retval MessageAtapiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node + and its last device path node's subtype is MSG_ATAPI_DP. + @retval MessageSataBoot If given device path contains MESSAGING_DEVICE_PATH type device path node + and its last device path node's subtype is MSG_SATA_DP. + @retval MessageScsiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node + and its last device path node's subtype is MSG_SCSI_DP. + @retval MessageUsbBoot If given device path contains MESSAGING_DEVICE_PATH type device path node + and its last device path node's subtype is MSG_USB_DP. + @retval BmMiscBoot If tiven device path doesn't match the above condition. + +**/ +BM_BOOT_TYPE +BmDevicePathType ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_DEVICE_PATH_PROTOCOL *Node; + EFI_DEVICE_PATH_PROTOCOL *NextNode; + + ASSERT (DevicePath != NULL); + + for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) { + switch (DevicePathType (Node)) { + case ACPI_DEVICE_PATH: + if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH *)Node)->HID) == 0x0604) { + return BmAcpiFloppyBoot; + } + + break; + + case HARDWARE_DEVICE_PATH: + if (DevicePathSubType (Node) == HW_CONTROLLER_DP) { + return BmHardwareDeviceBoot; + } + + break; + + case MESSAGING_DEVICE_PATH: + // + // Skip LUN device node + // + NextNode = Node; + do { + NextNode = NextDevicePathNode (NextNode); + } while ( + (DevicePathType (NextNode) == MESSAGING_DEVICE_PATH) && + (DevicePathSubType (NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP) + ); + + // + // If the device path not only point to driver device, it is not a messaging device path, + // + if (!IsDevicePathEndType (NextNode)) { + continue; + } + + switch (DevicePathSubType (Node)) { + case MSG_ATAPI_DP: + return BmMessageAtapiBoot; + break; + + case MSG_SATA_DP: + return BmMessageSataBoot; + break; + + case MSG_USB_DP: + return BmMessageUsbBoot; + break; + + case MSG_SCSI_DP: + return BmMessageScsiBoot; + break; + } + } + } + + return BmMiscBoot; +} + +/** + Eliminate the extra spaces in the Str to one space. + + @param Str Input string info. +**/ +VOID +BmEliminateExtraSpaces ( + IN CHAR16 *Str + ) +{ + UINTN Index; + UINTN ActualIndex; + + for (Index = 0, ActualIndex = 0; Str[Index] != L'\0'; Index++) { + if ((Str[Index] != L' ') || ((ActualIndex > 0) && (Str[ActualIndex - 1] != L' '))) { + Str[ActualIndex++] = Str[Index]; + } + } + + Str[ActualIndex] = L'\0'; +} + +/** + Try to get the controller's ATA/ATAPI description. + + @param Handle Controller handle. + + @return The description string. +**/ +CHAR16 * +BmGetDescriptionFromDiskInfo ( + IN EFI_HANDLE Handle + ) +{ + UINTN Index; + EFI_STATUS Status; + EFI_DISK_INFO_PROTOCOL *DiskInfo; + UINT32 BufferSize; + EFI_ATAPI_IDENTIFY_DATA IdentifyData; + EFI_SCSI_INQUIRY_DATA InquiryData; + CHAR16 *Description; + UINTN Length; + CONST UINTN ModelNameLength = 40; + CONST UINTN SerialNumberLength = 20; + CHAR8 *StrPtr; + UINT8 Temp; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + Description = NULL; + + Status = gBS->HandleProtocol ( + Handle, + &gEfiDiskInfoProtocolGuid, + (VOID **)&DiskInfo + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoAhciInterfaceGuid) || + CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) + { + BufferSize = sizeof (EFI_ATAPI_IDENTIFY_DATA); + Status = DiskInfo->Identify ( + DiskInfo, + &IdentifyData, + &BufferSize + ); + if (!EFI_ERROR (Status)) { + Description = AllocateZeroPool ((ModelNameLength + SerialNumberLength + 2) * sizeof (CHAR16)); + ASSERT (Description != NULL); + for (Index = 0; Index + 1 < ModelNameLength; Index += 2) { + Description[Index] = (CHAR16)IdentifyData.ModelName[Index + 1]; + Description[Index + 1] = (CHAR16)IdentifyData.ModelName[Index]; + } + + Length = Index; + Description[Length++] = L' '; + + for (Index = 0; Index + 1 < SerialNumberLength; Index += 2) { + Description[Length + Index] = (CHAR16)IdentifyData.SerialNo[Index + 1]; + Description[Length + Index + 1] = (CHAR16)IdentifyData.SerialNo[Index]; + } + + Length += Index; + Description[Length++] = L'\0'; + ASSERT (Length == ModelNameLength + SerialNumberLength + 2); + + BmEliminateExtraSpaces (Description); + } + } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoScsiInterfaceGuid) || + CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoUfsInterfaceGuid)) + { + BufferSize = sizeof (EFI_SCSI_INQUIRY_DATA); + Status = DiskInfo->Inquiry ( + DiskInfo, + &InquiryData, + &BufferSize + ); + if (!EFI_ERROR (Status)) { + Description = AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2) * sizeof (CHAR16)); + ASSERT (Description != NULL); + + // + // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification + // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification, + // Here combine the vendor identification and product identification to the description. + // + StrPtr = (CHAR8 *)(&InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET]); + Temp = StrPtr[VENDOR_IDENTIFICATION_LENGTH]; + StrPtr[VENDOR_IDENTIFICATION_LENGTH] = '\0'; + AsciiStrToUnicodeStrS (StrPtr, Description, VENDOR_IDENTIFICATION_LENGTH + 1); + StrPtr[VENDOR_IDENTIFICATION_LENGTH] = Temp; + + // + // Add one space at the middle of vendor information and product information. + // + Description[VENDOR_IDENTIFICATION_LENGTH] = L' '; + + StrPtr = (CHAR8 *)(&InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET]); + StrPtr[PRODUCT_IDENTIFICATION_LENGTH] = '\0'; + AsciiStrToUnicodeStrS (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1, PRODUCT_IDENTIFICATION_LENGTH + 1); + + BmEliminateExtraSpaces (Description); + } + } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoSdMmcInterfaceGuid)) { + DevicePath = DevicePathFromHandle (Handle); + if (DevicePath == NULL) { + return NULL; + } + + while (!IsDevicePathEnd (DevicePath) && (DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH)) { + DevicePath = NextDevicePathNode (DevicePath); + } + + if (IsDevicePathEnd (DevicePath)) { + return NULL; + } + + if (DevicePathSubType (DevicePath) == MSG_SD_DP) { + Description = L"SD Device"; + } else if (DevicePathSubType (DevicePath) == MSG_EMMC_DP) { + Description = L"eMMC Device"; + } else { + return NULL; + } + + Description = AllocateCopyPool (StrSize (Description), Description); + } + + return Description; +} + +/** + Try to get the controller's USB description. + + @param Handle Controller handle. + + @return The description string. +**/ +CHAR16 * +BmGetUsbDescription ( + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + EFI_USB_IO_PROTOCOL *UsbIo; + CHAR16 NullChar; + CHAR16 *Manufacturer; + CHAR16 *Product; + CHAR16 *SerialNumber; + CHAR16 *Description; + EFI_USB_DEVICE_DESCRIPTOR DevDesc; + UINTN DescMaxSize; + + Status = gBS->HandleProtocol ( + Handle, + &gEfiUsbIoProtocolGuid, + (VOID **)&UsbIo + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + NullChar = L'\0'; + + Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); + if (EFI_ERROR (Status)) { + return NULL; + } + + Status = UsbIo->UsbGetStringDescriptor ( + UsbIo, + mBmUsbLangId, + DevDesc.StrManufacturer, + &Manufacturer + ); + if (EFI_ERROR (Status)) { + Manufacturer = &NullChar; + } + + Status = UsbIo->UsbGetStringDescriptor ( + UsbIo, + mBmUsbLangId, + DevDesc.StrProduct, + &Product + ); + if (EFI_ERROR (Status)) { + Product = &NullChar; + } + + Status = UsbIo->UsbGetStringDescriptor ( + UsbIo, + mBmUsbLangId, + DevDesc.StrSerialNumber, + &SerialNumber + ); + if (EFI_ERROR (Status)) { + SerialNumber = &NullChar; + } + + if ((Manufacturer == &NullChar) && + (Product == &NullChar) && + (SerialNumber == &NullChar) + ) + { + return NULL; + } + + DescMaxSize = StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber); + Description = AllocateZeroPool (DescMaxSize); + ASSERT (Description != NULL); + StrCatS (Description, DescMaxSize/sizeof (CHAR16), Manufacturer); + StrCatS (Description, DescMaxSize/sizeof (CHAR16), L" "); + + StrCatS (Description, DescMaxSize/sizeof (CHAR16), Product); + StrCatS (Description, DescMaxSize/sizeof (CHAR16), L" "); + + StrCatS (Description, DescMaxSize/sizeof (CHAR16), SerialNumber); + + if (Manufacturer != &NullChar) { + FreePool (Manufacturer); + } + + if (Product != &NullChar) { + FreePool (Product); + } + + if (SerialNumber != &NullChar) { + FreePool (SerialNumber); + } + + BmEliminateExtraSpaces (Description); + + return Description; +} + +/** + Return the description for network boot device. + + @param Handle Controller handle. + + @return The description string. +**/ +CHAR16 * +BmGetNetworkDescription ( + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + MAC_ADDR_DEVICE_PATH *Mac; + VLAN_DEVICE_PATH *Vlan; + EFI_DEVICE_PATH_PROTOCOL *Ip; + EFI_DEVICE_PATH_PROTOCOL *Uri; + CHAR16 *Description; + UINTN DescriptionSize; + + Status = gBS->OpenProtocol ( + Handle, + &gEfiLoadFileProtocolGuid, + NULL, + gImageHandle, + Handle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + Status = gBS->OpenProtocol ( + Handle, + &gEfiDevicePathProtocolGuid, + (VOID **)&DevicePath, + gImageHandle, + Handle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status) || (DevicePath == NULL)) { + return NULL; + } + + // + // The PXE device path is like: + // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)] + // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...) + // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...) + // + // The HTTP device path is like: + // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)[/Dns(...)]/Uri(...) + // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)[/Dns(...)]/Uri(...) + // + while (!IsDevicePathEnd (DevicePath) && + ((DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH) || + (DevicePathSubType (DevicePath) != MSG_MAC_ADDR_DP)) + ) + { + DevicePath = NextDevicePathNode (DevicePath); + } + + if (IsDevicePathEnd (DevicePath)) { + return NULL; + } + + Mac = (MAC_ADDR_DEVICE_PATH *)DevicePath; + DevicePath = NextDevicePathNode (DevicePath); + + // + // Locate the optional Vlan node + // + if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) && + (DevicePathSubType (DevicePath) == MSG_VLAN_DP) + ) + { + Vlan = (VLAN_DEVICE_PATH *)DevicePath; + DevicePath = NextDevicePathNode (DevicePath); + } else { + Vlan = NULL; + } + + // + // Skip the optional Wi-Fi node + // + if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) && + (DevicePathSubType (DevicePath) == MSG_WIFI_DP) + ) + { + DevicePath = NextDevicePathNode (DevicePath); + } + + // + // Locate the IP node + // + if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) && + ((DevicePathSubType (DevicePath) == MSG_IPv4_DP) || + (DevicePathSubType (DevicePath) == MSG_IPv6_DP)) + ) + { + Ip = DevicePath; + DevicePath = NextDevicePathNode (DevicePath); + } else { + Ip = NULL; + } + + // + // Skip the optional DNS node + // + if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) && + (DevicePathSubType (DevicePath) == MSG_DNS_DP) + ) + { + DevicePath = NextDevicePathNode (DevicePath); + } + + // + // Locate the URI node + // + if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) && + (DevicePathSubType (DevicePath) == MSG_URI_DP) + ) + { + Uri = DevicePath; + DevicePath = NextDevicePathNode (DevicePath); + } else { + Uri = NULL; + } + + // + // Build description like below: + // "PXEv6 (MAC:112233445566 VLAN1)" + // "HTTPv4 (MAC:112233445566)" + // + DescriptionSize = sizeof (L"HTTPv6 (MAC:112233445566 VLAN65535)"); + Description = AllocatePool (DescriptionSize); + ASSERT (Description != NULL); + UnicodeSPrint ( + Description, + DescriptionSize, + (Vlan == NULL) ? + L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x)" : + L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x VLAN%d)", + (Uri == NULL) ? L"PXE" : L"HTTP", + ((Ip == NULL) || (DevicePathSubType (Ip) == MSG_IPv4_DP)) ? 4 : 6, + Mac->MacAddress.Addr[0], + Mac->MacAddress.Addr[1], + Mac->MacAddress.Addr[2], + Mac->MacAddress.Addr[3], + Mac->MacAddress.Addr[4], + Mac->MacAddress.Addr[5], + (Vlan == NULL) ? 0 : Vlan->VlanId + ); + return Description; +} + +/** + Return the boot description for LoadFile + + @param Handle Controller handle. + + @return The description string. +**/ +CHAR16 * +BmGetLoadFileDescription ( + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *FilePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; + CHAR16 *Description; + EFI_LOAD_FILE_PROTOCOL *LoadFile; + + Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFile); + if (EFI_ERROR (Status)) { + return NULL; + } + + // + // Get the file name + // + Description = NULL; + Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&FilePath); + if (!EFI_ERROR (Status)) { + DevicePathNode = FilePath; + while (!IsDevicePathEnd (DevicePathNode)) { + if ((DevicePathNode->Type == MEDIA_DEVICE_PATH) && (DevicePathNode->SubType == MEDIA_FILEPATH_DP)) { + Description = (CHAR16 *)(DevicePathNode + 1); + break; + } + + DevicePathNode = NextDevicePathNode (DevicePathNode); + } + } + + if (Description != NULL) { + return AllocateCopyPool (StrSize (Description), Description); + } + + return NULL; +} + +/** + Return the boot description for NVME boot device. + + @param Handle Controller handle. + + @return The description string. +**/ +CHAR16 * +BmGetNvmeDescription ( + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmePassthru; + EFI_DEV_PATH_PTR DevicePath; + EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; + EFI_NVM_EXPRESS_COMMAND Command; + EFI_NVM_EXPRESS_COMPLETION Completion; + NVME_ADMIN_CONTROLLER_DATA ControllerData; + CHAR16 *Description; + CHAR16 *Char; + UINTN Index; + + Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath.DevPath); + if (EFI_ERROR (Status)) { + return NULL; + } + + Status = gBS->LocateDevicePath (&gEfiNvmExpressPassThruProtocolGuid, &DevicePath.DevPath, &Handle); + if (EFI_ERROR (Status) || + (DevicePathType (DevicePath.DevPath) != MESSAGING_DEVICE_PATH) || + (DevicePathSubType (DevicePath.DevPath) != MSG_NVME_NAMESPACE_DP)) + { + // + // Do not return description when the Handle is not a child of NVME controller. + // + return NULL; + } + + // + // Send ADMIN_IDENTIFY command to NVME controller to get the model and serial number. + // + Status = gBS->HandleProtocol (Handle, &gEfiNvmExpressPassThruProtocolGuid, (VOID **)&NvmePassthru); + ASSERT_EFI_ERROR (Status); + + ZeroMem (&CommandPacket, sizeof (EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); + ZeroMem (&Command, sizeof (EFI_NVM_EXPRESS_COMMAND)); + ZeroMem (&Completion, sizeof (EFI_NVM_EXPRESS_COMPLETION)); + + Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD; + // + // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h. + // For the Identify command, the Namespace Identifier is only used for the Namespace data structure. + // + Command.Nsid = 0; + CommandPacket.NvmeCmd = &Command; + CommandPacket.NvmeCompletion = &Completion; + CommandPacket.TransferBuffer = &ControllerData; + CommandPacket.TransferLength = sizeof (ControllerData); + CommandPacket.CommandTimeout = EFI_TIMER_PERIOD_SECONDS (5); + CommandPacket.QueueType = NVME_ADMIN_QUEUE; + // + // Set bit 0 (Cns bit) to 1 to identify a controller + // + Command.Cdw10 = 1; + Command.Flags = CDW10_VALID; + + Status = NvmePassthru->PassThru ( + NvmePassthru, + 0, + &CommandPacket, + NULL + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + Description = AllocateZeroPool ( + (ARRAY_SIZE (ControllerData.Mn) + 1 + + ARRAY_SIZE (ControllerData.Sn) + 1 + + MAXIMUM_VALUE_CHARACTERS + 1 + ) * sizeof (CHAR16) + ); + if (Description != NULL) { + Char = Description; + for (Index = 0; Index < ARRAY_SIZE (ControllerData.Mn); Index++) { + *(Char++) = (CHAR16)ControllerData.Mn[Index]; + } + + *(Char++) = L' '; + for (Index = 0; Index < ARRAY_SIZE (ControllerData.Sn); Index++) { + *(Char++) = (CHAR16)ControllerData.Sn[Index]; + } + + *(Char++) = L' '; + UnicodeValueToStringS ( + Char, + sizeof (CHAR16) * (MAXIMUM_VALUE_CHARACTERS + 1), + 0, + DevicePath.NvmeNamespace->NamespaceId, + 0 + ); + BmEliminateExtraSpaces (Description); + } + + return Description; +} + +/** + Return the boot description for the controller based on the type. + + @param Handle Controller handle. + + @return The description string. +**/ +CHAR16 * +BmGetMiscDescription ( + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + CHAR16 *Description; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs; + + switch (BmDevicePathType (DevicePathFromHandle (Handle))) { + case BmAcpiFloppyBoot: + Description = L"Floppy"; + break; + + case BmMessageAtapiBoot: + case BmMessageSataBoot: + Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo); + ASSERT_EFI_ERROR (Status); + // + // Assume a removable SATA device should be the DVD/CD device + // + Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive"; + break; + + case BmMessageUsbBoot: + Description = L"USB Device"; + break; + + case BmMessageScsiBoot: + Description = L"SCSI Device"; + break; + + case BmHardwareDeviceBoot: + Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo); + if (!EFI_ERROR (Status)) { + Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive"; + } else { + Description = L"Misc Device"; + } + + break; + + default: + Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs); + if (!EFI_ERROR (Status)) { + Description = L"Non-Block Boot Device"; + } else { + Description = L"Misc Device"; + } + + break; + } + + return AllocateCopyPool (StrSize (Description), Description); +} + +/** + Register the platform provided boot description handler. + + @param Handler The platform provided boot description handler + + @retval EFI_SUCCESS The handler was registered successfully. + @retval EFI_ALREADY_STARTED The handler was already registered. + @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerRegisterBootDescriptionHandler ( + IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler + ) +{ + LIST_ENTRY *Link; + BM_BOOT_DESCRIPTION_ENTRY *Entry; + + for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers) + ; !IsNull (&mPlatformBootDescriptionHandlers, Link) + ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link) + ) + { + Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE); + if (Entry->Handler == Handler) { + return EFI_ALREADY_STARTED; + } + } + + Entry = AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY)); + if (Entry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Entry->Signature = BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE; + Entry->Handler = Handler; + InsertTailList (&mPlatformBootDescriptionHandlers, &Entry->Link); + return EFI_SUCCESS; +} + +BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers[] = { + BmGetUsbDescription, + BmGetDescriptionFromDiskInfo, + BmGetNetworkDescription, + BmGetLoadFileDescription, + BmGetNvmeDescription, + BmGetMiscDescription +}; + +/** + Return the boot description for the controller. + + @param Handle Controller handle. + + @return The description string. +**/ +CHAR16 * +BmGetBootDescription ( + IN EFI_HANDLE Handle + ) +{ + LIST_ENTRY *Link; + BM_BOOT_DESCRIPTION_ENTRY *Entry; + CHAR16 *Description; + CHAR16 *DefaultDescription; + CHAR16 *Temp; + UINTN Index; + + // + // Firstly get the default boot description + // + DefaultDescription = NULL; + for (Index = 0; Index < ARRAY_SIZE (mBmBootDescriptionHandlers); Index++) { + DefaultDescription = mBmBootDescriptionHandlers[Index](Handle); + if (DefaultDescription != NULL) { + // + // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix + // ONLY for core provided boot description handler. + // + Temp = AllocatePool (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)); + ASSERT (Temp != NULL); + StrCpyS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), mBmUefiPrefix); + StrCatS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), DefaultDescription); + FreePool (DefaultDescription); + DefaultDescription = Temp; + break; + } + } + + ASSERT (DefaultDescription != NULL); + + // + // Secondly query platform for the better boot description + // + for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers) + ; !IsNull (&mPlatformBootDescriptionHandlers, Link) + ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link) + ) + { + Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE); + Description = Entry->Handler (Handle, DefaultDescription); + if (Description != NULL) { + FreePool (DefaultDescription); + return Description; + } + } + + return DefaultDescription; +} + +/** + Enumerate all boot option descriptions and append " 2"/" 3"/... to make + unique description. + + @param BootOptions Array of boot options. + @param BootOptionCount Count of boot options. +**/ +VOID +BmMakeBootOptionDescriptionUnique ( + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions, + UINTN BootOptionCount + ) +{ + UINTN Base; + UINTN Index; + UINTN DescriptionSize; + UINTN MaxSuffixSize; + BOOLEAN *Visited; + UINTN MatchCount; + + if (BootOptionCount == 0) { + return; + } + + // + // Calculate the maximum buffer size for the number suffix. + // The initial sizeof (CHAR16) is for the blank space before the number. + // + MaxSuffixSize = sizeof (CHAR16); + for (Index = BootOptionCount; Index != 0; Index = Index / 10) { + MaxSuffixSize += sizeof (CHAR16); + } + + Visited = AllocateZeroPool (sizeof (BOOLEAN) * BootOptionCount); + ASSERT (Visited != NULL); + + for (Base = 0; Base < BootOptionCount; Base++) { + if (!Visited[Base]) { + MatchCount = 1; + Visited[Base] = TRUE; + DescriptionSize = StrSize (BootOptions[Base].Description); + for (Index = Base + 1; Index < BootOptionCount; Index++) { + if (!Visited[Index] && (StrCmp (BootOptions[Base].Description, BootOptions[Index].Description) == 0)) { + Visited[Index] = TRUE; + MatchCount++; + FreePool (BootOptions[Index].Description); + BootOptions[Index].Description = AllocatePool (DescriptionSize + MaxSuffixSize); + UnicodeSPrint ( + BootOptions[Index].Description, + DescriptionSize + MaxSuffixSize, + L"%s %d", + BootOptions[Base].Description, + MatchCount + ); + } + } + } + } + + FreePool (Visited); +} diff --git a/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c b/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c new file mode 100644 index 00000000000..21d8f3ad557 --- /dev/null +++ b/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c @@ -0,0 +1,1562 @@ +/** @file + The Miscellaneous Routines for TlsAuthConfigDxe driver. + +Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "TlsAuthConfigImpl.h" + +VOID *mStartOpCodeHandle = NULL; +VOID *mEndOpCodeHandle = NULL; +EFI_IFR_GUID_LABEL *mStartLabel = NULL; +EFI_IFR_GUID_LABEL *mEndLabel = NULL; + +CHAR16 mTlsAuthConfigStorageName[] = L"TLS_AUTH_CONFIG_IFR_NVDATA"; + +TLS_AUTH_CONFIG_PRIVATE_DATA *mTlsAuthPrivateData = NULL; + +HII_VENDOR_DEVICE_PATH mTlsAuthConfigHiiVendorDevicePath = { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8)(sizeof (VENDOR_DEVICE_PATH)), + (UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8) + } + }, + TLS_AUTH_CONFIG_GUID + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + (UINT8)(END_DEVICE_PATH_LENGTH), + (UINT8)((END_DEVICE_PATH_LENGTH) >> 8) + } + } +}; + +// +// Possible DER-encoded certificate file suffixes, end with NULL pointer. +// +CHAR16 *mDerPemEncodedSuffix[] = { + L".cer", + L".der", + L".crt", + L".pem", + NULL +}; + +/** + This code checks if the FileSuffix is one of the possible DER/PEM-encoded certificate suffix. + + @param[in] FileSuffix The suffix of the input certificate file + + @retval TRUE It's a DER/PEM-encoded certificate. + @retval FALSE It's NOT a DER/PEM-encoded certificate. + +**/ +BOOLEAN +IsDerPemEncodeCertificate ( + IN CONST CHAR16 *FileSuffix + ) +{ + UINTN Index; + + for (Index = 0; mDerPemEncodedSuffix[Index] != NULL; Index++) { + if (StrCmp (FileSuffix, mDerPemEncodedSuffix[Index]) == 0) { + return TRUE; + } + } + + return FALSE; +} + +/** + Worker function that prints an EFI_GUID into specified Buffer. + + @param[in] Guid Pointer to GUID to print. + @param[in] Buffer Buffer to print Guid into. + @param[in] BufferSize Size of Buffer. + + @retval Number of characters printed. + +**/ +UINTN +GuidToString ( + IN EFI_GUID *Guid, + IN CHAR16 *Buffer, + IN UINTN BufferSize + ) +{ + return UnicodeSPrint ( + Buffer, + BufferSize, + L"%g", + Guid + ); +} + +/** + List all cert in specified database by GUID in the page + for user to select and delete as needed. + + @param[in] PrivateData Module's private data. + @param[in] VariableName The variable name of the vendor's signature database. + @param[in] VendorGuid A unique identifier for the vendor. + @param[in] LabelNumber Label number to insert opcodes. + @param[in] FormId Form ID of current page. + @param[in] QuestionIdBase Base question id of the signature list. + + @retval EFI_SUCCESS Success to update the signature list page + @retval EFI_OUT_OF_RESOURCES Unable to allocate required resources. + +**/ +EFI_STATUS +UpdateDeletePage ( + IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT16 LabelNumber, + IN EFI_FORM_ID FormId, + IN EFI_QUESTION_ID QuestionIdBase + ) +{ + EFI_STATUS Status; + UINT32 Index; + UINTN CertCount; + UINTN GuidIndex; + VOID *StartOpCodeHandle; + VOID *EndOpCodeHandle; + EFI_IFR_GUID_LABEL *StartLabel; + EFI_IFR_GUID_LABEL *EndLabel; + UINTN DataSize; + UINT8 *Data; + EFI_SIGNATURE_LIST *CertList; + EFI_SIGNATURE_DATA *Cert; + UINT32 ItemDataSize; + CHAR16 *GuidStr; + EFI_STRING_ID GuidID; + EFI_STRING_ID Help; + + Data = NULL; + CertList = NULL; + Cert = NULL; + GuidStr = NULL; + StartOpCodeHandle = NULL; + EndOpCodeHandle = NULL; + + // + // Initialize the container for dynamic opcodes. + // + StartOpCodeHandle = HiiAllocateOpCodeHandle (); + if (StartOpCodeHandle == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + EndOpCodeHandle = HiiAllocateOpCodeHandle (); + if (EndOpCodeHandle == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Create Hii Extend Label OpCode. + // + StartLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode ( + StartOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + StartLabel->Number = LabelNumber; + + EndLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode ( + EndOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + EndLabel->Number = LABEL_END; + + // + // Read Variable. + // + DataSize = 0; + Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, Data); + if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) { + goto ON_EXIT; + } + + Data = (UINT8 *)AllocateZeroPool (DataSize); + if (Data == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, Data); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + GuidStr = AllocateZeroPool (100); + if (GuidStr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Enumerate all data. + // + ItemDataSize = (UINT32)DataSize; + CertList = (EFI_SIGNATURE_LIST *)Data; + GuidIndex = 0; + + while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) { + if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) { + Help = STRING_TOKEN (STR_CERT_TYPE_PCKS_GUID); + } else { + // + // The signature type is not supported in current implementation. + // + ItemDataSize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize); + continue; + } + + CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; + for (Index = 0; Index < CertCount; Index++) { + Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)CertList + + sizeof (EFI_SIGNATURE_LIST) + + CertList->SignatureHeaderSize + + Index * CertList->SignatureSize); + // + // Display GUID and help + // + GuidToString (&Cert->SignatureOwner, GuidStr, 100); + GuidID = HiiSetString (Private->RegisteredHandle, 0, GuidStr, NULL); + HiiCreateCheckBoxOpCode ( + StartOpCodeHandle, + (EFI_QUESTION_ID)(QuestionIdBase + GuidIndex++), + 0, + 0, + GuidID, + Help, + EFI_IFR_FLAG_CALLBACK, + 0, + NULL + ); + } + + ItemDataSize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize); + } + +ON_EXIT: + HiiUpdateForm ( + Private->RegisteredHandle, + &gTlsAuthConfigGuid, + FormId, + StartOpCodeHandle, + EndOpCodeHandle + ); + + if (StartOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (StartOpCodeHandle); + } + + if (EndOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (EndOpCodeHandle); + } + + if (Data != NULL) { + FreePool (Data); + } + + if (GuidStr != NULL) { + FreePool (GuidStr); + } + + return EFI_SUCCESS; +} + +/** + Delete one entry from cert database. + + @param[in] Private Module's private data. + @param[in] VariableName The variable name of the database. + @param[in] VendorGuid A unique identifier for the vendor. + @param[in] LabelNumber Label number to insert opcodes. + @param[in] FormId Form ID of current page. + @param[in] QuestionIdBase Base question id of the cert list. + @param[in] DeleteIndex Cert index to delete. + + @retval EFI_SUCCESS Delete signature successfully. + @retval EFI_NOT_FOUND Can't find the signature item, + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. +**/ +EFI_STATUS +DeleteCert ( + IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT16 LabelNumber, + IN EFI_FORM_ID FormId, + IN EFI_QUESTION_ID QuestionIdBase, + IN UINTN DeleteIndex + ) +{ + EFI_STATUS Status; + UINTN DataSize; + UINT8 *Data; + UINT8 *OldData; + UINT32 Attr; + UINT32 Index; + EFI_SIGNATURE_LIST *CertList; + EFI_SIGNATURE_LIST *NewCertList; + EFI_SIGNATURE_DATA *Cert; + UINTN CertCount; + UINT32 Offset; + BOOLEAN IsItemFound; + UINT32 ItemDataSize; + UINTN GuidIndex; + + Data = NULL; + OldData = NULL; + CertList = NULL; + Cert = NULL; + Attr = 0; + + // + // Get original signature list data. + // + DataSize = 0; + Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, NULL); + if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) { + goto ON_EXIT; + } + + OldData = (UINT8 *)AllocateZeroPool (DataSize); + if (OldData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = gRT->GetVariable (VariableName, VendorGuid, &Attr, &DataSize, OldData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Allocate space for new variable. + // + Data = (UINT8 *)AllocateZeroPool (DataSize); + if (Data == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Enumerate all data and erasing the target item. + // + IsItemFound = FALSE; + ItemDataSize = (UINT32)DataSize; + CertList = (EFI_SIGNATURE_LIST *)OldData; + Offset = 0; + GuidIndex = 0; + while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) { + if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) { + // + // Copy EFI_SIGNATURE_LIST header then calculate the signature count in this list. + // + CopyMem (Data + Offset, CertList, (sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize)); + NewCertList = (EFI_SIGNATURE_LIST *)(Data + Offset); + Offset += (sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); + Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); + CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; + for (Index = 0; Index < CertCount; Index++) { + if (GuidIndex == DeleteIndex) { + // + // Find it! Skip it! + // + NewCertList->SignatureListSize -= CertList->SignatureSize; + IsItemFound = TRUE; + } else { + // + // This item doesn't match. Copy it to the Data buffer. + // + CopyMem (Data + Offset, (UINT8 *)(Cert), CertList->SignatureSize); + Offset += CertList->SignatureSize; + } + + GuidIndex++; + Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)Cert + CertList->SignatureSize); + } + } else { + // + // This List doesn't match. Just copy it to the Data buffer. + // + CopyMem (Data + Offset, (UINT8 *)(CertList), CertList->SignatureListSize); + Offset += CertList->SignatureListSize; + } + + ItemDataSize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize); + } + + if (!IsItemFound) { + // + // Doesn't find the signature Item! + // + Status = EFI_NOT_FOUND; + goto ON_EXIT; + } + + // + // Delete the EFI_SIGNATURE_LIST header if there is no signature in the list. + // + ItemDataSize = Offset; + CertList = (EFI_SIGNATURE_LIST *)Data; + Offset = 0; + ZeroMem (OldData, ItemDataSize); + while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) { + CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; + DEBUG ((DEBUG_INFO, " CertCount = %x\n", CertCount)); + if (CertCount != 0) { + CopyMem (OldData + Offset, (UINT8 *)(CertList), CertList->SignatureListSize); + Offset += CertList->SignatureListSize; + } + + ItemDataSize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize); + } + + DataSize = Offset; + + Status = gRT->SetVariable ( + VariableName, + VendorGuid, + Attr, + DataSize, + OldData + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to set variable, Status = %r\n", Status)); + goto ON_EXIT; + } + +ON_EXIT: + if (Data != NULL) { + FreePool (Data); + } + + if (OldData != NULL) { + FreePool (OldData); + } + + return UpdateDeletePage ( + Private, + VariableName, + VendorGuid, + LabelNumber, + FormId, + QuestionIdBase + ); +} + +/** + Clean the file related resource. + + @param[in] Private Module's private data. + +**/ +VOID +CleanFileContext ( + IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private + ) +{ + if (Private->FileContext->FHandle != NULL) { + Private->FileContext->FHandle->Close (Private->FileContext->FHandle); + Private->FileContext->FHandle = NULL; + if (Private->FileContext->FileName != NULL) { + FreePool (Private->FileContext->FileName); + Private->FileContext->FileName = NULL; + } + } +} + +/** + Read file content into BufferPtr, the size of the allocate buffer + is *FileSize plus AddtionAllocateSize. + + @param[in] FileHandle The file to be read. + @param[in, out] BufferPtr Pointers to the pointer of allocated buffer. + @param[out] FileSize Size of input file + @param[in] AddtionAllocateSize Addtion size the buffer need to be allocated. + In case the buffer need to contain others besides the file content. + + @retval EFI_SUCCESS The file was read into the buffer. + @retval EFI_INVALID_PARAMETER A parameter was invalid. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval others Unexpected error. + +**/ +EFI_STATUS +ReadFileContent ( + IN EFI_FILE_HANDLE FileHandle, + IN OUT VOID **BufferPtr, + OUT UINTN *FileSize, + IN UINTN AddtionAllocateSize + ) + +{ + UINTN BufferSize; + UINT64 SourceFileSize; + VOID *Buffer; + EFI_STATUS Status; + + if ((FileHandle == NULL) || (FileSize == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Buffer = NULL; + + // + // Get the file size + // + Status = FileHandle->SetPosition (FileHandle, (UINT64)-1); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = FileHandle->GetPosition (FileHandle, &SourceFileSize); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = FileHandle->SetPosition (FileHandle, 0); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + BufferSize = (UINTN)SourceFileSize + AddtionAllocateSize; + Buffer = AllocateZeroPool (BufferSize); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + BufferSize = (UINTN)SourceFileSize; + *FileSize = BufferSize; + + Status = FileHandle->Read (FileHandle, &BufferSize, Buffer); + if (EFI_ERROR (Status) || (BufferSize != *FileSize)) { + FreePool (Buffer); + Buffer = NULL; + Status = EFI_BAD_BUFFER_SIZE; + goto ON_EXIT; + } + +ON_EXIT: + + *BufferPtr = Buffer; + return Status; +} + +/** + This function converts an input device structure to a Unicode string. + + @param[in] DevPath A pointer to the device path structure. + + @return A new allocated Unicode string that represents the device path. + +**/ +CHAR16 * +EFIAPI +DevicePathToStr ( + IN EFI_DEVICE_PATH_PROTOCOL *DevPath + ) +{ + return ConvertDevicePathToText ( + DevPath, + FALSE, + TRUE + ); +} + +/** + Extract filename from device path. The returned buffer is allocated using AllocateCopyPool. + The caller is responsible for freeing the allocated buffer using FreePool(). If return NULL + means not enough memory resource. + + @param DevicePath Device path. + + @retval NULL Not enough memory resource for AllocateCopyPool. + @retval Other A new allocated string that represents the file name. + +**/ +CHAR16 * +ExtractFileNameFromDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + CHAR16 *String; + CHAR16 *MatchString; + CHAR16 *LastMatch; + CHAR16 *FileName; + UINTN Length; + + ASSERT (DevicePath != NULL); + + String = DevicePathToStr (DevicePath); + MatchString = String; + LastMatch = String; + FileName = NULL; + + while (MatchString != NULL) { + LastMatch = MatchString + 1; + MatchString = StrStr (LastMatch, L"\\"); + } + + Length = StrLen (LastMatch); + FileName = AllocateCopyPool ((Length + 1) * sizeof (CHAR16), LastMatch); + if (FileName != NULL) { + *(FileName + Length) = 0; + } + + FreePool (String); + + return FileName; +} + +/** + Enroll a new X509 certificate into Variable. + + @param[in] PrivateData The module's private data. + @param[in] VariableName Variable name of CA database. + + @retval EFI_SUCCESS New X509 is enrolled successfully. + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. + +**/ +EFI_STATUS +EnrollX509toVariable ( + IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private, + IN CHAR16 *VariableName + ) +{ + EFI_STATUS Status; + UINTN X509DataSize; + VOID *X509Data; + EFI_SIGNATURE_LIST *CACert; + EFI_SIGNATURE_DATA *CACertData; + VOID *Data; + UINTN DataSize; + UINTN SigDataSize; + UINT32 Attr; + + X509DataSize = 0; + SigDataSize = 0; + DataSize = 0; + X509Data = NULL; + CACert = NULL; + CACertData = NULL; + Data = NULL; + Attr = 0; + + Status = ReadFileContent ( + Private->FileContext->FHandle, + &X509Data, + &X509DataSize, + 0 + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + ASSERT (X509Data != NULL); + + SigDataSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + X509DataSize; + + Data = AllocateZeroPool (SigDataSize); + if (Data == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Fill Certificate Database parameters. + // + CACert = (EFI_SIGNATURE_LIST *)Data; + CACert->SignatureListSize = (UINT32)SigDataSize; + CACert->SignatureHeaderSize = 0; + CACert->SignatureSize = (UINT32)(sizeof (EFI_SIGNATURE_DATA) - 1 + X509DataSize); + CopyGuid (&CACert->SignatureType, &gEfiCertX509Guid); + + CACertData = (EFI_SIGNATURE_DATA *)((UINT8 *)CACert + sizeof (EFI_SIGNATURE_LIST)); + CopyGuid (&CACertData->SignatureOwner, Private->CertGuid); + CopyMem ((UINT8 *)(CACertData->SignatureData), X509Data, X509DataSize); + + // + // Check if the signature database entry already exists. If it does, use the + // EFI_VARIABLE_APPEND_WRITE attribute to append the new signature data to + // the original variable, plus preserve the original variable attributes. + // + Status = gRT->GetVariable ( + VariableName, + &gEfiTlsCaCertificateGuid, + &Attr, + &DataSize, + NULL + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + Attr |= EFI_VARIABLE_APPEND_WRITE; + } else if (Status == EFI_NOT_FOUND) { + Attr = TLS_AUTH_CONFIG_VAR_BASE_ATTR; + } else { + goto ON_EXIT; + } + + Status = gRT->SetVariable ( + VariableName, + &gEfiTlsCaCertificateGuid, + Attr, + SigDataSize, + Data + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + +ON_EXIT: + CleanFileContext (Private); + + if (Private->CertGuid != NULL) { + FreePool (Private->CertGuid); + Private->CertGuid = NULL; + } + + if (Data != NULL) { + FreePool (Data); + } + + if (X509Data != NULL) { + FreePool (X509Data); + } + + return Status; +} + +/** + Enroll Cert into TlsCaCertificate. The GUID will be Private->CertGuid. + + @param[in] PrivateData The module's private data. + @param[in] VariableName Variable name of signature database. + + @retval EFI_SUCCESS New Cert enrolled successfully. + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval EFI_UNSUPPORTED The Cert file is unsupported type. + @retval others Fail to enroll Cert data. + +**/ +EFI_STATUS +EnrollCertDatabase ( + IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private, + IN CHAR16 *VariableName + ) +{ + UINT16 *FilePostFix; + UINTN NameLength; + + if ((Private->FileContext->FileName == NULL) || (Private->FileContext->FHandle == NULL) || (Private->CertGuid == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Parse the file's postfix. + // + NameLength = StrLen (Private->FileContext->FileName); + if (NameLength <= 4) { + return EFI_INVALID_PARAMETER; + } + + FilePostFix = Private->FileContext->FileName + NameLength - 4; + + if (IsDerPemEncodeCertificate (FilePostFix)) { + // + // Supports DER-encoded X509 certificate. + // + return EnrollX509toVariable (Private, VariableName); + } + + return EFI_UNSUPPORTED; +} + +/** + Refresh the global UpdateData structure. + +**/ +VOID +RefreshUpdateData ( + VOID + ) +{ + // + // Free current updated date + // + if (mStartOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (mStartOpCodeHandle); + } + + // + // Create new OpCode Handle + // + mStartOpCodeHandle = HiiAllocateOpCodeHandle (); + + // + // Create Hii Extend Label OpCode as the start opcode + // + mStartLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode ( + mStartOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + mStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; +} + +/** + Clean up the dynamic opcode at label and form specified by both LabelId. + + @param[in] LabelId It is both the Form ID and Label ID for opcode deletion. + @param[in] PrivateData Module private data. + +**/ +VOID +CleanUpPage ( + IN UINT16 LabelId, + IN TLS_AUTH_CONFIG_PRIVATE_DATA *PrivateData + ) +{ + RefreshUpdateData (); + + // + // Remove all op-codes from dynamic page + // + mStartLabel->Number = LabelId; + HiiUpdateForm ( + PrivateData->RegisteredHandle, + &gTlsAuthConfigGuid, + LabelId, + mStartOpCodeHandle, // Label LabelId + mEndOpCodeHandle // LABEL_END + ); +} + +/** + Update the form base on the selected file. + + @param FilePath Point to the file path. + @param FormId The form need to display. + + @retval TRUE Exit caller function. + @retval FALSE Not exit caller function. + +**/ +BOOLEAN +UpdatePage ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN EFI_FORM_ID FormId + ) +{ + CHAR16 *FileName; + EFI_STRING_ID StringToken; + + FileName = NULL; + + if (FilePath != NULL) { + FileName = ExtractFileNameFromDevicePath (FilePath); + } + + if (FileName == NULL) { + // + // FileName = NULL has two case: + // 1. FilePath == NULL, not select file. + // 2. FilePath != NULL, but ExtractFileNameFromDevicePath return NULL not enough memory resource. + // In these two case, no need to update the form, and exit the caller function. + // + return TRUE; + } + + StringToken = HiiSetString (mTlsAuthPrivateData->RegisteredHandle, 0, FileName, NULL); + + mTlsAuthPrivateData->FileContext->FileName = FileName; + + EfiOpenFileByDevicePath ( + &FilePath, + &mTlsAuthPrivateData->FileContext->FHandle, + EFI_FILE_MODE_READ, + 0 + ); + // + // Create Subtitle op-code for the display string of the option. + // + RefreshUpdateData (); + mStartLabel->Number = FormId; + + HiiCreateSubTitleOpCode ( + mStartOpCodeHandle, + StringToken, + 0, + 0, + 0 + ); + + HiiUpdateForm ( + mTlsAuthPrivateData->RegisteredHandle, + &gTlsAuthConfigGuid, + FormId, + mStartOpCodeHandle, /// Label FormId + mEndOpCodeHandle /// LABEL_END + ); + + return TRUE; +} + +/** + Update the form base on the input file path info. + + @param FilePath Point to the file path. + + @retval TRUE Exit caller function. + @retval FALSE Not exit caller function. +**/ +BOOLEAN +EFIAPI +UpdateCAFromFile ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath + ) +{ + return UpdatePage (FilePath, TLS_AUTH_CONFIG_FORMID4_FORM); +} + +/** + Unload the configuration form, this includes: delete all the configuration + entries, uninstall the form callback protocol, and free the resources used. + + @param[in] Private Pointer to the driver private data. + + @retval EFI_SUCCESS The configuration form is unloaded. + @retval Others Failed to unload the form. + +**/ +EFI_STATUS +TlsAuthConfigFormUnload ( + IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private + ) +{ + if (Private->DriverHandle != NULL) { + // + // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL + // + gBS->UninstallMultipleProtocolInterfaces ( + Private->DriverHandle, + &gEfiDevicePathProtocolGuid, + &mTlsAuthConfigHiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &Private->ConfigAccess, + NULL + ); + Private->DriverHandle = NULL; + } + + if (Private->RegisteredHandle != NULL) { + // + // Remove HII package list + // + HiiRemovePackages (Private->RegisteredHandle); + Private->RegisteredHandle = NULL; + } + + if (Private->CertGuid != NULL) { + FreePool (Private->CertGuid); + } + + if (Private->FileContext != NULL) { + FreePool (Private->FileContext); + } + + FreePool (Private); + + if (mStartOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (mStartOpCodeHandle); + } + + if (mEndOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (mEndOpCodeHandle); + } + + return EFI_SUCCESS; +} + +/** + Initialize the configuration form. + + @param[in] Private Pointer to the driver private data. + + @retval EFI_SUCCESS The configuration form is initialized. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +EFI_STATUS +TlsAuthConfigFormInit ( + IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + + Private->Signature = TLS_AUTH_CONFIG_PRIVATE_DATA_SIGNATURE; + + Private->ConfigAccess.ExtractConfig = TlsAuthConfigAccessExtractConfig; + Private->ConfigAccess.RouteConfig = TlsAuthConfigAccessRouteConfig; + Private->ConfigAccess.Callback = TlsAuthConfigAccessCallback; + + // + // Install Device Path Protocol and Config Access protocol to driver handle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->DriverHandle, + &gEfiDevicePathProtocolGuid, + &mTlsAuthConfigHiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &Private->ConfigAccess, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Publish our HII data. + // + Private->RegisteredHandle = HiiAddPackages ( + &gTlsAuthConfigGuid, + Private->DriverHandle, + TlsAuthConfigDxeStrings, + TlsAuthConfigVfrBin, + NULL + ); + if (Private->RegisteredHandle == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + Private->FileContext = AllocateZeroPool (sizeof (TLS_AUTH_CONFIG_FILE_CONTEXT)); + if (Private->FileContext == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + // + // Init OpCode Handle and Allocate space for creation of Buffer + // + mStartOpCodeHandle = HiiAllocateOpCodeHandle (); + if (mStartOpCodeHandle == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + mEndOpCodeHandle = HiiAllocateOpCodeHandle (); + if (mEndOpCodeHandle == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + // + // Create Hii Extend Label OpCode as the start opcode + // + mStartLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode ( + mStartOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + mStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + + // + // Create Hii Extend Label OpCode as the end opcode + // + mEndLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode ( + mEndOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + mEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + mEndLabel->Number = LABEL_END; + + return EFI_SUCCESS; + +Error: + TlsAuthConfigFormUnload (Private); + return Status; +} + +/** + + This function allows the caller to request the current + configuration for one or more named elements. The resulting + string is in format. Any and all alternative + configuration strings shall also be appended to the end of the + current configuration string. If they are, they must appear + after the current configuration. They must contain the same + routing (GUID, NAME, PATH) as the current configuration string. + They must have an additional description indicating the type of + alternative configuration the string represents, + "ALTCFG=". That (when + converted from Hex UNICODE to binary) is a reference to a + string in the associated string pack. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + + @param Request A null-terminated Unicode string in + format. Note that this + includes the routing information as well as + the configurable name / value pairs. It is + invalid for this string to be in + format. + If a NULL is passed in for the Request field, + all of the settings being abstracted by this function + will be returned in the Results field. In addition, + if a ConfigHdr is passed in with no request elements, + all of the settings being abstracted for that particular + ConfigHdr reference will be returned in the Results Field. + + @param Progress On return, points to a character in the + Request string. Points to the string's null + terminator if request was successful. Points + to the most recent "&" before the first + failing name / value pair (or the beginning + of the string if the failure is in the first + name / value pair) if the request was not + successful. + + @param Results A null-terminated Unicode string in + format which has all values + filled in for the names in the Request string. + String to be allocated by the called function. + + @retval EFI_SUCCESS The Results string is filled with the + values corresponding to all requested + names. + + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the + parts of the results that must be + stored awaiting possible future + protocols. + + @retval EFI_NOT_FOUND Routing data doesn't match any + known driver. Progress set to the + first character in the routing header. + Note: There is no requirement that the + driver validate the routing data. It + must skip the in order to + process the names. + + @retval EFI_INVALID_PARAMETER Illegal syntax. Progress set + to most recent "&" before the + error or the beginning of the + string. + + @retval EFI_INVALID_PARAMETER Unknown name. Progress points + to the & before the name in + question. + +**/ +EFI_STATUS +EFIAPI +TlsAuthConfigAccessExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + UINTN Size; + EFI_STRING ConfigRequest; + EFI_STRING ConfigRequestHdr; + TLS_AUTH_CONFIG_PRIVATE_DATA *Private; + BOOLEAN AllocatedRequest; + + if ((Progress == NULL) || (Results == NULL)) { + return EFI_INVALID_PARAMETER; + } + + AllocatedRequest = FALSE; + ConfigRequestHdr = NULL; + ConfigRequest = NULL; + Size = 0; + + Private = TLS_AUTH_CONFIG_PRIVATE_FROM_THIS (This); + + BufferSize = sizeof (TLS_AUTH_CONFIG_IFR_NVDATA); + ZeroMem (&Private->TlsAuthConfigNvData, BufferSize); + + *Progress = Request; + + if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &gTlsAuthConfigGuid, mTlsAuthConfigStorageName)) { + return EFI_NOT_FOUND; + } + + ConfigRequest = Request; + if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) { + // + // Request is set to NULL or OFFSET is NULL, construct full request string. + // + // Allocate and fill a buffer large enough to hold the template + // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator + // + ConfigRequestHdr = HiiConstructConfigHdr (&gTlsAuthConfigGuid, mTlsAuthConfigStorageName, Private->DriverHandle); + Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16); + ConfigRequest = AllocateZeroPool (Size); + ASSERT (ConfigRequest != NULL); + AllocatedRequest = TRUE; + UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize); + FreePool (ConfigRequestHdr); + ConfigRequestHdr = NULL; + } + + Status = gHiiConfigRouting->BlockToConfig ( + gHiiConfigRouting, + ConfigRequest, + (UINT8 *)&Private->TlsAuthConfigNvData, + BufferSize, + Results, + Progress + ); + + // + // Free the allocated config request string. + // + if (AllocatedRequest) { + FreePool (ConfigRequest); + } + + // + // Set Progress string to the original request string. + // + if (Request == NULL) { + *Progress = NULL; + } else if (StrStr (Request, L"OFFSET") == NULL) { + *Progress = Request + StrLen (Request); + } + + return Status; +} + +/** + + This function applies changes in a driver's configuration. + Input is a Configuration, which has the routing data for this + driver followed by name / value configuration pairs. The driver + must apply those pairs to its configurable storage. If the + driver's configuration is stored in a linear block of data + and the driver's name / value pairs are in + format, it may use the ConfigToBlock helper function (above) to + simplify the job. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + + @param Configuration A null-terminated Unicode string in + format. + + @param Progress A pointer to a string filled in with the + offset of the most recent '&' before the + first failing name / value pair (or the + beginning of the string if the failure + is in the first name / value pair) or + the terminating NULL if all was + successful. + + @retval EFI_SUCCESS The results have been distributed or are + awaiting distribution. + + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the + parts of the results that must be + stored awaiting possible future + protocols. + + @retval EFI_INVALID_PARAMETERS Passing in a NULL for the + Results parameter would result + in this type of error. + + @retval EFI_NOT_FOUND Target for the specified routing data + was not found + +**/ +EFI_STATUS +EFIAPI +TlsAuthConfigAccessRouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + TLS_AUTH_CONFIG_PRIVATE_DATA *Private; + + if (Progress == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Progress = Configuration; + + if (Configuration == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check routing data in . + // Note: there is no name for Name/Value storage, only GUID will be checked + // + if (!HiiIsConfigHdrMatch (Configuration, &gTlsAuthConfigGuid, mTlsAuthConfigStorageName)) { + return EFI_NOT_FOUND; + } + + Private = TLS_AUTH_CONFIG_PRIVATE_FROM_THIS (This); + + BufferSize = sizeof (TLS_AUTH_CONFIG_IFR_NVDATA); + ZeroMem (&Private->TlsAuthConfigNvData, BufferSize); + + Status = gHiiConfigRouting->ConfigToBlock ( + gHiiConfigRouting, + Configuration, + (UINT8 *)&Private->TlsAuthConfigNvData, + &BufferSize, + Progress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return Status; +} + +/** + + This function is called to provide results data to the driver. + This data consists of a unique key that is used to identify + which data is either being passed back or being asked for. + + @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param Action Specifies the type of action taken by the browser. + @param QuestionId A unique value which is sent to the original + exporting driver so that it can identify the type + of data to expect. The format of the data tends to + vary based on the opcode that generated the callback. + @param Type The type of value for the question. + @param Value A pointer to the data being sent to the original + exporting driver. + @param ActionRequest On return, points to the action requested by the + callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the + variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be saved. + @retval EFI_UNSUPPORTED The specified Action is not supported by the + callback. +**/ +EFI_STATUS +EFIAPI +TlsAuthConfigAccessCallback ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN EFI_BROWSER_ACTION Action, + IN EFI_QUESTION_ID QuestionId, + IN UINT8 Type, + IN OUT EFI_IFR_TYPE_VALUE *Value, + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest + ) +{ + EFI_STATUS Status; + RETURN_STATUS RStatus; + TLS_AUTH_CONFIG_PRIVATE_DATA *Private; + UINTN BufferSize; + TLS_AUTH_CONFIG_IFR_NVDATA *IfrNvData; + UINT16 LabelId; + EFI_DEVICE_PATH_PROTOCOL *File; + EFI_HII_POPUP_PROTOCOL *HiiPopUp; + EFI_HII_POPUP_SELECTION PopUpSelect; + + Status = EFI_SUCCESS; + File = NULL; + + if ((This == NULL) || (Value == NULL) || (ActionRequest == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = TLS_AUTH_CONFIG_PRIVATE_FROM_THIS (This); + + mTlsAuthPrivateData = Private; + Status = gBS->LocateProtocol (&gEfiHiiPopupProtocolGuid, NULL, (VOID **)&HiiPopUp); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Can't find Form PopUp protocol. Exit (%r)\n", Status)); + return Status; + } + + // + // Retrieve uncommitted data from Browser + // + BufferSize = sizeof (TLS_AUTH_CONFIG_IFR_NVDATA); + IfrNvData = AllocateZeroPool (BufferSize); + if (IfrNvData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + HiiGetBrowserData (&gTlsAuthConfigGuid, mTlsAuthConfigStorageName, BufferSize, (UINT8 *)IfrNvData); + + if ((Action != EFI_BROWSER_ACTION_CHANGED) && + (Action != EFI_BROWSER_ACTION_CHANGING) && + (Action != EFI_BROWSER_ACTION_FORM_CLOSE)) + { + Status = EFI_UNSUPPORTED; + goto EXIT; + } + + if (Action == EFI_BROWSER_ACTION_CHANGING) { + switch (QuestionId) { + case KEY_TLS_AUTH_CONFIG_CLIENT_CERT: + case KEY_TLS_AUTH_CONFIG_SERVER_CA: + // + // Clear Cert GUID. + // + ZeroMem (IfrNvData->CertGuid, sizeof (IfrNvData->CertGuid)); + if (Private->CertGuid == NULL) { + Private->CertGuid = (EFI_GUID *)AllocateZeroPool (sizeof (EFI_GUID)); + if (Private->CertGuid == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + if (QuestionId == KEY_TLS_AUTH_CONFIG_CLIENT_CERT) { + LabelId = TLS_AUTH_CONFIG_FORMID3_FORM; + } else { + LabelId = TLS_AUTH_CONFIG_FORMID4_FORM; + } + + // + // Refresh selected file. + // + CleanUpPage (LabelId, Private); + break; + case KEY_TLS_AUTH_CONFIG_ENROLL_CERT_FROM_FILE: + // + // If the file is already opened, clean the file related resource first. + // + CleanFileContext (Private); + + ChooseFile (NULL, NULL, UpdateCAFromFile, &File); + break; + + case KEY_TLS_AUTH_CONFIG_VALUE_SAVE_AND_EXIT: + Status = EnrollCertDatabase (Private, EFI_TLS_CA_CERTIFICATE_VARIABLE); + if (EFI_ERROR (Status)) { + CleanFileContext (Private); + + HiiPopUp->CreatePopup ( + HiiPopUp, + EfiHiiPopupStyleError, + EfiHiiPopupTypeOk, + Private->RegisteredHandle, + STRING_TOKEN (STR_TLS_AUTH_ENROLL_CERT_FAILURE), + &PopUpSelect + ); + } + + break; + + case KEY_TLS_AUTH_CONFIG_VALUE_NO_SAVE_AND_EXIT: + CleanFileContext (Private); + + if (Private->CertGuid != NULL) { + FreePool (Private->CertGuid); + Private->CertGuid = NULL; + } + + break; + + case KEY_TLS_AUTH_CONFIG_DELETE_CERT: + UpdateDeletePage ( + Private, + EFI_TLS_CA_CERTIFICATE_VARIABLE, + &gEfiTlsCaCertificateGuid, + LABEL_CA_DELETE, + TLS_AUTH_CONFIG_FORMID5_FORM, + OPTION_DEL_CA_ESTION_ID + ); + break; + + default: + if ((QuestionId >= OPTION_DEL_CA_ESTION_ID) && + (QuestionId < (OPTION_DEL_CA_ESTION_ID + OPTION_CONFIG_RANGE))) + { + DeleteCert ( + Private, + EFI_TLS_CA_CERTIFICATE_VARIABLE, + &gEfiTlsCaCertificateGuid, + LABEL_CA_DELETE, + TLS_AUTH_CONFIG_FORMID5_FORM, + OPTION_DEL_CA_ESTION_ID, + QuestionId - OPTION_DEL_CA_ESTION_ID + ); + } + + break; + } + } else if (Action == EFI_BROWSER_ACTION_CHANGED) { + switch (QuestionId) { + case KEY_TLS_AUTH_CONFIG_CERT_GUID: + ASSERT (Private->CertGuid != NULL); + RStatus = StrToGuid ( + IfrNvData->CertGuid, + Private->CertGuid + ); + if (RETURN_ERROR (RStatus) || (IfrNvData->CertGuid[GUID_STRING_LENGTH] != L'\0')) { + Status = EFI_INVALID_PARAMETER; + break; + } + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + break; + default: + break; + } + } else if (Action == EFI_BROWSER_ACTION_FORM_CLOSE) { + CleanFileContext (Private); + } + +EXIT: + + if (!EFI_ERROR (Status)) { + BufferSize = sizeof (TLS_AUTH_CONFIG_IFR_NVDATA); + HiiSetBrowserData (&gTlsAuthConfigGuid, mTlsAuthConfigStorageName, BufferSize, (UINT8 *)IfrNvData, NULL); + } + + FreePool (IfrNvData); + + if (File != NULL) { + FreePool (File); + File = NULL; + } + + return EFI_SUCCESS; +} From 48302df477bd71e6973a9c8c759302b0847e04c4 Mon Sep 17 00:00:00 2001 From: Mike Beaton Date: Tue, 8 Oct 2024 05:34:09 +0100 Subject: [PATCH 2/4] OpenNetworkBoot: Update imported EDK 2 files to contain only the methods which are kept and modified --- Platform/OpenNetworkBoot/BmBoot.c | 2874 +++--------------- Platform/OpenNetworkBoot/BmBootDescription.c | 744 ----- Platform/OpenNetworkBoot/TlsAuthConfigImpl.c | 1064 ------- 3 files changed, 378 insertions(+), 4304 deletions(-) diff --git a/Platform/OpenNetworkBoot/BmBoot.c b/Platform/OpenNetworkBoot/BmBoot.c index d3aebc9262b..ba66ca2bcad 100644 --- a/Platform/OpenNetworkBoot/BmBoot.c +++ b/Platform/OpenNetworkBoot/BmBoot.c @@ -12,2653 +12,535 @@ SPDX-License-Identifier: BSD-2-Clause-Patent EFI_RAM_DISK_PROTOCOL *mRamDisk = NULL; -EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION mBmRefreshLegacyBootOption = NULL; -EFI_BOOT_MANAGER_LEGACY_BOOT mBmLegacyBoot = NULL; - -/// -/// This GUID is used for an EFI Variable that stores the front device pathes -/// for a partial device path that starts with the HD node. -/// -EFI_GUID mBmHardDriveBootVariableGuid = { - 0xfab7e9e1, 0x39dd, 0x4f2b, { 0x84, 0x08, 0xe2, 0x0e, 0x90, 0x6c, 0xb6, 0xde } -}; -EFI_GUID mBmAutoCreateBootOptionGuid = { - 0x8108ac4e, 0x9f11, 0x4d59, { 0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2 } -}; - -/** - - End Perf entry of BDS - - @param Event The triggered event. - @param Context Context for this event. - -**/ -VOID -EFIAPI -BmEndOfBdsPerfCode ( - IN EFI_EVENT Event, - IN VOID *Context - ) -{ - // - // Record the performance data for End of BDS - // - PERF_CROSSMODULE_END ("BDS"); - - return; -} - -/** - The function registers the legacy boot support capabilities. - - @param RefreshLegacyBootOption The function pointer to create all the legacy boot options. - @param LegacyBoot The function pointer to boot the legacy boot option. -**/ -VOID -EFIAPI -EfiBootManagerRegisterLegacyBootSupport ( - EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION RefreshLegacyBootOption, - EFI_BOOT_MANAGER_LEGACY_BOOT LegacyBoot - ) -{ - mBmRefreshLegacyBootOption = RefreshLegacyBootOption; - mBmLegacyBoot = LegacyBoot; -} - -/** - Return TRUE when the boot option is auto-created instead of manually added. - - @param BootOption Pointer to the boot option to check. - - @retval TRUE The boot option is auto-created. - @retval FALSE The boot option is manually added. -**/ -BOOLEAN -BmIsAutoCreateBootOption ( - EFI_BOOT_MANAGER_LOAD_OPTION *BootOption - ) -{ - if ((BootOption->OptionalDataSize == sizeof (EFI_GUID)) && - CompareGuid ((EFI_GUID *)BootOption->OptionalData, &mBmAutoCreateBootOptionGuid) - ) - { - return TRUE; - } else { - return FALSE; - } -} - /** - Find the boot option in the NV storage and return the option number. - - @param OptionToFind Boot option to be checked. + Expand the media device path which points to a BlockIo or SimpleFileSystem instance + by appending EFI_REMOVABLE_MEDIA_FILE_NAME. - @return The option number of the found boot option. + @param DevicePath The media device path pointing to a BlockIo or SimpleFileSystem instance. + @param FullPath The full path returned by the routine in last call. + Set to NULL in first call. + @return The next possible full path pointing to the load option. + Caller is responsible to free the memory. **/ -UINTN -BmFindBootOptionInVariable ( - IN EFI_BOOT_MANAGER_LOAD_OPTION *OptionToFind +EFI_DEVICE_PATH_PROTOCOL * +BmExpandMediaDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN EFI_DEVICE_PATH_PROTOCOL *FullPath ) { - EFI_STATUS Status; - EFI_BOOT_MANAGER_LOAD_OPTION BootOption; - UINTN OptionNumber; - CHAR16 OptionName[BM_OPTION_NAME_LEN]; - EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; - UINTN BootOptionCount; - UINTN Index; - - OptionNumber = LoadOptionNumberUnassigned; - - // - // Try to match the variable exactly if the option number is assigned - // - if (OptionToFind->OptionNumber != LoadOptionNumberUnassigned) { - UnicodeSPrint ( - OptionName, - sizeof (OptionName), - L"%s%04x", - mBmLoadOptionName[OptionToFind->OptionType], - OptionToFind->OptionNumber - ); - Status = EfiBootManagerVariableToLoadOption (OptionName, &BootOption); - - if (!EFI_ERROR (Status)) { - ASSERT (OptionToFind->OptionNumber == BootOption.OptionNumber); - if ((OptionToFind->Attributes == BootOption.Attributes) && - (StrCmp (OptionToFind->Description, BootOption.Description) == 0) && - (CompareMem (OptionToFind->FilePath, BootOption.FilePath, GetDevicePathSize (OptionToFind->FilePath)) == 0) && - (OptionToFind->OptionalDataSize == BootOption.OptionalDataSize) && - (CompareMem (OptionToFind->OptionalData, BootOption.OptionalData, OptionToFind->OptionalDataSize) == 0) - ) - { - OptionNumber = OptionToFind->OptionNumber; - } - - EfiBootManagerFreeLoadOption (&BootOption); - } - } + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + VOID *Buffer; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + EFI_DEVICE_PATH_PROTOCOL *NextFullPath; + UINTN Size; + UINTN TempSize; + EFI_HANDLE *SimpleFileSystemHandles; + UINTN NumberSimpleFileSystemHandles; + UINTN Index; + BOOLEAN GetNext; + GetNext = (BOOLEAN)(FullPath == NULL); // - // The option number assigned is either incorrect or unassigned. + // Check whether the device is connected // - if (OptionNumber == LoadOptionNumberUnassigned) { - BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); + TempDevicePath = DevicePath; + Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle); + if (!EFI_ERROR (Status)) { + ASSERT (IsDevicePathEnd (TempDevicePath)); - Index = EfiBootManagerFindLoadOption (OptionToFind, BootOptions, BootOptionCount); - if (Index != -1) { - OptionNumber = BootOptions[Index].OptionNumber; + NextFullPath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME); + // + // For device path pointing to simple file system, it only expands to one full path. + // + if (GetNext) { + return NextFullPath; + } else { + FreePool (NextFullPath); + return NULL; } - - EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); } - return OptionNumber; -} - -/** - Return the correct FV file path. - FV address may change across reboot. This routine promises the FV file device path is right. - - @param FilePath The Memory Mapped Device Path to get the file buffer. - - @return The updated FV Device Path pointint to the file. -**/ -EFI_DEVICE_PATH_PROTOCOL * -BmAdjustFvFilePath ( - IN EFI_DEVICE_PATH_PROTOCOL *FilePath - ) -{ - EFI_STATUS Status; - UINTN Index; - EFI_DEVICE_PATH_PROTOCOL *FvFileNode; - EFI_HANDLE FvHandle; - EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; - UINTN FvHandleCount; - EFI_HANDLE *FvHandles; - EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; - EFI_DEVICE_PATH_PROTOCOL *FullPath; + Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle); + ASSERT_EFI_ERROR (Status); // - // Get the file buffer by using the exactly FilePath. + // For device boot option only pointing to the removable device handle, + // should make sure all its children handles (its child partion or media handles) + // are created and connected. // - FvFileNode = FilePath; - Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &FvFileNode, &FvHandle); - if (!EFI_ERROR (Status)) { - return DuplicateDevicePath (FilePath); - } + gBS->ConnectController (Handle, NULL, NULL, TRUE); // - // Only wide match other FVs if it's a memory mapped FV file path. + // Issue a dummy read to the device to check for media change. + // When the removable media is changed, any Block IO read/write will + // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is + // returned. After the Block IO protocol is reinstalled, subsequent + // Block IO read/write will success. // - if ((DevicePathType (FilePath) != HARDWARE_DEVICE_PATH) || (DevicePathSubType (FilePath) != HW_MEMMAP_DP)) { + Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { return NULL; } - FvFileNode = NextDevicePathNode (FilePath); - - // - // Firstly find the FV file in current FV - // - gBS->HandleProtocol ( - gImageHandle, - &gEfiLoadedImageProtocolGuid, - (VOID **)&LoadedImage - ); - NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), FvFileNode); - FullPath = BmAdjustFvFilePath (NewDevicePath); - FreePool (NewDevicePath); - if (FullPath != NULL) { - return FullPath; + Buffer = AllocatePool (BlockIo->Media->BlockSize); + if (Buffer != NULL) { + BlockIo->ReadBlocks ( + BlockIo, + BlockIo->Media->MediaId, + 0, + BlockIo->Media->BlockSize, + Buffer + ); + FreePool (Buffer); } // - // Secondly find the FV file in all other FVs + // Detect the the default boot file from removable Media // + NextFullPath = NULL; + Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH; gBS->LocateHandleBuffer ( ByProtocol, - &gEfiFirmwareVolume2ProtocolGuid, + &gEfiSimpleFileSystemProtocolGuid, NULL, - &FvHandleCount, - &FvHandles + &NumberSimpleFileSystemHandles, + &SimpleFileSystemHandles ); - for (Index = 0; Index < FvHandleCount; Index++) { - if (FvHandles[Index] == LoadedImage->DeviceHandle) { - // - // Skip current FV, it was handed in first step. - // - continue; - } - - NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandles[Index]), FvFileNode); - FullPath = BmAdjustFvFilePath (NewDevicePath); - FreePool (NewDevicePath); - if (FullPath != NULL) { - break; + for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) { + // + // Get the device path size of SimpleFileSystem handle + // + TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]); + TempSize = GetDevicePathSize (TempDevicePath) - END_DEVICE_PATH_LENGTH; + // + // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path + // + if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) { + NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME); + if (GetNext) { + break; + } else { + GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0); + FreePool (NextFullPath); + NextFullPath = NULL; + } } } - if (FvHandles != NULL) { - FreePool (FvHandles); + if (SimpleFileSystemHandles != NULL) { + FreePool (SimpleFileSystemHandles); } - return FullPath; + return NextFullPath; } /** - Check if it's a Device Path pointing to FV file. - - The function doesn't garentee the device path points to existing FV file. - - @param DevicePath Input device path. + Check whether Left and Right are the same without matching the specific + device path data in IP device path and URI device path node. - @retval TRUE The device path is a FV File Device Path. - @retval FALSE The device path is NOT a FV File Device Path. + @retval TRUE Left and Right are the same. + @retval FALSE Left and Right are the different. **/ BOOLEAN -BmIsFvFilePath ( - IN EFI_DEVICE_PATH_PROTOCOL *DevicePath +BmMatchHttpBootDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *Left, + IN EFI_DEVICE_PATH_PROTOCOL *Right ) { - EFI_STATUS Status; - EFI_HANDLE Handle; - EFI_DEVICE_PATH_PROTOCOL *Node; + for ( ; !IsDevicePathEnd (Left) && !IsDevicePathEnd (Right) + ; Left = NextDevicePathNode (Left), Right = NextDevicePathNode (Right) + ) + { + if (CompareMem (Left, Right, DevicePathNodeLength (Left)) != 0) { + if ((DevicePathType (Left) != MESSAGING_DEVICE_PATH) || (DevicePathType (Right) != MESSAGING_DEVICE_PATH)) { + return FALSE; + } - Node = DevicePath; - Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &Handle); - if (!EFI_ERROR (Status)) { - return TRUE; - } + if (DevicePathSubType (Left) == MSG_DNS_DP) { + Left = NextDevicePathNode (Left); + } + + if (DevicePathSubType (Right) == MSG_DNS_DP) { + Right = NextDevicePathNode (Right); + } - if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) && (DevicePathSubType (DevicePath) == HW_MEMMAP_DP)) { - DevicePath = NextDevicePathNode (DevicePath); - if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) && (DevicePathSubType (DevicePath) == MEDIA_PIWG_FW_FILE_DP)) { - return IsDevicePathEnd (NextDevicePathNode (DevicePath)); + if (((DevicePathSubType (Left) != MSG_IPv4_DP) || (DevicePathSubType (Right) != MSG_IPv4_DP)) && + ((DevicePathSubType (Left) != MSG_IPv6_DP) || (DevicePathSubType (Right) != MSG_IPv6_DP)) && + ((DevicePathSubType (Left) != MSG_URI_DP) || (DevicePathSubType (Right) != MSG_URI_DP)) + ) + { + return FALSE; + } } } - return FALSE; + return (BOOLEAN)(IsDevicePathEnd (Left) && IsDevicePathEnd (Right)); } /** - Check whether a USB device match the specified USB Class device path. This - function follows "Load Option Processing" behavior in UEFI specification. - - @param UsbIo USB I/O protocol associated with the USB device. - @param UsbClass The USB Class device path to match. + Get the file buffer from the file system produced by Load File instance. - @retval TRUE The USB device match the USB Class device path. - @retval FALSE The USB device does not match the USB Class device path. + @param LoadFileHandle The handle of LoadFile instance. + @param RamDiskHandle Return the RAM Disk handle. + @return The next possible full path pointing to the load option. + Caller is responsible to free the memory. **/ -BOOLEAN -BmMatchUsbClass ( - IN EFI_USB_IO_PROTOCOL *UsbIo, - IN USB_CLASS_DEVICE_PATH *UsbClass +EFI_DEVICE_PATH_PROTOCOL * +BmExpandNetworkFileSystem ( + IN EFI_HANDLE LoadFileHandle, + OUT EFI_HANDLE *RamDiskHandle ) { - EFI_STATUS Status; - EFI_USB_DEVICE_DESCRIPTOR DevDesc; - EFI_USB_INTERFACE_DESCRIPTOR IfDesc; - UINT8 DeviceClass; - UINT8 DeviceSubClass; - UINT8 DeviceProtocol; - - if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) || - (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)) - { - return FALSE; - } + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_HANDLE *Handles; + UINTN HandleCount; + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *Node; - // - // Check Vendor Id and Product Id. - // - Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiBlockIoProtocolGuid, + NULL, + &HandleCount, + &Handles + ); if (EFI_ERROR (Status)) { - return FALSE; - } - - if ((UsbClass->VendorId != 0xffff) && - (UsbClass->VendorId != DevDesc.IdVendor)) - { - return FALSE; - } - - if ((UsbClass->ProductId != 0xffff) && - (UsbClass->ProductId != DevDesc.IdProduct)) - { - return FALSE; + Handles = NULL; + HandleCount = 0; } - DeviceClass = DevDesc.DeviceClass; - DeviceSubClass = DevDesc.DeviceSubClass; - DeviceProtocol = DevDesc.DeviceProtocol; - if (DeviceClass == 0) { - // - // If Class in Device Descriptor is set to 0, use the Class, SubClass and - // Protocol in Interface Descriptor instead. - // - Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc); - if (EFI_ERROR (Status)) { - return FALSE; + Handle = NULL; + for (Index = 0; Index < HandleCount; Index++) { + Node = DevicePathFromHandle (Handles[Index]); + Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle); + if (!EFI_ERROR (Status) && + (Handle == LoadFileHandle) && + (DevicePathType (Node) == MEDIA_DEVICE_PATH) && (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)) + { + // + // Find the BlockIo instance populated from the LoadFile. + // + Handle = Handles[Index]; + break; } - - DeviceClass = IfDesc.InterfaceClass; - DeviceSubClass = IfDesc.InterfaceSubClass; - DeviceProtocol = IfDesc.InterfaceProtocol; } - // - // Check Class, SubClass and Protocol. - // - if ((UsbClass->DeviceClass != 0xff) && - (UsbClass->DeviceClass != DeviceClass)) - { - return FALSE; + if (Handles != NULL) { + FreePool (Handles); } - if ((UsbClass->DeviceSubClass != 0xff) && - (UsbClass->DeviceSubClass != DeviceSubClass)) - { - return FALSE; + if (Index == HandleCount) { + Handle = NULL; } - if ((UsbClass->DeviceProtocol != 0xff) && - (UsbClass->DeviceProtocol != DeviceProtocol)) - { - return FALSE; - } + *RamDiskHandle = Handle; - return TRUE; + if (Handle != NULL) { + // + // Re-use BmExpandMediaDevicePath() to get the full device path of load option. + // But assume only one SimpleFileSystem can be found under the BlockIo. + // + return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), NULL); + } else { + return NULL; + } } /** - Check whether a USB device match the specified USB WWID device path. This - function follows "Load Option Processing" behavior in UEFI specification. - - @param UsbIo USB I/O protocol associated with the USB device. - @param UsbWwid The USB WWID device path to match. + Return the RAM Disk device path created by LoadFile. - @retval TRUE The USB device match the USB WWID device path. - @retval FALSE The USB device does not match the USB WWID device path. + @param FilePath The source file path. + @return Callee-to-free RAM Disk device path **/ -BOOLEAN -BmMatchUsbWwid ( - IN EFI_USB_IO_PROTOCOL *UsbIo, - IN USB_WWID_DEVICE_PATH *UsbWwid +EFI_DEVICE_PATH_PROTOCOL * +BmGetRamDiskDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath ) { - EFI_STATUS Status; - EFI_USB_DEVICE_DESCRIPTOR DevDesc; - EFI_USB_INTERFACE_DESCRIPTOR IfDesc; - UINT16 *LangIdTable; - UINT16 TableSize; - UINT16 Index; - CHAR16 *CompareStr; - UINTN CompareLen; - CHAR16 *SerialNumberStr; - UINTN Length; - - if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) || - (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP)) + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath; + EFI_DEVICE_PATH_PROTOCOL *Node; + EFI_HANDLE Handle; + + Node = FilePath; + Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle); + if (!EFI_ERROR (Status) && + (DevicePathType (Node) == MEDIA_DEVICE_PATH) && + (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP) + ) { - return FALSE; + // + // Construct the device path pointing to RAM Disk + // + Node = NextDevicePathNode (Node); + RamDiskDevicePath = DuplicateDevicePath (FilePath); + ASSERT (RamDiskDevicePath != NULL); + SetDevicePathEndNode ((VOID *)((UINTN)RamDiskDevicePath + ((UINTN)Node - (UINTN)FilePath))); + return RamDiskDevicePath; } - // - // Check Vendor Id and Product Id. - // - Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); - if (EFI_ERROR (Status)) { - return FALSE; - } + return NULL; +} - if ((DevDesc.IdVendor != UsbWwid->VendorId) || - (DevDesc.IdProduct != UsbWwid->ProductId)) - { - return FALSE; - } +/** + Return the buffer and buffer size occupied by the RAM Disk. - // - // Check Interface Number. - // - Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc); - if (EFI_ERROR (Status)) { - return FALSE; - } + @param RamDiskDevicePath RAM Disk device path. + @param RamDiskSizeInPages Return RAM Disk size in pages. - if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) { - return FALSE; - } + @retval RAM Disk buffer. +**/ +VOID * +BmGetRamDiskMemoryInfo ( + IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath, + OUT UINTN *RamDiskSizeInPages + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + UINT64 StartingAddr; + UINT64 EndingAddr; - // - // Check Serial Number. - // - if (DevDesc.StrSerialNumber == 0) { - return FALSE; - } + ASSERT (RamDiskDevicePath != NULL); - // - // Get all supported languages. - // - TableSize = 0; - LangIdTable = NULL; - Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize); - if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) { - return FALSE; - } + *RamDiskSizeInPages = 0; // - // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters. + // Get the buffer occupied by RAM Disk. // - CompareStr = (CHAR16 *)(UINTN)(UsbWwid + 1); - CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16); - if (CompareStr[CompareLen - 1] == L'\0') { - CompareLen--; - } + Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &RamDiskDevicePath, &Handle); + ASSERT_EFI_ERROR (Status); + ASSERT ( + (DevicePathType (RamDiskDevicePath) == MEDIA_DEVICE_PATH) && + (DevicePathSubType (RamDiskDevicePath) == MEDIA_RAM_DISK_DP) + ); + StartingAddr = ReadUnaligned64 ((UINT64 *)((MEDIA_RAM_DISK_DEVICE_PATH *)RamDiskDevicePath)->StartingAddr); + EndingAddr = ReadUnaligned64 ((UINT64 *)((MEDIA_RAM_DISK_DEVICE_PATH *)RamDiskDevicePath)->EndingAddr); + *RamDiskSizeInPages = EFI_SIZE_TO_PAGES ((UINTN)(EndingAddr - StartingAddr + 1)); + return (VOID *)(UINTN)StartingAddr; +} - // - // Compare serial number in each supported language. - // - for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) { - SerialNumberStr = NULL; - Status = UsbIo->UsbGetStringDescriptor ( - UsbIo, - LangIdTable[Index], - DevDesc.StrSerialNumber, - &SerialNumberStr - ); - if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) { - continue; - } +/** + Destroy the RAM Disk. - Length = StrLen (SerialNumberStr); - if ((Length >= CompareLen) && - (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) - { - FreePool (SerialNumberStr); - return TRUE; - } + The destroy operation includes to call RamDisk.Unregister to + unregister the RAM DISK from RAM DISK driver, free the memory + allocated for the RAM Disk. + + @param RamDiskDevicePath RAM Disk device path. +**/ +VOID +BmDestroyRamDisk ( + IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath + ) +{ + EFI_STATUS Status; + VOID *RamDiskBuffer; + UINTN RamDiskSizeInPages; + + ASSERT (RamDiskDevicePath != NULL); - FreePool (SerialNumberStr); + RamDiskBuffer = BmGetRamDiskMemoryInfo (RamDiskDevicePath, &RamDiskSizeInPages); + + // + // Destroy RAM Disk. + // + if (mRamDisk == NULL) { + Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID *)&mRamDisk); + ASSERT_EFI_ERROR (Status); } - return FALSE; + Status = mRamDisk->Unregister (RamDiskDevicePath); + ASSERT_EFI_ERROR (Status); + FreePages (RamDiskBuffer, RamDiskSizeInPages); } /** - Find a USB device which match the specified short-form device path start with - USB Class or USB WWID device path. If ParentDevicePath is NULL, this function - will search in all USB devices of the platform. If ParentDevicePath is not NULL, - this function will only search in its child devices. - - @param DevicePath The device path that contains USB Class or USB WWID device path. - @param ParentDevicePathSize The length of the device path before the USB Class or - USB WWID device path. - @param UsbIoHandleCount A pointer to the count of the returned USB IO handles. + Get the file buffer from the specified Load File instance. - @retval NULL The matched USB IO handles cannot be found. - @retval other The matched USB IO handles. + @param LoadFileHandle The specified Load File instance. + @param FilePath The file path which will pass to LoadFile(). + @return The full device path pointing to the load option buffer. **/ -EFI_HANDLE * -BmFindUsbDevice ( - IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, - IN UINTN ParentDevicePathSize, - OUT UINTN *UsbIoHandleCount +EFI_DEVICE_PATH_PROTOCOL * +BmExpandLoadFile ( + IN EFI_HANDLE LoadFileHandle, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath ) { EFI_STATUS Status; - EFI_HANDLE *UsbIoHandles; - EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath; - EFI_USB_IO_PROTOCOL *UsbIo; - UINTN Index; - BOOLEAN Matched; - - ASSERT (UsbIoHandleCount != NULL); + EFI_LOAD_FILE_PROTOCOL *LoadFile; + VOID *FileBuffer; + EFI_HANDLE RamDiskHandle; + UINTN BufferSize; + EFI_DEVICE_PATH_PROTOCOL *FullPath; - // - // Get all UsbIo Handles. - // - Status = gBS->LocateHandleBuffer ( - ByProtocol, - &gEfiUsbIoProtocolGuid, + Status = gBS->OpenProtocol ( + LoadFileHandle, + &gEfiLoadFileProtocolGuid, + (VOID **)&LoadFile, + gImageHandle, NULL, - UsbIoHandleCount, - &UsbIoHandles + EFI_OPEN_PROTOCOL_GET_PROTOCOL ); - if (EFI_ERROR (Status)) { - *UsbIoHandleCount = 0; - UsbIoHandles = NULL; + ASSERT_EFI_ERROR (Status); + + FileBuffer = NULL; + BufferSize = 0; + Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer); + if ((Status != EFI_WARN_FILE_SYSTEM) && (Status != EFI_BUFFER_TOO_SMALL)) { + return NULL; } - for (Index = 0; Index < *UsbIoHandleCount; ) { + if (Status == EFI_BUFFER_TOO_SMALL) { // - // Get the Usb IO interface. + // The load option buffer is directly returned by LoadFile. // - Status = gBS->HandleProtocol ( - UsbIoHandles[Index], - &gEfiUsbIoProtocolGuid, - (VOID **)&UsbIo - ); - UsbIoDevicePath = DevicePathFromHandle (UsbIoHandles[Index]); - Matched = FALSE; - if (!EFI_ERROR (Status) && (UsbIoDevicePath != NULL)) { - // - // Compare starting part of UsbIoHandle's device path with ParentDevicePath. - // - if (CompareMem (UsbIoDevicePath, DevicePath, ParentDevicePathSize) == 0) { - if (BmMatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *)((UINTN)DevicePath + ParentDevicePathSize)) || - BmMatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *)((UINTN)DevicePath + ParentDevicePathSize))) - { - Matched = TRUE; - } - } - } - - if (!Matched) { - (*UsbIoHandleCount)--; - CopyMem (&UsbIoHandles[Index], &UsbIoHandles[Index + 1], (*UsbIoHandleCount - Index) * sizeof (EFI_HANDLE)); - } else { - Index++; - } + return DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle)); } - return UsbIoHandles; -} + // + // The load option resides in a RAM disk. + // + FileBuffer = AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize)); + if (FileBuffer == NULL) { + DEBUG_CODE_BEGIN (); + EFI_DEVICE_PATH *LoadFilePath; + CHAR16 *LoadFileText; + CHAR16 *FileText; -/** - Expand USB Class or USB WWID device path node to be full device path of a USB - device in platform. - - This function support following 4 cases: - 1) Boot Option device path starts with a USB Class or USB WWID device path, - and there is no Media FilePath device path in the end. - In this case, it will follow Removable Media Boot Behavior. - 2) Boot Option device path starts with a USB Class or USB WWID device path, - and ended with Media FilePath device path. - 3) Boot Option device path starts with a full device path to a USB Host Controller, - contains a USB Class or USB WWID device path node, while not ended with Media - FilePath device path. In this case, it will follow Removable Media Boot Behavior. - 4) Boot Option device path starts with a full device path to a USB Host Controller, - contains a USB Class or USB WWID device path node, and ended with Media - FilePath device path. - - @param FilePath The device path pointing to a load option. - It could be a short-form device path. - @param FullPath The full path returned by the routine in last call. - Set to NULL in first call. - @param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer. + LoadFilePath = DevicePathFromHandle (LoadFileHandle); + if (LoadFilePath == NULL) { + LoadFileText = NULL; + } else { + LoadFileText = ConvertDevicePathToText (LoadFilePath, FALSE, FALSE); + } - @return The next possible full path pointing to the load option. - Caller is responsible to free the memory. -**/ -EFI_DEVICE_PATH_PROTOCOL * -BmExpandUsbDevicePath ( - IN EFI_DEVICE_PATH_PROTOCOL *FilePath, - IN EFI_DEVICE_PATH_PROTOCOL *FullPath, - IN EFI_DEVICE_PATH_PROTOCOL *ShortformNode - ) -{ - UINTN ParentDevicePathSize; - EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath; - EFI_DEVICE_PATH_PROTOCOL *NextFullPath; - EFI_HANDLE *Handles; - UINTN HandleCount; - UINTN Index; - BOOLEAN GetNext; + FileText = ConvertDevicePathToText (FilePath, FALSE, FALSE); - NextFullPath = NULL; - GetNext = (BOOLEAN)(FullPath == NULL); - ParentDevicePathSize = (UINTN)ShortformNode - (UINTN)FilePath; - RemainingDevicePath = NextDevicePathNode (ShortformNode); - Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount); + DEBUG (( + DEBUG_ERROR, + "%a:%a: failed to allocate reserved pages: " + "BufferSize=%Lu LoadFile=\"%s\" FilePath=\"%s\"\n", + gEfiCallerBaseName, + __func__, + (UINT64)BufferSize, + LoadFileText, + FileText + )); - for (Index = 0; Index < HandleCount; Index++) { - FilePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath); - if (FilePath == NULL) { - // - // Out of memory. - // - continue; + if (FileText != NULL) { + FreePool (FileText); } - NextFullPath = BmGetNextLoadOptionDevicePath (FilePath, NULL); - FreePool (FilePath); - if (NextFullPath == NULL) { - // - // No BlockIo or SimpleFileSystem under FilePath. - // - continue; + if (LoadFileText != NULL) { + FreePool (LoadFileText); } - if (GetNext) { - break; - } else { - GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0); - FreePool (NextFullPath); - NextFullPath = NULL; - } + DEBUG_CODE_END (); + return NULL; } - if (Handles != NULL) { - FreePool (Handles); + Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer); + if (EFI_ERROR (Status)) { + FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize)); + return NULL; } - return NextFullPath; + FullPath = BmExpandNetworkFileSystem (LoadFileHandle, &RamDiskHandle); + if (FullPath == NULL) { + // + // Free the memory occupied by the RAM disk if there is no BlockIo or SimpleFileSystem instance. + // + BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle)); + } + + return FullPath; } /** - Expand File-path device path node to be full device path in platform. + Return the full device path pointing to the load option. - @param FilePath The device path pointing to a load option. - It could be a short-form device path. - @param FullPath The full path returned by the routine in last call. - Set to NULL in first call. + FilePath may: + 1. Exactly matches to a LoadFile instance. + 2. Cannot match to any LoadFile instance. Wide match is required. + In either case, the routine may return: + 1. A copy of FilePath when FilePath matches to a LoadFile instance and + the LoadFile returns a load option buffer. + 2. A new device path with IP and URI information updated when wide match + happens. + 3. A new device path pointing to a load option in RAM disk. + In either case, only one full device path is returned for a specified + FilePath. - @return The next possible full path pointing to the load option. - Caller is responsible to free the memory. -**/ -EFI_DEVICE_PATH_PROTOCOL * -BmExpandFileDevicePath ( - IN EFI_DEVICE_PATH_PROTOCOL *FilePath, - IN EFI_DEVICE_PATH_PROTOCOL *FullPath - ) -{ - EFI_STATUS Status; - UINTN Index; - UINTN HandleCount; - EFI_HANDLE *Handles; - EFI_BLOCK_IO_PROTOCOL *BlockIo; - UINTN MediaType; - EFI_DEVICE_PATH_PROTOCOL *NextFullPath; - BOOLEAN GetNext; - - EfiBootManagerConnectAll (); - Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandleCount, &Handles); - if (EFI_ERROR (Status)) { - HandleCount = 0; - Handles = NULL; - } - - GetNext = (BOOLEAN)(FullPath == NULL); - NextFullPath = NULL; - // - // Enumerate all removable media devices followed by all fixed media devices, - // followed by media devices which don't layer on block io. - // - for (MediaType = 0; MediaType < 3; MediaType++) { - for (Index = 0; Index < HandleCount; Index++) { - Status = gBS->HandleProtocol (Handles[Index], &gEfiBlockIoProtocolGuid, (VOID *)&BlockIo); - if (EFI_ERROR (Status)) { - BlockIo = NULL; - } - - if (((MediaType == 0) && (BlockIo != NULL) && BlockIo->Media->RemovableMedia) || - ((MediaType == 1) && (BlockIo != NULL) && !BlockIo->Media->RemovableMedia) || - ((MediaType == 2) && (BlockIo == NULL)) - ) - { - NextFullPath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), FilePath); - if (GetNext) { - break; - } else { - GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0); - FreePool (NextFullPath); - NextFullPath = NULL; - } - } - } - - if (NextFullPath != NULL) { - break; - } - } - - if (Handles != NULL) { - FreePool (Handles); - } - - return NextFullPath; -} - -/** - Expand URI device path node to be full device path in platform. - - @param FilePath The device path pointing to a load option. - It could be a short-form device path. - @param FullPath The full path returned by the routine in last call. - Set to NULL in first call. + @param FilePath The media device path pointing to a LoadFile instance. - @return The next possible full path pointing to the load option. - Caller is responsible to free the memory. + @return The load option buffer. **/ EFI_DEVICE_PATH_PROTOCOL * -BmExpandUriDevicePath ( - IN EFI_DEVICE_PATH_PROTOCOL *FilePath, - IN EFI_DEVICE_PATH_PROTOCOL *FullPath +BmExpandLoadFiles ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath ) { EFI_STATUS Status; - UINTN Index; - UINTN HandleCount; + EFI_HANDLE Handle; EFI_HANDLE *Handles; - EFI_DEVICE_PATH_PROTOCOL *NextFullPath; - EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath; - BOOLEAN GetNext; - - EfiBootManagerConnectAll (); - Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &HandleCount, &Handles); - if (EFI_ERROR (Status)) { - HandleCount = 0; - Handles = NULL; - } - - NextFullPath = NULL; - GetNext = (BOOLEAN)(FullPath == NULL); - for (Index = 0; Index < HandleCount; Index++) { - NextFullPath = BmExpandLoadFile (Handles[Index], FilePath); - - if (NextFullPath == NULL) { - continue; - } - - if (GetNext) { - break; - } else { - GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0); - // - // Free the resource occupied by the RAM disk. - // - RamDiskDevicePath = BmGetRamDiskDevicePath (NextFullPath); - if (RamDiskDevicePath != NULL) { - BmDestroyRamDisk (RamDiskDevicePath); - FreePool (RamDiskDevicePath); - } - - FreePool (NextFullPath); - NextFullPath = NULL; - } - } - - if (Handles != NULL) { - FreePool (Handles); - } - - return NextFullPath; -} - -/** - Save the partition DevicePath to the CachedDevicePath as the first instance. - - @param CachedDevicePath The device path cache. - @param DevicePath The partition device path to be cached. -**/ -VOID -BmCachePartitionDevicePath ( - IN OUT EFI_DEVICE_PATH_PROTOCOL **CachedDevicePath, - IN EFI_DEVICE_PATH_PROTOCOL *DevicePath - ) -{ - EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; - UINTN Count; - - if (BmMatchDevicePaths (*CachedDevicePath, DevicePath)) { - TempDevicePath = *CachedDevicePath; - *CachedDevicePath = BmDelPartMatchInstance (*CachedDevicePath, DevicePath); - FreePool (TempDevicePath); - } - - if (*CachedDevicePath == NULL) { - *CachedDevicePath = DuplicateDevicePath (DevicePath); - return; - } - - TempDevicePath = *CachedDevicePath; - *CachedDevicePath = AppendDevicePathInstance (DevicePath, *CachedDevicePath); - if (TempDevicePath != NULL) { - FreePool (TempDevicePath); - } + UINTN HandleCount; + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *Node; // - // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller - // If the user try to boot many OS in different HDs or partitions, in theory, the 'HDDP' variable maybe become larger and larger. + // Get file buffer from load file instance. // - Count = 0; - TempDevicePath = *CachedDevicePath; - while (!IsDevicePathEnd (TempDevicePath)) { - TempDevicePath = NextDevicePathNode (TempDevicePath); + Node = FilePath; + Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle); + if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) { // - // Parse one instance + // When wide match happens, pass full device path to LoadFile (), + // otherwise, pass remaining device path to LoadFile (). // - while (!IsDevicePathEndType (TempDevicePath)) { - TempDevicePath = NextDevicePathNode (TempDevicePath); - } - - Count++; + FilePath = Node; + } else { + Handle = NULL; // - // If the CachedDevicePath variable contain too much instance, only remain 12 instances. + // Use wide match algorithm to find one when + // cannot find a LoadFile instance to exactly match the FilePath // - if (Count == 12) { - SetDevicePathEndNode (TempDevicePath); - break; - } - } -} - -/** - Expand a device path that starts with a hard drive media device path node to be a - full device path that includes the full hardware path to the device. We need - to do this so it can be booted. As an optimization the front match (the part point - to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable - so a connect all is not required on every boot. All successful history device path - which point to partition node (the front part) will be saved. - - @param FilePath The device path pointing to a load option. - It could be a short-form device path. - - @return The full device path pointing to the load option. -**/ -EFI_DEVICE_PATH_PROTOCOL * -BmExpandPartitionDevicePath ( - IN EFI_DEVICE_PATH_PROTOCOL *FilePath - ) -{ - EFI_STATUS Status; - UINTN BlockIoHandleCount; - EFI_HANDLE *BlockIoBuffer; - EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath; - UINTN Index; - EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath; - EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath; - EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; - EFI_DEVICE_PATH_PROTOCOL *FullPath; - UINTN CachedDevicePathSize; - BOOLEAN NeedAdjust; - EFI_DEVICE_PATH_PROTOCOL *Instance; - UINTN Size; - BOOLEAN MatchFound; - BOOLEAN ConnectAllAttempted; - - // - // Check if there is prestore 'HDDP' variable. - // If exist, search the front path which point to partition node in the variable instants. - // If fail to find or 'HDDP' not exist, reconnect all and search in all system - // - GetVariable2 (L"HDDP", &mBmHardDriveBootVariableGuid, (VOID **)&CachedDevicePath, &CachedDevicePathSize); - - // - // Delete the invalid 'HDDP' variable. - // - if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) { - FreePool (CachedDevicePath); - CachedDevicePath = NULL; - Status = gRT->SetVariable ( - L"HDDP", - &mBmHardDriveBootVariableGuid, - 0, - 0, - NULL - ); - ASSERT_EFI_ERROR (Status); - } - - FullPath = NULL; - if (CachedDevicePath != NULL) { - TempNewDevicePath = CachedDevicePath; - NeedAdjust = FALSE; - do { - // - // Check every instance of the variable - // First, check whether the instance contain the partition node, which is needed for distinguishing multi - // partial partition boot option. Second, check whether the instance could be connected. - // - Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size); - if (BmMatchPartitionDevicePathNode (Instance, (HARDDRIVE_DEVICE_PATH *)FilePath)) { - // - // Connect the device path instance, the device path point to hard drive media device path node - // e.g. ACPI() /PCI()/ATA()/Partition() - // - Status = EfiBootManagerConnectDevicePath (Instance, NULL); - if (!EFI_ERROR (Status)) { - TempDevicePath = AppendDevicePath (Instance, NextDevicePathNode (FilePath)); - // - // TempDevicePath = ACPI()/PCI()/ATA()/Partition() - // or = ACPI()/PCI()/ATA()/Partition()/.../A.EFI - // - // When TempDevicePath = ACPI()/PCI()/ATA()/Partition(), - // it may expand to two potienal full paths (nested partition, rarely happen): - // 1. ACPI()/PCI()/ATA()/Partition()/Partition(A1)/EFI/BootX64.EFI - // 2. ACPI()/PCI()/ATA()/Partition()/Partition(A2)/EFI/BootX64.EFI - // For simplicity, only #1 is returned. - // - FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL); - FreePool (TempDevicePath); - - if (FullPath != NULL) { - // - // Adjust the 'HDDP' instances sequence if the matched one is not first one. - // - if (NeedAdjust) { - BmCachePartitionDevicePath (&CachedDevicePath, Instance); - // - // Save the matching Device Path so we don't need to do a connect all next time - // Failing to save only impacts performance next time expanding the short-form device path - // - Status = gRT->SetVariable ( - L"HDDP", - &mBmHardDriveBootVariableGuid, - EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, - GetDevicePathSize (CachedDevicePath), - CachedDevicePath - ); - } - - FreePool (Instance); - FreePool (CachedDevicePath); - return FullPath; - } - } - } - - // - // Come here means the first instance is not matched - // - NeedAdjust = TRUE; - FreePool (Instance); - } while (TempNewDevicePath != NULL); - } - - // - // If we get here we fail to find or 'HDDP' not exist, and now we need - // to search all devices in the system for a matched partition - // - BlockIoBuffer = NULL; - MatchFound = FALSE; - ConnectAllAttempted = FALSE; - do { - if (BlockIoBuffer != NULL) { - FreePool (BlockIoBuffer); - } - - Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer); + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiLoadFileProtocolGuid, + NULL, + &HandleCount, + &Handles + ); if (EFI_ERROR (Status)) { - BlockIoHandleCount = 0; - BlockIoBuffer = NULL; + Handles = NULL; + HandleCount = 0; } - // - // Loop through all the device handles that support the BLOCK_IO Protocol - // - for (Index = 0; Index < BlockIoHandleCount; Index++) { - BlockIoDevicePath = DevicePathFromHandle (BlockIoBuffer[Index]); - if (BlockIoDevicePath == NULL) { - continue; - } - - if (BmMatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *)FilePath)) { - // - // Find the matched partition device path - // - TempDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (FilePath)); - FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL); - FreePool (TempDevicePath); - - if (FullPath != NULL) { - BmCachePartitionDevicePath (&CachedDevicePath, BlockIoDevicePath); - - // - // Save the matching Device Path so we don't need to do a connect all next time - // Failing to save only impacts performance next time expanding the short-form device path - // - Status = gRT->SetVariable ( - L"HDDP", - &mBmHardDriveBootVariableGuid, - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_NON_VOLATILE, - GetDevicePathSize (CachedDevicePath), - CachedDevicePath - ); - MatchFound = TRUE; - break; - } + for (Index = 0; Index < HandleCount; Index++) { + if (BmMatchHttpBootDevicePath (DevicePathFromHandle (Handles[Index]), FilePath)) { + Handle = Handles[Index]; + break; } } - // - // If we found a matching BLOCK_IO handle or we've already - // tried a ConnectAll, we are done searching. - // - if (MatchFound || ConnectAllAttempted) { - break; - } - - EfiBootManagerConnectAll (); - ConnectAllAttempted = TRUE; - } while (1); - - if (CachedDevicePath != NULL) { - FreePool (CachedDevicePath); - } - - if (BlockIoBuffer != NULL) { - FreePool (BlockIoBuffer); - } - - return FullPath; -} - -/** - Expand the media device path which points to a BlockIo or SimpleFileSystem instance - by appending EFI_REMOVABLE_MEDIA_FILE_NAME. - - @param DevicePath The media device path pointing to a BlockIo or SimpleFileSystem instance. - @param FullPath The full path returned by the routine in last call. - Set to NULL in first call. - - @return The next possible full path pointing to the load option. - Caller is responsible to free the memory. -**/ -EFI_DEVICE_PATH_PROTOCOL * -BmExpandMediaDevicePath ( - IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, - IN EFI_DEVICE_PATH_PROTOCOL *FullPath - ) -{ - EFI_STATUS Status; - EFI_HANDLE Handle; - EFI_BLOCK_IO_PROTOCOL *BlockIo; - VOID *Buffer; - EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; - EFI_DEVICE_PATH_PROTOCOL *NextFullPath; - UINTN Size; - UINTN TempSize; - EFI_HANDLE *SimpleFileSystemHandles; - UINTN NumberSimpleFileSystemHandles; - UINTN Index; - BOOLEAN GetNext; - - GetNext = (BOOLEAN)(FullPath == NULL); - // - // Check whether the device is connected - // - TempDevicePath = DevicePath; - Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle); - if (!EFI_ERROR (Status)) { - ASSERT (IsDevicePathEnd (TempDevicePath)); - - NextFullPath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME); - // - // For device path pointing to simple file system, it only expands to one full path. - // - if (GetNext) { - return NextFullPath; - } else { - FreePool (NextFullPath); - return NULL; + if (Handles != NULL) { + FreePool (Handles); } } - Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle); - ASSERT_EFI_ERROR (Status); - - // - // For device boot option only pointing to the removable device handle, - // should make sure all its children handles (its child partion or media handles) - // are created and connected. - // - gBS->ConnectController (Handle, NULL, NULL, TRUE); - - // - // Issue a dummy read to the device to check for media change. - // When the removable media is changed, any Block IO read/write will - // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is - // returned. After the Block IO protocol is reinstalled, subsequent - // Block IO read/write will success. - // - Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo); - ASSERT_EFI_ERROR (Status); - if (EFI_ERROR (Status)) { + if (Handle == NULL) { return NULL; } - Buffer = AllocatePool (BlockIo->Media->BlockSize); - if (Buffer != NULL) { - BlockIo->ReadBlocks ( - BlockIo, - BlockIo->Media->MediaId, - 0, - BlockIo->Media->BlockSize, - Buffer - ); - FreePool (Buffer); - } - - // - // Detect the the default boot file from removable Media - // - NextFullPath = NULL; - Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH; - gBS->LocateHandleBuffer ( - ByProtocol, - &gEfiSimpleFileSystemProtocolGuid, - NULL, - &NumberSimpleFileSystemHandles, - &SimpleFileSystemHandles - ); - for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) { - // - // Get the device path size of SimpleFileSystem handle - // - TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]); - TempSize = GetDevicePathSize (TempDevicePath) - END_DEVICE_PATH_LENGTH; - // - // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path - // - if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) { - NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME); - if (GetNext) { - break; - } else { - GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0); - FreePool (NextFullPath); - NextFullPath = NULL; - } - } - } - - if (SimpleFileSystemHandles != NULL) { - FreePool (SimpleFileSystemHandles); - } - - return NextFullPath; -} - -/** - Check whether Left and Right are the same without matching the specific - device path data in IP device path and URI device path node. - - @retval TRUE Left and Right are the same. - @retval FALSE Left and Right are the different. -**/ -BOOLEAN -BmMatchHttpBootDevicePath ( - IN EFI_DEVICE_PATH_PROTOCOL *Left, - IN EFI_DEVICE_PATH_PROTOCOL *Right - ) -{ - for ( ; !IsDevicePathEnd (Left) && !IsDevicePathEnd (Right) - ; Left = NextDevicePathNode (Left), Right = NextDevicePathNode (Right) - ) - { - if (CompareMem (Left, Right, DevicePathNodeLength (Left)) != 0) { - if ((DevicePathType (Left) != MESSAGING_DEVICE_PATH) || (DevicePathType (Right) != MESSAGING_DEVICE_PATH)) { - return FALSE; - } - - if (DevicePathSubType (Left) == MSG_DNS_DP) { - Left = NextDevicePathNode (Left); - } - - if (DevicePathSubType (Right) == MSG_DNS_DP) { - Right = NextDevicePathNode (Right); - } - - if (((DevicePathSubType (Left) != MSG_IPv4_DP) || (DevicePathSubType (Right) != MSG_IPv4_DP)) && - ((DevicePathSubType (Left) != MSG_IPv6_DP) || (DevicePathSubType (Right) != MSG_IPv6_DP)) && - ((DevicePathSubType (Left) != MSG_URI_DP) || (DevicePathSubType (Right) != MSG_URI_DP)) - ) - { - return FALSE; - } - } - } - - return (BOOLEAN)(IsDevicePathEnd (Left) && IsDevicePathEnd (Right)); -} - -/** - Get the file buffer from the file system produced by Load File instance. - - @param LoadFileHandle The handle of LoadFile instance. - @param RamDiskHandle Return the RAM Disk handle. - - @return The next possible full path pointing to the load option. - Caller is responsible to free the memory. -**/ -EFI_DEVICE_PATH_PROTOCOL * -BmExpandNetworkFileSystem ( - IN EFI_HANDLE LoadFileHandle, - OUT EFI_HANDLE *RamDiskHandle - ) -{ - EFI_STATUS Status; - EFI_HANDLE Handle; - EFI_HANDLE *Handles; - UINTN HandleCount; - UINTN Index; - EFI_DEVICE_PATH_PROTOCOL *Node; - - Status = gBS->LocateHandleBuffer ( - ByProtocol, - &gEfiBlockIoProtocolGuid, - NULL, - &HandleCount, - &Handles - ); - if (EFI_ERROR (Status)) { - Handles = NULL; - HandleCount = 0; - } - - Handle = NULL; - for (Index = 0; Index < HandleCount; Index++) { - Node = DevicePathFromHandle (Handles[Index]); - Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle); - if (!EFI_ERROR (Status) && - (Handle == LoadFileHandle) && - (DevicePathType (Node) == MEDIA_DEVICE_PATH) && (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)) - { - // - // Find the BlockIo instance populated from the LoadFile. - // - Handle = Handles[Index]; - break; - } - } - - if (Handles != NULL) { - FreePool (Handles); - } - - if (Index == HandleCount) { - Handle = NULL; - } - - *RamDiskHandle = Handle; - - if (Handle != NULL) { - // - // Re-use BmExpandMediaDevicePath() to get the full device path of load option. - // But assume only one SimpleFileSystem can be found under the BlockIo. - // - return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), NULL); - } else { - return NULL; - } -} - -/** - Return the RAM Disk device path created by LoadFile. - - @param FilePath The source file path. - - @return Callee-to-free RAM Disk device path -**/ -EFI_DEVICE_PATH_PROTOCOL * -BmGetRamDiskDevicePath ( - IN EFI_DEVICE_PATH_PROTOCOL *FilePath - ) -{ - EFI_STATUS Status; - EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath; - EFI_DEVICE_PATH_PROTOCOL *Node; - EFI_HANDLE Handle; - - Node = FilePath; - Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle); - if (!EFI_ERROR (Status) && - (DevicePathType (Node) == MEDIA_DEVICE_PATH) && - (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP) - ) - { - // - // Construct the device path pointing to RAM Disk - // - Node = NextDevicePathNode (Node); - RamDiskDevicePath = DuplicateDevicePath (FilePath); - ASSERT (RamDiskDevicePath != NULL); - SetDevicePathEndNode ((VOID *)((UINTN)RamDiskDevicePath + ((UINTN)Node - (UINTN)FilePath))); - return RamDiskDevicePath; - } - - return NULL; -} - -/** - Return the buffer and buffer size occupied by the RAM Disk. - - @param RamDiskDevicePath RAM Disk device path. - @param RamDiskSizeInPages Return RAM Disk size in pages. - - @retval RAM Disk buffer. -**/ -VOID * -BmGetRamDiskMemoryInfo ( - IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath, - OUT UINTN *RamDiskSizeInPages - ) -{ - EFI_STATUS Status; - EFI_HANDLE Handle; - UINT64 StartingAddr; - UINT64 EndingAddr; - - ASSERT (RamDiskDevicePath != NULL); - - *RamDiskSizeInPages = 0; - - // - // Get the buffer occupied by RAM Disk. - // - Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &RamDiskDevicePath, &Handle); - ASSERT_EFI_ERROR (Status); - ASSERT ( - (DevicePathType (RamDiskDevicePath) == MEDIA_DEVICE_PATH) && - (DevicePathSubType (RamDiskDevicePath) == MEDIA_RAM_DISK_DP) - ); - StartingAddr = ReadUnaligned64 ((UINT64 *)((MEDIA_RAM_DISK_DEVICE_PATH *)RamDiskDevicePath)->StartingAddr); - EndingAddr = ReadUnaligned64 ((UINT64 *)((MEDIA_RAM_DISK_DEVICE_PATH *)RamDiskDevicePath)->EndingAddr); - *RamDiskSizeInPages = EFI_SIZE_TO_PAGES ((UINTN)(EndingAddr - StartingAddr + 1)); - return (VOID *)(UINTN)StartingAddr; -} - -/** - Destroy the RAM Disk. - - The destroy operation includes to call RamDisk.Unregister to - unregister the RAM DISK from RAM DISK driver, free the memory - allocated for the RAM Disk. - - @param RamDiskDevicePath RAM Disk device path. -**/ -VOID -BmDestroyRamDisk ( - IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath - ) -{ - EFI_STATUS Status; - VOID *RamDiskBuffer; - UINTN RamDiskSizeInPages; - - ASSERT (RamDiskDevicePath != NULL); - - RamDiskBuffer = BmGetRamDiskMemoryInfo (RamDiskDevicePath, &RamDiskSizeInPages); - - // - // Destroy RAM Disk. - // - if (mRamDisk == NULL) { - Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID *)&mRamDisk); - ASSERT_EFI_ERROR (Status); - } - - Status = mRamDisk->Unregister (RamDiskDevicePath); - ASSERT_EFI_ERROR (Status); - FreePages (RamDiskBuffer, RamDiskSizeInPages); -} - -/** - Get the file buffer from the specified Load File instance. - - @param LoadFileHandle The specified Load File instance. - @param FilePath The file path which will pass to LoadFile(). - - @return The full device path pointing to the load option buffer. -**/ -EFI_DEVICE_PATH_PROTOCOL * -BmExpandLoadFile ( - IN EFI_HANDLE LoadFileHandle, - IN EFI_DEVICE_PATH_PROTOCOL *FilePath - ) -{ - EFI_STATUS Status; - EFI_LOAD_FILE_PROTOCOL *LoadFile; - VOID *FileBuffer; - EFI_HANDLE RamDiskHandle; - UINTN BufferSize; - EFI_DEVICE_PATH_PROTOCOL *FullPath; - - Status = gBS->OpenProtocol ( - LoadFileHandle, - &gEfiLoadFileProtocolGuid, - (VOID **)&LoadFile, - gImageHandle, - NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL - ); - ASSERT_EFI_ERROR (Status); - - FileBuffer = NULL; - BufferSize = 0; - Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer); - if ((Status != EFI_WARN_FILE_SYSTEM) && (Status != EFI_BUFFER_TOO_SMALL)) { - return NULL; - } - - if (Status == EFI_BUFFER_TOO_SMALL) { - // - // The load option buffer is directly returned by LoadFile. - // - return DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle)); - } - - // - // The load option resides in a RAM disk. - // - FileBuffer = AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize)); - if (FileBuffer == NULL) { - DEBUG_CODE_BEGIN (); - EFI_DEVICE_PATH *LoadFilePath; - CHAR16 *LoadFileText; - CHAR16 *FileText; - - LoadFilePath = DevicePathFromHandle (LoadFileHandle); - if (LoadFilePath == NULL) { - LoadFileText = NULL; - } else { - LoadFileText = ConvertDevicePathToText (LoadFilePath, FALSE, FALSE); - } - - FileText = ConvertDevicePathToText (FilePath, FALSE, FALSE); - - DEBUG (( - DEBUG_ERROR, - "%a:%a: failed to allocate reserved pages: " - "BufferSize=%Lu LoadFile=\"%s\" FilePath=\"%s\"\n", - gEfiCallerBaseName, - __func__, - (UINT64)BufferSize, - LoadFileText, - FileText - )); - - if (FileText != NULL) { - FreePool (FileText); - } - - if (LoadFileText != NULL) { - FreePool (LoadFileText); - } - - DEBUG_CODE_END (); - return NULL; - } - - Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer); - if (EFI_ERROR (Status)) { - FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize)); - return NULL; - } - - FullPath = BmExpandNetworkFileSystem (LoadFileHandle, &RamDiskHandle); - if (FullPath == NULL) { - // - // Free the memory occupied by the RAM disk if there is no BlockIo or SimpleFileSystem instance. - // - BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle)); - } - - return FullPath; -} - -/** - Return the full device path pointing to the load option. - - FilePath may: - 1. Exactly matches to a LoadFile instance. - 2. Cannot match to any LoadFile instance. Wide match is required. - In either case, the routine may return: - 1. A copy of FilePath when FilePath matches to a LoadFile instance and - the LoadFile returns a load option buffer. - 2. A new device path with IP and URI information updated when wide match - happens. - 3. A new device path pointing to a load option in RAM disk. - In either case, only one full device path is returned for a specified - FilePath. - - @param FilePath The media device path pointing to a LoadFile instance. - - @return The load option buffer. -**/ -EFI_DEVICE_PATH_PROTOCOL * -BmExpandLoadFiles ( - IN EFI_DEVICE_PATH_PROTOCOL *FilePath - ) -{ - EFI_STATUS Status; - EFI_HANDLE Handle; - EFI_HANDLE *Handles; - UINTN HandleCount; - UINTN Index; - EFI_DEVICE_PATH_PROTOCOL *Node; - - // - // Get file buffer from load file instance. - // - Node = FilePath; - Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle); - if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) { - // - // When wide match happens, pass full device path to LoadFile (), - // otherwise, pass remaining device path to LoadFile (). - // - FilePath = Node; - } else { - Handle = NULL; - // - // Use wide match algorithm to find one when - // cannot find a LoadFile instance to exactly match the FilePath - // - Status = gBS->LocateHandleBuffer ( - ByProtocol, - &gEfiLoadFileProtocolGuid, - NULL, - &HandleCount, - &Handles - ); - if (EFI_ERROR (Status)) { - Handles = NULL; - HandleCount = 0; - } - - for (Index = 0; Index < HandleCount; Index++) { - if (BmMatchHttpBootDevicePath (DevicePathFromHandle (Handles[Index]), FilePath)) { - Handle = Handles[Index]; - break; - } - } - - if (Handles != NULL) { - FreePool (Handles); - } - } - - if (Handle == NULL) { - return NULL; - } - - return BmExpandLoadFile (Handle, FilePath); -} - -/** - Get the load option by its device path. - - @param FilePath The device path pointing to a load option. - It could be a short-form device path. - @param FullPath Return the full device path of the load option after - short-form device path expanding. - Caller is responsible to free it. - @param FileSize Return the load option size. - - @return The load option buffer. Caller is responsible to free the memory. -**/ -VOID * -EFIAPI -EfiBootManagerGetLoadOptionBuffer ( - IN EFI_DEVICE_PATH_PROTOCOL *FilePath, - OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, - OUT UINTN *FileSize - ) -{ - *FullPath = NULL; - - EfiBootManagerConnectDevicePath (FilePath, NULL); - return BmGetNextLoadOptionBuffer (LoadOptionTypeMax, FilePath, FullPath, FileSize); -} - -/** - Get the next possible full path pointing to the load option. - The routine doesn't guarantee the returned full path points to an existing - file, and it also doesn't guarantee the existing file is a valid load option. - BmGetNextLoadOptionBuffer() guarantees. - - @param FilePath The device path pointing to a load option. - It could be a short-form device path. - @param FullPath The full path returned by the routine in last call. - Set to NULL in first call. - - @return The next possible full path pointing to the load option. - Caller is responsible to free the memory. -**/ -EFI_DEVICE_PATH_PROTOCOL * -BmGetNextLoadOptionDevicePath ( - IN EFI_DEVICE_PATH_PROTOCOL *FilePath, - IN EFI_DEVICE_PATH_PROTOCOL *FullPath - ) -{ - EFI_HANDLE Handle; - EFI_DEVICE_PATH_PROTOCOL *Node; - EFI_STATUS Status; - - ASSERT (FilePath != NULL); - - // - // Boot from media device by adding a default file name \EFI\BOOT\BOOT{machine type short-name}.EFI - // - Node = FilePath; - Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle); - if (EFI_ERROR (Status)) { - Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &Node, &Handle); - } - - if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) { - return BmExpandMediaDevicePath (FilePath, FullPath); - } - - // - // Expand the short-form device path to full device path - // - if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) && - (DevicePathSubType (FilePath) == MEDIA_HARDDRIVE_DP)) - { - // - // Expand the Harddrive device path - // - if (FullPath == NULL) { - return BmExpandPartitionDevicePath (FilePath); - } else { - return NULL; - } - } else if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) && - (DevicePathSubType (FilePath) == MEDIA_FILEPATH_DP)) - { - // - // Expand the File-path device path - // - return BmExpandFileDevicePath (FilePath, FullPath); - } else if ((DevicePathType (FilePath) == MESSAGING_DEVICE_PATH) && - (DevicePathSubType (FilePath) == MSG_URI_DP)) - { - // - // Expand the URI device path - // - return BmExpandUriDevicePath (FilePath, FullPath); - } else { - Node = FilePath; - Status = gBS->LocateDevicePath (&gEfiUsbIoProtocolGuid, &Node, &Handle); - if (EFI_ERROR (Status)) { - // - // Only expand the USB WWID/Class device path - // when FilePath doesn't point to a physical UsbIo controller. - // Otherwise, infinite recursion will happen. - // - for (Node = FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) { - if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) && - ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) || (DevicePathSubType (Node) == MSG_USB_WWID_DP))) - { - break; - } - } - - // - // Expand the USB WWID/Class device path - // - if (!IsDevicePathEnd (Node)) { - if (FilePath == Node) { - // - // Boot Option device path starts with USB Class or USB WWID device path. - // For Boot Option device path which doesn't begin with the USB Class or - // USB WWID device path, it's not needed to connect again here. - // - BmConnectUsbShortFormDevicePath (FilePath); - } - - return BmExpandUsbDevicePath (FilePath, FullPath, Node); - } - } - } - - // - // For the below cases, FilePath only expands to one Full path. - // So just handle the case when FullPath == NULL. - // - if (FullPath != NULL) { - return NULL; - } - - // - // Load option resides in FV. - // - if (BmIsFvFilePath (FilePath)) { - return BmAdjustFvFilePath (FilePath); - } - - // - // Load option resides in Simple File System. - // - Node = FilePath; - Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle); - if (!EFI_ERROR (Status)) { - return DuplicateDevicePath (FilePath); - } - - // - // Last chance to try: Load option may be loaded through LoadFile. - // - return BmExpandLoadFiles (FilePath); -} - -/** - Check if it's a Device Path pointing to BootManagerMenu. - - @param DevicePath Input device path. - - @retval TRUE The device path is BootManagerMenu File Device Path. - @retval FALSE The device path is NOT BootManagerMenu File Device Path. -**/ -BOOLEAN -BmIsBootManagerMenuFilePath ( - EFI_DEVICE_PATH_PROTOCOL *DevicePath - ) -{ - EFI_HANDLE FvHandle; - VOID *NameGuid; - EFI_STATUS Status; - - Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &DevicePath, &FvHandle); - if (!EFI_ERROR (Status)) { - NameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)DevicePath); - if (NameGuid != NULL) { - return CompareGuid (NameGuid, PcdGetPtr (PcdBootManagerMenuFile)); - } - } - - return FALSE; -} - -/** - Report status code with EFI_RETURN_STATUS_EXTENDED_DATA about LoadImage() or - StartImage() failure. - - @param[in] ErrorCode An Error Code in the Software Class, DXE Boot - Service Driver Subclass. ErrorCode will be used to - compose the Value parameter for status code - reporting. Must be one of - EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR and - EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED. - - @param[in] FailureStatus The failure status returned by the boot service - that should be reported. -**/ -VOID -BmReportLoadFailure ( - IN UINT32 ErrorCode, - IN EFI_STATUS FailureStatus - ) -{ - EFI_RETURN_STATUS_EXTENDED_DATA ExtendedData; - - if (!ReportErrorCodeEnabled ()) { - return; - } - - ASSERT ( - (ErrorCode == EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR) || - (ErrorCode == EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED) - ); - - ZeroMem (&ExtendedData, sizeof (ExtendedData)); - ExtendedData.ReturnStatus = FailureStatus; - - REPORT_STATUS_CODE_EX ( - (EFI_ERROR_CODE | EFI_ERROR_MINOR), - (EFI_SOFTWARE_DXE_BS_DRIVER | ErrorCode), - 0, - NULL, - NULL, - &ExtendedData.DataHeader + 1, - sizeof (ExtendedData) - sizeof (ExtendedData.DataHeader) - ); -} - -/** - Attempt to boot the EFI boot option. This routine sets L"BootCurent" and - also signals the EFI ready to boot event. If the device path for the option - starts with a BBS device path a legacy boot is attempted via the registered - gLegacyBoot function. Short form device paths are also supported via this - rountine. A device path starting with MEDIA_HARDDRIVE_DP, MSG_USB_WWID_DP, - MSG_USB_CLASS_DP gets expaned out to find the first device that matches. - If the BootOption Device Path fails the removable media boot algorithm - is attempted (\EFI\BOOTIA32.EFI, \EFI\BOOTX64.EFI,... only one file type - is tried per processor type) - - @param BootOption Boot Option to try and boot. - On return, BootOption->Status contains the boot status. - EFI_SUCCESS BootOption was booted - EFI_UNSUPPORTED A BBS device path was found with no valid callback - registered via EfiBootManagerInitialize(). - EFI_NOT_FOUND The BootOption was not found on the system - !EFI_SUCCESS BootOption failed with this error status - -**/ -VOID -EFIAPI -EfiBootManagerBoot ( - IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption - ) -{ - EFI_STATUS Status; - EFI_HANDLE ImageHandle; - EFI_LOADED_IMAGE_PROTOCOL *ImageInfo; - UINT16 Uint16; - UINTN OptionNumber; - UINTN OriginalOptionNumber; - EFI_DEVICE_PATH_PROTOCOL *FilePath; - EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath; - VOID *FileBuffer; - UINTN FileSize; - EFI_BOOT_LOGO_PROTOCOL *BootLogo; - EFI_EVENT LegacyBootEvent; - - if (BootOption == NULL) { - return; - } - - if ((BootOption->FilePath == NULL) || (BootOption->OptionType != LoadOptionTypeBoot)) { - BootOption->Status = EFI_INVALID_PARAMETER; - return; - } - - // - // 1. Create Boot#### for a temporary boot if there is no match Boot#### (i.e. a boot by selected a EFI Shell using "Boot From File") - // - OptionNumber = BmFindBootOptionInVariable (BootOption); - if (OptionNumber == LoadOptionNumberUnassigned) { - Status = BmGetFreeOptionNumber (LoadOptionTypeBoot, &Uint16); - if (!EFI_ERROR (Status)) { - // - // Save the BootOption->OptionNumber to restore later - // - OptionNumber = Uint16; - OriginalOptionNumber = BootOption->OptionNumber; - BootOption->OptionNumber = OptionNumber; - Status = EfiBootManagerLoadOptionToVariable (BootOption); - BootOption->OptionNumber = OriginalOptionNumber; - } - - if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status)); - BootOption->Status = Status; - return; - } - } - - // - // 2. Set BootCurrent - // - Uint16 = (UINT16)OptionNumber; - BmSetVariableAndReportStatusCodeOnError ( - L"BootCurrent", - &gEfiGlobalVariableGuid, - EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, - sizeof (UINT16), - &Uint16 - ); - - // - // 3. Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute - // the boot option. - // - if (BmIsBootManagerMenuFilePath (BootOption->FilePath)) { - DEBUG ((DEBUG_INFO, "[Bds] Booting Boot Manager Menu.\n")); - BmStopHotkeyService (NULL, NULL); - } else { - EfiSignalEventReadyToBoot (); - // - // Report Status Code to indicate ReadyToBoot was signalled - // - REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT)); - // - // 4. Repair system through DriverHealth protocol - // - BmRepairAllControllers (0); - } - - PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32)OptionNumber); - - // - // 5. Adjust the different type memory page number just before booting - // and save the updated info into the variable for next boot to use - // - BmSetMemoryTypeInformationVariable ( - (BOOLEAN)((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT) - ); - - // - // 6. Load EFI boot option to ImageHandle - // - DEBUG_CODE_BEGIN (); - if (BootOption->Description == NULL) { - DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting from unknown device path\n")); - } else { - DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting %s\n", BootOption->Description)); - } - - DEBUG_CODE_END (); - - ImageHandle = NULL; - RamDiskDevicePath = NULL; - if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) { - Status = EFI_NOT_FOUND; - FilePath = NULL; - EfiBootManagerConnectDevicePath (BootOption->FilePath, NULL); - FileBuffer = BmGetNextLoadOptionBuffer (LoadOptionTypeBoot, BootOption->FilePath, &FilePath, &FileSize); - if (FileBuffer != NULL) { - RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath); - - REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad)); - Status = gBS->LoadImage ( - TRUE, - gImageHandle, - FilePath, - FileBuffer, - FileSize, - &ImageHandle - ); - } - - if (FileBuffer != NULL) { - FreePool (FileBuffer); - } - - if (FilePath != NULL) { - FreePool (FilePath); - } - - if (EFI_ERROR (Status)) { - // - // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created - // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now. - // If the caller doesn't have the option to defer the execution of an image, we should - // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak. - // - if (Status == EFI_SECURITY_VIOLATION) { - gBS->UnloadImage (ImageHandle); - } - - // - // Destroy the RAM disk - // - if (RamDiskDevicePath != NULL) { - BmDestroyRamDisk (RamDiskDevicePath); - FreePool (RamDiskDevicePath); - } - - // - // Report Status Code with the failure status to indicate that the failure to load boot option - // - BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR, Status); - BootOption->Status = Status; - return; - } - } - - // - // Check to see if we should legacy BOOT. If yes then do the legacy boot - // Write boot to OS performance data for Legacy boot - // - if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) { - if (mBmLegacyBoot != NULL) { - // - // Write boot to OS performance data for legacy boot. - // - PERF_CODE ( - // - // Create an event to be signalled when Legacy Boot occurs to write performance data. - // - Status = EfiCreateEventLegacyBootEx ( - TPL_NOTIFY, - BmEndOfBdsPerfCode, - NULL, - &LegacyBootEvent - ); - ASSERT_EFI_ERROR (Status); - ); - - mBmLegacyBoot (BootOption); - } else { - BootOption->Status = EFI_UNSUPPORTED; - } - - PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32)OptionNumber); - return; - } - - // - // Provide the image with its load options - // - Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&ImageInfo); - ASSERT_EFI_ERROR (Status); - - if (!BmIsAutoCreateBootOption (BootOption)) { - ImageInfo->LoadOptionsSize = BootOption->OptionalDataSize; - ImageInfo->LoadOptions = BootOption->OptionalData; - } - - // - // Clean to NULL because the image is loaded directly from the firmwares boot manager. - // - ImageInfo->ParentHandle = NULL; - - // - // Before calling the image, enable the Watchdog Timer for 5 minutes period - // - gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL); - - // - // Write boot to OS performance data for UEFI boot - // - PERF_CODE ( - BmEndOfBdsPerfCode (NULL, NULL); - ); - - REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart)); - - Status = gBS->StartImage (ImageHandle, &BootOption->ExitDataSize, &BootOption->ExitData); - DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status)); - BootOption->Status = Status; - - // - // Destroy the RAM disk - // - if (RamDiskDevicePath != NULL) { - BmDestroyRamDisk (RamDiskDevicePath); - FreePool (RamDiskDevicePath); - } - - if (EFI_ERROR (Status)) { - // - // Report Status Code with the failure status to indicate that boot failure - // - BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED, Status); - } - - PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32)OptionNumber); - - // - // Clear the Watchdog Timer after the image returns - // - gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL); - - // - // Set Logo status invalid after trying one boot option - // - BootLogo = NULL; - Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **)&BootLogo); - if (!EFI_ERROR (Status) && (BootLogo != NULL)) { - Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0); - ASSERT_EFI_ERROR (Status); - } - - // - // Clear Boot Current - // - Status = gRT->SetVariable ( - L"BootCurrent", - &gEfiGlobalVariableGuid, - 0, - 0, - NULL - ); - // - // Deleting variable with current variable implementation shouldn't fail. - // When BootXXXX (e.g.: BootManagerMenu) boots BootYYYY, exiting BootYYYY causes BootCurrent deleted, - // exiting BootXXXX causes deleting BootCurrent returns EFI_NOT_FOUND. - // - ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND); -} - -/** - Check whether there is a instance in BlockIoDevicePath, which contain multi device path - instances, has the same partition node with HardDriveDevicePath device path - - @param BlockIoDevicePath Multi device path instances which need to check - @param HardDriveDevicePath A device path which starts with a hard drive media - device path. - - @retval TRUE There is a matched device path instance. - @retval FALSE There is no matched device path instance. - -**/ -BOOLEAN -BmMatchPartitionDevicePathNode ( - IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath, - IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath - ) -{ - HARDDRIVE_DEVICE_PATH *Node; - - if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) { - return FALSE; - } - - // - // Match all the partition device path nodes including the nested partition nodes - // - while (!IsDevicePathEnd (BlockIoDevicePath)) { - if ((DevicePathType (BlockIoDevicePath) == MEDIA_DEVICE_PATH) && - (DevicePathSubType (BlockIoDevicePath) == MEDIA_HARDDRIVE_DP) - ) - { - // - // See if the harddrive device path in blockio matches the orig Hard Drive Node - // - Node = (HARDDRIVE_DEVICE_PATH *)BlockIoDevicePath; - - // - // Match Signature and PartitionNumber. - // Unused bytes in Signature are initiaized with zeros. - // - if ((Node->PartitionNumber == HardDriveDevicePath->PartitionNumber) && - (Node->MBRType == HardDriveDevicePath->MBRType) && - (Node->SignatureType == HardDriveDevicePath->SignatureType) && - (CompareMem (Node->Signature, HardDriveDevicePath->Signature, sizeof (Node->Signature)) == 0)) - { - return TRUE; - } - } - - BlockIoDevicePath = NextDevicePathNode (BlockIoDevicePath); - } - - return FALSE; -} - -/** - Emuerate all possible bootable medias in the following order: - 1. Removable BlockIo - The boot option only points to the removable media - device, like USB key, DVD, Floppy etc. - 2. Fixed BlockIo - The boot option only points to a Fixed blockIo device, - like HardDisk. - 3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting - SimpleFileSystem Protocol, but not supporting BlockIo - protocol. - 4. LoadFile - The boot option points to the media supporting - LoadFile protocol. - Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior - - @param BootOptionCount Return the boot option count which has been found. - - @retval Pointer to the boot option array. -**/ -EFI_BOOT_MANAGER_LOAD_OPTION * -BmEnumerateBootOptions ( - UINTN *BootOptionCount - ) -{ - EFI_STATUS Status; - EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; - UINTN HandleCount; - EFI_HANDLE *Handles; - EFI_BLOCK_IO_PROTOCOL *BlkIo; - UINTN Removable; - UINTN Index; - CHAR16 *Description; - - ASSERT (BootOptionCount != NULL); - - *BootOptionCount = 0; - BootOptions = NULL; - - // - // Parse removable block io followed by fixed block io - // - gBS->LocateHandleBuffer ( - ByProtocol, - &gEfiBlockIoProtocolGuid, - NULL, - &HandleCount, - &Handles - ); - - for (Removable = 0; Removable < 2; Removable++) { - for (Index = 0; Index < HandleCount; Index++) { - Status = gBS->HandleProtocol ( - Handles[Index], - &gEfiBlockIoProtocolGuid, - (VOID **)&BlkIo - ); - if (EFI_ERROR (Status)) { - continue; - } - - // - // Skip the logical partitions - // - if (BlkIo->Media->LogicalPartition) { - continue; - } - - // - // Skip the fixed block io then the removable block io - // - if (BlkIo->Media->RemovableMedia == ((Removable == 0) ? FALSE : TRUE)) { - continue; - } - - // - // Skip removable media if not present - // - if ((BlkIo->Media->RemovableMedia == TRUE) && - (BlkIo->Media->MediaPresent == FALSE)) - { - continue; - } - - Description = BmGetBootDescription (Handles[Index]); - BootOptions = ReallocatePool ( - sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), - sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), - BootOptions - ); - ASSERT (BootOptions != NULL); - - Status = EfiBootManagerInitializeLoadOption ( - &BootOptions[(*BootOptionCount)++], - LoadOptionNumberUnassigned, - LoadOptionTypeBoot, - LOAD_OPTION_ACTIVE, - Description, - DevicePathFromHandle (Handles[Index]), - NULL, - 0 - ); - ASSERT_EFI_ERROR (Status); - - FreePool (Description); - } - } - - if (HandleCount != 0) { - FreePool (Handles); - } - - // - // Parse simple file system not based on block io - // - gBS->LocateHandleBuffer ( - ByProtocol, - &gEfiSimpleFileSystemProtocolGuid, - NULL, - &HandleCount, - &Handles - ); - for (Index = 0; Index < HandleCount; Index++) { - Status = gBS->HandleProtocol ( - Handles[Index], - &gEfiBlockIoProtocolGuid, - (VOID **)&BlkIo - ); - if (!EFI_ERROR (Status)) { - // - // Skip if the file system handle supports a BlkIo protocol, which we've handled in above - // - continue; - } - - Description = BmGetBootDescription (Handles[Index]); - BootOptions = ReallocatePool ( - sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), - sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), - BootOptions - ); - ASSERT (BootOptions != NULL); - - Status = EfiBootManagerInitializeLoadOption ( - &BootOptions[(*BootOptionCount)++], - LoadOptionNumberUnassigned, - LoadOptionTypeBoot, - LOAD_OPTION_ACTIVE, - Description, - DevicePathFromHandle (Handles[Index]), - NULL, - 0 - ); - ASSERT_EFI_ERROR (Status); - FreePool (Description); - } - - if (HandleCount != 0) { - FreePool (Handles); - } - - // - // Parse load file protocol - // - gBS->LocateHandleBuffer ( - ByProtocol, - &gEfiLoadFileProtocolGuid, - NULL, - &HandleCount, - &Handles - ); - for (Index = 0; Index < HandleCount; Index++) { - // - // Ignore BootManagerMenu. its boot option will be created by EfiBootManagerGetBootManagerMenu(). - // - if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) { - continue; - } - - Description = BmGetBootDescription (Handles[Index]); - BootOptions = ReallocatePool ( - sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), - sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), - BootOptions - ); - ASSERT (BootOptions != NULL); - - Status = EfiBootManagerInitializeLoadOption ( - &BootOptions[(*BootOptionCount)++], - LoadOptionNumberUnassigned, - LoadOptionTypeBoot, - LOAD_OPTION_ACTIVE, - Description, - DevicePathFromHandle (Handles[Index]), - NULL, - 0 - ); - ASSERT_EFI_ERROR (Status); - FreePool (Description); - } - - if (HandleCount != 0) { - FreePool (Handles); - } - - BmMakeBootOptionDescriptionUnique (BootOptions, *BootOptionCount); - return BootOptions; -} - -/** - The function enumerates all boot options, creates them and registers them in the BootOrder variable. -**/ -VOID -EFIAPI -EfiBootManagerRefreshAllBootOption ( - VOID - ) -{ - EFI_STATUS Status; - EFI_BOOT_MANAGER_LOAD_OPTION *NvBootOptions; - UINTN NvBootOptionCount; - EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; - UINTN BootOptionCount; - EFI_BOOT_MANAGER_LOAD_OPTION *UpdatedBootOptions; - UINTN UpdatedBootOptionCount; - UINTN Index; - EDKII_PLATFORM_BOOT_MANAGER_PROTOCOL *PlatformBootManager; - - // - // Optionally refresh the legacy boot option - // - if (mBmRefreshLegacyBootOption != NULL) { - mBmRefreshLegacyBootOption (); - } - - BootOptions = BmEnumerateBootOptions (&BootOptionCount); - - // - // Mark the boot option as added by BDS by setting OptionalData to a special GUID - // - for (Index = 0; Index < BootOptionCount; Index++) { - BootOptions[Index].OptionalData = AllocateCopyPool (sizeof (EFI_GUID), &mBmAutoCreateBootOptionGuid); - BootOptions[Index].OptionalDataSize = sizeof (EFI_GUID); - } - - // - // Locate Platform Boot Options Protocol - // - Status = gBS->LocateProtocol ( - &gEdkiiPlatformBootManagerProtocolGuid, - NULL, - (VOID **)&PlatformBootManager - ); - if (!EFI_ERROR (Status)) { - // - // If found, call platform specific refresh to all auto enumerated and NV - // boot options. - // - Status = PlatformBootManager->RefreshAllBootOptions ( - (CONST EFI_BOOT_MANAGER_LOAD_OPTION *)BootOptions, - (CONST UINTN)BootOptionCount, - &UpdatedBootOptions, - &UpdatedBootOptionCount - ); - if (!EFI_ERROR (Status)) { - EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); - BootOptions = UpdatedBootOptions; - BootOptionCount = UpdatedBootOptionCount; - } - } - - NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot); - - // - // Remove invalid EFI boot options from NV - // - for (Index = 0; Index < NvBootOptionCount; Index++) { - if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) || - (DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP) - ) && BmIsAutoCreateBootOption (&NvBootOptions[Index]) - ) - { - // - // Only check those added by BDS - // so that the boot options added by end-user or OS installer won't be deleted - // - if (EfiBootManagerFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == -1) { - Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot); - // - // Deleting variable with current variable implementation shouldn't fail. - // - ASSERT_EFI_ERROR (Status); - } - } - } - - // - // Add new EFI boot options to NV - // - for (Index = 0; Index < BootOptionCount; Index++) { - if (EfiBootManagerFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == -1) { - EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN)-1); - // - // Try best to add the boot options so continue upon failure. - // - } - } - - EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); - EfiBootManagerFreeLoadOptions (NvBootOptions, NvBootOptionCount); -} - -/** - This function is called to get or create the boot option for the Boot Manager Menu. - - The Boot Manager Menu is shown after successfully booting a boot option. - This function will first try to search the BootManagerMenuFile is in the same FV as - the module links to this library. If fails, it will search in all FVs. - - @param BootOption Return the boot option of the Boot Manager Menu - - @retval EFI_SUCCESS Successfully register the Boot Manager Menu. - @retval EFI_NOT_FOUND The Boot Manager Menu cannot be found. - @retval others Return status of gRT->SetVariable (). BootOption still points - to the Boot Manager Menu even the Status is not EFI_SUCCESS - and EFI_NOT_FOUND. -**/ -EFI_STATUS -BmRegisterBootManagerMenu ( - OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption - ) -{ - EFI_STATUS Status; - CHAR16 *Description; - UINTN DescriptionLength; - EFI_DEVICE_PATH_PROTOCOL *DevicePath; - UINTN HandleCount; - EFI_HANDLE *Handles; - UINTN Index; - - DevicePath = NULL; - Description = NULL; - // - // Try to find BootManagerMenu from LoadFile protocol - // - gBS->LocateHandleBuffer ( - ByProtocol, - &gEfiLoadFileProtocolGuid, - NULL, - &HandleCount, - &Handles - ); - for (Index = 0; Index < HandleCount; Index++) { - if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) { - DevicePath = DuplicateDevicePath (DevicePathFromHandle (Handles[Index])); - Description = BmGetBootDescription (Handles[Index]); - break; - } - } - - if (HandleCount != 0) { - FreePool (Handles); - } - - if (DevicePath == NULL) { - Status = GetFileDevicePathFromAnyFv ( - PcdGetPtr (PcdBootManagerMenuFile), - EFI_SECTION_PE32, - 0, - &DevicePath - ); - if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_WARN, "[Bds]BootManagerMenu FFS section can not be found, skip its boot option registration\n")); - return EFI_NOT_FOUND; - } - - ASSERT (DevicePath != NULL); - // - // Get BootManagerMenu application's description from EFI User Interface Section. - // - Status = GetSectionFromAnyFv ( - PcdGetPtr (PcdBootManagerMenuFile), - EFI_SECTION_USER_INTERFACE, - 0, - (VOID **)&Description, - &DescriptionLength - ); - if (EFI_ERROR (Status)) { - Description = NULL; - } - } - - Status = EfiBootManagerInitializeLoadOption ( - BootOption, - LoadOptionNumberUnassigned, - LoadOptionTypeBoot, - LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN, - (Description != NULL) ? Description : L"Boot Manager Menu", - DevicePath, - NULL, - 0 - ); - ASSERT_EFI_ERROR (Status); - FreePool (DevicePath); - if (Description != NULL) { - FreePool (Description); - } - - DEBUG_CODE ( - EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; - UINTN BootOptionCount; - - BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); - ASSERT (EfiBootManagerFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1); - EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); - ); - - return EfiBootManagerAddLoadOptionVariable (BootOption, (UINTN)-1); -} - -/** - Return the boot option corresponding to the Boot Manager Menu. - It may automatically create one if the boot option hasn't been created yet. - - @param BootOption Return the Boot Manager Menu. - - @retval EFI_SUCCESS The Boot Manager Menu is successfully returned. - @retval EFI_NOT_FOUND The Boot Manager Menu cannot be found. - @retval others Return status of gRT->SetVariable (). BootOption still points - to the Boot Manager Menu even the Status is not EFI_SUCCESS - and EFI_NOT_FOUND. -**/ -EFI_STATUS -EFIAPI -EfiBootManagerGetBootManagerMenu ( - EFI_BOOT_MANAGER_LOAD_OPTION *BootOption - ) -{ - EFI_STATUS Status; - UINTN BootOptionCount; - EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; - UINTN Index; - - BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); - - for (Index = 0; Index < BootOptionCount; Index++) { - if (BmIsBootManagerMenuFilePath (BootOptions[Index].FilePath)) { - Status = EfiBootManagerInitializeLoadOption ( - BootOption, - BootOptions[Index].OptionNumber, - BootOptions[Index].OptionType, - BootOptions[Index].Attributes, - BootOptions[Index].Description, - BootOptions[Index].FilePath, - BootOptions[Index].OptionalData, - BootOptions[Index].OptionalDataSize - ); - ASSERT_EFI_ERROR (Status); - break; - } - } - - EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); - - // - // Automatically create the Boot#### for Boot Manager Menu when not found. - // - if (Index == BootOptionCount) { - return BmRegisterBootManagerMenu (BootOption); - } else { - return EFI_SUCCESS; - } -} - -/** - Get the next possible full path pointing to the load option. - The routine doesn't guarantee the returned full path points to an existing - file, and it also doesn't guarantee the existing file is a valid load option. - BmGetNextLoadOptionBuffer() guarantees. - - @param FilePath The device path pointing to a load option. - It could be a short-form device path. - @param FullPath The full path returned by the routine in last call. - Set to NULL in first call. - - @return The next possible full path pointing to the load option. - Caller is responsible to free the memory. -**/ -EFI_DEVICE_PATH_PROTOCOL * -EFIAPI -EfiBootManagerGetNextLoadOptionDevicePath ( - IN EFI_DEVICE_PATH_PROTOCOL *FilePath, - IN EFI_DEVICE_PATH_PROTOCOL *FullPath - ) -{ - return BmGetNextLoadOptionDevicePath (FilePath, FullPath); + return BmExpandLoadFile (Handle, FilePath); } diff --git a/Platform/OpenNetworkBoot/BmBootDescription.c b/Platform/OpenNetworkBoot/BmBootDescription.c index 480f0931e0c..4b8c8f472e8 100644 --- a/Platform/OpenNetworkBoot/BmBootDescription.c +++ b/Platform/OpenNetworkBoot/BmBootDescription.c @@ -9,359 +9,6 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "InternalBm.h" -#define VENDOR_IDENTIFICATION_OFFSET 3 -#define VENDOR_IDENTIFICATION_LENGTH 8 -#define PRODUCT_IDENTIFICATION_OFFSET 11 -#define PRODUCT_IDENTIFICATION_LENGTH 16 - -CONST UINT16 mBmUsbLangId = 0x0409; // English -CHAR16 mBmUefiPrefix[] = L"UEFI "; - -LIST_ENTRY mPlatformBootDescriptionHandlers = INITIALIZE_LIST_HEAD_VARIABLE (mPlatformBootDescriptionHandlers); - -/** - For a bootable Device path, return its boot type. - - @param DevicePath The bootable device Path to check - - @retval AcpiFloppyBoot If given device path contains ACPI_DEVICE_PATH type device path node - which HID is floppy device. - @retval MessageAtapiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node - and its last device path node's subtype is MSG_ATAPI_DP. - @retval MessageSataBoot If given device path contains MESSAGING_DEVICE_PATH type device path node - and its last device path node's subtype is MSG_SATA_DP. - @retval MessageScsiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node - and its last device path node's subtype is MSG_SCSI_DP. - @retval MessageUsbBoot If given device path contains MESSAGING_DEVICE_PATH type device path node - and its last device path node's subtype is MSG_USB_DP. - @retval BmMiscBoot If tiven device path doesn't match the above condition. - -**/ -BM_BOOT_TYPE -BmDevicePathType ( - IN EFI_DEVICE_PATH_PROTOCOL *DevicePath - ) -{ - EFI_DEVICE_PATH_PROTOCOL *Node; - EFI_DEVICE_PATH_PROTOCOL *NextNode; - - ASSERT (DevicePath != NULL); - - for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) { - switch (DevicePathType (Node)) { - case ACPI_DEVICE_PATH: - if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH *)Node)->HID) == 0x0604) { - return BmAcpiFloppyBoot; - } - - break; - - case HARDWARE_DEVICE_PATH: - if (DevicePathSubType (Node) == HW_CONTROLLER_DP) { - return BmHardwareDeviceBoot; - } - - break; - - case MESSAGING_DEVICE_PATH: - // - // Skip LUN device node - // - NextNode = Node; - do { - NextNode = NextDevicePathNode (NextNode); - } while ( - (DevicePathType (NextNode) == MESSAGING_DEVICE_PATH) && - (DevicePathSubType (NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP) - ); - - // - // If the device path not only point to driver device, it is not a messaging device path, - // - if (!IsDevicePathEndType (NextNode)) { - continue; - } - - switch (DevicePathSubType (Node)) { - case MSG_ATAPI_DP: - return BmMessageAtapiBoot; - break; - - case MSG_SATA_DP: - return BmMessageSataBoot; - break; - - case MSG_USB_DP: - return BmMessageUsbBoot; - break; - - case MSG_SCSI_DP: - return BmMessageScsiBoot; - break; - } - } - } - - return BmMiscBoot; -} - -/** - Eliminate the extra spaces in the Str to one space. - - @param Str Input string info. -**/ -VOID -BmEliminateExtraSpaces ( - IN CHAR16 *Str - ) -{ - UINTN Index; - UINTN ActualIndex; - - for (Index = 0, ActualIndex = 0; Str[Index] != L'\0'; Index++) { - if ((Str[Index] != L' ') || ((ActualIndex > 0) && (Str[ActualIndex - 1] != L' '))) { - Str[ActualIndex++] = Str[Index]; - } - } - - Str[ActualIndex] = L'\0'; -} - -/** - Try to get the controller's ATA/ATAPI description. - - @param Handle Controller handle. - - @return The description string. -**/ -CHAR16 * -BmGetDescriptionFromDiskInfo ( - IN EFI_HANDLE Handle - ) -{ - UINTN Index; - EFI_STATUS Status; - EFI_DISK_INFO_PROTOCOL *DiskInfo; - UINT32 BufferSize; - EFI_ATAPI_IDENTIFY_DATA IdentifyData; - EFI_SCSI_INQUIRY_DATA InquiryData; - CHAR16 *Description; - UINTN Length; - CONST UINTN ModelNameLength = 40; - CONST UINTN SerialNumberLength = 20; - CHAR8 *StrPtr; - UINT8 Temp; - EFI_DEVICE_PATH_PROTOCOL *DevicePath; - - Description = NULL; - - Status = gBS->HandleProtocol ( - Handle, - &gEfiDiskInfoProtocolGuid, - (VOID **)&DiskInfo - ); - if (EFI_ERROR (Status)) { - return NULL; - } - - if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoAhciInterfaceGuid) || - CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) - { - BufferSize = sizeof (EFI_ATAPI_IDENTIFY_DATA); - Status = DiskInfo->Identify ( - DiskInfo, - &IdentifyData, - &BufferSize - ); - if (!EFI_ERROR (Status)) { - Description = AllocateZeroPool ((ModelNameLength + SerialNumberLength + 2) * sizeof (CHAR16)); - ASSERT (Description != NULL); - for (Index = 0; Index + 1 < ModelNameLength; Index += 2) { - Description[Index] = (CHAR16)IdentifyData.ModelName[Index + 1]; - Description[Index + 1] = (CHAR16)IdentifyData.ModelName[Index]; - } - - Length = Index; - Description[Length++] = L' '; - - for (Index = 0; Index + 1 < SerialNumberLength; Index += 2) { - Description[Length + Index] = (CHAR16)IdentifyData.SerialNo[Index + 1]; - Description[Length + Index + 1] = (CHAR16)IdentifyData.SerialNo[Index]; - } - - Length += Index; - Description[Length++] = L'\0'; - ASSERT (Length == ModelNameLength + SerialNumberLength + 2); - - BmEliminateExtraSpaces (Description); - } - } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoScsiInterfaceGuid) || - CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoUfsInterfaceGuid)) - { - BufferSize = sizeof (EFI_SCSI_INQUIRY_DATA); - Status = DiskInfo->Inquiry ( - DiskInfo, - &InquiryData, - &BufferSize - ); - if (!EFI_ERROR (Status)) { - Description = AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2) * sizeof (CHAR16)); - ASSERT (Description != NULL); - - // - // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification - // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification, - // Here combine the vendor identification and product identification to the description. - // - StrPtr = (CHAR8 *)(&InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET]); - Temp = StrPtr[VENDOR_IDENTIFICATION_LENGTH]; - StrPtr[VENDOR_IDENTIFICATION_LENGTH] = '\0'; - AsciiStrToUnicodeStrS (StrPtr, Description, VENDOR_IDENTIFICATION_LENGTH + 1); - StrPtr[VENDOR_IDENTIFICATION_LENGTH] = Temp; - - // - // Add one space at the middle of vendor information and product information. - // - Description[VENDOR_IDENTIFICATION_LENGTH] = L' '; - - StrPtr = (CHAR8 *)(&InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET]); - StrPtr[PRODUCT_IDENTIFICATION_LENGTH] = '\0'; - AsciiStrToUnicodeStrS (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1, PRODUCT_IDENTIFICATION_LENGTH + 1); - - BmEliminateExtraSpaces (Description); - } - } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoSdMmcInterfaceGuid)) { - DevicePath = DevicePathFromHandle (Handle); - if (DevicePath == NULL) { - return NULL; - } - - while (!IsDevicePathEnd (DevicePath) && (DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH)) { - DevicePath = NextDevicePathNode (DevicePath); - } - - if (IsDevicePathEnd (DevicePath)) { - return NULL; - } - - if (DevicePathSubType (DevicePath) == MSG_SD_DP) { - Description = L"SD Device"; - } else if (DevicePathSubType (DevicePath) == MSG_EMMC_DP) { - Description = L"eMMC Device"; - } else { - return NULL; - } - - Description = AllocateCopyPool (StrSize (Description), Description); - } - - return Description; -} - -/** - Try to get the controller's USB description. - - @param Handle Controller handle. - - @return The description string. -**/ -CHAR16 * -BmGetUsbDescription ( - IN EFI_HANDLE Handle - ) -{ - EFI_STATUS Status; - EFI_USB_IO_PROTOCOL *UsbIo; - CHAR16 NullChar; - CHAR16 *Manufacturer; - CHAR16 *Product; - CHAR16 *SerialNumber; - CHAR16 *Description; - EFI_USB_DEVICE_DESCRIPTOR DevDesc; - UINTN DescMaxSize; - - Status = gBS->HandleProtocol ( - Handle, - &gEfiUsbIoProtocolGuid, - (VOID **)&UsbIo - ); - if (EFI_ERROR (Status)) { - return NULL; - } - - NullChar = L'\0'; - - Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); - if (EFI_ERROR (Status)) { - return NULL; - } - - Status = UsbIo->UsbGetStringDescriptor ( - UsbIo, - mBmUsbLangId, - DevDesc.StrManufacturer, - &Manufacturer - ); - if (EFI_ERROR (Status)) { - Manufacturer = &NullChar; - } - - Status = UsbIo->UsbGetStringDescriptor ( - UsbIo, - mBmUsbLangId, - DevDesc.StrProduct, - &Product - ); - if (EFI_ERROR (Status)) { - Product = &NullChar; - } - - Status = UsbIo->UsbGetStringDescriptor ( - UsbIo, - mBmUsbLangId, - DevDesc.StrSerialNumber, - &SerialNumber - ); - if (EFI_ERROR (Status)) { - SerialNumber = &NullChar; - } - - if ((Manufacturer == &NullChar) && - (Product == &NullChar) && - (SerialNumber == &NullChar) - ) - { - return NULL; - } - - DescMaxSize = StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber); - Description = AllocateZeroPool (DescMaxSize); - ASSERT (Description != NULL); - StrCatS (Description, DescMaxSize/sizeof (CHAR16), Manufacturer); - StrCatS (Description, DescMaxSize/sizeof (CHAR16), L" "); - - StrCatS (Description, DescMaxSize/sizeof (CHAR16), Product); - StrCatS (Description, DescMaxSize/sizeof (CHAR16), L" "); - - StrCatS (Description, DescMaxSize/sizeof (CHAR16), SerialNumber); - - if (Manufacturer != &NullChar) { - FreePool (Manufacturer); - } - - if (Product != &NullChar) { - FreePool (Product); - } - - if (SerialNumber != &NullChar) { - FreePool (SerialNumber); - } - - BmEliminateExtraSpaces (Description); - - return Description; -} - /** Return the description for network boot device. @@ -518,394 +165,3 @@ BmGetNetworkDescription ( ); return Description; } - -/** - Return the boot description for LoadFile - - @param Handle Controller handle. - - @return The description string. -**/ -CHAR16 * -BmGetLoadFileDescription ( - IN EFI_HANDLE Handle - ) -{ - EFI_STATUS Status; - EFI_DEVICE_PATH_PROTOCOL *FilePath; - EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; - CHAR16 *Description; - EFI_LOAD_FILE_PROTOCOL *LoadFile; - - Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFile); - if (EFI_ERROR (Status)) { - return NULL; - } - - // - // Get the file name - // - Description = NULL; - Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&FilePath); - if (!EFI_ERROR (Status)) { - DevicePathNode = FilePath; - while (!IsDevicePathEnd (DevicePathNode)) { - if ((DevicePathNode->Type == MEDIA_DEVICE_PATH) && (DevicePathNode->SubType == MEDIA_FILEPATH_DP)) { - Description = (CHAR16 *)(DevicePathNode + 1); - break; - } - - DevicePathNode = NextDevicePathNode (DevicePathNode); - } - } - - if (Description != NULL) { - return AllocateCopyPool (StrSize (Description), Description); - } - - return NULL; -} - -/** - Return the boot description for NVME boot device. - - @param Handle Controller handle. - - @return The description string. -**/ -CHAR16 * -BmGetNvmeDescription ( - IN EFI_HANDLE Handle - ) -{ - EFI_STATUS Status; - EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmePassthru; - EFI_DEV_PATH_PTR DevicePath; - EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket; - EFI_NVM_EXPRESS_COMMAND Command; - EFI_NVM_EXPRESS_COMPLETION Completion; - NVME_ADMIN_CONTROLLER_DATA ControllerData; - CHAR16 *Description; - CHAR16 *Char; - UINTN Index; - - Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath.DevPath); - if (EFI_ERROR (Status)) { - return NULL; - } - - Status = gBS->LocateDevicePath (&gEfiNvmExpressPassThruProtocolGuid, &DevicePath.DevPath, &Handle); - if (EFI_ERROR (Status) || - (DevicePathType (DevicePath.DevPath) != MESSAGING_DEVICE_PATH) || - (DevicePathSubType (DevicePath.DevPath) != MSG_NVME_NAMESPACE_DP)) - { - // - // Do not return description when the Handle is not a child of NVME controller. - // - return NULL; - } - - // - // Send ADMIN_IDENTIFY command to NVME controller to get the model and serial number. - // - Status = gBS->HandleProtocol (Handle, &gEfiNvmExpressPassThruProtocolGuid, (VOID **)&NvmePassthru); - ASSERT_EFI_ERROR (Status); - - ZeroMem (&CommandPacket, sizeof (EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET)); - ZeroMem (&Command, sizeof (EFI_NVM_EXPRESS_COMMAND)); - ZeroMem (&Completion, sizeof (EFI_NVM_EXPRESS_COMPLETION)); - - Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD; - // - // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h. - // For the Identify command, the Namespace Identifier is only used for the Namespace data structure. - // - Command.Nsid = 0; - CommandPacket.NvmeCmd = &Command; - CommandPacket.NvmeCompletion = &Completion; - CommandPacket.TransferBuffer = &ControllerData; - CommandPacket.TransferLength = sizeof (ControllerData); - CommandPacket.CommandTimeout = EFI_TIMER_PERIOD_SECONDS (5); - CommandPacket.QueueType = NVME_ADMIN_QUEUE; - // - // Set bit 0 (Cns bit) to 1 to identify a controller - // - Command.Cdw10 = 1; - Command.Flags = CDW10_VALID; - - Status = NvmePassthru->PassThru ( - NvmePassthru, - 0, - &CommandPacket, - NULL - ); - if (EFI_ERROR (Status)) { - return NULL; - } - - Description = AllocateZeroPool ( - (ARRAY_SIZE (ControllerData.Mn) + 1 - + ARRAY_SIZE (ControllerData.Sn) + 1 - + MAXIMUM_VALUE_CHARACTERS + 1 - ) * sizeof (CHAR16) - ); - if (Description != NULL) { - Char = Description; - for (Index = 0; Index < ARRAY_SIZE (ControllerData.Mn); Index++) { - *(Char++) = (CHAR16)ControllerData.Mn[Index]; - } - - *(Char++) = L' '; - for (Index = 0; Index < ARRAY_SIZE (ControllerData.Sn); Index++) { - *(Char++) = (CHAR16)ControllerData.Sn[Index]; - } - - *(Char++) = L' '; - UnicodeValueToStringS ( - Char, - sizeof (CHAR16) * (MAXIMUM_VALUE_CHARACTERS + 1), - 0, - DevicePath.NvmeNamespace->NamespaceId, - 0 - ); - BmEliminateExtraSpaces (Description); - } - - return Description; -} - -/** - Return the boot description for the controller based on the type. - - @param Handle Controller handle. - - @return The description string. -**/ -CHAR16 * -BmGetMiscDescription ( - IN EFI_HANDLE Handle - ) -{ - EFI_STATUS Status; - CHAR16 *Description; - EFI_BLOCK_IO_PROTOCOL *BlockIo; - EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs; - - switch (BmDevicePathType (DevicePathFromHandle (Handle))) { - case BmAcpiFloppyBoot: - Description = L"Floppy"; - break; - - case BmMessageAtapiBoot: - case BmMessageSataBoot: - Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo); - ASSERT_EFI_ERROR (Status); - // - // Assume a removable SATA device should be the DVD/CD device - // - Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive"; - break; - - case BmMessageUsbBoot: - Description = L"USB Device"; - break; - - case BmMessageScsiBoot: - Description = L"SCSI Device"; - break; - - case BmHardwareDeviceBoot: - Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo); - if (!EFI_ERROR (Status)) { - Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive"; - } else { - Description = L"Misc Device"; - } - - break; - - default: - Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs); - if (!EFI_ERROR (Status)) { - Description = L"Non-Block Boot Device"; - } else { - Description = L"Misc Device"; - } - - break; - } - - return AllocateCopyPool (StrSize (Description), Description); -} - -/** - Register the platform provided boot description handler. - - @param Handler The platform provided boot description handler - - @retval EFI_SUCCESS The handler was registered successfully. - @retval EFI_ALREADY_STARTED The handler was already registered. - @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration. -**/ -EFI_STATUS -EFIAPI -EfiBootManagerRegisterBootDescriptionHandler ( - IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler - ) -{ - LIST_ENTRY *Link; - BM_BOOT_DESCRIPTION_ENTRY *Entry; - - for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers) - ; !IsNull (&mPlatformBootDescriptionHandlers, Link) - ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link) - ) - { - Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE); - if (Entry->Handler == Handler) { - return EFI_ALREADY_STARTED; - } - } - - Entry = AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY)); - if (Entry == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - Entry->Signature = BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE; - Entry->Handler = Handler; - InsertTailList (&mPlatformBootDescriptionHandlers, &Entry->Link); - return EFI_SUCCESS; -} - -BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers[] = { - BmGetUsbDescription, - BmGetDescriptionFromDiskInfo, - BmGetNetworkDescription, - BmGetLoadFileDescription, - BmGetNvmeDescription, - BmGetMiscDescription -}; - -/** - Return the boot description for the controller. - - @param Handle Controller handle. - - @return The description string. -**/ -CHAR16 * -BmGetBootDescription ( - IN EFI_HANDLE Handle - ) -{ - LIST_ENTRY *Link; - BM_BOOT_DESCRIPTION_ENTRY *Entry; - CHAR16 *Description; - CHAR16 *DefaultDescription; - CHAR16 *Temp; - UINTN Index; - - // - // Firstly get the default boot description - // - DefaultDescription = NULL; - for (Index = 0; Index < ARRAY_SIZE (mBmBootDescriptionHandlers); Index++) { - DefaultDescription = mBmBootDescriptionHandlers[Index](Handle); - if (DefaultDescription != NULL) { - // - // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix - // ONLY for core provided boot description handler. - // - Temp = AllocatePool (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)); - ASSERT (Temp != NULL); - StrCpyS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), mBmUefiPrefix); - StrCatS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), DefaultDescription); - FreePool (DefaultDescription); - DefaultDescription = Temp; - break; - } - } - - ASSERT (DefaultDescription != NULL); - - // - // Secondly query platform for the better boot description - // - for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers) - ; !IsNull (&mPlatformBootDescriptionHandlers, Link) - ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link) - ) - { - Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE); - Description = Entry->Handler (Handle, DefaultDescription); - if (Description != NULL) { - FreePool (DefaultDescription); - return Description; - } - } - - return DefaultDescription; -} - -/** - Enumerate all boot option descriptions and append " 2"/" 3"/... to make - unique description. - - @param BootOptions Array of boot options. - @param BootOptionCount Count of boot options. -**/ -VOID -BmMakeBootOptionDescriptionUnique ( - EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions, - UINTN BootOptionCount - ) -{ - UINTN Base; - UINTN Index; - UINTN DescriptionSize; - UINTN MaxSuffixSize; - BOOLEAN *Visited; - UINTN MatchCount; - - if (BootOptionCount == 0) { - return; - } - - // - // Calculate the maximum buffer size for the number suffix. - // The initial sizeof (CHAR16) is for the blank space before the number. - // - MaxSuffixSize = sizeof (CHAR16); - for (Index = BootOptionCount; Index != 0; Index = Index / 10) { - MaxSuffixSize += sizeof (CHAR16); - } - - Visited = AllocateZeroPool (sizeof (BOOLEAN) * BootOptionCount); - ASSERT (Visited != NULL); - - for (Base = 0; Base < BootOptionCount; Base++) { - if (!Visited[Base]) { - MatchCount = 1; - Visited[Base] = TRUE; - DescriptionSize = StrSize (BootOptions[Base].Description); - for (Index = Base + 1; Index < BootOptionCount; Index++) { - if (!Visited[Index] && (StrCmp (BootOptions[Base].Description, BootOptions[Index].Description) == 0)) { - Visited[Index] = TRUE; - MatchCount++; - FreePool (BootOptions[Index].Description); - BootOptions[Index].Description = AllocatePool (DescriptionSize + MaxSuffixSize); - UnicodeSPrint ( - BootOptions[Index].Description, - DescriptionSize + MaxSuffixSize, - L"%s %d", - BootOptions[Base].Description, - MatchCount - ); - } - } - } - } - - FreePool (Visited); -} diff --git a/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c b/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c index 21d8f3ad557..9c0acaeeece 100644 --- a/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c +++ b/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c @@ -9,98 +9,6 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "TlsAuthConfigImpl.h" -VOID *mStartOpCodeHandle = NULL; -VOID *mEndOpCodeHandle = NULL; -EFI_IFR_GUID_LABEL *mStartLabel = NULL; -EFI_IFR_GUID_LABEL *mEndLabel = NULL; - -CHAR16 mTlsAuthConfigStorageName[] = L"TLS_AUTH_CONFIG_IFR_NVDATA"; - -TLS_AUTH_CONFIG_PRIVATE_DATA *mTlsAuthPrivateData = NULL; - -HII_VENDOR_DEVICE_PATH mTlsAuthConfigHiiVendorDevicePath = { - { - { - HARDWARE_DEVICE_PATH, - HW_VENDOR_DP, - { - (UINT8)(sizeof (VENDOR_DEVICE_PATH)), - (UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8) - } - }, - TLS_AUTH_CONFIG_GUID - }, - { - END_DEVICE_PATH_TYPE, - END_ENTIRE_DEVICE_PATH_SUBTYPE, - { - (UINT8)(END_DEVICE_PATH_LENGTH), - (UINT8)((END_DEVICE_PATH_LENGTH) >> 8) - } - } -}; - -// -// Possible DER-encoded certificate file suffixes, end with NULL pointer. -// -CHAR16 *mDerPemEncodedSuffix[] = { - L".cer", - L".der", - L".crt", - L".pem", - NULL -}; - -/** - This code checks if the FileSuffix is one of the possible DER/PEM-encoded certificate suffix. - - @param[in] FileSuffix The suffix of the input certificate file - - @retval TRUE It's a DER/PEM-encoded certificate. - @retval FALSE It's NOT a DER/PEM-encoded certificate. - -**/ -BOOLEAN -IsDerPemEncodeCertificate ( - IN CONST CHAR16 *FileSuffix - ) -{ - UINTN Index; - - for (Index = 0; mDerPemEncodedSuffix[Index] != NULL; Index++) { - if (StrCmp (FileSuffix, mDerPemEncodedSuffix[Index]) == 0) { - return TRUE; - } - } - - return FALSE; -} - -/** - Worker function that prints an EFI_GUID into specified Buffer. - - @param[in] Guid Pointer to GUID to print. - @param[in] Buffer Buffer to print Guid into. - @param[in] BufferSize Size of Buffer. - - @retval Number of characters printed. - -**/ -UINTN -GuidToString ( - IN EFI_GUID *Guid, - IN CHAR16 *Buffer, - IN UINTN BufferSize - ) -{ - return UnicodeSPrint ( - Buffer, - BufferSize, - L"%g", - Guid - ); -} - /** List all cert in specified database by GUID in the page for user to select and delete as needed. @@ -471,170 +379,6 @@ DeleteCert ( ); } -/** - Clean the file related resource. - - @param[in] Private Module's private data. - -**/ -VOID -CleanFileContext ( - IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private - ) -{ - if (Private->FileContext->FHandle != NULL) { - Private->FileContext->FHandle->Close (Private->FileContext->FHandle); - Private->FileContext->FHandle = NULL; - if (Private->FileContext->FileName != NULL) { - FreePool (Private->FileContext->FileName); - Private->FileContext->FileName = NULL; - } - } -} - -/** - Read file content into BufferPtr, the size of the allocate buffer - is *FileSize plus AddtionAllocateSize. - - @param[in] FileHandle The file to be read. - @param[in, out] BufferPtr Pointers to the pointer of allocated buffer. - @param[out] FileSize Size of input file - @param[in] AddtionAllocateSize Addtion size the buffer need to be allocated. - In case the buffer need to contain others besides the file content. - - @retval EFI_SUCCESS The file was read into the buffer. - @retval EFI_INVALID_PARAMETER A parameter was invalid. - @retval EFI_OUT_OF_RESOURCES A memory allocation failed. - @retval others Unexpected error. - -**/ -EFI_STATUS -ReadFileContent ( - IN EFI_FILE_HANDLE FileHandle, - IN OUT VOID **BufferPtr, - OUT UINTN *FileSize, - IN UINTN AddtionAllocateSize - ) - -{ - UINTN BufferSize; - UINT64 SourceFileSize; - VOID *Buffer; - EFI_STATUS Status; - - if ((FileHandle == NULL) || (FileSize == NULL)) { - return EFI_INVALID_PARAMETER; - } - - Buffer = NULL; - - // - // Get the file size - // - Status = FileHandle->SetPosition (FileHandle, (UINT64)-1); - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } - - Status = FileHandle->GetPosition (FileHandle, &SourceFileSize); - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } - - Status = FileHandle->SetPosition (FileHandle, 0); - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } - - BufferSize = (UINTN)SourceFileSize + AddtionAllocateSize; - Buffer = AllocateZeroPool (BufferSize); - if (Buffer == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - BufferSize = (UINTN)SourceFileSize; - *FileSize = BufferSize; - - Status = FileHandle->Read (FileHandle, &BufferSize, Buffer); - if (EFI_ERROR (Status) || (BufferSize != *FileSize)) { - FreePool (Buffer); - Buffer = NULL; - Status = EFI_BAD_BUFFER_SIZE; - goto ON_EXIT; - } - -ON_EXIT: - - *BufferPtr = Buffer; - return Status; -} - -/** - This function converts an input device structure to a Unicode string. - - @param[in] DevPath A pointer to the device path structure. - - @return A new allocated Unicode string that represents the device path. - -**/ -CHAR16 * -EFIAPI -DevicePathToStr ( - IN EFI_DEVICE_PATH_PROTOCOL *DevPath - ) -{ - return ConvertDevicePathToText ( - DevPath, - FALSE, - TRUE - ); -} - -/** - Extract filename from device path. The returned buffer is allocated using AllocateCopyPool. - The caller is responsible for freeing the allocated buffer using FreePool(). If return NULL - means not enough memory resource. - - @param DevicePath Device path. - - @retval NULL Not enough memory resource for AllocateCopyPool. - @retval Other A new allocated string that represents the file name. - -**/ -CHAR16 * -ExtractFileNameFromDevicePath ( - IN EFI_DEVICE_PATH_PROTOCOL *DevicePath - ) -{ - CHAR16 *String; - CHAR16 *MatchString; - CHAR16 *LastMatch; - CHAR16 *FileName; - UINTN Length; - - ASSERT (DevicePath != NULL); - - String = DevicePathToStr (DevicePath); - MatchString = String; - LastMatch = String; - FileName = NULL; - - while (MatchString != NULL) { - LastMatch = MatchString + 1; - MatchString = StrStr (LastMatch, L"\\"); - } - - Length = StrLen (LastMatch); - FileName = AllocateCopyPool ((Length + 1) * sizeof (CHAR16), LastMatch); - if (FileName != NULL) { - *(FileName + Length) = 0; - } - - FreePool (String); - - return FileName; -} - /** Enroll a new X509 certificate into Variable. @@ -752,811 +496,3 @@ EnrollX509toVariable ( return Status; } - -/** - Enroll Cert into TlsCaCertificate. The GUID will be Private->CertGuid. - - @param[in] PrivateData The module's private data. - @param[in] VariableName Variable name of signature database. - - @retval EFI_SUCCESS New Cert enrolled successfully. - @retval EFI_INVALID_PARAMETER The parameter is invalid. - @retval EFI_UNSUPPORTED The Cert file is unsupported type. - @retval others Fail to enroll Cert data. - -**/ -EFI_STATUS -EnrollCertDatabase ( - IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private, - IN CHAR16 *VariableName - ) -{ - UINT16 *FilePostFix; - UINTN NameLength; - - if ((Private->FileContext->FileName == NULL) || (Private->FileContext->FHandle == NULL) || (Private->CertGuid == NULL)) { - return EFI_INVALID_PARAMETER; - } - - // - // Parse the file's postfix. - // - NameLength = StrLen (Private->FileContext->FileName); - if (NameLength <= 4) { - return EFI_INVALID_PARAMETER; - } - - FilePostFix = Private->FileContext->FileName + NameLength - 4; - - if (IsDerPemEncodeCertificate (FilePostFix)) { - // - // Supports DER-encoded X509 certificate. - // - return EnrollX509toVariable (Private, VariableName); - } - - return EFI_UNSUPPORTED; -} - -/** - Refresh the global UpdateData structure. - -**/ -VOID -RefreshUpdateData ( - VOID - ) -{ - // - // Free current updated date - // - if (mStartOpCodeHandle != NULL) { - HiiFreeOpCodeHandle (mStartOpCodeHandle); - } - - // - // Create new OpCode Handle - // - mStartOpCodeHandle = HiiAllocateOpCodeHandle (); - - // - // Create Hii Extend Label OpCode as the start opcode - // - mStartLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode ( - mStartOpCodeHandle, - &gEfiIfrTianoGuid, - NULL, - sizeof (EFI_IFR_GUID_LABEL) - ); - mStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; -} - -/** - Clean up the dynamic opcode at label and form specified by both LabelId. - - @param[in] LabelId It is both the Form ID and Label ID for opcode deletion. - @param[in] PrivateData Module private data. - -**/ -VOID -CleanUpPage ( - IN UINT16 LabelId, - IN TLS_AUTH_CONFIG_PRIVATE_DATA *PrivateData - ) -{ - RefreshUpdateData (); - - // - // Remove all op-codes from dynamic page - // - mStartLabel->Number = LabelId; - HiiUpdateForm ( - PrivateData->RegisteredHandle, - &gTlsAuthConfigGuid, - LabelId, - mStartOpCodeHandle, // Label LabelId - mEndOpCodeHandle // LABEL_END - ); -} - -/** - Update the form base on the selected file. - - @param FilePath Point to the file path. - @param FormId The form need to display. - - @retval TRUE Exit caller function. - @retval FALSE Not exit caller function. - -**/ -BOOLEAN -UpdatePage ( - IN EFI_DEVICE_PATH_PROTOCOL *FilePath, - IN EFI_FORM_ID FormId - ) -{ - CHAR16 *FileName; - EFI_STRING_ID StringToken; - - FileName = NULL; - - if (FilePath != NULL) { - FileName = ExtractFileNameFromDevicePath (FilePath); - } - - if (FileName == NULL) { - // - // FileName = NULL has two case: - // 1. FilePath == NULL, not select file. - // 2. FilePath != NULL, but ExtractFileNameFromDevicePath return NULL not enough memory resource. - // In these two case, no need to update the form, and exit the caller function. - // - return TRUE; - } - - StringToken = HiiSetString (mTlsAuthPrivateData->RegisteredHandle, 0, FileName, NULL); - - mTlsAuthPrivateData->FileContext->FileName = FileName; - - EfiOpenFileByDevicePath ( - &FilePath, - &mTlsAuthPrivateData->FileContext->FHandle, - EFI_FILE_MODE_READ, - 0 - ); - // - // Create Subtitle op-code for the display string of the option. - // - RefreshUpdateData (); - mStartLabel->Number = FormId; - - HiiCreateSubTitleOpCode ( - mStartOpCodeHandle, - StringToken, - 0, - 0, - 0 - ); - - HiiUpdateForm ( - mTlsAuthPrivateData->RegisteredHandle, - &gTlsAuthConfigGuid, - FormId, - mStartOpCodeHandle, /// Label FormId - mEndOpCodeHandle /// LABEL_END - ); - - return TRUE; -} - -/** - Update the form base on the input file path info. - - @param FilePath Point to the file path. - - @retval TRUE Exit caller function. - @retval FALSE Not exit caller function. -**/ -BOOLEAN -EFIAPI -UpdateCAFromFile ( - IN EFI_DEVICE_PATH_PROTOCOL *FilePath - ) -{ - return UpdatePage (FilePath, TLS_AUTH_CONFIG_FORMID4_FORM); -} - -/** - Unload the configuration form, this includes: delete all the configuration - entries, uninstall the form callback protocol, and free the resources used. - - @param[in] Private Pointer to the driver private data. - - @retval EFI_SUCCESS The configuration form is unloaded. - @retval Others Failed to unload the form. - -**/ -EFI_STATUS -TlsAuthConfigFormUnload ( - IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private - ) -{ - if (Private->DriverHandle != NULL) { - // - // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL - // - gBS->UninstallMultipleProtocolInterfaces ( - Private->DriverHandle, - &gEfiDevicePathProtocolGuid, - &mTlsAuthConfigHiiVendorDevicePath, - &gEfiHiiConfigAccessProtocolGuid, - &Private->ConfigAccess, - NULL - ); - Private->DriverHandle = NULL; - } - - if (Private->RegisteredHandle != NULL) { - // - // Remove HII package list - // - HiiRemovePackages (Private->RegisteredHandle); - Private->RegisteredHandle = NULL; - } - - if (Private->CertGuid != NULL) { - FreePool (Private->CertGuid); - } - - if (Private->FileContext != NULL) { - FreePool (Private->FileContext); - } - - FreePool (Private); - - if (mStartOpCodeHandle != NULL) { - HiiFreeOpCodeHandle (mStartOpCodeHandle); - } - - if (mEndOpCodeHandle != NULL) { - HiiFreeOpCodeHandle (mEndOpCodeHandle); - } - - return EFI_SUCCESS; -} - -/** - Initialize the configuration form. - - @param[in] Private Pointer to the driver private data. - - @retval EFI_SUCCESS The configuration form is initialized. - @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. - -**/ -EFI_STATUS -TlsAuthConfigFormInit ( - IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private - ) -{ - EFI_STATUS Status; - - Private->Signature = TLS_AUTH_CONFIG_PRIVATE_DATA_SIGNATURE; - - Private->ConfigAccess.ExtractConfig = TlsAuthConfigAccessExtractConfig; - Private->ConfigAccess.RouteConfig = TlsAuthConfigAccessRouteConfig; - Private->ConfigAccess.Callback = TlsAuthConfigAccessCallback; - - // - // Install Device Path Protocol and Config Access protocol to driver handle. - // - Status = gBS->InstallMultipleProtocolInterfaces ( - &Private->DriverHandle, - &gEfiDevicePathProtocolGuid, - &mTlsAuthConfigHiiVendorDevicePath, - &gEfiHiiConfigAccessProtocolGuid, - &Private->ConfigAccess, - NULL - ); - if (EFI_ERROR (Status)) { - return Status; - } - - // - // Publish our HII data. - // - Private->RegisteredHandle = HiiAddPackages ( - &gTlsAuthConfigGuid, - Private->DriverHandle, - TlsAuthConfigDxeStrings, - TlsAuthConfigVfrBin, - NULL - ); - if (Private->RegisteredHandle == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto Error; - } - - Private->FileContext = AllocateZeroPool (sizeof (TLS_AUTH_CONFIG_FILE_CONTEXT)); - if (Private->FileContext == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto Error; - } - - // - // Init OpCode Handle and Allocate space for creation of Buffer - // - mStartOpCodeHandle = HiiAllocateOpCodeHandle (); - if (mStartOpCodeHandle == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto Error; - } - - mEndOpCodeHandle = HiiAllocateOpCodeHandle (); - if (mEndOpCodeHandle == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto Error; - } - - // - // Create Hii Extend Label OpCode as the start opcode - // - mStartLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode ( - mStartOpCodeHandle, - &gEfiIfrTianoGuid, - NULL, - sizeof (EFI_IFR_GUID_LABEL) - ); - mStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; - - // - // Create Hii Extend Label OpCode as the end opcode - // - mEndLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode ( - mEndOpCodeHandle, - &gEfiIfrTianoGuid, - NULL, - sizeof (EFI_IFR_GUID_LABEL) - ); - mEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; - mEndLabel->Number = LABEL_END; - - return EFI_SUCCESS; - -Error: - TlsAuthConfigFormUnload (Private); - return Status; -} - -/** - - This function allows the caller to request the current - configuration for one or more named elements. The resulting - string is in format. Any and all alternative - configuration strings shall also be appended to the end of the - current configuration string. If they are, they must appear - after the current configuration. They must contain the same - routing (GUID, NAME, PATH) as the current configuration string. - They must have an additional description indicating the type of - alternative configuration the string represents, - "ALTCFG=". That (when - converted from Hex UNICODE to binary) is a reference to a - string in the associated string pack. - - @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. - - @param Request A null-terminated Unicode string in - format. Note that this - includes the routing information as well as - the configurable name / value pairs. It is - invalid for this string to be in - format. - If a NULL is passed in for the Request field, - all of the settings being abstracted by this function - will be returned in the Results field. In addition, - if a ConfigHdr is passed in with no request elements, - all of the settings being abstracted for that particular - ConfigHdr reference will be returned in the Results Field. - - @param Progress On return, points to a character in the - Request string. Points to the string's null - terminator if request was successful. Points - to the most recent "&" before the first - failing name / value pair (or the beginning - of the string if the failure is in the first - name / value pair) if the request was not - successful. - - @param Results A null-terminated Unicode string in - format which has all values - filled in for the names in the Request string. - String to be allocated by the called function. - - @retval EFI_SUCCESS The Results string is filled with the - values corresponding to all requested - names. - - @retval EFI_OUT_OF_RESOURCES Not enough memory to store the - parts of the results that must be - stored awaiting possible future - protocols. - - @retval EFI_NOT_FOUND Routing data doesn't match any - known driver. Progress set to the - first character in the routing header. - Note: There is no requirement that the - driver validate the routing data. It - must skip the in order to - process the names. - - @retval EFI_INVALID_PARAMETER Illegal syntax. Progress set - to most recent "&" before the - error or the beginning of the - string. - - @retval EFI_INVALID_PARAMETER Unknown name. Progress points - to the & before the name in - question. - -**/ -EFI_STATUS -EFIAPI -TlsAuthConfigAccessExtractConfig ( - IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, - IN CONST EFI_STRING Request, - OUT EFI_STRING *Progress, - OUT EFI_STRING *Results - ) -{ - EFI_STATUS Status; - UINTN BufferSize; - UINTN Size; - EFI_STRING ConfigRequest; - EFI_STRING ConfigRequestHdr; - TLS_AUTH_CONFIG_PRIVATE_DATA *Private; - BOOLEAN AllocatedRequest; - - if ((Progress == NULL) || (Results == NULL)) { - return EFI_INVALID_PARAMETER; - } - - AllocatedRequest = FALSE; - ConfigRequestHdr = NULL; - ConfigRequest = NULL; - Size = 0; - - Private = TLS_AUTH_CONFIG_PRIVATE_FROM_THIS (This); - - BufferSize = sizeof (TLS_AUTH_CONFIG_IFR_NVDATA); - ZeroMem (&Private->TlsAuthConfigNvData, BufferSize); - - *Progress = Request; - - if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &gTlsAuthConfigGuid, mTlsAuthConfigStorageName)) { - return EFI_NOT_FOUND; - } - - ConfigRequest = Request; - if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) { - // - // Request is set to NULL or OFFSET is NULL, construct full request string. - // - // Allocate and fill a buffer large enough to hold the template - // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator - // - ConfigRequestHdr = HiiConstructConfigHdr (&gTlsAuthConfigGuid, mTlsAuthConfigStorageName, Private->DriverHandle); - Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16); - ConfigRequest = AllocateZeroPool (Size); - ASSERT (ConfigRequest != NULL); - AllocatedRequest = TRUE; - UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize); - FreePool (ConfigRequestHdr); - ConfigRequestHdr = NULL; - } - - Status = gHiiConfigRouting->BlockToConfig ( - gHiiConfigRouting, - ConfigRequest, - (UINT8 *)&Private->TlsAuthConfigNvData, - BufferSize, - Results, - Progress - ); - - // - // Free the allocated config request string. - // - if (AllocatedRequest) { - FreePool (ConfigRequest); - } - - // - // Set Progress string to the original request string. - // - if (Request == NULL) { - *Progress = NULL; - } else if (StrStr (Request, L"OFFSET") == NULL) { - *Progress = Request + StrLen (Request); - } - - return Status; -} - -/** - - This function applies changes in a driver's configuration. - Input is a Configuration, which has the routing data for this - driver followed by name / value configuration pairs. The driver - must apply those pairs to its configurable storage. If the - driver's configuration is stored in a linear block of data - and the driver's name / value pairs are in - format, it may use the ConfigToBlock helper function (above) to - simplify the job. - - @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. - - @param Configuration A null-terminated Unicode string in - format. - - @param Progress A pointer to a string filled in with the - offset of the most recent '&' before the - first failing name / value pair (or the - beginning of the string if the failure - is in the first name / value pair) or - the terminating NULL if all was - successful. - - @retval EFI_SUCCESS The results have been distributed or are - awaiting distribution. - - @retval EFI_OUT_OF_RESOURCES Not enough memory to store the - parts of the results that must be - stored awaiting possible future - protocols. - - @retval EFI_INVALID_PARAMETERS Passing in a NULL for the - Results parameter would result - in this type of error. - - @retval EFI_NOT_FOUND Target for the specified routing data - was not found - -**/ -EFI_STATUS -EFIAPI -TlsAuthConfigAccessRouteConfig ( - IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, - IN CONST EFI_STRING Configuration, - OUT EFI_STRING *Progress - ) -{ - EFI_STATUS Status; - UINTN BufferSize; - TLS_AUTH_CONFIG_PRIVATE_DATA *Private; - - if (Progress == NULL) { - return EFI_INVALID_PARAMETER; - } - - *Progress = Configuration; - - if (Configuration == NULL) { - return EFI_INVALID_PARAMETER; - } - - // - // Check routing data in . - // Note: there is no name for Name/Value storage, only GUID will be checked - // - if (!HiiIsConfigHdrMatch (Configuration, &gTlsAuthConfigGuid, mTlsAuthConfigStorageName)) { - return EFI_NOT_FOUND; - } - - Private = TLS_AUTH_CONFIG_PRIVATE_FROM_THIS (This); - - BufferSize = sizeof (TLS_AUTH_CONFIG_IFR_NVDATA); - ZeroMem (&Private->TlsAuthConfigNvData, BufferSize); - - Status = gHiiConfigRouting->ConfigToBlock ( - gHiiConfigRouting, - Configuration, - (UINT8 *)&Private->TlsAuthConfigNvData, - &BufferSize, - Progress - ); - if (EFI_ERROR (Status)) { - return Status; - } - - return Status; -} - -/** - - This function is called to provide results data to the driver. - This data consists of a unique key that is used to identify - which data is either being passed back or being asked for. - - @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. - @param Action Specifies the type of action taken by the browser. - @param QuestionId A unique value which is sent to the original - exporting driver so that it can identify the type - of data to expect. The format of the data tends to - vary based on the opcode that generated the callback. - @param Type The type of value for the question. - @param Value A pointer to the data being sent to the original - exporting driver. - @param ActionRequest On return, points to the action requested by the - callback function. - - @retval EFI_SUCCESS The callback successfully handled the action. - @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the - variable and its data. - @retval EFI_DEVICE_ERROR The variable could not be saved. - @retval EFI_UNSUPPORTED The specified Action is not supported by the - callback. -**/ -EFI_STATUS -EFIAPI -TlsAuthConfigAccessCallback ( - IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, - IN EFI_BROWSER_ACTION Action, - IN EFI_QUESTION_ID QuestionId, - IN UINT8 Type, - IN OUT EFI_IFR_TYPE_VALUE *Value, - OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest - ) -{ - EFI_STATUS Status; - RETURN_STATUS RStatus; - TLS_AUTH_CONFIG_PRIVATE_DATA *Private; - UINTN BufferSize; - TLS_AUTH_CONFIG_IFR_NVDATA *IfrNvData; - UINT16 LabelId; - EFI_DEVICE_PATH_PROTOCOL *File; - EFI_HII_POPUP_PROTOCOL *HiiPopUp; - EFI_HII_POPUP_SELECTION PopUpSelect; - - Status = EFI_SUCCESS; - File = NULL; - - if ((This == NULL) || (Value == NULL) || (ActionRequest == NULL)) { - return EFI_INVALID_PARAMETER; - } - - Private = TLS_AUTH_CONFIG_PRIVATE_FROM_THIS (This); - - mTlsAuthPrivateData = Private; - Status = gBS->LocateProtocol (&gEfiHiiPopupProtocolGuid, NULL, (VOID **)&HiiPopUp); - if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "Can't find Form PopUp protocol. Exit (%r)\n", Status)); - return Status; - } - - // - // Retrieve uncommitted data from Browser - // - BufferSize = sizeof (TLS_AUTH_CONFIG_IFR_NVDATA); - IfrNvData = AllocateZeroPool (BufferSize); - if (IfrNvData == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - HiiGetBrowserData (&gTlsAuthConfigGuid, mTlsAuthConfigStorageName, BufferSize, (UINT8 *)IfrNvData); - - if ((Action != EFI_BROWSER_ACTION_CHANGED) && - (Action != EFI_BROWSER_ACTION_CHANGING) && - (Action != EFI_BROWSER_ACTION_FORM_CLOSE)) - { - Status = EFI_UNSUPPORTED; - goto EXIT; - } - - if (Action == EFI_BROWSER_ACTION_CHANGING) { - switch (QuestionId) { - case KEY_TLS_AUTH_CONFIG_CLIENT_CERT: - case KEY_TLS_AUTH_CONFIG_SERVER_CA: - // - // Clear Cert GUID. - // - ZeroMem (IfrNvData->CertGuid, sizeof (IfrNvData->CertGuid)); - if (Private->CertGuid == NULL) { - Private->CertGuid = (EFI_GUID *)AllocateZeroPool (sizeof (EFI_GUID)); - if (Private->CertGuid == NULL) { - return EFI_OUT_OF_RESOURCES; - } - } - - if (QuestionId == KEY_TLS_AUTH_CONFIG_CLIENT_CERT) { - LabelId = TLS_AUTH_CONFIG_FORMID3_FORM; - } else { - LabelId = TLS_AUTH_CONFIG_FORMID4_FORM; - } - - // - // Refresh selected file. - // - CleanUpPage (LabelId, Private); - break; - case KEY_TLS_AUTH_CONFIG_ENROLL_CERT_FROM_FILE: - // - // If the file is already opened, clean the file related resource first. - // - CleanFileContext (Private); - - ChooseFile (NULL, NULL, UpdateCAFromFile, &File); - break; - - case KEY_TLS_AUTH_CONFIG_VALUE_SAVE_AND_EXIT: - Status = EnrollCertDatabase (Private, EFI_TLS_CA_CERTIFICATE_VARIABLE); - if (EFI_ERROR (Status)) { - CleanFileContext (Private); - - HiiPopUp->CreatePopup ( - HiiPopUp, - EfiHiiPopupStyleError, - EfiHiiPopupTypeOk, - Private->RegisteredHandle, - STRING_TOKEN (STR_TLS_AUTH_ENROLL_CERT_FAILURE), - &PopUpSelect - ); - } - - break; - - case KEY_TLS_AUTH_CONFIG_VALUE_NO_SAVE_AND_EXIT: - CleanFileContext (Private); - - if (Private->CertGuid != NULL) { - FreePool (Private->CertGuid); - Private->CertGuid = NULL; - } - - break; - - case KEY_TLS_AUTH_CONFIG_DELETE_CERT: - UpdateDeletePage ( - Private, - EFI_TLS_CA_CERTIFICATE_VARIABLE, - &gEfiTlsCaCertificateGuid, - LABEL_CA_DELETE, - TLS_AUTH_CONFIG_FORMID5_FORM, - OPTION_DEL_CA_ESTION_ID - ); - break; - - default: - if ((QuestionId >= OPTION_DEL_CA_ESTION_ID) && - (QuestionId < (OPTION_DEL_CA_ESTION_ID + OPTION_CONFIG_RANGE))) - { - DeleteCert ( - Private, - EFI_TLS_CA_CERTIFICATE_VARIABLE, - &gEfiTlsCaCertificateGuid, - LABEL_CA_DELETE, - TLS_AUTH_CONFIG_FORMID5_FORM, - OPTION_DEL_CA_ESTION_ID, - QuestionId - OPTION_DEL_CA_ESTION_ID - ); - } - - break; - } - } else if (Action == EFI_BROWSER_ACTION_CHANGED) { - switch (QuestionId) { - case KEY_TLS_AUTH_CONFIG_CERT_GUID: - ASSERT (Private->CertGuid != NULL); - RStatus = StrToGuid ( - IfrNvData->CertGuid, - Private->CertGuid - ); - if (RETURN_ERROR (RStatus) || (IfrNvData->CertGuid[GUID_STRING_LENGTH] != L'\0')) { - Status = EFI_INVALID_PARAMETER; - break; - } - - *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; - break; - default: - break; - } - } else if (Action == EFI_BROWSER_ACTION_FORM_CLOSE) { - CleanFileContext (Private); - } - -EXIT: - - if (!EFI_ERROR (Status)) { - BufferSize = sizeof (TLS_AUTH_CONFIG_IFR_NVDATA); - HiiSetBrowserData (&gTlsAuthConfigGuid, mTlsAuthConfigStorageName, BufferSize, (UINT8 *)IfrNvData, NULL); - } - - FreePool (IfrNvData); - - if (File != NULL) { - FreePool (File); - File = NULL; - } - - return EFI_SUCCESS; -} From 7819fb274a29b9247f3c34ef80ba520d19ac49b8 Mon Sep 17 00:00:00 2001 From: Mike Beaton Date: Tue, 8 Oct 2024 05:38:02 +0100 Subject: [PATCH 3/4] OpenNetworkBoot: Add PXE and HTTP(S) Boot support --- Changelog.md | 2 + Docs/Configuration.tex | 138 ++++- Docs/Flavours.md | 24 +- Docs/Sample.plist | 84 +++ Docs/SampleCustom.plist | 84 +++ .../Acidanthera/Library/OcAppleDiskImageLib.h | 11 + .../Acidanthera/Library/OcBootManagementLib.h | 352 +++++++----- Include/Acidanthera/Protocol/OcAudio.h | 1 + Include/Acidanthera/Protocol/OcBootEntry.h | 2 +- Library/OcBootManagementLib/BootAudio.c | 6 +- .../OcBootManagementLib/BootEntryManagement.c | 128 +++-- .../OcBootManagementLib/BootEntryProtocol.c | 4 + .../BootManagementInternal.h | 8 +- .../OcBootManagementLib/DefaultEntryChoice.c | 92 +-- Library/OcBootManagementLib/DmgBootSupport.c | 229 ++++---- .../OcBootManagementLib/OcBootManagementLib.c | 6 +- Library/OcMainLib/OpenCoreMisc.c | 33 +- Library/OcPngLib/lodepng.c | 3 - OpenCorePkg.dsc | 33 +- OpenDuetPkg.dsc | 4 +- OpenDuetPkgDefines.fdf.inc | 2 +- Platform/OpenCanopy/GuiApp.c | 3 +- Platform/OpenCanopy/GuiApp.h | 1 + Platform/OpenCanopy/Views/BootPicker.c | 7 +- Platform/OpenLegacyBoot/OpenLegacyBoot.c | 2 +- Platform/OpenNetworkBoot/BmBoot.c | 79 ++- Platform/OpenNetworkBoot/BmBootDescription.c | 15 +- Platform/OpenNetworkBoot/HttpBootCallback.c | 286 ++++++++++ Platform/OpenNetworkBoot/HttpBootCustomRead.c | 298 ++++++++++ .../OpenNetworkBoot/NetworkBootInternal.h | 217 +++++++ Platform/OpenNetworkBoot/OpenNetworkBoot.c | 535 ++++++++++++++++++ Platform/OpenNetworkBoot/OpenNetworkBoot.inf | 54 ++ Platform/OpenNetworkBoot/README.md | 453 +++++++++++++++ Platform/OpenNetworkBoot/TlsAuthConfigImpl.c | 517 +++++++++-------- Platform/OpenNetworkBoot/Uri.c | 304 ++++++++++ build_oc.tool | 8 + 36 files changed, 3402 insertions(+), 623 deletions(-) create mode 100644 Platform/OpenNetworkBoot/HttpBootCallback.c create mode 100644 Platform/OpenNetworkBoot/HttpBootCustomRead.c create mode 100644 Platform/OpenNetworkBoot/NetworkBootInternal.h create mode 100644 Platform/OpenNetworkBoot/OpenNetworkBoot.c create mode 100644 Platform/OpenNetworkBoot/OpenNetworkBoot.inf create mode 100644 Platform/OpenNetworkBoot/README.md create mode 100644 Platform/OpenNetworkBoot/Uri.c diff --git a/Changelog.md b/Changelog.md index b32a14aead5..1448d1d1538 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,8 @@ OpenCore Changelog #### v1.0.3 - Fixed support for `AMD_CPU_EXT_FAMILY_1AH`, thx @Shaneee - Fixed EHCI handoff logic in OpenDuet, causing older machines to hang at start +- Added OpenNetworkBoot driver to support HTTP(S) and PXE boot +- Supported DMG loading and verification (e.g. macOS Recovery) over HTTP(S) boot #### v1.0.2 - Fixed error in macrecovery when running headless, thx @mkorje diff --git a/Docs/Configuration.tex b/Docs/Configuration.tex index 63738e0c97b..fa9d680b88d 100755 --- a/Docs/Configuration.tex +++ b/Docs/Configuration.tex @@ -4119,6 +4119,7 @@ \subsection{Debug Properties}\label{miscdebugprops} \item \texttt{HDA} --- AudioDxe \item \texttt{KKT} --- KeyTester \item \texttt{LNX} --- OpenLinuxBoot + \item \texttt{NTBT} --- OpenNetworkBoot \item \texttt{MMDD} --- MmapDump \item \texttt{OCPAVP} --- PavpProvision \item \texttt{OCRST} --- ResetSystem @@ -6579,6 +6580,9 @@ \subsection{Drivers}\label{uefidrivers} & \hyperref[uefilinux]{OpenCore plugin} implementing \texttt{OC\_BOOT\_ENTRY\_PROTOCOL} to allow direct detection and booting of Linux distributions from OpenCore, without chainloading via GRUB. \\ +\href{https://github.com/acidanthera/OpenCorePkg}{\texttt{OpenNetworkBoot}}\textbf{*} +& \hyperref[uefipxe]{OpenCore plugin} implementing \texttt{OC\_BOOT\_ENTRY\_PROTOCOL} + to show available PXE and HTTP(S) boot options on the OpenCore boot menu. \\ \href{https://github.com/acidanthera/OpenCorePkg}{\texttt{OpenNtfsDxe}}\textbf{*} & New Technologies File System (NTFS) read-only driver. NTFS is the primary file system for Microsoft Windows versions that are based on Windows NT. \\ @@ -7080,9 +7084,141 @@ \subsubsection{Additional information} therefore \texttt{efibootmgr} rather than \texttt{bootctl} must be used for any low-level Linux command line interaction with the boot menu. +\subsection{OpenNetworkBoot}\label{uefipxe} + +OpenNetworkBoot is an OpenCore plugin implementing \texttt{OC\_BOOT\_ENTRY\_PROTOCOL}. +It enables PXE and HTTP(S) Boot options in the OpenCore menu if these +are supported by the underlying firmware, or if the required network boot drivers +have been loaded using OpenCore. + +It has additional support for loading \texttt{.dmg} files and their associated +\texttt{.chunklist} file over HTTP(S) Boot, allowing macOS recovery to be +started over HTTP(S) Boot: if either extension is seen in the HTTP(S) Boot URI +then the other file of the pair is automatically loaded as well, and both are +passed to OpenCore to verify and boot from the DMG file. + +PXE Boot is already supported on most firmware, so in most cases PXE Boot entries +should appear as soon as the driver is loaded. Using the additional network boot +drivers provided with OpenCore, when needed, HTTP(S) Boot should be available on +most firmware even if not natively supported. + +Detailed information about the available network boot drivers and how to configure +PXE and HTTP(S) Boot is provided on +\href{https://github.com/acidanthera/OpenCorePkg/blob/master/Platform/OpenNetworkBoot/README.md}{this page}. + +The following configuration options may be specified in the \texttt{Arguments} section for this driver: + +\begin{itemize} + \item \texttt{-4} - Boolean flag, enabled if present. \medskip + + If specified enable IPv4 for PXE and HTTP(S) Boot. Disable IPV6 + unless the \texttt{-6} flag is also present. If neither flag is + present, both are enabled by default. \medskip + + \item \texttt{-6} - Boolean flag, enabled if present. \medskip + + If specified enable IPv6 for PXE and HTTP(S) Boot. Disable IPV4 + unless the \texttt{-4} flag is also present. If neither flag is + present, both are enabled by default. \medskip + + \item \texttt{-{}-aux} - Boolean flag, enabled if present. \medskip + + If specified the driver will generate auxiliary boot entries. \medskip + + \item \texttt{-{}-delete-all-certs[:\{OWNER\_GUID\}]} - Default: not set. \medskip + + If specified, delete all certificates present for \texttt{OWNER\_GUID}. + \texttt{OWNER\_GUID} is optional, and will default to all zeros if not specified. \medskip + + \item \texttt{-{}-delete-cert[:\{OWNER\_GUID\}]="\{cert-text\}"} - Default: not set. \medskip + + If specified, delete the given certificate(s) for HTTPS Boot. The certificate(s) can be specified + as a multi-line PEM value between double quotes. + \texttt{OWNER\_GUID} is optional, and will default to all zeros if not specified. + A single PEM file can contain one or more certicates. + Multiple instances of this option can be used to delete multiple different + PEM files, if required. + + \item \texttt{-{}-enroll-cert[:\{OWNER\_GUID\}]="\{cert-text\}"} - Default: not set. \medskip + + If specified, enroll the given certificate(s) for HTTPS Boot. The certificate(s) can be specified + as a multi-line PEM value between double quotes. + \texttt{OWNER\_GUID} is optional, and will default to all zeros if not specified. + A single PEM file can contain one or more certicates. + Multiple instances of this option can be used to enroll multiple different + PEM files, if required. \medskip + + \item \texttt{-{}-http} - Boolean flag, enabled if present. \medskip + + If specified enable HTTP(S) Boot. Disable PXE Boot unless + the \texttt{-{}-pxe} flag is also present. If neither flag is + present, both are enabled by default. \medskip + + \item \texttt{-{}-https} - Boolean flag, enabled if present. \medskip + + If enabled, allow only \texttt{https://} URIs for HTTP(S) Boot. + Additionally has the same behaviour as the \texttt{-{}-http} flag. \medskip + + \item \texttt{-{}-pxe} - Boolean flag, enabled if present. \medskip + + If specified enable PXE Boot, and disable HTTP(S) Boot unless + the \texttt{-{}-http} or \texttt{-{}-https} flags are present. + If none of these flags are present, both PXE and HTTP(S) Boot are + enabled by default. \medskip + + \item \texttt{-{}-uri} - String value, no default. \medskip + + If present, specify the URI to use for HTTP(S) Boot. If not present then + DHCP boot options must be enabled on the network in order for HTTP(S) + Boot to know what to boot. + +\end{itemize} \medskip + +\subsubsection{OpenNetworkBoot Certificate Management} + +Certificates are enrolled to NVRAM storage, therefore once +a certificate has been enrolled, it will remain enrolled even if the \texttt{-{}-enroll-cert} config +option is removed. \texttt{-{}-delete-cert} or \texttt{-{}-delete-all-certs} +should be used to remove enrolled certificates. + +Checking for certificate presence by the \texttt{-{}-enroll-cert} +and \texttt{-{}-delete-cert} options uses the simple algorithm +of matching by exact file contents, not by file meaning. The intended +usage is to leave an \texttt{-{}-enroll-cert} option present in the config +file until it is time to delete it, e.g. after another more up-to-date +\texttt{-{}-enroll-cert} option has been added and tested. At this point +the user can change \texttt{-{}-enroll-cert} to \texttt{-{}-delete-cert} +for the old certificate. \medskip + +Certificate options are processed one at a time, in +order, and each will potentially make changes to the certificate NVRAM storage. +However each option will not change the NVRAM store if it is already correct +for the option at that point in time (e.g. will not enroll a certificate if it is +already enrolled). +Avoid combinations such as \texttt{-{}-delete-all-certs} followed by +\texttt{-{}-enroll-cert}, as this will modify the NVRAM certificate +storage twice on every boot. However a combination such as +\texttt{-{}-delete-cert="\{certA-text\}"} followed by \texttt{-{}-enroll-cert="\{certB-text\}"} +(with \texttt{certA-text} and \texttt{certB-text} different) is safe, +because certA will only be deleted if it is present +and certB will only be added if it is not present, therefore no +NVRAM changes will be made on the second and subsequent boots +with these options. + +In some cases (such as OVMF with https:// boot support) the +\texttt{OpenNetworkBoot} certificate configuration options manage the same +certificates as those seen in the firmware UI. In other cases of vendor customised +HTTPS Boot firmware, the certificates managed by this driver will be +separate from those managed by firmware. + +When using the debug version of this driver, the OpenCore debug log includes \texttt{NTBT:} entries +that show which certificates are enrolled and removed by these options, and which +certificates are present after all certificate configuration options have been processed. + \subsection{Other Boot Entry Protocol drivers} -In addition to the \hyperref[uefilinux]{OpenLinuxBoot} plugin, the following \texttt{OC\_BOOT\_ENTRY\_PROTOCOL} +In addition to the \hyperref[uefilinux]{OpenLinuxBoot} and \hyperref[uefipxe]{OpenNetworkBoot} plugins, +the following \texttt{OC\_BOOT\_ENTRY\_PROTOCOL} plugins are made available to add optional, configurable boot entries to the OpenCore boot picker. \subsubsection{ResetNvramEntry}\label{uefiresetnvram} diff --git a/Docs/Flavours.md b/Docs/Flavours.md index 7ab4913596b..85407dc3b60 100644 --- a/Docs/Flavours.md +++ b/Docs/Flavours.md @@ -21,7 +21,7 @@ Icon pack authors are encouraged to provide only those icons for which there is In the case of macOS only, a flavour based on the detected OS version is applied automatically (as shown below), and the user does not normally need to override this. -For icon pack authors, the **Apple** icon is recommended, **AppleRecovery** and **AppleTM** are suggested, all others are entirely optional. +For icon pack authors, the **Apple** icon is recommended, **AppleRecv** and **AppleTM** are suggested, all others are entirely optional. - **Apple12:Apple** - Monterey (`Apple12.icns`) - **Apple11:Apple** - Big Sur (`Apple11.icns`) @@ -155,18 +155,31 @@ If providing `NVRAMTool.icns`, it should be themed so that it could be applied t - **ResetNVRAM:NVRAMTool** - Reset NVRAM tool (`ResetNVRAM.icns`) - This is the recommended flavour, used for the entry created by the `ResetNvramEntry.efi` driver. - As another example of how flavours work: **ResetNVRAM:NVRAMTool** will look for `ResetNVRAM.icns`, then `NVRAMTool.icns` (and then, by OC default behaviour, `Tool.icns` then `HardDrive.icns`) - - **Note**: Including **ResetNVRAM** anywhere in a user flavour triggers picker audio-assist and builtin label support for "Reset NVRAM" + - **Note**: Including **ResetNVRAM** anywhere in a flavour triggers picker audio-assist and builtin label support for "Reset NVRAM" - **ToggleSIP:NVRAMTool** - Icon themed for Toggle SIP tool (`ToggleSIP.icns`) - **ToggleSIP_Enabled:ToggleSIP:NVRAMTool** - Icon themed for Toggle SIP tool when SIP is enabled (system is protected) - **ToggleSIP_Disabled:ToggleSIP:NVRAMTool** - Icon themed for Toggle SIP tool when SIP is disabled (system is unprotected) - - **Note**: Including **ToggleSIP_Enabled** or **ToggleSIP_Disabled** anywhere in a user flavour triggers picker audio-assist and builtin label support for the two states of the Toggle SIP menu entry + - **Note**: Including **ToggleSIP_Enabled** or **ToggleSIP_Disabled** anywhere in a flavour triggers picker audio-assist and builtin label support for the two states of the Toggle SIP menu entry + +### Network Boot + +`OpenNetworkBoot.efi` uses the following flavours: + + - **HttpBoot4:HttpBoot:NetworkBoot** - IPv4 HTTP(S) Boot + - **HttpBoot6:HttpBoot:NetworkBoot** - IPv6 HTTP(S) Boot + - **PxeBoot4:PxeBoot:NetworkBoot** - IPv4 PXE Boot + - **PxeBoot6:PxeBoot:NetworkBoot** - IPv6 PXE Boot + +If none of these icons are available, network boot is treated like an external OS, so the fallbacks are **Other** followed by **HardDrive**. + + - **Note**: Including **NetworkBoot** anywhere in a flavour triggers picker audio-assist and builtin label support for "Network Boot" ### Other Tools A list of other known tools which are common enough that some icon pack artists may wish to provide a standard icon for them: - - **FirmwareSettings** - A boot menu entry for accessing firmware settings (`FirmwareSettings.icns`) - - **Note**: Including **FirmwareSettings** anywhere in a user flavour triggers picker audio-assist and builtin label support for "Firmware Settings" + - **FirmwareSettings** - A boot menu entry for accessing firmware settings, such as generated by `FirmwareSettingsEntry.efi` (`FirmwareSettings.icns`) + - **Note**: Including **FirmwareSettings** anywhere in a flavour triggers picker audio-assist and builtin label support for "Firmware Settings" - **MemTest** - A system memory testing tool such as that available from [memtest86.com](https://www.memtest86.com/) (`MemTest.icns`) ## Bootloaders @@ -198,6 +211,7 @@ Provided by OcBinaryData. Used automatically by OC in some circumstances, if pro - **ExtAppleTM** - Apple Time Machine (on external drive) (fallback: **ExtHardDrive**) - **Shell** - Shell tool (fallback: **Tool**) - **Tool** - Generic tool (fallback: **HardDrive**) + - **Other** - Other OS (fallback: **HardDrive**) - **Windows** - Microsoft Windows (fallback: **HardDrive**) ### Additional Optional diff --git a/Docs/Sample.plist b/Docs/Sample.plist index 7ebd9181235..57065355d88 100644 --- a/Docs/Sample.plist +++ b/Docs/Sample.plist @@ -1697,6 +1697,30 @@ Path Ext4Dxe.efi + + Arguments + + Comment + + Enabled + + LoadEarly + + Path + RngDxe.efi + + + Arguments + + Comment + + Enabled + + LoadEarly + + Path + Hash2DxeCrypto.efi + Arguments @@ -1781,6 +1805,42 @@ Path Udp4Dxe.efi + + Arguments + + Comment + + Enabled + + LoadEarly + + Path + Dhcp6Dxe.efi + + + Arguments + + Comment + + Enabled + + LoadEarly + + Path + Ip6Dxe.efi + + + Arguments + + Comment + + Enabled + + LoadEarly + + Path + Udp6Dxe.efi + Arguments @@ -1841,6 +1901,30 @@ Path HttpBootDxe.efi + + Arguments + + Comment + + Enabled + + LoadEarly + + Path + TlsDxe.efi + + + Arguments + + Comment + + Enabled + + LoadEarly + + Path + RamDiskDxe.efi + Arguments diff --git a/Docs/SampleCustom.plist b/Docs/SampleCustom.plist index d8841be15c8..3092006b659 100644 --- a/Docs/SampleCustom.plist +++ b/Docs/SampleCustom.plist @@ -2065,6 +2065,30 @@ Path Ext4Dxe.efi + + Arguments + + Comment + + Enabled + + LoadEarly + + Path + RngDxe.efi + + + Arguments + + Comment + + Enabled + + LoadEarly + + Path + Hash2DxeCrypto.efi + Arguments @@ -2149,6 +2173,42 @@ Path Udp4Dxe.efi + + Arguments + + Comment + + Enabled + + LoadEarly + + Path + Dhcp6Dxe.efi + + + Arguments + + Comment + + Enabled + + LoadEarly + + Path + Ip6Dxe.efi + + + Arguments + + Comment + + Enabled + + LoadEarly + + Path + Udp6Dxe.efi + Arguments @@ -2209,6 +2269,30 @@ Path HttpBootDxe.efi + + Arguments + + Comment + + Enabled + + LoadEarly + + Path + TlsDxe.efi + + + Arguments + + Comment + + Enabled + + LoadEarly + + Path + RamDiskDxe.efi + Arguments diff --git a/Include/Acidanthera/Library/OcAppleDiskImageLib.h b/Include/Acidanthera/Library/OcAppleDiskImageLib.h index c3174b3748e..78e24e1df0d 100644 --- a/Include/Acidanthera/Library/OcAppleDiskImageLib.h +++ b/Include/Acidanthera/Library/OcAppleDiskImageLib.h @@ -32,6 +32,17 @@ typedef struct { APPLE_DISK_IMAGE_BLOCK_DATA **Blocks; } OC_APPLE_DISK_IMAGE_CONTEXT; +// +// Disk image preload context, for network boot. +// +typedef struct { + OC_APPLE_DISK_IMAGE_CONTEXT *DmgContext; + EFI_FILE_PROTOCOL *DmgFile; + UINT32 DmgFileSize; + VOID *ChunklistBuffer; + UINT32 ChunklistFileSize; +} OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT; + BOOLEAN OcAppleDiskImageInitializeContext ( OUT OC_APPLE_DISK_IMAGE_CONTEXT *Context, diff --git a/Include/Acidanthera/Library/OcBootManagementLib.h b/Include/Acidanthera/Library/OcBootManagementLib.h index 9c49cec5129..6be22731e51 100644 --- a/Include/Acidanthera/Library/OcBootManagementLib.h +++ b/Include/Acidanthera/Library/OcBootManagementLib.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -78,6 +79,10 @@ typedef struct OC_HOTKEY_CONTEXT_ OC_HOTKEY_CONTEXT; #define OC_FLAVOUR_TOGGLE_SIP_ENABLED "ToggleSIP_Enabled:ToggleSIP:NVRAMTool" #define OC_FLAVOUR_TOGGLE_SIP_DISABLED "ToggleSIP_Disabled:ToggleSIP:NVRAMTool" #define OC_FLAVOUR_FIRMWARE_SETTINGS "FirmwareSettings" +#define OC_FLAVOUR_HTTP_BOOT4 "HttpBoot4:HttpBoot:NetworkBoot" +#define OC_FLAVOUR_HTTP_BOOT6 "HttpBoot6:HttpBoot:NetworkBoot" +#define OC_FLAVOUR_PXE_BOOT4 "PxeBoot4:PxeBoot:NetworkBoot" +#define OC_FLAVOUR_PXE_BOOT6 "PxeBoot6:PxeBoot:NetworkBoot" #define OC_FLAVOUR_APPLE_OS "Apple" #define OC_FLAVOUR_APPLE_RECOVERY "AppleRecv:Apple" #define OC_FLAVOUR_APPLE_FW "AppleRecv:Apple" @@ -92,6 +97,7 @@ typedef struct OC_HOTKEY_CONTEXT_ OC_HOTKEY_CONTEXT; #define OC_FLAVOUR_ID_TOGGLE_SIP_ENABLED "ToggleSIP_Enabled" #define OC_FLAVOUR_ID_TOGGLE_SIP_DISABLED "ToggleSIP_Disabled" #define OC_FLAVOUR_ID_FIRMWARE_SETTINGS "FirmwareSettings" +#define OC_FLAVOUR_ID_NETWORK_BOOT "NetworkBoot" /** Paths allowed to be accessible by the interfaces. @@ -190,6 +196,139 @@ typedef enum OC_PICKER_MODE_ { #define OC_KERN_CAPABILITY_K32_U32_U64 (OC_KERN_CAPABILITY_K32_U32 | OC_KERN_CAPABILITY_K32_U64) #define OC_KERN_CAPABILITY_ALL (OC_KERN_CAPABILITY_K32_U32 | OC_KERN_CAPABILITY_K32_K64_U64) +/** + Perform filtering based on file system basis. + Ignores all filesystems by default. + Remove this bit to allow any file system. +**/ +#define OC_SCAN_FILE_SYSTEM_LOCK BIT0 + +/** + Perform filtering based on device basis. + Ignores all devices by default. + Remove this bit to allow any device type. +**/ +#define OC_SCAN_DEVICE_LOCK BIT1 + +/** + Allow scanning APFS filesystems. +**/ +#define OC_SCAN_ALLOW_FS_APFS BIT8 + +/** + Allow scanning HFS filesystems. +**/ +#define OC_SCAN_ALLOW_FS_HFS BIT9 + +/** + Allow scanning ESP filesystems. +**/ +#define OC_SCAN_ALLOW_FS_ESP BIT10 + +/** + Allow scanning NTFS filesystems. +**/ +#define OC_SCAN_ALLOW_FS_NTFS BIT11 + +/** + Allow scanning Linux Root filesystems. + https://systemd.io/DISCOVERABLE_PARTITIONS/ +**/ +#define OC_SCAN_ALLOW_FS_LINUX_ROOT BIT12 + +/** + Allow scanning Linux Data filesystems. + https://systemd.io/DISCOVERABLE_PARTITIONS/ +**/ +#define OC_SCAN_ALLOW_FS_LINUX_DATA BIT13 + +/** + Allow scanning XBOOTLDR filesystems. +**/ +#define OC_SCAN_ALLOW_FS_XBOOTLDR BIT14 + +/** + Allow scanning SATA devices. +**/ +#define OC_SCAN_ALLOW_DEVICE_SATA BIT16 + +/** + Allow scanning SAS and Mac NVMe devices. +**/ +#define OC_SCAN_ALLOW_DEVICE_SASEX BIT17 + +/** + Allow scanning SCSI devices. +**/ +#define OC_SCAN_ALLOW_DEVICE_SCSI BIT18 + +/** + Allow scanning NVMe devices. +**/ +#define OC_SCAN_ALLOW_DEVICE_NVME BIT19 + +/** + Allow scanning ATAPI devices. +**/ +#define OC_SCAN_ALLOW_DEVICE_ATAPI BIT20 + +/** + Allow scanning USB devices. +**/ +#define OC_SCAN_ALLOW_DEVICE_USB BIT21 + +/** + Allow scanning FireWire devices. +**/ +#define OC_SCAN_ALLOW_DEVICE_FIREWIRE BIT22 + +/** + Allow scanning SD card devices. +**/ +#define OC_SCAN_ALLOW_DEVICE_SDCARD BIT23 + +/** + Allow scanning PCI devices (e.g. virtio). +**/ +#define OC_SCAN_ALLOW_DEVICE_PCI BIT24 + +/** + All device bits used by OC_SCAN_DEVICE_LOCK. +**/ +#define OC_SCAN_DEVICE_BITS (\ + OC_SCAN_ALLOW_DEVICE_SATA | OC_SCAN_ALLOW_DEVICE_SASEX | \ + OC_SCAN_ALLOW_DEVICE_SCSI | OC_SCAN_ALLOW_DEVICE_NVME | \ + OC_SCAN_ALLOW_DEVICE_ATAPI | OC_SCAN_ALLOW_DEVICE_USB | \ + OC_SCAN_ALLOW_DEVICE_FIREWIRE | OC_SCAN_ALLOW_DEVICE_SDCARD | \ + OC_SCAN_ALLOW_DEVICE_PCI) + +/** + All file system bits used by OC_SCAN_FILE_SYSTEM_LOCK. +**/ +#define OC_SCAN_FILE_SYSTEM_BITS (\ + OC_SCAN_ALLOW_FS_APFS | OC_SCAN_ALLOW_FS_HFS | OC_SCAN_ALLOW_FS_ESP | \ + OC_SCAN_ALLOW_FS_NTFS | OC_SCAN_ALLOW_FS_LINUX_ROOT | \ + OC_SCAN_ALLOW_FS_LINUX_DATA | OC_SCAN_ALLOW_FS_XBOOTLDR ) + +/** + By default allow booting from APFS from internal drives. +**/ +#define OC_SCAN_DEFAULT_POLICY (\ + OC_SCAN_FILE_SYSTEM_LOCK | OC_SCAN_DEVICE_LOCK | \ + OC_SCAN_ALLOW_FS_APFS | \ + OC_SCAN_ALLOW_DEVICE_SATA | OC_SCAN_ALLOW_DEVICE_SASEX | \ + OC_SCAN_ALLOW_DEVICE_SCSI | OC_SCAN_ALLOW_DEVICE_NVME | \ + OC_SCAN_ALLOW_DEVICE_PCI) + +/** + OcLoadBootEntry DMG loading policy rules. +**/ +typedef enum { + OcDmgLoadingDisabled, + OcDmgLoadingAnyImage, + OcDmgLoadingAppleSigned, +} OC_DMG_LOADING_SUPPORT; + /** Action to perform as part of executing a system boot entry. **/ @@ -219,11 +358,44 @@ EFI_STATUS IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath ); +/** + Forward declaration of OC_BOOT_ENTRY structure. +**/ +typedef struct OC_BOOT_ENTRY_ OC_BOOT_ENTRY; + +/** + Exposed custom entry load interface. + Returns allocated file buffer from pool on success. +**/ +typedef +EFI_STATUS +(EFIAPI *OC_CUSTOM_READ)( + IN OC_STORAGE_CONTEXT *Storage, + IN OC_BOOT_ENTRY *ChosenEntry, + OUT VOID **Data, + OUT UINT32 *DataSize, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, + OUT EFI_HANDLE *StorageHandle, + OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath, + IN OC_DMG_LOADING_SUPPORT DmgLoading, + OUT OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext, + OUT VOID **CustomFreeContext + ); + +/** + Exposed custom entry interface to free any custom items after load. +**/ +typedef +EFI_STATUS +(EFIAPI *OC_CUSTOM_FREE)( + IN VOID *CustomFreeContext + ); + /** Discovered boot entry. Note, inner resources must be freed with FreeBootEntry. **/ -typedef struct OC_BOOT_ENTRY_ { +struct OC_BOOT_ENTRY_ { // // Link in entry list in OC_BOOT_FILESYSTEM. // @@ -246,6 +418,14 @@ typedef struct OC_BOOT_ENTRY_ { // OC_BOOT_UNMANAGED_GET_FINAL_DP UnmanagedBootGetFinalDevicePath; // + // Custom entry image read routine, optional for non-custom entries. + // + OC_CUSTOM_READ CustomRead; + // + // Custom entry routine to free custom items. Optional. + // + OC_CUSTOM_FREE CustomFree; + // // Id under which to save entry as default. // CHAR16 *Id; @@ -332,7 +512,7 @@ typedef struct OC_BOOT_ENTRY_ { // Audio base type for system action. Boot Entry Protocol only. // CHAR8 *AudioBaseType; -} OC_BOOT_ENTRY; +}; /** Parsed load option or shell variable. @@ -417,139 +597,6 @@ typedef struct OC_BOOT_CONTEXT_ { OC_PICKER_CONTEXT *PickerContext; } OC_BOOT_CONTEXT; -/** - Perform filtering based on file system basis. - Ignores all filesystems by default. - Remove this bit to allow any file system. -**/ -#define OC_SCAN_FILE_SYSTEM_LOCK BIT0 - -/** - Perform filtering based on device basis. - Ignores all devices by default. - Remove this bit to allow any device type. -**/ -#define OC_SCAN_DEVICE_LOCK BIT1 - -/** - Allow scanning APFS filesystems. -**/ -#define OC_SCAN_ALLOW_FS_APFS BIT8 - -/** - Allow scanning HFS filesystems. -**/ -#define OC_SCAN_ALLOW_FS_HFS BIT9 - -/** - Allow scanning ESP filesystems. -**/ -#define OC_SCAN_ALLOW_FS_ESP BIT10 - -/** - Allow scanning NTFS filesystems. -**/ -#define OC_SCAN_ALLOW_FS_NTFS BIT11 - -/** - Allow scanning Linux Root filesystems. - https://systemd.io/DISCOVERABLE_PARTITIONS/ -**/ -#define OC_SCAN_ALLOW_FS_LINUX_ROOT BIT12 - -/** - Allow scanning Linux Data filesystems. - https://systemd.io/DISCOVERABLE_PARTITIONS/ -**/ -#define OC_SCAN_ALLOW_FS_LINUX_DATA BIT13 - -/** - Allow scanning XBOOTLDR filesystems. -**/ -#define OC_SCAN_ALLOW_FS_XBOOTLDR BIT14 - -/** - Allow scanning SATA devices. -**/ -#define OC_SCAN_ALLOW_DEVICE_SATA BIT16 - -/** - Allow scanning SAS and Mac NVMe devices. -**/ -#define OC_SCAN_ALLOW_DEVICE_SASEX BIT17 - -/** - Allow scanning SCSI devices. -**/ -#define OC_SCAN_ALLOW_DEVICE_SCSI BIT18 - -/** - Allow scanning NVMe devices. -**/ -#define OC_SCAN_ALLOW_DEVICE_NVME BIT19 - -/** - Allow scanning ATAPI devices. -**/ -#define OC_SCAN_ALLOW_DEVICE_ATAPI BIT20 - -/** - Allow scanning USB devices. -**/ -#define OC_SCAN_ALLOW_DEVICE_USB BIT21 - -/** - Allow scanning FireWire devices. -**/ -#define OC_SCAN_ALLOW_DEVICE_FIREWIRE BIT22 - -/** - Allow scanning SD card devices. -**/ -#define OC_SCAN_ALLOW_DEVICE_SDCARD BIT23 - -/** - Allow scanning PCI devices (e.g. virtio). -**/ -#define OC_SCAN_ALLOW_DEVICE_PCI BIT24 - -/** - All device bits used by OC_SCAN_DEVICE_LOCK. -**/ -#define OC_SCAN_DEVICE_BITS (\ - OC_SCAN_ALLOW_DEVICE_SATA | OC_SCAN_ALLOW_DEVICE_SASEX | \ - OC_SCAN_ALLOW_DEVICE_SCSI | OC_SCAN_ALLOW_DEVICE_NVME | \ - OC_SCAN_ALLOW_DEVICE_ATAPI | OC_SCAN_ALLOW_DEVICE_USB | \ - OC_SCAN_ALLOW_DEVICE_FIREWIRE | OC_SCAN_ALLOW_DEVICE_SDCARD | \ - OC_SCAN_ALLOW_DEVICE_PCI) - -/** - All file system bits used by OC_SCAN_FILE_SYSTEM_LOCK. -**/ -#define OC_SCAN_FILE_SYSTEM_BITS (\ - OC_SCAN_ALLOW_FS_APFS | OC_SCAN_ALLOW_FS_HFS | OC_SCAN_ALLOW_FS_ESP | \ - OC_SCAN_ALLOW_FS_NTFS | OC_SCAN_ALLOW_FS_LINUX_ROOT | \ - OC_SCAN_ALLOW_FS_LINUX_DATA | OC_SCAN_ALLOW_FS_XBOOTLDR ) - -/** - By default allow booting from APFS from internal drives. -**/ -#define OC_SCAN_DEFAULT_POLICY (\ - OC_SCAN_FILE_SYSTEM_LOCK | OC_SCAN_DEVICE_LOCK | \ - OC_SCAN_ALLOW_FS_APFS | \ - OC_SCAN_ALLOW_DEVICE_SATA | OC_SCAN_ALLOW_DEVICE_SASEX | \ - OC_SCAN_ALLOW_DEVICE_SCSI | OC_SCAN_ALLOW_DEVICE_NVME | \ - OC_SCAN_ALLOW_DEVICE_PCI) - -/** - OcLoadBootEntry DMG loading policy rules. -**/ -typedef enum { - OcDmgLoadingDisabled, - OcDmgLoadingAnyImage, - OcDmgLoadingAppleSigned, -} OC_DMG_LOADING_SUPPORT; - /** Exposed start interface with chosen boot entry but otherwise equivalent to EFI_BOOT_SERVICES StartImage. @@ -564,22 +611,6 @@ EFI_STATUS IN BOOLEAN LaunchInText ); -/** - Exposed custom entry load interface. - Returns allocated file buffer from pool on success. -**/ -typedef -EFI_STATUS -(EFIAPI *OC_CUSTOM_READ)( - IN OC_STORAGE_CONTEXT *Storage, - IN OC_BOOT_ENTRY *ChosenEntry, - OUT VOID **Data, - OUT UINT32 *DataSize, - OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, - OUT EFI_HANDLE *StorageHandle, - OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath - ); - /** Custom picker entry. Note that OC_BOOT_ENTRY_PROTOCOL_REVISION needs incrementing @@ -650,9 +681,17 @@ typedef struct { // OC_BOOT_UNMANAGED_GET_FINAL_DP UnmanagedBootGetFinalDevicePath; // - // Unmanaged boot Device Path. Boot Entry Protocol unmanaged boot entries only. + // Absolute Device Path. May be used instead of text Path above for Boot Entry Protocol entries. Optional. + // + EFI_DEVICE_PATH_PROTOCOL *UnmanagedDevicePath; + // + // Custom entry image read routine, optional for non-custom entries. + // + OC_CUSTOM_READ CustomRead; + // + // Custom entry routine to free custom items. Optional. // - EFI_DEVICE_PATH_PROTOCOL *UnmanagedBootDevicePath; + OC_CUSTOM_FREE CustomFree; // // Whether this entry should be labeled as external to the system. Boot Entry Protocol only. Optional. // @@ -925,7 +964,7 @@ struct OC_PICKER_CONTEXT_ { // BOOLEAN CustomBootGuid; // - // Custom entry reading routine, optional for no custom entries. + // Custom entry image read routine, optional for non-custom entries. // OC_CUSTOM_READ CustomRead; // @@ -1537,9 +1576,10 @@ typedef struct OC_BOOT_ARGUMENTS_ { } OC_BOOT_ARGUMENTS; // -// Sanity check max. size for LoadOptions. +// Sanity check max. size for LoadOptions. We need to pass PEM certificates +// to some drivers (e.g. OpenNetworkBoot), so this has to be quite large. // -#define MAX_LOAD_OPTIONS_SIZE SIZE_4KB +#define MAX_LOAD_OPTIONS_SIZE SIZE_16KB /** Are load options apparently valid (Unicode string or cleanly non-present)? diff --git a/Include/Acidanthera/Protocol/OcAudio.h b/Include/Acidanthera/Protocol/OcAudio.h index a06e59cfdcb..8aa8f4c5a41 100644 --- a/Include/Acidanthera/Protocol/OcAudio.h +++ b/Include/Acidanthera/Protocol/OcAudio.h @@ -56,6 +56,7 @@ typedef struct OC_AUDIO_PROTOCOL_ OC_AUDIO_PROTOCOL; #define OC_VOICE_OVER_AUDIO_FILE_MAC_OS_RECOVERY "macOS_Recovery" #define OC_VOICE_OVER_AUDIO_FILE_MAC_OS_TIME_MACHINE "macOS_TimeMachine" #define OC_VOICE_OVER_AUDIO_FILE_MAC_OS_UPDATE_FW "macOS_UpdateFw" +#define OC_VOICE_OVER_AUDIO_FILE_NETWORK_BOOT "NetworkBoot" #define OC_VOICE_OVER_AUDIO_FILE_OTHER_OS "OtherOS" #define OC_VOICE_OVER_AUDIO_FILE_PASSWORD_ACCEPTED "PasswordAccepted" #define OC_VOICE_OVER_AUDIO_FILE_PASSWORD_INCORRECT "PasswordIncorrect" diff --git a/Include/Acidanthera/Protocol/OcBootEntry.h b/Include/Acidanthera/Protocol/OcBootEntry.h index 9ac8326c89a..2a3e261a70a 100644 --- a/Include/Acidanthera/Protocol/OcBootEntry.h +++ b/Include/Acidanthera/Protocol/OcBootEntry.h @@ -28,7 +28,7 @@ WARNING: This protocol is currently undergoing active design. **/ -#define OC_BOOT_ENTRY_PROTOCOL_REVISION 5 +#define OC_BOOT_ENTRY_PROTOCOL_REVISION 6 /** Forward declaration of OC_BOOT_ENTRY_PROTOCOL structure. diff --git a/Library/OcBootManagementLib/BootAudio.c b/Library/OcBootManagementLib/BootAudio.c index bdc4b4ad2a0..f539feb5939 100644 --- a/Library/OcBootManagementLib/BootAudio.c +++ b/Library/OcBootManagementLib/BootAudio.c @@ -214,7 +214,11 @@ OcPlayAudioEntry ( } else if (Entry->Type == OC_BOOT_WINDOWS) { OcPlayAudioFile (Context, OC_VOICE_OVER_AUDIO_FILE_WINDOWS, OC_VOICE_OVER_AUDIO_BASE_TYPE_OPEN_CORE, FALSE); } else if (Entry->Type == OC_BOOT_EXTERNAL_OS) { - OcPlayAudioFile (Context, OC_VOICE_OVER_AUDIO_FILE_EXTERNAL_OS, OC_VOICE_OVER_AUDIO_BASE_TYPE_OPEN_CORE, FALSE); + if (OcAsciiStriStr (Entry->Flavour, OC_FLAVOUR_ID_NETWORK_BOOT) != NULL) { + OcPlayAudioFile (Context, OC_VOICE_OVER_AUDIO_FILE_NETWORK_BOOT, OC_VOICE_OVER_AUDIO_BASE_TYPE_OPEN_CORE, FALSE); + } else { + OcPlayAudioFile (Context, OC_VOICE_OVER_AUDIO_FILE_EXTERNAL_OS, OC_VOICE_OVER_AUDIO_BASE_TYPE_OPEN_CORE, FALSE); + } } else if (Entry->Type == OC_BOOT_SYSTEM) { OcPlayAudioFile (Context, Entry->AudioBasePath, Entry->AudioBaseType, FALSE); } else if (Entry->Type == OC_BOOT_EXTERNAL_TOOL) { diff --git a/Library/OcBootManagementLib/BootEntryManagement.c b/Library/OcBootManagementLib/BootEntryManagement.c index 4837ab74dc0..26c02174ee6 100644 --- a/Library/OcBootManagementLib/BootEntryManagement.c +++ b/Library/OcBootManagementLib/BootEntryManagement.c @@ -676,6 +676,8 @@ InternalAddBootEntryFromCustomEntry ( } BootEntry->IsExternal = FileSystem->External; + BootEntry->CustomRead = CustomEntry->CustomRead; + BootEntry->CustomFree = CustomEntry->CustomFree; if (CustomEntry->Id != NULL) { BootEntry->Id = AsciiStrCopyToUnicode (CustomEntry->Id, 0); @@ -692,7 +694,7 @@ InternalAddBootEntryFromCustomEntry ( return EFI_OUT_OF_RESOURCES; } - if (!CustomEntry->UnmanagedBootAction && !CustomEntry->SystemAction) { + if (!CustomEntry->UnmanagedBootAction && !CustomEntry->SystemAction && !CustomEntry->UnmanagedDevicePath) { ASSERT (CustomEntry->Path != NULL); PathName = AsciiStrCopyToUnicode (CustomEntry->Path, 0); if (PathName == NULL) { @@ -727,7 +729,7 @@ InternalAddBootEntryFromCustomEntry ( BootEntry->AudioBasePath = CustomEntry->AudioBasePath; BootEntry->AudioBaseType = CustomEntry->AudioBaseType; BootEntry->IsExternal = CustomEntry->External; - BootEntry->DevicePath = DuplicateDevicePath (CustomEntry->UnmanagedBootDevicePath); + BootEntry->DevicePath = DuplicateDevicePath (CustomEntry->UnmanagedDevicePath); if (BootEntry->DevicePath == NULL) { FreeBootEntry (BootEntry); @@ -739,7 +741,10 @@ InternalAddBootEntryFromCustomEntry ( BootEntry->AudioBasePath = CustomEntry->AudioBasePath; BootEntry->AudioBaseType = CustomEntry->AudioBaseType; } else if (CustomEntry->Tool) { - BootEntry->Type = OC_BOOT_EXTERNAL_TOOL; + ASSERT (CustomEntry->CustomRead == NULL && CustomEntry->CustomFree == NULL); + BootEntry->Type = OC_BOOT_EXTERNAL_TOOL; + BootEntry->CustomRead = BootContext->PickerContext->CustomRead; + BootEntry->CustomFree = NULL; UnicodeUefiSlashes (PathName); BootEntry->PathName = PathName; } else { @@ -750,13 +755,19 @@ InternalAddBootEntryFromCustomEntry ( // for user entry path is absolute device path. // if (IsBootEntryProtocol) { - UnicodeUefiSlashes (PathName); - BootEntry->DevicePath = FileDevicePath (FileSystem->Handle, PathName); + if (CustomEntry->UnmanagedDevicePath) { + BootEntry->DevicePath = DuplicateDevicePath (CustomEntry->UnmanagedDevicePath); + } else { + UnicodeUefiSlashes (PathName); + BootEntry->DevicePath = FileDevicePath (FileSystem->Handle, PathName); + FreePool (PathName); + } } else { + ASSERT (CustomEntry->UnmanagedDevicePath == NULL); BootEntry->DevicePath = ConvertTextToDevicePath (PathName); + FreePool (PathName); } - FreePool (PathName); if (BootEntry->DevicePath == NULL) { FreeBootEntry (BootEntry); return EFI_OUT_OF_RESOURCES; @@ -770,22 +781,24 @@ InternalAddBootEntryFromCustomEntry ( ) ); if (FilePath == NULL) { - DEBUG (( - DEBUG_WARN, - "OCB: Invalid device path, not adding entry %a\n", - CustomEntry->Name - )); - FreeBootEntry (BootEntry); - return EFI_UNSUPPORTED; - } - - BootEntry->PathName = AllocateCopyPool ( - OcFileDevicePathNameSize (FilePath), - FilePath->PathName - ); - if (BootEntry->PathName == NULL) { - FreeBootEntry (BootEntry); - return EFI_OUT_OF_RESOURCES; + if (BootEntry->CustomRead == NULL) { + DEBUG (( + DEBUG_WARN, + "OCB: Invalid device path, not adding entry %a\n", + CustomEntry->Name + )); + FreeBootEntry (BootEntry); + return EFI_UNSUPPORTED; + } + } else { + BootEntry->PathName = AllocateCopyPool ( + OcFileDevicePathNameSize (FilePath), + FilePath->PathName + ); + if (BootEntry->PathName == NULL) { + FreeBootEntry (BootEntry); + return EFI_OUT_OF_RESOURCES; + } } // @@ -843,7 +856,7 @@ InternalAddBootEntryFromCustomEntry ( BootEntry->ExposeDevicePath = CustomEntry->RealPath; BootEntry->FullNvramAccess = CustomEntry->FullNvramAccess; - if ((BootEntry->UnmanagedBootAction != NULL) || (BootEntry->SystemAction != NULL)) { + if ((BootEntry->UnmanagedBootAction != NULL) || (BootEntry->SystemAction != NULL) || (CustomEntry->CustomRead != NULL)) { ASSERT (CustomEntry->Arguments == NULL); } else { ASSERT (CustomEntry->Arguments != NULL); @@ -1464,6 +1477,7 @@ AddBootEntryFromBootOption ( ); } while (NumPatchedNodes > 0); + Status = EFI_NOT_FOUND; if ((ExpandedDevicePath == NULL) && (CustomFileSystem != NULL)) { // // If non-standard device path, attempt to pre-construct a user config @@ -1484,12 +1498,12 @@ AddBootEntryFromBootOption ( *CustomIndex = Index; } - InternalAddBootEntryFromCustomEntry ( - BootContext, - CustomFileSystem, - &BootContext->PickerContext->CustomEntries[Index], - FALSE - ); + Status = InternalAddBootEntryFromCustomEntry ( + BootContext, + CustomFileSystem, + &BootContext->PickerContext->CustomEntries[Index], + FALSE + ); break; } } @@ -1503,6 +1517,36 @@ AddBootEntryFromBootOption ( EntryProtocolDevPath = InternalGetOcEntryProtocolDevPath (DevicePath); + if (EntryProtocolDevPath != NULL) { + // + // Zero GUID can be non-file-based entry (e.g. from network boot), + // or file-based entry on OVMF mounted drives where GPT GUIDs are + // not available. Try non-file-based first. + // + if (CompareGuid (&gEfiPartTypeUnusedGuid, &EntryProtocolDevPath->Partuuid)) { + Status = OcAddEntriesFromBootEntryProtocol ( + BootContext, + CustomFileSystem, + EntryProtocolHandles, + EntryProtocolHandleCount, + EntryProtocolDevPath->EntryName.PathName, + TRUE, + FALSE + ); + if (!EFI_ERROR (Status)) { + if (EntryProtocolPartuuid != NULL) { + CopyGuid (EntryProtocolPartuuid, &gEfiPartTypeUnusedGuid); + } + + if (EntryProtocolId != NULL) { + *EntryProtocolId = AllocateCopyPool (StrSize (EntryProtocolDevPath->EntryName.PathName), EntryProtocolDevPath->EntryName.PathName); + } + + EntryProtocolDevPath = NULL; + } + } + } + if (EntryProtocolDevPath != NULL) { // // Search for ID on matching device only. @@ -1574,7 +1618,7 @@ AddBootEntryFromBootOption ( DevicePath = ExpandedDevicePath; if (DevicePath == NULL) { - return EFI_NOT_FOUND; + return Status; } } else if (NumPatchedNodes == -1) { // @@ -2511,6 +2555,7 @@ OcLoadBootEntry ( EFI_STATUS Status; EFI_HANDLE EntryHandle; INTERNAL_DMG_LOAD_CONTEXT DmgLoadContext; + VOID *CustomFreeContext; if ((BootEntry->Type & OC_BOOT_UNMANAGED) != 0) { ASSERT (BootEntry->UnmanagedBootAction != NULL); @@ -2527,7 +2572,8 @@ OcLoadBootEntry ( BootEntry, ParentHandle, &EntryHandle, - &DmgLoadContext + &DmgLoadContext, + &CustomFreeContext ); if (!EFI_ERROR (Status)) { // @@ -2546,14 +2592,26 @@ OcLoadBootEntry ( if (EFI_ERROR (Status)) { DEBUG ((DEBUG_WARN, "OCB: StartImage failed - %r\n", Status)); // - // Unload dmg if any. - // - InternalUnloadDmg (&DmgLoadContext); - // // Unload image. + // Note: This is not needed on success, since this has already been done + // and image handle is now invalid, if image was an application and it + // exited successfully: + // https://github.com/tianocore/edk2/blob/a3aab12c34dba35d1fd592f4939cb70617668f7e/MdeModulePkg/Core/Dxe/Image/Image.c#L1789-L1793 // gBS->UnloadImage (EntryHandle); } + + // + // Unload dmg if any. + // + InternalUnloadDmg (&DmgLoadContext); + // + // Unload any entry protocol custom items. + // For instance HTTP Boot natively supported RAM disk, on loading .iso or .img. + // + if (BootEntry->CustomFree != NULL) { + BootEntry->CustomFree (CustomFreeContext); + } } else { DEBUG ((DEBUG_WARN, "OCB: LoadImage failed - %r\n", Status)); } diff --git a/Library/OcBootManagementLib/BootEntryProtocol.c b/Library/OcBootManagementLib/BootEntryProtocol.c index 6bcde243c36..6d463f81589 100644 --- a/Library/OcBootManagementLib/BootEntryProtocol.c +++ b/Library/OcBootManagementLib/BootEntryProtocol.c @@ -248,6 +248,10 @@ OcAddEntriesFromBootEntryProtocol ( { BEP_ADD_ENTRIES_CONTEXT AddEntriesContext; + // + // May be CustomFileSystem, but not NULL. + // + ASSERT (FileSystem != NULL); ASSERT (!CreateDefault || (DefaultEntryId != NULL)); AddEntriesContext.ReturnStatus = EFI_NOT_FOUND; diff --git a/Library/OcBootManagementLib/BootManagementInternal.h b/Library/OcBootManagementLib/BootManagementInternal.h index 1a3ac19387a..0e156c2725d 100644 --- a/Library/OcBootManagementLib/BootManagementInternal.h +++ b/Library/OcBootManagementLib/BootManagementInternal.h @@ -133,8 +133,9 @@ InternalCheckScanPolicy ( EFI_DEVICE_PATH_PROTOCOL * InternalLoadDmg ( - IN OUT INTERNAL_DMG_LOAD_CONTEXT *Context, - IN OC_DMG_LOADING_SUPPORT DmgLoading + IN OUT INTERNAL_DMG_LOAD_CONTEXT *Context, + IN OC_DMG_LOADING_SUPPORT DmgLoading, + IN OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext ); VOID @@ -177,7 +178,8 @@ InternalLoadBootEntry ( IN OC_BOOT_ENTRY *BootEntry, IN EFI_HANDLE ParentHandle, OUT EFI_HANDLE *EntryHandle, - OUT INTERNAL_DMG_LOAD_CONTEXT *DmgLoadContext + OUT INTERNAL_DMG_LOAD_CONTEXT *DmgLoadContext, + OUT VOID **CustomFreeContext ); UINT16 * diff --git a/Library/OcBootManagementLib/DefaultEntryChoice.c b/Library/OcBootManagementLib/DefaultEntryChoice.c index d45c9b4c248..6d38bd2fce1 100644 --- a/Library/OcBootManagementLib/DefaultEntryChoice.c +++ b/Library/OcBootManagementLib/DefaultEntryChoice.c @@ -1529,19 +1529,21 @@ InternalLoadBootEntry ( IN OC_BOOT_ENTRY *BootEntry, IN EFI_HANDLE ParentHandle, OUT EFI_HANDLE *EntryHandle, - OUT INTERNAL_DMG_LOAD_CONTEXT *DmgLoadContext + OUT INTERNAL_DMG_LOAD_CONTEXT *DmgLoadContext, + OUT VOID **CustomFreeContext ) { - EFI_STATUS Status; - EFI_STATUS OptionalStatus; - EFI_DEVICE_PATH_PROTOCOL *DevicePath; - EFI_HANDLE StorageHandle; - EFI_DEVICE_PATH_PROTOCOL *StoragePath; - CHAR16 *UnicodeDevicePath; - EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; - VOID *EntryData; - UINT32 EntryDataSize; - CONST CHAR8 *Args; + EFI_STATUS Status; + EFI_STATUS OptionalStatus; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_HANDLE StorageHandle; + EFI_DEVICE_PATH_PROTOCOL *StoragePath; + CHAR16 *UnicodeDevicePath; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + VOID *EntryData; + UINT32 EntryDataSize; + CONST CHAR8 *Args; + OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT DmgPreloadContext; ASSERT (BootEntry != NULL); // @@ -1558,38 +1560,57 @@ InternalLoadBootEntry ( ZeroMem (DmgLoadContext, sizeof (*DmgLoadContext)); - EntryData = NULL; - EntryDataSize = 0; - StorageHandle = NULL; - StoragePath = NULL; + EntryData = NULL; + EntryDataSize = 0; + StorageHandle = NULL; + StoragePath = NULL; + *CustomFreeContext = NULL; + ZeroMem (&DmgPreloadContext, sizeof (DmgPreloadContext)); - if (BootEntry->IsFolder) { + // + // CustomRead must be set for external tools, but may also be set for boot + // entry protocol entries. + // + ASSERT (BootEntry->Type != OC_BOOT_EXTERNAL_TOOL || BootEntry->CustomRead != NULL); + + if (BootEntry->CustomRead != NULL) { + Status = BootEntry->CustomRead ( + Context->StorageContext, + BootEntry, + &EntryData, + &EntryDataSize, + &DevicePath, + &StorageHandle, + &StoragePath, + Context->DmgLoading, + &DmgPreloadContext, + CustomFreeContext + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "OCB: Custom read failed - %r\n", Status)); + return Status; + } + } + + if ( (DmgPreloadContext.DmgFile != NULL) + || (DmgPreloadContext.DmgContext != NULL) + || BootEntry->IsFolder) + { if (Context->DmgLoading == OcDmgLoadingDisabled) { return EFI_SECURITY_VIOLATION; } DmgLoadContext->DevicePath = BootEntry->DevicePath; - DevicePath = InternalLoadDmg (DmgLoadContext, Context->DmgLoading); + DevicePath = InternalLoadDmg ( + DmgLoadContext, + Context->DmgLoading, + &DmgPreloadContext + ); if (DevicePath == NULL) { return EFI_UNSUPPORTED; } - } else if (BootEntry->Type == OC_BOOT_EXTERNAL_TOOL) { - ASSERT (Context->CustomRead != NULL); - - Status = Context->CustomRead ( - Context->StorageContext, - BootEntry, - &EntryData, - &EntryDataSize, - &DevicePath, - &StorageHandle, - &StoragePath - ); - - if (EFI_ERROR (Status)) { - return Status; - } - } else { + } else if (BootEntry->CustomRead == NULL) { DevicePath = BootEntry->DevicePath; } @@ -1691,6 +1712,9 @@ InternalLoadBootEntry ( } } else { InternalUnloadDmg (DmgLoadContext); + if (BootEntry->CustomFree != NULL) { + BootEntry->CustomFree (*CustomFreeContext); + } } return Status; diff --git a/Library/OcBootManagementLib/DmgBootSupport.c b/Library/OcBootManagementLib/DmgBootSupport.c index e0a717de6aa..e2b31ff6caa 100644 --- a/Library/OcBootManagementLib/DmgBootSupport.c +++ b/Library/OcBootManagementLib/DmgBootSupport.c @@ -317,8 +317,9 @@ InternalFindDmgChunklist ( EFI_DEVICE_PATH_PROTOCOL * InternalLoadDmg ( - IN OUT INTERNAL_DMG_LOAD_CONTEXT *Context, - IN OC_DMG_LOADING_SUPPORT DmgLoading + IN OUT INTERNAL_DMG_LOAD_CONTEXT *Context, + IN OC_DMG_LOADING_SUPPORT DmgLoading, + IN OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext ) { EFI_DEVICE_PATH_PROTOCOL *DevPath; @@ -342,130 +343,154 @@ InternalLoadDmg ( ASSERT (Context != NULL); - DevPath = Context->DevicePath; - Status = OcOpenFileByDevicePath ( - &DevPath, - &DmgDir, - EFI_FILE_MODE_READ, - EFI_FILE_DIRECTORY - ); - if (EFI_ERROR (Status)) { - DevPathText = ConvertDevicePathToText (Context->DevicePath, FALSE, FALSE); - DEBUG ((DEBUG_INFO, "OCB: Failed to open DMG directory %s\n", DevPathText)); - if (DevPathText != NULL) { - FreePool (DevPathText); - } - - return NULL; - } + if (DmgPreloadContext->DmgContext != NULL) { + Context->DmgContext = DmgPreloadContext->DmgContext; + DmgFileSize = DmgPreloadContext->DmgFileSize; + } else { + if (DmgPreloadContext->DmgFile != NULL) { + DmgFile = DmgPreloadContext->DmgFile; + DmgFileSize = DmgPreloadContext->DmgFileSize; + } else { + DevPath = Context->DevicePath; + Status = OcOpenFileByDevicePath ( + &DevPath, + &DmgDir, + EFI_FILE_MODE_READ, + EFI_FILE_DIRECTORY + ); + if (EFI_ERROR (Status)) { + DevPathText = ConvertDevicePathToText (Context->DevicePath, FALSE, FALSE); + DEBUG ((DEBUG_INFO, "OCB: Failed to open DMG directory %s\n", DevPathText)); + if (DevPathText != NULL) { + FreePool (DevPathText); + } - DmgFileInfo = InternalFindFirstDmgFileName (DmgDir, &DmgFileNameLen); - if (DmgFileInfo == NULL) { - DevPathText = ConvertDevicePathToText (Context->DevicePath, FALSE, FALSE); - DEBUG ((DEBUG_INFO, "OCB: Unable to find any DMG at %s\n")); - if (DevPathText != NULL) { - FreePool (DevPathText); - } + return NULL; + } - DmgDir->Close (DmgDir); - return NULL; - } + DmgFileInfo = InternalFindFirstDmgFileName (DmgDir, &DmgFileNameLen); + if (DmgFileInfo == NULL) { + DevPathText = ConvertDevicePathToText (Context->DevicePath, FALSE, FALSE); + DEBUG ((DEBUG_INFO, "OCB: Unable to find any DMG at %s\n")); + if (DevPathText != NULL) { + FreePool (DevPathText); + } - Status = OcSafeFileOpen ( - DmgDir, - &DmgFile, - DmgFileInfo->FileName, - EFI_FILE_MODE_READ, - 0 - ); - if (EFI_ERROR (Status)) { - DEBUG (( - DEBUG_INFO, - "OCB: Failed to open DMG file %s - %r\n", - DmgFileInfo->FileName, - Status - )); + DmgDir->Close (DmgDir); + return NULL; + } - FreePool (DmgFileInfo); - DmgDir->Close (DmgDir); - return NULL; - } + Status = OcSafeFileOpen ( + DmgDir, + &DmgFile, + DmgFileInfo->FileName, + EFI_FILE_MODE_READ, + 0 + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "OCB: Failed to open DMG file %s - %r\n", + DmgFileInfo->FileName, + Status + )); + + FreePool (DmgFileInfo); + DmgDir->Close (DmgDir); + return NULL; + } - Status = OcGetFileSize (DmgFile, &DmgFileSize); - if (EFI_ERROR (Status)) { - DEBUG (( - DEBUG_INFO, - "OCB: Failed to retrieve DMG file size - %r\n", - Status - )); + Status = OcGetFileSize (DmgFile, &DmgFileSize); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "OCB: Failed to retrieve DMG file size - %r\n", + Status + )); + + FreePool (DmgFileInfo); + DmgDir->Close (DmgDir); + DmgFile->Close (DmgFile); + return NULL; + } + } - FreePool (DmgFileInfo); - DmgDir->Close (DmgDir); - DmgFile->Close (DmgFile); - return NULL; - } + Context->DmgContext = AllocatePool (sizeof (*Context->DmgContext)); + if (Context->DmgContext == NULL) { + DEBUG ((DEBUG_INFO, "OCB: Failed to allocate DMG context\n")); + return NULL; + } - Context->DmgContext = AllocatePool (sizeof (*Context->DmgContext)); - if (Context->DmgContext == NULL) { - DEBUG ((DEBUG_INFO, "OCB: Failed to allocate DMG context\n")); - return NULL; - } + Result = OcAppleDiskImageInitializeFromFile (Context->DmgContext, DmgFile); - Result = OcAppleDiskImageInitializeFromFile (Context->DmgContext, DmgFile); + DmgFile->Close (DmgFile); - DmgFile->Close (DmgFile); + if (!Result) { + DEBUG ((DEBUG_INFO, "OCB: Failed to initialise DMG from file\n")); - if (!Result) { - DEBUG ((DEBUG_INFO, "OCB: Failed to initialise DMG from file\n")); + if (DmgPreloadContext->DmgFile == NULL) { + FreePool (DmgFileInfo); + DmgDir->Close (DmgDir); + } - FreePool (DmgFileInfo); - FreePool (Context->DmgContext); - DmgDir->Close (DmgDir); - return NULL; + FreePool (Context->DmgContext); + return NULL; + } } ChunklistBuffer = NULL; ChunklistFileSize = 0; + if ( (DmgPreloadContext->DmgFile != NULL) + || (DmgPreloadContext->DmgContext != NULL)) + { + if (DmgPreloadContext->ChunklistBuffer != NULL) { + ChunklistBuffer = DmgPreloadContext->ChunklistBuffer; + ChunklistFileSize = DmgPreloadContext->ChunklistFileSize; + } + } else { + ChunklistFileInfo = InternalFindDmgChunklist ( + DmgDir, + DmgFileInfo->FileName, + DmgFileNameLen + ); + if (ChunklistFileInfo != NULL) { + Status = OcSafeFileOpen ( + DmgDir, + &ChunklistFile, + ChunklistFileInfo->FileName, + EFI_FILE_MODE_READ, + 0 + ); + if (!EFI_ERROR (Status)) { + Status = OcGetFileSize (ChunklistFile, &ChunklistFileSize); + if (Status == EFI_SUCCESS) { + ChunklistBuffer = AllocatePool (ChunklistFileSize); - ChunklistFileInfo = InternalFindDmgChunklist ( - DmgDir, - DmgFileInfo->FileName, - DmgFileNameLen - ); - if (ChunklistFileInfo != NULL) { - Status = OcSafeFileOpen ( - DmgDir, - &ChunklistFile, - ChunklistFileInfo->FileName, - EFI_FILE_MODE_READ, - 0 - ); - if (!EFI_ERROR (Status)) { - Status = OcGetFileSize (ChunklistFile, &ChunklistFileSize); - if (Status == EFI_SUCCESS) { - ChunklistBuffer = AllocatePool (ChunklistFileSize); - - if (ChunklistBuffer == NULL) { - ChunklistFileSize = 0; - } else { - Status = OcGetFileData (ChunklistFile, 0, ChunklistFileSize, ChunklistBuffer); - if (EFI_ERROR (Status)) { - FreePool (ChunklistBuffer); - ChunklistBuffer = NULL; + if (ChunklistBuffer == NULL) { ChunklistFileSize = 0; + } else { + Status = OcGetFileData (ChunklistFile, 0, ChunklistFileSize, ChunklistBuffer); + if (EFI_ERROR (Status)) { + FreePool (ChunklistBuffer); + ChunklistBuffer = NULL; + ChunklistFileSize = 0; + } } } + + ChunklistFile->Close (ChunklistFile); } - ChunklistFile->Close (ChunklistFile); + FreePool (ChunklistFileInfo); } - - FreePool (ChunklistFileInfo); } - FreePool (DmgFileInfo); - - DmgDir->Close (DmgDir); + if ( (DmgPreloadContext->DmgFile == NULL) + && (DmgPreloadContext->DmgContext == NULL)) + { + FreePool (DmgFileInfo); + DmgDir->Close (DmgDir); + } DevPath = InternalGetDiskImageBootFile ( Context, diff --git a/Library/OcBootManagementLib/OcBootManagementLib.c b/Library/OcBootManagementLib/OcBootManagementLib.c index b1d778226fa..8f9a242e87f 100644 --- a/Library/OcBootManagementLib/OcBootManagementLib.c +++ b/Library/OcBootManagementLib/OcBootManagementLib.c @@ -508,7 +508,6 @@ OcRunBootPicker ( ); OcRestoreNvramProtection (FwRuntime); - RestoreMode (); // // Do not wait on successful return code. @@ -524,6 +523,11 @@ OcRunBootPicker ( OcPlayAudioFile (Context, OC_VOICE_OVER_AUDIO_FILE_EXECUTION_SUCCESSFUL, OC_VOICE_OVER_AUDIO_BASE_TYPE_OPEN_CORE, FALSE); } + // + // Restore mode after any delay. + // + RestoreMode (); + // // Ensure that we flush all pressed keys after the application. // This resolves the problem of application-pressed keys being used to control the menu. diff --git a/Library/OcMainLib/OpenCoreMisc.c b/Library/OcMainLib/OpenCoreMisc.c index 070bd8bed75..999f44d98de 100644 --- a/Library/OcMainLib/OpenCoreMisc.c +++ b/Library/OcMainLib/OpenCoreMisc.c @@ -25,6 +25,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include +#include #include #include #include @@ -232,6 +233,21 @@ ProduceDebugReport ( DEBUG ((DEBUG_INFO, "OC: GOPInfo dumping - %r\n", Status)); + Status = OcSafeFileOpen ( + SysReport, + &SubReport, + L"Drivers", + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, + EFI_FILE_DIRECTORY + ); + if (!EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "OC: Dumping DriverImageNames for report...\n")); + Status = OcDriverInfoDump (SubReport); + SubReport->Close (SubReport); + } + + DEBUG ((DEBUG_INFO, "OC: DriverImageNames dumping - %r\n", Status)); + SysReport->Close (SysReport); Fs->Close (Fs); @@ -242,13 +258,16 @@ STATIC EFI_STATUS EFIAPI OcToolLoadEntry ( - IN OC_STORAGE_CONTEXT *Storage, - IN OC_BOOT_ENTRY *ChosenEntry, - OUT VOID **Data, - OUT UINT32 *DataSize, - OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, - OUT EFI_HANDLE *StorageHandle, - OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath + IN OC_STORAGE_CONTEXT *Storage, + IN OC_BOOT_ENTRY *ChosenEntry, + OUT VOID **Data, + OUT UINT32 *DataSize, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, + OUT EFI_HANDLE *StorageHandle, + OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath, + IN OC_DMG_LOADING_SUPPORT DmgLoading, + OUT OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext, + OUT VOID **CustomFreeContext ) { EFI_STATUS Status; diff --git a/Library/OcPngLib/lodepng.c b/Library/OcPngLib/lodepng.c index 9090f30ffc6..16aa8a55ba2 100644 --- a/Library/OcPngLib/lodepng.c +++ b/Library/OcPngLib/lodepng.c @@ -121,9 +121,6 @@ to something as fast. */ #ifdef EFIAPI -// Floating point operations are used here, this must be defined to prevent linker error -const int32_t _fltused = 0; - #define LODEPNG_MAX_ALLOC ((size_t)256*1024*1024) void* lodepng_malloc(size_t size) { diff --git a/OpenCorePkg.dsc b/OpenCorePkg.dsc index d5d157a6357..1861d4923f3 100755 --- a/OpenCorePkg.dsc +++ b/OpenCorePkg.dsc @@ -30,8 +30,8 @@ DEFINE NETWORK_ENABLE = TRUE DEFINE NETWORK_SNP_ENABLE = TRUE DEFINE NETWORK_IP4_ENABLE = TRUE - DEFINE NETWORK_IP6_ENABLE = FALSE - DEFINE NETWORK_TLS_ENABLE = FALSE + DEFINE NETWORK_IP6_ENABLE = TRUE + DEFINE NETWORK_TLS_ENABLE = TRUE DEFINE NETWORK_HTTP_ENABLE = TRUE DEFINE NETWORK_HTTP_BOOT_ENABLE = TRUE DEFINE NETWORK_ALLOW_HTTP_CONNECTIONS = TRUE @@ -160,7 +160,7 @@ UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf UefiBootManagerLib|MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf UefiDriverEntryPoint|OpenCorePkg/Library/OcDriverEntryPoint/UefiDriverEntryPoint.inf - UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf + UefiHiiServicesLib|OpenCorePkg/Library/OcHiiServicesLib/OcHiiServicesLib.inf UefiImageExtraActionLib|MdePkg/Library/BaseUefiImageExtraActionLibNull/BaseUefiImageExtraActionLibNull.inf UefiLib|MdePkg/Library/UefiLib/UefiLib.inf UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf @@ -169,8 +169,21 @@ VariableFlashInfoLib|MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf ResetSystemLib|OpenCorePkg/Library/OcResetSystemLib/OcResetSystemLib.inf + !if $(NETWORK_TLS_ENABLE) == TRUE + BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf + # FileExplorerLib is for TlsAuthConfigDxe only (not used by us, but enabled by NETWORK_TLS_ENABLE) + FileExplorerLib|MdeModulePkg/Library/FileExplorerLib/FileExplorerLib.inf + IntrinsicLib|MdePkg/Library/IntrinsicLib/IntrinsicLib.inf + OpensslLib|CryptoPkg/Library/OpensslLib/OpensslLib.inf + RngLib|MdeModulePkg/Library/BaseRngLibTimerLib/BaseRngLibTimerLib.inf + SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf + TlsLib|CryptoPkg/Library/TlsLib/TlsLib.inf + !endif + !include NetworkPkg/NetworkLibs.dsc.inc + HttpLib|NetworkPkg/Library/DxeHttpLib/DxeHttpLib.inf + !include Ext4Pkg/Ext4Defines.dsc.inc !include Ext4Pkg/Ext4Libs.dsc.inc @@ -244,7 +257,6 @@ OpenCorePkg/Library/OcBlitLib/OcBlitLib.inf OpenCorePkg/Library/OcBootManagementLib/OcBootManagementLib.inf OpenCorePkg/Library/OcBootServicesTableLib/OcBootServicesTableLib.inf - OpenCorePkg/Library/OcCompilerIntrinsicsLib/OcCompilerIntrinsicsLib.inf OpenCorePkg/Library/OcCompressionLib/OcCompressionLib.inf OpenCorePkg/Library/OcConfigurationLib/OcConfigurationLib.inf OpenCorePkg/Library/OcConsoleControlEntryModeLib/OcConsoleControlEntryModeGenericLib.inf @@ -301,6 +313,7 @@ OpenCorePkg/Platform/OpenCanopy/OpenCanopy.inf OpenCorePkg/Platform/OpenLegacyBoot/OpenLegacyBoot.inf OpenCorePkg/Platform/OpenLinuxBoot/OpenLinuxBoot.inf + OpenCorePkg/Platform/OpenNetworkBoot/OpenNetworkBoot.inf OpenCorePkg/Platform/OpenNtfsDxe/OpenNtfsDxe.inf OpenCorePkg/Platform/OpenPartitionDxe/PartitionDxe.inf OpenCorePkg/Platform/OpenRuntime/OpenRuntime.inf @@ -370,13 +383,23 @@ # Ext4 driver Ext4Pkg/Ext4Dxe/Ext4Dxe.inf + # RNG and HASH2 protocols are required by various network boot drivers since edk2-stable202405 + # REF: https://github.com/acidanthera/bugtracker/issues/2421 + SecurityPkg/RandomNumberGenerator/RngDxe/RngDxe.inf + SecurityPkg/Hash2DxeCrypto/Hash2DxeCrypto.inf + # # Network Support # !include NetworkPkg/NetworkComponents.dsc.inc + # + # Ramdisk support (driver required for network boot native .iso/.img support) + # + MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf + [LibraryClasses] - NULL|OpenCorePkg/Library/OcCompilerIntrinsicsLib/OcCompilerIntrinsicsLib.inf + NULL|MdePkg/Library/IntrinsicLib/IntrinsicLib.inf [PcdsFixedAtBuild] gEfiMdePkgTokenSpaceGuid.PcdMaximumAsciiStringLength|0 diff --git a/OpenDuetPkg.dsc b/OpenDuetPkg.dsc index 3704c47fdb7..f8ab35e26c6 100644 --- a/OpenDuetPkg.dsc +++ b/OpenDuetPkg.dsc @@ -245,10 +245,8 @@ OpenCorePkg/Legacy/BootPlatform/LegacyRegion2Dxe/LegacyRegion2Dxe.inf OpenCorePkg/Legacy/BootPlatform/BiosVideo/BiosVideo.inf - OpenCorePkg/Library/OcCompilerIntrinsicsLib/OcCompilerIntrinsicsLib.inf - [LibraryClasses] - NULL|OpenCorePkg/Library/OcCompilerIntrinsicsLib/OcCompilerIntrinsicsLib.inf + NULL|MdePkg/Library/IntrinsicLib/IntrinsicLib.inf [PcdsFeatureFlag] gEfiMdeModulePkgTokenSpaceGuid.PcdSupportHiiImageProtocol|FALSE diff --git a/OpenDuetPkgDefines.fdf.inc b/OpenDuetPkgDefines.fdf.inc index 4a688b389a1..40e2f7cff9c 100644 --- a/OpenDuetPkgDefines.fdf.inc +++ b/OpenDuetPkgDefines.fdf.inc @@ -22,7 +22,7 @@ !if ($(TARGET) == DEBUG) NumBlocks = 0xe #GenFv image size 0xe0000 !else - NumBlocks = 0x24 #GenFv image size 0x240000 + NumBlocks = 0x25 #GenFv image size 0x250000 !endif !endif FvAlignment = 16 #FV alignment and FV attributes setting. diff --git a/Platform/OpenCanopy/GuiApp.c b/Platform/OpenCanopy/GuiApp.c index acf6131201d..6081f5920cc 100644 --- a/Platform/OpenCanopy/GuiApp.c +++ b/Platform/OpenCanopy/GuiApp.c @@ -47,7 +47,8 @@ CONST CHAR8 * [LABEL_SHELL] = "Shell", [LABEL_SIP_IS_ENABLED] = "SIPEnabled", [LABEL_SIP_IS_DISABLED] = "SIPDisabled", - [LABEL_FIRMWARE_SETTINGS] = "FirmwareSettings" + [LABEL_FIRMWARE_SETTINGS] = "FirmwareSettings", + [LABEL_NETWORK_BOOT] = "NetworkBoot" }; STATIC diff --git a/Platform/OpenCanopy/GuiApp.h b/Platform/OpenCanopy/GuiApp.h index fc9bd0852fa..efce8957589 100644 --- a/Platform/OpenCanopy/GuiApp.h +++ b/Platform/OpenCanopy/GuiApp.h @@ -67,6 +67,7 @@ typedef enum { LABEL_SIP_IS_ENABLED, LABEL_SIP_IS_DISABLED, LABEL_FIRMWARE_SETTINGS, + LABEL_NETWORK_BOOT, LABEL_NUM_TOTAL } LABEL_TARGET; diff --git a/Platform/OpenCanopy/Views/BootPicker.c b/Platform/OpenCanopy/Views/BootPicker.c index ba714682cf0..cf0b92c26b3 100644 --- a/Platform/OpenCanopy/Views/BootPicker.c +++ b/Platform/OpenCanopy/Views/BootPicker.c @@ -1470,7 +1470,12 @@ BootPickerEntriesSet ( Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_WINDOWS]); break; case OC_BOOT_EXTERNAL_OS: - Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_OTHER]); + if (OcAsciiStriStr (Entry->Flavour, OC_FLAVOUR_ID_NETWORK_BOOT) != NULL) { + Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_NETWORK_BOOT]); + } else { + Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_OTHER]); + } + break; // // Use flavour-based labels for system entries (e.g. from boot entry protocol). diff --git a/Platform/OpenLegacyBoot/OpenLegacyBoot.c b/Platform/OpenLegacyBoot/OpenLegacyBoot.c index f18577f434d..51879712998 100644 --- a/Platform/OpenLegacyBoot/OpenLegacyBoot.c +++ b/Platform/OpenLegacyBoot/OpenLegacyBoot.c @@ -426,7 +426,7 @@ OcGetLegacyBootEntries ( PickerEntry->External = IsExternal; PickerEntry->UnmanagedBootAction = UnmanagedBootActionDoLegacyBoot; PickerEntry->UnmanagedBootGetFinalDevicePath = UnmanagedBootGetFinalDevicePath; - PickerEntry->UnmanagedBootDevicePath = BlockDevicePath; + PickerEntry->UnmanagedDevicePath = BlockDevicePath; if ((PickerEntry->Name == NULL) || (PickerEntry->Flavour == NULL)) { OcFlexArrayFree (&FlexPickerEntries); diff --git a/Platform/OpenNetworkBoot/BmBoot.c b/Platform/OpenNetworkBoot/BmBoot.c index ba66ca2bcad..69919829be2 100644 --- a/Platform/OpenNetworkBoot/BmBoot.c +++ b/Platform/OpenNetworkBoot/BmBoot.c @@ -1,14 +1,15 @@ /** @file - Library functions which relates with booting. + Library functions which relate to booting. Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. Copyright (c) 2011 - 2021, Intel Corporation. All rights reserved.
(C) Copyright 2015-2021 Hewlett Packard Enterprise Development LP
+Copyright (C) 2024, Mike Beaton. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ -#include "InternalBm.h" +#include "NetworkBootInternal.h" EFI_RAM_DISK_PROTOCOL *mRamDisk = NULL; @@ -23,6 +24,7 @@ EFI_RAM_DISK_PROTOCOL *mRamDisk = NULL; @return The next possible full path pointing to the load option. Caller is responsible to free the memory. **/ +STATIC EFI_DEVICE_PATH_PROTOCOL * BmExpandMediaDevicePath ( IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, @@ -145,6 +147,7 @@ BmExpandMediaDevicePath ( @retval TRUE Left and Right are the same. @retval FALSE Left and Right are the different. **/ +STATIC BOOLEAN BmMatchHttpBootDevicePath ( IN EFI_DEVICE_PATH_PROTOCOL *Left, @@ -190,6 +193,7 @@ BmMatchHttpBootDevicePath ( @return The next possible full path pointing to the load option. Caller is responsible to free the memory. **/ +STATIC EFI_DEVICE_PATH_PROTOCOL * BmExpandNetworkFileSystem ( IN EFI_HANDLE LoadFileHandle, @@ -297,6 +301,7 @@ BmGetRamDiskDevicePath ( @retval RAM Disk buffer. **/ +STATIC VOID * BmGetRamDiskMemoryInfo ( IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath, @@ -370,10 +375,13 @@ BmDestroyRamDisk ( @return The full device path pointing to the load option buffer. **/ +STATIC EFI_DEVICE_PATH_PROTOCOL * BmExpandLoadFile ( IN EFI_HANDLE LoadFileHandle, - IN EFI_DEVICE_PATH_PROTOCOL *FilePath + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + OUT VOID **Data, + OUT UINT32 *DataSize ) { EFI_STATUS Status; @@ -383,6 +391,12 @@ BmExpandLoadFile ( UINTN BufferSize; EFI_DEVICE_PATH_PROTOCOL *FullPath; + ASSERT (Data != NULL); + ASSERT (DataSize != NULL); + + *Data = NULL; + *DataSize = 0; + Status = gBS->OpenProtocol ( LoadFileHandle, &gEfiLoadFileProtocolGuid, @@ -400,11 +414,38 @@ BmExpandLoadFile ( return NULL; } + // + // In call tree of original BmGetLoadOptionBuffer, handling this case + // is deferred to subsequent call to GetFileBufferByFilePath. + // if (Status == EFI_BUFFER_TOO_SMALL) { // - // The load option buffer is directly returned by LoadFile. + // Limited to UINT32 by DataSize in OC_CUSTOM_READ, which is limited + // by Size in OcGetFileSize. + // + if (BufferSize > MAX_UINT32) { + return NULL; + } + + FileBuffer = AllocatePool (BufferSize); + if (FileBuffer == NULL) { + return NULL; + } + + // + // Call LoadFile with the correct buffer size. // - return DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle)); + FullPath = DevicePathFromHandle (LoadFileHandle); + Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer); + if (EFI_ERROR (Status)) { + FreePool (FileBuffer); + return NULL; + } + + *DataSize = (UINT32)BufferSize; + *Data = FileBuffer; + + return DuplicateDevicePath (FullPath); } // @@ -487,7 +528,10 @@ BmExpandLoadFile ( **/ EFI_DEVICE_PATH_PROTOCOL * BmExpandLoadFiles ( - IN EFI_DEVICE_PATH_PROTOCOL *FilePath + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + OUT VOID **Data, + OUT UINT32 *DataSize, + IN BOOLEAN ValidateHttp ) { EFI_STATUS Status; @@ -496,6 +540,7 @@ BmExpandLoadFiles ( UINTN HandleCount; UINTN Index; EFI_DEVICE_PATH_PROTOCOL *Node; + EFI_EVENT NotifyEvent; // // Get file buffer from load file instance. @@ -542,5 +587,25 @@ BmExpandLoadFiles ( return NULL; } - return BmExpandLoadFile (Handle, FilePath); + if (ValidateHttp) { + NotifyEvent = MonitorHttpBootCallback (Handle); + if (NotifyEvent == NULL) { + return NULL; + } + } + + Node = BmExpandLoadFile (Handle, FilePath, Data, DataSize); + + if (ValidateHttp) { + gBS->CloseEvent (NotifyEvent); + + if ((Node != NULL) && !UriWasValidated ()) { + Print (L"\n"); ///< Sort out cramped spacing + DEBUG ((DEBUG_ERROR, "NTBT: LoadFile returned value but URI was never validated\n")); + FreePool (Node); + return NULL; + } + } + + return Node; } diff --git a/Platform/OpenNetworkBoot/BmBootDescription.c b/Platform/OpenNetworkBoot/BmBootDescription.c index 4b8c8f472e8..d4a054127b4 100644 --- a/Platform/OpenNetworkBoot/BmBootDescription.c +++ b/Platform/OpenNetworkBoot/BmBootDescription.c @@ -1,13 +1,14 @@ /** @file - Library functions which relate with boot option description. + Library functions which relate to boot option description. Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
(C) Copyright 2015 Hewlett Packard Enterprise Development LP
+Copyright (C) 2024, Mike Beaton. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ -#include "InternalBm.h" +#include "NetworkBootInternal.h" /** Return the description for network boot device. @@ -141,18 +142,18 @@ BmGetNetworkDescription ( // // Build description like below: - // "PXEv6 (MAC:112233445566 VLAN1)" - // "HTTPv4 (MAC:112233445566)" + // "PXE Boot IPv6 (MAC:11-22-33-44-55-66 VLAN1)" + // "HTTP Boot IPv4 (MAC:11-22-33-44-55-66)" // - DescriptionSize = sizeof (L"HTTPv6 (MAC:112233445566 VLAN65535)"); + DescriptionSize = sizeof (L"HTTP Boot IPv6 (MAC:11-22-33-44-55-66 VLAN65535)"); Description = AllocatePool (DescriptionSize); ASSERT (Description != NULL); UnicodeSPrint ( Description, DescriptionSize, (Vlan == NULL) ? - L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x)" : - L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x VLAN%d)", + L"%s Boot IPv%d (MAC:%02x-%02x-%02x-%02x-%02x-%02x)" : + L"%s Boot IPv%d (MAC:%02x-%02x-%02x-%02x-%02x-%02x VLAN%d)", (Uri == NULL) ? L"PXE" : L"HTTP", ((Ip == NULL) || (DevicePathSubType (Ip) == MSG_IPv4_DP)) ? 4 : 6, Mac->MacAddress.Addr[0], diff --git a/Platform/OpenNetworkBoot/HttpBootCallback.c b/Platform/OpenNetworkBoot/HttpBootCallback.c new file mode 100644 index 00000000000..145f60e8323 --- /dev/null +++ b/Platform/OpenNetworkBoot/HttpBootCallback.c @@ -0,0 +1,286 @@ +/** @file + Callback handler for HTTP Boot. + + Copyright (c) 2024, Mike Beaton. All rights reserved.
+ SPDX-License-Identifier: BSD-3-Clause +**/ + +#include "NetworkBootInternal.h" + +#include +#include +#include + +#define HTTP_CONTENT_TYPE_APP_EFI "application/efi" + +OC_DMG_LOADING_SUPPORT gDmgLoading; + +STATIC EFI_HTTP_BOOT_CALLBACK mOriginalHttpBootCallback; +STATIC BOOLEAN mUriValidated; + +// +// Abort if we are loading a .dmg and these are banned, or if underlying drivers have +// allowed http:// in URL but user setting for OpenNetworkBoot does not allow it. +// If PcdAllowHttpConnections was not set (via NETWORK_ALLOW_HTTP_CONNECTIONS compilation +// flag) then both HttpDxe and HttpBootDxe will enforce https:// before we get to here. +// +EFI_STATUS +ValidateDmgAndHttps ( + CHAR16 *Uri, + BOOLEAN ShowLog, + BOOLEAN *HasDmgExtension + ) +{ + CHAR8 *Match; + CHAR8 *Uri8; + UINTN UriSize; + + if (gRequireHttpsUri && !HasHttpsUri (Uri)) { + // + // Do not return ACCESS_DENIED as this will attempt to add authentication to the request. + // + if (ShowLog) { + DEBUG ((DEBUG_INFO, "NTBT: Invalid URI https:// is required\n")); + } + + return EFI_UNSUPPORTED; + } + + UriSize = StrSize (Uri); + Uri8 = AllocatePool (UriSize); + if (Uri8 == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UnicodeStrToAsciiStrS (Uri, Uri8, UriSize); + + Match = ".dmg"; + *HasDmgExtension = UriFileHasExtension (Uri8, Match); + if (!*HasDmgExtension) { + Match = ".chunklist"; + *HasDmgExtension = UriFileHasExtension (Uri8, Match); + } + + FreePool (Uri8); + + if (gDmgLoading == OcDmgLoadingDisabled) { + if (*HasDmgExtension) { + if (ShowLog) { + DEBUG ((DEBUG_INFO, "NTBT: %a file is requested while DMG loading is disabled\n", Match)); + } + + return EFI_UNSUPPORTED; + } + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +EFIAPI +OpenNetworkBootHttpBootCallback ( + IN EFI_HTTP_BOOT_CALLBACK_PROTOCOL *This, + IN EFI_HTTP_BOOT_CALLBACK_DATA_TYPE DataType, + IN BOOLEAN Received, + IN UINT32 DataLength, + IN VOID *Data OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HTTP_MESSAGE *HttpMessage; + EFI_HTTP_HEADER *Header; + STATIC BOOLEAN HasDmgExtension = FALSE; + + switch (DataType) { + // + // Abort if http:// is specified but only https:// is allowed. + // Since the URI can come from DHCP boot options this is important for security. + // This will fail to be enforced if this callback doesn't get registered, or + // isn't used by a given implementation of HTTP Boot (it is used by ours, ofc). + // Therefore if a file is returned from HTTP Boot, we check that this + // was really hit, and won't load it otherwise. (Which is less ideal than + // not even fetching the file, as will happen when this callback is hit.) + // + // Also abort early if .dmg or .chunklist is found when DmgLoading is disabled. + // This is a convenience, we could allow these to load and they would be + // rejected eventually anyway. + // + case HttpBootHttpRequest: + if (Data != NULL) { + HttpMessage = (EFI_HTTP_MESSAGE *)Data; + if (HttpMessage->Data.Request->Url != NULL) { + // + // Print log messages once on initial access with HTTP HEAD, don't + // log on subsequent GET which is an attempt to get file size by + // pre-loading entire file for case of chunked encoding (where file + // size is not known until it has been transferred). + // + Status = ValidateDmgAndHttps ( + HttpMessage->Data.Request->Url, + HttpMessage->Data.Request->Method == HttpMethodHead, + &HasDmgExtension + ); + if (EFI_ERROR (Status)) { + return Status; + } + + mUriValidated = TRUE; + } + } + + break; + + // + // Provide fake MIME type of 'application/efi' for .dmg and .chunklist. + // This is also a convenience of sorts, in that as long as the user + // sets 'application/efi' MIME type for these files on their web server, + // they would work anyway. + // + case HttpBootHttpResponse: + if ((Data != NULL) && HasDmgExtension) { + // + // We do not need to keep modifying Content-Type for subsequent packets. + // + HasDmgExtension = FALSE; + + HttpMessage = (EFI_HTTP_MESSAGE *)Data; + Header = HttpFindHeader (HttpMessage->HeaderCount, HttpMessage->Headers, HTTP_HEADER_CONTENT_TYPE); + + if (Header == NULL) { + Header = HttpMessage->Headers; + ++HttpMessage->HeaderCount; + HttpMessage->Headers = AllocatePool (HttpMessage->HeaderCount * sizeof (HttpMessage->Headers[0])); + if (HttpMessage->Headers == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (HttpMessage->Headers, Header, HttpMessage->HeaderCount * sizeof (HttpMessage->Headers[0])); + Header = &HttpMessage->Headers[HttpMessage->HeaderCount - 1]; + Header->FieldValue = NULL; + Header->FieldName = AllocateCopyPool (L_STR_SIZE (HTTP_HEADER_CONTENT_TYPE), HTTP_HEADER_CONTENT_TYPE); + if (Header->FieldName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } else { + ASSERT (Header->FieldValue != NULL); + if (AsciiStrCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_EFI) != 0) { + FreePool (Header->FieldValue); + Header->FieldValue = NULL; + } + } + + if (Header->FieldValue == NULL) { + Header->FieldValue = AllocateCopyPool (L_STR_SIZE (HTTP_CONTENT_TYPE_APP_EFI), HTTP_CONTENT_TYPE_APP_EFI); + if (Header->FieldValue == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + } + + break; + + default: + break; + } + + return mOriginalHttpBootCallback ( + This, + DataType, + Received, + DataLength, + Data + ); +} + +STATIC +VOID +EFIAPI +NotifyInstallHttpBootCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_HANDLE LoadFileHandle; + EFI_HTTP_BOOT_CALLBACK_PROTOCOL *HttpBootCallback; + + LoadFileHandle = Context; + + Status = gBS->HandleProtocol ( + LoadFileHandle, + &gEfiHttpBootCallbackProtocolGuid, + (VOID **)&HttpBootCallback + ); + + if (!EFI_ERROR (Status)) { + // + // Due to a bug in EDK 2 HttpBootUninstallCallback, they do not uninstall + // this protocol when they try to. This is okay as long as there is only + // one consumer of the protocol, because our hooked version stays installed + // and gets reused (found as the already installed protocol) on second + // and subsequent tries in HttpBootInstallCallback. + // REF: https://edk2.groups.io/g/devel/message/117469 + // TODO: Add edk2 bugzilla issue. + // TODO: To safely allow for more than one consumer while allowing for + // their bug, we would need to store multiple original callbacks, one + // per http boot callback protocol address. (Otherwise using consumer + // A then consumer B, so that we are already installed on both handles, + // then consumer A again, will use the original callback for B.) + // + mOriginalHttpBootCallback = HttpBootCallback->Callback; + HttpBootCallback->Callback = OpenNetworkBootHttpBootCallback; + } +} + +BOOLEAN +UriWasValidated ( + VOID + ) +{ + return mUriValidated; +} + +EFI_EVENT +MonitorHttpBootCallback ( + EFI_HANDLE LoadFileHandle + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + VOID *Registration; + + // + // Everything in our callback except https validation is convenient but optional. + // So we can make our driver fully usable with some (hypothetical?) http boot + // implementation which never hits our callback, as long as we treat the URI as + // already validated (even if the callback is never hit) when https validation + // is not turned on. + // + mUriValidated = !gRequireHttpsUri; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + NotifyInstallHttpBootCallback, + LoadFileHandle, + &Event + ); + + if (EFI_ERROR (Status)) { + return NULL; + } + + Status = gBS->RegisterProtocolNotify ( + &gEfiHttpBootCallbackProtocolGuid, + Event, + &Registration + ); + + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Event); + return NULL; + } + + return Event; +} diff --git a/Platform/OpenNetworkBoot/HttpBootCustomRead.c b/Platform/OpenNetworkBoot/HttpBootCustomRead.c new file mode 100644 index 00000000000..fa6875da1be --- /dev/null +++ b/Platform/OpenNetworkBoot/HttpBootCustomRead.c @@ -0,0 +1,298 @@ +/** @file + Top level LoadFile protocol handler for HTTP Boot. + + Copyright (c) 2024, Mike Beaton. All rights reserved.
+ SPDX-License-Identifier: BSD-3-Clause +**/ + +#include "NetworkBootInternal.h" + +#include +#include + +typedef struct { + EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath; +} CUSTOM_FREE_CONTEXT; + +STATIC +EFI_STATUS +SetDmgPreloadDmgFile ( + IN OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext, + IN OUT VOID **Data, + IN OUT UINT32 *DataSize + ) +{ + EFI_STATUS Status; + + Status = CreateVirtualFileFileNameCopy (L"__HTTPBoot__.dmg", *Data, *DataSize, NULL, &DmgPreloadContext->DmgFile); + + if (EFI_ERROR (Status)) { + FreePool (Data); + } else { + DmgPreloadContext->DmgFileSize = *DataSize; + } + + *Data = NULL; + *DataSize = 0; + + return Status; +} + +STATIC +EFI_STATUS +SetDmgPreloadChunklist ( + IN OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext, + IN OUT VOID **Data, + IN OUT UINT32 *DataSize + ) +{ + DmgPreloadContext->ChunklistBuffer = *Data; + DmgPreloadContext->ChunklistFileSize = *DataSize; + + *Data = NULL; + *DataSize = 0; + + return EFI_SUCCESS; +} + +STATIC +VOID +FreeDmgPreloadContext ( + IN OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext + ) +{ + if (DmgPreloadContext->ChunklistBuffer != NULL) { + FreePool (DmgPreloadContext->ChunklistBuffer); + } + + DmgPreloadContext->ChunklistFileSize = 0; + if (DmgPreloadContext->DmgFile != NULL) { + DmgPreloadContext->DmgFile->Close (DmgPreloadContext->DmgFile); + DmgPreloadContext->DmgFile = NULL; + } + + DmgPreloadContext->DmgFileSize = 0; +} + +// +// Equivalent to lines in original BmBoot.c which free RAM disk unconditionally +// when image loaded from RAM disk exits: +// https://github.com/tianocore/edk2/blob/a6648418c1600f0a81f2914d9dd14de1adbfe598/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c#L2061-L2071 +// +EFI_STATUS +EFIAPI +HttpBootCustomFree ( + IN VOID *Context + ) +{ + CUSTOM_FREE_CONTEXT *CustomFreeContext; + + if (Context != NULL) { + CustomFreeContext = Context; + if (CustomFreeContext->RamDiskDevicePath != NULL) { + BmDestroyRamDisk (CustomFreeContext->RamDiskDevicePath); + FreePool (CustomFreeContext->RamDiskDevicePath); + } + + FreePool (CustomFreeContext); + } + + return EFI_SUCCESS; +} + +// +// Within BmExpandLoadFiles: +// - Only DevicePath will be set if we're returning a boot file on an HTTP +// Boot native ram disk (from .iso or .img). In this case the first and +// second calls to LoadFile occur inside this method. +// - The boot file is then loaded from the RAM disk (via the returned +// device path) in a subsequent call to gBS->LoadImage made by the +// caller. +// - The above applies in the orginal and our modified method. +// - Our method is modified from EDK-II original so that the second LoadFile +// call will also be made inside the method, and Data and DataSize will be +// filled in, if a single .efi file is loaded. +// - In the EDK-II original, final loading of .efi files is delayed to the +// subsequent call to GetFileBufferByFilePath in BmGetLoadOptionBuffer. +// - Note that in the HttpBootDxe LoadFile implementation (which will be +// used by the original and our modified code), if HTTP chunked transfer +// encoding is used then the entire file is downloaded (chunked HTTP GET) +// and cached (in a linked list of fixed-sized download sections, not +// corresponding in size to the actual HTTP chunks) in order to get its +// size, before the final buffer for the file can be allocated; then within +// the second LoadFile call the file is transferred from this cache into +// the final allocated buffer. +// - For non-chunked (so, more or less, normal) transfer encoding, the +// file size is available from a simple HTTP HEAD request, then in the +// second LoadFile call the file HTTP GET is written directly into +// allocated buffer. +// - So chunked transfer encoding should ideally be avoided, especially +// for large downloads, but is supported here and in the original. +// +EFI_STATUS +EFIAPI +HttpBootCustomRead ( + IN OC_STORAGE_CONTEXT *Storage, + IN OC_BOOT_ENTRY *ChosenEntry, + OUT VOID **Data, + OUT UINT32 *DataSize, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, + OUT EFI_HANDLE *StorageHandle, + OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath, + IN OC_DMG_LOADING_SUPPORT DmgLoading, + OUT OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext, + OUT VOID **Context + ) +{ + EFI_STATUS Status; + CUSTOM_FREE_CONTEXT *CustomFreeContext; + CHAR8 *OtherUri; + BOOLEAN GotDmgFirst; + EFI_DEVICE_PATH_PROTOCOL *OtherLoadFile; + EFI_DEVICE_PATH_PROTOCOL *OtherDevicePath; + + ASSERT (Context != NULL); + *Context = NULL; + + CustomFreeContext = AllocateZeroPool (sizeof (CUSTOM_FREE_CONTEXT)); + if (CustomFreeContext == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + gDmgLoading = DmgLoading; + + OcConsoleControlSetMode (EfiConsoleControlScreenText); + + // + // Load the first (or only) file. This method has been extended to + // abort early (avoiding a pointless, long, slow load of a DMG) if DmgLoading + // is disabled and the requested file extension is `.dmg` (or `.chunklist`). + // + *DevicePath = BmExpandLoadFiles (ChosenEntry->DevicePath, Data, DataSize, TRUE); + + if (*DevicePath == NULL) { + FreePool (CustomFreeContext); + return EFI_NOT_FOUND; + } + + Status = EFI_SUCCESS; + GotDmgFirst = FALSE; + OtherUri = NULL; + + // + // Only potentially treat first file as .dmg/.chunklist if it was loaded as + // a normal single file. HttpBootDxe Content-Type header handling may force + // any file, regardless of extension, to be treated as an .iso or .img and + // loaded as a RAM disk. + // + if (*DataSize != 0) { + Status = ExtractOtherUriFromDevicePath (*DevicePath, ".dmg", ".chunklist", &OtherUri, FALSE); + if (!EFI_ERROR (Status)) { + GotDmgFirst = TRUE; + Status = SetDmgPreloadDmgFile (DmgPreloadContext, Data, DataSize); + if (EFI_ERROR (Status)) { + FreePool (OtherUri); + OtherUri = NULL; + } + } else { + Status = ExtractOtherUriFromDevicePath (*DevicePath, ".chunklist", ".dmg", &OtherUri, FALSE); + if (!EFI_ERROR (Status)) { + Status = SetDmgPreloadChunklist (DmgPreloadContext, Data, DataSize); + if (EFI_ERROR (Status)) { + FreePool (OtherUri); + OtherUri = NULL; + } + } else if (Status == EFI_NOT_FOUND) { + Status = EFI_SUCCESS; + OtherUri = NULL; + } + } + } + + // + // Is there a potential matched file? + // + if (OtherUri != NULL) { + // + // Always require .dmg if .chunklist was fetched first; only fetch (and + // require) .chunklist after .dmg when it will be used. + // + if (!GotDmgFirst || (DmgLoading == OcDmgLoadingAppleSigned)) { + Status = HttpBootAddUri (ChosenEntry->DevicePath, OtherUri, OcStringFormatAscii, &OtherLoadFile); + if (!EFI_ERROR (Status)) { + // + // Sort out cramped spacing between the two HTTP Boot calls. + // Hopefully should not affect GUI-based firmware. + // + Print (L"\n"); + + // + // Load the second file of .dmg/.chunklist pair. + // + OtherDevicePath = BmExpandLoadFiles (OtherLoadFile, Data, DataSize, TRUE); + FreePool (OtherLoadFile); + if (OtherDevicePath == NULL) { + DEBUG ((DEBUG_INFO, "NTBT: Failed to fetch required matching file %a\r", OtherUri)); + Status = EFI_NOT_FOUND; + } else { + if (GotDmgFirst) { + FreePool (OtherDevicePath); + Status = SetDmgPreloadChunklist (DmgPreloadContext, Data, DataSize); + } else { + FreePool (*DevicePath); + *DevicePath = OtherDevicePath; + Status = SetDmgPreloadDmgFile (DmgPreloadContext, Data, DataSize); + } + } + } + } + + FreePool (OtherUri); + } + + // + // Sort out OC debug messages following HTTP Boot progress message on the same line, after completion. + // + Print (L"\n"); + + if (EFI_ERROR (Status)) { + FreeDmgPreloadContext (DmgPreloadContext); + FreePool (CustomFreeContext); + FreePool (*DevicePath); + *DevicePath = NULL; + return Status; + } + + // + // It is okay to follow EDK-II code and check for this when it might not be there. + // + CustomFreeContext->RamDiskDevicePath = BmGetRamDiskDevicePath (*DevicePath); + *Context = CustomFreeContext; + + return EFI_SUCCESS; +} + +// +// There is no possibility of DMGs, chunklists or ISOs with PXE boot. +// +EFI_STATUS +EFIAPI +PxeBootCustomRead ( + IN OC_STORAGE_CONTEXT *Storage, + IN OC_BOOT_ENTRY *ChosenEntry, + OUT VOID **Data, + OUT UINT32 *DataSize, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, + OUT EFI_HANDLE *StorageHandle, + OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath, + IN OC_DMG_LOADING_SUPPORT DmgLoading, + OUT OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext, + OUT VOID **Context + ) +{ + OcConsoleControlSetMode (EfiConsoleControlScreenText); + + *DevicePath = BmExpandLoadFiles (ChosenEntry->DevicePath, Data, DataSize, FALSE); + + return (*DevicePath == NULL ? EFI_NOT_FOUND : EFI_SUCCESS); +} diff --git a/Platform/OpenNetworkBoot/NetworkBootInternal.h b/Platform/OpenNetworkBoot/NetworkBootInternal.h new file mode 100644 index 00000000000..bf51cf17bec --- /dev/null +++ b/Platform/OpenNetworkBoot/NetworkBootInternal.h @@ -0,0 +1,217 @@ +/** @file + Copyright (C) 2024, Mike Beaton. All rights reserved.
+ SPDX-License-Identifier: BSD-3-Clause +**/ + +#ifndef LOAD_FILE_INTERNAL_H +#define LOAD_FILE_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + Set if we should enforce https only within this driver. +**/ +extern BOOLEAN gRequireHttpsUri; + +/** + Current DmgLoading setting, for HTTP BOOT callback validation. +**/ +extern OC_DMG_LOADING_SUPPORT gDmgLoading; + +/** + Custom validation for network boot device path. + + @param Path Device path to validate. + + @retval EFI_SUCCESS Device path should be accepted. + @retval other Device path should be rejected. +**/ +typedef +EFI_STATUS +(*VALIDATE_BOOT_DEVICE_PATH)( + IN VOID *Context, + IN EFI_DEVICE_PATH_PROTOCOL *Path + ); + +/* + Return pointer to final node in device path, if it as a URI node. + Used to return the URI node for an HTTP Boot device path. + + @return Required device path node if available, NULL otherwise. +*/ +EFI_DEVICE_PATH_PROTOCOL * +GetUriNode ( + EFI_DEVICE_PATH_PROTOCOL *DevicePath + ); + +/// +/// BmBootDescription.c +/// + +CHAR16 * +BmGetNetworkDescription ( + IN EFI_HANDLE Handle + ); + +/// +/// BmBoot.c +/// + +EFI_DEVICE_PATH_PROTOCOL * +BmExpandLoadFiles ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + OUT VOID **Data, + OUT UINT32 *DataSize, + IN BOOLEAN ValidateHttp + ); + +EFI_DEVICE_PATH_PROTOCOL * +BmGetRamDiskDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath + ); + +VOID +BmDestroyRamDisk ( + IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath + ); + +/// +/// CustomRead.c +/// + +EFI_STATUS +EFIAPI +HttpBootCustomFree ( + IN VOID *Context + ); + +EFI_STATUS +EFIAPI +HttpBootCustomRead ( + IN OC_STORAGE_CONTEXT *Storage, + IN OC_BOOT_ENTRY *ChosenEntry, + OUT VOID **Data, + OUT UINT32 *DataSize, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, + OUT EFI_HANDLE *StorageHandle, + OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath, + IN OC_DMG_LOADING_SUPPORT DmgLoading, + OUT OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext, + OUT VOID **Context + ); + +EFI_STATUS +EFIAPI +PxeBootCustomRead ( + IN OC_STORAGE_CONTEXT *Storage, + IN OC_BOOT_ENTRY *ChosenEntry, + OUT VOID **Data, + OUT UINT32 *DataSize, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, + OUT EFI_HANDLE *StorageHandle, + OUT EFI_DEVICE_PATH_PROTOCOL **StoragePath, + IN OC_DMG_LOADING_SUPPORT DmgLoading, + OUT OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT *DmgPreloadContext, + OUT VOID **Context + ); + +/// +/// Uri.c +/// + +BOOLEAN +HasHttpsUri ( + CHAR16 *Uri + ); + +EFI_STATUS +ExtractOtherUriFromDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN CHAR8 *FromExt, + IN CHAR8 *ToExt, + OUT CHAR8 **OtherUri, + IN BOOLEAN OnlySearchForFromExt + ); + +BOOLEAN +UriFileHasExtension ( + IN CHAR8 *Uri, + IN CHAR8 *Ext + ); + +EFI_STATUS +HttpBootAddUri ( + EFI_DEVICE_PATH_PROTOCOL *DevicePath, + VOID *Uri, + OC_STRING_FORMAT StringFormat, + EFI_DEVICE_PATH_PROTOCOL **UriDevicePath + ); + +EFI_EVENT +MonitorHttpBootCallback ( + EFI_HANDLE LoadFileHandle + ); + +BOOLEAN +UriWasValidated ( + VOID + ); + +/// +/// TlsAuthConfigImpl.c +/// + +EFI_STATUS +LogInstalledCerts ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ); + +EFI_STATUS +CertIsPresent ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN EFI_GUID *OwnerGuid, + IN UINTN X509DataSize, + IN VOID *X509Data + ); + +EFI_STATUS +DeleteCertsForOwner ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN EFI_GUID *OwnerGuid, + IN UINTN X509DataSize, + IN VOID *X509Data, + OUT UINTN *DeletedCount + ); + +EFI_STATUS +EnrollX509toVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN EFI_GUID *OwnerGuid, + IN UINTN X509DataSize, + IN VOID *X509Data + ); + +#endif // LOAD_FILE_INTERNAL_H diff --git a/Platform/OpenNetworkBoot/OpenNetworkBoot.c b/Platform/OpenNetworkBoot/OpenNetworkBoot.c new file mode 100644 index 00000000000..0b90a83d3b8 --- /dev/null +++ b/Platform/OpenNetworkBoot/OpenNetworkBoot.c @@ -0,0 +1,535 @@ +/** @file + Boot entry protocol handler for PXE and HTTP Boot. + + Copyright (c) 2024, Mike Beaton. All rights reserved.
+ SPDX-License-Identifier: BSD-3-Clause +**/ + +#include "NetworkBootInternal.h" + +#define ENROLL_CERT L"--enroll-cert" +#define DELETE_CERT L"--delete-cert" +#define DELETE_ALL_CERTS L"--delete-all-certs" + +BOOLEAN gRequireHttpsUri; + +STATIC BOOLEAN mAllowPxeBoot; +STATIC BOOLEAN mAllowHttpBoot; +STATIC BOOLEAN mAllowIpv4; +STATIC BOOLEAN mAllowIpv6; +STATIC BOOLEAN mAuxEntries; +STATIC CHAR16 *mHttpBootUri; + +STATIC CHAR16 PxeBootId[] = L"PXE Boot IPv"; +STATIC CHAR16 HttpBootId[] = L"HTTP Boot IPv"; + +VOID +InternalFreePickerEntry ( + IN OC_PICKER_ENTRY *Entry + ) +{ + ASSERT (Entry != NULL); + + if (Entry == NULL) { + return; + } + + if (Entry->Id != NULL) { + FreePool ((CHAR8 *)Entry->Id); + } + + if (Entry->Name != NULL) { + FreePool ((CHAR8 *)Entry->Name); + } + + if (Entry->Path != NULL) { + FreePool ((CHAR8 *)Entry->Path); + } + + if (Entry->Arguments != NULL) { + FreePool ((CHAR8 *)Entry->Arguments); + } + + if (Entry->UnmanagedDevicePath != NULL) { + FreePool (Entry->UnmanagedDevicePath); + } +} + +STATIC +VOID +EFIAPI +FreeNetworkBootEntries ( + IN OC_PICKER_ENTRY **Entries, + IN UINTN NumEntries + ) +{ + UINTN Index; + + ASSERT (Entries != NULL); + ASSERT (*Entries != NULL); + if ((Entries == NULL) || (*Entries == NULL)) { + return; + } + + for (Index = 0; Index < NumEntries; Index++) { + InternalFreePickerEntry (&(*Entries)[Index]); + } + + FreePool (*Entries); + *Entries = NULL; +} + +STATIC +EFI_STATUS +InternalAddEntry ( + OC_FLEX_ARRAY *FlexPickerEntries, + CHAR16 *Description, + EFI_HANDLE Handle, + CHAR16 *HttpBootUri, + BOOLEAN IsIPv4, + BOOLEAN IsHttpBoot + ) +{ + EFI_STATUS Status; + OC_PICKER_ENTRY *PickerEntry; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + UINTN IdLen; + + Status = gBS->HandleProtocol ( + Handle, + &gEfiDevicePathProtocolGuid, + (VOID **)&DevicePath + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "NTBT: Missing device path - %r\n", + Status + )); + return Status; + } + + PickerEntry = OcFlexArrayAddItem (FlexPickerEntries); + if (PickerEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + IdLen = StrLen (Description); + PickerEntry->Id = AllocatePool ((IdLen + 1) * sizeof (PickerEntry->Id[0])); + if (PickerEntry->Id == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UnicodeStrToAsciiStrS (Description, (CHAR8 *)PickerEntry->Id, IdLen + 1); + + PickerEntry->Name = AllocateCopyPool (IdLen + 1, PickerEntry->Id); + if (PickerEntry->Name == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (IsHttpBoot && (HttpBootUri != NULL)) { + Status = HttpBootAddUri (DevicePath, HttpBootUri, OcStringFormatUnicode, &NewDevicePath); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + NewDevicePath = DuplicateDevicePath (DevicePath); + if (NewDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + PickerEntry->UnmanagedDevicePath = NewDevicePath; + + if (IsHttpBoot) { + PickerEntry->CustomRead = HttpBootCustomRead; + PickerEntry->CustomFree = HttpBootCustomFree; + PickerEntry->Flavour = IsIPv4 ? OC_FLAVOUR_HTTP_BOOT4 : OC_FLAVOUR_HTTP_BOOT6; + } else { + PickerEntry->CustomRead = PxeBootCustomRead; + PickerEntry->Flavour = IsIPv4 ? OC_FLAVOUR_PXE_BOOT4 : OC_FLAVOUR_PXE_BOOT6; + } + + PickerEntry->TextMode = TRUE; + PickerEntry->Auxiliary = mAuxEntries; + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +EFIAPI +GetNetworkBootEntries ( + IN OUT OC_PICKER_CONTEXT *PickerContext, + IN CONST EFI_HANDLE Device OPTIONAL, + OUT OC_PICKER_ENTRY **Entries, + OUT UINTN *NumEntries + ) +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN Index; + CHAR16 *NetworkDescription; + CHAR16 *IdStr; + OC_FLEX_ARRAY *FlexPickerEntries; + BOOLEAN IsIPv4; + BOOLEAN IsHttpBoot; + + // + // Here we produce custom entries only, not entries found on filesystems. + // + if (Device != NULL) { + return EFI_NOT_FOUND; + } + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiLoadFileProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "NTBT: Load file protocol - %r\n", Status)); + return Status; + } + + FlexPickerEntries = OcFlexArrayInit (sizeof (OC_PICKER_ENTRY), (OC_FLEX_ARRAY_FREE_ITEM)InternalFreePickerEntry); + if (FlexPickerEntries == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (Index = 0; Index < HandleCount; ++Index) { + NetworkDescription = BmGetNetworkDescription (HandleBuffer[Index]); + if (NetworkDescription == NULL) { + DebugPrintDevicePathForHandle (DEBUG_INFO, "NTBT: LoadFile handle not PXE/HTTP boot DP", HandleBuffer[Index]); + } else { + // + // Use fixed format network description which we control as shortcut + // to identify PXE/HTTP and IPv4/6. + // + if ((IdStr = StrStr (NetworkDescription, PxeBootId)) != NULL) { + IsIPv4 = IdStr[L_STR_LEN (PxeBootId)] == L'4'; + ASSERT (IsIPv4 || (IdStr[L_STR_LEN (PxeBootId)] == L'6')); + IsHttpBoot = FALSE; + } else if ((IdStr = StrStr (NetworkDescription, HttpBootId)) != NULL) { + IsIPv4 = IdStr[L_STR_LEN (HttpBootId)] == L'4'; + ASSERT (IsIPv4 || (IdStr[L_STR_LEN (HttpBootId)] == L'6')); + IsHttpBoot = TRUE; + } + + if ( (IdStr != NULL) + && ((IsIPv4 && mAllowIpv4) || (!IsIPv4 && mAllowIpv6)) + && ((IsHttpBoot && mAllowHttpBoot) || (!IsHttpBoot && mAllowPxeBoot)) + ) + { + DEBUG ((DEBUG_INFO, "NTBT: Adding %s\n", NetworkDescription)); + Status = InternalAddEntry ( + FlexPickerEntries, + NetworkDescription, + HandleBuffer[Index], + IsHttpBoot ? mHttpBootUri : NULL, + IsIPv4, + IsHttpBoot + ); + } else { + DEBUG ((DEBUG_INFO, "NTBT: Ignoring %s\n", NetworkDescription)); + } + + FreePool (NetworkDescription); + } + + if (EFI_ERROR (Status)) { + break; + } + } + + FreePool (HandleBuffer); + + if (EFI_ERROR (Status)) { + OcFlexArrayFree (&FlexPickerEntries); + return Status; + } + + OcFlexArrayFreeContainer (&FlexPickerEntries, (VOID **)Entries, NumEntries); + + if (*NumEntries == 0) { + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EnrollCerts ( + OC_FLEX_ARRAY *ParsedLoadOptions + ) +{ + EFI_STATUS Status; + UINTN Index; + OC_PARSED_VAR *Option; + EFI_GUID *OwnerGuid; + UINTN CertSize; + CHAR8 *CertData; + BOOLEAN EnrollCert; + BOOLEAN DeleteCert; + BOOLEAN DeleteAllCerts; + UINTN OptionLen; + UINTN DeletedCount; + + Status = EFI_SUCCESS; + + // + // Find certs in options. + // + for (Index = 0; Index < ParsedLoadOptions->Count; ++Index) { + Option = OcFlexArrayItemAt (ParsedLoadOptions, Index); + + EnrollCert = FALSE; + DeleteCert = FALSE; + DeleteAllCerts = FALSE; + + if (OcUnicodeStartsWith (Option->Unicode.Name, ENROLL_CERT, TRUE)) { + EnrollCert = TRUE; + OptionLen = L_STR_LEN (ENROLL_CERT); + } else if (OcUnicodeStartsWith (Option->Unicode.Name, DELETE_CERT, TRUE)) { + DeleteCert = TRUE; + OptionLen = L_STR_LEN (DELETE_CERT); + } else if (OcUnicodeStartsWith (Option->Unicode.Name, DELETE_ALL_CERTS, TRUE)) { + DeleteAllCerts = TRUE; + OptionLen = L_STR_LEN (DELETE_ALL_CERTS); + } + + if ( (EnrollCert || DeleteCert || DeleteAllCerts) + && (Option->Unicode.Name[OptionLen] != CHAR_NULL) + && (Option->Unicode.Name[OptionLen] != L':') + ) + { + EnrollCert = FALSE; + DeleteCert = FALSE; + DeleteAllCerts = FALSE; + } + + if ((EnrollCert || DeleteCert) && (Option->Unicode.Value == NULL)) { + DEBUG ((DEBUG_INFO, "NTBT: Ignoring %s option with no cert value\n", Option->Unicode.Name)); + EnrollCert = FALSE; + DeleteCert = FALSE; + } + + if (EnrollCert || DeleteCert || DeleteAllCerts) { + OwnerGuid = AllocateZeroPool (sizeof (EFI_GUID)); + if (OwnerGuid == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + // + // Use all zeros GUID if no user value supplied. + // + if (Option->Unicode.Name[OptionLen] == L':') { + Status = StrToGuid (&Option->Unicode.Name[OptionLen + 1], OwnerGuid); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "NTBT: Cannot parse cert owner GUID from %s - %r\n", Option->Unicode.Name, Status)); + break; + } + } + + if (DeleteAllCerts) { + Status = DeleteCertsForOwner ( + EFI_TLS_CA_CERTIFICATE_VARIABLE, + &gEfiTlsCaCertificateGuid, + OwnerGuid, + 0, + NULL, + &DeletedCount + ); + DEBUG ((DEBUG_INFO, "NTBT: %s %u deleted - %r\n", Option->Unicode.Name, DeletedCount, Status)); + } else { + // + // We do not include the terminating '\0' in the stored certificate, + // which matches how stored by e.g. OVMF when loaded from file; + // but we must allocate space for '\0' for Unicode to ASCII conversion. + // + CertSize = StrLen (Option->Unicode.Value); + CertData = AllocateZeroPool (CertSize + 1); + if (CertData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + UnicodeStrToAsciiStrS (Option->Unicode.Value, CertData, CertSize + 1); + + if (DeleteCert) { + Status = DeleteCertsForOwner ( + EFI_TLS_CA_CERTIFICATE_VARIABLE, + &gEfiTlsCaCertificateGuid, + OwnerGuid, + CertSize, + CertData, + &DeletedCount + ); + DEBUG ((DEBUG_INFO, "NTBT: %s %u deleted - %r\n", Option->Unicode.Name, DeletedCount, Status)); + } else { + Status = CertIsPresent ( + EFI_TLS_CA_CERTIFICATE_VARIABLE, + &gEfiTlsCaCertificateGuid, + OwnerGuid, + CertSize, + CertData + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_ALREADY_STARTED) { + DEBUG ((DEBUG_INFO, "NTBT: %s already present\n", Option->Unicode.Name)); + Status = EFI_SUCCESS; + } else { + DEBUG ((DEBUG_INFO, "NTBT: Error checking for cert presence - %r\n", Status)); + } + } else { + Status = EnrollX509toVariable ( + EFI_TLS_CA_CERTIFICATE_VARIABLE, + &gEfiTlsCaCertificateGuid, + OwnerGuid, + CertSize, + CertData + ); + DEBUG ((DEBUG_INFO, "NTBT: %s - %r\n", Option->Unicode.Name, Status)); + } + } + + FreePool (CertData); + } + + FreePool (OwnerGuid); + + if (EFI_ERROR (Status)) { + break; + } + } + } + + return Status; +} + +STATIC +OC_BOOT_ENTRY_PROTOCOL + mNetworkBootEntryProtocol = { + OC_BOOT_ENTRY_PROTOCOL_REVISION, + GetNetworkBootEntries, + FreeNetworkBootEntries, + NULL +}; + +EFI_STATUS +EFIAPI +UefiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + OC_FLEX_ARRAY *ParsedLoadOptions; + CHAR16 *TempUri; + + Status = gBS->HandleProtocol ( + ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); + if (EFI_ERROR (Status)) { + return Status; + } + + mAllowIpv4 = FALSE; + mAllowIpv6 = FALSE; + mAllowPxeBoot = FALSE; + mAllowHttpBoot = FALSE; + gRequireHttpsUri = FALSE; + mHttpBootUri = NULL; + + Status = OcParseLoadOptions (LoadedImage, &ParsedLoadOptions); + if (EFI_ERROR (Status)) { + if (Status != EFI_NOT_FOUND) { + return Status; + } + + Status = EFI_SUCCESS; + } else { + // + // e.g. --https --uri=https://imageserver.org/OpenShell.efi + // + mAllowIpv4 = OcHasParsedVar (ParsedLoadOptions, L"-4", OcStringFormatUnicode); + mAllowIpv6 = OcHasParsedVar (ParsedLoadOptions, L"-6", OcStringFormatUnicode); + mAllowPxeBoot = OcHasParsedVar (ParsedLoadOptions, L"--pxe", OcStringFormatUnicode); + mAllowHttpBoot = OcHasParsedVar (ParsedLoadOptions, L"--http", OcStringFormatUnicode); + mAuxEntries = OcHasParsedVar (ParsedLoadOptions, L"--aux", OcStringFormatUnicode); + gRequireHttpsUri = OcHasParsedVar (ParsedLoadOptions, L"--https", OcStringFormatUnicode); + + TempUri = NULL; + OcParsedVarsGetUnicodeStr (ParsedLoadOptions, L"--uri", &TempUri); + if (TempUri != NULL) { + mHttpBootUri = AllocateCopyPool (StrSize (TempUri), TempUri); + if (mHttpBootUri == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } + } + + if (!EFI_ERROR (Status)) { + Status = EnrollCerts (ParsedLoadOptions); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "NTBT: Failed to enroll certs - %r\n", Status)); + } + + DEBUG_CODE_BEGIN (); + LogInstalledCerts (EFI_TLS_CA_CERTIFICATE_VARIABLE, &gEfiTlsCaCertificateGuid); + DEBUG_CODE_END (); + } + } + + if (!EFI_ERROR (Status)) { + if (!mAllowIpv4 && !mAllowIpv6) { + mAllowIpv4 = TRUE; + mAllowIpv6 = TRUE; + } + + if (!gRequireHttpsUri && !mAllowHttpBoot && !mAllowPxeBoot) { + mAllowHttpBoot = TRUE; + mAllowPxeBoot = TRUE; + } + + if (gRequireHttpsUri) { + mAllowHttpBoot = TRUE; + } + + if (mHttpBootUri != NULL) { + if (!mAllowHttpBoot) { + DEBUG ((DEBUG_INFO, "NTBT: URI specified but HTTP boot is disabled\n")); + } else { + if (gRequireHttpsUri && !HasHttpsUri (mHttpBootUri)) { + DEBUG ((DEBUG_WARN, "NTBT: Invalid URI https:// is required\n")); + mAllowHttpBoot = FALSE; + } + } + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gOcBootEntryProtocolGuid, + &mNetworkBootEntryProtocol, + NULL + ); + } + + if (ParsedLoadOptions != NULL) { + OcFlexArrayFree (&ParsedLoadOptions); + } + + if (EFI_ERROR (Status) && (mHttpBootUri != NULL)) { + FreePool (mHttpBootUri); + } + + return Status; +} diff --git a/Platform/OpenNetworkBoot/OpenNetworkBoot.inf b/Platform/OpenNetworkBoot/OpenNetworkBoot.inf new file mode 100644 index 00000000000..b892e86b11f --- /dev/null +++ b/Platform/OpenNetworkBoot/OpenNetworkBoot.inf @@ -0,0 +1,54 @@ +## @file +# Boot entry protocol handler for PXE and HTTP Boot. +# +# Copyright (C) 2024, Mike Beaton. All rights reserved.
+# SPDX-License-Identifier: BSD-3-Clause +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = OpenNetworkBoot + ENTRY_POINT = UefiMain + FILE_GUID = CA2BA189-98E8-4126-ABA5-2CE5C9B4C2D8 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + +[Packages] + OpenCorePkg/OpenCorePkg.dec + MdePkg/MdePkg.dec + NetworkPkg/NetworkPkg.dec + +[LibraryClasses] + DebugLib + DevicePathLib + HttpLib + OcConsoleLib + OcBootManagementLib + OcFlexArrayLib + OcVirtualFsLib + PrintLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + UefiDriverEntryPoint + UefiLib + +[Protocols] + gEfiHttpBootCallbackProtocolGuid ## CONSUMES + gEfiLoadFileProtocolGuid ## CONSUMES + gEfiRamDiskProtocolGuid ## SOMETIMES_CONSUMES + gOcBootEntryProtocolGuid ## PRODUCES + +[Guids] + gEfiTlsCaCertificateGuid ## SOMETIMES_CONSUMES ## Variable:L"TlsCaCertificate" + gEdkiiHttpTlsCipherListGuid ## SOMETIMES_CONSUMES ## Variable:L"HttpTlsCipherList" + gEfiCertX509Guid ## SOMETIMES_CONSUMES ## GUID # Check the cert type + +[Sources] + BmBoot.c + BmBootDescription.c + HttpBootCallback.c + HttpBootCustomRead.c + NetworkBootInternal.h + OpenNetworkBoot.c + TlsAuthConfigImpl.c + Uri.c diff --git a/Platform/OpenNetworkBoot/README.md b/Platform/OpenNetworkBoot/README.md new file mode 100644 index 00000000000..be450416965 --- /dev/null +++ b/Platform/OpenNetworkBoot/README.md @@ -0,0 +1,453 @@ +# OpenNetworkBoot + +`OpenNetworkBoot` is an OpenCore boot entry protocol driver which provides +PXE and HTTP boot entries if the underlying firmware supports it, or if +the required network boot drivers have been loaded using OpenCore. Using the +additional network boot drivers provided with OpenCore, when needed, HTTP +boot should be available on most firmware even if not natively supported. + +See [OpenCore documentation](https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/Configuration.pdf) +for information on the optional configuration arguments which are available for this driver. + +> **Note**: In this file 'HTTP boot' refers to booting using either +`http://` or `https://` URIs. The additional steps to configure a certificate for +`https://` (and to lock `OpenNetworkBoot` to `https://` only, if required) +are covered below. + +## PXE Boot + +On almost all firmware, the drivers for PXE boot will already be present; +adding `OpenNetworkBoot.efi` to the OpenCore drivers should produce PXE +boot entries. + +> **Note**: On some firmware (e.g. HP) the native network boot drivers are not loaded +if the system boots directly to OpenCore and it is necessary to start +OpenCore from the firmware boot menu in order to see PXE and HTTP boot entries. +(Alternatively, it should be possible to load the network boot stack provided with +OpenCore, see end of this document.) + +## HTTP Boot + +On most recent firmware either no or only a few additional drivers are needed +for HTTP boot, as most of the required drivers are already present in firmware. + +After adding `OpenNetworkBoot`, if no HTTP boot entries are seen, +try adding just the driver `HttpBootDxe`. If this does not produce +network boot entries, try also adding `HttpDxe` and `HttpUtilitiesDxe`. +If `http://` URIs can be booted but not `https://` try adding `TlsDxe.efi`. + +If the above steps do not work, proceed to the next section to identify +which drivers are required. + +> **Note 1**: When using `https://` as opposed to `http://` URIs, one or +more certificates, as required to validate the connection, must +be configured on the network boot client. This can be done using +OpenNetworkBoot's certificate configuration options, as documented in the +[OpenCore documentation](https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/Configuration.pdf). + +> **Note 2**: In some firmware the existing `HttpBootDxe` driver may produce +options which do not work correctly (e.g. blank screen when selected, +because they are designed to work with a firmware UI which is not present +when OpenCore is running). +If so, in order to get working HTTP Boot options it may be necessary to use +the `UEFI/Unload` config setting to unload the existing `HttpBootDxe` driver +before loading the `HttpBootDxe` driver provided with OpenCore. + +> **Note 3**: In some firmware the existing `HttpDxe` (and `HttpBootDxe`) drivers +may be locked down to `https://` URIs (even if the machine +has no BIOS UI for HTTP Boot; e.g. Dell OptiPlex 3070). +This means that while the `HttpBootDxe` from OpenCore can +work with the native `HttpDxe`, it will only boot from `https://` URIs, giving a +failure message otherwise. If `http://` URIs are required, this limitation can +be worked around by using the `UEFI/Unload` config setting to unload the existing +`HttpDxe` driver before loading the `HttpDxe` driver provided with OpenCore. + +> **Note 4**: During HTTP Boot '*Error: Could not retrieve NBP file size from HTTP server*' +is a very generic error message for 'something went wrong'. +It could be that `http://` URIs are not allowed by `HttpDxe` or `HttpBootDxe`, +or that a file does not exist at the specified URI on the server, or that the certificates +(if any) stored in NVRAM could not be used to validate an `https://` URI, or any one of a number +of other similar problems. + +## Identifying missing network boot drivers + +The `dh` command in UEFI Shell (e.g. `OpenShell` provided with +OpenCore) is useful for working out which drivers are missing for network +boot. + +`dh -p LoadFile` shows available network boot entries. Handles with a device +path ending in an IPv4 or IPv6 address should indicate available PXE boot +options. Handles with a device path ending in `Uri(...)` should indicate +available HTTP boot options + +> **Note 1**: On some systems, there may be additional +`LoadFile` handles with vendor-specific device paths. These may correspond, for +instance, to GUI network boot options. These will not produce boot entries when +using OpenNetworkBoot. + +After identifying the handles for network boot entries, +the other handles just before and after these, in the full +list of handles displayed by `dh`, should correspond to the currently loaded +network boot drivers. If there are no LoadFile options, then +search in the full handle list for strings such as 'tcp', 'tls', 'http' +(normally the native network boot drivers will appear grouped together). +Examining the names printed by `dh` for these handles +and comparing them to the available network boot drivers (see Network Boot Stack +section) can be used to identify missing drivers. + +> **Note 2**: On systems with reasonably fast console text output, the `-b` +option can be used with `dh` (as with most UEFI Shell commands) to +display results one page at a time. + +> **Note 3**: For systems with slow console output, it may be more +convenient to pipe the results of `dh` to a file on a convenient file system +to examine later, within a booted OS. For example `dh > fs0:\dh_dump` or: + +``` +> fs0: +> dh > dh_dump +``` + +## Configuring a network boot server + +In standard PXE and HTTP boot, the network's normal DHCP server responds to a +client device's request for an IP address, but a separate DHCP helper service +(often running on a different machine from the DHCP server) responds to the +device's DHCP extension request to know which network boot program (NBP) to +load. It is possible (but less standard, even on enterprise networks; +and usually more complex) to configure the main DHCP server to respond to both +requests. + +**Note 1**: The NBP for HTTP boot can be configured by specifying the `--uri` +flag for `OpenNetworkBoot`. When using this option, only an HTTP server (and +certificate, for HTTPS), needs to be set up, but no DHCP helper service is +required. + +**Note 2**: No equivalent NBP path option is provided for PXE boot, since the most +standard (and recommended) set up is for the program specifying the +NBP file and the program serving it via TFTP to be one and the same. + +### PXE Boot + +In PXE boot, the NBP is loaded via TFTP, which is a slow protocol not suitable +for large files. Standard PXE boot NBPs typically load any further large files +which they need using their own network stack and not via TFTP. + +`dnsmasq`, WDS, or other options, such as FOGProject, may be used to specify +PXE boot responses. + +#### dnsmasq + +`dnsmasq` can be used to both provide the location of the PXE boot NBP file +and then serve it by TFTP. + +A basic `dnsmasq` PXE boot configuration is as follows: + +``` +# Disable DNS Server +port=0 + +# Run as network boot DHCP proxy +dhcp-range=192.168.10.0,proxy + +# Identify requested arch +# REF: https://wiki.archlinux.org/title/dnsmasq#PXE_server +dhcp-match=set:arch_x64,option:client-arch,7 +dhcp-match=set:arch_x64,option:client-arch,9 +dhcp-match=set:arch_ia32,option:client-arch,6 + +# Specify one or more boot options per architecture +pxe-service=tag:arch_x64,x86-64_EFI,"Open Shell",OpenShell.efi +pxe-service=tag:arch_x64,x86-64_EFI,"Boot Helper",BootHelper.efi + +# Enable TFTP support +enable-tftp +tftp-root=/home/mjsbeaton/tftp +``` + +A more advanced configuration might serve different files to different +machines, depending on their hardware id. (The same point applies to +HTTP boot.) See `dnsmasq` documentation. + +See **HTTP Boot** **dnsmasq** section below for command to quickly start +`dnsmasq` for testing. + +#### WDS + +Windows Deployment Services (WDS, which is incuded with Windows Server) can be +used to provide responses to PXE boot requests, and can be configured to serve +non-Windows NBP files. + +**Note 1**: Certain aspects of WDS are now deprecated: +https://aka.ms/WDSSupport + +**Note 2**: On certain systems, the OpenCore `TextRenderer` setting +must be set to one of the `System` values in order to see the early output of +`wdsmgfw.efi` (the NBP served by default by WDS). If this text is not visible, +this can be worked round by pressing either `F12` or `Enter` in the pause +after the program has loaded, in order to access the next screen. +The issue of the early text of this software not appearing in some circumstances +is not unique to OpenCore: https://serverfault.com/q/683314 + +### HTTP Boot + +#### dnsmasq + +Although `dnsmasq` does not provide complete support for HTTP +boot, as it does for PXE boot, its PXE boot features can be used +to respond to requests for the location of HTTP boot NBP files. + +A basic `dnsmasq` HTTP boot configuration is as follows: + +``` +# Disable DNS Server +port=0 + +# Run as PXE Boot DHCP proxy for specified network (use default /24 network size) +dhcp-range=192.168.2.0,proxy + +# Trigger PXE Boot support on HTTP Boot client request +dhcp-pxe-vendor=HTTPClient + +# Set triggering tag if correct arch is present in option 60 +dhcp-match=set:arch_x64,option:client-arch,16 + +# Make PXE Boot support believe it has something to send... +pxe-service=tag:arch_x64,x86-64_EFI,"Network Boot" + +# Specify bootfile-name via PXE Boot setting +dhcp-boot=tag:arch_x64,https://michaels-air.lan:8443/images/OpenShell.efi + +# Force required vendor class in response, even if not requested +dhcp-option-force=tag:arch_x64,option:vendor-class,HTTPClient +``` + +To quickly start `dnsmasq` for testing, without running it as a server, +the following command can be used: + +``` +sudo dnsmasq --no-daemon -C dnsmasq.conf --log-dhcp --log-debug +``` + +An HTTP server (such as Apache, nginx, or multiple other options) will be +required to serve the actual NBP files over `http://` or `https://`. + +References: + - https://github.com/ipxe/ipxe/discussions/569 + - https://www.mail-archive.com/dnsmasq-discuss@lists.thekelleys.org.uk/msg16278.html + +### HTTPS Boot + +Note that the certificate for validating `https://` requests should be loaded into +firmware using the OpenNetworkBoot `--enroll-cert` option. + +A normal https site would not serve files using a self-signed certificate +authority (CA), but since we are only attempting to serve files to HTTP boot +clients, in this case we can do so. + +## Booting ISO and IMG files + +Though not often noted in the documentation, the majority of HTTP Boot +implementations support loading `.iso` and `.img` files, which will be +automatically mounted as a ramdisk. If the mounted filesystem includes +`\EFI\BOOT\BOOTx64.efi` (or `\EFI\BOOT\BOOTIA32.efi` for 32-bit) then this +file will be loaded from the ramdisk and started. + +The MIME types corresponding to `.iso` and `.img` files are: + + - `application/vnd.efi-iso` + - `application/vnd.efi-img` + +The MIME type for `.efi` files is: + + - `application/efi` + +If the MIME type is none of the above, then the corresponding file +extensions (`.iso`, `.img` and `.efi`) are used instead to identify the NBP +file type. + +Additionally, for boot options generated by `OpenNetworkBoot`, `.dmg` +and `.chunklist` files will be recognised by extension and loaded, +regardless of MIME type. + +> **Note**: Files which cannot be recognised by any of the above MIME types or +file extensions will _not_ be loaded by HTTP boot drivers. + +## Booting DMG files + +In order to allow booting macOS Recovery, `OpenNetworkBoot` includes +additional support for loading `.dmg` files via HTTP boot. If the NBP +filename is `{filename}.dmg` or `{filename}.chunklist` then the other +file of this pair will be automatically loaded, in order to allow DMG +chunklist verification, and both files will be used for OpenCore DMG booting. + +### `DmgLoading` configuration setting + +The behaviour of `OpenNetworkBoot`'s DMG support depends on the OpenCore +`DmgLoading` setting as follows: + + - If `DmgLoading` is set to `Signed` then both `.chunklist` and `.dmg` files +must be available from the HTTP server. Either file can be specified as +the NBP, and the other matching file will be loaded afterwards, automatically. + - If `DmgLoading` is set to `Disabled` and either of these two file extensions +are found as the NBP, then the HTTP boot process will be aborted. (If we allowed +these files to load and then passed them to the OpenCore DMG loading process, +they would be rejected at that point anyway.) + - If `DmgLoading` is set to `Any` and the NBP is `{filename}.dmg` then only +the `.dmg` file will be loaded, as verification via `.chunklist` is not +carried out with this setting. If the NBP is `{filename}.chunklist` then +the `.chunklist` followed by the `.dmg` will be loaded, but only the `.dmg` +will be used. + +## OVMF + +OVMF can be compiled with the following flags for full network boot support: + +`-D NETWORK_HTTP_BOOT_ENABLE -D NETWORK_TLS_ENABLE -D NETWORK_IP6_ENABLE` + +Since a May 2024 security update to the network boot stack, Random +Number Generator (RNG) protocol support is required. If running OVMF +with an Ivy Bridge or above CPU, then the `RngDxe` driver included in +OVMF will provide the required support. For CPUs below Ivy Bridge +the qemu option `-device virtio-rng-pci` must be provided, so that +the `VirtioRngDxe` driver which is also present in OVMF can provide +the required RNG support. + +If OVMF is compiled without network boot support (`-D NETWORK_ENABLE=0`), then +network boot support provided with OpenCore may be added by loading the full +Network Boot Stack (see below). + +### OVMF networking + +If any network boot clients (e.g. OVMF, VMWare) or server programs +(e.g. Apache, `dnsmasq`, WDS) are running on VMs, it is normally easiest +to set these up using bridged network support, which allows the VM to +appear as a separate device with its own IP address on the network. + +To start OVMF with bridged network support the following macOS-specific +`qemu` options (which require `sudo`) may be used: + +``` +-netdev vmnet-bridged,id=mynet0,ifname=en0 \ +-device e1000,netdev=mynet0,id=mynic0 +``` + +PXE boot may also be tested in OVMF using qemu's built-in TFTP/PXE server, +available with the qemu user mode network stack, for example using the +following options: + +``` +-netdev user,id=net0,tftp=/Users/user/tftp,bootfile=/OpenShell.efi \ +-device virtio-net-pci,netdev=net0 +``` + +No equivalent option is available for HTTP boot, so to experiment with this, +a combination such as bridged networking and `dnsmasq` should be used. + +### OVMF HTTPS certificate + +When using `https://` as opposed to `http://`, a certificate must be +configured on the network boot client. Within the OVMF menus this may +be done using +`Device Manager/Tls Auth Configuration/Server CA Configuration/Enroll Cert/Enroll Cert Using File`. + +> **Tip**: No GUID needs to be provided in the above dialog. All zeroes will be +used if nothing is specified, which is fine if only a single certificate is going +to be configured and managed. + +### Debugging network boot on OVMF + +Building OVMF with the `-D DEBUG_ON_SERIAL_PORT` option and then passing the +`-serial stdio` option to qemu (and then scrolling back in the output as +needed, to the lines generated during a failed network boot) can be very +useful when trying to debug network boot setup. + +OVMF can capture packets using +`-object filter-dump,netdev={net-id},id=filter0,file=/Users/user/ovmf.cap` +(`{net-id}` should be replaced as appropriate with the `id` value specified in the +corresponding `-netdev` option). + +For additional information on debugging OpenCore using OVMF, +see https://github.com/acidanthera/OpenCorePkg/blob/master/Debug/README.md. + +## Network Boot Stack + +The following drivers supplied with OpenCore make up the network boot +stack. Please follow the procedures given towards the start of this +document for deciding which drivers to add. + +### Prerequisites +Various network boot drivers depend on the presence of HiiDatabase. + +A recent (May 2024) security update to the EDK 2 network stack +means that various drivers also depend on the RNG and Hash2 protocols. + +These protocols can be checked for in UEFI Shell with: + +``` +dh -p HIIDatabase +dh -p Rng +dh -p Hash2 +``` + +If not present, the respective drivers should be loaded before +the network boot stack. + +``` +HiiDatabase +RngDxe +Hash2DxeCrypto +``` + +### RAM Disk +Required if not already present in firmware, and the user wishes +to load `.iso` or `.img` files with HTTP Boot. + +Can be checked for in UEFI Shell with `dh -p RamDisk`. + +``` +RamDiskDxe +``` + +### Network Boot Base +``` +DpcDxe +SnpDxe +MnpDxe +TcpDxe +``` + +### IPv4 +``` +ArpDxe +Dhcp4Dxe +Ip4Dxe +Udp4Dxe +``` + +### IPv6 +``` +Dhcp6Dxe +Ip6Dxe +Udp6Dxe +``` + +### HTTP Boot +``` +DnsDxe +HttpDxe +HttpUtilitiesDxe +HttpBootDxe +``` + +### HTTPS (TLS) support for HTTP Boot +``` +TlsDxe +``` + +### PXE +For PXE Boot support it would also be necessary to add (not provided with OpenCore): +``` +Mtftp4Dxe (IPv4) +Mtftp6Dxe (IPv6) +``` diff --git a/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c b/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c index 9c0acaeeece..ed2aa313e23 100644 --- a/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c +++ b/Platform/OpenNetworkBoot/TlsAuthConfigImpl.c @@ -1,98 +1,67 @@ /** @file - The Miscellaneous Routines for TlsAuthConfigDxe driver. + Miscellaneous routines for TLS auth config. -Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2024, Mike Beaton. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ -SPDX-License-Identifier: BSD-2-Clause-Patent +#include "NetworkBootInternal.h" -**/ +#define TLS_AUTH_CONFIG_VAR_BASE_ATTR (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS) -#include "TlsAuthConfigImpl.h" +typedef +EFI_STATUS +(*PROCESS_CERT)( + IN VOID *Context, + IN UINTN CertIndex, + IN UINTN CertSize, + IN EFI_SIGNATURE_DATA *Cert + ); + +typedef struct { + EFI_GUID *OwnerGuid; + UINTN X509DataSize; + VOID *X509Data; +} CERT_IS_PRESENT_CONTEXT; /** - List all cert in specified database by GUID in the page - for user to select and delete as needed. + Perform action for all signatures in specified database, with + possibility of aborting early. - @param[in] PrivateData Module's private data. @param[in] VariableName The variable name of the vendor's signature database. - @param[in] VendorGuid A unique identifier for the vendor. - @param[in] LabelNumber Label number to insert opcodes. - @param[in] FormId Form ID of current page. - @param[in] QuestionIdBase Base question id of the signature list. - - @retval EFI_SUCCESS Success to update the signature list page - @retval EFI_OUT_OF_RESOURCES Unable to allocate required resources. + @param[in] VendorGuid A unique identifier for the signature database vendor. + @param[in] ProcessCert The method to call for each certificate. + @param[in] Context Context for ProcessCert, if required. + @retval EFI_SUCCESS Looped over all signatures. + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. + @retval Other Other error or return code from from ProcessCert. **/ +STATIC EFI_STATUS -UpdateDeletePage ( - IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private, - IN CHAR16 *VariableName, - IN EFI_GUID *VendorGuid, - IN UINT16 LabelNumber, - IN EFI_FORM_ID FormId, - IN EFI_QUESTION_ID QuestionIdBase +ProcessAllCerts ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN PROCESS_CERT ProcessCert, + IN VOID *Context OPTIONAL ) { EFI_STATUS Status; UINT32 Index; UINTN CertCount; UINTN GuidIndex; - VOID *StartOpCodeHandle; - VOID *EndOpCodeHandle; - EFI_IFR_GUID_LABEL *StartLabel; - EFI_IFR_GUID_LABEL *EndLabel; UINTN DataSize; UINT8 *Data; EFI_SIGNATURE_LIST *CertList; EFI_SIGNATURE_DATA *Cert; UINT32 ItemDataSize; - CHAR16 *GuidStr; - EFI_STRING_ID GuidID; - EFI_STRING_ID Help; - - Data = NULL; - CertList = NULL; - Cert = NULL; - GuidStr = NULL; - StartOpCodeHandle = NULL; - EndOpCodeHandle = NULL; - - // - // Initialize the container for dynamic opcodes. - // - StartOpCodeHandle = HiiAllocateOpCodeHandle (); - if (StartOpCodeHandle == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; - } - EndOpCodeHandle = HiiAllocateOpCodeHandle (); - if (EndOpCodeHandle == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; - } + ASSERT (ProcessCert != NULL); - // - // Create Hii Extend Label OpCode. - // - StartLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode ( - StartOpCodeHandle, - &gEfiIfrTianoGuid, - NULL, - sizeof (EFI_IFR_GUID_LABEL) - ); - StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; - StartLabel->Number = LabelNumber; - - EndLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode ( - EndOpCodeHandle, - &gEfiIfrTianoGuid, - NULL, - sizeof (EFI_IFR_GUID_LABEL) - ); - EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; - EndLabel->Number = LABEL_END; + Data = NULL; + CertList = NULL; + Cert = NULL; // // Read Variable. @@ -100,24 +69,22 @@ UpdateDeletePage ( DataSize = 0; Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, Data); if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) { - goto ON_EXIT; + if (Status == EFI_NOT_FOUND) { + Status = EFI_SUCCESS; + } + + return Status; } Data = (UINT8 *)AllocateZeroPool (DataSize); if (Data == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; + return EFI_OUT_OF_RESOURCES; } Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, Data); if (EFI_ERROR (Status)) { - goto ON_EXIT; - } - - GuidStr = AllocateZeroPool (100); - if (GuidStr == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; + FreePool (Data); + return Status; } // @@ -128,9 +95,7 @@ UpdateDeletePage ( GuidIndex = 0; while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) { - if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) { - Help = STRING_TOKEN (STR_CERT_TYPE_PCKS_GUID); - } else { + if (!CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) { // // The signature type is not supported in current implementation. // @@ -139,86 +104,165 @@ UpdateDeletePage ( continue; } + Status = EFI_SUCCESS; CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; for (Index = 0; Index < CertCount; Index++) { Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize + Index * CertList->SignatureSize); - // - // Display GUID and help - // - GuidToString (&Cert->SignatureOwner, GuidStr, 100); - GuidID = HiiSetString (Private->RegisteredHandle, 0, GuidStr, NULL); - HiiCreateCheckBoxOpCode ( - StartOpCodeHandle, - (EFI_QUESTION_ID)(QuestionIdBase + GuidIndex++), - 0, - 0, - GuidID, - Help, - EFI_IFR_FLAG_CALLBACK, - 0, - NULL - ); + + Status = ProcessCert (Context, GuidIndex, CertList->SignatureSize, Cert); + if (EFI_ERROR (Status)) { + break; + } + + ++GuidIndex; + } + + if (EFI_ERROR (Status)) { + break; } ItemDataSize -= CertList->SignatureListSize; CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize); } -ON_EXIT: - HiiUpdateForm ( - Private->RegisteredHandle, - &gTlsAuthConfigGuid, - FormId, - StartOpCodeHandle, - EndOpCodeHandle - ); - - if (StartOpCodeHandle != NULL) { - HiiFreeOpCodeHandle (StartOpCodeHandle); - } + FreePool (Data); - if (EndOpCodeHandle != NULL) { - HiiFreeOpCodeHandle (EndOpCodeHandle); - } + return Status; +} - if (Data != NULL) { - FreePool (Data); +/** + @retval EFI_SUCCESS Continue processing. +**/ +STATIC +EFI_STATUS +LogCert ( + IN VOID *Context, + IN UINTN CertIndex, + IN UINTN CertSize, + IN EFI_SIGNATURE_DATA *Cert + ) +{ + DEBUG ((DEBUG_INFO, "NTBT: Cert %u owner %g\n", CertIndex, &Cert->SignatureOwner)); + return EFI_SUCCESS; +} + +/** + Log owner GUID of each installed certificate in signature database. + + @param[in] VariableName The variable name of the signature database. + @param[in] VendorGuid A unique identifier for the signature database vendor. + + @retval EFI_SUCCESS Success. +**/ +EFI_STATUS +LogInstalledCerts ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + DEBUG ((DEBUG_INFO, "NTBT: Listing installed certs...\n")); + return ProcessAllCerts ( + VariableName, + VendorGuid, + LogCert, + NULL + ); +} + +/** + @retval EFI_SUCCESS Certificate not found; continue processing. + @retval EFI_ALREADY_STARTED Certificate found; stop processing. +**/ +STATIC +EFI_STATUS +CheckCertPresent ( + IN VOID *VoidContext, + IN UINTN CertIndex, + IN UINTN CertSize, + IN EFI_SIGNATURE_DATA *Cert + ) +{ + CERT_IS_PRESENT_CONTEXT *Context; + + Context = VoidContext; + + if (!CompareGuid (&Cert->SignatureOwner, Context->OwnerGuid)) { + return EFI_SUCCESS; } - if (GuidStr != NULL) { - FreePool (GuidStr); + if ( (CertSize == sizeof (EFI_SIGNATURE_DATA) - 1 + Context->X509DataSize) + && (CompareMem (Cert->SignatureData, Context->X509Data, Context->X509DataSize) == 0) + ) + { + return EFI_ALREADY_STARTED; } return EFI_SUCCESS; } /** - Delete one entry from cert database. + Report whether specified signature is already enrolled for given owner. + + @param[in] VariableName Variable name of CA database. + @param[in] VendorGuid Unique identifier for the CA database vendor. + @param[in] OwnerGuid Unique identifier for owner of the certificate to be searched for. + @param[in] X509DataSize Certificate data size. + @param[in] X509Data Certificate data. + + @retval EFI_SUCCESS Certificate is already enrolled. + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. +**/ +EFI_STATUS +CertIsPresent ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN EFI_GUID *OwnerGuid, + IN UINTN X509DataSize, + IN VOID *X509Data + ) +{ + CERT_IS_PRESENT_CONTEXT Context; + + ASSERT (X509Data != NULL); + + Context.OwnerGuid = OwnerGuid; + Context.X509DataSize = X509DataSize; + Context.X509Data = X509Data; + + return ProcessAllCerts ( + VariableName, + VendorGuid, + CheckCertPresent, + &Context + ); +} + +/** + Delete specific entry or all entries with owner guid from signature database. + (Based on original EDK 2 DeleteCert which removes one cert, identified by index.) - @param[in] Private Module's private data. - @param[in] VariableName The variable name of the database. - @param[in] VendorGuid A unique identifier for the vendor. - @param[in] LabelNumber Label number to insert opcodes. - @param[in] FormId Form ID of current page. - @param[in] QuestionIdBase Base question id of the cert list. - @param[in] DeleteIndex Cert index to delete. + @param[in] VariableName The variable name of the signature database. + @param[in] VendorGuid A unique identifier for the signature database vendor. + @param[in] OwnerGuid A unique identifier for owner of the certificate(s) to be deleted. + @param[in] X509DataSize Optional certificate data size. + @param[in] X509Data Optional certificate data. If non-NULL, delete only specific certificate + for owner, if present. If NULL, delete all certificates for owner. + @param[in] DeletedCount Optional return count of deleted certificates. @retval EFI_SUCCESS Delete signature successfully. - @retval EFI_NOT_FOUND Can't find the signature item, @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. **/ EFI_STATUS -DeleteCert ( - IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private, - IN CHAR16 *VariableName, - IN EFI_GUID *VendorGuid, - IN UINT16 LabelNumber, - IN EFI_FORM_ID FormId, - IN EFI_QUESTION_ID QuestionIdBase, - IN UINTN DeleteIndex +DeleteCertsForOwner ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN EFI_GUID *OwnerGuid, + IN UINTN X509DataSize, + IN VOID *X509Data, + OUT UINTN *DeletedCount ) { EFI_STATUS Status; @@ -232,9 +276,16 @@ DeleteCert ( EFI_SIGNATURE_DATA *Cert; UINTN CertCount; UINT32 Offset; - BOOLEAN IsItemFound; + UINTN LocalDeleteCount; UINT32 ItemDataSize; - UINTN GuidIndex; + + ASSERT ((X509Data == NULL) || (X509DataSize != 0)); + + if (DeletedCount == NULL) { + DeletedCount = &LocalDeleteCount; + } + + *DeletedCount = 0; Data = NULL; OldData = NULL; @@ -248,37 +299,39 @@ DeleteCert ( DataSize = 0; Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, NULL); if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) { - goto ON_EXIT; + if (Status == EFI_NOT_FOUND) { + Status = EFI_SUCCESS; + } + + return Status; } - OldData = (UINT8 *)AllocateZeroPool (DataSize); + OldData = AllocateZeroPool (DataSize); if (OldData == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; + return EFI_OUT_OF_RESOURCES; } Status = gRT->GetVariable (VariableName, VendorGuid, &Attr, &DataSize, OldData); if (EFI_ERROR (Status)) { - goto ON_EXIT; + FreePool (OldData); + return Status; } // // Allocate space for new variable. // - Data = (UINT8 *)AllocateZeroPool (DataSize); + Data = AllocateZeroPool (DataSize); if (Data == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; + FreePool (OldData); + return EFI_OUT_OF_RESOURCES; } // - // Enumerate all data and erasing the target item. + // Enumerate all data, erasing target items. // - IsItemFound = FALSE; ItemDataSize = (UINT32)DataSize; CertList = (EFI_SIGNATURE_LIST *)OldData; Offset = 0; - GuidIndex = 0; while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) { if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) { // @@ -290,12 +343,19 @@ DeleteCert ( Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; for (Index = 0; Index < CertCount; Index++) { - if (GuidIndex == DeleteIndex) { + if ( CompareGuid (&Cert->SignatureOwner, OwnerGuid) + && ( (X509Data == NULL) + || ( (CertList->SignatureSize == (UINT32)(sizeof (EFI_SIGNATURE_DATA) - 1 + X509DataSize)) + && (CompareMem ((UINT8 *)(Cert->SignatureData), X509Data, X509DataSize) == 0) + ) + ) + ) + { // // Find it! Skip it! // NewCertList->SignatureListSize -= CertList->SignatureSize; - IsItemFound = TRUE; + ++(*DeletedCount); } else { // // This item doesn't match. Copy it to the Data buffer. @@ -304,7 +364,6 @@ DeleteCert ( Offset += CertList->SignatureSize; } - GuidIndex++; Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)Cert + CertList->SignatureSize); } } else { @@ -319,85 +378,67 @@ DeleteCert ( CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize); } - if (!IsItemFound) { + if (*DeletedCount != 0) { // - // Doesn't find the signature Item! + // Delete the EFI_SIGNATURE_LIST header if there is no signature remaining in any list. // - Status = EFI_NOT_FOUND; - goto ON_EXIT; - } + ItemDataSize = Offset; + CertList = (EFI_SIGNATURE_LIST *)Data; + Offset = 0; + ZeroMem (OldData, ItemDataSize); + while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) { + CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; + if (CertCount != 0) { + CopyMem (OldData + Offset, (UINT8 *)(CertList), CertList->SignatureListSize); + Offset += CertList->SignatureListSize; + } - // - // Delete the EFI_SIGNATURE_LIST header if there is no signature in the list. - // - ItemDataSize = Offset; - CertList = (EFI_SIGNATURE_LIST *)Data; - Offset = 0; - ZeroMem (OldData, ItemDataSize); - while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) { - CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; - DEBUG ((DEBUG_INFO, " CertCount = %x\n", CertCount)); - if (CertCount != 0) { - CopyMem (OldData + Offset, (UINT8 *)(CertList), CertList->SignatureListSize); - Offset += CertList->SignatureListSize; + ItemDataSize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize); } - ItemDataSize -= CertList->SignatureListSize; - CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize); - } - - DataSize = Offset; - - Status = gRT->SetVariable ( - VariableName, - VendorGuid, - Attr, - DataSize, - OldData - ); - if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "Failed to set variable, Status = %r\n", Status)); - goto ON_EXIT; - } + DataSize = Offset; -ON_EXIT: - if (Data != NULL) { - FreePool (Data); + // + // Set (or delete if everything was removed) the Variable. + // + Status = gRT->SetVariable ( + VariableName, + VendorGuid, + Attr, + DataSize, + OldData + ); } - if (OldData != NULL) { - FreePool (OldData); - } + FreePool (Data); + FreePool (OldData); - return UpdateDeletePage ( - Private, - VariableName, - VendorGuid, - LabelNumber, - FormId, - QuestionIdBase - ); + return Status; } /** Enroll a new X509 certificate into Variable. - @param[in] PrivateData The module's private data. @param[in] VariableName Variable name of CA database. + @param[in] VendorGuid Unique identifier for the CA database vendor. + @param[in] OwnerGuid Unique identifier for owner of the certificate to be installed. + @param[in] X509DataSize Certificate data size. + @param[in] X509Data Certificate data. @retval EFI_SUCCESS New X509 is enrolled successfully. @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. - **/ EFI_STATUS EnrollX509toVariable ( - IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private, - IN CHAR16 *VariableName + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN EFI_GUID *OwnerGuid, + IN UINTN X509DataSize, + IN VOID *X509Data ) { EFI_STATUS Status; - UINTN X509DataSize; - VOID *X509Data; EFI_SIGNATURE_LIST *CACert; EFI_SIGNATURE_DATA *CACertData; VOID *Data; @@ -405,33 +446,29 @@ EnrollX509toVariable ( UINTN SigDataSize; UINT32 Attr; - X509DataSize = 0; - SigDataSize = 0; - DataSize = 0; - X509Data = NULL; - CACert = NULL; - CACertData = NULL; - Data = NULL; - Attr = 0; - - Status = ReadFileContent ( - Private->FileContext->FHandle, - &X509Data, - &X509DataSize, - 0 - ); - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } + SigDataSize = 0; + DataSize = 0; + CACert = NULL; + CACertData = NULL; + Data = NULL; + Attr = 0; ASSERT (X509Data != NULL); + // + // Note: As implemented in EDK 2, each signature list can have multiple + // instances of signature data (owner guid followed by raw signature data), + // but every instance in one list must have the same size. + // The signature data is the unprocessed contents of a .pem or .der file. + // It is not immediately obvious how the multiple signature feature would + // be useful as signature file data does not in general have a fixed size + // (not even for .pem files: https://security.stackexchange.com/q/152584). + // SigDataSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + X509DataSize; Data = AllocateZeroPool (SigDataSize); if (Data == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; + return EFI_OUT_OF_RESOURCES; } // @@ -444,7 +481,7 @@ EnrollX509toVariable ( CopyGuid (&CACert->SignatureType, &gEfiCertX509Guid); CACertData = (EFI_SIGNATURE_DATA *)((UINT8 *)CACert + sizeof (EFI_SIGNATURE_LIST)); - CopyGuid (&CACertData->SignatureOwner, Private->CertGuid); + CopyGuid (&CACertData->SignatureOwner, OwnerGuid); CopyMem ((UINT8 *)(CACertData->SignatureData), X509Data, X509DataSize); // @@ -454,7 +491,7 @@ EnrollX509toVariable ( // Status = gRT->GetVariable ( VariableName, - &gEfiTlsCaCertificateGuid, + VendorGuid, &Attr, &DataSize, NULL @@ -464,35 +501,19 @@ EnrollX509toVariable ( } else if (Status == EFI_NOT_FOUND) { Attr = TLS_AUTH_CONFIG_VAR_BASE_ATTR; } else { - goto ON_EXIT; + FreePool (Data); + return Status; } Status = gRT->SetVariable ( VariableName, - &gEfiTlsCaCertificateGuid, + VendorGuid, Attr, SigDataSize, Data ); - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } - -ON_EXIT: - CleanFileContext (Private); - - if (Private->CertGuid != NULL) { - FreePool (Private->CertGuid); - Private->CertGuid = NULL; - } - if (Data != NULL) { - FreePool (Data); - } - - if (X509Data != NULL) { - FreePool (X509Data); - } + FreePool (Data); return Status; } diff --git a/Platform/OpenNetworkBoot/Uri.c b/Platform/OpenNetworkBoot/Uri.c new file mode 100644 index 00000000000..6a3ce120f81 --- /dev/null +++ b/Platform/OpenNetworkBoot/Uri.c @@ -0,0 +1,304 @@ +/** @file + URI utilities for HTTP Boot. + + Copyright (c) 2024, Mike Beaton. All rights reserved.
+ SPDX-License-Identifier: BSD-3-Clause +**/ + +#include "NetworkBootInternal.h" + +STATIC EFI_DEVICE_PATH_PROTOCOL mEndDevicePath = { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + END_DEVICE_PATH_LENGTH, + 0 + } +}; + +BOOLEAN +HasHttpsUri ( + CHAR16 *Uri + ) +{ + ASSERT (Uri != NULL); + + return (OcStrniCmp (L"https://", Uri, L_STR_LEN (L"https://")) == 0); +} + +EFI_DEVICE_PATH_PROTOCOL * +GetUriNode ( + EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_DEVICE_PATH_PROTOCOL *Previous; + EFI_DEVICE_PATH_PROTOCOL *Node; + + for ( Previous = NULL, Node = DevicePath + ; !IsDevicePathEnd (Node) + ; Node = NextDevicePathNode (Node)) + { + Previous = Node; + } + + if ( (Previous != NULL) + && (DevicePathType (Previous) == MESSAGING_DEVICE_PATH) + && (DevicePathSubType (Previous) == MSG_URI_DP) + ) + { + return Previous; + } + + return NULL; +} + +// +// See HttpBootCheckImageType and HttpUrlGetPath within it for how to get +// the proper filename from URL: we would need to use HttpParseUrl, then +// HttpUrlGetPath, then HttpUrlFreeParser. +// +// However, here are some examples: +// +// - https://myserver.com/images/OpenShell.efi +// - https://myserver.com/download.php?a=1&b=2&filename=OpenShell.efi +// - https://myserver.com/images/OpenShell.efi?secure=123 +// +// Rather than parse the URL properly, we can handle all of the above +// (one of which would not be handled by proper parsing of the filename +// part, since the filename is in a parameter; even though this is a fudge, +// as the parameter must come last to be fixed up) if we check if url ends +// with the file ext, and replace it if so; otherwise check if the part +// ending at '?' ends with ext and replace it if so. +// +STATIC +EFI_STATUS +ExtractOtherUriFromUri ( + IN CHAR8 *Uri, + IN CHAR8 *FromExt, + IN CHAR8 *ToExt, + OUT CHAR8 **OtherUri, + IN BOOLEAN OnlySearchForFromExt + ) +{ + CHAR8 *SearchUri; + UINTN UriLen; + UINTN OtherUriLen; + UINTN FromExtLen; + UINTN ToExtLen; + INTN SizeChange; + CHAR8 *ParamsStart; + + ASSERT (FromExt != NULL); + + if (!OnlySearchForFromExt) { + ASSERT (ToExt != NULL); + ASSERT (OtherUri != NULL); + *OtherUri = NULL; + } + + UriLen = AsciiStrLen (Uri); + if (UriLen > MAX_INTN - 1) { + ///< i.e. UriSize > MAX_INTN + return EFI_INVALID_PARAMETER; + } + + FromExtLen = AsciiStrLen (FromExt); + if (FromExtLen > MAX_INTN - 1) { + ///< i.e. FromExtSize > MAX_INTN + return EFI_INVALID_PARAMETER; + } + + if (UriLen < FromExtLen) { + return EFI_NOT_FOUND; + } + + if (OnlySearchForFromExt) { + SearchUri = Uri; + } else { + ToExtLen = AsciiStrLen (ToExt); + if (ToExtLen > MAX_INTN - 1) { + ///< i.e. ToExtSize > MAX_INTN + return EFI_INVALID_PARAMETER; + } + + if (BaseOverflowSubSN ((INTN)ToExtLen, (INTN)FromExtLen, &SizeChange)) { + return EFI_INVALID_PARAMETER; + } + + if (BaseOverflowAddSN ((INTN)UriLen, SizeChange, (INTN *)&OtherUriLen)) { + return EFI_INVALID_PARAMETER; + } + + if (OtherUriLen > MAX_INTN - 1) { + ///< i.e. OtherUriSize > MAX_INTN + return EFI_INVALID_PARAMETER; + } + + *OtherUri = AllocatePool (MAX (UriLen, OtherUriLen) + 1); + if (*OtherUri == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (*OtherUri, Uri, UriLen + 1); + SearchUri = *OtherUri; + } + + if (AsciiStrnCmp (&SearchUri[UriLen - FromExtLen], FromExt, FromExtLen) == 0) { + if (!OnlySearchForFromExt) { + CopyMem (&SearchUri[UriLen - FromExtLen], ToExt, ToExtLen + 1); + if (SizeChange < -1) { + ZeroMem (&SearchUri[UriLen + SizeChange + 1], -(SizeChange + 1)); + } + } + + return EFI_SUCCESS; + } + + ParamsStart = AsciiStrStr (SearchUri, "?"); + if ( (ParamsStart != NULL) + && (AsciiStrnCmp (ParamsStart - FromExtLen, FromExt, FromExtLen) == 0)) + { + if (!OnlySearchForFromExt) { + CopyMem (ParamsStart + SizeChange, ParamsStart, &SearchUri[UriLen] - ParamsStart + 1); + CopyMem (ParamsStart - FromExtLen, ToExt, ToExtLen); + if (SizeChange < -1) { + ZeroMem (&SearchUri[UriLen + SizeChange + 1], -(SizeChange + 1)); + } + } + + return EFI_SUCCESS; + } + + if (!OnlySearchForFromExt) { + FreePool (*OtherUri); + *OtherUri = NULL; + } + + return EFI_NOT_FOUND; +} + +// +// We have the filename in the returned device path, so we can try to load the +// matching file (.chunklist or .dmg, whichever one was not fetched), if we +// we find the other. +// +EFI_STATUS +ExtractOtherUriFromDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN CHAR8 *FromExt, + IN CHAR8 *ToExt, + OUT CHAR8 **OtherUri, + IN BOOLEAN OnlySearchForFromExt + ) +{ + EFI_DEVICE_PATH_PROTOCOL *Previous; + CHAR8 *Uri; + + ASSERT (DevicePath != NULL); + + Previous = GetUriNode (DevicePath); + if (Previous == NULL) { + return EFI_INVALID_PARAMETER; + } + + Uri = (CHAR8 *)Previous + sizeof (EFI_DEVICE_PATH_PROTOCOL); + + return ExtractOtherUriFromUri ( + Uri, + FromExt, + ToExt, + OtherUri, + OnlySearchForFromExt + ); +} + +// +// Determine whether file at URI has extension. This isn't only checking +// the file part of the URI, instead it first checks the argument to the last +// param, if there is one. This is a convenience to allow the 'download.php' +// example shown at ExtractOtherUriFromUri. +// +BOOLEAN +UriFileHasExtension ( + IN CHAR8 *Uri, + IN CHAR8 *Ext + ) +{ + return (!EFI_ERROR (ExtractOtherUriFromUri (Uri, Ext, NULL, NULL, TRUE))); +} + +EFI_STATUS +HttpBootAddUri ( + EFI_DEVICE_PATH_PROTOCOL *DevicePath, + VOID *Uri, + OC_STRING_FORMAT StringFormat, + EFI_DEVICE_PATH_PROTOCOL **UriDevicePath + ) +{ + UINTN Length; + UINTN UriSize; + VOID *OriginalUri; + EFI_DEVICE_PATH_PROTOCOL *Previous; + EFI_DEVICE_PATH_PROTOCOL *Node; + EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath; + + ASSERT (UriDevicePath != NULL); + *UriDevicePath = NULL; + + TmpDevicePath = DuplicateDevicePath (DevicePath); + if (TmpDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // If last non-end node is a URI node, replace it with an end node. + // + Previous = GetUriNode (TmpDevicePath); + if (Previous == NULL) { + FreePool (TmpDevicePath); + return EFI_INVALID_PARAMETER; + } + + CopyMem (Previous, &mEndDevicePath, sizeof (mEndDevicePath)); + + // + // Create replacement URI node with the input boot file URI. + // + if (StringFormat == OcStringFormatUnicode) { + OriginalUri = Uri; + UriSize = StrSize (Uri); + Uri = AllocatePool (UriSize * sizeof (CHAR8)); + if (Uri == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UnicodeStrToAsciiStrS (OriginalUri, Uri, UriSize); + } else { + OriginalUri = NULL; + UriSize = AsciiStrSize (Uri); + } + + Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + UriSize; + Node = AllocatePool (Length); + if (Node == NULL) { + FreePool (TmpDevicePath); + return EFI_OUT_OF_RESOURCES; + } + + Node->Type = MESSAGING_DEVICE_PATH; + Node->SubType = MSG_URI_DP; + SetDevicePathNodeLength (Node, Length); + CopyMem ((UINT8 *)Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Uri, UriSize); + if (OriginalUri != NULL) { + FreePool (Uri); + } + + *UriDevicePath = AppendDevicePathNode (TmpDevicePath, Node); + FreePool (Node); + FreePool (TmpDevicePath); + if (*UriDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} diff --git a/build_oc.tool b/build_oc.tool index 374fc943918..ab20e1d15e0 100755 --- a/build_oc.tool +++ b/build_oc.tool @@ -186,21 +186,25 @@ package() { "BiosVideo.efi" "CrScreenshotDxe.efi" "Dhcp4Dxe.efi" + "Dhcp6Dxe.efi" "DnsDxe.efi" "DpcDxe.efi" "Ext4Dxe.efi" "FirmwareSettingsEntry.efi" + "Hash2DxeCrypto.efi" "HiiDatabase.efi" "HttpBootDxe.efi" "HttpDxe.efi" "HttpUtilitiesDxe.efi" "Ip4Dxe.efi" + "Ip6Dxe.efi" "MnpDxe.efi" "NvmExpressDxe.efi" "OpenCanopy.efi" "OpenHfsPlus.efi" "OpenLegacyBoot.efi" "OpenLinuxBoot.efi" + "OpenNetworkBoot.efi" "OpenNtfsDxe.efi" "OpenPartitionDxe.efi" "OpenRuntime.efi" @@ -208,11 +212,15 @@ package() { "OpenVariableRuntimeDxe.efi" "Ps2KeyboardDxe.efi" "Ps2MouseDxe.efi" + "RamDiskDxe.efi" "ResetNvramEntry.efi" + "RngDxe.efi" "SnpDxe.efi" "TcpDxe.efi" + "TlsDxe.efi" "ToggleSipEntry.efi" "Udp4Dxe.efi" + "Udp6Dxe.efi" "UsbMouseDxe.efi" "XhciDxe.efi" ) From 0ea90192a21047e7c88789394fbab584057119d7 Mon Sep 17 00:00:00 2001 From: Mike Beaton Date: Sat, 9 Nov 2024 12:45:42 +0000 Subject: [PATCH 4/4] OpenNetworkBoot: Add additional information on debugging network issues --- Platform/OpenNetworkBoot/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Platform/OpenNetworkBoot/README.md b/Platform/OpenNetworkBoot/README.md index be450416965..587f757e595 100644 --- a/Platform/OpenNetworkBoot/README.md +++ b/Platform/OpenNetworkBoot/README.md @@ -69,6 +69,17 @@ or that a file does not exist at the specified URI on the server, or that the ce (if any) stored in NVRAM could not be used to validate an `https://` URI, or any one of a number of other similar problems. +> **Note 5**: During HTTP Boot, an initial error such as 'IP address not found' or 'Server response timeout', +even if preceded by the above message, may mean that no IP address was issued by DHCP, or +that the additional NBP (i.e. boot file) info requested over DHCP was not found. Using `dnsmasq` as the DHCP helper +with the logging options shown below can be helpful to resolve this: any DHCP request which reaches `dnsmasq` +will show a couple of log lines. If these are not seen during a network boot attempt then there is no chance of +`dnsmasq` responding, and network connectivity issues need to be resolved. (It is worth noting that unless blocked, +this DHCP request traffic will normally be broadcast between local networks.) If these log lines are seen but `dnsmasq` does not +additionally log that it is responding with NBP information, make sure that it is configured with the correct +subnetwork for the client machine. It can help to boot the client computer into an OS to confirm which subnetwork +it is on. + ## Identifying missing network boot drivers The `dh` command in UEFI Shell (e.g. `OpenShell` provided with