/** @file
|
*
|
* Copyright (c) 2018, Andrei Warkentin <andrey.warkentin@gmail.com>
|
* Copyright (C) 2015, Red Hat, Inc.
|
* Copyright (c) 2006-2014, Intel Corporation. All rights reserved.
|
*
|
* SPDX-License-Identifier: BSD-2-Clause-Patent
|
*
|
**/
|
|
#include "VarBlockService.h"
|
|
//
|
// Minimum delay to enact before reset, when variables are dirty (in μs).
|
// Needed to ensure that SSD-based USB 3.0 devices have time to flush their
|
// write cache after updating the NV vars. A much smaller delay is applied
|
// on Pi 3 compared to Pi 4, as we haven't had reports of issues there yet.
|
//
|
#if (RPI_MODEL == 3)
|
#define PLATFORM_RESET_DELAY 500000
|
#else
|
#define PLATFORM_RESET_DELAY 3500000
|
#endif
|
|
VOID *mSFSRegistration;
|
|
|
VOID
|
InstallProtocolInterfaces (
|
IN EFI_FW_VOL_BLOCK_DEVICE *FvbDevice
|
)
|
{
|
EFI_STATUS Status;
|
EFI_HANDLE FwbHandle;
|
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *OldFwbInterface;
|
|
//
|
// Find a handle with a matching device path that has supports FW Block
|
// protocol.
|
//
|
Status = gBS->LocateDevicePath (&gEfiFirmwareVolumeBlockProtocolGuid,
|
&FvbDevice->DevicePath, &FwbHandle);
|
if (EFI_ERROR (Status)) {
|
//
|
// LocateDevicePath fails so install a new interface and device path.
|
//
|
FwbHandle = NULL;
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
&FwbHandle,
|
&gEfiFirmwareVolumeBlockProtocolGuid,
|
&FvbDevice->FwVolBlockInstance,
|
&gEfiDevicePathProtocolGuid,
|
FvbDevice->DevicePath,
|
NULL
|
);
|
ASSERT_EFI_ERROR (Status);
|
} else if (IsDevicePathEnd (FvbDevice->DevicePath)) {
|
//
|
// Device already exists, so reinstall the FVB protocol
|
//
|
Status = gBS->HandleProtocol (
|
FwbHandle,
|
&gEfiFirmwareVolumeBlockProtocolGuid,
|
(VOID**)&OldFwbInterface
|
);
|
ASSERT_EFI_ERROR (Status);
|
|
Status = gBS->ReinstallProtocolInterface (
|
FwbHandle,
|
&gEfiFirmwareVolumeBlockProtocolGuid,
|
OldFwbInterface,
|
&FvbDevice->FwVolBlockInstance
|
);
|
ASSERT_EFI_ERROR (Status);
|
} else {
|
//
|
// There was a FVB protocol on an End Device Path node
|
//
|
ASSERT (FALSE);
|
}
|
}
|
|
|
STATIC
|
VOID
|
EFIAPI
|
FvbVirtualAddressChangeEvent (
|
IN EFI_EVENT Event,
|
IN VOID *Context
|
)
|
/*++
|
|
Routine Description:
|
|
Fixup internal data so that EFI can be called in virtual mode.
|
|
Arguments:
|
|
(Standard EFI notify event - EFI_EVENT_NOTIFY)
|
|
Returns:
|
|
None
|
|
--*/
|
{
|
EfiConvertPointer (0x0, (VOID**)&mFvInstance->FvBase);
|
EfiConvertPointer (0x0, (VOID**)&mFvInstance->VolumeHeader);
|
EfiConvertPointer (0x0, (VOID**)&mFvInstance);
|
}
|
|
|
VOID
|
InstallVirtualAddressChangeHandler (
|
VOID
|
)
|
{
|
EFI_STATUS Status;
|
EFI_EVENT VirtualAddressChangeEvent;
|
|
Status = gBS->CreateEventEx (
|
EVT_NOTIFY_SIGNAL,
|
TPL_NOTIFY,
|
FvbVirtualAddressChangeEvent,
|
NULL,
|
&gEfiEventVirtualAddressChangeGuid,
|
&VirtualAddressChangeEvent
|
);
|
ASSERT_EFI_ERROR (Status);
|
}
|
|
|
STATIC
|
EFI_STATUS
|
DoDump (
|
IN EFI_DEVICE_PATH_PROTOCOL *Device
|
)
|
{
|
EFI_STATUS Status;
|
EFI_FILE_PROTOCOL *File;
|
|
Status = FileOpen (Device,
|
mFvInstance->MappedFile,
|
&File,
|
EFI_FILE_MODE_WRITE |
|
EFI_FILE_MODE_READ);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Status = FileWrite (File,
|
mFvInstance->Offset,
|
mFvInstance->FvBase,
|
mFvInstance->FvLength);
|
FileClose (File);
|
return Status;
|
}
|
|
|
STATIC
|
VOID
|
EFIAPI
|
DumpVars (
|
IN EFI_EVENT Event,
|
IN VOID *Context
|
)
|
{
|
EFI_STATUS Status;
|
RETURN_STATUS PcdStatus;
|
|
if (mFvInstance->Device == NULL) {
|
DEBUG ((DEBUG_INFO, "Variable store not found?\n"));
|
return;
|
}
|
|
if (!mFvInstance->Dirty) {
|
DEBUG ((DEBUG_INFO, "Variables not dirty, not dumping!\n"));
|
return;
|
}
|
|
Status = DoDump (mFvInstance->Device);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Couldn't dump '%s'\n", mFvInstance->MappedFile));
|
ASSERT_EFI_ERROR (Status);
|
return;
|
}
|
|
DEBUG ((DEBUG_INFO, "Variables dumped!\n"));
|
|
//
|
// Add a reset delay to give time for slow/cached devices
|
// to flush the NV variables write to permanent storage.
|
// But only do so if this won't reduce an existing user-set delay.
|
//
|
if (PcdGet32 (PcdPlatformResetDelay) < PLATFORM_RESET_DELAY) {
|
PcdStatus = PcdSet32S (PcdPlatformResetDelay, PLATFORM_RESET_DELAY);
|
ASSERT_RETURN_ERROR (PcdStatus);
|
}
|
|
mFvInstance->Dirty = FALSE;
|
}
|
|
|
VOID
|
ReadyToBootHandler (
|
IN EFI_EVENT Event,
|
IN VOID *Context
|
)
|
{
|
EFI_STATUS Status;
|
EFI_EVENT ImageInstallEvent;
|
VOID *ImageRegistration;
|
|
Status = gBS->CreateEvent (
|
EVT_NOTIFY_SIGNAL,
|
TPL_CALLBACK,
|
DumpVars,
|
NULL,
|
&ImageInstallEvent
|
);
|
ASSERT_EFI_ERROR (Status);
|
|
Status = gBS->RegisterProtocolNotify (
|
&gEfiLoadedImageProtocolGuid,
|
ImageInstallEvent,
|
&ImageRegistration
|
);
|
ASSERT_EFI_ERROR (Status);
|
|
DumpVars (NULL, NULL);
|
Status = gBS->CloseEvent (Event);
|
ASSERT_EFI_ERROR (Status);
|
}
|
|
|
VOID
|
InstallDumpVarEventHandlers (
|
VOID
|
)
|
{
|
EFI_STATUS Status;
|
EFI_EVENT ResetEvent;
|
EFI_EVENT ReadyToBootEvent;
|
|
Status = gBS->CreateEventEx (
|
EVT_NOTIFY_SIGNAL,
|
TPL_CALLBACK,
|
DumpVars,
|
NULL,
|
&gRaspberryPiEventResetGuid,
|
&ResetEvent
|
);
|
ASSERT_EFI_ERROR (Status);
|
|
Status = gBS->CreateEventEx (
|
EVT_NOTIFY_SIGNAL,
|
TPL_CALLBACK,
|
ReadyToBootHandler,
|
NULL,
|
&gEfiEventReadyToBootGuid,
|
&ReadyToBootEvent
|
);
|
ASSERT_EFI_ERROR (Status);
|
}
|
|
|
VOID
|
EFIAPI
|
OnSimpleFileSystemInstall (
|
IN EFI_EVENT Event,
|
IN VOID *Context
|
)
|
{
|
EFI_STATUS Status;
|
UINTN HandleSize;
|
EFI_HANDLE Handle;
|
EFI_DEVICE_PATH_PROTOCOL *Device;
|
|
if ((mFvInstance->Device != NULL) &&
|
!EFI_ERROR (CheckStoreExists (mFvInstance->Device))) {
|
//
|
// We've already found the variable store before,
|
// and that device is not removed from the ssystem.
|
//
|
return;
|
}
|
|
while (TRUE) {
|
HandleSize = sizeof (EFI_HANDLE);
|
Status = gBS->LocateHandle (
|
ByRegisterNotify,
|
NULL,
|
mSFSRegistration,
|
&HandleSize,
|
&Handle
|
);
|
if (Status == EFI_NOT_FOUND) {
|
break;
|
}
|
|
ASSERT_EFI_ERROR (Status);
|
|
Status = CheckStore (Handle, &Device);
|
if (EFI_ERROR (Status)) {
|
continue;
|
}
|
|
Status = DoDump (Device);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Couldn't update '%s'\n", mFvInstance->MappedFile));
|
ASSERT_EFI_ERROR (Status);
|
continue;
|
}
|
|
if (mFvInstance->Device != NULL) {
|
gBS->FreePool (mFvInstance->Device);
|
}
|
|
DEBUG ((DEBUG_INFO, "Found variable store!\n"));
|
mFvInstance->Device = Device;
|
break;
|
}
|
}
|
|
|
VOID
|
InstallFSNotifyHandler (
|
VOID
|
)
|
{
|
EFI_STATUS Status;
|
EFI_EVENT Event;
|
|
Status = gBS->CreateEvent (
|
EVT_NOTIFY_SIGNAL,
|
TPL_CALLBACK,
|
OnSimpleFileSystemInstall,
|
NULL,
|
&Event
|
);
|
ASSERT_EFI_ERROR (Status);
|
|
Status = gBS->RegisterProtocolNotify (
|
&gEfiSimpleFileSystemProtocolGuid,
|
Event,
|
&mSFSRegistration
|
);
|
ASSERT_EFI_ERROR (Status);
|
}
|