453 lines
12 KiB
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;
|
|
}
|