alder_lake_bios/Oem/L05/FeatureCommon/InsydeL05ModulePkg/LegacyToEfiSmm/LegacyToEfiSmm.c

832 lines
23 KiB
C

/** @file
This driver is for providing the Legacy To Efi Services routines.
;******************************************************************************
;* Copyright (c) 2012 - 2019, Insyde Software Corporation. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#include "LegacyToEfiSmm.h"
EFI_SMM_VARIABLE_PROTOCOL *mSmmVariable;
EFI_PHYSICAL_ADDRESS *mBootOrderBuffer;
EFI_PHYSICAL_ADDRESS *mBootOptionBuffer;
UINT8 mBBSIndex;
BOOLEAN mAllbootFailed = FALSE;
BBS_TABLE *mLocalBbsTable = NULL;
/**
This function is for getting variable data and size in SMM mode
@param Name Point to variable string name
@param VendorGuid Point to variable guid
@param VariableSize Point to Variable data size
@retval VOID Return point of variable data
**/
VOID *
SmmGetVariableAndSize (
IN CHAR16 *Name,
IN EFI_GUID *VendorGuid,
OUT UINTN *VariableSize
)
{
EFI_STATUS Status;
UINTN BufferSize;
VOID *Buffer;
Buffer = NULL;
//
// Pass in a zero size buffer to find the required buffer size.
//
BufferSize = 0;
Status = mSmmVariable->SmmGetVariable (
Name,
VendorGuid,
NULL,
&BufferSize,
Buffer
);
if (Status == EFI_BUFFER_TOO_SMALL) {
//
// Allocate the buffer to return
//
Status = gSmst->SmmAllocatePool (
EfiRuntimeServicesData,
BufferSize,
&Buffer
);
if (EFI_ERROR (Status)) {
return NULL;
}
ZeroMem (Buffer, BufferSize);
//
// Read variable into the allocated buffer.
//
Status = mSmmVariable->SmmGetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
if (EFI_ERROR (Status)) {
BufferSize = 0;
gSmst->SmmFreePool (Buffer);
Buffer = NULL;
}
}
*VariableSize = BufferSize;
return Buffer;
}
/**
Check the Boot#### number of device is vaild or not
@param BootOptionNum The Boot device number in BootOrder variable
@retval TRUE This is valid EFI Boot Option.
@retval FALSE This is not valid EFI Boot Option (Legacy, Recovery, ...etc.).
**/
BOOLEAN
IsValidEfiBootOption (
IN UINT16 BootOptionNum
)
{
UINT16 BootOption[10];
UINT8 *BootOptionVar;
UINTN BootOptionSize;
UINT8 *BootOptionPtr;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
BOOLEAN IsValid;
CHAR16 *RecoverySystemNameString = L05_NOVO_RECOVERY_SYSTEM_NAME;
UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOptionNum);
BootOptionVar = SmmGetVariableAndSize (
BootOption,
&gEfiGlobalVariableGuid,
&BootOptionSize
);
if (BootOptionVar == NULL) {
//
// Boot Option is not exist
//
return FALSE;
}
IsValid = TRUE;
BootOptionPtr = BootOptionVar;
BootOptionPtr += sizeof (UINT32);
BootOptionPtr += sizeof (UINT16);
//
// Check device name in Boot#### variable
//
if (StrSize (RecoverySystemNameString) == StrSize ((UINT16 *) BootOptionPtr) &&
CompareMem (RecoverySystemNameString, BootOptionPtr, StrSize (RecoverySystemNameString)) == 0x0) {
IsValid = FALSE;
}
BootOptionPtr += (UINTN) StrSize ((UINT16 *) BootOptionPtr);
//
// Check device path in Boot#### variable
//
DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) BootOptionPtr;
if ((DevicePath->Type == BBS_DEVICE_PATH) && (DevicePath->SubType == BBS_BBS_DP)) {
IsValid = FALSE;
}
gSmst->SmmFreePool (BootOptionVar);
return IsValid;
}
/**
Find the next boot EFI Boot Option and set L"BootNext" variable
@retval TRUE Do LegacyToEfi feature that meaning is the next boot is EFI boot.
@retval FALSE Next boot is Legacy boot or this is last boot OS.
**/
BOOLEAN
IsSetBootNextVariable (
)
{
EFI_STATUS Status;
UINTN OptionOrderSize;
UINT16 *OptionOrder;
UINT16 PriorityIndex;
UINT16 BootOption[10];
UINT16 BootOrderNumber[10];
UINTN BootOptionSize;
UINT8 *BootOptionVar;
UINT8 *Ptr;
UINT16 DevPathSize;
CHAR16 *BootDesc;
EFI_DEVICE_PATH_PROTOCOL *DevPath;
UINT16 BbsIndex;
UINT32 Attributes;
UINT16 BootNext;
UINT8 LegacyToEfi;
OptionOrderSize = 0x400;
OptionOrder = NULL;
BootOptionSize = 0x1000;
BootOptionVar = NULL;
Ptr = NULL;
DevPathSize = 0;
BootDesc = NULL;
DevPath = NULL;
BbsIndex = 0;
OptionOrder = (UINT16 *) (mBootOrderBuffer);
Status = mSmmVariable->SmmGetVariable (
L"BootOrder",
&gEfiGlobalVariableGuid,
&Attributes,
&OptionOrderSize,
(VOID *) OptionOrder
);
if (EFI_ERROR (Status)) {
return FALSE;
}
//
// Skip to set BootNext if only one boot option exist.
//
if ((OptionOrderSize / sizeof (UINT16)) == 1) {
return FALSE;
}
BootNext = 0xFFFF;
//
// Set BBS priority according OptionOrder variable
//
for (PriorityIndex = 0; PriorityIndex < OptionOrderSize / sizeof (UINT16); PriorityIndex++) {
ZeroMem ((VOID *) BootOption, sizeof (BootOption));
ZeroMem ((VOID *) BootOrderNumber, sizeof (BootOrderNumber));
StrCpyS (BootOption, (sizeof (BootOption) / sizeof (UINT16)), L"Boot");
UnicodeValueToStringS (BootOrderNumber, sizeof (BootOrderNumber), (PREFIX_ZERO | RADIX_HEX), OptionOrder[PriorityIndex], 4);
StrCatS (BootOption, (sizeof (BootOption) / sizeof (UINT16)), BootOrderNumber);
BootOptionVar = (UINT8 *) (mBootOptionBuffer);
BootOptionSize = 0x1000;
Status = mSmmVariable->SmmGetVariable (
BootOption,
&gEfiGlobalVariableGuid,
&Attributes,
&BootOptionSize,
(VOID *) BootOptionVar
);
if (EFI_ERROR (Status)) {
continue;
}
Ptr = BootOptionVar;
Ptr += sizeof (UINT32);
DevPathSize = *(UINT16 *) Ptr;
Ptr += sizeof (UINT16);
BootDesc = (CHAR16 *) Ptr;
Ptr += StrSize (BootDesc);
DevPath = (EFI_DEVICE_PATH_PROTOCOL *) Ptr;
//
// Skip the native boot options(EFI shell, Win8..)
//
if (DevPath->Type != BBS_DEVICE_PATH) {
continue;
}
Ptr += DevPathSize;
Ptr += sizeof (BBS_TABLE);
BbsIndex = *(UINT16 *) Ptr;
if (mBBSIndex == BbsIndex) {
BootNext = OptionOrder[PriorityIndex + 1];
if ((PriorityIndex + 1) >= (OptionOrderSize / sizeof (UINT16))) {
BootNext = OptionOrder[0];
}
if (IsValidEfiBootOption (BootNext)) {
//
// Boot next is valid EFI Boot Option
//
break;
} else {
//
// Boot next is not valid EFI Boot Option
//
return FALSE;
}
}
}
if (BootNext != 0xFFFF) {
Status = mSmmVariable->SmmSetVariable (
L"BootNext",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
sizeof (UINT16),
&BootNext
);
if (!EFI_ERROR (Status)) {
//
// Set "LegacyToEfi" variable and do reset for boot next
//
LegacyToEfi = 1;
Status = mSmmVariable->SmmSetVariable (
L"LegacyToEfi",
&gEfiGenericVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
sizeof (UINT8),
&LegacyToEfi
);
return TRUE;
}
}
return FALSE;
}
/**
Update Legacy BBS Table
@param EfiToLegacy Next Legacy BootXXXX
**/
VOID
SetBootTableAttribute (
IN UINT16 *EfiToLegacy
)
{
UINTN OptionOrderSize;
UINT16 *OptionOrder;
UINT16 PriorityIndex;
UINT16 BootOption[10];
UINT16 BootOrderNumber[10];
UINTN BootOptionSize;
UINT8 *BootOptionVar;
UINT8 *Ptr;
UINT16 DevPathSize;
CHAR16 *BootDesc;
EFI_DEVICE_PATH_PROTOCOL *DevPath;
UINT16 BbsIndex;
UINT8 *BootTablePtr;
UINT16 BootPriority;
OptionOrder = NULL;
BootOptionVar = NULL;
Ptr = NULL;
DevPathSize = 0;
BootDesc = NULL;
DevPath = NULL;
BbsIndex = 0;
BootPriority = 0;
if (mLocalBbsTable == NULL) {
return;
}
OptionOrder = SmmGetVariableAndSize (
L"BootOrder",
&gEfiGlobalVariableGuid,
&OptionOrderSize
);
//
// Set BBS priority according OptionOrder variable
//
for (PriorityIndex = 0; PriorityIndex < OptionOrderSize / sizeof (UINT16); PriorityIndex++) {
ZeroMem ((VOID *) BootOption, sizeof (BootOption));
ZeroMem ((VOID *) BootOrderNumber, sizeof (BootOrderNumber));
StrCpyS (BootOption, (sizeof (BootOption) / sizeof (UINT16)), L"Boot");
UnicodeValueToStringS (BootOrderNumber, sizeof (BootOrderNumber), (PREFIX_ZERO | RADIX_HEX), OptionOrder[PriorityIndex], 4);
StrCatS (BootOption, (sizeof (BootOption) / sizeof (UINT16)), BootOrderNumber);
BootOptionVar = NULL;
BootOptionVar = SmmGetVariableAndSize (
BootOption,
&gEfiGlobalVariableGuid,
&BootOptionSize
);
if (BootOptionVar == NULL) {
continue;
}
Ptr = BootOptionVar;
Ptr += sizeof (UINT32);
DevPathSize = *(UINT16 *) Ptr;
Ptr += sizeof (UINT16);
BootDesc = (CHAR16 *) Ptr;
Ptr += StrSize (BootDesc);
DevPath = (EFI_DEVICE_PATH_PROTOCOL *) Ptr;
//
// Skip the native boot options(EFI shell, Win8..)
//
if (DevPath->Type != BBS_DEVICE_PATH) {
gSmst->SmmFreePool (BootOptionVar);
continue;
}
Ptr += DevPathSize;
Ptr += sizeof (BBS_TABLE);
BbsIndex = *(UINT16 *) Ptr;
if (OptionOrder[PriorityIndex] != *EfiToLegacy) {
BootPriority = mLocalBbsTable[BbsIndex].BootPriority;
}
if (OptionOrder[PriorityIndex] == *EfiToLegacy) {
gSmst->SmmFreePool (BootOptionVar);
break;
}
if (BootPriority != 0) {
//
// Update Boot table attribute
//
BootTablePtr = (UINT8 *) (UINTN) ((BDA (0xe) << 4) + EBDA (0x180));
BootTablePtr = BootTablePtr + (BootPriority * 0x20);
*BootTablePtr |= ATTEMPED_BOOT;
}
gSmst->SmmFreePool (BootOptionVar);
}
gSmst->SmmFreePool (OptionOrder);
return;
}
/**
Clear variable of EfiToLegacyBootNext, if varaible existed.
**/
VOID
EfiToLegacy (
)
{
EFI_STATUS Status;
UINTN OptionOrderSize;
UINT16 *EfiToLegacy;
EfiToLegacy = SmmGetVariableAndSize (
L"EfiToLegacyBootNext",
&gEfiGenericVariableGuid,
&OptionOrderSize
);
if (EfiToLegacy != NULL) {
SetBootTableAttribute (EfiToLegacy);
//
// Erase "EfiToLegacyBootNext" variable
//
Status = mSmmVariable->SmmSetVariable (
L"EfiToLegacyBootNext",
&gEfiGenericVariableGuid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
0,
EfiToLegacy
);
gSmst->SmmFreePool (EfiToLegacy);
}
}
/**
Execute STANDBY_IMMEDIATE command by AtaPassThru protocol
@param None
@retval EFI_SUCCESS Success to finish hard disk spin down function
@retval Others Fail to execute hard disk spin down function
**/
EFI_STATUS
EFIAPI
HddSpinDownFunc (
VOID
)
{
EFI_STATUS Status;
EFI_L05_HDD_SPINDOWN_PROTOCOL *L05HddSpindownProtocol;
L05HddSpindownProtocol = NULL;
Status = gSmst->SmmLocateProtocol (
&gEfiL05HddSpindownProtocolGuid,
NULL,
(VOID **) &L05HddSpindownProtocol
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = L05HddSpindownProtocol->HddSpinDownAllPort ();
return Status;
}
/**
Calling this function causes a system-wide reset. This sets
all circuitry within the system to its initial state. This type of reset
is asynchronous to system operation and operates without regard to
cycle boundaries.
System reset should not return, if it returns, it means the system does
not support cold reset.
**/
VOID
EFIAPI
ResetCold (
VOID
)
{
IoWrite8 (IO_RST_CNT, IO_RST_CNT_HARDRESET);
}
/**
INT15 Callback Routine. It contains several INT15 Services corresponding
to specific Function Number such as 0x5F49 for Backlight Brightness.
@param CpuRegs The structure containing CPU Registers (AX, BX, CX, DX etc.)
@param Context Context
**/
VOID
LegacyToEfiInt15HookCore (
IN OUT EFI_IA32_REGISTER_SET *CpuRegs,
IN VOID *Context
)
{
UINT32 Int15FunNum;
//
// GET THE INT15 FUNCTION NUMBER
//
Int15FunNum = (CpuRegs->X.AX & 0xFFFF);
if (!Int15FunNum) {
return;
}
if (Int15FunNum == INT19_HOOK_POINTS) {
//
// SWITCH CASE Used For Applying Different HOOK corresponding to the Function Number
//
switch (CpuRegs->X.BX) {
case INT19_ENTRY:
//
// wait check plicy......
//
if (mAllbootFailed) {
//
// To do : retry all boot devices (maybe set "L05Legacy2EFIEntry" to bypass display...)
//
//Status = gRT->SetVariable (
// L"L05Legacy2EFIEntry",
// &gL05UefiBootEntryListGuid,
// EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS |
// EFI_VARIABLE_NON_VOLATILE,
// 0,
// NULL
// );
HddSpinDownFunc ();
ResetCold ();
}
//
// Int19 entry
//
EfiToLegacy ();
break;
case BEFORE_BOOT_ENTRY:
//
// Before Boot entry
// AX = 9998h, BX= 1h, DL=Drive number (floppy:00, 01,... / HDD: 80h, 81h, .../ ODD: A0h, A1h,.../ BEV: 0FFh),DI = BBS index
//
mBBSIndex = (UINT8) CpuRegs->X.DI;
break;
case LEGACY_BOOT_FAILURE:
//
// Single Legacy Boot failure
// AX = 9998h, BX= 2h
//
if (IsSetBootNextVariable ()) {
HddSpinDownFunc ();
ResetCold ();
}
break;
case ALL_LEGACY_BOOT_FAILURE:
//
// All Legacy Boot failure
// AX = 9998h, BX= 3h
//
mAllbootFailed = TRUE;
break;
case BEFORE_BOOT_TO_OS:
//
// Before boot to OS (0x7c00)
// AX = 9998h, BX= 4h, DL=Drive number (floppy:00, 01,... / HDD: 80h, 81h, .../ ODD: A0h, A1h,.../ BEV: 0FFh),DI = BBS index
//
break;
default:
break;
}
}
return;
}
/**
LegacyToEfi INT 15 Hook Core
@retval EFI_SUCCESS The operation completed successfully.
@retval Others An unexpected error occurred.
**/
EFI_STATUS
SmmInt15ServiceCallbackCore (
)
{
EFI_STATUS Status;
EFI_SMM_INT15_SERVICE_PROTOCOL *SmmInt15Service;
SmmInt15Service = NULL;
Status = gSmst->SmmLocateProtocol (&gEfiSmmInt15ServiceProtocolGuid, NULL, &SmmInt15Service);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Register the 0x9998 function for INT19 callback
//
Status = SmmInt15Service->InstallInt15ProtocolInterface (
SmmInt15Service,
INT19_HOOK_POINTS,
LegacyToEfiInt15HookCore,
NULL
);
if (Status == EFI_ALREADY_STARTED) {
//
// use new callback function to replace original one
//
Status = SmmInt15Service->ReinstallInt15ProtocolInterface (
SmmInt15Service,
INT19_HOOK_POINTS,
LegacyToEfiInt15HookCore,
NULL
);
}
return EFI_SUCCESS;
}
/**
Protocol notify function for gEfiSmmInt15ServiceProtocolGuid protocol
@param Protocol Points to the protocol's unique identifier.
@param Interface Points to the interface instance.
@param Handle The handle on which the interface was installed.
@return Status Code
**/
EFI_STATUS
EFIAPI
SmmInt15ServiceCallback (
IN CONST EFI_GUID *Protocol,
IN VOID *Interface,
IN EFI_HANDLE Handle
)
{
EFI_STATUS Status;
Status = SmmInt15ServiceCallbackCore ();
return EFI_SUCCESS;
}
/**
SMM communication handler.
This function is used to get some initial data from non-SMM environment.
@param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
@param RegisterContext Points to an optional handler context which was specified when the
handler was registered.
@param CommBuffer A pointer to a collection of data in memory that will
be conveyed from a non-SMM environment into an SMM environment.
@param CommBufferSize The size of the CommBuffer.
@retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
should still be called.
**/
EFI_STATUS
EFIAPI
LegacyToEfiSmiHandler (
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *RegisterContext,
IN OUT VOID *CommBuffer,
IN OUT UINTN *CommBufferSize
)
{
//
// If input is invalid, stop processing this SMI
//
if ((CommBuffer == NULL) || (CommBufferSize == NULL)) {
return EFI_SUCCESS;
}
mLocalBbsTable = *((BBS_TABLE **) CommBuffer);
return EFI_SUCCESS;
}
/**
This is the declaration of an EFI image entry point. This entry point is
the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
both device drivers and bus drivers.
@param ImageHandle The firmware allocated handle for the UEFI image.
@param SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The operation completed successfully.
@retval Others An unexpected error occurred.
**/
EFI_STATUS
EFIAPI
LegacyToEfiSmmEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_SMM_INT15_SERVICE_PROTOCOL *SmmInt15Service;
EFI_EVENT InstallLegacyToEfiSmmCallBackFunctionEvent;
EFI_HANDLE DispatchHandle;
SmmInt15Service = NULL;
Status = gSmst->SmmLocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID **) &mSmmVariable);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Allocate the buffer for BootOrder Variable
//
Status = gSmst->SmmAllocatePool (
EfiRuntimeServicesData,
0x400,
&mBootOrderBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
ZeroMem ((VOID *) mBootOrderBuffer, 0x400);
//
// Allocate the buffer for Boot Option Variable
//
Status = gSmst->SmmAllocatePool (
EfiRuntimeServicesData,
EFI_PAGE_SIZE,
&mBootOptionBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
ZeroMem ((VOID *) mBootOptionBuffer, EFI_PAGE_SIZE);
Status = gSmst->SmmLocateProtocol (&gEfiSmmInt15ServiceProtocolGuid, NULL, &SmmInt15Service);
if (!EFI_ERROR (Status)) {
Status = SmmInt15ServiceCallbackCore ();
} else {
Status = gSmst->SmmRegisterProtocolNotify (
&gEfiSmmInt15ServiceProtocolGuid,
SmmInt15ServiceCallback,
&InstallLegacyToEfiSmmCallBackFunctionEvent
);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Register SMM communication handler
//
DispatchHandle = NULL;
Status = gSmst->SmiHandlerRegister (
LegacyToEfiSmiHandler,
&gEfiL05LegacyToEfiCommunicationGuid,
&DispatchHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}