Reviewed-by: Chao Li  <lichao@loongson.cn>


Thanks,
Chao
--------

On 11月 11 2022, at 5:12 δΈ‹εˆ, xianglai li <lixianglai@loongson.cn> wrote:
The Library provides Boot Manager interfaces.



REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054



Cc: Bibo Mao <maobibo@loongson.cn>

Cc: Chao Li <lichao@loongson.cn>

Cc: Leif Lindholm <quic_llindhol@quicinc.com>

Cc: Liming Gao <gaoliming@byosoft.com.cn>

Cc: Michael D Kinney <michael.d.kinney@intel.com>

Signed-off-by: xianglai li <lixianglai@loongson.cn>

---

.../PlatformBootManagerLib/PlatformBm.c | 742 ++++++++++++++++++

.../PlatformBootManagerLib/PlatformBm.h | 112 +++

.../PlatformBootManagerLib.inf | 75 ++

.../PlatformBootManagerLib/QemuKernel.c | 81 ++

4 files changed, 1010 insertions(+)

create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.c

create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.h

create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf

create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/QemuKernel.c



diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.c b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.c

new file mode 100644

index 0000000000..eb7f4241f0

--- /dev/null

+++ b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.c

@@ -0,0 +1,742 @@

+/** @file

+ Implementation for PlatformBootManagerLib library class interfaces.

+

+ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+ SPDX-License-Identifier: BSD-2-Clause-Patent

+

+**/

+

+#include <IndustryStandard/Pci22.h>

+#include <Library/BootLogoLib.h>

+#include <Library/PcdLib.h>

+#include <Library/QemuBootOrderLib.h>

+#include <Library/UefiBootManagerLib.h>

+#include <Protocol/FirmwareVolume2.h>

+#include <Protocol/LoadedImage.h>

+#include <Protocol/PciIo.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/DebugLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/UefiLib.h>

+#include <Library/BaseMemoryLib.h>

+#include "PlatformBm.h"

+

+STATIC PLATFORM_SERIAL_CONSOLE mSerialConsole = {

+ //

+ // VENDOR_DEVICE_PATH SerialDxe

+ //

+ {

+ { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, DP_NODE_LEN (VENDOR_DEVICE_PATH) },

+ SERIAL_DXE_FILE_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)

+ }

+};

+

+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)

+ }

+};

+

+/**

+ 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.

+**/

+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", __FUNCTION__, ProtocolGuid,

+ Status));

+ return;

+ }

+

+ ASSERT (NoHandles > 0);

+ for (Idx = 0; Idx < NoHandles; ++Idx) {

+ CHAR16 *DevicePathText;

+ STATIC CHAR16 Fallback[] = L"<device path unavailable>";

+

+ //

+ // 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.

+

+ @param Handle The handle to check

+ @param ReportText A pointer to a string at the time of the error.

+

+ @retval TURE THe handle corresponds to a PCI display device.

+ @retval FALSE THe handle does not corresponds to a PCI display device.

+**/

+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", __FUNCTION__, ReportText, Status));

+ return FALSE;

+ }

+

+ return IS_PCI_DISPLAY (&Pci);

+}

+

+/**

+ This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking

+ the matching driver to produce all first-level child handles.

+

+ @param Handle The handle to connect.

+ @param ReportText A pointer to a string at the time of the error.

+

+ @retval VOID

+**/

+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",

+ __FUNCTION__, ReportText, Status));

+}

+

+/**

+ This CALLBACK_FUNCTION retrieves the EFI_DEVICE_PATH_PROTOCOL from the

+ handle, and adds it to ConOut and ErrOut.

+

+ @param Handle The handle to retrieves.

+ @param ReportText A pointer to a string at the time of the error.

+

+ @retval VOID

+**/

+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",

+ __FUNCTION__, ReportText, Handle));

+ return;

+ }

+

+ Status = EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);

+ if (EFI_ERROR (Status)) {

+ DEBUG ((DEBUG_ERROR, "%a: %s: adding to ConOut: %r\n", __FUNCTION__,

+ ReportText, Status));

+ return;

+ }

+

+ Status = EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);

+ if (EFI_ERROR (Status)) {

+ DEBUG ((DEBUG_ERROR, "%a: %s: adding to ErrOut: %r\n", __FUNCTION__,

+ ReportText, Status));

+ return;

+ }

+

+ DEBUG ((DEBUG_VERBOSE, "%a: %s: added to ConOut and ErrOut\n", __FUNCTION__,

+ ReportText));

+}

+/**

+ Register the boot option.

+

+ @param FileGuid File Guid.

+ @param Description Option descriptor.

+ @param Attributes Option Attributes.

+

+ @retval VOID

+**/

+VOID

+PlatformRegisterFvBootOption (

+ IN EFI_GUID *FileGuid,

+ IN CHAR16 *Description,

+ IN UINT32 Attributes

+ )

+{

+ 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);

+ }

+ EfiBootManagerFreeLoadOption (&NewOption);

+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);

+}

+

+/**

+ Remove all MemoryMapped (...)/FvFile (...) and Fv (...)/FvFile (...) boot options

+ whose device paths do not resolve exactly to an FvFile in the system.

+

+ This removes any boot options that point to binaries built into the firmware

+ and have become stale due to any of the following:

+ - FvMain's base address or size changed (historical),

+ - FvMain's FvNameGuid changed,

+ - the FILE_GUID of the pointed-to binary changed,

+ - the referenced binary is no longer built into the firmware.

+

+ EfiBootManagerFindLoadOption () used in PlatformRegisterFvBootOption () only

+ avoids exact duplicates.

+**/

+VOID

+RemoveStaleFvFileOptions (

+ VOID

+ )

+{

+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;

+ UINTN BootOptionCount;

+ UINTN Index;

+

+ BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount,

+ LoadOptionTypeBoot);

+

+ for (Index = 0; Index < BootOptionCount; ++Index) {

+ EFI_DEVICE_PATH_PROTOCOL *Node1, *Node2, *SearchNode;

+ EFI_STATUS Status;

+ EFI_HANDLE FvHandle;

+

+ //

+ // If the device path starts with neither MemoryMapped (...) nor Fv (...),

+ // then keep the boot option.

+ //

+ Node1 = BootOptions[Index].FilePath;

+ if (!(DevicePathType (Node1) == HARDWARE_DEVICE_PATH

+ && DevicePathSubType (Node1) == HW_MEMMAP_DP)

+ && !(DevicePathType (Node1) == MEDIA_DEVICE_PATH

+ && DevicePathSubType (Node1) == MEDIA_PIWG_FW_VOL_DP))

+ {

+ continue;

+ }

+

+ //

+ // If the second device path node is not FvFile (...), then keep the boot

+ // option.

+ //

+ Node2 = NextDevicePathNode (Node1);

+ if ((DevicePathType (Node2) != MEDIA_DEVICE_PATH)

+ || (DevicePathSubType (Node2) != MEDIA_PIWG_FW_FILE_DP))

+ {

+ continue;

+ }

+

+ //

+ // Locate the Firmware Volume2 protocol instance that is denoted by the

+ // boot option. If this lookup fails (i.e., the boot option references a

+ // firmware volume that doesn't exist), then we'll proceed to delete the

+ // boot option.

+ //

+ SearchNode = Node1;

+ Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid,

+ &SearchNode, &FvHandle);

+

+ if (!EFI_ERROR (Status)) {

+ //

+ // The firmware volume was found; now let's see if it contains the FvFile

+ // identified by GUID.

+ //

+ EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;

+ MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFileNode;

+ UINTN BufferSize;

+ EFI_FV_FILETYPE FoundType;

+ EFI_FV_FILE_ATTRIBUTES FileAttributes;

+ UINT32 AuthenticationStatus;

+

+ Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid,

+ (VOID **)&FvProtocol);

+ ASSERT_EFI_ERROR (Status);

+

+ FvFileNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)Node2;

+ //

+ // Buffer==NULL means we request metadata only: BufferSize, FoundType,

+ // FileAttributes.

+ //

+ Status = FvProtocol->ReadFile (

+ FvProtocol,

+ &FvFileNode->FvFileName, // NameGuid

+ NULL, // Buffer

+ &BufferSize,

+ &FoundType,

+ &FileAttributes,

+ &AuthenticationStatus

+ );

+ if (!EFI_ERROR (Status)) {

+ //

+ // The FvFile was found. Keep the boot option.

+ //

+ continue;

+ }

+ }

+

+ //

+ // Delete the boot option.

+ //

+ Status = EfiBootManagerDeleteLoadOptionVariable (

+ BootOptions[Index].OptionNumber, LoadOptionTypeBoot);

+ DEBUG_CODE (

+ CHAR16 *DevicePathString;

+

+ DevicePathString = ConvertDevicePathToText (BootOptions[Index].FilePath,

+ FALSE, FALSE);

+ DEBUG ((

+ EFI_ERROR (Status) ? EFI_D_WARN : DEBUG_VERBOSE,

+ "%a: removing stale Boot#%04x %s: %r\n",

+ __FUNCTION__,

+ (UINT32)BootOptions[Index].OptionNumber,

+ DevicePathString == NULL ? L"<unavailable>" : DevicePathString,

+ Status

+ ));

+ if (DevicePathString != NULL) {

+ FreePool (DevicePathString);

+ }

+ );

+ }

+

+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);

+}

+

+/**

+ Register the boot option And Keys.

+

+ @param VOID

+

+ @retval VOID

+**/

+VOID

+PlatformRegisterOptionsAndKeys (

+ VOID

+ )

+{

+ EFI_STATUS Status;

+ EFI_INPUT_KEY Enter;

+ EFI_INPUT_KEY F2;

+ EFI_INPUT_KEY Esc;

+ EFI_BOOT_MANAGER_LOAD_OPTION BootOption;

+

+ //

+ // 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

+ )

+{

+ RETURN_STATUS PcdStatus;

+

+ //

+ // 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);

+

+ //

+ // Signal the ACPI platform driver that it can download QEMU ACPI tables.

+ //

+ EfiEventGroupSignal (&gRootBridgesConnectedEventGroupGuid);

+

+ //

+ // 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);

+

+ //

+ // 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.

+ //

+ 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);

+

+ //

+ // Set the front page timeout from the QEMU configuration.

+ //

+ PcdStatus = PcdSet16S (PcdPlatformBootTimeOut,

+ GetFrontPageTimeoutFromQemu ());

+ ASSERT_RETURN_ERROR (PcdStatus);

+

+ //

+ // Register platform-specific boot options and keyboard shortcuts.

+ //

+ PlatformRegisterOptionsAndKeys ();

+}

+

+/**

+ 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 senarino

+ > Signal console ready platform customized event

+ > Run diagnostics like memory testing

+ > Connect certain devices

+ > Dispatch aditional option roms

+ > Special boot: e.g.: USB boot, enter UI

+**/

+VOID

+EFIAPI

+PlatformBootManagerAfterConsole (

+ VOID

+ )

+{

+ //

+ // Show the splash screen.

+ //

+ BootLogoEnableLogo ();

+

+ //

+ // Connect the rest of the devices.

+ //

+ EfiBootManagerConnectAll ();

+

+ //

+ // Process QEMU's -kernel command line option. Note that the kernel booted

+ // this way should receive ACPI tables, which is why we connect all devices

+ // first (see above) -- PCI enumeration blocks ACPI table installation, if

+ // there is a PCI host.

+ //

+ TryRunningQemuKernel ();

+

+ //

+ // Enumerate all possible boot options, then filter and reorder them based on

+ // the QEMU configuration.

+ //

+ EfiBootManagerRefreshAllBootOption ();

+

+ //

+ // Register UEFI Shell

+ //

+ PlatformRegisterFvBootOption (

+ &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE

+ );

+

+ RemoveStaleFvFileOptions ();

+ SetBootOrderFromQemu ();

+}

+

+/**

+ This function is called each second during the boot manager waits the

+ timeout.

+

+ @param TimeoutRemain The remaining timeout.

+**/

+VOID

+EFIAPI

+PlatformBootManagerWaitCallback (

+ IN UINT16 TimeoutRemain

+ )

+{

+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black;

+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White;

+ UINT16 Timeout;

+

+ Timeout = PcdGet16 (PcdPlatformBootTimeOut);

+

+ Black.Raw = 0x00000000;

+ White.Raw = 0x00FFFFFF;

+

+ BootLogoUpdateProgress (

+ White.Pixel,

+ Black.Pixel,

+ L"Start boot option",

+ White.Pixel,

+ (Timeout - TimeoutRemain) * 100 / Timeout,

+ 0

+ );

+}

+

+/**

+ 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_INPUT_KEY Key;

+ EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;

+ UINTN Index;

+

+ //

+ // BootManagerMenu doesn't contain the correct information when return status

+ // is EFI_NOT_FOUND.

+ //

+ Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);

+ if (EFI_ERROR (Status)) {

+ return;

+ }

+ //

+ // Normally BdsDxe does not print anything to the system console, but this is

+ // a last resort -- the end-user will likely not see any DEBUG messages

+ // logged in this situation.

+ //

+ // AsciiPrint () will NULL-check gST->ConOut internally. We check gST->ConIn

+ // here to see if it makes sense to request and wait for a keypress.

+ //

+ if (gST->ConIn != NULL) {

+ AsciiPrint (

+ "%a: No bootable option or device was found.\n"

+ "%a: Press any key to enter the Boot Manager Menu.\n",

+ gEfiCallerBaseName,

+ gEfiCallerBaseName

+ );

+ Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index);

+ ASSERT_EFI_ERROR (Status);

+ ASSERT (Index == 0);

+

+ //

+ // Drain any queued keys.

+ //

+ while (!EFI_ERROR (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key))) {

+ //

+ // just throw away Key

+ //

+ }

+ }

+

+ for (;;) {

+ EfiBootManagerBoot (&BootManagerMenu);

+ }

+}

diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.h b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.h

new file mode 100644

index 0000000000..f20c78252f

--- /dev/null

+++ b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBm.h

@@ -0,0 +1,112 @@

+/** @file

+ Head file for BDS Platform specific code

+

+ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+ SPDX-License-Identifier: BSD-2-Clause-Patent

+

+**/

+

+#ifndef PLATFORM_BM_H_

+#define PLATFORM_BM_H_

+

+#include <Library/DevicePathLib.h>

+

+#define DP_NODE_LEN(Type) { (UINT8)sizeof (Type), (UINT8)(sizeof (Type) >> 8) }

+

+#define SERIAL_DXE_FILE_GUID { \

+ 0xD3987D4B, 0x971A, 0x435F, \

+ { 0x8C, 0xAF, 0x49, 0x67, 0xEB, 0x62, 0x72, 0x41 } \

+ }

+

+#define ALIGN_UP(addr, align) \

+ ((addr + (typeof (addr)) align - 1) & ~((typeof (addr)) align - 1))

+

+#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 ()

+

+#pragma pack (1)

+typedef struct {

+ USB_CLASS_DEVICE_PATH Keyboard;

+ EFI_DEVICE_PATH_PROTOCOL End;

+} PLATFORM_USB_KEYBOARD;

+#pragma pack ()

+

+/**

+ 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

+ );

+

+/**

+ * execute from kernel entry point.

+ *

+ * @param[in] Argc The count of args.

+ * @param[in] Argv The pointer to args array.

+ * @param[in] Bpi The pointer to bootparaminterface struct.

+ * @param[in] Vec The fourth args for kernel.

+ ***/

+typedef

+VOID

+(EFIAPI *EFI_KERNEL_ENTRY_POINT) (

+ IN UINTN Argc,

+ IN VOID *Argv,

+ IN VOID *Bpi,

+ IN VOID *Vec

+ );

+

+/**

+ Download the kernel, the initial ramdisk, and the kernel command line from

+ QEMU's fw_cfg. Construct a minimal SimpleFileSystem that contains the two

+ image files, and load and start the kernel from it.

+

+ The kernel will be instructed via its command line to load the initrd from

+ the same Simple FileSystem.

+

+ @retval EFI_NOT_FOUND Kernel image was not found.

+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.

+ @retval EFI_PROTOCOL_ERROR Unterminated kernel command line.

+

+ @return Error codes from any of the underlying

+ functions. On success, the function doesn't

+ return.

+**/

+EFI_STATUS

+EFIAPI

+TryRunningQemuKernel (

+ VOID

+ );

+

+#endif // PLATFORM_BM_H_

diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf

new file mode 100644

index 0000000000..0ea6fea5c5

--- /dev/null

+++ b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf

@@ -0,0 +1,75 @@

+## @file

+# Implementation for PlatformBootManagerLib library class interfaces.

+#

+# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>

+#

+# SPDX-License-Identifier: BSD-2-Clause-Patent

+#

+##

+

+[Defines]

+ INF_VERSION = 0x00010005

+ BASE_NAME = PlatformBootManagerLib

+ FILE_GUID = 469184E8-FADA-41E4-8823-012CA19B40D4

+ MODULE_TYPE = DXE_DRIVER

+ VERSION_STRING = 1.0

+ LIBRARY_CLASS = PlatformBootManagerLib|DXE_DRIVER

+

+#

+# VALID_ARCHITECTURES = LOONGARCH64

+#

+

+[Sources]

+ PlatformBm.c

+ QemuKernel.c

+

+[Packages]

+ Platform/Loongson/LoongArchQemuPkg/Loongson.dec

+ MdeModulePkg/MdeModulePkg.dec

+ MdePkg/MdePkg.dec

+ OvmfPkg/OvmfPkg.dec

+ ShellPkg/ShellPkg.dec

+

+[LibraryClasses]

+ BaseLib

+ BaseMemoryLib

+ BootLogoLib

+ DebugLib

+ DevicePathLib

+ MemoryAllocationLib

+ PcdLib

+ PrintLib

+ QemuBootOrderLib

+ QemuLoadImageLib

+ QemuFwCfgLib

+ UefiBootManagerLib

+ UefiBootServicesTableLib

+ UefiLib

+ UefiRuntimeServicesTableLib

+

+[FixedPcd]

+ gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate

+ gEfiMdePkgTokenSpaceGuid.PcdUartDefaultDataBits

+ gEfiMdePkgTokenSpaceGuid.PcdUartDefaultParity

+ gEfiMdePkgTokenSpaceGuid.PcdUartDefaultStopBits

+

+[Pcd]

+ gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut

+

+[Guids]

+ gEfiFileInfoGuid

+ gEfiFileSystemInfoGuid

+ gEfiFileSystemVolumeLabelInfoIdGuid

+ gEfiEndOfDxeEventGroupGuid

+ gRootBridgesConnectedEventGroupGuid

+ gUefiShellFileGuid

+ gEfiLoongsonBootparamsTableGuid ## SOMETIMES_PRODUCES ## SystemTable

+ gEfiTtyTermGuid

+

+[Protocols]

+ gEfiDevicePathProtocolGuid

+ gEfiFirmwareVolume2ProtocolGuid

+ gEfiGraphicsOutputProtocolGuid

+ gEfiLoadedImageProtocolGuid

+ gEfiPciRootBridgeIoProtocolGuid

+ gEfiSimpleFileSystemProtocolGuid

diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/QemuKernel.c b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/QemuKernel.c

new file mode 100644

index 0000000000..386003a8d7

--- /dev/null

+++ b/Platform/Loongson/LoongArchQemuPkg/Library/PlatformBootManagerLib/QemuKernel.c

@@ -0,0 +1,81 @@

+/** @file

+ Try to run Linux kernel.

+

+ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+ SPDX-License-Identifier: BSD-2-Clause-Patent

+

+ @par Glossary:

+ - mem - Memory

+ - Bpi - Boot Parameter Interface

+ - FwCfg - FirmWare Configure

+**/

+

+#include <Library/QemuLoadImageLib.h>

+#include <Library/ReportStatusCodeLib.h>

+

+#include <Library/BaseLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/DebugLib.h>

+#include <Library/DevicePathLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/UefiLib.h>

+#include <Library/UefiRuntimeServicesTableLib.h>

+

+/**

+ Download the kernel, the initial ramdisk, and the kernel command line from

+ QEMU's fw_cfg. Construct a minimal SimpleFileSystem that contains the two

+ image files, and load and start the kernel from it.

+

+ The kernel will be instructed via its command line to load the initrd from

+ the same Simple FileSystem.

+

+ @retval EFI_NOT_FOUND Kernel image was not found.

+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.

+ @retval EFI_PROTOCOL_ERROR Unterminated kernel command line.

+

+ @return Error codes from any of the underlying

+ functions. On success, the function doesn't

+ return.

+**/

+EFI_STATUS

+TryRunningQemuKernel (

+ VOID

+ )

+{

+ EFI_STATUS Status;

+ EFI_HANDLE KernelImageHandle;

+

+ Status = QemuLoadKernelImage (&KernelImageHandle);

+ if (EFI_ERROR (Status)) {

+ return Status;

+ }

+

+ //

+ // Signal the EFI_EVENT_GROUP_READY_TO_BOOT event.

+ //

+ EfiSignalEventReadyToBoot ();

+

+ REPORT_STATUS_CODE (

+ EFI_PROGRESS_CODE,

+ (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT)

+ );

+

+ //

+ // Start the image.

+ //

+ Status = QemuStartKernelImage (&KernelImageHandle);

+ if (EFI_ERROR (Status)) {

+ DEBUG ((

+ DEBUG_ERROR,

+ "%a: QemuStartKernelImage(): %r\n",

+ __FUNCTION__,

+ Status

+ ));

+ }

+

+ QemuUnloadKernelImage (KernelImageHandle);

+

+ return Status;

+}

--

2.31.1