From 4cefa69254bd187eaefeba7be2afcece0592e4e0 Mon Sep 17 00:00:00 2001 From: Nhi Pham Date: Mon, 14 Oct 2024 10:17:51 +0700 Subject: [PATCH 01/10] JadePkg: Enable NETWORK_TLS_ENABLE by default This sets NETWORK_TLS_ENABLE to TRUE to enable HTTPS boot. Signed-off-by: Nhi Pham --- Platform/Ampere/JadePkg/Jade.dsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Platform/Ampere/JadePkg/Jade.dsc b/Platform/Ampere/JadePkg/Jade.dsc index 223184a7d3..7db21b5ea6 100644 --- a/Platform/Ampere/JadePkg/Jade.dsc +++ b/Platform/Ampere/JadePkg/Jade.dsc @@ -60,7 +60,7 @@ DEFINE NETWORK_IP6_ENABLE = FALSE DEFINE NETWORK_HTTP_BOOT_ENABLE = TRUE DEFINE NETWORK_ALLOW_HTTP_CONNECTIONS = TRUE - DEFINE NETWORK_TLS_ENABLE = FALSE + DEFINE NETWORK_TLS_ENABLE = TRUE DEFINE REDFISH_ENABLE = TRUE !include MdePkg/MdeLibs.dsc.inc From 3fe016fbe4e82d71beb80cd44b072dd7096ee5cc Mon Sep 17 00:00:00 2001 From: Nhi Pham Date: Mon, 14 Oct 2024 10:21:08 +0700 Subject: [PATCH 02/10] JadePkg: Enable SECURE_BOOT_ENABLE by default This sets SECURE_BOOT_ENABLE to TRUE in the DSC file to enable the UEFI secure boot configuration support by default. Signed-off-by: Nhi Pham --- Platform/Ampere/JadePkg/Jade.dsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Platform/Ampere/JadePkg/Jade.dsc b/Platform/Ampere/JadePkg/Jade.dsc index 7db21b5ea6..1ce9ff576f 100644 --- a/Platform/Ampere/JadePkg/Jade.dsc +++ b/Platform/Ampere/JadePkg/Jade.dsc @@ -49,7 +49,7 @@ # DEBUG_ERROR 0x80000000 // Error DEFINE DEBUG_PRINT_ERROR_LEVEL = 0x8000000F DEFINE FIRMWARE_VER = 0.01.001 - DEFINE SECURE_BOOT_ENABLE = FALSE + DEFINE SECURE_BOOT_ENABLE = TRUE DEFINE TPM2_ENABLE = TRUE DEFINE INCLUDE_TFTP_COMMAND = TRUE DEFINE PLATFORM_CONFIG_UUID = 84BC921F-9D4A-4D1D-A1A1-1AE13EDD07E5 From fbd38a173f8e4acd45f11b712a1f7df009aaebed Mon Sep 17 00:00:00 2001 From: Nhi Pham Date: Mon, 14 Oct 2024 11:16:30 +0700 Subject: [PATCH 03/10] JadePkg/SmbiosPlatformDxe: Fix uninitialized variable 'Status' This fixes the error 'uninitialized variable' catched by clang compilation. Signed-off-by: Nhi Pham --- .../Drivers/SmbiosPlatformDxe/Type07/PlatformCacheFunction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Platform/Ampere/JadePkg/Drivers/SmbiosPlatformDxe/Type07/PlatformCacheFunction.c b/Platform/Ampere/JadePkg/Drivers/SmbiosPlatformDxe/Type07/PlatformCacheFunction.c index c1cafe4103..3b33a5d167 100644 --- a/Platform/Ampere/JadePkg/Drivers/SmbiosPlatformDxe/Type07/PlatformCacheFunction.c +++ b/Platform/Ampere/JadePkg/Drivers/SmbiosPlatformDxe/Type07/PlatformCacheFunction.c @@ -164,5 +164,5 @@ SMBIOS_PLATFORM_DXE_TABLE_FUNCTION (PlatformCache) { } } - return Status; + return EFI_SUCCESS; } From 6220d1a4480943f7060d1505ce787474e737bcbe Mon Sep 17 00:00:00 2001 From: Nhi Pham Date: Tue, 15 Oct 2024 14:07:16 +0700 Subject: [PATCH 04/10] JadePkg: Add PlatformBootManagerDxe to customize boot options This introduces a PlatformBootManagerDxe driver, which implements the EDKII_PLATFORM_BOOT_MANAGER_PROTOCOL to enhance the presentation of boot options for the platform. The primary movitation behind this change is to provide a user-friendly boot experience by customizing the list of boot options generated by EDK2 boot manager. In addition, the driver filters out non-OS entries. This reduces unnecessary boot options, ensuring the boot menu is cleaner and less confusing. Signed-off-by: Nhi Pham --- Platform/Ampere/JadePkg/Jade.fdf | 1 + .../AmpereAltraPkg/AmpereAltraPkg.dsc.inc | 1 + .../PlatformBootManagerDxe.c | 968 ++++++++++++++++++ .../PlatformBootManagerDxe.inf | 41 + 4 files changed, 1011 insertions(+) create mode 100644 Silicon/Ampere/AmpereSiliconPkg/Drivers/PlatformBootManagerDxe/PlatformBootManagerDxe.c create mode 100644 Silicon/Ampere/AmpereSiliconPkg/Drivers/PlatformBootManagerDxe/PlatformBootManagerDxe.inf diff --git a/Platform/Ampere/JadePkg/Jade.fdf b/Platform/Ampere/JadePkg/Jade.fdf index 24099a1137..4c1c8c2a56 100644 --- a/Platform/Ampere/JadePkg/Jade.fdf +++ b/Platform/Ampere/JadePkg/Jade.fdf @@ -362,6 +362,7 @@ APRIORI DXE { INF MdeModulePkg/Universal/BdsDxe/BdsDxe.inf INF MdeModulePkg/Application/UiApp/UiApp.inf INF MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.inf + INF Silicon/Ampere/AmpereSiliconPkg/Drivers/PlatformBootManagerDxe/PlatformBootManagerDxe.inf # # Networking stack diff --git a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc index 97f93c0d7e..3aeb143279 100644 --- a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc +++ b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc @@ -848,3 +848,4 @@ !endif #$(INCLUDE_TFTP_COMMAND) EmbeddedPkg/Drivers/MemoryAttributeManagerDxe/MemoryAttributeManagerDxe.inf + Silicon/Ampere/AmpereSiliconPkg/Drivers/PlatformBootManagerDxe/PlatformBootManagerDxe.inf diff --git a/Silicon/Ampere/AmpereSiliconPkg/Drivers/PlatformBootManagerDxe/PlatformBootManagerDxe.c b/Silicon/Ampere/AmpereSiliconPkg/Drivers/PlatformBootManagerDxe/PlatformBootManagerDxe.c new file mode 100644 index 0000000000..01abd52b0e --- /dev/null +++ b/Silicon/Ampere/AmpereSiliconPkg/Drivers/PlatformBootManagerDxe/PlatformBootManagerDxe.c @@ -0,0 +1,968 @@ +/** @file + + Copyright (c) 2024, Ampere Computing LLC. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#pragma pack (1) +typedef struct { + CHAR16 *OSLoaderPath; + CHAR16 *OSLoaderName; +} OS_LOADER_DESC; +#pragma pack () + +CONST OS_LOADER_DESC KnownOsLoaderList[] = +{ + { + L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi", + L"Windows Boot Manager" + }, + { + L"\\EFI\\sles\\shim.efi", + L"SUSE" + }, + { + L"\\EFI\\fedora\\shimaa64.efi", + L"Fedora" + }, + { + L"\\EFI\\centos\\shimaa64.efi", + L"CentOS" + }, + { + L"\\EFI\\redhat\\shimaa64.efi", + L"Redhat" + }, + { + L"\\EFI\\ubuntu\\shimaa64.efi", + L"Ubuntu" + }, + { + L"\\EFI\\FreeBSD\\loader.efi", + L"FreeBSD" + }, + { + NULL, + NULL + } +}; + +CONST OS_LOADER_DESC DefaultUefiLoader = +{ + L"\\EFI\\BOOT\\bootaa64.efi", + L"UEFI OS" +}; + +CONST UINT16 UsbEnglishLang = 0x0409; + +extern EFI_GUID mBmAutoCreateBootOptionGuid; + +/** + Append a Boot Option to a Boot Options list. If the description + and the device path are null, this function will copy data from + BootOptionToCopy. If BootOptionToCopy is also null, then return + EFI_INVALID_PARAMETER. + + Note that, if BootOptionNumber is smaller than maximum order in + Boot Options List, the object is in this BootOptionNumber order + replaced .And the object which have higher order will be removed. + + @param BootOptionsList Pointer to List which Boot Option is added. + @param BootOptionNumber Boot Option order in list. + @param BootOptionToCopy Pointer to Boot Option copied when Desscription or Device not specific. + @param Description Description of Boot Option. + @param DevicePath Device path of Boot Option. + @param OptionalData Optional Data of Boot Option. + @param OptionalDataSize Optional data size of Boot Option. + + @retval EFI_SUCCESS Success to append Boot Option to list. + @retval other Some error occurs when executing this function. + **/ +EFI_STATUS +AppendBootOption ( + IN OUT EFI_BOOT_MANAGER_LOAD_OPTION **BootOptionsList, + IN UINTN BootOptionNumber, + IN OPTIONAL EFI_BOOT_MANAGER_LOAD_OPTION *BootOptionToCopy, + IN CHAR16 *Description, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN OPTIONAL UINT8 *OptionalData, + IN OPTIONAL UINT32 OptionalDataSize + ) +{ + EFI_STATUS Status; + + if ((BootOptionToCopy == NULL) && ((Description == NULL) || (DevicePath == NULL))) { + return EFI_INVALID_PARAMETER; + } + + *BootOptionsList = ReallocatePool ( + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (BootOptionNumber), + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (BootOptionNumber + sizeof (CHAR16)), + (VOID *)*BootOptionsList + ); + if (*BootOptionsList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if ((Description == NULL) || (DevicePath == NULL)) { + Status = EfiBootManagerInitializeLoadOption ( + *BootOptionsList + BootOptionNumber, + LoadOptionNumberUnassigned, + BootOptionToCopy->OptionType, + BootOptionToCopy->Attributes, + BootOptionToCopy->Description, + BootOptionToCopy->FilePath, + BootOptionToCopy->OptionalData, + BootOptionToCopy->OptionalDataSize + ); + } else { + Status = EfiBootManagerInitializeLoadOption ( + *BootOptionsList + BootOptionNumber, + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + LOAD_OPTION_ACTIVE, + Description, + DevicePath, + OptionalData, + OptionalDataSize + ); + } + + return Status; +} + +/** + Queries a Device path to determine if it supports the block io protocol. + + @param DevicePath Pointer to Device Path. + + @retval TRUE It supports the block io protocol. + @retval FALSE It does not support the block io protocol. + **/ +BOOLEAN +IsBlockIoProtocolInstalled ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_BLOCK_IO_PROTOCOL *BlkIo; + + ASSERT (DevicePath != NULL); + + Status = gBS->LocateDevicePath ( + &gEfiBlockIoProtocolGuid, + &DevicePath, + &Handle + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Status = gBS->HandleProtocol ( + Handle, + &gEfiBlockIoProtocolGuid, + (VOID **)&BlkIo + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + return TRUE; +} + +VOID +RemoveUnnecessarySpaces ( + IN OUT CHAR16 *String, + IN UINTN StringLength + ) +{ + UINTN Index; + UINTN ActualIndex; + + for (Index = 0, ActualIndex = 0; Index < StringLength; Index++) { + if ( (String[Index] != L' ') + || ((ActualIndex > 0) && (String[ActualIndex - 1] != L' '))) + { + String[ActualIndex++] = String[Index]; + } + } + + if (String[ActualIndex - 1] == L' ') { + String[ActualIndex - 1] = L'\0'; + } else { + String[ActualIndex] = L'\0'; + } +} + +/** + Return the boot description for NVME boot device. + + @param Handle Controller handle. + + @return The description string. +**/ +CHAR16 * +GetNvmeDescription ( + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + CHAR16 *Description; + CHAR16 *Char; + UINTN Index; + 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; + + 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 + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + 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) + sizeof (CHAR16)) * sizeof (CHAR16)); + if (Description != NULL) { + Char = Description; + for (Index = 0; Index < ARRAY_SIZE (ControllerData.Mn); Index++) { + *(Char++) = (CHAR16)ControllerData.Mn[Index]; + } + + RemoveUnnecessarySpaces (Description, ARRAY_SIZE (ControllerData.Mn)); + } + + return Description; +} + +/** + Try to get the controller's USB description. + + @param Handle Controller handle. + + @retval The description string. +**/ +CHAR16 * +GetUsbDescription ( + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + CHAR16 NullChar; + CHAR16 *Manufacturer; + CHAR16 *Product; + CHAR16 *Description; + UINTN DescMaxSize; + EFI_USB_DEVICE_DESCRIPTOR DevDesc; + EFI_USB_IO_PROTOCOL *UsbIo; + EFI_DEVICE_PATH_PROTOCOL *UsbDevicePath; + EFI_HANDLE UsbHandle; + + Status = gBS->HandleProtocol ( + Handle, + &gEfiDevicePathProtocolGuid, + (VOID **)&UsbDevicePath + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + Status = gBS->LocateDevicePath ( + &gEfiUsbIoProtocolGuid, + &UsbDevicePath, + &UsbHandle + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + Status = gBS->HandleProtocol ( + UsbHandle, + &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, + UsbEnglishLang, + DevDesc.StrManufacturer, + &Manufacturer + ); + if (EFI_ERROR (Status)) { + Manufacturer = &NullChar; + } + + Status = UsbIo->UsbGetStringDescriptor ( + UsbIo, + UsbEnglishLang, + DevDesc.StrProduct, + &Product + ); + if (EFI_ERROR (Status)) { + Product = &NullChar; + } + + if ((Manufacturer == &NullChar) && + (Product == &NullChar)) + { + return NULL; + } + + DescMaxSize = StrSize (Manufacturer) + StrSize (Product) + sizeof (CHAR16); + Description = AllocateZeroPool (DescMaxSize); + if (Description == NULL) { + DEBUG ((DEBUG_ERROR, "%a: %d: Out of Resources\n", __func__, __LINE__)); + return NULL; + } + + StrCatS (Description, DescMaxSize / sizeof (CHAR16), Manufacturer); + StrCatS (Description, DescMaxSize / sizeof (CHAR16), L" "); + StrCatS (Description, DescMaxSize / sizeof (CHAR16), Product); + + if (Manufacturer != &NullChar) { + FreePool (Manufacturer); + } + + if (Product != &NullChar) { + FreePool (Product); + } + + RemoveUnnecessarySpaces (Description, DescMaxSize / sizeof (CHAR16)); + + return Description; +} + +/** + Check if Bootable Image exists or not based on file path. + + The SimpleFileSystem protocol is the programmatic access to the FAT (12,16,32) + file system specified in UEFI 2.8. It can also be used to abstract a file + system other than FAT. + + SimpleFileSystem protocol is used to open a device volume and return a + File protocol that provides interfaces to access files on a device volume. + + Use File_protocol.Open() with read mode, if it returns EFI_SUCCESS + it means file existing; return another, refer to UEFI 2.8 section 13.5. + + @param Handle The handle for Block IO + @param Loader OS loader description + + @retval TRUE File exists + @retval FALSE File does not exists +**/ +BOOLEAN +UefiBootableImageExists ( + IN EFI_HANDLE Handle, + IN CONST OS_LOADER_DESC Loader + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileFs; + EFI_FILE_PROTOCOL *Dir; + EFI_FILE_PROTOCOL *File; + + Status = gBS->HandleProtocol ( + Handle, + &gEfiSimpleFileSystemProtocolGuid, + (VOID *)&FileFs + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Status = FileFs->OpenVolume ( + FileFs, + &Dir + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Status = Dir->Open ( + Dir, + &File, + Loader.OSLoaderPath, + EFI_FILE_MODE_READ, + EFI_FILE_RESERVED + ); + if (!EFI_ERROR (Status)) { + return TRUE; + } + + return FALSE; +} + +/** + Compare two device path nodes from the end node. + + @param DevicePath1 Pointer to Device path protocol 1 + @param DevicePath2 Pointer to Device path protocol 2 + + @retval TRUE They are identical + @retval FALSE They are not identical. +**/ +BOOLEAN +CompareDevicePath ( + EFI_DEVICE_PATH_PROTOCOL *DevicePath1, + EFI_DEVICE_PATH_PROTOCOL *DevicePath2 + ) +{ + UINTN Size1; + UINTN Size2; + EFI_DEVICE_PATH_PROTOCOL *DevicePathList1[3]; + EFI_DEVICE_PATH_PROTOCOL *DevicePathList2[3]; + + DevicePathList1[1] = DevicePath1; + do { + DevicePathList1[0] = DevicePathList1[1]; + DevicePathList1[1] = NextDevicePathNode (DevicePathList1[0]); + DevicePathList1[2] = IsDevicePathEnd (DevicePathList1[1]) ? DevicePathList1[1] : NextDevicePathNode (DevicePathList1[1]); + } while (!IsDevicePathEnd (DevicePathList1[2])); + + DevicePathList2[1] = DevicePath2; + do { + DevicePathList2[0] = DevicePathList2[1]; + DevicePathList2[1] = NextDevicePathNode (DevicePathList2[0]); + DevicePathList2[2] = IsDevicePathEnd (DevicePathList2[1]) ? DevicePathList2[1] : NextDevicePathNode (DevicePathList2[1]); + } while (!IsDevicePathEnd (DevicePathList2[2])); + + Size1 = GetDevicePathSize (DevicePathList1[0]); + Size2 = GetDevicePathSize (DevicePathList2[0]); + + if (Size1 != Size2) { + return FALSE; + } + + if (CompareMem (DevicePathList1[0], DevicePathList2[0], Size1) != 0) { + return FALSE; + } + + return TRUE; +} + +/** + Check Boot Option, which has a specific Device path, whether it exists. + + @param DevicePath Pointer to Device Path Protocol of Boot Option + + @retval TRUE Boot Option for device path exists + @retval FALSE Boot Option for device path does not exists + **/ +BOOLEAN +IsBootOptionAvailable ( + EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + UINTN Index; + UINTN NvBootOptionCount; + EFI_BOOT_MANAGER_LOAD_OPTION *NvBootOptions; + + NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot); + + for (Index = 0; Index < NvBootOptionCount; Index++) { + // + // Compare device path + // + if (CompareDevicePath (DevicePath, NvBootOptions[Index].FilePath)) { + // + // With Boot Option which is not automatically created, + // mark it as already existing, do not need to create more. + // + if (NvBootOptions[Index].OptionalData == NULL) { + return TRUE; + } + + // + // Boot Manager will eliminate all Boot Options + // which are automatically created if in the next cycle, + // they are not created again. So we need to create + // them again. + // + if (CompareGuid ( + (CONST GUID *)&mBmAutoCreateBootOptionGuid, + (CONST GUID *)NvBootOptions[Index].OptionalData + )) + { + return FALSE; + } + + return TRUE; + } + } + + return FALSE; +} + +EFI_STATUS +CreateNewBootOption ( + IN EFI_HANDLE Handle, + IN OS_LOADER_DESC OsDescription, + OUT EFI_BOOT_MANAGER_LOAD_OPTION **BootOptionList, + OUT UINTN *BootOptionCount + ) +{ + EFI_STATUS Status; + CHAR16 BootOptionDescription[128]; + CHAR16 *DiskName; + EFI_DEVICE_PATH_PROTOCOL *BootOptionDevicePath; + EFI_DEVICE_PATH_PROTOCOL *Node; + + DiskName = NULL; + + if (!UefiBootableImageExists (Handle, OsDescription)) { + return EFI_NOT_FOUND; + } + + BootOptionDevicePath = FileDevicePath ( + Handle, + OsDescription.OSLoaderPath + ); + if (BootOptionDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Skip if Boot Option already exists + // + if (IsBootOptionAvailable (BootOptionDevicePath)) { + return EFI_SUCCESS; + } + + // + // Create boot option name into format of "OS Name (Disk Name)" + // + Node = BootOptionDevicePath; + while (Node->Type != END_DEVICE_PATH_TYPE) { + if (Node->Type == MESSAGING_DEVICE_PATH) { + switch (Node->SubType) { + case MSG_NVME_NAMESPACE_DP: + DiskName = GetNvmeDescription (Handle); + break; + + case MSG_USB_DP: + DiskName = GetUsbDescription (Handle); + break; + + default: + DiskName = NULL; + break; + } + + break; + } + + Node = NextDevicePathNode (Node); + } + + if (DiskName != NULL) { + UnicodeSPrint ( + BootOptionDescription, + sizeof (BootOptionDescription), + L"%s (%s)", + OsDescription.OSLoaderName, + DiskName + ); + FreePool (DiskName); + } else { + UnicodeSPrint ( + BootOptionDescription, + sizeof (BootOptionDescription), + L"%s", + OsDescription.OSLoaderName + ); + } + + Status = AppendBootOption ( + BootOptionList, + *BootOptionCount, + NULL, + BootOptionDescription, + BootOptionDevicePath, + NULL, + 0 + ); + if (!EFI_ERROR (Status)) { + (*BootOptionCount)++; + } + + if (BootOptionDevicePath != NULL) { + FreePool (BootOptionDevicePath); + } + + return Status; +} + +EFI_STATUS +AddCustomBootOptionToList ( + OUT EFI_BOOT_MANAGER_LOAD_OPTION **BootOptionList, + OUT UINTN *BootOptionCount + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN OrderInList; + UINTN HandleCount; + BOOLEAN NeedDefaultBoot; + EFI_HANDLE *Handle; + + Handle = NULL; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleFileSystemProtocolGuid, + NULL, + &HandleCount, + &Handle + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to locate handle buffer for Simple FS Protocol - %r\n", Status)); + return Status; + } + + for (Index = 0; Index < HandleCount; Index++) { + for (OrderInList = 0, NeedDefaultBoot = TRUE; + KnownOsLoaderList[OrderInList].OSLoaderPath != NULL; + OrderInList++) + { + Status = CreateNewBootOption ( + Handle[Index], + KnownOsLoaderList[OrderInList], + BootOptionList, + BootOptionCount + ); + if (!EFI_ERROR (Status)) { + NeedDefaultBoot = FALSE; + } + } + + if (NeedDefaultBoot) { + CreateNewBootOption ( + Handle[Index], + DefaultUefiLoader, + BootOptionList, + BootOptionCount + ); + } + } + + if (Handle != NULL) { + FreePool (Handle); + } + + return EFI_SUCCESS; +} + +/** + Caching the Boot Options valid enumerated by default. + + @param LegacyBootOptionList The list of Boot Options enumerated by default + @param LegacyBootOptionListCount The number of Boot Option in legacy list + @param BootOptionList The list that legacy Boot Options valid stored + @param BootOptionCount The number of Boot Option in new list. + + @retval VOID +**/ +VOID +CacheLegacyBootOptions ( + IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *LegacyBootOptionList, + IN CONST UINTN LegacyBootOptionListCount, + OUT EFI_BOOT_MANAGER_LOAD_OPTION **BootOptionList, + IN OUT UINTN *BootOptionListCount + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN NVBootOptionCount; + UINTN CacheBootOptionCount; + EFI_BOOT_MANAGER_LOAD_OPTION *NVBootOptions; + + NVBootOptionCount = 0; + NVBootOptions = NULL; + CacheBootOptionCount = *BootOptionListCount; + + // + // Load Boot Options have been stored in the NVRAM + // + NVBootOptions = EfiBootManagerGetLoadOptions (&NVBootOptionCount, LoadOptionTypeBoot); + + for (Index = 0; Index < LegacyBootOptionListCount; Index++) { + if ( (EfiBootManagerFindLoadOption ( + &LegacyBootOptionList[Index], + (CONST EFI_BOOT_MANAGER_LOAD_OPTION *)NVBootOptions, + NVBootOptionCount + ) != -1) + && (EfiBootManagerFindLoadOption ( + &LegacyBootOptionList[Index], + (CONST EFI_BOOT_MANAGER_LOAD_OPTION *)*BootOptionList, + CacheBootOptionCount + ) == -1) + ) + { + Status = AppendBootOption ( + BootOptionList, + *BootOptionListCount, + (EFI_BOOT_MANAGER_LOAD_OPTION *)&LegacyBootOptionList[Index], + NULL, + NULL, + NULL, + 0 + ); + if (!EFI_ERROR (Status)) { + (*BootOptionListCount)++; + } + } + } +} + +EFI_STATUS +RefreshAllBootOptions ( + IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions, + IN CONST UINTN BootOptionsCount, + OUT EFI_BOOT_MANAGER_LOAD_OPTION **UpdatedBootOptions, + OUT UINTN *UpdatedBootOptionsCount + ) +{ + EFI_STATUS Status; + BOOLEAN DoCustomBootOptionsAdd; + UINTN Index; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptionTemp; + + *UpdatedBootOptions = NULL; + *UpdatedBootOptionsCount = 0; + DoCustomBootOptionsAdd = TRUE; + + if ((BootOptionsCount == 0) || (BootOptions == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Add custom Boot Options + // + Status = AddCustomBootOptionToList ( + UpdatedBootOptions, + UpdatedBootOptionsCount + ); + if (EFI_ERROR (Status)) { + if (*UpdatedBootOptions != NULL) { + FreePool (*UpdatedBootOptions); + } + + *UpdatedBootOptions = NULL; + *UpdatedBootOptionsCount = 0; + DoCustomBootOptionsAdd = FALSE; + } + + // + // Collect Boot Options that are enumerated by Boot Manager + // + for (Index = 0; Index < BootOptionsCount; Index++) { + // + // If custom boot options are successfully added, bypass those options + // based on Block IO Protocol + // + if (IsBlockIoProtocolInstalled (BootOptions[Index].FilePath) && DoCustomBootOptionsAdd) { + continue; + } + + Status = AppendBootOption ( + UpdatedBootOptions, + *UpdatedBootOptionsCount, + (EFI_BOOT_MANAGER_LOAD_OPTION *)&BootOptions[Index], + NULL, + NULL, + NULL, + 0 + ); + if (!EFI_ERROR (Status)) { + (*UpdatedBootOptionsCount)++; + } + } + + CacheLegacyBootOptions ( + BootOptions, + BootOptionsCount, + UpdatedBootOptions, + UpdatedBootOptionsCount + ); + + for (Index = 0, BootOptionTemp = *UpdatedBootOptions; + Index < *UpdatedBootOptionsCount; + Index++) + { + if (BootOptionTemp[Index].OptionalData != NULL) { + FreePool (BootOptionTemp[Index].OptionalData); + } + + BootOptionTemp[Index].OptionalData = AllocateCopyPool (sizeof (EFI_GUID), &mBmAutoCreateBootOptionGuid); + BootOptionTemp[Index].OptionalDataSize = sizeof (EFI_GUID); + } + + return Status; +} + +EDKII_PLATFORM_BOOT_MANAGER_PROTOCOL mPlatformBootManager = { + EDKII_PLATFORM_BOOT_MANAGER_PROTOCOL_REVISION, + RefreshAllBootOptions +}; + +EFI_STATUS +PlatformBootManagerInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN HandleCount; + EDKII_PLATFORM_BOOT_MANAGER_PROTOCOL *Interface; + EFI_HANDLE *Buffer, Handle; + + Buffer = NULL; + Handle = NULL; + // + // Check existing protocol + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEdkiiPlatformBootManagerProtocolGuid, + NULL, + &HandleCount, + &Buffer + ); + if (!EFI_ERROR (Status)) { + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + Buffer[Index], + &gEdkiiPlatformBootManagerProtocolGuid, + (VOID **)&Interface + ); + if (EFI_ERROR (Status)) { + continue; + } + + Status = gBS->UninstallProtocolInterface ( + Buffer[Index], + &gEdkiiPlatformBootManagerProtocolGuid, + (VOID *)Interface + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to uninstall existing PlatformBootManagerProtocol - %r\n", Status)); + goto Done; + } + } + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEdkiiPlatformBootManagerProtocolGuid, + &mPlatformBootManager, + NULL + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to install PlatformBootManager Protocol - %r\n", Status)); + } + +Done: + if (Buffer != NULL) { + FreePool (Buffer); + } + + return Status; +} diff --git a/Silicon/Ampere/AmpereSiliconPkg/Drivers/PlatformBootManagerDxe/PlatformBootManagerDxe.inf b/Silicon/Ampere/AmpereSiliconPkg/Drivers/PlatformBootManagerDxe/PlatformBootManagerDxe.inf new file mode 100644 index 0000000000..cb84fa1a54 --- /dev/null +++ b/Silicon/Ampere/AmpereSiliconPkg/Drivers/PlatformBootManagerDxe/PlatformBootManagerDxe.inf @@ -0,0 +1,41 @@ +## @file +# +# Copyright (c) 2024, Ampere Computing LLC. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = PlatformBootManagerDxe + FILE_GUID = 6D37777E-F9CF-4FFE-8743-6666FB321B05 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = PlatformBootManagerInitialize + +[Sources] + PlatformBootManagerDxe.c + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseMemoryLib + DebugLib + DevicePathLib + MemoryAllocationLib + PrintLib + UefiBootManagerLib + UefiDriverEntryPoint + UefiLib + +[Protocols] + gEdkiiPlatformBootManagerProtocolGuid + gEfiBlockIoProtocolGuid + gEfiSimpleFileSystemProtocolGuid + gEfiUsbIoProtocolGuid + +[Depex] + TRUE From c81825225fa2ce9566da8d958074d77632bdcdb9 Mon Sep 17 00:00:00 2001 From: Nhi Pham Date: Wed, 16 Oct 2024 10:53:57 +0700 Subject: [PATCH 05/10] AmpereSiliconPkg: Add Ampere defined Variables GUID This introduces a GUID for Ampere defined variables. Signed-off-by: Nhi Pham --- .../AmpereSiliconPkg/AmpereSiliconPkg.dec | 5 +++++ .../Include/Guid/AmpereVariableGuid.h | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 Silicon/Ampere/AmpereSiliconPkg/Include/Guid/AmpereVariableGuid.h diff --git a/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec b/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec index 92d37bc727..7a572dbc40 100644 --- a/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec +++ b/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec @@ -53,6 +53,11 @@ ## Include/Guid/BmcConfigHii.h gBmcConfigFormSetGuid = { 0xC4D6ED50, 0x769D, 0x4319, { 0xEB, 0xB7, 0xCC, 0xDD, 0xC8, 0x9D, 0x3D, 0x2D } } + # + # GUID for the Ampere defined UEFI variables + ## Include/Guid/AmpereVariableGuid.h + gAmpereVariableGuid = { 0x6B9F9381, 0xB1D0, 0x4991, { 0xBC, 0x4A, 0x17, 0xBC, 0x6B, 0x5B, 0xB0, 0x81 } } + [Ppis] [PcdsFixedAtBuild] diff --git a/Silicon/Ampere/AmpereSiliconPkg/Include/Guid/AmpereVariableGuid.h b/Silicon/Ampere/AmpereSiliconPkg/Include/Guid/AmpereVariableGuid.h new file mode 100644 index 0000000000..694c85914f --- /dev/null +++ b/Silicon/Ampere/AmpereSiliconPkg/Include/Guid/AmpereVariableGuid.h @@ -0,0 +1,19 @@ +/** @file + + Copyright (c) 2024, Ampere Computing LLC. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef AMPERE_VARIABLE_GUID_H_ +#define AMPERE_VARIABLE_GUID_H_ + +#define AMPERE_VARIABLE_GUID \ + { \ + { 0x6B9F9381, 0xB1D0, 0x4991, { 0xBC, 0x4A, 0x17, 0xBC, 0x6B, 0x5B, 0xB0, 0x81 } } \ + } + +extern EFI_GUID gAmpereVariableGuid; + +#endif /* AMPERE_VARIABLE_GUID_H_ */ From 193cf36e13f8e8fc83628015bed1794c6c9480c0 Mon Sep 17 00:00:00 2001 From: Nhi Pham Date: Wed, 16 Oct 2024 10:57:09 +0700 Subject: [PATCH 06/10] AmpereSiliconPkg: Add Ampere defined After Console Event GUID This introduces a GUID for the Ampere defined AfterConsole event, which serves as a notification that AfterConsole is ready before ReadyToBoot. This event is used to handle the IPMI boot order change request from BMC. Signed-off-by: Nhi Pham --- .../AmpereSiliconPkg/AmpereSiliconPkg.dec | 5 +++++ .../Include/Guid/AmpereEventAfterConsole.h | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 Silicon/Ampere/AmpereSiliconPkg/Include/Guid/AmpereEventAfterConsole.h diff --git a/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec b/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec index 7a572dbc40..28c1c1905e 100644 --- a/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec +++ b/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec @@ -53,6 +53,11 @@ ## Include/Guid/BmcConfigHii.h gBmcConfigFormSetGuid = { 0xC4D6ED50, 0x769D, 0x4319, { 0xEB, 0xB7, 0xCC, 0xDD, 0xC8, 0x9D, 0x3D, 0x2D } } + # + # GUID for the After Console event + ## Include/Guid/AmpereEventAfterConsole.h + gAmpereEventAfterConsoleGuid = { 0x7CCB608E, 0x428C, 0x44AE, { 0x95, 0x06, 0x58, 0x58, 0x2F, 0x0E, 0x8F, 0xB1 } } + # # GUID for the Ampere defined UEFI variables ## Include/Guid/AmpereVariableGuid.h diff --git a/Silicon/Ampere/AmpereSiliconPkg/Include/Guid/AmpereEventAfterConsole.h b/Silicon/Ampere/AmpereSiliconPkg/Include/Guid/AmpereEventAfterConsole.h new file mode 100644 index 0000000000..8ed1e27ef9 --- /dev/null +++ b/Silicon/Ampere/AmpereSiliconPkg/Include/Guid/AmpereEventAfterConsole.h @@ -0,0 +1,19 @@ +/** @file + + Copyright (c) 2024, Ampere Computing LLC. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef AMPERE_EVENT_AFTER_CONSOLE_H_ +#define AMPERE_EVENT_AFTER_CONSOLE_H_ + +#define AMPERE_EVENT_AFTER_CONSOLE_GUID \ + { \ + 0x7CCB608E, 0x428C, 0x44AE, { 0x95, 0x06, 0x58, 0x58, 0x2F, 0x0E, 0x8F, 0xB1 } \ + } + +extern EFI_GUID gAmpereEventAfterConsoleGuid; + +#endif /* AMPERE_EVENT_AFTER_CONSOLE_H_ */ From f9be6996ca0e83f9fb2f7e541cf67d65c2c35a79 Mon Sep 17 00:00:00 2001 From: Nhi Pham Date: Wed, 16 Oct 2024 11:07:52 +0700 Subject: [PATCH 07/10] AmpereSiliconPkg: Clone PlatformBootManagerLib from ArmPkg This clones the ArmPkg's PlatformBootManagerLib to modify for platform specific boot handler. The initial application is to create the Ampere defined AfterConsole event for integrating the IPMI boot order change handler. NOTE: There is no Ampere's modification in this patch. Signed-off-by: Nhi Pham --- .../AmpereAltraPkg/AmpereAltraPkg.dsc.inc | 2 +- .../PlatformBootManagerLib/PlatformBm.c | 1183 +++++++++++++++++ .../PlatformBootManagerLib/PlatformBm.h | 53 + .../PlatformBootManagerLib.inf | 95 ++ 4 files changed, 1332 insertions(+), 1 deletion(-) create mode 100644 Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.c create mode 100644 Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.h create mode 100644 Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf diff --git a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc index 3aeb143279..4c4d91fcc9 100644 --- a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc +++ b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc @@ -125,7 +125,7 @@ # BDS Libraries # UefiBootManagerLib|MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf - PlatformBootManagerLib|ArmPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf + PlatformBootManagerLib|Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf BootLogoLib|MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf # diff --git a/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.c b/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.c new file mode 100644 index 0000000000..ea093bb725 --- /dev/null +++ b/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.c @@ -0,0 +1,1183 @@ +/** @file + Implementation for PlatformBootManagerLib library class interfaces. + + Copyright (C) 2015-2016, Red Hat, Inc. + Copyright (c) 2014 - 2023, Arm Ltd. All rights reserved.
+ Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2016, Linaro Ltd. All rights reserved.
+ Copyright (c) 2021, Semihalf All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PlatformBm.h" + +#define DP_NODE_LEN(Type) { (UINT8)sizeof (Type), (UINT8)(sizeof (Type) >> 8) } + +#pragma pack (1) +typedef struct { + VENDOR_DEVICE_PATH SerialDxe; + UART_DEVICE_PATH Uart; + VENDOR_DEFINED_DEVICE_PATH TermType; + EFI_DEVICE_PATH_PROTOCOL End; +} PLATFORM_SERIAL_CONSOLE; +#pragma pack () + +STATIC PLATFORM_SERIAL_CONSOLE mSerialConsole = { + // + // VENDOR_DEVICE_PATH SerialDxe + // + { + { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, DP_NODE_LEN (VENDOR_DEVICE_PATH) }, + EDKII_SERIAL_PORT_LIB_VENDOR_GUID + }, + + // + // UART_DEVICE_PATH Uart + // + { + { MESSAGING_DEVICE_PATH, MSG_UART_DP, DP_NODE_LEN (UART_DEVICE_PATH) }, + 0, // Reserved + FixedPcdGet64 (PcdUartDefaultBaudRate), // BaudRate + FixedPcdGet8 (PcdUartDefaultDataBits), // DataBits + FixedPcdGet8 (PcdUartDefaultParity), // Parity + FixedPcdGet8 (PcdUartDefaultStopBits) // StopBits + }, + + // + // VENDOR_DEFINED_DEVICE_PATH TermType + // + { + { + MESSAGING_DEVICE_PATH, MSG_VENDOR_DP, + DP_NODE_LEN (VENDOR_DEFINED_DEVICE_PATH) + } + // + // Guid to be filled in dynamically + // + }, + + // + // EFI_DEVICE_PATH_PROTOCOL End + // + { + END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, + DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL) + } +}; + +#pragma pack (1) +typedef struct { + USB_CLASS_DEVICE_PATH Keyboard; + EFI_DEVICE_PATH_PROTOCOL End; +} PLATFORM_USB_KEYBOARD; +#pragma pack () + +STATIC PLATFORM_USB_KEYBOARD mUsbKeyboard = { + // + // USB_CLASS_DEVICE_PATH Keyboard + // + { + { + MESSAGING_DEVICE_PATH, MSG_USB_CLASS_DP, + DP_NODE_LEN (USB_CLASS_DEVICE_PATH) + }, + 0xFFFF, // VendorId: any + 0xFFFF, // ProductId: any + 3, // DeviceClass: HID + 1, // DeviceSubClass: boot + 1 // DeviceProtocol: keyboard + }, + + // + // EFI_DEVICE_PATH_PROTOCOL End + // + { + END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, + DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL) + } +}; + +/** + Check if the handle satisfies a particular condition. + + @param[in] Handle The handle to check. + @param[in] ReportText A caller-allocated string passed in for reporting + purposes. It must never be NULL. + + @retval TRUE The condition is satisfied. + @retval FALSE Otherwise. This includes the case when the condition could not + be fully evaluated due to an error. +**/ +typedef +BOOLEAN +(EFIAPI *FILTER_FUNCTION)( + IN EFI_HANDLE Handle, + IN CONST CHAR16 *ReportText + ); + +/** + Process a handle. + + @param[in] Handle The handle to process. + @param[in] ReportText A caller-allocated string passed in for reporting + purposes. It must never be NULL. +**/ +typedef +VOID +(EFIAPI *CALLBACK_FUNCTION)( + IN EFI_HANDLE Handle, + IN CONST CHAR16 *ReportText + ); + +/** + Locate all handles that carry the specified protocol, filter them with a + callback function, and pass each handle that passes the filter to another + callback. + + @param[in] ProtocolGuid The protocol to look for. + + @param[in] Filter The filter function to pass each handle to. If this + parameter is NULL, then all handles are processed. + + @param[in] Process The callback function to pass each handle to that + clears the filter. +**/ +STATIC +VOID +FilterAndProcess ( + IN EFI_GUID *ProtocolGuid, + IN FILTER_FUNCTION Filter OPTIONAL, + IN CALLBACK_FUNCTION Process + ) +{ + EFI_STATUS Status; + EFI_HANDLE *Handles; + UINTN NoHandles; + UINTN Idx; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + ProtocolGuid, + NULL /* SearchKey */, + &NoHandles, + &Handles + ); + if (EFI_ERROR (Status)) { + // + // This is not an error, just an informative condition. + // + DEBUG (( + DEBUG_VERBOSE, + "%a: %g: %r\n", + __func__, + ProtocolGuid, + Status + )); + return; + } + + ASSERT (NoHandles > 0); + for (Idx = 0; Idx < NoHandles; ++Idx) { + CHAR16 *DevicePathText; + STATIC CHAR16 Fallback[] = L""; + + // + // The ConvertDevicePathToText() function handles NULL input transparently. + // + DevicePathText = ConvertDevicePathToText ( + DevicePathFromHandle (Handles[Idx]), + FALSE, // DisplayOnly + FALSE // AllowShortcuts + ); + if (DevicePathText == NULL) { + DevicePathText = Fallback; + } + + if ((Filter == NULL) || Filter (Handles[Idx], DevicePathText)) { + Process (Handles[Idx], DevicePathText); + } + + if (DevicePathText != Fallback) { + FreePool (DevicePathText); + } + } + + gBS->FreePool (Handles); +} + +/** + This FILTER_FUNCTION checks if a handle corresponds to a PCI display device. +**/ +STATIC +BOOLEAN +EFIAPI +IsPciDisplay ( + IN EFI_HANDLE Handle, + IN CONST CHAR16 *ReportText + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + + Status = gBS->HandleProtocol ( + Handle, + &gEfiPciIoProtocolGuid, + (VOID **)&PciIo + ); + if (EFI_ERROR (Status)) { + // + // This is not an error worth reporting. + // + return FALSE; + } + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0 /* Offset */, + sizeof Pci / sizeof (UINT32), + &Pci + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: %s: %r\n", __func__, ReportText, Status)); + return FALSE; + } + + return IS_PCI_DISPLAY (&Pci); +} + +/** + This FILTER_FUNCTION checks if a handle corresponds to a non-discoverable + USB host controller. +**/ +STATIC +BOOLEAN +EFIAPI +IsUsbHost ( + IN EFI_HANDLE Handle, + IN CONST CHAR16 *ReportText + ) +{ + NON_DISCOVERABLE_DEVICE *Device; + EFI_STATUS Status; + + Status = gBS->HandleProtocol ( + Handle, + &gEdkiiNonDiscoverableDeviceProtocolGuid, + (VOID **)&Device + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + if (CompareGuid (Device->Type, &gEdkiiNonDiscoverableUhciDeviceGuid) || + CompareGuid (Device->Type, &gEdkiiNonDiscoverableEhciDeviceGuid) || + CompareGuid (Device->Type, &gEdkiiNonDiscoverableXhciDeviceGuid)) + { + return TRUE; + } + + return FALSE; +} + +/** + This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking + the matching driver to produce all first-level child handles. +**/ +STATIC +VOID +EFIAPI +Connect ( + IN EFI_HANDLE Handle, + IN CONST CHAR16 *ReportText + ) +{ + EFI_STATUS Status; + + Status = gBS->ConnectController ( + Handle, // ControllerHandle + NULL, // DriverImageHandle + NULL, // RemainingDevicePath -- produce all children + FALSE // Recursive + ); + DEBUG (( + EFI_ERROR (Status) ? DEBUG_ERROR : DEBUG_VERBOSE, + "%a: %s: %r\n", + __func__, + ReportText, + Status + )); +} + +/** + This CALLBACK_FUNCTION retrieves the EFI_DEVICE_PATH_PROTOCOL from the + handle, and adds it to ConOut and ErrOut. +**/ +STATIC +VOID +EFIAPI +AddOutput ( + IN EFI_HANDLE Handle, + IN CONST CHAR16 *ReportText + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + DevicePath = DevicePathFromHandle (Handle); + if (DevicePath == NULL) { + DEBUG (( + DEBUG_ERROR, + "%a: %s: handle %p: device path not found\n", + __func__, + ReportText, + Handle + )); + return; + } + + Status = EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: %s: adding to ConOut: %r\n", + __func__, + ReportText, + Status + )); + return; + } + + Status = EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: %s: adding to ErrOut: %r\n", + __func__, + ReportText, + Status + )); + return; + } + + DEBUG (( + DEBUG_VERBOSE, + "%a: %s: added to ConOut and ErrOut\n", + __func__, + ReportText + )); +} + +STATIC +VOID +PlatformRegisterFvBootOption ( + CONST EFI_GUID *FileGuid, + CHAR16 *Description, + UINT32 Attributes, + EFI_INPUT_KEY *Key + ) +{ + EFI_STATUS Status; + INTN OptionIndex; + EFI_BOOT_MANAGER_LOAD_OPTION NewOption; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINTN BootOptionCount; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + Status = gBS->HandleProtocol ( + gImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); + ASSERT_EFI_ERROR (Status); + + EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid); + DevicePath = DevicePathFromHandle (LoadedImage->DeviceHandle); + ASSERT (DevicePath != NULL); + DevicePath = AppendDevicePathNode ( + DevicePath, + (EFI_DEVICE_PATH_PROTOCOL *)&FileNode + ); + ASSERT (DevicePath != NULL); + + Status = EfiBootManagerInitializeLoadOption ( + &NewOption, + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + Attributes, + Description, + DevicePath, + NULL, + 0 + ); + ASSERT_EFI_ERROR (Status); + FreePool (DevicePath); + + BootOptions = EfiBootManagerGetLoadOptions ( + &BootOptionCount, + LoadOptionTypeBoot + ); + + OptionIndex = EfiBootManagerFindLoadOption ( + &NewOption, + BootOptions, + BootOptionCount + ); + + if (OptionIndex == -1) { + Status = EfiBootManagerAddLoadOptionVariable (&NewOption, MAX_UINTN); + ASSERT_EFI_ERROR (Status); + Status = EfiBootManagerAddKeyOptionVariable ( + NULL, + (UINT16)NewOption.OptionNumber, + 0, + Key, + NULL + ); + ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED); + } + + EfiBootManagerFreeLoadOption (&NewOption); + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); +} + +/** Boot a Fv Boot Option. + + This function is useful for booting the UEFI Shell as it is loaded + as a non active boot option. + + @param[in] FileGuid The File GUID. + @param[in] Description String describing the Boot Option. + +**/ +STATIC +VOID +PlatformBootFvBootOption ( + IN CONST EFI_GUID *FileGuid, + IN CHAR16 *Description + ) +{ + EFI_STATUS Status; + EFI_BOOT_MANAGER_LOAD_OPTION NewOption; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + Status = gBS->HandleProtocol ( + gImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); + ASSERT_EFI_ERROR (Status); + + // + // The UEFI Shell was registered in PlatformRegisterFvBootOption () + // previously, thus it must still be available in this FV. + // + EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid); + DevicePath = DevicePathFromHandle (LoadedImage->DeviceHandle); + ASSERT (DevicePath != NULL); + DevicePath = AppendDevicePathNode ( + DevicePath, + (EFI_DEVICE_PATH_PROTOCOL *)&FileNode + ); + ASSERT (DevicePath != NULL); + + Status = EfiBootManagerInitializeLoadOption ( + &NewOption, + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + LOAD_OPTION_ACTIVE, + Description, + DevicePath, + NULL, + 0 + ); + ASSERT_EFI_ERROR (Status); + FreePool (DevicePath); + + EfiBootManagerBoot (&NewOption); +} + +STATIC +VOID +GetPlatformOptions ( + VOID + ) +{ + EFI_STATUS Status; + EFI_BOOT_MANAGER_LOAD_OPTION *CurrentBootOptions; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + EFI_INPUT_KEY *BootKeys; + PLATFORM_BOOT_MANAGER_PROTOCOL *PlatformBootManager; + UINTN CurrentBootOptionCount; + UINTN Index; + UINTN BootCount; + + Status = gBS->LocateProtocol ( + &gPlatformBootManagerProtocolGuid, + NULL, + (VOID **)&PlatformBootManager + ); + if (EFI_ERROR (Status)) { + return; + } + + Status = PlatformBootManager->GetPlatformBootOptionsAndKeys ( + &BootCount, + &BootOptions, + &BootKeys + ); + if (EFI_ERROR (Status)) { + return; + } + + // + // Fetch the existent boot options. If there are none, CurrentBootCount + // will be zeroed. + // + CurrentBootOptions = EfiBootManagerGetLoadOptions ( + &CurrentBootOptionCount, + LoadOptionTypeBoot + ); + // + // Process the platform boot options. + // + for (Index = 0; Index < BootCount; Index++) { + INTN Match; + UINTN BootOptionNumber; + + // + // If there are any preexistent boot options, and the subject platform boot + // option is already among them, then don't try to add it. Just get its + // assigned boot option number so we can associate a hotkey with it. Note + // that EfiBootManagerFindLoadOption() deals fine with (CurrentBootOptions + // == NULL) if (CurrentBootCount == 0). + // + Match = EfiBootManagerFindLoadOption ( + &BootOptions[Index], + CurrentBootOptions, + CurrentBootOptionCount + ); + if (Match >= 0) { + BootOptionNumber = CurrentBootOptions[Match].OptionNumber; + } else { + // + // Add the platform boot options as a new one, at the end of the boot + // order. Note that if the platform provided this boot option with an + // unassigned option number, then the below function call will assign a + // number. + // + Status = EfiBootManagerAddLoadOptionVariable ( + &BootOptions[Index], + MAX_UINTN + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: failed to register \"%s\": %r\n", + __func__, + BootOptions[Index].Description, + Status + )); + continue; + } + + BootOptionNumber = BootOptions[Index].OptionNumber; + } + + // + // Register a hotkey with the boot option, if requested. + // + if (BootKeys[Index].UnicodeChar == L'\0') { + continue; + } + + Status = EfiBootManagerAddKeyOptionVariable ( + NULL, + BootOptionNumber, + 0, + &BootKeys[Index], + NULL + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: failed to register hotkey for \"%s\": %r\n", + __func__, + BootOptions[Index].Description, + Status + )); + } + } + + EfiBootManagerFreeLoadOptions (CurrentBootOptions, CurrentBootOptionCount); + EfiBootManagerFreeLoadOptions (BootOptions, BootCount); + FreePool (BootKeys); +} + +STATIC +VOID +PlatformRegisterOptionsAndKeys ( + VOID + ) +{ + EFI_STATUS Status; + EFI_INPUT_KEY Enter; + EFI_INPUT_KEY F2; + EFI_INPUT_KEY Esc; + EFI_BOOT_MANAGER_LOAD_OPTION BootOption; + + GetPlatformOptions (); + + // + // Register ENTER as CONTINUE key + // + Enter.ScanCode = SCAN_NULL; + Enter.UnicodeChar = CHAR_CARRIAGE_RETURN; + Status = EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL); + ASSERT_EFI_ERROR (Status); + + // + // Map F2 and ESC to Boot Manager Menu + // + F2.ScanCode = SCAN_F2; + F2.UnicodeChar = CHAR_NULL; + Esc.ScanCode = SCAN_ESC; + Esc.UnicodeChar = CHAR_NULL; + Status = EfiBootManagerGetBootManagerMenu (&BootOption); + ASSERT_EFI_ERROR (Status); + Status = EfiBootManagerAddKeyOptionVariable ( + NULL, + (UINT16)BootOption.OptionNumber, + 0, + &F2, + NULL + ); + ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED); + Status = EfiBootManagerAddKeyOptionVariable ( + NULL, + (UINT16)BootOption.OptionNumber, + 0, + &Esc, + NULL + ); + ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED); +} + +// +// BDS Platform Functions +// + +/** + Do the platform init, can be customized by OEM/IBV + Possible things that can be done in PlatformBootManagerBeforeConsole: + > Update console variable: 1. include hot-plug devices; + > 2. Clear ConIn and add SOL for AMT + > Register new Driver#### or Boot#### + > Register new Key####: e.g.: F12 + > Signal ReadyToLock event + > Authentication action: 1. connect Auth devices; + > 2. Identify auto logon user. +**/ +VOID +EFIAPI +PlatformBootManagerBeforeConsole ( + VOID + ) +{ + // + // Signal EndOfDxe PI Event + // + EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid); + + // + // Dispatch deferred images after EndOfDxe event. + // + EfiBootManagerDispatchDeferredImages (); + + // + // Locate the PCI root bridges and make the PCI bus driver connect each, + // non-recursively. This will produce a number of child handles with PciIo on + // them. + // + FilterAndProcess (&gEfiPciRootBridgeIoProtocolGuid, NULL, Connect); + + // + // Find all display class PCI devices (using the handles from the previous + // step), and connect them non-recursively. This should produce a number of + // child handles with GOPs on them. + // + FilterAndProcess (&gEfiPciIoProtocolGuid, IsPciDisplay, Connect); + + // + // Now add the device path of all handles with GOP on them to ConOut and + // ErrOut. + // + FilterAndProcess (&gEfiGraphicsOutputProtocolGuid, NULL, AddOutput); + + // + // The core BDS code connects short-form USB device paths by explicitly + // looking for handles with PCI I/O installed, and checking the PCI class + // code whether it matches the one for a USB host controller. This means + // non-discoverable USB host controllers need to have the non-discoverable + // PCI driver attached first. + // + FilterAndProcess (&gEdkiiNonDiscoverableDeviceProtocolGuid, IsUsbHost, Connect); + + // + // Add the hardcoded short-form USB keyboard device path to ConIn. + // + EfiBootManagerUpdateConsoleVariable ( + ConIn, + (EFI_DEVICE_PATH_PROTOCOL *)&mUsbKeyboard, + NULL + ); + + // + // Add the hardcoded serial console device path to ConIn, ConOut, ErrOut. + // + STATIC_ASSERT ( + FixedPcdGet8 (PcdDefaultTerminalType) == 4, + "PcdDefaultTerminalType must be TTYTERM" + ); + STATIC_ASSERT ( + FixedPcdGet8 (PcdUartDefaultParity) != 0, + "PcdUartDefaultParity must be set to an actual value, not 'default'" + ); + STATIC_ASSERT ( + FixedPcdGet8 (PcdUartDefaultStopBits) != 0, + "PcdUartDefaultStopBits must be set to an actual value, not 'default'" + ); + + CopyGuid (&mSerialConsole.TermType.Guid, &gEfiTtyTermGuid); + + EfiBootManagerUpdateConsoleVariable ( + ConIn, + (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, + NULL + ); + EfiBootManagerUpdateConsoleVariable ( + ConOut, + (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, + NULL + ); + EfiBootManagerUpdateConsoleVariable ( + ErrOut, + (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, + NULL + ); + + // + // Register platform-specific boot options and keyboard shortcuts. + // + PlatformRegisterOptionsAndKeys (); +} + +STATIC +VOID +HandleCapsules ( + VOID + ) +{ + ESRT_MANAGEMENT_PROTOCOL *EsrtManagement; + EFI_PEI_HOB_POINTERS HobPointer; + EFI_CAPSULE_HEADER *CapsuleHeader; + BOOLEAN NeedReset; + EFI_STATUS Status; + + DEBUG ((DEBUG_INFO, "%a: processing capsules ...\n", __func__)); + + Status = gBS->LocateProtocol ( + &gEsrtManagementProtocolGuid, + NULL, + (VOID **)&EsrtManagement + ); + if (!EFI_ERROR (Status)) { + EsrtManagement->SyncEsrtFmp (); + } + + // + // Find all capsule images from hob + // + HobPointer.Raw = GetHobList (); + NeedReset = FALSE; + while ((HobPointer.Raw = GetNextHob ( + EFI_HOB_TYPE_UEFI_CAPSULE, + HobPointer.Raw + )) != NULL) + { + CapsuleHeader = (VOID *)(UINTN)HobPointer.Capsule->BaseAddress; + + Status = ProcessCapsuleImage (CapsuleHeader); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: failed to process capsule %p - %r\n", + __func__, + CapsuleHeader, + Status + )); + return; + } + + NeedReset = TRUE; + HobPointer.Raw = GET_NEXT_HOB (HobPointer); + } + + if (NeedReset) { + DEBUG (( + DEBUG_WARN, + "%a: capsule update successful, resetting ...\n", + __func__ + )); + + gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL); + CpuDeadLoop (); + } +} + +#define VERSION_STRING_PREFIX L"Tianocore/EDK2 firmware version " + +/** + This functions checks the value of BootDiscoverPolicy variable and + connect devices of class specified by that variable. Then it refreshes + Boot order for newly discovered boot device. + + @retval EFI_SUCCESS Devices connected successfully or connection + not required. + @retval others Return values from GetVariable(), LocateProtocol() + and ConnectDeviceClass(). +**/ +STATIC +EFI_STATUS +BootDiscoveryPolicyHandler ( + VOID + ) +{ + EFI_STATUS Status; + UINT32 DiscoveryPolicy; + UINT32 DiscoveryPolicyOld; + UINTN Size; + EFI_BOOT_MANAGER_POLICY_PROTOCOL *BMPolicy; + EFI_GUID *Class; + + Size = sizeof (DiscoveryPolicy); + Status = gRT->GetVariable ( + BOOT_DISCOVERY_POLICY_VAR, + &gBootDiscoveryPolicyMgrFormsetGuid, + NULL, + &Size, + &DiscoveryPolicy + ); + if (Status == EFI_NOT_FOUND) { + DiscoveryPolicy = PcdGet32 (PcdBootDiscoveryPolicy); + Status = PcdSet32S (PcdBootDiscoveryPolicy, DiscoveryPolicy); + if (Status == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } else if (EFI_ERROR (Status)) { + return Status; + } + } else if (EFI_ERROR (Status)) { + return Status; + } + + if (DiscoveryPolicy == BDP_CONNECT_MINIMAL) { + return EFI_SUCCESS; + } + + switch (DiscoveryPolicy) { + case BDP_CONNECT_NET: + Class = &gEfiBootManagerPolicyNetworkGuid; + break; + case BDP_CONNECT_ALL: + Class = &gEfiBootManagerPolicyConnectAllGuid; + break; + default: + DEBUG (( + DEBUG_INFO, + "%a - Unexpected DiscoveryPolicy (0x%x). Run Minimal Discovery Policy\n", + __func__, + DiscoveryPolicy + )); + return EFI_SUCCESS; + } + + Status = gBS->LocateProtocol ( + &gEfiBootManagerPolicyProtocolGuid, + NULL, + (VOID **)&BMPolicy + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "%a - Failed to locate gEfiBootManagerPolicyProtocolGuid." + "Driver connect will be skipped.\n", + __func__ + )); + return Status; + } + + Status = BMPolicy->ConnectDeviceClass (BMPolicy, Class); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a - ConnectDeviceClass returns - %r\n", __func__, Status)); + return Status; + } + + // + // Refresh Boot Options if Boot Discovery Policy has been changed + // + Size = sizeof (DiscoveryPolicyOld); + Status = gRT->GetVariable ( + BOOT_DISCOVERY_POLICY_OLD_VAR, + &gBootDiscoveryPolicyMgrFormsetGuid, + NULL, + &Size, + &DiscoveryPolicyOld + ); + if ((Status == EFI_NOT_FOUND) || (DiscoveryPolicyOld != DiscoveryPolicy)) { + EfiBootManagerRefreshAllBootOption (); + + Status = gRT->SetVariable ( + BOOT_DISCOVERY_POLICY_OLD_VAR, + &gBootDiscoveryPolicyMgrFormsetGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (DiscoveryPolicyOld), + &DiscoveryPolicy + ); + } + + return EFI_SUCCESS; +} + +/** + Do the platform specific action after the console is ready + Possible things that can be done in PlatformBootManagerAfterConsole: + > Console post action: + > Dynamically switch output mode from 100x31 to 80x25 for certain scenario + > Signal console ready platform customized event + > Run diagnostics like memory testing + > Connect certain devices + > Dispatch additional option roms + > Special boot: e.g.: USB boot, enter UI +**/ +VOID +EFIAPI +PlatformBootManagerAfterConsole ( + VOID + ) +{ + EFI_STATUS Status; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + UINTN FirmwareVerLength; + UINTN PosX; + UINTN PosY; + EFI_INPUT_KEY Key; + + FirmwareVerLength = StrLen (PcdGetPtr (PcdFirmwareVersionString)); + + // + // Show the splash screen. + // + Status = BootLogoEnableLogo (); + if (EFI_ERROR (Status)) { + if (FirmwareVerLength > 0) { + Print ( + VERSION_STRING_PREFIX L"%s\n", + PcdGetPtr (PcdFirmwareVersionString) + ); + } + + Print (L"Press ESCAPE for boot options "); + } else if (FirmwareVerLength > 0) { + Status = gBS->HandleProtocol ( + gST->ConsoleOutHandle, + &gEfiGraphicsOutputProtocolGuid, + (VOID **)&GraphicsOutput + ); + if (!EFI_ERROR (Status)) { + PosX = (GraphicsOutput->Mode->Info->HorizontalResolution - + (StrLen (VERSION_STRING_PREFIX) + FirmwareVerLength) * + EFI_GLYPH_WIDTH) / 2; + PosY = 0; + + PrintXY ( + PosX, + PosY, + NULL, + NULL, + VERSION_STRING_PREFIX L"%s", + PcdGetPtr (PcdFirmwareVersionString) + ); + } + } + + // + // Connect device specified by BootDiscoverPolicy variable and + // refresh Boot order for newly discovered boot devices + // + BootDiscoveryPolicyHandler (); + + // + // On ARM, there is currently no reason to use the phased capsule + // update approach where some capsules are dispatched before EndOfDxe + // and some are dispatched after. So just handle all capsules here, + // when the console is up and we can actually give the user some + // feedback about what is going on. + // + HandleCapsules (); + + // + // Register UEFI Shell + // + Key.ScanCode = SCAN_NULL; + Key.UnicodeChar = L's'; + PlatformRegisterFvBootOption (&gUefiShellFileGuid, L"UEFI Shell", 0, &Key); +} + +/** + This function is called each second during the boot manager waits the + timeout. + + @param TimeoutRemain The remaining timeout. +**/ +VOID +EFIAPI +PlatformBootManagerWaitCallback ( + UINT16 TimeoutRemain + ) +{ + EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White; + UINT16 Timeout; + EFI_STATUS Status; + + Timeout = PcdGet16 (PcdPlatformBootTimeOut); + + Black.Raw = 0x00000000; + White.Raw = 0x00FFFFFF; + + Status = BootLogoUpdateProgress ( + White.Pixel, + Black.Pixel, + L"Press ESCAPE for boot options", + White.Pixel, + (Timeout - TimeoutRemain) * 100 / Timeout, + 0 + ); + if (EFI_ERROR (Status)) { + Print (L"."); + } +} + +/** + The function is called when no boot option could be launched, + including platform recovery options and options pointing to applications + built into firmware volumes. + + If this function returns, BDS attempts to enter an infinite loop. +**/ +VOID +EFIAPI +PlatformBootManagerUnableToBoot ( + VOID + ) +{ + EFI_STATUS Status; + EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINTN OldBootOptionCount; + UINTN NewBootOptionCount; + + // + // Record the total number of boot configured boot options + // + BootOptions = EfiBootManagerGetLoadOptions ( + &OldBootOptionCount, + LoadOptionTypeBoot + ); + EfiBootManagerFreeLoadOptions (BootOptions, OldBootOptionCount); + + // + // Connect all devices, and regenerate all boot options + // + EfiBootManagerConnectAll (); + EfiBootManagerRefreshAllBootOption (); + + // + // Boot the 'UEFI Shell'. If the Pcd is not set, the UEFI Shell is not + // an active boot option and must be manually selected through UiApp + // (at least during the fist boot). + // + if (FixedPcdGetBool (PcdUefiShellDefaultBootEnable)) { + PlatformBootFvBootOption ( + &gUefiShellFileGuid, + L"UEFI Shell (default)" + ); + } + + // + // Record the updated number of boot configured boot options + // + BootOptions = EfiBootManagerGetLoadOptions ( + &NewBootOptionCount, + LoadOptionTypeBoot + ); + EfiBootManagerFreeLoadOptions (BootOptions, NewBootOptionCount); + + // + // If the number of configured boot options has changed, reboot + // the system so the new boot options will be taken into account + // while executing the ordinary BDS bootflow sequence. + // *Unless* persistent varstore is being emulated, since we would + // then end up in an endless reboot loop. + // + if (!PcdGetBool (PcdEmuVariableNvModeEnable)) { + if (NewBootOptionCount != OldBootOptionCount) { + DEBUG (( + DEBUG_WARN, + "%a: rebooting after refreshing all boot options\n", + __func__ + )); + gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL); + } + } + + Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu); + if (EFI_ERROR (Status)) { + return; + } + + for ( ; ;) { + EfiBootManagerBoot (&BootManagerMenu); + } +} diff --git a/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.h b/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.h new file mode 100644 index 0000000000..a40a2ff5cb --- /dev/null +++ b/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.h @@ -0,0 +1,53 @@ +/** @file + Head file for BDS Platform specific code + + Copyright (C) 2015-2016, Red Hat, Inc. + Copyright (c) 2004 - 2008, Intel Corporation. All rights reserved.
+ Copyright (c) 2016, Linaro Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef PLATFORM_BM_H_ +#define PLATFORM_BM_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + Use SystemTable Conout to stop video based Simple Text Out consoles from + going to the video device. Put up LogoFile on every video device that is a + console. + + @param[in] LogoFile File name of logo to display on the center of the + screen. + + @retval EFI_SUCCESS ConsoleControl has been flipped to graphics and logo + displayed. + @retval EFI_UNSUPPORTED Logo not found +**/ +EFI_STATUS +EnableQuietBoot ( + IN EFI_GUID *LogoFile + ); + +/** + Use SystemTable Conout to turn on video based Simple Text Out consoles. The + Simple Text Out screens will now be synced up with all non video output + devices + + @retval EFI_SUCCESS UGA devices are back in text mode and synced up. +**/ +EFI_STATUS +DisableQuietBoot ( + VOID + ); + +#endif // PLATFORM_BM_H_ diff --git a/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf new file mode 100644 index 0000000000..bc029be635 --- /dev/null +++ b/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf @@ -0,0 +1,95 @@ +## @file +# Implementation for PlatformBootManagerLib library class interfaces. +# +# Copyright (C) 2015-2016, Red Hat, Inc. +# Copyright (c) 2014 - 2023, Arm Ltd. All rights reserved.
+# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.
+# Copyright (c) 2016, Linaro Ltd. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PlatformBootManagerLib + FILE_GUID = 92FD2DE3-B9CB-4B35-8141-42AD34D73C9F + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = PlatformBootManagerLib|DXE_DRIVER + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = ARM AARCH64 +# + +[Sources] + PlatformBm.c + PlatformBm.h + +[Packages] + ArmPkg/ArmPkg.dec + EmbeddedPkg/EmbeddedPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + ShellPkg/ShellPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + BootLogoLib + CapsuleLib + DebugLib + DevicePathLib + DxeServicesLib + HobLib + MemoryAllocationLib + PcdLib + PrintLib + UefiBootManagerLib + UefiBootServicesTableLib + UefiLib + UefiRuntimeServicesTableLib + +[FeaturePcd] + gEfiMdePkgTokenSpaceGuid.PcdUgaConsumeSupport + +[FixedPcd] + gArmTokenSpaceGuid.PcdUefiShellDefaultBootEnable + gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwareVersionString + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultDataBits + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultParity + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultStopBits + gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut + gEfiMdeModulePkgTokenSpaceGuid.PcdBootDiscoveryPolicy + gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvModeEnable + +[Guids] + gBootDiscoveryPolicyMgrFormsetGuid + gEdkiiNonDiscoverableEhciDeviceGuid + gEdkiiNonDiscoverableUhciDeviceGuid + gEdkiiNonDiscoverableXhciDeviceGuid + gEfiBootManagerPolicyNetworkGuid + gEfiBootManagerPolicyConnectAllGuid + gEfiFileInfoGuid + gEfiFileSystemInfoGuid + gEfiFileSystemVolumeLabelInfoIdGuid + gEfiEndOfDxeEventGroupGuid + gEfiTtyTermGuid + gUefiShellFileGuid + +[Protocols] + gEdkiiNonDiscoverableDeviceProtocolGuid + gEfiBootManagerPolicyProtocolGuid + gEfiDevicePathProtocolGuid + gEfiGraphicsOutputProtocolGuid + gEfiLoadedImageProtocolGuid + gEfiPciRootBridgeIoProtocolGuid + gEfiSimpleFileSystemProtocolGuid + gEsrtManagementProtocolGuid + gPlatformBootManagerProtocolGuid From 1ca2718fd6646a30b12747582370f5835533a913 Mon Sep 17 00:00:00 2001 From: Nhi Pham Date: Wed, 16 Oct 2024 11:20:35 +0700 Subject: [PATCH 08/10] AmpereSiliconPkg/PlatformBootManagerLib: Add AfterConsole event creation This adds event creation for the AfterConsole event at the end of PlatformBootManagerAfterConsole function. At this point, all boot options are expected to be available for subsequent customization. Signed-off-by: Nhi Pham --- .../PlatformBootManagerLib/PlatformBm.c | 18 ++++++++++++++++++ .../PlatformBootManagerLib.inf | 2 ++ 2 files changed, 20 insertions(+) diff --git a/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.c b/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.c index ea093bb725..c380a32a58 100644 --- a/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.c +++ b/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -1002,6 +1003,7 @@ PlatformBootManagerAfterConsole ( UINTN PosX; UINTN PosY; EFI_INPUT_KEY Key; + EFI_EVENT AfterConsoleEvent; FirmwareVerLength = StrLen (PcdGetPtr (PcdFirmwareVersionString)); @@ -1062,6 +1064,22 @@ PlatformBootManagerAfterConsole ( Key.ScanCode = SCAN_NULL; Key.UnicodeChar = L's'; PlatformRegisterFvBootOption (&gUefiShellFileGuid, L"UEFI Shell", 0, &Key); + + // + // Signal After Console event + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + EfiEventEmptyFunction, + NULL, + &gAmpereEventAfterConsoleGuid, + &AfterConsoleEvent + ); + if (!EFI_ERROR (Status)) { + gBS->SignalEvent (AfterConsoleEvent); + gBS->CloseEvent (AfterConsoleEvent); + } } /** diff --git a/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf index bc029be635..4d44b2b100 100644 --- a/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf +++ b/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf @@ -34,6 +34,7 @@ MdeModulePkg/MdeModulePkg.dec MdePkg/MdePkg.dec ShellPkg/ShellPkg.dec + Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec [LibraryClasses] BaseLib @@ -70,6 +71,7 @@ gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvModeEnable [Guids] + gAmpereEventAfterConsoleGuid gBootDiscoveryPolicyMgrFormsetGuid gEdkiiNonDiscoverableEhciDeviceGuid gEdkiiNonDiscoverableUhciDeviceGuid From 001bceed6f89db49db72b3c5dcb0e75125a879f0 Mon Sep 17 00:00:00 2001 From: Nhi Pham Date: Wed, 16 Oct 2024 11:31:07 +0700 Subject: [PATCH 09/10] JadePkg: Add boot option override via IPMI This introduces IpmiBootDxe which enables users to override the UEFI boot option via IPMI bootdev commands. As an example, it can be used to force the boot to the BIOS setup menu as the following commands: * ipmitool chassis bootdev * ipmitool chassis bootparam get 5 # to confirm the setting Signed-off-by: Nhi Pham --- Platform/Ampere/JadePkg/Jade.fdf | 1 + .../AmpereAltraPkg/AmpereAltraPkg.dsc.inc | 1 + .../Drivers/IpmiBootDxe/IpmiBootDxe.c | 814 ++++++++++++++++++ .../Drivers/IpmiBootDxe/IpmiBootDxe.inf | 44 + 4 files changed, 860 insertions(+) create mode 100644 Silicon/Ampere/AmpereSiliconPkg/Drivers/IpmiBootDxe/IpmiBootDxe.c create mode 100644 Silicon/Ampere/AmpereSiliconPkg/Drivers/IpmiBootDxe/IpmiBootDxe.inf diff --git a/Platform/Ampere/JadePkg/Jade.fdf b/Platform/Ampere/JadePkg/Jade.fdf index 4c1c8c2a56..655df1cc9a 100644 --- a/Platform/Ampere/JadePkg/Jade.fdf +++ b/Platform/Ampere/JadePkg/Jade.fdf @@ -363,6 +363,7 @@ APRIORI DXE { INF MdeModulePkg/Application/UiApp/UiApp.inf INF MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.inf INF Silicon/Ampere/AmpereSiliconPkg/Drivers/PlatformBootManagerDxe/PlatformBootManagerDxe.inf + INF Silicon/Ampere/AmpereSiliconPkg/Drivers/IpmiBootDxe/IpmiBootDxe.inf # # Networking stack diff --git a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc index 4c4d91fcc9..1e39e28919 100644 --- a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc +++ b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc @@ -849,3 +849,4 @@ EmbeddedPkg/Drivers/MemoryAttributeManagerDxe/MemoryAttributeManagerDxe.inf Silicon/Ampere/AmpereSiliconPkg/Drivers/PlatformBootManagerDxe/PlatformBootManagerDxe.inf + Silicon/Ampere/AmpereSiliconPkg/Drivers/IpmiBootDxe/IpmiBootDxe.inf diff --git a/Silicon/Ampere/AmpereSiliconPkg/Drivers/IpmiBootDxe/IpmiBootDxe.c b/Silicon/Ampere/AmpereSiliconPkg/Drivers/IpmiBootDxe/IpmiBootDxe.c new file mode 100644 index 0000000000..39e495aa37 --- /dev/null +++ b/Silicon/Ampere/AmpereSiliconPkg/Drivers/IpmiBootDxe/IpmiBootDxe.c @@ -0,0 +1,814 @@ +/** @file + Process IPMI bootdev command and force the UEFI to boot with selected option. + + Copyright (c) 2024, Ampere Computing LLC. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BBS_TYPE_OS_HARDDRIVE 0xFD +#define BBS_TYPE_MENU 0xFE +#define LAST_BOOT_ORDER_VARIABLE_NAME L"LastBootOrder" + +// +// This variable is used to inidicate the persistent boot to UiApp. +// 0: Disable persistent boot to UiApp +// 1: Enable persistent boot to UiApp +// +#define FORCE_UIAPP_VARIABLE_NAME L"ForceUiApp" + +/** + Get BBS Type from a messaging device path. + + @param[in] DevicePath Current node of the Messaging device path. + + @retval UINT16 Return BBS Type. + +**/ +UINT16 +GetBBSTypeFromMessagingDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *Node + ) +{ + UINT16 Result; + VENDOR_DEVICE_PATH *VendorDevicePathNode; + + ASSERT (Node != NULL); + + Result = BBS_TYPE_UNKNOWN; + + switch (DevicePathSubType (Node)) { + case MSG_MAC_ADDR_DP: + Result = BBS_TYPE_EMBEDDED_NETWORK; + break; + + case MSG_USB_DP: + Result = BBS_TYPE_FLOPPY; + break; + + case MSG_SATA_DP: + case MSG_NVME_NAMESPACE_DP: + Result = BBS_TYPE_HARDDRIVE; + break; + + case MSG_VENDOR_DP: + VendorDevicePathNode = (VENDOR_DEVICE_PATH *)Node; + if (&VendorDevicePathNode->Guid != NULL) { + if (CompareGuid (&VendorDevicePathNode->Guid, &((EFI_GUID)DEVICE_PATH_MESSAGING_SAS))) { + Result = BBS_TYPE_HARDDRIVE; + } + } + + break; + + default: + break; + } + + return Result; +} + +/** + Get BBS Type from a Media device path. + + @param[in] Node Current node of the Media device path. + + @retval UINT16 Return BBS Type. + +**/ +UINT16 +GetBBSTypeFromMediaDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *Node + ) +{ + UINT16 Result; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvDevicePathNode; + + ASSERT (Node != NULL); + + Result = BBS_TYPE_UNKNOWN; + + switch (DevicePathSubType (Node)) { + case MEDIA_CDROM_DP: + Result = BBS_TYPE_CDROM; + break; + + case MEDIA_PIWG_FW_FILE_DP: + FvDevicePathNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)Node; + if (&FvDevicePathNode->FvFileName != NULL) { + if (CompareGuid (&FvDevicePathNode->FvFileName, PcdGetPtr (PcdBootManagerMenuFile))) { + Result = BBS_TYPE_MENU; + } + } + + break; + + default: + break; + } + + return Result; +} + +/** + Get BBS type of a boot option from its option number. + + @param[in] OptionNumber Option number of the Boot option. + + @retval UINT8 Return BBS type. + +**/ +UINT8 +GetBBSType ( + UINT16 OptionNumber + ) +{ + CHAR16 OptionName[sizeof ("Boot####")]; + EFI_BOOT_MANAGER_LOAD_OPTION Option; + EFI_DEVICE_PATH_PROTOCOL *StartOfMediaDevicePath; + EFI_DEVICE_PATH_PROTOCOL *StartOfMessagingDevicePath; + EFI_DEVICE_PATH_PROTOCOL *Node; + EFI_STATUS Status; + UINT16 Result; + + StartOfMediaDevicePath = NULL; + StartOfMessagingDevicePath = NULL; + + UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", OptionNumber); + Status = EfiBootManagerVariableToLoadOption (OptionName, &Option); + if (EFI_ERROR (Status)) { + return BBS_TYPE_UNKNOWN; + } + + DEBUG_CODE ( + CHAR16 *Str = ConvertDevicePathToText (Option.FilePath, TRUE, TRUE); + if (Str != NULL) { + DEBUG ((DEBUG_INFO, "Boot0x%04x: %s\n", OptionNumber, Str)); + FreePool (Str); + } + + ); + + Node = Option.FilePath; + while (!IsDevicePathEnd (Node)) { + if ( (DevicePathType (Node) == MESSAGING_DEVICE_PATH) + && (StartOfMessagingDevicePath == NULL)) + { + StartOfMessagingDevicePath = Node; + } + + if ( (DevicePathType (Node) == MEDIA_DEVICE_PATH) + && (StartOfMediaDevicePath == NULL)) + { + StartOfMediaDevicePath = Node; + break; + } + + Node = NextDevicePathNode (Node); + } + + Result = BBS_TYPE_UNKNOWN; + + if (StartOfMediaDevicePath != NULL) { + while (!IsDevicePathEnd (Node)) { + Result = GetBBSTypeFromMediaDevicePath (Node); + if (Result != BBS_TYPE_UNKNOWN) { + return Result; + } + + Node = NextDevicePathNode (Node); + } + } + + if (StartOfMessagingDevicePath != NULL) { + while (!IsDevicePathEnd (StartOfMessagingDevicePath)) { + Result = GetBBSTypeFromMessagingDevicePath (StartOfMessagingDevicePath); + if (Result != BBS_TYPE_UNKNOWN) { + return Result; + } + + StartOfMessagingDevicePath = NextDevicePathNode (StartOfMessagingDevicePath); + } + } + + return Result; +} + +/** + Build a new BootOrder base on an existed one with the head includes all options of the same selected type. + + @param[in] BootType BBS Type to find for + @param[in] BootOrder Pointer to the array of an existed BootOrder. + @param[in] BootOrderSize Size of the existed BootOrder. + + @retval UINT16 * Pointer to the new BootOrder array. + @retval NULL Can't build the new BootOrder or current BootOrder is good enough. + +**/ +UINT16 * +BuildBootOrder ( + IN UINT8 BootType, + IN UINT16 *BootOrder, + IN UINTN BootOrderSize + ) +{ + UINT16 *NewBootOrder; + UINT16 *SelectOptions; + UINT16 *RemainOptions; + UINT8 Temp; + UINTN Index; + UINTN NewCnt; + UINTN RemainCnt; + UINTN SelectCnt; + + ASSERT (BootOrder != NULL); + + NewBootOrder = AllocatePool (BootOrderSize); + SelectOptions = AllocatePool (BootOrderSize); + RemainOptions = AllocatePool (BootOrderSize); + if ( (NewBootOrder == NULL) + || (SelectOptions == NULL) + || (RemainOptions == NULL)) + { + DEBUG ((DEBUG_ERROR, "%a: Out of resources", __func__)); + goto Exit; + } + + NewCnt = 0; + SelectCnt = 0; + RemainCnt = 0; + + for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) { + Temp = GetBBSType (BootOrder[Index]); + // + // Hard drive with OS is a special type of hard drive boot option. + // Treat it with higher priority than normal hard drive. + // + if ((BootType == BBS_TYPE_HARDDRIVE) && (Temp == BBS_TYPE_OS_HARDDRIVE)) { + NewBootOrder[NewCnt++] = BootOrder[Index]; + } else if (Temp == BootType) { + SelectOptions[SelectCnt++] = BootOrder[Index]; + } else { + RemainOptions[RemainCnt++] = BootOrder[Index]; + } + } + + CopyMem (&SelectOptions[SelectCnt], RemainOptions, RemainCnt * sizeof (UINT16)); + CopyMem (&NewBootOrder[NewCnt], SelectOptions, (SelectCnt + RemainCnt) * sizeof (UINT16)); + + if (CompareMem (NewBootOrder, BootOrder, BootOrderSize) != 0) { + FreePool (SelectOptions); + FreePool (RemainOptions); + return NewBootOrder; + } + +Exit: + if (SelectOptions != NULL) { + FreePool (SelectOptions); + } + + if (RemainOptions != NULL) { + FreePool (RemainOptions); + } + + if (NewBootOrder != NULL) { + FreePool (NewBootOrder); + } + + return NULL; +} + +/** + Convert IPMI device selector to BBS Type. + + @param[in] DeviceSelector Input device selector. + + @retval UINT8 Return BBS Type. + +**/ +UINT8 +DeviceSelectorToBBSType ( + UINT8 DeviceSelector + ) +{ + switch (DeviceSelector) { + case IPMI_BOOT_DEVICE_SELECTOR_PXE: + return BBS_TYPE_EMBEDDED_NETWORK; + + case IPMI_BOOT_DEVICE_SELECTOR_HARDDRIVE: + return BBS_TYPE_HARDDRIVE; + + case IPMI_BOOT_DEVICE_SELECTOR_CD_DVD: + return BBS_TYPE_CDROM; + + case IPMI_BOOT_DEVICE_SELECTOR_BIOS_SETUP: + return BBS_TYPE_MENU; + + case IPMI_BOOT_DEVICE_SELECTOR_FLOPPY: + return BBS_TYPE_FLOPPY; + + default: + return BBS_TYPE_UNKNOWN; + } +} + +/** + Check if there is a request from BMC for boot option override. + + @param[out] BootDeviceSelector Pointer to buffer to get overrided boot options. + @param[out] IsPersistent Pointer to buffer for persistent boot option flag. + + @retval TRUE IPMI Boot Options Override Request is valid. + @retval FALSE IPMI Boot Options Override Request is invalid. + +**/ +BOOLEAN +IsIpmiBootOptionsValid ( + UINT8 *BootDeviceSelector, + BOOLEAN *IsPersistent + ) +{ + EFI_STATUS Status; + IPMI_GET_BOOT_OPTIONS_REQUEST BootOptionsRequest; + IPMI_GET_BOOT_OPTIONS_RESPONSE *BootOptionsResponse; + IPMI_BOOT_OPTIONS_RESPONSE_PARAMETER_4 *BootOptionsParameterData4; + IPMI_BOOT_OPTIONS_RESPONSE_PARAMETER_5 *BootOptionsParameterData5; + + if ((BootDeviceSelector == NULL) || (IsPersistent == NULL)) { + return FALSE; + } + + ZeroMem (&BootOptionsRequest, sizeof (IPMI_GET_BOOT_OPTIONS_REQUEST)); + + // + // Retrieve Boot Info Acknowledge from BMC. + // + BootOptionsResponse = AllocateZeroPool (sizeof (IPMI_GET_BOOT_OPTIONS_REQUEST) + sizeof (IPMI_BOOT_OPTIONS_PARAMETERS)); + if (BootOptionsResponse == NULL) { + return FALSE; + } + + BootOptionsRequest.ParameterSelector.Bits.ParameterSelector = IPMI_BOOT_OPTIONS_PARAMETER_BOOT_INFO_ACK; + + Status = IpmiGetSystemBootOptions (&BootOptionsRequest, BootOptionsResponse); + if (EFI_ERROR (Status)) { + FreePool (BootOptionsResponse); + return FALSE; + } + + BootOptionsParameterData4 = (IPMI_BOOT_OPTIONS_RESPONSE_PARAMETER_4 *)BootOptionsResponse->ParameterData; + if (BootOptionsParameterData4->BootInitiatorAcknowledgeData != BOOT_OPTION_HANDLED_BY_BIOS) { + FreePool (BootOptionsResponse); + return FALSE; + } + + // + // Retrieve Boot Options parameter data + // + ZeroMem (&BootOptionsRequest, sizeof (IPMI_GET_BOOT_OPTIONS_REQUEST)); + + BootOptionsRequest.ParameterSelector.Bits.ParameterSelector = IPMI_BOOT_OPTIONS_PARAMETER_BOOT_FLAGS; + + Status = IpmiGetSystemBootOptions (&BootOptionsRequest, BootOptionsResponse); + if (EFI_ERROR (Status)) { + FreePool (BootOptionsResponse); + return FALSE; + } + + BootOptionsParameterData5 = (IPMI_BOOT_OPTIONS_RESPONSE_PARAMETER_5 *)BootOptionsResponse->ParameterData; + if (BootOptionsParameterData5->Data1.Bits.BootFlagValid != 0) { + *IsPersistent = (BootOptionsParameterData5->Data1.Bits.PersistentOptions != 0) ? TRUE : FALSE; + *BootDeviceSelector = BootOptionsParameterData5->Data2.Bits.BootDeviceSelector; + FreePool (BootOptionsResponse); + return TRUE; + } + + FreePool (BootOptionsResponse); + return FALSE; +} + +/** + Acknowledge and clear the boot flags requested from BMC. + + @retval EFI_SUCCESS The operation is completed sucessfully. + @retval Others An error when sending IPMI command. + +**/ +EFI_STATUS +ClearIpmiBootOptionsOverride ( + VOID + ) +{ + EFI_STATUS Status; + IPMI_SET_BOOT_OPTIONS_REQUEST *SetBootOptionsRequest; + IPMI_SET_BOOT_OPTIONS_RESPONSE SetBootOptionsResponse; + IPMI_BOOT_OPTIONS_RESPONSE_PARAMETER_4 *BootOptionsParameterData; + + // + // Set Boot Info Acknowledge to notify BMC that the Boot Flags has been handled by UEFI + // + ZeroMem (&SetBootOptionsResponse, sizeof (IPMI_SET_BOOT_OPTIONS_RESPONSE)); + + SetBootOptionsRequest = AllocateZeroPool (sizeof (SetBootOptionsRequest) + sizeof (IPMI_BOOT_OPTIONS_PARAMETERS)); + if (SetBootOptionsRequest == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SetBootOptionsRequest->ParameterValid.Bits.ParameterSelector = IPMI_BOOT_OPTIONS_PARAMETER_BOOT_INFO_ACK; + SetBootOptionsRequest->ParameterValid.Bits.MarkParameterInvalid = 0x0; + + BootOptionsParameterData = (IPMI_BOOT_OPTIONS_RESPONSE_PARAMETER_4 *)&SetBootOptionsRequest->ParameterData; + + BootOptionsParameterData->WriteMask = BIT0; + BootOptionsParameterData->BootInitiatorAcknowledgeData = 0; + + Status = IpmiSetSystemBootOptions (SetBootOptionsRequest, &SetBootOptionsResponse); + if (EFI_ERROR (Status)) { + FreePool (SetBootOptionsRequest); + return Status; + } + + // + // Send command to clear BMC Boot Flags parameter + // + ZeroMem (&SetBootOptionsResponse, sizeof (IPMI_SET_BOOT_OPTIONS_RESPONSE)); + ZeroMem (SetBootOptionsRequest, sizeof (SetBootOptionsRequest) + sizeof (IPMI_BOOT_OPTIONS_PARAMETERS)); + + SetBootOptionsRequest->ParameterValid.Bits.ParameterSelector = IPMI_BOOT_OPTIONS_PARAMETER_BOOT_FLAGS; + SetBootOptionsRequest->ParameterValid.Bits.MarkParameterInvalid = 0x0; + + Status = IpmiSetSystemBootOptions (SetBootOptionsRequest, &SetBootOptionsResponse); + + FreePool (SetBootOptionsRequest); + return Status; +} + +/** + Handle IPMI bootdev request on AfterConsole Event notification. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +HandleIpmiBootOption ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + BOOLEAN UpdateNeed; + EFI_STATUS Status; + UINT16 *CurrBootOrder; + UINT16 *NewBootOrder; + UINT16 FirstBootOption; + UINT8 BootType; + UINT8 DeviceSelector; + BOOLEAN IsPersistent; + UINT8 ForceUiApp; + UINTN CurrBootOrderSize; + UINTN DataSize; + UINT64 OsIndication; + + OsIndication = 0; + NewBootOrder = NULL; + BootType = 0xFF; + DeviceSelector = 0xFF; + IsPersistent = FALSE; + + GetEfiGlobalVariable2 (EFI_BOOT_ORDER_VARIABLE_NAME, (VOID **)&CurrBootOrder, &CurrBootOrderSize); + if (CurrBootOrder == NULL) { + DEBUG ((DEBUG_ERROR, "%a: BootOrder not found\n", __func__)); + return; + } + + // + // It is supposed that BootOrder must contain at least one boot option. + // + FirstBootOption = CurrBootOrder[0]; + + // + // Cache the FORCE_UIAPP_VARIABLE to reduce the accessing time to the NVRAM. + // + DataSize = sizeof (UINT8); + Status = gRT->GetVariable ( + FORCE_UIAPP_VARIABLE_NAME, + &gAmpereVariableGuid, + NULL, + &DataSize, + &ForceUiApp + ); + if (EFI_ERROR (Status) || (DataSize != sizeof (UINT8))) { + ForceUiApp = 0; + } + + if (!IsIpmiBootOptionsValid (&DeviceSelector, &IsPersistent)) { + goto Exit; + } + + BootType = DeviceSelectorToBBSType (DeviceSelector); + if (BootType == BBS_TYPE_UNKNOWN) { + goto Exit; + } + + DEBUG ((DEBUG_INFO, "IPMI Boot Type %d, Persistent %d\n", BootType, IsPersistent)); + + NewBootOrder = BuildBootOrder (BootType, CurrBootOrder, CurrBootOrderSize); + + if (NewBootOrder != NULL) { + // + // Backup current BootOrder if this is not a persistent request. + // + if (!IsPersistent) { + Status = gRT->SetVariable ( + LAST_BOOT_ORDER_VARIABLE_NAME, + &gAmpereVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + CurrBootOrderSize, + CurrBootOrder + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to backup the BootOrder %r\n", __func__, Status)); + goto Exit; + } + } + + // + // Update BootOrder with new value. + // + Status = gRT->SetVariable ( + EFI_BOOT_ORDER_VARIABLE_NAME, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS + | EFI_VARIABLE_NON_VOLATILE, + CurrBootOrderSize, + NewBootOrder + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Set BootOrder Variable %r\n", __func__, Status)); + goto Exit; + } + + FirstBootOption = NewBootOrder[0]; + } + + UpdateNeed = FALSE; + if (BootType == BBS_TYPE_MENU) { + if (ForceUiApp == 0) { + // + // IPMI boot type is MENU, but FORCE_UIAPP_VARIABLE is not set. + // Set ForceUiApp to allow boot to UiApp on this boot cycle. + // + ForceUiApp = 1; + if (IsPersistent) { + // + // This is a persistent boot to UiApp request. + // Update FORCE_UIAPP_VARIABLE to force the system to boot to UiApp + // on next boot. + // + UpdateNeed = TRUE; + } + } + } else if (ForceUiApp == 1) { + // + // IPMI boot type is not the UiApp, but FORCE_UIAPP_VARIABLE is set + // Clear ForceUiApp to prevent the system from booting to UiApp on this boot cycle. + // + ForceUiApp = 0; + if (IsPersistent) { + // + // This is a persistent request other than UiApp. + // Update FORCE_UIAPP_VARIABLE to prevent the system from booting to UiApp + // on next boot. + // + UpdateNeed = TRUE; + } + } + + if (UpdateNeed) { + Status = gRT->SetVariable ( + FORCE_UIAPP_VARIABLE_NAME, + &gAmpereVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof (UINT8), + &ForceUiApp + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to set the Force UiApp variable - %r\n", __func__, Status)); + } + } + + // + // Acknowledge and clear the boot flags + // + Status = ClearIpmiBootOptionsOverride (); + ASSERT_EFI_ERROR (Status); + +Exit: + // + // UiApp was registered with HIDDEN attribute and will be ignored by BDS. + // In order to boot to the UiApp, manually update the BootNext variable. + // + if ((ForceUiApp == 1) && (GetBBSType (FirstBootOption) == BBS_TYPE_MENU)) { + DataSize = sizeof (UINT64); + Status = gRT->GetVariable ( + EFI_OS_INDICATIONS_VARIABLE_NAME, + &gEfiGlobalVariableGuid, + NULL, + &DataSize, + &OsIndication + ); + OsIndication |= EFI_OS_INDICATIONS_BOOT_TO_FW_UI; + Status = gRT->SetVariable ( + EFI_OS_INDICATIONS_VARIABLE_NAME, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof (UINT64), + &OsIndication + ); + ASSERT_EFI_ERROR (Status); + } + + if (NewBootOrder != NULL) { + FreePool (NewBootOrder); + } + + FreePool (CurrBootOrder); +} + +/** + Looking for the backup of previous BootOrder, then restore it with respect to current BootOrder. + + @retval EFI_SUCCESS The function executed successfully. + @retval Other Some error occurs when saving the variables. + +**/ +EFI_STATUS +RestoreBootOrder ( + VOID + ) +{ + EFI_STATUS Status; + UINT16 *CurrBootOrder; + UINT16 *LastBootOrder; + UINTN CurrBootOrderSize; + UINTN LastBootOrderSize; + + CurrBootOrderSize = 0; + LastBootOrderSize = 0; + + Status = GetVariable2 ( + LAST_BOOT_ORDER_VARIABLE_NAME, + &gAmpereVariableGuid, + (VOID **)&LastBootOrder, + &LastBootOrderSize + ); + if (LastBootOrder == NULL) { + return EFI_SUCCESS; + } + + // + // On first boot, OS loader might add a new boot option to the head of the BootOrder. + // If this is true, append that option to the tail of the LastBootOrder. + // + GetEfiGlobalVariable2 (EFI_BOOT_ORDER_VARIABLE_NAME, (VOID **)&CurrBootOrder, &CurrBootOrderSize); + if (LastBootOrderSize == (CurrBootOrderSize - sizeof (UINT16))) { + CurrBootOrder[CurrBootOrderSize / sizeof (UINT16) - 1] = CurrBootOrder[0]; + CopyMem (CurrBootOrder, LastBootOrder, LastBootOrderSize); + FreePool (LastBootOrder); + LastBootOrder = CurrBootOrder; + LastBootOrderSize = CurrBootOrderSize; + } else { + FreePool (CurrBootOrder); + } + + Status = gRT->SetVariable ( + EFI_BOOT_ORDER_VARIABLE_NAME, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS + | EFI_VARIABLE_NON_VOLATILE, + LastBootOrderSize, + LastBootOrder + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to restore the BootOrder\n", __func__)); + goto Exit; + } + + Status = gRT->SetVariable ( + LAST_BOOT_ORDER_VARIABLE_NAME, + &gAmpereVariableGuid, + 0, + 0, + NULL + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to erase the LastBootOrder\n", __func__)); + goto Exit; + } + +Exit: + FreePool (LastBootOrder); + + return Status; +} + +/** + ReadyToBoot notification. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +RestoreBootOrderOnReadytoBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + // + // Restore BootOrder variable in normal condition. + // + Status = RestoreBootOrder (); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error when restoring boot order\n")); + } +} + +/** + The Entry Point for IPMI Boot handler. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval Other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +IpmiBootEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_EVENT Event; + EFI_STATUS Status; + + Status = RestoreBootOrder (); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error when restoring boot order\n")); + return Status; + } + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + HandleIpmiBootOption, + NULL, + &gAmpereEventAfterConsoleGuid, + &Event + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error when creating an event callback for gAmpereEventAfterConsoleGuid\n")); + return Status; + } + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + RestoreBootOrderOnReadytoBoot, + NULL, + &gEfiEventReadyToBootGuid, + &Event + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error when creating an event callback for gEfiEventReadyToBootGuid\n")); + } + + return Status; +} diff --git a/Silicon/Ampere/AmpereSiliconPkg/Drivers/IpmiBootDxe/IpmiBootDxe.inf b/Silicon/Ampere/AmpereSiliconPkg/Drivers/IpmiBootDxe/IpmiBootDxe.inf new file mode 100644 index 0000000000..06c2ebab6c --- /dev/null +++ b/Silicon/Ampere/AmpereSiliconPkg/Drivers/IpmiBootDxe/IpmiBootDxe.inf @@ -0,0 +1,44 @@ + +## @file +# +# Copyright (c) 2024, Ampere Computing LLC. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = IpmiBootDxe + FILE_GUID = 8FFC6750-110D-49F0-872F-B580B77926AB + MODULE_TYPE = DXE_DRIVER + ENTRY_POINT = IpmiBootEntry + +[Sources] + IpmiBootDxe.c + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec + +[LibraryClasses] + BaseLib + DevicePathLib + IpmiCommandLib + MemoryAllocationLib + UefiBootManagerLib + UefiBootServicesTableLib + UefiDriverEntryPoint + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile + +[Guids] + gAmpereEventAfterConsoleGuid + gAmpereVariableGuid + gEfiGlobalVariableGuid + gEfiEventReadyToBootGuid + +[Depex] + TRUE From 470deb02bf6ee796d8db474cb15ba7ba78ee1bf5 Mon Sep 17 00:00:00 2001 From: Nhi Pham Date: Wed, 16 Oct 2024 13:56:05 +0700 Subject: [PATCH 10/10] JadePkg: Add EDK2 Porting Guide for Ampere Altra based platforms This introduces PlatformPortingGuide.md document which provides detailed guidelines for Ampere Altra EDK2 porting process. Signed-off-by: Nhi Pham --- .../Ampere/JadePkg/PlatformPortingGuide.md | 366 ++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100644 Platform/Ampere/JadePkg/PlatformPortingGuide.md diff --git a/Platform/Ampere/JadePkg/PlatformPortingGuide.md b/Platform/Ampere/JadePkg/PlatformPortingGuide.md new file mode 100644 index 0000000000..23fcba5af5 --- /dev/null +++ b/Platform/Ampere/JadePkg/PlatformPortingGuide.md @@ -0,0 +1,366 @@ +# Ampere Altra Based Platforms Porting Guide + +## Overview + +The purpose of this document is to describe changes commonly made when adding a +new Ampere Altra based platform. + +Users of this document should have prior firmware development experience with +EDKII, including knowledge of UEFI specification. + +This document also assumes your new platform meets Ampere(R) Altra(R) Interface +Firmware Requirements and is working with the Ampere system firmware and Trusted Firmware-A (TF-A). + +--- + +## EDKII Code Organization + +The following diagram is the hierarchical structure of the silicon and platform +directory. It is designed for easy portability and cross-platform reuse between +Ampere Altra based platforms. + +```mermaid +flowchart TD + edk2-platforms --> Silicon/Ampere + edk2-platforms --> Platform/Ampere + Silicon/Ampere --> AmpereSiliconPkg + Silicon/Ampere --> AmpereAltraPkg + Platform/Ampere --> JadePkg +``` + +All the packages are currently located under Silicon/Ampere and Platform/Ampere +directories. + +The following table describes the intention of the above package organization: + +Directory Name | Description +------------------|------------------------------------------------------------- +AmpereSiliconPkg | Ampere silicon common code that can be shared for multiple silicon. +AmpereAltraPkg | Ampere Altra specific code. +JadePkg | Ampere Mt. Jade Platform code. Any new platform/board should be based on this package. + +--- + +## Porting TianoCore EDKII to a new platform + +This section describes the common process of adding a new platform based on the Mt. +Jade reference platform. First, construct a minimal EDK2 that can boot to UEFI Shell. +Then, enable features one-by-one such as PCIe, ACPI, SMBIOS, etc. + +1. Create a vendor directory and a board directory with the pattern: `edk2-platforms/Platform//Pkg`. + For the sake of simplicity, the new platform will be under `Platform/Ampere/NewBoardPkg`. +2. Copy `Platform/Ampere/JadePkg` to `Platform/Ampere/NewBoardPkg`. +3. Rename the file name under the `NewBoardPkg` like BoardSetting.cfg and DSC/FDF file to match the platform name. + +### DSC/FDF + +Replace correspondingly the `Jade`/`JADE` with `NewBoard`/`NEWBOARD` throughout the files. + +### Board Setting + +The Board Setting is a collection of board and hardware configurations for an Altra-based ARM64 platform. + +Modify it appropriately for the new platform configuration. + +Please refer to the Ampere Altra Interface Firmware Requirements at the Table 4 for detailed descriptions of each Board Setting fields. + +### PCIe Root Complex Configuration + +Below modules are generic and can be shared among Ampere Altra-based platforms. + +Module Name | Description +-------------------------------------------------------|--------------------------------------------------------------------------------- +Platform/Ampere/JadePkg/Drivers/AcpiPlatformDxe | Patch DSDT and install MCFG, IORT tables +Platform/Ampere/JadePkg/Drivers/PciPlatformDxe | Install PCI platform protocol for post-initialization phase +Silicon/Ampere/AmpereAltraPkg/Drivers/PcieInitPei | Install Root Complex Shared HOB consumed by other modules +Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib | Core library including basic function to initialize the PCIe Root Complex +Silicon/Ampere/AmpereAltraBinPkg/Library/PciePhyLib | Provide functions to initialize the PCIe PHY +Silicon/Ampere/AmpereAltraPkg/Library/PciHostbridgeLib | Provide functions to build and free the array of Root Bridge resource +Silicon/Ampere/AmpereAltraPkg/Library/PciSegmentLib | Provide functions to access the PCIe Configuration space + +The difference between platforms could be the mechanism to control the PERST line +for each PCIe controller and the segment number. Platform code must modify the functions +below in the Platform/Ampere/JadePkg/Library/BoardPcieLib/BoardPcieLib.c to comply +with specific platform hardware. + +This `BoardPcieAssertPesrt()` function controls the toggling of PERST to release reset to endpoint card. +The Mt. Jade platform uses a combination of GPIO pins to assert PERST to endpoint card. + +**BoardPcieAssertPerst** + + ```c + /** + Assert PERST of the PCIe controller + + @param[in] RootComplex Root Complex instance. + @param[in] PcieIndex PCIe controller index of input Root Complex. + @param[in] IsPullToHigh Target status for the PERST. + + @retval RETURN_SUCCESS The operation is successful. + @retval Others An error occurred. + **/ + RETURN_STATUS + EFIAPI + BoardPcieAssertPerst ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex, + IN BOOLEAN IsPullToHigh + ); + ``` + +This `BoardPcieGetSegmentNumber()` defines the segment number mapping of PCIe root complexes. +See the content in the inline function for more information about PCIe segment mapping in Ampere Mt. Jade platform. + +**BoardPcieGetSegmentNumber** + + ```c + /** + Override the segment number for a root complex with a board specific number. + + @param[in] RootComplex Root Complex instance with properties. + + @retval Segment number corresponding to the input root complex. + Default segment number is 0x0F. + **/ + UINT16 + BoardPcieGetSegmentNumber ( + IN AC01_ROOT_COMPLEX *RootComplex + ); + ``` +Note: Although the PCIe segment number mapping could be changed but it is not recommended +unless the custom board has to use a different mapping. If using different mapping, +along with the change in `BoardPcieGetSegmentNumber()`, the PCIe segment number must be +also updated in ACPI DSDT and MCFG tables. + +### PCIe Hot-Plug + +`Silicon/Ampere/AmpereAltraPkg/Library/PcieHotPlugLib` is the base library that +supports the function calls to start PCIe Hot-Plug service including the SPCI callings +to ATF to set hot-plug port map, and GPIO or I2C for PCIe reset. There are two functions +in this library that a custom board can use to change the hot-plug configuration +if it is different from Mt. Jade platform: `PcieHotPlugSetPortMap()` and +`PcieHotplugSetGpioMap()`. By default, the configuration of Ampere Mt. Jade 2U platform +will be set. In case the new board uses a different configuration, user can override +the default PCDs used by these two functions with the custom configuration in `NewBoard.dsc`. + +**PcieHotplugSetPortMap()** + +Function `PcieHotplugSetPortMap()` passes hot-plug configuration to Trusted Firmware-A (TF-A) +to update/customize port map entries such as I2C address, RCA port, RCA sub-port, etc. +It uses SPCI PORTMAP_SET_CMD to send that information to ATF. +Refer to the document titled Altra ATF Interface Specification for more details on this function. + +A custom platform that does not use the same Hot-Plug PortMap configuration with +Ampere Mt. Jade 2U platform must define the corresponding PCD below. + +```c +# +# Flag to indicate option of using default or specific platform Port Map table +# Set TRUE: if using the default setting in Ampere Mt. Jade 2U platform. +# Set FALSE: if using a custom PCIe Hot-Plug Port Map settings. +# +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.UseDefaultConfig|TRUE + +# +# Setting Portmap table +# +# * Elements of array: +# - 0: Index of Portmap entry in Portmap table structure (Vport). +# - 1: Socket number (Socket). +# - 2: Root complex port for each Portmap entry (RcaPort). +# - 3: Root complex sub-port for each Portmap entry (RcaSubPort). +# - 4: Select output port of IO expander (PinPort). +# - 5: I2C address of IO expander that CPLD backplane simulates (I2cAddress). +# - 6: Address of I2C switch between CPU and CPLD backplane (MuxAddress). +# - 7: Channel of I2C switch (MuxChannel). +# - 8: It is set from PcieHotPlugSetGpioMap () function to select GPIO[16:21] (PcdPcieHotPlugGpioResetMap) or I2C for PCIe reset purpose. +# - 9: Segment of root complex (Segment). +# - 10: SSD slot index on the front panel of backplane (DriveIndex). +# +# * Caution: +# - The last array ({ 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF }) require if no fully structured used. +# - Size of Portmap table: PortMap[MAX_PORTMAP_ENTRY][sizeof(PCIE_HOTPLUG_PORTMAP_ENTRY)] <=> PortMap[96][11]. +# * Example: Bellow configuration is an example for Portmap table of Mt. Jade 2U platform. +# +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[0]|{ 0, 0, 2, 0, 0, 0x00, 0x00, 0x0, 0, 1, 0xFF } # S0 RCA2.0 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[1]|{ 1, 0, 3, 0, 1, 0x00, 0x00, 0x0, 0, 0, 0xFF } # S0 RCA3.0 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[2]|{ 2, 0, 4, 0, 2, 0x27, 0x70, 0x1, 0, 2, 6 } # S0 RCB0.0 - SSD6 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[3]|{ 3, 0, 4, 2, 3, 0x27, 0x70, 0x1, 0, 2, 7 } # S0 RCB0.2 - SSD7 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[4]|{ 4, 0, 4, 4, 0, 0x25, 0x70, 0x1, 0, 2, 2 } # S0 RCB0.4 - SSD2 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[5]|{ 5, 0, 4, 6, 1, 0x25, 0x70, 0x1, 0, 2, 3 } # S0 RCB0.6 - SSD3 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[6]|{ 6, 0, 5, 0, 0, 0x24, 0x70, 0x1, 0, 3, 0 } # S0 RCB1.0 - SSD0 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[7]|{ 7, 0, 5, 2, 1, 0x24, 0x70, 0x1, 0, 3, 1 } # S0 RCB1.2 - SSD1 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[8]|{ 8, 0, 5, 4, 2, 0x26, 0x70, 0x1, 0, 3, 4 } # S0 RCB1.4 - SSD4 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[9]|{ 9, 0, 5, 6, 3, 0x26, 0x70, 0x1, 0, 3, 5 } # S0 RCB1.6 - SSD5 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[10]|{ 10, 0, 6, 0, 2, 0x00, 0x00, 0x0, 0, 4, 0xFF } # S0 RCB2.0 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[11]|{ 11, 0, 6, 2, 3, 0x00, 0x00, 0x0, 0, 4, 0xFF } # S0 RCB2.2 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[12]|{ 12, 0, 6, 4, 0, 0x00, 0x00, 0x0, 0, 4, 0xFF } # S0 RCB2.4 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[13]|{ 13, 0, 7, 0, 1, 0x00, 0x00, 0x0, 0, 5, 0xFF } # S0 RCB3.0 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[14]|{ 14, 0, 7, 4, 2, 0x00, 0x00, 0x0, 0, 5, 0xFF } # S0 RCB3.4 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[15]|{ 15, 0, 7, 6, 3, 0x00, 0x00, 0x0, 0, 5, 0xFF } # S0 RCB3.6 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[16]|{ 16, 1, 2, 0, 0, 0x26, 0x70, 0x2, 0, 6, 20 } # S1 RCA2.0 - SSD20 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[17]|{ 17, 1, 2, 1, 1, 0x26, 0x70, 0x2, 0, 6, 21 } # S1 RCA2.1 - SSD21 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[18]|{ 18, 1, 2, 2, 2, 0x27, 0x70, 0x2, 0, 6, 22 } # S1 RCA2.2 - SSD22 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[19]|{ 19, 1, 2, 3, 3, 0x27, 0x70, 0x2, 0, 6, 23 } # S1 RCA2.3 - SSD23 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[20]|{ 20, 1, 3, 0, 0, 0x00, 0x00, 0x0, 0, 7, 0xFF } # S1 RCA3.0 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[21]|{ 21, 1, 3, 2, 1, 0x00, 0x00, 0x0, 0, 7, 0xFF } # S1 RCA3.2 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[22]|{ 22, 1, 4, 0, 0, 0x00, 0x00, 0x0, 0, 8, 0xFF } # S1 RCB0.0 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[23]|{ 23, 1, 4, 4, 2, 0x25, 0x70, 0x2, 0, 8, 18 } # S1 RCB0.4 - SSD18 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[24]|{ 24, 1, 4, 6, 3, 0x25, 0x70, 0x2, 0, 8, 19 } # S1 RCB0.6 - SSD19 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[25]|{ 25, 1, 5, 0, 0, 0x24, 0x70, 0x2, 0, 9, 16 } # S1 RCB1.0 - SSD16 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[26]|{ 26, 1, 5, 2, 1, 0x24, 0x70, 0x2, 0, 9, 17 } # S1 RCB1.2 - SSD17 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[27]|{ 27, 1, 5, 4, 2, 0x00, 0x00, 0x0, 0, 9, 0xFF } # S1 RCB1.4 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[28]|{ 28, 1, 6, 0, 3, 0x25, 0x70, 0x4, 0, 10, 11 } # S1 RCB2.0 - SSD11 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[29]|{ 29, 1, 6, 2, 2, 0x25, 0x70, 0x4, 0, 10, 10 } # S1 RCB2.2 - SSD10 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[30]|{ 30, 1, 6, 4, 1, 0x27, 0x70, 0x4, 0, 10, 15 } # S1 RCB2.4 - SSD15 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[31]|{ 31, 1, 6, 6, 0, 0x27, 0x70, 0x4, 0, 10, 14 } # S1 RCB2.6 - SSD14 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[32]|{ 32, 1, 7, 0, 3, 0x26, 0x70, 0x4, 0, 11, 13 } # S1 RCB3.0 - SSD13 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[33]|{ 33, 1, 7, 2, 2, 0x26, 0x70, 0x4, 0, 11, 12 } # S1 RCB3.2 - SSD12 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[34]|{ 34, 1, 7, 4, 1, 0x24, 0x70, 0x4, 0, 11, 9 } # S1 RCB3.4 - SSD9 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[35]|{ 35, 1, 7, 6, 0, 0x24, 0x70, 0x4, 0, 11, 8 } # S1 RCB3.6 - SSD8 +gAmpereTokenSpaceGuid.PcdPcieHotPlugPortMapTable.PortMap[36]|{ 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF } # Require if no fully structure used +``` + +**PcieHotplugSetGpioMap()** + +Function `PcieHotplugSetGpioMap()` is used to limit the number of GPIO[16:21] pins or +use I2C instead of GPIO for PCIe reset. It uses SPCI GPIOMAP_CMD to send information to ATF. +Ampere EDK2 defines `gPcieHotPlugGpioResetMap` PCD to allow user to define a custom GPIO Map. +In `NewBoard.dsc`, user should set the new PCD indicating the GPIO Map for PCIe Hot-Plug. +Below is the GPIO Reset Map PCD definition for Ampere Mt. Jade platform. + +```c +gAmpereTokenSpaceGuid.PcdPcieHotPlugGpioResetMap|0x3F +``` + +### ACPI + +The ACPI tables are split into two main parts: common ACPI tables and platform specific ACPI tables. +The common tables are under AmpereAltraPkg so that they can be reused by different platforms. + +In order to modify the platform specific tables under `Platform/Ampere/NewBoardPkg/AcpiTables`, +update correspondingly with the following points: + +* DSDT/GED1 Device (Shutdown Button) + * The interrupt number, which is currently set to 327. +* DSDT/I2C4 Device (IPMI SSIF) + * The values of the SSCN (Standard Mode) and FMCN (Fast Mode) based on the new platform's frequency calibration. + * The BMC slave address in the I2cSerialBusV2 () function, which is currently set to 0x0010. +* DSDT/UART Devices + * There are three UART devices that can be used by EDK2: + * UART0 for ACPI SPCR table (CPU main console) + * UART2 for ACPI DBG2 table + * UART3: non-use + * Add or remove UART devices based on the platform configuration. +* DSDT/MHPP for Memory Hot-Plug Port Mapping + * This is a shared DRAM memory region between ATF and UEFI to indicate a PCIe hot-plug event action. + An item of this MHPP region is used to reflect a hot-plug event action of each PCIe port. + Whenever there is a PCIe hot-plug event, ATF will update the action value to each item of MHPP region, + and UEFI will handle the hot-plug ejection/insertion accordingly + * An item of MHPP is 24-bytes in size and is named by a 4-character word (mnemonic) to represent a PCIe Root Port device. + The name format is . + For example, if the board has a PCIe Root Port device RCA2.0 on socket 1 the hot-plug event action item must be named A120. + * This MHPP region is declared in the file named `Platform/Ampere/JadePkg/AcpiTable/MHPP.asi`. + For Altra Max, this ACPI table is located at `Platform/Ampere/JadePkg/Ac02AcpiTables/MHPP.asi`. + If the new board has the list of PCIe devices different than the Ampere Mt. Jade board, these items in MHPP region must be renamed. +* DSDT/PCI for Hot-Plug events + * The files to declare the PCIe Root Port are `Platform/Ampere/JadePkg/AcpiTables/{PCI-S0.asi, PCI-S1.asi}`. + Note that the ACPI tables for Altra Max based platform is at `Platform/Ampere/JadePkg/Ac02AcpiTables/` folder. + To support full hot-plug feature (software and hardware hot-plug with LED behavior functioning), + the Segment:Bus:Device.Function (S:B:D.F) of the Root Port must be declared correctly. Follow the steps listed below to declare them. + * List down PCIe devices tree in Linux using lspci -tv command to see all Root Port S:B:D.F values. + * Find out which PCIe device includes the segment that the Root Port has. + * Based on this S:B:D.F, declare Root Port device into the PCIe Device just found. + * Add _STA and _EJ0 instance to handle insertion and ejection events. + +### SMBIOS + +Two key modules are responsible for producing SMBIOS tables: **SmbiosPlatformDxe** and **OemMiscLib**. These modules are essential for +defining the platform-specific information derived from hardware characteristics of your platform. + +You need to update these modules SmbiosPlatformDxe and OemMiscLib to reflect your platform's hardware. + +Most of fields in SMBIOS Type 1, 2, and 3 are fetched from FRU Device via the IPMI SSIF interface. +If the new platform does not support the IPMI SSIF, please drop the support, especially the `IpmiFruInfo.c` file. + +### RTC + +On Ampere Mt. Jade, the hardware RTC connects to I2C6 on the master socket and validated with PCF85063AT chip. +The GPI4 is dedicated for obtaining exclusive access because the RTC is shared between BMC and the SoC. + +If you use the same RTC hardware on the new platform, modify appropriately the following macros for the I2C and GPI: + +* GPI + * `I2C_RTC_ACCESS_GPIO_PIN`: RTC Access Lock +* I2C + * `I2C_RTC_BUS_ADDRESS`: I2C bus + * `I2C_RTC_BUS_SPEED`: I2C bus speed + * `I2C_RTC_CHIP_ADDRESS`: I2C slave address of the RTC + +If you use a different RTC on the new platform, you will need to port to the new hardware RTC chip. + +You can also leverage `EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf` if the hardware RTC has not supported yet. + +### IPMI/SSIF + +The IPMI SSIF is used for the communication between the BMC and the SoC. It requires the following platform-specific modules: + +* `Silicon/Ampere/AmpereAltraPkg/Drivers/SmbusHc/SmbusHcDxe.inf`: This driver produces the SMBUS protocol at DXE phase. +* `Silicon/Ampere/AmpereAltraPkg/Drivers/SmbusHc/SmbusHcPei.inf`: This driver produces the SMBUS protocol at PEI phase. +* `Platform/Ampere/NewBoardPkg/Library/PlatformBmcReadyLib/PlatformBmcReadyLib.inf`: + This platform-dependent library provides API to check BMC status before executing SMBus commands for IPMI communication. + On the Mt. Jade platform, the Altra SoC relies on GPI0 (referred to as BMC_READY GPIO) to determine status of BMC is ready for the IPMI communication. + If the custom platform does not support this pin, use a NULL library that assumes BMC is always on and ready to accept IPMI messages. Put below line to NewBoard.dsc. + + ```c + Features/ManageabilityPkg/Library/PlatformBmcReadyLibNull/PlatformBmcReadyLibNull.inf + ``` + +In order to enable the IPMI SSIF support: + +1. Ensure that the I2C4 is connected to the BMC and the frequency is operated at 400KHz. +2. Update correspondingly the following PCDs if there are different configuration + from the BMC slave address and the BMC_READY GPIO if the custom board uses BMC_READY GPIO to determine the status of BMC. + * `gAmpereTokenSpaceGuid.PcdBmcReadyGpio` + +### Signed Capsule Update + +There are two types of the firmware image supported on the Ampere Mt. Jade platform: + +* The combination of Ampere Trusted Firmware-A (TF-A) FIP, Board Settings, and EDK2 FD. +* Ampere System Firmware (SCP Firmware) + +In order to enable the capsule update feature, update the IMAGE_ID and IMAGE_ID_STRING +to match the platform name in the `Platform/Ampere/NewBoardPkg/Capsule/SystemFirmwareDescriptor/SystemFirmwareDescriptor.aslc` file. + +Please refer to +[Capsule Based Firmware Update and Firmware Recovery](https://github.com/tianocore/tianocore.github.io/wiki/Capsule-Based-Firmware-Update-and-Firmware-Recovery) +for detailed porting instructions. + +### Trusted Platform Module (TPM) + +No change is needed. + +It's important that the ATF running on the new platform must comply with the Ampere Altra Interface Firmware Requirements. + +### Secure Boot and Update +No change is needed. The new platform can inherit the required libraries and modules to support this feature. + +Ampere EDK2 supports UEFI Secure Boot and it complies with UEFI Specifications. +User can change the UEFI secure boot keys using UEFI Menu Setup. + +If the DBB/DBU secure keys are deployed, requires to boot and update EDK2 firmware via capsule using the signed firmware image. + +Users can run firmware update at run-time OS without reboot using +[amp_fwupgrade](https://github.com/AmpereComputing/amp-fwupgrade) utility to update signed ATF-UEFI EDK2 image and signed SCP image. + +--- + +## Contact Information + +If you have the patches and want to share with Ampere, use git-send-mail to Ampere EDK2 maintainers/reviewers +* Nhi Pham +* Chuong Tran +* Rebecca Cran + +For any technical questions that you want to consult with, send email to Ampere Support support@amperecomputing.com.