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; } diff --git a/Platform/Ampere/JadePkg/Jade.dsc b/Platform/Ampere/JadePkg/Jade.dsc index 223184a7d3..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 @@ -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 diff --git a/Platform/Ampere/JadePkg/Jade.fdf b/Platform/Ampere/JadePkg/Jade.fdf index 24099a1137..655df1cc9a 100644 --- a/Platform/Ampere/JadePkg/Jade.fdf +++ b/Platform/Ampere/JadePkg/Jade.fdf @@ -362,6 +362,8 @@ 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 + INF Silicon/Ampere/AmpereSiliconPkg/Drivers/IpmiBootDxe/IpmiBootDxe.inf # # Networking stack 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. diff --git a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc index 97f93c0d7e..1e39e28919 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 # @@ -848,3 +848,5 @@ !endif #$(INCLUDE_TFTP_COMMAND) 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/AmpereSiliconPkg.dec b/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec index 92d37bc727..28c1c1905e 100644 --- a/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec +++ b/Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.dec @@ -53,6 +53,16 @@ ## 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 + gAmpereVariableGuid = { 0x6B9F9381, 0xB1D0, 0x4991, { 0xBC, 0x4A, 0x17, 0xBC, 0x6B, 0x5B, 0xB0, 0x81 } } + [Ppis] [PcdsFixedAtBuild] 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 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 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_ */ 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_ */ diff --git a/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.c b/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.c new file mode 100644 index 0000000000..c380a32a58 --- /dev/null +++ b/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBm.c @@ -0,0 +1,1201 @@ +/** @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 + +#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; + EFI_EVENT AfterConsoleEvent; + + 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); + + // + // 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); + } +} + +/** + 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..4d44b2b100 --- /dev/null +++ b/Silicon/Ampere/AmpereSiliconPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf @@ -0,0 +1,97 @@ +## @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 + Silicon/Ampere/AmpereSiliconPkg/AmpereSiliconPkg.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] + gAmpereEventAfterConsoleGuid + gBootDiscoveryPolicyMgrFormsetGuid + gEdkiiNonDiscoverableEhciDeviceGuid + gEdkiiNonDiscoverableUhciDeviceGuid + gEdkiiNonDiscoverableXhciDeviceGuid + gEfiBootManagerPolicyNetworkGuid + gEfiBootManagerPolicyConnectAllGuid + gEfiFileInfoGuid + gEfiFileSystemInfoGuid + gEfiFileSystemVolumeLabelInfoIdGuid + gEfiEndOfDxeEventGroupGuid + gEfiTtyTermGuid + gUefiShellFileGuid + +[Protocols] + gEdkiiNonDiscoverableDeviceProtocolGuid + gEfiBootManagerPolicyProtocolGuid + gEfiDevicePathProtocolGuid + gEfiGraphicsOutputProtocolGuid + gEfiLoadedImageProtocolGuid + gEfiPciRootBridgeIoProtocolGuid + gEfiSimpleFileSystemProtocolGuid + gEsrtManagementProtocolGuid + gPlatformBootManagerProtocolGuid