/** @file * * Copyright (c) 2018, Andrei Warkentin * 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); }