Files
system76-edk2/UefiPayloadPkg/SmmStoreFvb/SmmStoreFvbRuntime.c
Sean Rhodes 1f31c0e81e UefiPayloadPkg: Add support for Firmware Volume Block Protocol
This adds support for FVB in order to support a platform independent
and non-volatile variable store on UefiPayloadPkg. The variable store
makes use the SmmStoreLib to provide an unauthenticed variable store.

Since commit bc744f5893fc4d53275ed26dd8d968011c6a09c1 coreboot supports
the SMMSTORE v2 feature. It implements a SMI handler that is able to
write, read and erase pages in the boot media (SPI flash).
The communication is done using a fixed communication buffer that is
allocated in CBMEM. The existence of this optional feature is advertised
by a coreboot table.
When the SMMSTORE feature is not available the variable emulation is used
by setting PcdEmuVariableNvModeEnable to TRUE.

The DXE component provides runtime services and takes care of virtual to
physical mapping the communication buffers between SMM and OS.

The contents of the variable store can be accessed and modified by any
priviledged application. As authentication is done by runtime services
only the store shouldn't be used to store authenticated variables.

Tested on Linux and Windows 10 on real hardware.
Currently this cannot be tested on coreboot and qemu as it doesn't support
the SMMSTORE on qemu.

Cc: Guo Dong <guo.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Maurice Ma <maurice.ma@intel.com>
Cc: Benjamin You <benjamin.you@intel.com>
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Signed-off-by: Sean Rhodes <sean@starlabs.systems>
Change-Id: I79a234275e25961869edf959cfbf4f8d17df2383
2022-06-03 09:03:42 -06:00

283 lines
8.6 KiB
C

/** @file SmmStoreFvbRuntime.c
Copyright (c) 2022, 9elements GmbH<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Library/UefiLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/DxeServicesTableLib.h>
#include <Library/DevicePathLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/PcdLib.h>
#include <Library/SmmStoreLib.h>
#include "SmmStoreFvbRuntime.h"
STATIC EFI_EVENT mSmmStoreVirtualAddrChangeEvent;
//
// Global variable declarations
//
SMMSTORE_INSTANCE *mSmmStoreInstance;
SMMSTORE_INSTANCE mSmmStoreInstanceTemplate = {
SMMSTORE_SIGNATURE, // Signature
NULL, // Handle ... NEED TO BE FILLED
{
FvbGetAttributes, // GetAttributes
FvbSetAttributes, // SetAttributes
FvbGetPhysicalAddress, // GetPhysicalAddress
FvbGetBlockSize, // GetBlockSize
FvbRead, // Read
FvbWrite, // Write
FvbEraseBlocks, // EraseBlocks
NULL, // ParentHandle
}, // FvbProtoccol
0, // BlockSize ... NEED TO BE FILLED
0, // LastBlock ... NEED TO BE FILLED
0, // MmioAddress ... NEED TO BE FILLED
{
{
{
HARDWARE_DEVICE_PATH,
HW_MEMMAP_DP,
{
(UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
(UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)
}
},
EfiMemoryMappedIO,
(EFI_PHYSICAL_ADDRESS)0, // NEED TO BE FILLED
(EFI_PHYSICAL_ADDRESS)0, // NEED TO BE FILLED
},
{
END_DEVICE_PATH_TYPE,
END_ENTIRE_DEVICE_PATH_SUBTYPE,
{
END_DEVICE_PATH_LENGTH,
0
}
}
} // DevicePath
};
/**
Initialize the SmmStore instance.
@param[in] FvBase The physical MMIO base address of the FV containing
the variable store.
@param[in] NumberofBlocks Number of blocks within the FV.
@param[in] BlockSize The size in bytes of one block within the FV.
@param[in, out] Instance The SmmStore instace to initialize
**/
STATIC
EFI_STATUS
SmmStoreInitInstance (
IN EFI_PHYSICAL_ADDRESS FvBase,
IN UINTN NumberofBlocks,
IN UINTN BlockSize,
IN OUT SMMSTORE_INSTANCE *Instance
)
{
EFI_STATUS Status;
FV_MEMMAP_DEVICE_PATH *FvDevicePath;
ASSERT (Instance != NULL);
Instance->BlockSize = BlockSize;
Instance->LastBlock = NumberofBlocks - 1;
Instance->MmioAddress = FvBase;
FvDevicePath = &Instance->DevicePath;
FvDevicePath->MemMapDevPath.StartingAddress = FvBase;
FvDevicePath->MemMapDevPath.EndingAddress = FvBase + BlockSize * NumberofBlocks - 1;
Status = FvbInitialize (Instance);
if (EFI_ERROR (Status)) {
return Status;
}
Status = gBS->InstallMultipleProtocolInterfaces (
&Instance->Handle,
&gEfiDevicePathProtocolGuid,
&Instance->DevicePath,
&gEfiFirmwareVolumeBlockProtocolGuid,
&Instance->FvbProtocol,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
DEBUG ((DEBUG_INFO, "%a: Created a new instance\n", __FUNCTION__));
return Status;
}
/**
Fixup internal data so that EFI can be call in virtual mode.
Call the passed in Child Notify event and convert any pointers in
lib to virtual mode.
@param[in] Event The Event that is being processed
@param[in] Context Event Context
**/
STATIC
VOID
EFIAPI
SmmStoreVirtualNotifyEvent (
IN EFI_EVENT Event,
IN VOID *Context
)
{
// Convert Fvb
EfiConvertPointer (0x0, (VOID **)&mSmmStoreInstance->FvbProtocol.EraseBlocks);
EfiConvertPointer (0x0, (VOID **)&mSmmStoreInstance->FvbProtocol.GetAttributes);
EfiConvertPointer (0x0, (VOID **)&mSmmStoreInstance->FvbProtocol.GetBlockSize);
EfiConvertPointer (0x0, (VOID **)&mSmmStoreInstance->FvbProtocol.GetPhysicalAddress);
EfiConvertPointer (0x0, (VOID **)&mSmmStoreInstance->FvbProtocol.Read);
EfiConvertPointer (0x0, (VOID **)&mSmmStoreInstance->FvbProtocol.SetAttributes);
EfiConvertPointer (0x0, (VOID **)&mSmmStoreInstance->FvbProtocol.Write);
EfiConvertPointer (0x0, (VOID **)&mSmmStoreInstance->MmioAddress);
EfiConvertPointer (0x0, (VOID **)&mSmmStoreInstance);
return;
}
/**
The user Entry Point for module SmmStoreFvbRuntimeDxe. The user code starts with this function.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The entry point is executed successfully.
@retval other Some error occurs when executing this entry point.
**/
EFI_STATUS
EFIAPI
SmmStoreInitialize (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS MmioAddress;
UINTN BlockSize;
UINTN BlockCount;
UINT32 NvStorageBase;
UINT32 NvStorageSize;
UINT32 NvVariableSize;
UINT32 FtwWorkingSize;
UINT32 FtwSpareSize;
Status = SmmStoreLibInitialize ();
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Failed to initialize SmmStoreLib\n", __FUNCTION__));
return Status;
}
Status = SmmStoreLibGetMmioAddress (&MmioAddress);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Failed to get SmmStore MMIO address\n", __FUNCTION__));
SmmStoreLibDeinitialize ();
return Status;
}
Status = SmmStoreLibGetNumBlocks (&BlockCount);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Failed to get SmmStore No. blocks\n", __FUNCTION__));
SmmStoreLibDeinitialize ();
return Status;
}
Status = SmmStoreLibGetBlockSize (&BlockSize);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: Failed to get SmmStore block size\n", __FUNCTION__));
SmmStoreLibDeinitialize ();
return Status;
}
NvStorageSize = BlockCount * BlockSize;
NvStorageBase = MmioAddress;
FtwSpareSize = (BlockCount / 2) * BlockSize;
FtwWorkingSize = BlockSize;
NvVariableSize = NvStorageSize - FtwSpareSize - FtwWorkingSize;
DEBUG ((DEBUG_INFO, "NvStorageBase:0x%x, NvStorageSize:0x%x\n", NvStorageBase, NvStorageSize));
if (NvVariableSize >= 0x80000000) {
SmmStoreLibDeinitialize ();
return EFI_INVALID_PARAMETER;
}
Status = PcdSet32S (PcdFlashNvStorageVariableSize, NvVariableSize);
ASSERT_EFI_ERROR (Status);
Status = PcdSet32S (PcdFlashNvStorageVariableBase, NvStorageBase);
ASSERT_EFI_ERROR (Status);
Status = PcdSet64S (PcdFlashNvStorageVariableBase64, NvStorageBase);
ASSERT_EFI_ERROR (Status);
Status = PcdSet32S (PcdFlashNvStorageFtwWorkingSize, FtwWorkingSize);
ASSERT_EFI_ERROR (Status);
Status = PcdSet32S (PcdFlashNvStorageFtwWorkingBase, NvStorageBase + NvVariableSize);
ASSERT_EFI_ERROR (Status);
Status = PcdSet64S (PcdFlashNvStorageFtwWorkingBase64, NvStorageBase + NvVariableSize);
ASSERT_EFI_ERROR (Status);
Status = PcdSet32S (PcdFlashNvStorageFtwSpareSize, FtwSpareSize);
ASSERT_EFI_ERROR (Status);
Status = PcdSet32S (PcdFlashNvStorageFtwSpareBase, NvStorageBase + NvVariableSize + FtwWorkingSize);
ASSERT_EFI_ERROR (Status);
Status = PcdSet64S (PcdFlashNvStorageFtwSpareBase64, NvStorageBase + NvVariableSize + FtwWorkingSize);
ASSERT_EFI_ERROR (Status);
mSmmStoreInstance = AllocateRuntimeCopyPool (sizeof (SMMSTORE_INSTANCE), &mSmmStoreInstanceTemplate);
if (mSmmStoreInstance == NULL) {
SmmStoreLibDeinitialize ();
DEBUG ((DEBUG_ERROR, "%a: Out of resources\n", __FUNCTION__));
return EFI_OUT_OF_RESOURCES;
}
Status = SmmStoreInitInstance (
MmioAddress,
BlockCount,
BlockSize,
mSmmStoreInstance
);
if (EFI_ERROR (Status)) {
DEBUG (
(
DEBUG_ERROR,
"%a: Fail to create instance for SmmStore\n",
__FUNCTION__
)
);
FreePool (mSmmStoreInstance);
SmmStoreLibDeinitialize ();
return Status;
}
//
// Register for the virtual address change event
//
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
SmmStoreVirtualNotifyEvent,
NULL,
&gEfiEventVirtualAddressChangeGuid,
&mSmmStoreVirtualAddrChangeEvent
);
ASSERT_EFI_ERROR (Status);
return Status;
}