alder_lake_bios/Intel/AlderLake/AlderLakePlatSamplePkg/Features/Dnx/DnxDxe/DnxDxe.c

598 lines
18 KiB
C

/** @file
Module Name:
DnxDxe.c
Abstract:
Dnx driver main function.
@copyright
INTEL CONFIDENTIAL
Copyright 2018 - 2020 Intel Corporation.
The source code contained or described herein and all documents related to the
source code ("Material") are owned by Intel Corporation or its suppliers or
licensors. Title to the Material remains with Intel Corporation or its suppliers
and licensors. The Material may contain trade secrets and proprietary and
confidential information of Intel Corporation and its suppliers and licensors,
and is protected by worldwide copyright and trade secret laws and treaty
provisions. No part of the Material may be used, copied, reproduced, modified,
published, uploaded, posted, transmitted, distributed, or disclosed in any way
without Intel's prior express written permission.
No license under any patent, copyright, trade secret or other intellectual
property right is granted to or conferred upon you by disclosure or delivery
of the Materials, either expressly, by implication, inducement, estoppel or
otherwise. Any license under such intellectual property rights must be
express and approved by Intel in writing.
Unless otherwise agreed by Intel in writing, you may not remove or alter
this notice or any other notice embedded in Materials by Intel or
Intel's suppliers or licensors in any way.
This file contains a 'Sample Driver' and is licensed as such under the terms
of your license agreement with Intel or your vendor. This file may be modified
by the user, subject to the additional terms of the license agreement.
@par Specification
**/
#include "DnxDxe.h"
#include <SetupVariable.h>
#include <Library/HobLib.h>
#include <MeBiosPayloadHob.h>
#include <Library/UefiBootManagerLib.h>
#include <Library/DxeMeLib.h>
#include <Protocol/FirmwareVolume2.h>
#include <Library/DevicePathLib.h>
#include <Library/EcLib.h>
#include <Library/PcdLib.h>
#include <Protocol/BlockIo.h>
#define VOL_DWN_BTN_PRESSED BIT2
/**
Function to locate Ffu Applcation and launch it safely.
@param[in] None
@retval None
**/
VOID
LaunchFfuApplication (
VOID
)
{
EFI_STATUS Status;
UINT8 *ImageBuffer;
EFI_HANDLE ImageHandle;
UINTN Index;
UINTN FvHandleCount;
EFI_HANDLE *FvHandleBuffer;
UINTN Size;
UINT32 AuthenticationStatus;
EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FfuNode;
DEBUG ((DEBUG_INFO, "LaunchFfuApplication..\n"));
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiFirmwareVolume2ProtocolGuid,
NULL,
&FvHandleCount,
&FvHandleBuffer
);
if (EFI_ERROR (Status) || (FvHandleCount == 0)) {
DEBUG ((DEBUG_INFO, "LocateHandleBuffer Status %r, Count %d..\n", Status, FvHandleCount));
return;
}
for (Index = 0; Index < FvHandleCount; Index++) {
Status = gBS->HandleProtocol (
FvHandleBuffer[Index],
&gEfiFirmwareVolume2ProtocolGuid,
(VOID **)&Fv
);
ImageBuffer = NULL;
Size = 0;
Status = Fv->ReadSection (
Fv,
PcdGetPtr(PcdFfuLoaderFile),
EFI_SECTION_PE32,
0,
(VOID **) &ImageBuffer,
&Size,
&AuthenticationStatus
);
if (EFI_ERROR(Status)) {
//
// Skip if no file in the FV
//
continue;
}
DEBUG ((DEBUG_INFO, "Image located..\n"));
DevicePath = DevicePathFromHandle(FvHandleBuffer[Index]);
//
// Build device path
//
EfiInitializeFwVolDevicepathNode (&FfuNode, PcdGetPtr(PcdFfuLoaderFile));
DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&FfuNode);
Status = gBS->LoadImage (
FALSE,
gImageHandle,
DevicePath,
ImageBuffer,
Size,
&ImageHandle
);
if (!EFI_ERROR(Status)) {
DEBUG ((DEBUG_INFO, "Load Image Successful..\n"));
Status = gBS->StartImage (ImageHandle, NULL, NULL);
DEBUG ((DEBUG_INFO, "Start Image Status %r..\n",Status));
}
if (EFI_ERROR(Status)) {
gST->ConOut->ClearScreen (gST->ConOut);
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
gST->ConOut->OutputString (gST->ConOut, L"Failed to Launch FFU Loader Application...");
}
}
}
/**
Function to check for setup Option which will be set if Boot Loader is corrupted or not present
@param[in] None
@retval Boolean TRUE if setup option is set
**/
BOOLEAN
CheckSetupOption (
VOID
)
{
EFI_STATUS Status;
SETUP_DATA SetupData;
UINTN DataSize;
UINT32 Attributes;
Attributes = 0;
DataSize = sizeof(SETUP_DATA);
DEBUG ((DEBUG_INFO, "Os Dnx Check Setup Option \n"));
Status = gRT->GetVariable (
L"Setup",
&gSetupVariableGuid,
&Attributes,
&DataSize,
&SetupData
);
if (!EFI_ERROR(Status)) {
DEBUG ((DEBUG_INFO, "OsDnx Value : %x \n", SetupData.OsDnx));
if (SetupData.OsDnx == 1) {
SetupData.OsDnx = 0;
Status = gRT->SetVariable (
L"Setup",
&gSetupVariableGuid,
Attributes,
DataSize,
&SetupData
);
ASSERT_EFI_ERROR (Status);
return TRUE;
}
}
return FALSE;
}
/**
Function to check for Cse Os Recovery
@param[in] None
@retval Boolean TRUE if Cse Os Recovery is set
**/
BOOLEAN
CheckCseDnxOsRecovery (
VOID
)
{
ME_BIOS_PAYLOAD_HOB *MbpHob;
MbpHob = NULL;
DEBUG ((DEBUG_INFO, "CheckCseDnxOsRecovery\n"));
MbpHob = GetFirstGuidHob (&gMeBiosPayloadHobGuid);
ASSERT (MbpHob != NULL);
if (MbpHob == NULL) {
return FALSE;
}
if (MbpHob->MeBiosPayload.IfwiDnxRequest.Available) {
DEBUG ((DEBUG_INFO, "IFWI Dnx request Data is present\n"));
if (((MbpHob->MeBiosPayload.IfwiDnxRequest.EnterRecovery & BIT0) == BIT0)) {
DEBUG ((DEBUG_INFO, "IFWI Dnx requested for according to CSE\n"));
HeciDnxReqClear (0);
DEBUG ((DEBUG_INFO, "Clearing CSE Dnx request\n"));
return TRUE;
}
}
return FALSE;
}
/**
Function to check for Bootloader corruption/not present
@param[in] None
@retval Boolean TRUE if Bootloader is corrupted/not present
**/
BOOLEAN
CheckBootLoaderCorruption (
VOID
)
{
DEBUG ((DEBUG_INFO, "CheckBootLoaderCorruption\n"));
return (CheckSetupOption());
}
/**
Function to check for Volume down button press
@param[in] None
@retval Boolean TRUE if VolumeDown button is pressed
**/
BOOLEAN
CheckVolumeDownButton (
VOID
)
{
EFI_STATUS Status;
UINT8 VolumeDownButtonStatus;
DEBUG ((DEBUG_INFO, " CheckVolumeDownButton\n"));
// Send EC Command to check if Volume Down Button is Pressed.
Status = SendEcCommand (EC_C_QUERY_BOARD_BUTTON_BITS);
if (Status == EFI_SUCCESS) {
Status = ReceiveEcData (&VolumeDownButtonStatus);
if (Status == EFI_SUCCESS) {
// EcData:Bit2 = 1 => Volume DownButton is pressed
if ((VolumeDownButtonStatus & VOL_DWN_BTN_PRESSED) != 0){
DEBUG ((DEBUG_INFO, "VolumeDownButton: PRESSED\n"));
return TRUE;
}
}
}
return FALSE;
}
/**
Function to check if Os Recovery Triggered
@param[in] None
@retval Boolean TRUE if Os Recovery is triggered
**/
BOOLEAN
IsDnxModeTriggered (
VOID
)
{
DEBUG ((DEBUG_INFO, " IsDnxModeTriggered\n"));
//
// 1. Check if Volume Down Button is pressed
// 2. Bad OS boot loader and cable connection detected
// 3. One shot device image request as indicated by the MBP Os Dnx bit
//
if ((CheckCseDnxOsRecovery()) || (CheckBootLoaderCorruption()) || (CheckVolumeDownButton())) {
return TRUE;
}
return FALSE;
}
/**
Dnx ready to Boot Function
@param[in] Event
@param[in] Context
@retval EFI_SUCCESS if driver support is initilized without any issue.
**/
VOID
EFIAPI
DnxReadyToBootFunction (
IN EFI_EVENT Event,
IN VOID *Context
)
{
UINTN NoBlkIoHandles;
EFI_HANDLE *BlkIoHandle;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
UINTN Index;
EFI_STATUS Status;
BOOLEAN StorageDeviceFound = FALSE;
DEBUG ((DEBUG_INFO, "Dnx Entry\n"));
if (IsDnxModeTriggered () == TRUE) {
DEBUG ((DEBUG_INFO, "OS Dnx is Triggerred\n"));
//
// Locate Handles that support BlockIo protocol
//
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiBlockIoProtocolGuid,
NULL,
&NoBlkIoHandles,
&BlkIoHandle
);
if (!EFI_ERROR(Status)) {
for (Index = 0; Index < NoBlkIoHandles; Index++) {
Status = gBS->HandleProtocol(
BlkIoHandle[Index],
&gEfiDevicePathProtocolGuid,
(VOID **)&DevicePath
);
if (!EFI_ERROR(Status)) {
while (!IsDevicePathEndType(NextDevicePathNode(DevicePath))) {
DevicePath = NextDevicePathNode(DevicePath);
if (DevicePath->Type == MESSAGING_DEVICE_PATH &&
((DevicePath->SubType == MSG_NVME_NAMESPACE_DP) ||
(DevicePath->SubType == MSG_SATA_DP) ||
(DevicePath->SubType == MSG_EMMC_DP) ||
(DevicePath->SubType == MSG_SCSI_DP))) {
StorageDeviceFound = TRUE;
break;
}
}
}
}
}
if (StorageDeviceFound != TRUE) {
DEBUG((DEBUG_INFO, "FFU tool supports only SATA/NVME/SCSI/EMMC Storage Device and there is no device detected in this boot \n"));
gST->ConOut->ClearScreen(gST->ConOut);
gST->ConOut->EnableCursor(gST->ConOut, TRUE);
gST->ConOut->OutputString(gST->ConOut, L"FFU tool supports only SATA/NVME/SCSI/EMMC Storage Device and there is no device detected in this boot \n");
return;
}
gST->ConOut->ClearScreen (gST->ConOut);
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
gST->ConOut->OutputString (gST->ConOut, L"OS Recovery Triggerred...");
LaunchFfuApplication();
}
return;
}
/**
Trigger OS Dnx Call back event, upon OS Boot failure from supported storage paths
@param CodeType Indicates the type of status code being reported. Type EFI_STATUS_CODE_TYPE is defined in "Related Definitions" below.
@param Value Describes the current status of a hardware or software entity.
This included information about the class and subclass that is used to classify the entity
as well as an operation. For progress codes, the operation is the current activity.
For error codes, it is the exception. For debug codes, it is not defined at this time.
Type EFI_STATUS_CODE_VALUE is defined in "Related Definitions" below.
Specific values are discussed in the Intel? Platform Innovation Framework for EFI Status Code Specification.
@param Instance The enumeration of a hardware or software entity within the system.
A system may contain multiple entities that match a class/subclass pairing.
The instance differentiates between them. An instance of 0 indicates that instance information is unavailable,
not meaningful, or not relevant. Valid instance numbers start with 1.
@param CallerId This optional parameter may be used to identify the caller.
This parameter allows the status code driver to apply different rules to different callers.
Type EFI_GUID is defined in InstallProtocolInterface() in the UEFI 2.0 Specification.
@param Data This optional parameter may be used to pass additional data
@retval EFI_SUCCESS Status code is what we expected.
@retval EFI_UNSUPPORTED Status code not supported.
**/
EFI_STATUS
EFIAPI
BootFailTriggerDnx (
IN EFI_STATUS_CODE_TYPE CodeType,
IN EFI_STATUS_CODE_VALUE Value,
IN UINT32 Instance,
IN EFI_GUID *CallerId,
IN EFI_STATUS_CODE_DATA *Data OPTIONAL
)
{
EFI_STATUS Status;
BOOLEAN TriggerDnxFlag = FALSE;
UINTN VarSize;
UINT16 BootCurrent;
UINTN Index;
UINTN BootOptionCount;
EFI_BOOT_MANAGER_LOAD_OPTION *BootOption;
EFI_DEVICE_PATH_PROTOCOL *BootMediaDevicePath;
//
// Check if reported status code indicates that the failure to load boot option
// Triggered from Bmboot.c by BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR, Status);
//
if ((CodeType == (EFI_ERROR_CODE | EFI_ERROR_MINOR)) &&
((Value == (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR)) ||
(Value == (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED)))) {
DEBUG ((DEBUG_INFO, "Inside BootFailTriggerDnx()\n"));
//
// Get BootCurrent variable
//
VarSize = sizeof (UINT16);
Status = gRT->GetVariable (
L"BootCurrent",
&gEfiGlobalVariableGuid,
NULL,
&VarSize,
&BootCurrent
);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
//
// Get the Boot options
//
BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
//
//
//
for (Index=0; Index < BootOptionCount; Index++) {
//
// Get the Bootoption corresponding to the Current Failed Boot Option Index
//
if (BootOption->OptionNumber == (UINTN) BootCurrent) {
//
// Check if this Failed Bootoption is of ATA/SCSI/NVME/SATA/M.2/PCIeSSD Device Path to initiate recovery
//
BootMediaDevicePath = BootOption->FilePath;
while (!IsDevicePathEndType (NextDevicePathNode (BootMediaDevicePath))) {
BootMediaDevicePath = NextDevicePathNode (BootMediaDevicePath);
if (BootMediaDevicePath->Type == MESSAGING_DEVICE_PATH &&
((BootMediaDevicePath->SubType == MSG_NVME_NAMESPACE_DP) ||
(BootMediaDevicePath->SubType == MSG_EMMC_DP) ||
(BootMediaDevicePath->SubType == MSG_SCSI_DP) ||
(BootMediaDevicePath->SubType == MSG_SATA_DP))) {
DEBUG ((EFI_D_INFO, "[Bds] Found OS Boot Option Failure, Report Status Code To Trigger Os Recovery"));
TriggerDnxFlag = TRUE; // Signal the event to Launchnote FFU Loader
break;
}
}
}
}
EfiBootManagerFreeLoadOptions (BootOption, BootOptionCount);
//
// Proceed with Launch FFU, only if the Boot Option load has failed from EMMC/SCSI/NVME/SATA Device Paths
//
if (TriggerDnxFlag != TRUE) {
DEBUG((DEBUG_INFO, "Returning from boot fail RSC Listener.Failed Boot Option is not from the FFU Supported storage.\n"));
return EFI_UNSUPPORTED;
}
gST->ConOut->ClearScreen (gST->ConOut);
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
gST->ConOut->OutputString (gST->ConOut, L"OS Recovery Triggerred...");
LaunchFfuApplication();
return EFI_SUCCESS;
} else {
return EFI_UNSUPPORTED;
}
}
/**
Unregister status code callback functions.
@param Event Event whose notification function is being invoked.
@param Context Pointer to the notification function's context, which is
always zero in current implementation.
**/
VOID
EFIAPI
UnregisterBootFailTriggerDnx (
IN EFI_EVENT Event,
IN VOID *Context
)
{
mRscHandlerProtocol->Unregister (BootFailTriggerDnx);
DEBUG ((DEBUG_INFO, "BootFailTriggerDnx: Unregistered\n"));
}
/**
Initializes the Dnx driver
@param[in] ImageHandle A copy of the ImageHandle.
@param[in] SystemTable A pointer to the SystemTable for the application.
@retval EFI_SUCCESS if driver support is initilized without any issue.
**/
EFI_STATUS
EFIAPI
DnxDxeEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_EVENT Event;
EFI_EVENT DnxEvent;
DEBUG ((DEBUG_INFO, "Entry DnxDxe driver\n"));
Status = EFI_SUCCESS;
Status = EfiCreateEventReadyToBootEx (
TPL_CALLBACK,
DnxReadyToBootFunction,
NULL,
&Event
);
//
// Register the event handling function to return to Dnx when bootloader failed.
//
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
DnxReadyToBootFunction,
NULL,
&gPlatformOsDnxEventGuid,
&DnxEvent
);
ASSERT_EFI_ERROR (Status);
//
// Register RSC Listener fucntion to check for NVME/SATA/ISCI storage device availability
// and give control to the FFU Image EFI, upon failure to load OS boot option
//
//
// Locate report status code protocol.
//
Status = gBS->LocateProtocol (
&gEfiRscHandlerProtocolGuid,
NULL,
(VOID **) &mRscHandlerProtocol
);
ASSERT_EFI_ERROR (Status);
//
//Register the RSC Listener function for ReportStatusCode() notification.
//
mRscHandlerProtocol->Register (BootFailTriggerDnx, TPL_HIGH_LEVEL);
//
// Unregister boot time report status code listener at ExitBootService Event.
//
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
UnregisterBootFailTriggerDnx,
NULL,
&gEfiEventExitBootServicesGuid,
&mExitBootServicesEvent
);
ASSERT_EFI_ERROR (Status);
DEBUG ((DEBUG_INFO, "Exit DnxDxe driver %r\n", Status));
return Status;
}