598 lines
18 KiB
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;
|
|
}
|