| /** @file | |
| This file include all platform action which can be customized | |
| by IBV/OEM. | |
| Copyright (c) 2015 - 2023, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "PlatformBootManager.h" | |
| #include "PlatformConsole.h" | |
| #include <Protocol/FirmwareVolume2.h> | |
| /** | |
| Signal EndOfDxe event and install SMM Ready to lock protocol. | |
| **/ | |
| VOID | |
| InstallReadyToLock ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_HANDLE Handle; | |
| EFI_SMM_ACCESS2_PROTOCOL *SmmAccess; | |
| DEBUG ((DEBUG_INFO, "InstallReadyToLock entering......\n")); | |
| // | |
| // Inform the SMM infrastructure that we're entering BDS and may run 3rd party code hereafter | |
| // Since PI1.2.1, we need signal EndOfDxe as ExitPmAuth | |
| // | |
| EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid); | |
| DEBUG ((DEBUG_INFO, "All EndOfDxe callbacks have returned successfully\n")); | |
| // | |
| // Install DxeSmmReadyToLock protocol in order to lock SMM | |
| // | |
| Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess); | |
| if (!EFI_ERROR (Status)) { | |
| Handle = NULL; | |
| Status = gBS->InstallProtocolInterface ( | |
| &Handle, | |
| &gEfiDxeSmmReadyToLockProtocolGuid, | |
| EFI_NATIVE_INTERFACE, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| DEBUG ((DEBUG_INFO, "InstallReadyToLock end\n")); | |
| return; | |
| } | |
| /** | |
| Return the index of the load option in the load option array. | |
| The function consider two load options are equal when the | |
| OptionType, Attributes, Description, FilePath and OptionalData are equal. | |
| @param Key Pointer to the load option to be found. | |
| @param Array Pointer to the array of load options to be found. | |
| @param Count Number of entries in the Array. | |
| @retval -1 Key wasn't found in the Array. | |
| @retval 0 ~ Count-1 The index of the Key in the Array. | |
| **/ | |
| INTN | |
| PlatformFindLoadOption ( | |
| IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key, | |
| IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array, | |
| IN UINTN Count | |
| ) | |
| { | |
| UINTN Index; | |
| for (Index = 0; Index < Count; Index++) { | |
| if ((Key->OptionType == Array[Index].OptionType) && | |
| (Key->Attributes == Array[Index].Attributes) && | |
| (StrCmp (Key->Description, Array[Index].Description) == 0) && | |
| (CompareMem (Key->FilePath, Array[Index].FilePath, GetDevicePathSize (Key->FilePath)) == 0) && | |
| (Key->OptionalDataSize == Array[Index].OptionalDataSize) && | |
| (CompareMem (Key->OptionalData, Array[Index].OptionalData, Key->OptionalDataSize) == 0)) | |
| { | |
| return (INTN)Index; | |
| } | |
| } | |
| return -1; | |
| } | |
| /** | |
| Get the FV device path for the shell file. | |
| @return A pointer to device path structure. | |
| **/ | |
| EFI_DEVICE_PATH_PROTOCOL * | |
| BdsGetShellFvDevicePath ( | |
| VOID | |
| ) | |
| { | |
| UINTN FvHandleCount; | |
| EFI_HANDLE *FvHandleBuffer; | |
| UINTN Index; | |
| EFI_STATUS Status; | |
| EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; | |
| UINTN Size; | |
| UINT32 AuthenticationStatus; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| EFI_FV_FILETYPE FoundType; | |
| EFI_FV_FILE_ATTRIBUTES FileAttributes; | |
| Status = EFI_SUCCESS; | |
| gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| NULL, | |
| &FvHandleCount, | |
| &FvHandleBuffer | |
| ); | |
| for (Index = 0; Index < FvHandleCount; Index++) { | |
| Size = 0; | |
| gBS->HandleProtocol ( | |
| FvHandleBuffer[Index], | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| (VOID **)&Fv | |
| ); | |
| Status = Fv->ReadFile ( | |
| Fv, | |
| &gUefiShellFileGuid, | |
| NULL, | |
| &Size, | |
| &FoundType, | |
| &FileAttributes, | |
| &AuthenticationStatus | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Found the shell file | |
| // | |
| break; | |
| } | |
| } | |
| if (EFI_ERROR (Status)) { | |
| if (FvHandleCount) { | |
| FreePool (FvHandleBuffer); | |
| } | |
| return NULL; | |
| } | |
| DevicePath = DevicePathFromHandle (FvHandleBuffer[Index]); | |
| if (FvHandleCount) { | |
| FreePool (FvHandleBuffer); | |
| } | |
| return DevicePath; | |
| } | |
| /** | |
| Register a boot option using a file GUID in the FV. | |
| @param FileGuid The file GUID name in FV. | |
| @param Description The boot option description. | |
| @param Attributes The attributes used for the boot option loading. | |
| **/ | |
| VOID | |
| PlatformRegisterFvBootOption ( | |
| EFI_GUID *FileGuid, | |
| CHAR16 *Description, | |
| UINT32 Attributes | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN OptionIndex; | |
| EFI_BOOT_MANAGER_LOAD_OPTION NewOption; | |
| EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; | |
| UINTN BootOptionCount; | |
| MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid); | |
| DevicePath = AppendDevicePathNode ( | |
| BdsGetShellFvDevicePath (), | |
| (EFI_DEVICE_PATH_PROTOCOL *)&FileNode | |
| ); | |
| Status = EfiBootManagerInitializeLoadOption ( | |
| &NewOption, | |
| LoadOptionNumberUnassigned, | |
| LoadOptionTypeBoot, | |
| Attributes, | |
| Description, | |
| DevicePath, | |
| NULL, | |
| 0 | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); | |
| OptionIndex = PlatformFindLoadOption (&NewOption, BootOptions, BootOptionCount); | |
| if (OptionIndex == -1) { | |
| Status = EfiBootManagerAddLoadOptionVariable (&NewOption, (UINTN)-1); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| EfiBootManagerFreeLoadOption (&NewOption); | |
| EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); | |
| } | |
| } | |
| /** | |
| Do the platform specific action before the console is connected. | |
| Such as: | |
| Update console variable; | |
| Register new Driver#### or Boot####; | |
| Signal ReadyToLock event. | |
| **/ | |
| VOID | |
| EFIAPI | |
| PlatformBootManagerBeforeConsole ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_INPUT_KEY Enter; | |
| EFI_INPUT_KEY CustomKey; | |
| EFI_INPUT_KEY Down; | |
| EFI_BOOT_MANAGER_LOAD_OPTION BootOption; | |
| // | |
| // Register ENTER as CONTINUE key | |
| // | |
| Enter.ScanCode = SCAN_NULL; | |
| Enter.UnicodeChar = CHAR_CARRIAGE_RETURN; | |
| EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL); | |
| if (FixedPcdGetBool (PcdBootManagerEscape)) { | |
| // | |
| // Map Esc to Boot Manager Menu | |
| // | |
| CustomKey.ScanCode = SCAN_ESC; | |
| CustomKey.UnicodeChar = CHAR_NULL; | |
| } else { | |
| // | |
| // Map Esc to Boot Manager Menu | |
| // | |
| CustomKey.ScanCode = SCAN_F2; | |
| CustomKey.UnicodeChar = CHAR_NULL; | |
| } | |
| EfiBootManagerGetBootManagerMenu (&BootOption); | |
| EfiBootManagerAddKeyOptionVariable (NULL, (UINT16)BootOption.OptionNumber, 0, &CustomKey, NULL); | |
| // | |
| // Also add Down key to Boot Manager Menu since some serial terminals don't support F2 key. | |
| // | |
| Down.ScanCode = SCAN_DOWN; | |
| Down.UnicodeChar = CHAR_NULL; | |
| EfiBootManagerGetBootManagerMenu (&BootOption); | |
| EfiBootManagerAddKeyOptionVariable (NULL, (UINT16)BootOption.OptionNumber, 0, &Down, NULL); | |
| // | |
| // Process update capsules that don't contain embedded drivers. | |
| // | |
| if (GetBootModeHob () == BOOT_ON_FLASH_UPDATE) { | |
| // TODO: when enabling capsule support for laptops, add a battery check here | |
| Status = ProcessCapsules (); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a(): ProcessCapsule() failed with: %r\n", __func__, Status)); | |
| } | |
| } | |
| // | |
| // Install ready to lock. | |
| // This needs to be done before option rom dispatched. | |
| // | |
| InstallReadyToLock (); | |
| // | |
| // Dispatch deferred images after EndOfDxe event and ReadyToLock installation. | |
| // | |
| EfiBootManagerDispatchDeferredImages (); | |
| PlatformConsoleInit (); | |
| } | |
| /** | |
| Do the platform specific action after the console is connected. | |
| Such as: | |
| Dynamically switch output mode; | |
| Signal console ready platform customized event; | |
| Run diagnostics like memory testing; | |
| Connect certain devices; | |
| Dispatch additional option roms. | |
| **/ | |
| VOID | |
| EFIAPI | |
| PlatformBootManagerAfterConsole ( | |
| VOID | |
| ) | |
| { | |
| EFI_GRAPHICS_OUTPUT_BLT_PIXEL Black; | |
| EFI_GRAPHICS_OUTPUT_BLT_PIXEL White; | |
| EDKII_PLATFORM_LOGO_PROTOCOL *PlatformLogo; | |
| EFI_STATUS Status; | |
| Black.Blue = Black.Green = Black.Red = Black.Reserved = 0; | |
| White.Blue = White.Green = White.Red = White.Reserved = 0xFF; | |
| Status = gBS->LocateProtocol (&gEdkiiPlatformLogoProtocolGuid, NULL, (VOID **)&PlatformLogo); | |
| if (!EFI_ERROR (Status)) { | |
| gST->ConOut->ClearScreen (gST->ConOut); | |
| BootLogoEnableLogo (); | |
| } | |
| EfiBootManagerConnectAll (); | |
| EfiBootManagerRefreshAllBootOption (); | |
| // | |
| // Active BOOT_ON_FLASH_UPDATE mode means that at least one capsule has been | |
| // discovered by a bootloader and passed for further processing into EDK which | |
| // created EFI_HOB_TYPE_UEFI_CAPSULE HOB(s). | |
| // | |
| // Process update capsules that weren't processed on the first call to | |
| // ProcessCapsules() in PlatformBootManagerBeforeConsole(). | |
| // | |
| if (GetBootModeHob () == BOOT_ON_FLASH_UPDATE) { | |
| // TODO: when enabling capsule support for laptops, add a battery check here | |
| Status = ProcessCapsules (); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a(): ProcessCapsule() failed with: %r\n", __func__, Status)); | |
| } | |
| // | |
| // Reset the system to disable SMI handler in order to exclude the | |
| // possibility of it being used outside of the firmware. | |
| // | |
| // In practice, this will rarely execute because even the first | |
| // ProcessCapsules() invocation might do a reset if all capsules were | |
| // processed and at least one of them needed a reset. This is just to catch | |
| // a case when this doesn't happen which is possible on error. | |
| // | |
| gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL); | |
| } | |
| // | |
| // Register UEFI Shell | |
| // | |
| PlatformRegisterFvBootOption (&gUefiShellFileGuid, L"UEFI Shell", LOAD_OPTION_ACTIVE); | |
| if (FixedPcdGetBool (PcdBootManagerEscape)) { | |
| Print ( | |
| L"\n" | |
| L" Esc or Down to enter Boot Manager Menu.\n" | |
| L" ENTER to boot directly.\n" | |
| L"\n" | |
| ); | |
| } else { | |
| Print ( | |
| L"\n" | |
| L" F2 or Down to enter Boot Manager Menu.\n" | |
| L" ENTER to boot directly.\n" | |
| L"\n" | |
| ); | |
| } | |
| } | |
| /** | |
| This function is called each second during the boot manager waits the timeout. | |
| @param TimeoutRemain The remaining timeout. | |
| **/ | |
| VOID | |
| EFIAPI | |
| PlatformBootManagerWaitCallback ( | |
| UINT16 TimeoutRemain | |
| ) | |
| { | |
| return; | |
| } | |
| /** | |
| 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 | |
| ) | |
| { | |
| return; | |
| } |