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

453 lines
12 KiB
C

/** @file
;******************************************************************************
;* Copyright (c) 2017, Insyde Software Corp. 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 "HddSpinDownDxe.h"
EFI_SMM_CONTROL2_PROTOCOL *mSmmControl = NULL;
#if (FixedPcdGetBool (PcdL05PchResetSupported))
PCH_RESET_PROTOCOL *mPchResetProtocol = NULL;
PCH_RESET mOriginalPchReset;
#endif
EFI_RESET_SYSTEM mOriginalResetSystem;
EFI_VARIABLE_DEFAULT_UPDATE_PROTOCOL *mVariableDefaultUpdateProtocol = NULL;
EFI_VARIABLE_DEFAULT_UPDATE_FACTORY_SETTING mOriginalUpdateFactorySetting;
BOOLEAN mPostponeReset = FALSE;
BOOLEAN mHddSpinDownSwSmiIsReady = FALSE;
/**
L05 Trigger HDD spin down.
@param None
@retval EFI_SUCCESS This function execute successfully.
@retval Orther An unexpected error occurred.
**/
EFI_STATUS
L05TriggerHddSpinDown (
)
{
EFI_STATUS Status;
UINT8 SmiDataValue;
if (!mHddSpinDownSwSmiIsReady) {
return EFI_UNSUPPORTED;
}
SmiDataValue = EFI_L05_HDD_SPIN_DOWN_CALLBACK;
Status = mSmmControl->Trigger (
mSmmControl,
&SmiDataValue,
NULL,
0,
0
);
return Status;
}
/**
Hook UpdateFactorySetting() to set Postpone Reset.
@param This Pointer to EFI_VARIABLE_DEFAULT_UPDATE_PROTOCOL instance.
@param RestoreType Restore type to update for the variable store.
@retval EFI_INVALID_PARAMETER Input parameter is invalid.
@retval EFI_SUCCESS Update system setting to factory default successful.
@return Other Other error cause update system to factory default failed.
**/
EFI_STATUS
EFIAPI
HookUpdateFactorySetting (
IN EFI_VARIABLE_DEFAULT_UPDATE_PROTOCOL *This,
IN UINT32 RestoreType
)
{
EFI_STATUS Status;
Status = mOriginalUpdateFactorySetting (This, RestoreType);
if (!EFI_ERROR (Status)) {
mPostponeReset = TRUE;
}
return Status;
}
/**
Restore UpdateFactorySetting() from HookUpdateFactorySetting() to original UpdateFactorySetting().
Set ResetType to PcdL05PostponeResetType for Postpone Reset.
Clean Postpone Reset flag.
@param None
@retval EFI_SUCCESS This function execute successfully.
@retval Orther An unexpected error occurred.
**/
EFI_STATUS
RestoreUpdateFactorySetting (
IN EFI_RESET_TYPE ResetType
)
{
if (!mPostponeReset) {
return EFI_UNSUPPORTED;
}
PcdSet8S (PcdL05PostponeResetType, ((UINT8) ResetType));
mVariableDefaultUpdateProtocol->UpdateFactorySetting = mOriginalUpdateFactorySetting;
mPostponeReset = FALSE;
return EFI_SUCCESS;
}
/**
Notification function of gEfiVariableDefaultUpdateProtocolGuid.
This is a notification function registered on gEfiVariableDefaultUpdateProtocolGuid.
It Hook UpdateFactorySetting() to Postpone Reset.
@param Event Event whose notification function is being invoked.
@param Context Pointer to the notification function's context.
**/
VOID
EFIAPI
VariableDefaultUpdateNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
Status = gBS->LocateProtocol (
&gEfiVariableDefaultUpdateProtocolGuid,
NULL,
(VOID **)&mVariableDefaultUpdateProtocol
);
if (EFI_ERROR (Status)) {
return;
}
mOriginalUpdateFactorySetting = mVariableDefaultUpdateProtocol->UpdateFactorySetting;
mVariableDefaultUpdateProtocol->UpdateFactorySetting = HookUpdateFactorySetting;
}
/**
Postpone Reset.
Check PcdL05PostponeResetType to do Postpone Reset.
@param None
@retval None
**/
VOID
PostponeReset (
)
{
if (PcdGet8 (PcdL05PostponeResetType) != 0xFF) {
gRT->ResetSystem (((EFI_RESET_TYPE) PcdGet8 (PcdL05PostponeResetType)), EFI_SUCCESS, 0, NULL);
}
}
/**
Hdd Spin Down register DisplayBeforeCp to PostponeReset().
@param[in] Event The Event this notify function registered to.
@param[in] Handle The handle associated with a previously registered checkpoint handler.
**/
STATIC
VOID
EFIAPI
HddSpinDownDisplayBeforeCp (
IN EFI_EVENT Event,
IN H2O_CP_HANDLE Handle
)
{
PostponeReset ();
}
#if (FixedPcdGetBool (PcdL05PchResetSupported))
/**
Trigge HDD spin down before PCH reset.
@param None
@retval None
**/
EFI_STATUS
EFIAPI
L05HddSpinDownPchReset (
IN PCH_RESET_PROTOCOL *This,
IN EFI_RESET_TYPE ResetType,
IN UINTN DataSize,
IN VOID *ResetData OPTIONAL
)
{
EFI_STATUS Status;
Status = RestoreUpdateFactorySetting (ResetType);
if (!EFI_ERROR (Status)) {
return Status;
}
if (ResetType != EfiResetWarm) {
L05TriggerHddSpinDown ();
}
Status = mOriginalPchReset (This, ResetType, DataSize, ResetData);
return Status;
}
/**
L05 HDD spin down hook PCH reset.
@param None
@retval None
**/
VOID
L05HddSpinDownHookPchReset (
)
{
mOriginalPchReset = mPchResetProtocol->Reset;
mPchResetProtocol->Reset = L05HddSpinDownPchReset;
}
#endif
/**
Trigge HDD spin down before gRT->ResetSystem.
@param None
@retval None
**/
VOID
L05HddSpinDownResetSystem (
IN EFI_RESET_TYPE ResetType,
IN EFI_STATUS ResetStatus,
IN UINTN DataSize,
IN CHAR16 *ResetData OPTIONAL
)
{
EFI_STATUS Status;
Status = RestoreUpdateFactorySetting (ResetType);
if (!EFI_ERROR (Status)) {
return;
}
if (ResetType != EfiResetWarm) {
L05TriggerHddSpinDown ();
}
mOriginalResetSystem (ResetType, ResetStatus, DataSize, ResetData);
}
/**
L05 HDD spin down hook gRT->ResetSystem.
@param None
@retval None
**/
VOID
L05HddSpinDownHookResetSystem (
)
{
mOriginalResetSystem = gRT->ResetSystem;
gRT->ResetSystem = L05HddSpinDownResetSystem;
}
/**
Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
It converts pointer to new virtual address.
@param Event Event whose notification function is being invoked.
@param Context Pointer to the notification function's context.
**/
VOID
EFIAPI
VirtualAddressChangeNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EfiConvertPointer (0x0, (VOID **) &mSmmControl);
#if (FixedPcdGetBool (PcdL05PchResetSupported))
EfiConvertPointer (0x0, (VOID **) &mOriginalPchReset);
#endif
EfiConvertPointer (0x0, (VOID **) &mOriginalResetSystem);
EfiConvertPointer (0x0, (VOID **) &mHddSpinDownSwSmiIsReady);
}
VOID
EFIAPI
HddSpinDownSwSmiReadyProtocolNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
gBS->CloseEvent (Event);
mHddSpinDownSwSmiIsReady = TRUE;
}
/**
HDD spin down DXE entry
@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
L05HddSpinDownDxeEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_EVENT VirtualAddressChangeEvent;
H2O_CP_HANDLE CpHandle;
EFI_VARIABLE_DEFAULT_UPDATE_PROTOCOL *VariableDefaultUpdateProtocol;
EFI_EVENT Event;
VOID *Registration;
Status = EFI_SUCCESS;
VirtualAddressChangeEvent = NULL;
Status = gBS->LocateProtocol (
&gEfiSmmControl2ProtocolGuid,
NULL,
(VOID **) &mSmmControl
);
if (EFI_ERROR (Status)) {
return Status;
}
#if (FixedPcdGetBool (PcdL05PchResetSupported))
//
// Hook PCH Reset
//
Status = gBS->LocateProtocol (
&gPchResetProtocolGuid,
NULL,
(VOID **) &mPchResetProtocol
);
if (!EFI_ERROR (Status)) {
L05HddSpinDownHookPchReset ();
}
#endif
//
// Hook gRT->ResetSystem
//
L05HddSpinDownHookResetSystem ();
//
// Register the event to convert the pointer for runtime.
//
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
VirtualAddressChangeNotify,
NULL,
&gEfiEventVirtualAddressChangeGuid,
&VirtualAddressChangeEvent
);
Status = gBS->LocateProtocol (
&gEfiVariableDefaultUpdateProtocolGuid,
NULL,
(VOID **)&VariableDefaultUpdateProtocol
);
if (!EFI_ERROR (Status)) {
VariableDefaultUpdateNotify (NULL, NULL);
} else {
//
// Registers the gEfiVariableDefaultUpdateProtocolGuid protocol notification for Hook UpdateFactorySetting()
//
Event = NULL;
Registration = NULL;
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
VariableDefaultUpdateNotify,
NULL,
&Event
);
if (!EFI_ERROR (Status)) {
Status = gBS->RegisterProtocolNotify (
&gEfiVariableDefaultUpdateProtocolGuid,
Event,
&Registration
);
}
}
//
// Register notification on H2O_CP_LOW of gH2OBdsCpDisplayBeforeGuid event.
//
Status = H2OCpRegisterHandler (
&gH2OBdsCpDisplayBeforeGuid,
HddSpinDownDisplayBeforeCp,
H2O_CP_LOW,
&CpHandle
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Checkpoint Register Fail: %g (%r)\n", &gH2OBdsCpDisplayBeforeGuid, Status));
return Status;
}
//
// Register the event to check HddSpinDown SW SMI is ready.
//
Event = NULL;
Registration = NULL;
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
HddSpinDownSwSmiReadyProtocolNotify,
NULL,
&Event
);
if (!EFI_ERROR (Status)) {
Status = gBS->RegisterProtocolNotify (
&gEfiL05HddSpindownSwSmiReadyProtocolGuid,
Event,
&Registration
);
}
return Status;
}