* [edk2-devel] [PATCH 1/3] OvmfPkg/VirtNorFlash: Move low level NOR flash functions into library
2025-04-21 16:58 [edk2-devel] [PATCH 0/3] OvmfPkg/RiscVVirt: Add support for Capsule Firmware Upgrade Tuan Phan
@ 2025-04-21 16:58 ` Tuan Phan
2025-04-21 16:58 ` [edk2-devel] [PATCH 2/3] ArmVirtPkg: Link all targets to the new VirtNorFlashDeviceLib Tuan Phan
2025-04-21 16:58 ` [edk2-devel] [PATCH 3/3] OvmfPkg/RiscVVirt: Add support for Capsule Firmware Upgrade Tuan Phan
2 siblings, 0 replies; 4+ messages in thread
From: Tuan Phan @ 2025-04-21 16:58 UTC (permalink / raw)
To: devel
Cc: andyw, maobibo, lichao, kraxel, jiewen.yao, leif.lindholm,
sami.mujawar, sunilvl, ardb+tianocore, lixianglai, Tuan Phan
This patch refactors low level NOR flash functions into a dedicated library
to enable reuse, particularly for supporting the firmware upgrade feature.
Signed-off-by: Tuan Phan <tphan@ventanamicro.com>
---
.../Include/Library/VirtNorFlashDeviceLib.h | 201 +++++++++
.../VirtNorFlashDeviceLib.c} | 411 ++++++++++++------
.../VirtNorFlashDeviceLib.inf | 30 ++
OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc | 5 +-
OvmfPkg/OvmfPkg.dec | 4 +
OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc | 5 +-
OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h | 345 ---------------
OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c | 180 ++------
OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.h | 156 +++++++
OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf | 3 +-
OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c | 53 ++-
11 files changed, 749 insertions(+), 644 deletions(-)
create mode 100644 OvmfPkg/Include/Library/VirtNorFlashDeviceLib.h
rename OvmfPkg/{VirtNorFlashDxe/VirtNorFlash.c => Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.c} (63%)
create mode 100644 OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.inf
delete mode 100644 OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h
create mode 100644 OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.h
diff --git a/OvmfPkg/Include/Library/VirtNorFlashDeviceLib.h b/OvmfPkg/Include/Library/VirtNorFlashDeviceLib.h
new file mode 100644
index 000000000000..45f4bf628097
--- /dev/null
+++ b/OvmfPkg/Include/Library/VirtNorFlashDeviceLib.h
@@ -0,0 +1,201 @@
+/** @file VirtNorFlashDeviceLib.h
+
+ Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>
+ Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __VIRT_NOR_FLASH_DEVICE_LIB__
+#define __VIRT_NOR_FLASH_DEVICE_LIB__
+
+#include <Base.h>
+#include <PiDxe.h>
+
+#include <Guid/EventGroup.h>
+
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/UefiLib.h>
+
+#define NOR_FLASH_ERASE_RETRY 10
+
+// Device access macros
+// These are necessary because we use 2 x 16bit parts to make up 32bit data
+
+#define HIGH_16_BITS 0xFFFF0000
+#define LOW_16_BITS 0x0000FFFF
+#define LOW_8_BITS 0x000000FF
+
+#define FOLD_32BIT_INTO_16BIT(value) ( ( value >> 16 ) | ( value & LOW_16_BITS ) )
+
+#define GET_LOW_BYTE(value) ( value & LOW_8_BITS )
+#define GET_HIGH_BYTE(value) ( GET_LOW_BYTE( value >> 16 ) )
+
+// Each command must be sent simultaneously to both chips,
+// i.e. at the lower 16 bits AND at the higher 16 bits
+#define CREATE_NOR_ADDRESS(BaseAddr, OffsetAddr) ((BaseAddr) + ((OffsetAddr) << 2))
+#define CREATE_DUAL_CMD(Cmd) ( ( Cmd << 16) | ( Cmd & LOW_16_BITS) )
+#define SEND_NOR_COMMAND(BaseAddr, Offset, Cmd) MmioWrite32 (CREATE_NOR_ADDRESS(BaseAddr,Offset), CREATE_DUAL_CMD(Cmd))
+#define GET_NOR_BLOCK_ADDRESS(BaseAddr, Lba, LbaSize) ( BaseAddr + (UINTN)((Lba) * LbaSize) )
+
+// Status Register Bits
+#define P30_SR_BIT_WRITE (BIT7 << 16 | BIT7)
+#define P30_SR_BIT_ERASE_SUSPEND (BIT6 << 16 | BIT6)
+#define P30_SR_BIT_ERASE (BIT5 << 16 | BIT5)
+#define P30_SR_BIT_PROGRAM (BIT4 << 16 | BIT4)
+#define P30_SR_BIT_VPP (BIT3 << 16 | BIT3)
+#define P30_SR_BIT_PROGRAM_SUSPEND (BIT2 << 16 | BIT2)
+#define P30_SR_BIT_BLOCK_LOCKED (BIT1 << 16 | BIT1)
+#define P30_SR_BIT_BEFP (BIT0 << 16 | BIT0)
+
+// Device Commands for Intel StrataFlash(R) Embedded Memory (P30) Family
+
+// On chip buffer size for buffered programming operations
+// There are 2 chips, each chip can buffer up to 32 (16-bit)words, and each word is 2 bytes.
+// Therefore the total size of the buffer is 2 x 32 x 2 = 128 bytes
+#define P30_MAX_BUFFER_SIZE_IN_BYTES ((UINTN)128)
+#define P30_MAX_BUFFER_SIZE_IN_WORDS (P30_MAX_BUFFER_SIZE_IN_BYTES/((UINTN)4))
+#define MAX_BUFFERED_PROG_ITERATIONS 10000000
+#define BOUNDARY_OF_32_WORDS ((UINTN)0x7F)
+
+// CFI Addresses
+#define P30_CFI_ADDR_QUERY_UNIQUE_QRY 0x10
+#define P30_CFI_ADDR_VENDOR_ID 0x13
+
+// CFI Data
+#define CFI_QRY 0x00595251
+
+// READ Commands
+#define P30_CMD_READ_DEVICE_ID 0x0090
+#define P30_CMD_READ_STATUS_REGISTER 0x0070
+#define P30_CMD_CLEAR_STATUS_REGISTER 0x0050
+#define P30_CMD_READ_ARRAY 0x00FF
+#define P30_CMD_READ_CFI_QUERY 0x0098
+
+// WRITE Commands
+#define P30_CMD_WORD_PROGRAM_SETUP 0x0040
+#define P30_CMD_ALTERNATE_WORD_PROGRAM_SETUP 0x0010
+#define P30_CMD_BUFFERED_PROGRAM_SETUP 0x00E8
+#define P30_CMD_BUFFERED_PROGRAM_CONFIRM 0x00D0
+#define P30_CMD_BEFP_SETUP 0x0080
+#define P30_CMD_BEFP_CONFIRM 0x00D0
+
+// ERASE Commands
+#define P30_CMD_BLOCK_ERASE_SETUP 0x0020
+#define P30_CMD_BLOCK_ERASE_CONFIRM 0x00D0
+
+// SUSPEND Commands
+#define P30_CMD_PROGRAM_OR_ERASE_SUSPEND 0x00B0
+#define P30_CMD_SUSPEND_RESUME 0x00D0
+
+// BLOCK LOCKING / UNLOCKING Commands
+#define P30_CMD_LOCK_BLOCK_SETUP 0x0060
+#define P30_CMD_LOCK_BLOCK 0x0001
+#define P30_CMD_UNLOCK_BLOCK 0x00D0
+#define P30_CMD_LOCK_DOWN_BLOCK 0x002F
+
+// PROTECTION Commands
+#define P30_CMD_PROGRAM_PROTECTION_REGISTER_SETUP 0x00C0
+
+// CONFIGURATION Commands
+#define P30_CMD_READ_CONFIGURATION_REGISTER_SETUP 0x0060
+#define P30_CMD_READ_CONFIGURATION_REGISTER 0x0003
+
+EFI_STATUS
+NorFlashWriteBuffer (
+ IN UINTN DeviceBaseAddress,
+ IN UINTN TargetAddress,
+ IN UINTN BufferSizeInBytes,
+ IN UINT32 *Buffer
+ );
+
+EFI_STATUS
+NorFlashWriteSingleBlock (
+ IN UINTN DeviceBaseAddress,
+ IN UINTN RegionBaseAddress,
+ IN EFI_LBA Lba,
+ IN UINT32 LastBlock,
+ IN UINT32 BlockSize,
+ IN UINTN Size,
+ IN UINTN Offset,
+ IN OUT UINTN *NumBytes,
+ IN UINT8 *Buffer,
+ IN VOID *ShadowBuffer
+ );
+
+EFI_STATUS
+NorFlashWriteBlocks (
+ IN UINTN DeviceBaseAddress,
+ IN UINTN RegionBaseAddress,
+ IN EFI_LBA Lba,
+ IN EFI_LBA LastBlock,
+ IN UINT32 BlockSize,
+ IN UINTN BufferSizeInBytes,
+ IN VOID *Buffer
+ );
+
+EFI_STATUS
+NorFlashReadBlocks (
+ IN UINTN DeviceBaseAddress,
+ IN UINTN RegionBaseAddress,
+ IN EFI_LBA Lba,
+ IN EFI_LBA LastBlock,
+ IN UINT32 BlockSize,
+ IN UINTN BufferSizeInBytes,
+ OUT VOID *Buffer
+ );
+
+EFI_STATUS
+NorFlashRead (
+ IN UINTN DeviceBaseAddress,
+ IN UINTN RegionBaseAddress,
+ IN EFI_LBA Lba,
+ IN UINT32 BlockSize,
+ IN UINTN Size,
+ IN UINTN Offset,
+ IN UINTN BufferSizeInBytes,
+ OUT VOID *Buffer
+ );
+
+EFI_STATUS
+NorFlashEraseSingleBlock (
+ IN UINTN DeviceBaseAddress,
+ IN UINTN BlockAddress
+ );
+
+EFI_STATUS
+NorFlashUnlockSingleBlockIfNecessary (
+ IN UINTN DeviceBaseAddress,
+ IN UINTN BlockAddress
+ );
+
+EFI_STATUS
+NorFlashWriteSingleWord (
+ IN UINTN DeviceBaseAddress,
+ IN UINTN WordAddress,
+ IN UINT32 WriteData
+ );
+
+EFI_STATUS
+NorFlashWriteFullBlock (
+ IN UINTN DeviceBaseAddress,
+ IN UINTN RegionBaseAddress,
+ IN EFI_LBA Lba,
+ IN UINT32 *DataBuffer,
+ IN UINT32 BlockSizeInWords
+ );
+
+EFI_STATUS
+NorFlashUnlockAndEraseSingleBlock (
+ IN UINTN DeviceBaseAddress,
+ IN UINTN BlockAddress
+ );
+
+EFI_STATUS
+NorFlashReset (
+ IN UINTN DeviceBaseAddress
+ );
+
+#endif /* __VIRT_NOR_FLASH_DEVICE_LIB__ */
diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c b/OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.c
similarity index 63%
rename from OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c
rename to OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.c
index e6aaed27ceba..604ac4df3f0b 100644
--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c
+++ b/OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.c
@@ -8,31 +8,25 @@
**/
#include <Library/BaseMemoryLib.h>
-
-#include "VirtNorFlash.h"
-
-//
-// Global variable declarations
-//
-extern NOR_FLASH_INSTANCE **mNorFlashInstances;
-extern UINT32 mNorFlashDeviceCount;
+#include <Library/IoLib.h>
+#include <Library/VirtNorFlashDeviceLib.h>
UINT32
NorFlashReadStatusRegister (
- IN NOR_FLASH_INSTANCE *Instance,
- IN UINTN SR_Address
+ IN UINTN DeviceBaseAddress,
+ IN UINTN SR_Address
)
{
// Prepare to read the status register
- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_STATUS_REGISTER);
- return MmioRead32 (Instance->DeviceBaseAddress);
+ SEND_NOR_COMMAND (DeviceBaseAddress, 0, P30_CMD_READ_STATUS_REGISTER);
+ return MmioRead32 (DeviceBaseAddress);
}
STATIC
BOOLEAN
NorFlashBlockIsLocked (
- IN NOR_FLASH_INSTANCE *Instance,
- IN UINTN BlockAddress
+ IN UINTN DeviceBaseAddress,
+ IN UINTN BlockAddress
)
{
UINT32 LockStatus;
@@ -56,8 +50,8 @@ NorFlashBlockIsLocked (
STATIC
EFI_STATUS
NorFlashUnlockSingleBlock (
- IN NOR_FLASH_INSTANCE *Instance,
- IN UINTN BlockAddress
+ IN UINTN DeviceBaseAddress,
+ IN UINTN BlockAddress
)
{
UINT32 LockStatus;
@@ -73,7 +67,7 @@ NorFlashUnlockSingleBlock (
// Wait until the status register gives us the all clear
do {
- LockStatus = NorFlashReadStatusRegister (Instance, BlockAddress);
+ LockStatus = NorFlashReadStatusRegister (DeviceBaseAddress, BlockAddress);
} while ((LockStatus & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
// Put device back into Read Array mode
@@ -86,16 +80,16 @@ NorFlashUnlockSingleBlock (
EFI_STATUS
NorFlashUnlockSingleBlockIfNecessary (
- IN NOR_FLASH_INSTANCE *Instance,
- IN UINTN BlockAddress
+ IN UINTN DeviceBaseAddress,
+ IN UINTN BlockAddress
)
{
EFI_STATUS Status;
Status = EFI_SUCCESS;
- if (NorFlashBlockIsLocked (Instance, BlockAddress)) {
- Status = NorFlashUnlockSingleBlock (Instance, BlockAddress);
+ if (NorFlashBlockIsLocked (DeviceBaseAddress, BlockAddress)) {
+ Status = NorFlashUnlockSingleBlock (DeviceBaseAddress, BlockAddress);
}
return Status;
@@ -106,8 +100,8 @@ NorFlashUnlockSingleBlockIfNecessary (
**/
EFI_STATUS
NorFlashEraseSingleBlock (
- IN NOR_FLASH_INSTANCE *Instance,
- IN UINTN BlockAddress
+ IN UINTN DeviceBaseAddress,
+ IN UINTN BlockAddress
)
{
EFI_STATUS Status;
@@ -121,7 +115,7 @@ NorFlashEraseSingleBlock (
// Wait until the status register gives us the all clear
do {
- StatusRegister = NorFlashReadStatusRegister (Instance, BlockAddress);
+ StatusRegister = NorFlashReadStatusRegister (DeviceBaseAddress, BlockAddress);
} while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
if (StatusRegister & P30_SR_BIT_VPP) {
@@ -147,20 +141,20 @@ NorFlashEraseSingleBlock (
if (EFI_ERROR (Status)) {
// Clear the Status Register
- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
+ SEND_NOR_COMMAND (DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
}
// Put device back into Read Array mode
- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
+ SEND_NOR_COMMAND (DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
return Status;
}
EFI_STATUS
NorFlashWriteSingleWord (
- IN NOR_FLASH_INSTANCE *Instance,
- IN UINTN WordAddress,
- IN UINT32 WriteData
+ IN UINTN DeviceBaseAddress,
+ IN UINTN WordAddress,
+ IN UINT32 WriteData
)
{
EFI_STATUS Status;
@@ -177,7 +171,7 @@ NorFlashWriteSingleWord (
// Wait for the write to complete and then check for any errors; i.e. check the Status Register
do {
// Prepare to read the status register
- StatusRegister = NorFlashReadStatusRegister (Instance, WordAddress);
+ StatusRegister = NorFlashReadStatusRegister (DeviceBaseAddress, WordAddress);
// The chip is busy while the WRITE bit is not asserted
} while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
@@ -202,7 +196,7 @@ NorFlashWriteSingleWord (
if (!EFI_ERROR (Status)) {
// Clear the Status Register
- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
+ SEND_NOR_COMMAND (DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
}
return Status;
@@ -225,10 +219,10 @@ NorFlashWriteSingleWord (
*/
EFI_STATUS
NorFlashWriteBuffer (
- IN NOR_FLASH_INSTANCE *Instance,
- IN UINTN TargetAddress,
- IN UINTN BufferSizeInBytes,
- IN UINT32 *Buffer
+ IN UINTN DeviceBaseAddress,
+ IN UINTN TargetAddress,
+ IN UINTN BufferSizeInBytes,
+ IN UINT32 *Buffer
)
{
EFI_STATUS Status;
@@ -299,11 +293,11 @@ NorFlashWriteBuffer (
}
// Issue the Buffered Program Confirm command, to start the programming operation
- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_BUFFERED_PROGRAM_CONFIRM);
+ SEND_NOR_COMMAND (DeviceBaseAddress, 0, P30_CMD_BUFFERED_PROGRAM_CONFIRM);
// Wait for the write to complete and then check for any errors; i.e. check the Status Register
do {
- StatusRegister = NorFlashReadStatusRegister (Instance, TargetAddress);
+ StatusRegister = NorFlashReadStatusRegister (DeviceBaseAddress, TargetAddress);
// The chip is busy while the WRITE bit is not asserted
} while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
@@ -330,7 +324,135 @@ NorFlashWriteBuffer (
if (!EFI_ERROR (Status)) {
// Clear the Status Register
- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
+ SEND_NOR_COMMAND (DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
+ }
+
+ return Status;
+}
+
+/**
+ * This function unlock and erase an entire NOR Flash block.
+ **/
+EFI_STATUS
+NorFlashUnlockAndEraseSingleBlock (
+ IN UINTN DeviceBaseAddress,
+ IN UINTN BlockAddress
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+
+ Index = 0;
+ // The block erase might fail a first time (SW bug ?). Retry it ...
+ do {
+ // Unlock the block if we have to
+ Status = NorFlashUnlockSingleBlockIfNecessary (DeviceBaseAddress, BlockAddress);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ Status = NorFlashEraseSingleBlock (DeviceBaseAddress, BlockAddress);
+ Index++;
+ } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED));
+
+ if (Index == NOR_FLASH_ERASE_RETRY) {
+ DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress, Index));
+ }
+
+ return Status;
+}
+
+EFI_STATUS
+NorFlashWriteFullBlock (
+ IN UINTN DeviceBaseAddress,
+ IN UINTN RegionBaseAddress,
+ IN EFI_LBA Lba,
+ IN UINT32 *DataBuffer,
+ IN UINT32 BlockSizeInWords
+ )
+{
+ EFI_STATUS Status;
+ UINTN WordAddress;
+ UINT32 WordIndex;
+ UINTN BufferIndex;
+ UINTN BlockAddress;
+ UINTN BuffersInBlock;
+ UINTN RemainingWords;
+ UINTN Cnt;
+
+ Status = EFI_SUCCESS;
+
+ // Get the physical address of the block
+ BlockAddress = GET_NOR_BLOCK_ADDRESS (RegionBaseAddress, Lba, BlockSizeInWords * 4);
+
+ // Start writing from the first address at the start of the block
+ WordAddress = BlockAddress;
+ Status = NorFlashUnlockAndEraseSingleBlock (DeviceBaseAddress, BlockAddress);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress));
+ goto EXIT;
+ }
+
+ // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
+
+ // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero
+ if ((WordAddress & BOUNDARY_OF_32_WORDS) == 0x00) {
+ // First, break the entire block into buffer-sized chunks.
+ BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES;
+
+ // Then feed each buffer chunk to the NOR Flash
+ // If a buffer does not contain any data, don't write it.
+ for (BufferIndex = 0;
+ BufferIndex < BuffersInBlock;
+ BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS
+ )
+ {
+ // Check the buffer to see if it contains any data (not set all 1s).
+ for (Cnt = 0; Cnt < P30_MAX_BUFFER_SIZE_IN_WORDS; Cnt++) {
+ if (~DataBuffer[Cnt] != 0 ) {
+ // Some data found, write the buffer.
+ Status = NorFlashWriteBuffer (
+ DeviceBaseAddress,
+ WordAddress,
+ P30_MAX_BUFFER_SIZE_IN_BYTES,
+ DataBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Finally, finish off any remaining words that are less than the maximum size of the buffer
+ RemainingWords = BlockSizeInWords % P30_MAX_BUFFER_SIZE_IN_WORDS;
+
+ if (RemainingWords != 0) {
+ Status = NorFlashWriteBuffer (DeviceBaseAddress, WordAddress, (RemainingWords * 4), DataBuffer);
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+ }
+ } else {
+ // For now, use the single word programming algorithm
+ // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,
+ // i.e. which ends in the range 0x......01 - 0x......7F.
+ for (WordIndex = 0; WordIndex < BlockSizeInWords; WordIndex++, DataBuffer++, WordAddress = WordAddress + 4) {
+ Status = NorFlashWriteSingleWord (DeviceBaseAddress, WordAddress, *DataBuffer);
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+ }
+ }
+
+EXIT:
+ // Put device back into Read Array mode
+ SEND_NOR_COMMAND (DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status));
}
return Status;
@@ -338,10 +460,13 @@ NorFlashWriteBuffer (
EFI_STATUS
NorFlashWriteBlocks (
- IN NOR_FLASH_INSTANCE *Instance,
- IN EFI_LBA Lba,
- IN UINTN BufferSizeInBytes,
- IN VOID *Buffer
+ IN UINTN DeviceBaseAddress,
+ IN UINTN RegionBaseAddress,
+ IN EFI_LBA Lba,
+ IN EFI_LBA LastBlock,
+ IN UINT32 BlockSize,
+ IN UINTN BufferSizeInBytes,
+ IN VOID *Buffer
)
{
UINT32 *pWriteBuffer;
@@ -365,22 +490,22 @@ NorFlashWriteBlocks (
}
// The size of the buffer must be a multiple of the block size
- DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->BlockSize));
- if ((BufferSizeInBytes % Instance->BlockSize) != 0) {
+ DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", BlockSize));
+ if ((BufferSizeInBytes % BlockSize) != 0) {
return EFI_BAD_BUFFER_SIZE;
}
// All blocks must be within the device
- NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->BlockSize;
+ NumBlocks = ((UINT32)BufferSizeInBytes) / BlockSize;
- DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->LastBlock, Lba));
+ DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, LastBlock, Lba));
- if ((Lba + NumBlocks) > (Instance->LastBlock + 1)) {
+ if ((Lba + NumBlocks) > (LastBlock + 1)) {
DEBUG ((DEBUG_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n"));
return EFI_INVALID_PARAMETER;
}
- BlockSizeInWords = Instance->BlockSize / 4;
+ BlockSizeInWords = BlockSize / 4;
// Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer
// to a proper data type, so use *ReadBuffer
@@ -390,7 +515,13 @@ NorFlashWriteBlocks (
for (BlockCount = 0; BlockCount < NumBlocks; BlockCount++, CurrentBlock++, pWriteBuffer = pWriteBuffer + BlockSizeInWords) {
DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN)CurrentBlock));
- Status = NorFlashWriteFullBlock (Instance, CurrentBlock, pWriteBuffer, BlockSizeInWords);
+ Status = NorFlashWriteFullBlock (
+ DeviceBaseAddress,
+ RegionBaseAddress,
+ CurrentBlock,
+ pWriteBuffer,
+ BlockSizeInWords
+ );
if (EFI_ERROR (Status)) {
break;
@@ -403,10 +534,13 @@ NorFlashWriteBlocks (
EFI_STATUS
NorFlashReadBlocks (
- IN NOR_FLASH_INSTANCE *Instance,
- IN EFI_LBA Lba,
- IN UINTN BufferSizeInBytes,
- OUT VOID *Buffer
+ IN UINTN DeviceBaseAddress,
+ IN UINTN RegionBaseAddress,
+ IN EFI_LBA Lba,
+ IN EFI_LBA LastBlock,
+ IN UINT32 BlockSize,
+ IN UINTN BufferSizeInBytes,
+ OUT VOID *Buffer
)
{
UINT32 NumBlocks;
@@ -416,8 +550,8 @@ NorFlashReadBlocks (
DEBUG_BLKIO,
"NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n",
BufferSizeInBytes,
- Instance->BlockSize,
- Instance->LastBlock,
+ BlockSize,
+ LastBlock,
Lba
));
@@ -432,27 +566,27 @@ NorFlashReadBlocks (
}
// The size of the buffer must be a multiple of the block size
- if ((BufferSizeInBytes % Instance->BlockSize) != 0) {
+ if ((BufferSizeInBytes % BlockSize) != 0) {
return EFI_BAD_BUFFER_SIZE;
}
// All blocks must be within the device
- NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->BlockSize;
+ NumBlocks = ((UINT32)BufferSizeInBytes) / BlockSize;
- if ((Lba + NumBlocks) > (Instance->LastBlock + 1)) {
+ if ((Lba + NumBlocks) > (LastBlock + 1)) {
DEBUG ((DEBUG_ERROR, "NorFlashReadBlocks: ERROR - Read will exceed last block\n"));
return EFI_INVALID_PARAMETER;
}
// Get the address to start reading from
StartAddress = GET_NOR_BLOCK_ADDRESS (
- Instance->RegionBaseAddress,
+ RegionBaseAddress,
Lba,
- Instance->BlockSize
+ BlockSize
);
// Put the device into Read Array mode
- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
+ SEND_NOR_COMMAND (DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
// Readout the data
CopyMem (Buffer, (VOID *)StartAddress, BufferSizeInBytes);
@@ -462,11 +596,14 @@ NorFlashReadBlocks (
EFI_STATUS
NorFlashRead (
- IN NOR_FLASH_INSTANCE *Instance,
- IN EFI_LBA Lba,
- IN UINTN Offset,
- IN UINTN BufferSizeInBytes,
- OUT VOID *Buffer
+ IN UINTN DeviceBaseAddress,
+ IN UINTN RegionBaseAddress,
+ IN EFI_LBA Lba,
+ IN UINT32 BlockSize,
+ IN UINTN Size,
+ IN UINTN Offset,
+ IN UINTN BufferSizeInBytes,
+ OUT VOID *Buffer
)
{
UINTN StartAddress;
@@ -481,20 +618,20 @@ NorFlashRead (
return EFI_SUCCESS;
}
- if (((Lba * Instance->BlockSize) + Offset + BufferSizeInBytes) > Instance->Size) {
+ if (((Lba * BlockSize) + Offset + BufferSizeInBytes) > Size) {
DEBUG ((DEBUG_ERROR, "NorFlashRead: ERROR - Read will exceed device size.\n"));
return EFI_INVALID_PARAMETER;
}
// Get the address to start reading from
StartAddress = GET_NOR_BLOCK_ADDRESS (
- Instance->RegionBaseAddress,
+ RegionBaseAddress,
Lba,
- Instance->BlockSize
+ BlockSize
);
// Put the device into Read Array mode
- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
+ SEND_NOR_COMMAND (DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
// Readout the data
CopyMem (Buffer, (VOID *)(StartAddress + Offset), BufferSizeInBytes);
@@ -505,27 +642,47 @@ NorFlashRead (
STATIC
EFI_STATUS
NorFlashWriteSingleBlockWithErase (
- IN NOR_FLASH_INSTANCE *Instance,
- IN EFI_LBA Lba,
- IN UINTN Offset,
- IN OUT UINTN *NumBytes,
- IN UINT8 *Buffer
+ IN UINTN DeviceBaseAddress,
+ IN UINTN RegionBaseAddress,
+ IN EFI_LBA Lba,
+ IN UINT32 LastBlock,
+ IN UINT32 BlockSize,
+ IN UINTN Offset,
+ IN OUT UINTN *NumBytes,
+ IN UINT8 *Buffer,
+ IN VOID *ShadowBuffer
)
{
EFI_STATUS Status;
// Read NOR Flash data into shadow buffer
- Status = NorFlashReadBlocks (Instance, Lba, Instance->BlockSize, Instance->ShadowBuffer);
+ Status = NorFlashReadBlocks (
+ DeviceBaseAddress,
+ RegionBaseAddress,
+ Lba,
+ LastBlock,
+ BlockSize,
+ BlockSize,
+ ShadowBuffer
+ );
if (EFI_ERROR (Status)) {
// Return one of the pre-approved error statuses
return EFI_DEVICE_ERROR;
}
// Put the data at the appropriate location inside the buffer area
- CopyMem ((VOID *)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes);
+ CopyMem ((VOID *)((UINTN)ShadowBuffer + Offset), Buffer, *NumBytes);
// Write the modified buffer back to the NorFlash
- Status = NorFlashWriteBlocks (Instance, Lba, Instance->BlockSize, Instance->ShadowBuffer);
+ Status = NorFlashWriteBlocks (
+ DeviceBaseAddress,
+ RegionBaseAddress,
+ Lba,
+ LastBlock,
+ BlockSize,
+ BlockSize,
+ ShadowBuffer
+ );
if (EFI_ERROR (Status)) {
// Return one of the pre-approved error statuses
return EFI_DEVICE_ERROR;
@@ -540,16 +697,20 @@ NorFlashWriteSingleBlockWithErase (
*/
EFI_STATUS
NorFlashWriteSingleBlock (
- IN NOR_FLASH_INSTANCE *Instance,
- IN EFI_LBA Lba,
- IN UINTN Offset,
- IN OUT UINTN *NumBytes,
- IN UINT8 *Buffer
+ IN UINTN DeviceBaseAddress,
+ IN UINTN RegionBaseAddress,
+ IN EFI_LBA Lba,
+ IN UINT32 LastBlock,
+ IN UINT32 BlockSize,
+ IN UINTN Size,
+ IN UINTN Offset,
+ IN OUT UINTN *NumBytes,
+ IN UINT8 *Buffer,
+ IN VOID *ShadowBuffer
)
{
EFI_STATUS Status;
UINTN CurOffset;
- UINTN BlockSize;
UINTN BlockAddress;
UINT8 *OrigData;
UINTN Start, End;
@@ -558,14 +719,11 @@ NorFlashWriteSingleBlock (
DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer));
// Check we did get some memory. Buffer is BlockSize.
- if (Instance->ShadowBuffer == NULL) {
+ if (ShadowBuffer == NULL) {
DEBUG ((DEBUG_ERROR, "FvbWrite: ERROR - Buffer not ready\n"));
return EFI_DEVICE_ERROR;
}
- // Cache the block size to avoid de-referencing pointers all the time
- BlockSize = Instance->BlockSize;
-
// The write must not span block boundaries.
// We need to check each variable individually because adding two large values together overflows.
if ((Offset >= BlockSize) ||
@@ -620,11 +778,14 @@ NorFlashWriteSingleBlock (
// Read the old version of the data into the shadow buffer
Status = NorFlashRead (
- Instance,
+ DeviceBaseAddress,
+ RegionBaseAddress,
Lba,
+ BlockSize,
+ Size,
Start,
End - Start,
- Instance->ShadowBuffer
+ ShadowBuffer
);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
@@ -632,7 +793,7 @@ NorFlashWriteSingleBlock (
// Make OrigData point to the start of the old version of the data inside
// the word aligned buffer
- OrigData = Instance->ShadowBuffer + (Offset & BOUNDARY_OF_32_WORDS);
+ OrigData = ShadowBuffer + (Offset & BOUNDARY_OF_32_WORDS);
// Update the buffer containing the old version of the data with the new
// contents, while checking whether the old version had any bits cleared
@@ -640,11 +801,15 @@ NorFlashWriteSingleBlock (
for (CurOffset = 0; CurOffset < *NumBytes; CurOffset++) {
if (~(UINT32)OrigData[CurOffset] & (UINT32)Buffer[CurOffset]) {
Status = NorFlashWriteSingleBlockWithErase (
- Instance,
+ DeviceBaseAddress,
+ RegionBaseAddress,
Lba,
+ LastBlock,
+ BlockSize,
Offset,
NumBytes,
- Buffer
+ Buffer,
+ ShadowBuffer
);
return Status;
}
@@ -655,10 +820,10 @@ NorFlashWriteSingleBlock (
//
// Write the updated buffer to NOR.
//
- BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize);
+ BlockAddress = GET_NOR_BLOCK_ADDRESS (RegionBaseAddress, Lba, BlockSize);
// Unlock the block if we have to
- Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
+ Status = NorFlashUnlockSingleBlockIfNecessary (DeviceBaseAddress, BlockAddress);
if (EFI_ERROR (Status)) {
goto Exit;
}
@@ -666,10 +831,10 @@ NorFlashWriteSingleBlock (
Count = (End - Start) / P30_MAX_BUFFER_SIZE_IN_BYTES;
for (Index = 0; Index < Count; Index++) {
Status = NorFlashWriteBuffer (
- Instance,
+ DeviceBaseAddress,
BlockAddress + Start + Index * P30_MAX_BUFFER_SIZE_IN_BYTES,
P30_MAX_BUFFER_SIZE_IN_BYTES,
- Instance->ShadowBuffer + Index * P30_MAX_BUFFER_SIZE_IN_BYTES
+ ShadowBuffer + Index * P30_MAX_BUFFER_SIZE_IN_BYTES
);
if (EFI_ERROR (Status)) {
goto Exit;
@@ -677,66 +842,32 @@ NorFlashWriteSingleBlock (
}
} else {
Status = NorFlashWriteSingleBlockWithErase (
- Instance,
+ DeviceBaseAddress,
+ RegionBaseAddress,
Lba,
+ LastBlock,
+ BlockSize,
Offset,
NumBytes,
- Buffer
+ Buffer,
+ ShadowBuffer
);
return Status;
}
Exit:
// Put device back into Read Array mode
- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
+ SEND_NOR_COMMAND (DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
return Status;
}
EFI_STATUS
NorFlashReset (
- IN NOR_FLASH_INSTANCE *Instance
+ IN UINTN DeviceBaseAddress
)
{
// As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode
- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
+ SEND_NOR_COMMAND (DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
return EFI_SUCCESS;
}
-
-/**
- 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
-**/
-VOID
-EFIAPI
-NorFlashVirtualNotifyEvent (
- IN EFI_EVENT Event,
- IN VOID *Context
- )
-{
- UINTN Index;
-
- for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
- EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->DeviceBaseAddress);
- EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->RegionBaseAddress);
-
- // Convert Fvb
- EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks);
- EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes);
- EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize);
- EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress);
- EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.Read);
- EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes);
- EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.Write);
-
- if (mNorFlashInstances[Index]->ShadowBuffer != NULL) {
- EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->ShadowBuffer);
- }
- }
-
- return;
-}
diff --git a/OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.inf b/OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.inf
new file mode 100644
index 000000000000..f99c8fb6b33a
--- /dev/null
+++ b/OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.inf
@@ -0,0 +1,30 @@
+#/** @file
+#
+# Component description file for NorFlashDxe module
+#
+# Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+[Defines]
+ INF_VERSION = 1.29
+ BASE_NAME = VirtNorFlashDeviceLib
+ FILE_GUID = 3f8c8c4b-5b92-4e71-ae73-9f7d2f1e33d4
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = VirtNorFlashDeviceLib
+
+[Sources.common]
+ VirtNorFlashDeviceLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ IoLib
diff --git a/OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc b/OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc
index 89abdf9f779e..27a2d1baf5df 100644
--- a/OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc
+++ b/OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc
@@ -507,7 +507,10 @@
#
# Variable
#
- OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf
+ OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf {
+ <LibraryClasses>
+ VirtNorFlashDeviceLib|OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.inf
+ }
MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf {
<LibraryClasses>
NULL|EmbeddedPkg/Library/NvVarStoreFormattedLib/NvVarStoreFormattedLib.inf
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 1703f5063711..529983af1923 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -120,6 +120,10 @@
# transports.
VirtioMmioDeviceLib|Include/Library/VirtioMmioDeviceLib.h
+ ## @libraryclass Provides a Device Nor flash interface.
+ #
+ VirtNorFlashDeviceLib|Include/Library/VirtNorFlashDeviceLib.h
+
## @libraryclass Provides a Nor flash interface.
#
VirtNorFlashPlatformLib|Include/Library/VirtNorFlashPlatformLib.h
diff --git a/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc b/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc
index ca211b3db356..8e864e123f08 100644
--- a/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc
+++ b/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc
@@ -324,7 +324,10 @@
MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf
- OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf
+ OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf {
+ <LibraryClasses>
+ VirtNorFlashDeviceLib|OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.inf
+ }
MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
#
diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h
deleted file mode 100644
index 455eafacc2cf..000000000000
--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h
+++ /dev/null
@@ -1,345 +0,0 @@
-/** @file NorFlash.h
-
- Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>
-
- SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#ifndef __VIRT_NOR_FLASH__
-#define __VIRT_NOR_FLASH__
-
-#include <Base.h>
-#include <PiDxe.h>
-
-#include <Guid/EventGroup.h>
-
-#include <Protocol/FirmwareVolumeBlock.h>
-
-#include <Library/DebugLib.h>
-#include <Library/IoLib.h>
-#include <Library/UefiLib.h>
-#include <Library/UefiRuntimeLib.h>
-#include <Library/VirtNorFlashPlatformLib.h>
-
-#define NOR_FLASH_ERASE_RETRY 10
-
-// Device access macros
-// These are necessary because we use 2 x 16bit parts to make up 32bit data
-
-#define HIGH_16_BITS 0xFFFF0000
-#define LOW_16_BITS 0x0000FFFF
-#define LOW_8_BITS 0x000000FF
-
-#define FOLD_32BIT_INTO_16BIT(value) ( ( value >> 16 ) | ( value & LOW_16_BITS ) )
-
-#define GET_LOW_BYTE(value) ( value & LOW_8_BITS )
-#define GET_HIGH_BYTE(value) ( GET_LOW_BYTE( value >> 16 ) )
-
-// Each command must be sent simultaneously to both chips,
-// i.e. at the lower 16 bits AND at the higher 16 bits
-#define CREATE_NOR_ADDRESS(BaseAddr, OffsetAddr) ((BaseAddr) + ((OffsetAddr) << 2))
-#define CREATE_DUAL_CMD(Cmd) ( ( Cmd << 16) | ( Cmd & LOW_16_BITS) )
-#define SEND_NOR_COMMAND(BaseAddr, Offset, Cmd) MmioWrite32 (CREATE_NOR_ADDRESS(BaseAddr,Offset), CREATE_DUAL_CMD(Cmd))
-#define GET_NOR_BLOCK_ADDRESS(BaseAddr, Lba, LbaSize) ( BaseAddr + (UINTN)((Lba) * LbaSize) )
-
-// Status Register Bits
-#define P30_SR_BIT_WRITE (BIT7 << 16 | BIT7)
-#define P30_SR_BIT_ERASE_SUSPEND (BIT6 << 16 | BIT6)
-#define P30_SR_BIT_ERASE (BIT5 << 16 | BIT5)
-#define P30_SR_BIT_PROGRAM (BIT4 << 16 | BIT4)
-#define P30_SR_BIT_VPP (BIT3 << 16 | BIT3)
-#define P30_SR_BIT_PROGRAM_SUSPEND (BIT2 << 16 | BIT2)
-#define P30_SR_BIT_BLOCK_LOCKED (BIT1 << 16 | BIT1)
-#define P30_SR_BIT_BEFP (BIT0 << 16 | BIT0)
-
-// Device Commands for Intel StrataFlash(R) Embedded Memory (P30) Family
-
-// On chip buffer size for buffered programming operations
-// There are 2 chips, each chip can buffer up to 32 (16-bit)words, and each word is 2 bytes.
-// Therefore the total size of the buffer is 2 x 32 x 2 = 128 bytes
-#define P30_MAX_BUFFER_SIZE_IN_BYTES ((UINTN)128)
-#define P30_MAX_BUFFER_SIZE_IN_WORDS (P30_MAX_BUFFER_SIZE_IN_BYTES/((UINTN)4))
-#define MAX_BUFFERED_PROG_ITERATIONS 10000000
-#define BOUNDARY_OF_32_WORDS ((UINTN)0x7F)
-
-// CFI Addresses
-#define P30_CFI_ADDR_QUERY_UNIQUE_QRY 0x10
-#define P30_CFI_ADDR_VENDOR_ID 0x13
-
-// CFI Data
-#define CFI_QRY 0x00595251
-
-// READ Commands
-#define P30_CMD_READ_DEVICE_ID 0x0090
-#define P30_CMD_READ_STATUS_REGISTER 0x0070
-#define P30_CMD_CLEAR_STATUS_REGISTER 0x0050
-#define P30_CMD_READ_ARRAY 0x00FF
-#define P30_CMD_READ_CFI_QUERY 0x0098
-
-// WRITE Commands
-#define P30_CMD_WORD_PROGRAM_SETUP 0x0040
-#define P30_CMD_ALTERNATE_WORD_PROGRAM_SETUP 0x0010
-#define P30_CMD_BUFFERED_PROGRAM_SETUP 0x00E8
-#define P30_CMD_BUFFERED_PROGRAM_CONFIRM 0x00D0
-#define P30_CMD_BEFP_SETUP 0x0080
-#define P30_CMD_BEFP_CONFIRM 0x00D0
-
-// ERASE Commands
-#define P30_CMD_BLOCK_ERASE_SETUP 0x0020
-#define P30_CMD_BLOCK_ERASE_CONFIRM 0x00D0
-
-// SUSPEND Commands
-#define P30_CMD_PROGRAM_OR_ERASE_SUSPEND 0x00B0
-#define P30_CMD_SUSPEND_RESUME 0x00D0
-
-// BLOCK LOCKING / UNLOCKING Commands
-#define P30_CMD_LOCK_BLOCK_SETUP 0x0060
-#define P30_CMD_LOCK_BLOCK 0x0001
-#define P30_CMD_UNLOCK_BLOCK 0x00D0
-#define P30_CMD_LOCK_DOWN_BLOCK 0x002F
-
-// PROTECTION Commands
-#define P30_CMD_PROGRAM_PROTECTION_REGISTER_SETUP 0x00C0
-
-// CONFIGURATION Commands
-#define P30_CMD_READ_CONFIGURATION_REGISTER_SETUP 0x0060
-#define P30_CMD_READ_CONFIGURATION_REGISTER 0x0003
-
-#define NOR_FLASH_SIGNATURE SIGNATURE_32('n', 'o', 'r', '0')
-#define INSTANCE_FROM_FVB_THIS(a) CR(a, NOR_FLASH_INSTANCE, FvbProtocol, NOR_FLASH_SIGNATURE)
-
-typedef struct _NOR_FLASH_INSTANCE NOR_FLASH_INSTANCE;
-
-#pragma pack (1)
-typedef struct {
- VENDOR_DEVICE_PATH Vendor;
- UINT8 Index;
- EFI_DEVICE_PATH_PROTOCOL End;
-} NOR_FLASH_DEVICE_PATH;
-#pragma pack ()
-
-struct _NOR_FLASH_INSTANCE {
- UINT32 Signature;
- EFI_HANDLE Handle;
-
- UINTN DeviceBaseAddress;
- UINTN RegionBaseAddress;
- UINTN Size;
- EFI_LBA StartLba;
- EFI_LBA LastBlock;
- UINT32 BlockSize;
-
- EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL FvbProtocol;
- VOID *ShadowBuffer;
-
- NOR_FLASH_DEVICE_PATH DevicePath;
-};
-
-EFI_STATUS
-NorFlashReadCfiData (
- IN UINTN DeviceBaseAddress,
- IN UINTN CFI_Offset,
- IN UINT32 NumberOfBytes,
- OUT UINT32 *Data
- );
-
-EFI_STATUS
-NorFlashWriteBuffer (
- IN NOR_FLASH_INSTANCE *Instance,
- IN UINTN TargetAddress,
- IN UINTN BufferSizeInBytes,
- IN UINT32 *Buffer
- );
-
-//
-// NorFlashFvbDxe.c
-//
-
-EFI_STATUS
-EFIAPI
-FvbGetAttributes (
- IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
- OUT EFI_FVB_ATTRIBUTES_2 *Attributes
- );
-
-EFI_STATUS
-EFIAPI
-FvbSetAttributes (
- IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
- IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes
- );
-
-EFI_STATUS
-EFIAPI
-FvbGetPhysicalAddress (
- IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
- OUT EFI_PHYSICAL_ADDRESS *Address
- );
-
-EFI_STATUS
-EFIAPI
-FvbGetBlockSize (
- IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
- IN EFI_LBA Lba,
- OUT UINTN *BlockSize,
- OUT UINTN *NumberOfBlocks
- );
-
-EFI_STATUS
-EFIAPI
-FvbRead (
- IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
- IN EFI_LBA Lba,
- IN UINTN Offset,
- IN OUT UINTN *NumBytes,
- IN OUT UINT8 *Buffer
- );
-
-EFI_STATUS
-EFIAPI
-FvbWrite (
- IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
- IN EFI_LBA Lba,
- IN UINTN Offset,
- IN OUT UINTN *NumBytes,
- IN UINT8 *Buffer
- );
-
-EFI_STATUS
-EFIAPI
-FvbEraseBlocks (
- IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
- ...
- );
-
-EFI_STATUS
-ValidateFvHeader (
- IN NOR_FLASH_INSTANCE *Instance
- );
-
-EFI_STATUS
-InitializeFvAndVariableStoreHeaders (
- IN NOR_FLASH_INSTANCE *Instance
- );
-
-VOID
-EFIAPI
-FvbVirtualNotifyEvent (
- IN EFI_EVENT Event,
- IN VOID *Context
- );
-
-//
-// NorFlashDxe.c
-//
-
-EFI_STATUS
-NorFlashWriteFullBlock (
- IN NOR_FLASH_INSTANCE *Instance,
- IN EFI_LBA Lba,
- IN UINT32 *DataBuffer,
- IN UINT32 BlockSizeInWords
- );
-
-EFI_STATUS
-NorFlashUnlockAndEraseSingleBlock (
- IN NOR_FLASH_INSTANCE *Instance,
- IN UINTN BlockAddress
- );
-
-EFI_STATUS
-NorFlashCreateInstance (
- IN UINTN NorFlashDeviceBase,
- IN UINTN NorFlashRegionBase,
- IN UINTN NorFlashSize,
- IN UINT32 Index,
- IN UINT32 BlockSize,
- IN BOOLEAN SupportFvb,
- OUT NOR_FLASH_INSTANCE **NorFlashInstance
- );
-
-EFI_STATUS
-EFIAPI
-NorFlashFvbInitialize (
- IN NOR_FLASH_INSTANCE *Instance
- );
-
-//
-// NorFlash.c
-//
-EFI_STATUS
-NorFlashWriteSingleBlock (
- IN NOR_FLASH_INSTANCE *Instance,
- IN EFI_LBA Lba,
- IN UINTN Offset,
- IN OUT UINTN *NumBytes,
- IN UINT8 *Buffer
- );
-
-EFI_STATUS
-NorFlashWriteBlocks (
- IN NOR_FLASH_INSTANCE *Instance,
- IN EFI_LBA Lba,
- IN UINTN BufferSizeInBytes,
- IN VOID *Buffer
- );
-
-EFI_STATUS
-NorFlashReadBlocks (
- IN NOR_FLASH_INSTANCE *Instance,
- IN EFI_LBA Lba,
- IN UINTN BufferSizeInBytes,
- OUT VOID *Buffer
- );
-
-EFI_STATUS
-NorFlashRead (
- IN NOR_FLASH_INSTANCE *Instance,
- IN EFI_LBA Lba,
- IN UINTN Offset,
- IN UINTN BufferSizeInBytes,
- OUT VOID *Buffer
- );
-
-EFI_STATUS
-NorFlashWrite (
- IN NOR_FLASH_INSTANCE *Instance,
- IN EFI_LBA Lba,
- IN UINTN Offset,
- IN OUT UINTN *NumBytes,
- IN UINT8 *Buffer
- );
-
-EFI_STATUS
-NorFlashReset (
- IN NOR_FLASH_INSTANCE *Instance
- );
-
-EFI_STATUS
-NorFlashEraseSingleBlock (
- IN NOR_FLASH_INSTANCE *Instance,
- IN UINTN BlockAddress
- );
-
-EFI_STATUS
-NorFlashUnlockSingleBlockIfNecessary (
- IN NOR_FLASH_INSTANCE *Instance,
- IN UINTN BlockAddress
- );
-
-EFI_STATUS
-NorFlashWriteSingleWord (
- IN NOR_FLASH_INSTANCE *Instance,
- IN UINTN WordAddress,
- IN UINT32 WriteData
- );
-
-VOID
-EFIAPI
-NorFlashVirtualNotifyEvent (
- IN EFI_EVENT Event,
- IN VOID *Context
- );
-
-#endif /* __VIRT_NOR_FLASH__ */
diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c
index 6b9ef261335e..1b46d2690108 100644
--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c
+++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c
@@ -1,4 +1,4 @@
-/** @file NorFlashDxe.c
+/** @file VirtNorFlashDxe.c
Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.<BR>
@@ -14,7 +14,7 @@
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
-#include "VirtNorFlash.h"
+#include "VirtNorFlashDxe.h"
STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent;
@@ -138,161 +138,41 @@ NorFlashCreateInstance (
}
/**
- * This function unlock and erase an entire NOR Flash block.
- **/
-EFI_STATUS
-NorFlashUnlockAndEraseSingleBlock (
- IN NOR_FLASH_INSTANCE *Instance,
- IN UINTN BlockAddress
- )
-{
- EFI_STATUS Status;
- UINTN Index;
- EFI_TPL OriginalTPL;
-
- if (!EfiAtRuntime ()) {
- // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
- OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
- } else {
- // This initialization is only to prevent the compiler to complain about the
- // use of uninitialized variables
- OriginalTPL = TPL_HIGH_LEVEL;
- }
+ 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.
- Index = 0;
- // The block erase might fail a first time (SW bug ?). Retry it ...
- do {
- // Unlock the block if we have to
- Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
- if (EFI_ERROR (Status)) {
- break;
- }
-
- Status = NorFlashEraseSingleBlock (Instance, BlockAddress);
- Index++;
- } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED));
-
- if (Index == NOR_FLASH_ERASE_RETRY) {
- DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress, Index));
- }
-
- if (!EfiAtRuntime ()) {
- // Interruptions can resume.
- gBS->RestoreTPL (OriginalTPL);
- }
-
- return Status;
-}
-
-EFI_STATUS
-NorFlashWriteFullBlock (
- IN NOR_FLASH_INSTANCE *Instance,
- IN EFI_LBA Lba,
- IN UINT32 *DataBuffer,
- IN UINT32 BlockSizeInWords
+ @param[in] Event The Event that is being processed
+ @param[in] Context Event Context
+**/
+VOID
+EFIAPI
+NorFlashVirtualNotifyEvent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
)
{
- EFI_STATUS Status;
- UINTN WordAddress;
- UINT32 WordIndex;
- UINTN BufferIndex;
- UINTN BlockAddress;
- UINTN BuffersInBlock;
- UINTN RemainingWords;
- EFI_TPL OriginalTPL;
- UINTN Cnt;
-
- Status = EFI_SUCCESS;
-
- // Get the physical address of the block
- BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSizeInWords * 4);
-
- // Start writing from the first address at the start of the block
- WordAddress = BlockAddress;
-
- if (!EfiAtRuntime ()) {
- // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
- OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
- } else {
- // This initialization is only to prevent the compiler to complain about the
- // use of uninitialized variables
- OriginalTPL = TPL_HIGH_LEVEL;
- }
-
- Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress);
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_ERROR, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress));
- goto EXIT;
- }
-
- // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
-
- // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero
- if ((WordAddress & BOUNDARY_OF_32_WORDS) == 0x00) {
- // First, break the entire block into buffer-sized chunks.
- BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES;
-
- // Then feed each buffer chunk to the NOR Flash
- // If a buffer does not contain any data, don't write it.
- for (BufferIndex = 0;
- BufferIndex < BuffersInBlock;
- BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS
- )
- {
- // Check the buffer to see if it contains any data (not set all 1s).
- for (Cnt = 0; Cnt < P30_MAX_BUFFER_SIZE_IN_WORDS; Cnt++) {
- if (~DataBuffer[Cnt] != 0 ) {
- // Some data found, write the buffer.
- Status = NorFlashWriteBuffer (
- Instance,
- WordAddress,
- P30_MAX_BUFFER_SIZE_IN_BYTES,
- DataBuffer
- );
- if (EFI_ERROR (Status)) {
- goto EXIT;
- }
-
- break;
- }
- }
- }
-
- // Finally, finish off any remaining words that are less than the maximum size of the buffer
- RemainingWords = BlockSizeInWords % P30_MAX_BUFFER_SIZE_IN_WORDS;
+ UINTN Index;
- if (RemainingWords != 0) {
- Status = NorFlashWriteBuffer (Instance, WordAddress, (RemainingWords * 4), DataBuffer);
- if (EFI_ERROR (Status)) {
- goto EXIT;
- }
- }
- } else {
- // For now, use the single word programming algorithm
- // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,
- // i.e. which ends in the range 0x......01 - 0x......7F.
- for (WordIndex = 0; WordIndex < BlockSizeInWords; WordIndex++, DataBuffer++, WordAddress = WordAddress + 4) {
- Status = NorFlashWriteSingleWord (Instance, WordAddress, *DataBuffer);
- if (EFI_ERROR (Status)) {
- goto EXIT;
- }
+ for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
+ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->DeviceBaseAddress);
+ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->RegionBaseAddress);
+
+ // Convert Fvb
+ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks);
+ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes);
+ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize);
+ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress);
+ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.Read);
+ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes);
+ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.Write);
+
+ if (mNorFlashInstances[Index]->ShadowBuffer != NULL) {
+ EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->ShadowBuffer);
}
}
-EXIT:
- // Put device back into Read Array mode
- SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
-
- if (!EfiAtRuntime ()) {
- // Interruptions can resume.
- gBS->RestoreTPL (OriginalTPL);
- }
-
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status));
- }
-
- return Status;
+ return;
}
EFI_STATUS
diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.h b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.h
new file mode 100644
index 000000000000..3c7f5094153f
--- /dev/null
+++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.h
@@ -0,0 +1,156 @@
+/** @file VirtNorFlashDxe.h
+
+ Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.<BR>
+ Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __VIRT_NOR_FLASH_DXE__
+#define __VIRT_NOR_FLASH_DXE__
+
+#include <Base.h>
+#include <PiDxe.h>
+
+#include <Guid/EventGroup.h>
+
+#include <Protocol/FirmwareVolumeBlock.h>
+
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/VirtNorFlashDeviceLib.h>
+#include <Library/VirtNorFlashPlatformLib.h>
+
+#define NOR_FLASH_SIGNATURE SIGNATURE_32('n', 'o', 'r', '0')
+#define INSTANCE_FROM_FVB_THIS(a) CR(a, NOR_FLASH_INSTANCE, FvbProtocol, NOR_FLASH_SIGNATURE)
+
+typedef struct _NOR_FLASH_INSTANCE NOR_FLASH_INSTANCE;
+
+#pragma pack (1)
+typedef struct {
+ VENDOR_DEVICE_PATH Vendor;
+ UINT8 Index;
+ EFI_DEVICE_PATH_PROTOCOL End;
+} NOR_FLASH_DEVICE_PATH;
+#pragma pack ()
+
+struct _NOR_FLASH_INSTANCE {
+ UINT32 Signature;
+ EFI_HANDLE Handle;
+
+ UINTN DeviceBaseAddress;
+ UINTN RegionBaseAddress;
+ UINTN Size;
+ EFI_LBA StartLba;
+ EFI_LBA LastBlock;
+ UINT32 BlockSize;
+
+ EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL FvbProtocol;
+ VOID *ShadowBuffer;
+
+ NOR_FLASH_DEVICE_PATH DevicePath;
+};
+
+//
+// NorFlashFvbDxe.c
+//
+
+EFI_STATUS
+EFIAPI
+FvbGetAttributes (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+ );
+
+EFI_STATUS
+EFIAPI
+FvbSetAttributes (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+ );
+
+EFI_STATUS
+EFIAPI
+FvbGetPhysicalAddress (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ OUT EFI_PHYSICAL_ADDRESS *Address
+ );
+
+EFI_STATUS
+EFIAPI
+FvbGetBlockSize (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ OUT UINTN *BlockSize,
+ OUT UINTN *NumberOfBlocks
+ );
+
+EFI_STATUS
+EFIAPI
+FvbRead (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN OUT UINTN *NumBytes,
+ IN OUT UINT8 *Buffer
+ );
+
+EFI_STATUS
+EFIAPI
+FvbWrite (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN OUT UINTN *NumBytes,
+ IN UINT8 *Buffer
+ );
+
+EFI_STATUS
+EFIAPI
+FvbEraseBlocks (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ ...
+ );
+
+EFI_STATUS
+ValidateFvHeader (
+ IN NOR_FLASH_INSTANCE *Instance
+ );
+
+EFI_STATUS
+InitializeFvAndVariableStoreHeaders (
+ IN NOR_FLASH_INSTANCE *Instance
+ );
+
+VOID
+EFIAPI
+FvbVirtualNotifyEvent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+//
+// NorFlashDxe.c
+//
+EFI_STATUS
+NorFlashCreateInstance (
+ IN UINTN NorFlashDeviceBase,
+ IN UINTN NorFlashRegionBase,
+ IN UINTN NorFlashSize,
+ IN UINT32 Index,
+ IN UINT32 BlockSize,
+ IN BOOLEAN SupportFvb,
+ OUT NOR_FLASH_INSTANCE **NorFlashInstance
+ );
+
+EFI_STATUS
+EFIAPI
+NorFlashFvbInitialize (
+ IN NOR_FLASH_INSTANCE *Instance
+ );
+
+#endif /* __VIRT_NOT_FLASH_DXE__ */
diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf
index f549400280a1..6d6d615a6219 100644
--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf
+++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf
@@ -17,8 +17,6 @@
ENTRY_POINT = NorFlashInitialise
[Sources.common]
- VirtNorFlash.c
- VirtNorFlash.h
VirtNorFlashDxe.c
VirtNorFlashFvb.c
@@ -39,6 +37,7 @@
UefiDriverEntryPoint
UefiLib
UefiRuntimeLib
+ VirtNorFlashDeviceLib
VirtNorFlashPlatformLib
[Guids]
diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c
index c8b5e0be1379..0d6c9ef01631 100644
--- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c
+++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c
@@ -19,7 +19,7 @@
#include <Guid/SystemNvDataGuid.h>
#include <Guid/VariableFormat.h>
-#include "VirtNorFlash.h"
+#include "VirtNorFlashDxe.h"
extern UINTN mFlashNvStorageVariableBase;
///
@@ -653,13 +653,30 @@ FvbRead (
// Decide if we are doing full block reads or not.
if (*NumBytes % BlockSize != 0) {
- TempStatus = NorFlashRead (Instance, Instance->StartLba + Lba, Offset, *NumBytes, Buffer);
+ TempStatus = NorFlashRead (
+ Instance->DeviceBaseAddress,
+ Instance->RegionBaseAddress,
+ Instance->StartLba + Lba,
+ Instance->BlockSize,
+ Instance->Size,
+ Offset,
+ *NumBytes,
+ Buffer
+ );
if (EFI_ERROR (TempStatus)) {
return EFI_DEVICE_ERROR;
}
} else {
// Read NOR Flash data into shadow buffer
- TempStatus = NorFlashReadBlocks (Instance, Instance->StartLba + Lba, BlockSize, Buffer);
+ TempStatus = NorFlashReadBlocks (
+ Instance->DeviceBaseAddress,
+ Instance->RegionBaseAddress,
+ Instance->StartLba + Lba,
+ Instance->LastBlock,
+ Instance->BlockSize,
+ BlockSize,
+ Buffer
+ );
if (EFI_ERROR (TempStatus)) {
// Return one of the pre-approved error statuses
return EFI_DEVICE_ERROR;
@@ -737,7 +754,18 @@ FvbWrite (
Instance = INSTANCE_FROM_FVB_THIS (This);
- return NorFlashWriteSingleBlock (Instance, Instance->StartLba + Lba, Offset, NumBytes, Buffer);
+ return NorFlashWriteSingleBlock (
+ Instance->DeviceBaseAddress,
+ Instance->RegionBaseAddress,
+ Instance->StartLba + Lba,
+ Instance->LastBlock,
+ Instance->BlockSize,
+ Instance->Size,
+ Offset,
+ NumBytes,
+ Buffer,
+ Instance->ShadowBuffer
+ );
}
/**
@@ -795,6 +823,7 @@ FvbEraseBlocks (
UINTN BlockAddress; // Physical address of Lba to erase
EFI_LBA StartingLba; // Lba from which we start erasing
UINTN NumOfLba; // Number of Lba blocks to erase
+ EFI_TPL OriginalTPL;
NOR_FLASH_INSTANCE *Instance;
Instance = INSTANCE_FROM_FVB_THIS (This);
@@ -865,7 +894,21 @@ FvbEraseBlocks (
// Erase it
DEBUG ((DEBUG_BLKIO, "FvbEraseBlocks: Erasing Lba=%ld @ 0x%08x.\n", Instance->StartLba + StartingLba, BlockAddress));
- Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress);
+ if (!EfiAtRuntime ()) {
+ // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
+ OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
+ } else {
+ // This initialization is only to prevent the compiler to complain about the
+ // use of uninitialized variables
+ OriginalTPL = TPL_HIGH_LEVEL;
+ }
+
+ Status = NorFlashUnlockAndEraseSingleBlock (Instance->DeviceBaseAddress, BlockAddress);
+ if (!EfiAtRuntime ()) {
+ // Interruptions can resume.
+ gBS->RestoreTPL (OriginalTPL);
+ }
+
if (EFI_ERROR (Status)) {
VA_END (Args);
Status = EFI_DEVICE_ERROR;
--
2.34.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#121276): https://edk2.groups.io/g/devel/message/121276
Mute This Topic: https://groups.io/mt/112379038/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [edk2-devel] [PATCH 3/3] OvmfPkg/RiscVVirt: Add support for Capsule Firmware Upgrade
2025-04-21 16:58 [edk2-devel] [PATCH 0/3] OvmfPkg/RiscVVirt: Add support for Capsule Firmware Upgrade Tuan Phan
2025-04-21 16:58 ` [edk2-devel] [PATCH 1/3] OvmfPkg/VirtNorFlash: Move low level NOR flash functions into library Tuan Phan
2025-04-21 16:58 ` [edk2-devel] [PATCH 2/3] ArmVirtPkg: Link all targets to the new VirtNorFlashDeviceLib Tuan Phan
@ 2025-04-21 16:58 ` Tuan Phan
2 siblings, 0 replies; 4+ messages in thread
From: Tuan Phan @ 2025-04-21 16:58 UTC (permalink / raw)
To: devel
Cc: andyw, maobibo, lichao, kraxel, jiewen.yao, leif.lindholm,
sami.mujawar, sunilvl, ardb+tianocore, lixianglai, Tuan Phan
This patch introduces support for firmware upgrades using the
FMP capsule update mechanism.
Signed-off-by: Tuan Phan <tphan@ventanamicro.com>
---
OvmfPkg/OvmfPkg.ci.yaml | 3 +-
.../Capsule/GenerateCapsule/GenCapsule.py | 332 ++++++++
.../CapsuleUpdatePolicyLib.c | 121 +++
.../CapsuleUpdatePolicyLib.inf | 29 +
.../CapsuleUpdatePolicyLib.uni | 12 +
.../Library/FmpDeviceLib/FmpDeviceLib.c | 774 ++++++++++++++++++
.../Library/FmpDeviceLib/FmpDeviceLib.inf | 46 ++
.../PlatformFlashAccessLib.c | 236 ++++++
.../PlatformFlashAccessLib.h | 95 +++
.../PlatformFlashAccessLib.inf | 34 +
OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc | 39 +
OvmfPkg/RiscVVirt/RiscVVirtQemu.fdf | 5 +
OvmfPkg/RiscVVirt/RiscVVirtSystemFW.dsc.inc | 61 ++
13 files changed, 1786 insertions(+), 1 deletion(-)
create mode 100644 OvmfPkg/RiscVVirt/Feature/Capsule/GenerateCapsule/GenCapsule.py
create mode 100644 OvmfPkg/RiscVVirt/Feature/Capsule/Library/CapsuleUpdatePolicyLib/CapsuleUpdatePolicyLib.c
create mode 100644 OvmfPkg/RiscVVirt/Feature/Capsule/Library/CapsuleUpdatePolicyLib/CapsuleUpdatePolicyLib.inf
create mode 100644 OvmfPkg/RiscVVirt/Feature/Capsule/Library/CapsuleUpdatePolicyLib/CapsuleUpdatePolicyLib.uni
create mode 100644 OvmfPkg/RiscVVirt/Feature/Capsule/Library/FmpDeviceLib/FmpDeviceLib.c
create mode 100644 OvmfPkg/RiscVVirt/Feature/Capsule/Library/FmpDeviceLib/FmpDeviceLib.inf
create mode 100644 OvmfPkg/RiscVVirt/Feature/Capsule/Library/PlatformFlashAccessLib/PlatformFlashAccessLib.c
create mode 100644 OvmfPkg/RiscVVirt/Feature/Capsule/Library/PlatformFlashAccessLib/PlatformFlashAccessLib.h
create mode 100644 OvmfPkg/RiscVVirt/Feature/Capsule/Library/PlatformFlashAccessLib/PlatformFlashAccessLib.inf
create mode 100644 OvmfPkg/RiscVVirt/RiscVVirtSystemFW.dsc.inc
diff --git a/OvmfPkg/OvmfPkg.ci.yaml b/OvmfPkg/OvmfPkg.ci.yaml
index e4b729b4a80e..056e681b3dd2 100644
--- a/OvmfPkg/OvmfPkg.ci.yaml
+++ b/OvmfPkg/OvmfPkg.ci.yaml
@@ -53,7 +53,8 @@
"UefiCpuPkg/UefiCpuPkg.dec",
"ShellPkg/ShellPkg.dec",
"EmbeddedPkg/EmbeddedPkg.dec",
- "SourceLevelDebugPkg/SourceLevelDebugPkg.dec"
+ "SourceLevelDebugPkg/SourceLevelDebugPkg.dec",
+ "FmpDevicePkg/FmpDevicePkg.dec"
],
# For host based unit tests
"AcceptableDependencies-HOST_APPLICATION":[
diff --git a/OvmfPkg/RiscVVirt/Feature/Capsule/GenerateCapsule/GenCapsule.py b/OvmfPkg/RiscVVirt/Feature/Capsule/GenerateCapsule/GenCapsule.py
new file mode 100644
index 000000000000..1889328fb9ba
--- /dev/null
+++ b/OvmfPkg/RiscVVirt/Feature/Capsule/GenerateCapsule/GenCapsule.py
@@ -0,0 +1,332 @@
+## @file
+# Generate capsules for RiscVVirt platform
+# openssl, gcab must be install and in path
+#
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+
+'''
+GenCapsuleAll
+'''
+
+import os
+import sys
+import argparse
+import subprocess
+import glob
+import shutil
+import struct
+import datetime
+
+#
+# Globals for help information
+#
+__prog__ = 'GenCapsuleAll'
+__copyright__ = 'Copyright (c) 2019, Intel Corporation. All rights reserved.'
+__description__ = 'Generate RiscVVirt capsules.\n'
+
+#
+# Globals
+#
+gWorkspace = ''
+gBaseToolsPath = ''
+gArgs = None
+
+def LogAlways(Message):
+ sys.stdout.write (__prog__ + ': ' + Message + '\n')
+ sys.stdout.flush()
+
+def Log(Message):
+ global gArgs
+ if not gArgs.Verbose:
+ return
+ sys.stdout.write (__prog__ + ': ' + Message + '\n')
+ sys.stdout.flush()
+
+def Error(Message, ExitValue=1):
+ sys.stderr.write (__prog__ + ': ERROR: ' + Message + '\n')
+ sys.exit (ExitValue)
+
+def RelativePath(target):
+ global gWorkspace
+ Log('RelativePath' + target)
+ return os.path.relpath (target, gWorkspace)
+
+def NormalizePath(target):
+ if isinstance(target, tuple):
+ return os.path.normpath (os.path.join (*target))
+ else:
+ return os.path.normpath (target)
+
+def RemoveFile(target):
+ target = NormalizePath(target)
+ if not target or target == os.pathsep:
+ Error ('RemoveFile() invalid target')
+ if os.path.exists(target):
+ os.remove (target)
+ Log ('remove %s' % (RelativePath (target)))
+
+def RemoveDirectory(target):
+ target = NormalizePath(target)
+ if not target or target == os.pathsep:
+ Error ('RemoveDirectory() invalid target')
+ if os.path.exists(target):
+ Log ('rmdir %s' % (RelativePath (target)))
+ shutil.rmtree(target)
+
+def CreateDirectory(target):
+ target = NormalizePath(target)
+ if not os.path.exists(target):
+ Log ('mkdir %s' % (RelativePath (target)))
+ os.makedirs (target)
+
+def Copy(src, dst):
+ src = NormalizePath(src)
+ dst = NormalizePath(dst)
+ for File in glob.glob(src):
+ Log ('copy %s -> %s' % (RelativePath (File), RelativePath (dst)))
+ shutil.copy (File, dst)
+
+GenerateCapsuleCommand = '''
+GenerateCapsule
+--encode
+--guid {FMP_CAPSULE_GUID}
+--fw-version {FMP_CAPSULE_VERSION}
+--lsv {FMP_CAPSULE_LSV}
+--signer-private-cert={BASE_TOOLS_PATH}/Source/Python/Pkcs7Sign/TestCert.pem
+--other-public-cert={BASE_TOOLS_PATH}/Source/Python/Pkcs7Sign/TestSub.pub.pem
+--trusted-public-cert={BASE_TOOLS_PATH}/Source/Python/Pkcs7Sign/TestRoot.pub.pem
+-o {FMP_CAPSULE_FILE}
+{FMP_CAPSULE_PAYLOAD}
+'''
+MetaInfoXmlTemplate = '''
+<?xml version="1.0" encoding="UTF-8"?>
+<component type="firmware">
+ <id>com.intel.FMP_CAPSULE_BASE_NAME.firmware</id>
+ <name>FMP_CAPSULE_BASE_NAME</name>
+ <summary>System firmware for the FMP_CAPSULE_BASE_NAME</summary>
+ <description>
+ Description of System firmware for the FMP_CAPSULE_BASE_NAME
+ </description>
+ <provides>
+ <firmware type="flashed">FMP_CAPSULE_GUID</firmware>
+ </provides>
+ <url type="homepage">http://www.tianocore.org</url>
+ <metadata_license>CC0-1.0</metadata_license>
+ <project_license>BSD</project_license>
+ <developer_name>Tianocore</developer_name>
+ <releases>
+ <release version="FMP_CAPSULE_VERSION_DECIMAL" date="FMP_CAPSULE_DATE">
+ <description>
+ Build FMP_CAPSULE_STRING
+ </description>
+ </release>
+ </releases>
+ <!-- most OEMs do not need to do this... -->
+ <custom>
+ <value key="LVFS::InhibitDownload"/>
+ </custom>
+</component>
+'''
+
+def GenCapsuleDevice (BaseName, PayloadFileName, Guid, Version, Lsv, CapsulesPath, CapsulesSubDir):
+ global gBaseToolsPath
+ LogAlways ('Generate Capsule: {0} {1:08x} {2:08x} {3}'.format (Guid, Version, Lsv, PayloadFileName))
+
+ VersionString = '.'.join([str(ord(x)) for x in struct.pack('>I', Version).decode()])
+
+ FmpCapsuleFile = NormalizePath ((CapsulesPath, CapsulesSubDir, BaseName + '.' + VersionString + '.cap'))
+ Command = GenerateCapsuleCommand.format (
+ FMP_CAPSULE_GUID = Guid,
+ FMP_CAPSULE_VERSION = Version,
+ FMP_CAPSULE_LSV = Lsv,
+ BASE_TOOLS_PATH = gBaseToolsPath,
+ FMP_CAPSULE_FILE = FmpCapsuleFile,
+ FMP_CAPSULE_PAYLOAD = PayloadFileName
+ )
+ Command = ' '.join(Command.splitlines()).strip()
+ if gArgs.Verbose:
+ Command = Command + ' -v'
+
+ Log (Command)
+
+ Process = subprocess.Popen(Command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+ ProcessOutput = Process.communicate()
+
+ if Process.returncode == 0:
+ Log (ProcessOutput[0].decode())
+ else:
+ LogAlways (Command)
+ LogAlways (ProcessOutput[0].decode())
+ Error ('GenerateCapsule returned an error')
+
+ Copy (PayloadFileName, (CapsulesPath, 'firmware.bin'))
+ MetaInfoXml = MetaInfoXmlTemplate
+ MetaInfoXml = MetaInfoXml.replace ('FMP_CAPSULE_GUID', Guid)
+ MetaInfoXml = MetaInfoXml.replace ('FMP_CAPSULE_BASE_NAME', BaseName)
+ MetaInfoXml = MetaInfoXml.replace ('FMP_CAPSULE_VERSION_DECIMAL', str(Version))
+ MetaInfoXml = MetaInfoXml.replace ('FMP_CAPSULE_STRING', VersionString)
+ MetaInfoXml = MetaInfoXml.replace ('FMP_CAPSULE_DATE', str(datetime.date.today()))
+ f = open (NormalizePath ((CapsulesPath, 'firmware.metainfo.xml')), 'w')
+ f.write(MetaInfoXml)
+ f.close()
+
+ Command = 'gcab --create firmware.cab firmware.bin firmware.metainfo.xml'
+ Log (Command)
+
+ Process = subprocess.Popen(Command, cwd=CapsulesPath, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+ ProcessOutput = Process.communicate()
+
+ if Process.returncode == 0:
+ Log (ProcessOutput[0].decode())
+ else:
+ LogAlways (Command)
+ LogAlways (ProcessOutput[0].decode())
+ Error ('GenerateCapsule returned an error')
+
+ FmpCabinetFile = NormalizePath ((CapsulesPath, CapsulesSubDir, BaseName + '.' + VersionString + '.cab'))
+
+ Copy ((CapsulesPath, 'firmware.cab'), FmpCabinetFile)
+
+ RemoveFile ((CapsulesPath, 'firmware.cab'))
+ RemoveFile ((CapsulesPath, 'firmware.metainfo.xml'))
+ RemoveFile ((CapsulesPath, 'firmware.bin'))
+
+if __name__ == '__main__':
+ #
+ # Create command line argument parser object
+ #
+ parser = argparse.ArgumentParser (
+ prog = __prog__,
+ description = __description__ + __copyright__,
+ conflict_handler = 'resolve'
+ )
+ parser.add_argument (
+ '-a', '--arch', dest = 'Arch', nargs = '+', action = 'append',
+ required = True,
+ help = '''ARCHS is one of list: IA32, X64, IPF, ARM, AARCH64, RISCV64
+ or EBC, which overrides target.txt's TARGET_ARCH definition.
+ To specify more archs, please repeat this option.'''
+ )
+ parser.add_argument (
+ '-t', '--tagname', dest = 'ToolChain', required = True,
+ help = '''Using the Tool Chain Tagname to build the platform,
+ overriding target.txt's TOOL_CHAIN_TAG definition.'''
+ )
+ parser.add_argument (
+ '-p', '--platform', dest = 'PlatformFile', required = True,
+ help = '''Build the platform specified by the DSC file name argument,
+ overriding target.txt's ACTIVE_PLATFORM definition.'''
+ )
+ parser.add_argument (
+ '-b', '--buildtarget', dest = 'BuildTarget', required = True,
+ help = '''Using the TARGET to build the platform, overriding
+ target.txt's TARGET definition.'''
+ )
+ parser.add_argument (
+ '--conf=', dest = 'ConfDirectory', required = True,
+ help = '''Specify the customized Conf directory.'''
+ )
+ parser.add_argument (
+ '-D', '--define', dest = 'Define', nargs='*', action = 'append',
+ help = '''Macro: "Name [= Value]".'''
+ )
+ parser.add_argument (
+ '-v', '--verbose', dest = 'Verbose', action = 'store_true',
+ help = '''Turn on verbose output with informational messages printed'''
+ )
+ parser.add_argument (
+ '--package', dest = 'Package', nargs = '*', action = 'append',
+ help = '''The directory name of a package of tests to copy'''
+ )
+
+ #
+ # Parse command line arguments
+ #
+ gArgs, remaining = parser.parse_known_args()
+ gArgs.BuildType = 'all'
+ for BuildType in ['all', 'fds', 'genc', 'genmake', 'clean', 'cleanall', 'modules', 'libraries', 'run']:
+ if BuildType in remaining:
+ gArgs.BuildType = BuildType
+ remaining.remove(BuildType)
+ break
+ gArgs.Remaining = ' '.join(remaining)
+
+ #
+ # Get WORKSPACE environment variable
+ #
+ try:
+ gWorkspace = os.environ['WORKSPACE']
+ except:
+ Error ('WORKSPACE environment variable not set')
+
+ #
+ # Get PACKAGES_PATH and generate prioritized list of paths
+ #
+ PathList = [gWorkspace]
+ try:
+ PathList += os.environ['PACKAGES_PATH'].split(os.pathsep)
+ except:
+ pass
+
+ #
+ # Determine full path to BaseTools
+ #
+ for Path in PathList:
+ if os.path.exists (os.path.join (Path, 'BaseTools')):
+ gBaseToolsPath = os.path.join (Path, 'BaseTools')
+ break
+
+ #
+ # Parse PLATFORM_NAME from DSC file
+ #
+ for Path in PathList:
+ if os.path.exists (os.path.join (Path, gArgs.PlatformFile)):
+ Dsc = open (os.path.join (Path, gArgs.PlatformFile), 'r').readlines()
+ break
+ for Line in Dsc:
+ if Line.strip().startswith('PLATFORM_NAME'):
+ OutputDirectory = os.path.join ('Build', Line.strip().split('=')[1].strip())
+ break
+
+ #
+ # Determine full paths to EDK II build directory, EDK II build output
+ # directory and the CPU arch of the UEFI phase.
+ #
+ CommandDir = os.path.dirname(sys.argv[0])
+ EdkiiBuildDir = os.path.join (gWorkspace, OutputDirectory)
+ EdkiiBuildOutput = os.path.join (EdkiiBuildDir, gArgs.BuildTarget + '_' + gArgs.ToolChain)
+ UefiArch = gArgs.Arch[0][0]
+ if len (gArgs.Arch) > 1:
+ if ['RISCV64'] in gArgs.Arch:
+ UefiArch = 'RISCV64'
+
+ CapsulesPath = NormalizePath((EdkiiBuildDir, 'Capsules'))
+
+ CapsulesSubDir = 'TestCert' + '_' + UefiArch + '_' + gArgs.BuildTarget + '_' + gArgs.ToolChain
+
+ #
+ # Create output directories
+ #
+ try:
+ CreateDirectory ((CapsulesPath))
+ except:
+ pass
+ try:
+ CreateDirectory ((CapsulesPath, CapsulesSubDir))
+ except:
+ pass
+
+ #
+ # Copy CapsuleApp
+ #
+ Copy ((EdkiiBuildOutput, UefiArch, 'CapsuleApp.efi'), (CapsulesPath, CapsulesSubDir))
+
+ #
+ # Generate capsules for RiscVVirt Firmware Updates
+ #
+ CodeFW = os.path.join (EdkiiBuildOutput, 'FV', 'RISCV_VIRT_CODE.fd')
+ GenCapsuleDevice('RISCVVIRT', CodeFW,'bcbacac2-1d1d-4c14-89a3-5e27496b702d',0x00010000,0x00000000, CapsulesPath, CapsulesSubDir)
diff --git a/OvmfPkg/RiscVVirt/Feature/Capsule/Library/CapsuleUpdatePolicyLib/CapsuleUpdatePolicyLib.c b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/CapsuleUpdatePolicyLib/CapsuleUpdatePolicyLib.c
new file mode 100644
index 000000000000..3d1adfac1849
--- /dev/null
+++ b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/CapsuleUpdatePolicyLib/CapsuleUpdatePolicyLib.c
@@ -0,0 +1,121 @@
+/** @file
+ Provides platform policy services used during a capsule update.
+
+ Copyright (c) 2016, Microsoft Corporation. All rights reserved.<BR>
+ Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Library/CapsuleUpdatePolicyLib.h>
+
+/**
+ Determine if the system power state supports a capsule update.
+
+ @param[out] Good Returns TRUE if system power state supports a capsule
+ update. Returns FALSE if system power state does not
+ support a capsule update. Return value is only valid if
+ return status is EFI_SUCCESS.
+
+ @retval EFI_SUCCESS Good parameter has been updated with result.
+ @retval EFI_INVALID_PARAMETER Good is NULL.
+ @retval EFI_DEVICE_ERROR System power state can not be determined.
+
+**/
+EFI_STATUS
+EFIAPI
+CheckSystemPower (
+ OUT BOOLEAN *Good
+ )
+{
+ *Good = TRUE;
+ return EFI_SUCCESS;
+}
+
+/**
+ Determines if the system thermal state supports a capsule update.
+
+ @param[out] Good Returns TRUE if system thermal state supports a capsule
+ update. Returns FALSE if system thermal state does not
+ support a capsule update. Return value is only valid if
+ return status is EFI_SUCCESS.
+
+ @retval EFI_SUCCESS Good parameter has been updated with result.
+ @retval EFI_INVALID_PARAMETER Good is NULL.
+ @retval EFI_DEVICE_ERROR System thermal state can not be determined.
+
+**/
+EFI_STATUS
+EFIAPI
+CheckSystemThermal (
+ OUT BOOLEAN *Good
+ )
+{
+ *Good = TRUE;
+ return EFI_SUCCESS;
+}
+
+/**
+ Determines if the system environment state supports a capsule update.
+
+ @param[out] Good Returns TRUE if system environment state supports a capsule
+ update. Returns FALSE if system environment state does not
+ support a capsule update. Return value is only valid if
+ return status is EFI_SUCCESS.
+
+ @retval EFI_SUCCESS Good parameter has been updated with result.
+ @retval EFI_INVALID_PARAMETER Good is NULL.
+ @retval EFI_DEVICE_ERROR System environment state can not be determined.
+
+**/
+EFI_STATUS
+EFIAPI
+CheckSystemEnvironment (
+ OUT BOOLEAN *Good
+ )
+{
+ *Good = TRUE;
+ return EFI_SUCCESS;
+}
+
+/**
+ Determines if the Lowest Supported Version checks should be performed. The
+ expected result from this function is TRUE. A platform can choose to return
+ FALSE (e.g. during manufacturing or servicing) to allow a capsule update to a
+ version below the current Lowest Supported Version.
+
+ @retval TRUE The lowest supported version check is required.
+ @retval FALSE Do not perform lowest support version check.
+
+**/
+BOOLEAN
+EFIAPI
+IsLowestSupportedVersionCheckRequired (
+ VOID
+ )
+{
+ return TRUE;
+}
+
+/**
+ Determines if the FMP device should be locked when the event specified by
+ PcdFmpDeviceLockEventGuid is signaled. The expected result from this function
+ is TRUE so the FMP device is always locked. A platform can choose to return
+ FALSE (e.g. during manufacturing) to allow FMP devices to remain unlocked.
+
+ @retval TRUE The FMP device lock action is required at lock event guid.
+ @retval FALSE Do not perform FMP device lock at lock event guid.
+
+**/
+BOOLEAN
+EFIAPI
+IsLockFmpDeviceAtLockEventGuidRequired (
+ VOID
+ )
+{
+ // Not lock FMP device for this platform
+ return FALSE;
+}
diff --git a/OvmfPkg/RiscVVirt/Feature/Capsule/Library/CapsuleUpdatePolicyLib/CapsuleUpdatePolicyLib.inf b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/CapsuleUpdatePolicyLib/CapsuleUpdatePolicyLib.inf
new file mode 100644
index 000000000000..fcefadd0551d
--- /dev/null
+++ b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/CapsuleUpdatePolicyLib/CapsuleUpdatePolicyLib.inf
@@ -0,0 +1,29 @@
+## @file
+# Provides platform policy services used during a capsule update.
+#
+# Copyright (c) 2016, Microsoft Corporation. All rights reserved.<BR>
+# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.30
+ BASE_NAME = CapsuleUpdatePolicyLib
+ MODULE_UNI_FILE = CapsuleUpdatePolicyLib.uni
+ FILE_GUID = 3ed188f7-7aba-47ff-bdeb-2ac23287a5ea
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = CapsuleUpdatePolicyLib
+
+#
+# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 RISCV64
+#
+
+[Sources]
+ CapsuleUpdatePolicyLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ FmpDevicePkg/FmpDevicePkg.dec
diff --git a/OvmfPkg/RiscVVirt/Feature/Capsule/Library/CapsuleUpdatePolicyLib/CapsuleUpdatePolicyLib.uni b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/CapsuleUpdatePolicyLib/CapsuleUpdatePolicyLib.uni
new file mode 100644
index 000000000000..d26e716b11ad
--- /dev/null
+++ b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/CapsuleUpdatePolicyLib/CapsuleUpdatePolicyLib.uni
@@ -0,0 +1,12 @@
+// /** @file
+// Provides platform policy services used during a capsule update.
+//
+// Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "Provides platform policy services used during a capsule update."
+
+#string STR_MODULE_DESCRIPTION #language en-US "Provides platform policy services used during a capsule update."
diff --git a/OvmfPkg/RiscVVirt/Feature/Capsule/Library/FmpDeviceLib/FmpDeviceLib.c b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/FmpDeviceLib/FmpDeviceLib.c
new file mode 100644
index 000000000000..4fe21b763d5b
--- /dev/null
+++ b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/FmpDeviceLib/FmpDeviceLib.c
@@ -0,0 +1,774 @@
+/** @file
+ Provides firmware device specific services to support updates of a firmware
+ image stored in a firmware device.
+
+ Copyright (c) Microsoft Corporation.<BR>
+ Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Guid/SystemResourceTable.h>
+#include <Library/DebugLib.h>
+#include <Library/FmpDeviceLib.h>
+#include <Library/PcdLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Include/LastAttemptStatus.h>
+#include "../PlatformFlashAccessLib/PlatformFlashAccessLib.h"
+
+#define __BUILD_STRING(x) L ## #x
+#define BUILD_STRING(x) L"build #" __BUILD_STRING(x)
+#define CURRENT_FIRMWARE_VERSION FixedPcdGet32 (PcdFirmwareRevision)
+#define CURRENT_FIRMWARE_VERSION_STRING BUILD_STRING (CURRENT_FIRMWARE_VERSION)
+
+typedef struct {
+ PLATFORM_FIRMWARE_TYPE FirmwareType;
+ FLASH_ADDRESS_TYPE AddressType;
+ EFI_PHYSICAL_ADDRESS BaseAddress;
+ UINTN Length;
+ UINTN ImageOffset;
+} UPDATE_CONFIG_DATA;
+
+UPDATE_CONFIG_DATA mUpdateConfigData[] = {
+ { PlatformFirmwareTypeSystemFirmware, FlashAddressTypeAbsoluteAddress, 0x20000000, 0x00800000, 0x00000000 },
+};
+
+/**
+ Provide a function to install the Firmware Management Protocol instance onto a
+ device handle when the device is managed by a driver that follows the UEFI
+ Driver Model. If the device is not managed by a driver that follows the UEFI
+ Driver Model, then EFI_UNSUPPORTED is returned.
+
+ @param[in] FmpInstaller Function that installs the Firmware Management
+ Protocol.
+
+ @retval EFI_SUCCESS The device is managed by a driver that follows the
+ UEFI Driver Model. FmpInstaller must be called on
+ each Driver Binding Start().
+ @retval EFI_UNSUPPORTED The device is not managed by a driver that follows
+ the UEFI Driver Model.
+ @retval other The Firmware Management Protocol for this firmware
+ device is not installed. The firmware device is
+ still locked using FmpDeviceLock().
+
+**/
+EFI_STATUS
+EFIAPI
+RegisterFmpInstaller (
+ IN FMP_DEVICE_LIB_REGISTER_FMP_INSTALLER Function
+ )
+{
+ //
+ // This is a system firmware update that does not use Driver Binding Protocol
+ //
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Provide a function to uninstall the Firmware Management Protocol instance from a
+ device handle when the device is managed by a driver that follows the UEFI
+ Driver Model. If the device is not managed by a driver that follows the UEFI
+ Driver Model, then EFI_UNSUPPORTED is returned.
+
+ @param[in] FmpUninstaller Function that installs the Firmware Management
+ Protocol.
+
+ @retval EFI_SUCCESS The device is managed by a driver that follows the
+ UEFI Driver Model. FmpUninstaller must be called on
+ each Driver Binding Stop().
+ @retval EFI_UNSUPPORTED The device is not managed by a driver that follows
+ the UEFI Driver Model.
+ @retval other The Firmware Management Protocol for this firmware
+ device is not installed. The firmware device is
+ still locked using FmpDeviceLock().
+
+**/
+EFI_STATUS
+EFIAPI
+RegisterFmpUninstaller (
+ IN FMP_DEVICE_LIB_REGISTER_FMP_UNINSTALLER Function
+ )
+{
+ //
+ // This is a system firmware update that does not use Driver Binding Protocol
+ //
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Set the device context for the FmpDeviceLib services when the device is
+ managed by a driver that follows the UEFI Driver Model. If the device is not
+ managed by a driver that follows the UEFI Driver Model, then EFI_UNSUPPORTED
+ is returned. Once a device context is set, the FmpDeviceLib services
+ operate on the currently set device context.
+
+ @param[in] Handle Device handle for the FmpDeviceLib services.
+ If Handle is NULL, then Context is freed.
+ @param[in, out] Context Device context for the FmpDeviceLib services.
+ If Context is NULL, then a new context is allocated
+ for Handle and the current device context is set and
+ returned in Context. If Context is not NULL, then
+ the current device context is set.
+
+ @retval EFI_SUCCESS The device is managed by a driver that follows the
+ UEFI Driver Model.
+ @retval EFI_UNSUPPORTED The device is not managed by a driver that follows
+ the UEFI Driver Model.
+ @retval other The Firmware Management Protocol for this firmware
+ device is not installed. The firmware device is
+ still locked using FmpDeviceLock().
+
+**/
+EFI_STATUS
+EFIAPI
+FmpDeviceSetContext (
+ IN EFI_HANDLE Handle,
+ IN OUT VOID **Context
+ )
+{
+ //
+ // This is a system firmware update that does not use Driver Binding Protocol
+ //
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Returns the size, in bytes, of the firmware image currently stored in the
+ firmware device. This function is used to by the GetImage() and
+ GetImageInfo() services of the Firmware Management Protocol. If the image
+ size can not be determined from the firmware device, then 0 must be returned.
+
+ @param[out] Size Pointer to the size, in bytes, of the firmware image
+ currently stored in the firmware device.
+
+ @retval EFI_SUCCESS The size of the firmware image currently
+ stored in the firmware device was returned.
+ @retval EFI_INVALID_PARAMETER Size is NULL.
+ @retval EFI_UNSUPPORTED The firmware device does not support reporting
+ the size of the currently stored firmware image.
+ @retval EFI_DEVICE_ERROR An error occurred attempting to determine the
+ size of the firmware image currently stored in
+ in the firmware device.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpDeviceGetSize (
+ OUT UINTN *Size
+ )
+{
+ if (Size == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Returns the GUID value used to fill in the ImageTypeId field of the
+ EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo()
+ service of the Firmware Management Protocol. If EFI_UNSUPPORTED is returned,
+ then the ImageTypeId field is set to gEfiCallerIdGuid. If EFI_SUCCESS is
+ returned, then ImageTypeId is set to the Guid returned from this function.
+
+ @param[out] Guid Double pointer to a GUID value that is updated to point to
+ to a GUID value. The GUID value is not allocated and must
+ not be modified or freed by the caller.
+
+ @retval EFI_SUCCESS EFI_FIRMWARE_IMAGE_DESCRIPTOR ImageTypeId GUID is set
+ to the returned Guid value.
+ @retval EFI_UNSUPPORTED EFI_FIRMWARE_IMAGE_DESCRIPTOR ImageTypeId GUID is set
+ to gEfiCallerIdGuid.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpDeviceGetImageTypeIdGuidPtr (
+ OUT EFI_GUID **Guid
+ )
+{
+ *Guid = &gEfiCallerIdGuid;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Returns values used to fill in the AttributesSupported and AttributesSettings
+ fields of the EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the
+ GetImageInfo() service of the Firmware Management Protocol. The following
+ bit values from the Firmware Management Protocol may be combined:
+ IMAGE_ATTRIBUTE_IMAGE_UPDATABLE
+ IMAGE_ATTRIBUTE_RESET_REQUIRED
+ IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED
+ IMAGE_ATTRIBUTE_IN_USE
+ IMAGE_ATTRIBUTE_UEFI_IMAGE
+
+ @param[out] Supported Attributes supported by this firmware device.
+ @param[out] Setting Attributes settings for this firmware device.
+
+ @retval EFI_SUCCESS The attributes supported by the firmware
+ device were returned.
+ @retval EFI_INVALID_PARAMETER Supported is NULL.
+ @retval EFI_INVALID_PARAMETER Setting is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpDeviceGetAttributes (
+ OUT UINT64 *Supported,
+ OUT UINT64 *Setting
+ )
+{
+ if ((Supported == NULL) || (Setting == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Supported = (IMAGE_ATTRIBUTE_IMAGE_UPDATABLE |
+ IMAGE_ATTRIBUTE_RESET_REQUIRED |
+ IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED |
+ IMAGE_ATTRIBUTE_IN_USE
+ );
+ *Setting = (IMAGE_ATTRIBUTE_IMAGE_UPDATABLE |
+ IMAGE_ATTRIBUTE_RESET_REQUIRED |
+ IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED |
+ IMAGE_ATTRIBUTE_IN_USE
+ );
+ return EFI_SUCCESS;
+}
+
+/**
+ Returns the value used to fill in the LowestSupportedVersion field of the
+ EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo()
+ service of the Firmware Management Protocol. If EFI_SUCCESS is returned, then
+ the firmware device supports a method to report the LowestSupportedVersion
+ value from the currently stored firmware image. If the value can not be
+ reported for the firmware image currently stored in the firmware device, then
+ EFI_UNSUPPORTED must be returned. EFI_DEVICE_ERROR is returned if an error
+ occurs attempting to retrieve the LowestSupportedVersion value for the
+ currently stored firmware image.
+
+ @note It is recommended that all firmware devices support a method to report
+ the LowestSupportedVersion value from the currently stored firmware
+ image.
+
+ @param[out] LowestSupportedVersion LowestSupportedVersion value retrieved
+ from the currently stored firmware image.
+
+ @retval EFI_SUCCESS The lowest supported version of currently stored
+ firmware image was returned in LowestSupportedVersion.
+ @retval EFI_UNSUPPORTED The firmware device does not support a method to
+ report the lowest supported version of the currently
+ stored firmware image.
+ @retval EFI_DEVICE_ERROR An error occurred attempting to retrieve the lowest
+ supported version of the currently stored firmware
+ image.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpDeviceGetLowestSupportedVersion (
+ OUT UINT32 *LowestSupportedVersion
+ )
+{
+ *LowestSupportedVersion = PcdGet32 (PcdFmpDeviceBuildTimeLowestSupportedVersion);
+ return EFI_SUCCESS;
+}
+
+/**
+ Returns the Null-terminated Unicode string that is used to fill in the
+ VersionName field of the EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is
+ returned by the GetImageInfo() service of the Firmware Management Protocol.
+ The returned string must be allocated using EFI_BOOT_SERVICES.AllocatePool().
+
+ @note It is recommended that all firmware devices support a method to report
+ the VersionName string from the currently stored firmware image.
+
+ @param[out] VersionString The version string retrieved from the currently
+ stored firmware image.
+
+ @retval EFI_SUCCESS The version string of currently stored
+ firmware image was returned in Version.
+ @retval EFI_INVALID_PARAMETER VersionString is NULL.
+ @retval EFI_UNSUPPORTED The firmware device does not support a method
+ to report the version string of the currently
+ stored firmware image.
+ @retval EFI_DEVICE_ERROR An error occurred attempting to retrieve the
+ version string of the currently stored
+ firmware image.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate the
+ buffer for the version string of the currently
+ stored firmware image.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpDeviceGetVersionString (
+ OUT CHAR16 **VersionString
+ )
+{
+ if (VersionString == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *VersionString = (CHAR16 *)AllocateCopyPool (
+ sizeof (CURRENT_FIRMWARE_VERSION_STRING),
+ CURRENT_FIRMWARE_VERSION_STRING
+ );
+ if (*VersionString == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Returns the value used to fill in the Version field of the
+ EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo()
+ service of the Firmware Management Protocol. If EFI_SUCCESS is returned, then
+ the firmware device supports a method to report the Version value from the
+ currently stored firmware image. If the value can not be reported for the
+ firmware image currently stored in the firmware device, then EFI_UNSUPPORTED
+ must be returned. EFI_DEVICE_ERROR is returned if an error occurs attempting
+ to retrieve the LowestSupportedVersion value for the currently stored firmware
+ image.
+
+ @note It is recommended that all firmware devices support a method to report
+ the Version value from the currently stored firmware image.
+
+ @param[out] Version The version value retrieved from the currently stored
+ firmware image.
+
+ @retval EFI_SUCCESS The version of currently stored firmware image was
+ returned in Version.
+ @retval EFI_UNSUPPORTED The firmware device does not support a method to
+ report the version of the currently stored firmware
+ image.
+ @retval EFI_DEVICE_ERROR An error occurred attempting to retrieve the version
+ of the currently stored firmware image.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpDeviceGetVersion (
+ OUT UINT32 *Version
+ )
+{
+ *Version = PcdGet32 (PcdFirmwareRevision);
+ return EFI_SUCCESS;
+}
+
+/**
+ Returns the value used to fill in the HardwareInstance field of the
+ EFI_FIRMWARE_IMAGE_DESCRIPTOR structure that is returned by the GetImageInfo()
+ service of the Firmware Management Protocol. If EFI_SUCCESS is returned, then
+ the firmware device supports a method to report the HardwareInstance value.
+ If the value can not be reported for the firmware device, then EFI_UNSUPPORTED
+ must be returned. EFI_DEVICE_ERROR is returned if an error occurs attempting
+ to retrieve the HardwareInstance value for the firmware device.
+
+ @param[out] HardwareInstance The hardware instance value for the firmware
+ device.
+
+ @retval EFI_SUCCESS The hardware instance for the current firmware
+ device is returned in HardwareInstance.
+ @retval EFI_UNSUPPORTED The firmware device does not support a method to
+ report the hardware instance value.
+ @retval EFI_DEVICE_ERROR An error occurred attempting to retrieve the hardware
+ instance value.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpDeviceGetHardwareInstance (
+ OUT UINT64 *HardwareInstance
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Returns a copy of the firmware image currently stored in the firmware device.
+
+ @note It is recommended that all firmware devices support a method to retrieve
+ a copy currently stored firmware image. This can be used to support
+ features such as recovery and rollback.
+
+ @param[out] Image Pointer to a caller allocated buffer where the
+ currently stored firmware image is copied to.
+ @param[in, out] ImageSize Pointer the size, in bytes, of the Image buffer.
+ On return, points to the size, in bytes, of firmware
+ image currently stored in the firmware device.
+
+ @retval EFI_SUCCESS Image contains a copy of the firmware image
+ currently stored in the firmware device, and
+ ImageSize contains the size, in bytes, of the
+ firmware image currently stored in the
+ firmware device.
+ @retval EFI_BUFFER_TOO_SMALL The buffer specified by ImageSize is too small
+ to hold the firmware image currently stored in
+ the firmware device. The buffer size required
+ is returned in ImageSize.
+ @retval EFI_INVALID_PARAMETER The Image is NULL.
+ @retval EFI_INVALID_PARAMETER The ImageSize is NULL.
+ @retval EFI_UNSUPPORTED The operation is not supported.
+ @retval EFI_DEVICE_ERROR An error occurred attempting to retrieve the
+ firmware image currently stored in the firmware
+ device.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpDeviceGetImage (
+ OUT VOID *Image,
+ IN OUT UINTN *ImageSize
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Checks if a new firmware image is valid for the firmware device. This
+ function allows firmware update operation to validate the firmware image
+ before FmpDeviceSetImage() is called.
+
+ @param[in] Image Points to a new firmware image.
+ @param[in] ImageSize Size, in bytes, of a new firmware image.
+ @param[out] ImageUpdatable Indicates if a new firmware image is valid for
+ a firmware update to the firmware device. The
+ following values from the Firmware Management
+ Protocol are supported:
+ IMAGE_UPDATABLE_VALID
+ IMAGE_UPDATABLE_INVALID
+ IMAGE_UPDATABLE_INVALID_TYPE
+ IMAGE_UPDATABLE_INVALID_OLD
+ IMAGE_UPDATABLE_VALID_WITH_VENDOR_CODE
+
+ @retval EFI_SUCCESS The image was successfully checked. Additional
+ status information is returned in
+ ImageUpdatable.
+ @retval EFI_INVALID_PARAMETER Image is NULL.
+ @retval EFI_INVALID_PARAMETER ImageUpdatable is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpDeviceCheckImage (
+ IN CONST VOID *Image,
+ IN UINTN ImageSize,
+ OUT UINT32 *ImageUpdatable
+ )
+{
+ UINT32 LastAttemptStatus;
+
+ return FmpDeviceCheckImageWithStatus (Image, ImageSize, ImageUpdatable, &LastAttemptStatus);
+}
+
+/**
+ Checks if a new firmware image is valid for the firmware device. This
+ function allows firmware update operation to validate the firmware image
+ before FmpDeviceSetImage() is called.
+
+ @param[in] Image Points to a new firmware image.
+ @param[in] ImageSize Size, in bytes, of a new firmware image.
+ @param[out] ImageUpdatable Indicates if a new firmware image is valid for
+ a firmware update to the firmware device. The
+ following values from the Firmware Management
+ Protocol are supported:
+ IMAGE_UPDATABLE_VALID
+ IMAGE_UPDATABLE_INVALID
+ IMAGE_UPDATABLE_INVALID_TYPE
+ IMAGE_UPDATABLE_INVALID_OLD
+ IMAGE_UPDATABLE_VALID_WITH_VENDOR_CODE
+ @param[out] LastAttemptStatus A pointer to a UINT32 that holds the last attempt
+ status to report back to the ESRT table in case
+ of error.
+
+ The return status code must fall in the range of
+ LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MIN_ERROR_CODE_VALUE to
+ LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MAX_ERROR_CODE_VALUE.
+
+ If the value falls outside this range, it will be converted
+ to LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL.
+
+ @retval EFI_SUCCESS The image was successfully checked. Additional
+ status information is returned in
+ ImageUpdatable.
+ @retval EFI_INVALID_PARAMETER Image is NULL.
+ @retval EFI_INVALID_PARAMETER ImageUpdatable is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpDeviceCheckImageWithStatus (
+ IN CONST VOID *Image,
+ IN UINTN ImageSize,
+ OUT UINT32 *ImageUpdatable,
+ OUT UINT32 *LastAttemptStatus
+ )
+{
+ if (LastAttemptStatus == NULL) {
+ DEBUG ((DEBUG_ERROR, "CheckImageWithStatus - LastAttemptStatus Pointer Parameter is NULL.\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;
+
+ if (ImageUpdatable == NULL) {
+ DEBUG ((DEBUG_ERROR, "CheckImageWithStatus - ImageUpdatable Pointer Parameter is NULL.\n"));
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MIN_ERROR_CODE_VALUE;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Set to valid and then if any tests fail it will update this flag.
+ //
+ *ImageUpdatable = IMAGE_UPDATABLE_VALID;
+
+ if (Image == NULL) {
+ DEBUG ((DEBUG_ERROR, "CheckImageWithStatus - Image Pointer Parameter is NULL.\n"));
+ //
+ // Not sure if this is needed
+ //
+ *ImageUpdatable = IMAGE_UPDATABLE_INVALID;
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MIN_ERROR_CODE_VALUE;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Updates a firmware device with a new firmware image. This function returns
+ EFI_UNSUPPORTED if the firmware image is not updatable. If the firmware image
+ is updatable, the function should perform the following minimal validations
+ before proceeding to do the firmware image update.
+ - Validate that the image is a supported image for this firmware device.
+ Return EFI_ABORTED if the image is not supported. Additional details
+ on why the image is not a supported image may be returned in AbortReason.
+ - Validate the data from VendorCode if is not NULL. Firmware image
+ validation must be performed before VendorCode data validation.
+ VendorCode data is ignored or considered invalid if image validation
+ fails. Return EFI_ABORTED if the VendorCode data is invalid.
+
+ VendorCode enables vendor to implement vendor-specific firmware image update
+ policy. Null if the caller did not specify the policy or use the default
+ policy. As an example, vendor can implement a policy to allow an option to
+ force a firmware image update when the abort reason is due to the new firmware
+ image version is older than the current firmware image version or bad image
+ checksum. Sensitive operations such as those wiping the entire firmware image
+ and render the device to be non-functional should be encoded in the image
+ itself rather than passed with the VendorCode. AbortReason enables vendor to
+ have the option to provide a more detailed description of the abort reason to
+ the caller.
+
+ @param[in] Image Points to the new firmware image.
+ @param[in] ImageSize Size, in bytes, of the new firmware image.
+ @param[in] VendorCode This enables vendor to implement vendor-specific
+ firmware image update policy. NULL indicates
+ the caller did not specify the policy or use the
+ default policy.
+ @param[in] Progress A function used to report the progress of
+ updating the firmware device with the new
+ firmware image.
+ @param[in] CapsuleFwVersion The version of the new firmware image from the
+ update capsule that provided the new firmware
+ image.
+ @param[out] AbortReason A pointer to a pointer to a Null-terminated
+ Unicode string providing more details on an
+ aborted operation. The buffer is allocated by
+ this function with
+ EFI_BOOT_SERVICES.AllocatePool(). It is the
+ caller's responsibility to free this buffer with
+ EFI_BOOT_SERVICES.FreePool().
+
+ @retval EFI_SUCCESS The firmware device was successfully updated
+ with the new firmware image.
+ @retval EFI_ABORTED The operation is aborted. Additional details
+ are provided in AbortReason.
+ @retval EFI_INVALID_PARAMETER The Image was NULL.
+ @retval EFI_UNSUPPORTED The operation is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpDeviceSetImage (
+ IN CONST VOID *Image,
+ IN UINTN ImageSize,
+ IN CONST VOID *VendorCode OPTIONAL,
+ IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress OPTIONAL,
+ IN UINT32 CapsuleFwVersion,
+ OUT CHAR16 **AbortReason
+ )
+{
+ UINT32 LastAttemptStatus;
+
+ return FmpDeviceSetImageWithStatus (
+ Image,
+ ImageSize,
+ VendorCode,
+ Progress,
+ CapsuleFwVersion,
+ AbortReason,
+ &LastAttemptStatus
+ );
+}
+
+/**
+ Updates a firmware device with a new firmware image. This function returns
+ EFI_UNSUPPORTED if the firmware image is not updatable. If the firmware image
+ is updatable, the function should perform the following minimal validations
+ before proceeding to do the firmware image update.
+ - Validate that the image is a supported image for this firmware device.
+ Return EFI_ABORTED if the image is not supported. Additional details
+ on why the image is not a supported image may be returned in AbortReason.
+ - Validate the data from VendorCode if is not NULL. Firmware image
+ validation must be performed before VendorCode data validation.
+ VendorCode data is ignored or considered invalid if image validation
+ fails. Return EFI_ABORTED if the VendorCode data is invalid.
+
+ VendorCode enables vendor to implement vendor-specific firmware image update
+ policy. Null if the caller did not specify the policy or use the default
+ policy. As an example, vendor can implement a policy to allow an option to
+ force a firmware image update when the abort reason is due to the new firmware
+ image version is older than the current firmware image version or bad image
+ checksum. Sensitive operations such as those wiping the entire firmware image
+ and render the device to be non-functional should be encoded in the image
+ itself rather than passed with the VendorCode. AbortReason enables vendor to
+ have the option to provide a more detailed description of the abort reason to
+ the caller.
+
+ @param[in] Image Points to the new firmware image.
+ @param[in] ImageSize Size, in bytes, of the new firmware image.
+ @param[in] VendorCode This enables vendor to implement vendor-specific
+ firmware image update policy. NULL indicates
+ the caller did not specify the policy or use the
+ default policy.
+ @param[in] Progress A function used to report the progress of
+ updating the firmware device with the new
+ firmware image.
+ @param[in] CapsuleFwVersion The version of the new firmware image from the
+ update capsule that provided the new firmware
+ image.
+ @param[out] AbortReason A pointer to a pointer to a Null-terminated
+ Unicode string providing more details on an
+ aborted operation. The buffer is allocated by
+ this function with
+ EFI_BOOT_SERVICES.AllocatePool(). It is the
+ caller's responsibility to free this buffer with
+ EFI_BOOT_SERVICES.FreePool().
+ @param[out] LastAttemptStatus A pointer to a UINT32 that holds the last attempt
+ status to report back to the ESRT table in case
+ of error. This value will only be checked when this
+ function returns an error.
+
+ The return status code must fall in the range of
+ LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MIN_ERROR_CODE_VALUE to
+ LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MAX_ERROR_CODE_VALUE.
+
+ If the value falls outside this range, it will be converted
+ to LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL.
+
+ @retval EFI_SUCCESS The firmware device was successfully updated
+ with the new firmware image.
+ @retval EFI_ABORTED The operation is aborted. Additional details
+ are provided in AbortReason.
+ @retval EFI_INVALID_PARAMETER The Image was NULL.
+ @retval EFI_UNSUPPORTED The operation is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+FmpDeviceSetImageWithStatus (
+ IN CONST VOID *Image,
+ IN UINTN ImageSize,
+ IN CONST VOID *VendorCode OPTIONAL,
+ IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress OPTIONAL,
+ IN UINT32 CapsuleFwVersion,
+ OUT CHAR16 **AbortReason,
+ OUT UINT32 *LastAttemptStatus
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UPDATE_CONFIG_DATA *ConfigData;
+ UINTN TotalSize;
+ UINTN BytesWritten;
+
+ if (Progress == NULL) {
+ DEBUG ((DEBUG_ERROR, "FmpDeviceSetImageWithStatus - Invalid progress callback\n"));
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSATISFIED_DEPENDENCIES;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;
+ DEBUG ((DEBUG_INFO, "FmpDeviceSetImageWithStatus - %d Images ...\n", ARRAY_SIZE (mUpdateConfigData)));
+
+ //
+ // Compute total size of update
+ //
+ for (Index = 0, TotalSize = 0; Index < ARRAY_SIZE (mUpdateConfigData); Index++) {
+ TotalSize += mUpdateConfigData[Index].Length;
+ }
+
+ BytesWritten = 0;
+ for (Index = 0, ConfigData = mUpdateConfigData; Index < ARRAY_SIZE (mUpdateConfigData); Index++, ConfigData++) {
+ DEBUG ((
+ DEBUG_INFO,
+ "PlatformUpdate(%d): BaseAddress - 0x%lx ImageOffset - 0x%x Length - 0x%x\n",
+ Index,
+ ConfigData->BaseAddress,
+ ConfigData->ImageOffset,
+ ConfigData->Length
+ ));
+ Status = PerformFlashWriteWithProgress (
+ ConfigData->FirmwareType, // FirmwareType
+ ConfigData->BaseAddress, // FlashAddress
+ ConfigData->AddressType, // FlashAddressType
+ (VOID *)((UINTN)Image + (UINTN)ConfigData->ImageOffset), // Buffer
+ ConfigData->Length, // BufferLength
+ Progress, // Progress
+ BytesWritten / TotalSize, // StartPercentage
+ (BytesWritten + ConfigData->Length) * 80 / TotalSize // EndPercentage
+ );
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ BytesWritten += ConfigData->Length;
+ }
+
+ DEBUG ((DEBUG_INFO, "FmpDeviceSetImageWithStatus - %r\n", Status));
+
+ if (EFI_ERROR (Status)) {
+ *LastAttemptStatus = LAST_ATTEMPT_STATUS_DEVICE_LIBRARY_MIN_ERROR_CODE_VALUE;
+ }
+
+ return Status;
+}
+
+/**
+ Lock the firmware device that contains a firmware image. Once a firmware
+ device is locked, any attempts to modify the firmware image contents in the
+ firmware device must fail.
+
+ @note It is recommended that all firmware devices support a lock method to
+ prevent modifications to a stored firmware image.
+
+ @note A firmware device lock mechanism is typically only cleared by a full
+ system reset (not just sleep state/low power mode).
+
+ @retval EFI_SUCCESS The firmware device was locked.
+ @retval EFI_UNSUPPORTED The firmware device does not support locking
+
+**/
+EFI_STATUS
+EFIAPI
+FmpDeviceLock (
+ VOID
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/OvmfPkg/RiscVVirt/Feature/Capsule/Library/FmpDeviceLib/FmpDeviceLib.inf b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/FmpDeviceLib/FmpDeviceLib.inf
new file mode 100644
index 000000000000..abdf5d5d6ddc
--- /dev/null
+++ b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/FmpDeviceLib/FmpDeviceLib.inf
@@ -0,0 +1,46 @@
+## @file
+# Provides firmware device specific services to support updates of a firmware
+# image stored in a firmware device.
+#
+# Copyright (c) 2016, Microsoft Corporation. All rights reserved.<BR>
+# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2025, Ventana Micro Systems Inc. All Rights Reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.30
+ BASE_NAME = FmpDeviceLib
+ FILE_GUID = BCBACAC2-1D1D-4C14-89A3-5E27496B702D
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = FmpDeviceLib|DXE_DRIVER UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 RISCV64
+#
+
+[Sources]
+ FmpDeviceLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ FmpDevicePkg/FmpDevicePkg.dec
+
+[LibraryClasses]
+ DebugLib
+ BaseLib
+ BaseMemoryLib
+ MemoryAllocationLib
+ PlatformFlashAccessLib
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwareRevision
+ gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceBuildTimeLowestSupportedVersion
+
+
+
diff --git a/OvmfPkg/RiscVVirt/Feature/Capsule/Library/PlatformFlashAccessLib/PlatformFlashAccessLib.c b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/PlatformFlashAccessLib/PlatformFlashAccessLib.c
new file mode 100644
index 000000000000..491757d51903
--- /dev/null
+++ b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/PlatformFlashAccessLib/PlatformFlashAccessLib.c
@@ -0,0 +1,236 @@
+/** @file
+ Platform Flash Access library.
+
+ Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+#include <Library/VirtNorFlashDeviceLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include "PlatformFlashAccessLib.h"
+
+#define ALIGN(v, a) (UINTN)((((v) - 1) | ((a) - 1)) + 1)
+
+#define FLASH_CODE_BASE 0x20000000
+#define FLASH_DATA_BASE 0x22000000
+#define FLASH_SIZE SIZE_32MB
+#define BLOCK_SIZE SIZE_256KB
+
+/**
+ Perform flash write operation with progress indicator. The start and end
+ completion percentage values are passed into this function. If the requested
+ flash write operation is broken up, then completion percentage between the
+ start and end values may be passed to the provided Progress function. The
+ caller of this function is required to call the Progress function for the
+ start and end completion percentage values. This allows the Progress,
+ StartPercentage, and EndPercentage parameters to be ignored if the requested
+ flash write operation can not be broken up
+
+ @param[in] FirmwareType The type of firmware.
+ @param[in] FlashAddress The address of flash device to be accessed.
+ @param[in] FlashAddressType The type of flash device address.
+ @param[in] Buffer The pointer to the data buffer.
+ @param[in] Length The length of data buffer in bytes.
+ @param[in] Progress A function used report the progress of the
+ firmware update. This is an optional parameter
+ that may be NULL.
+ @param[in] StartPercentage The start completion percentage value that may
+ be used to report progress during the flash
+ write operation.
+ @param[in] EndPercentage The end completion percentage value that may
+ be used to report progress during the flash
+ write operation.
+
+ @retval EFI_SUCCESS The operation returns successfully.
+ @retval EFI_WRITE_PROTECTED The flash device is read only.
+ @retval EFI_UNSUPPORTED The flash device access is unsupported.
+ @retval EFI_INVALID_PARAMETER The input parameter is not valid.
+**/
+EFI_STATUS
+EFIAPI
+PerformFlashWriteWithProgress (
+ IN PLATFORM_FIRMWARE_TYPE FirmwareType,
+ IN EFI_PHYSICAL_ADDRESS FlashAddress,
+ IN FLASH_ADDRESS_TYPE FlashAddressType,
+ IN VOID *Buffer,
+ IN UINTN Length,
+ IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress, OPTIONAL
+ IN UINTN StartPercentage,
+ IN UINTN EndPercentage
+ )
+{
+ EFI_STATUS Status = EFI_SUCCESS;
+ UINTN LbaNum;
+ UINTN Lba;
+ UINTN Index;
+ VOID *ShadowBuffer;
+ UINTN LastBlock;
+ UINTN NumByte;
+ UINTN FlashBase;
+
+ DEBUG ((DEBUG_INFO, "PerformFlashWrite - 0x%x(%x) - 0x%x\n", (UINTN)FlashAddress, (UINTN)FlashAddressType, Length));
+
+ if (FlashAddressType != FlashAddressTypeAbsoluteAddress) {
+ // Only support absolute address for this platform
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (FirmwareType == PlatformFirmwareTypeSystemFirmware) {
+ if ((FlashAddress < FLASH_CODE_BASE) || ((FlashAddress + Length) > (FLASH_CODE_BASE + FLASH_SIZE))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FlashBase = FLASH_CODE_BASE;
+ } else {
+ if ((FlashAddress < FLASH_DATA_BASE) || ((FlashAddress + Length) > (FLASH_DATA_BASE + FLASH_SIZE))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FlashBase = FLASH_DATA_BASE;
+ }
+
+ ShadowBuffer = AllocateZeroPool (BLOCK_SIZE);
+ LastBlock = FLASH_SIZE / BLOCK_SIZE - 1;
+ if ( (ALIGN (FlashAddress, BLOCK_SIZE) != FlashAddress)
+ || (Length % BLOCK_SIZE))
+ {
+ // Not expect un-aligned flash address
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Erase & Write
+ //
+ LbaNum = Length / BLOCK_SIZE;
+ Lba = (FlashAddress - FlashBase) / BLOCK_SIZE;
+ for (Index = 0; Index < LbaNum; Index++) {
+ if (Progress != NULL) {
+ Progress (StartPercentage + ((Index * (EndPercentage - StartPercentage)) / LbaNum));
+ }
+
+ if (CompareMem (
+ (UINT8 *)(UINTN)(GET_NOR_BLOCK_ADDRESS (FlashBase, Lba + Index, BLOCK_SIZE)),
+ (UINT8 *)Buffer + Index * BLOCK_SIZE,
+ BLOCK_SIZE
+ ) == 0)
+ {
+ DEBUG ((DEBUG_INFO, "Sector - 0x%x - skip\n", Index));
+ continue;
+ }
+
+ DEBUG ((DEBUG_INFO, "Sector - 0x%x - update...\n", Index));
+ NumByte = BLOCK_SIZE;
+ Status = NorFlashWriteSingleBlock (
+ FlashBase,
+ FlashBase,
+ Lba + Index,
+ LastBlock,
+ BLOCK_SIZE,
+ FLASH_SIZE,
+ 0,
+ &NumByte,
+ (UINT8 *)Buffer + Index * BLOCK_SIZE,
+ ShadowBuffer
+ );
+ if ((Status != EFI_SUCCESS) || (NumByte != BLOCK_SIZE)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "Sector - 0x%x - update failed (bytes written)...\n",
+ Index,
+ NumByte
+ ));
+ break;
+ }
+ }
+
+ if (Progress != NULL) {
+ Progress (EndPercentage);
+ }
+
+ FreePool (ShadowBuffer);
+ return Status;
+}
+
+/**
+ Perform flash write operation.
+
+ @param[in] FirmwareType The type of firmware.
+ @param[in] FlashAddress The address of flash device to be accessed.
+ @param[in] FlashAddressType The type of flash device address.
+ @param[in] Buffer The pointer to the data buffer.
+ @param[in] Length The length of data buffer in bytes.
+
+ @retval EFI_SUCCESS The operation returns successfully.
+ @retval EFI_WRITE_PROTECTED The flash device is read only.
+ @retval EFI_UNSUPPORTED The flash device access is unsupported.
+ @retval EFI_INVALID_PARAMETER The input parameter is not valid.
+**/
+EFI_STATUS
+EFIAPI
+PerformFlashWrite (
+ IN PLATFORM_FIRMWARE_TYPE FirmwareType,
+ IN EFI_PHYSICAL_ADDRESS FlashAddress,
+ IN FLASH_ADDRESS_TYPE FlashAddressType,
+ IN VOID *Buffer,
+ IN UINTN Length
+ )
+{
+ return PerformFlashWriteWithProgress (
+ FirmwareType,
+ FlashAddress,
+ FlashAddressType,
+ Buffer,
+ Length,
+ NULL,
+ 0,
+ 0
+ );
+}
+
+/**
+ Platform Flash Access Lib Constructor.
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS Constructor returns successfully.
+**/
+EFI_STATUS
+EFIAPI
+PlatformFlashAccessLibConstructor (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // map the code flash region, the data flash region
+ // already mapped via variable driver
+ //
+ Status = gDS->AddMemorySpace (
+ EfiGcdMemoryTypeMemoryMappedIo,
+ FLASH_CODE_BASE,
+ SIZE_32MB,
+ EFI_MEMORY_UC
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = gDS->SetMemorySpaceAttributes (
+ FLASH_CODE_BASE,
+ SIZE_32MB,
+ EFI_MEMORY_UC
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/RiscVVirt/Feature/Capsule/Library/PlatformFlashAccessLib/PlatformFlashAccessLib.h b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/PlatformFlashAccessLib/PlatformFlashAccessLib.h
new file mode 100644
index 000000000000..fe3cf0a17b3c
--- /dev/null
+++ b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/PlatformFlashAccessLib/PlatformFlashAccessLib.h
@@ -0,0 +1,95 @@
+/** @file
+ Platform flash device access library.
+
+ Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __PLATFORM_FLASH_ACCESS_LIB_H__
+#define __PLATFORM_FLASH_ACCESS_LIB_H__
+
+#include <Protocol/FirmwareManagement.h>
+
+typedef enum {
+ FlashAddressTypeRelativeAddress,
+ FlashAddressTypeAbsoluteAddress,
+} FLASH_ADDRESS_TYPE;
+
+//
+// Type 0 ~ 0x7FFFFFFF is defined in this library.
+// Type 0x80000000 ~ 0xFFFFFFFF is reserved for OEM.
+//
+typedef enum {
+ PlatformFirmwareTypeSystemFirmware,
+ PlatformFirmwareTypeNvRam,
+} PLATFORM_FIRMWARE_TYPE;
+
+/**
+ Perform flash write operation.
+
+ @param[in] FirmwareType The type of firmware.
+ @param[in] FlashAddress The address of flash device to be accessed.
+ @param[in] FlashAddressType The type of flash device address.
+ @param[in] Buffer The pointer to the data buffer.
+ @param[in] Length The length of data buffer in bytes.
+
+ @retval EFI_SUCCESS The operation returns successfully.
+ @retval EFI_WRITE_PROTECTED The flash device is read only.
+ @retval EFI_UNSUPPORTED The flash device access is unsupported.
+ @retval EFI_INVALID_PARAMETER The input parameter is not valid.
+**/
+EFI_STATUS
+EFIAPI
+PerformFlashWrite (
+ IN PLATFORM_FIRMWARE_TYPE FirmwareType,
+ IN EFI_PHYSICAL_ADDRESS FlashAddress,
+ IN FLASH_ADDRESS_TYPE FlashAddressType,
+ IN VOID *Buffer,
+ IN UINTN Length
+ );
+
+/**
+ Perform flash write operation with progress indicator. The start and end
+ completion percentage values are passed into this function. If the requested
+ flash write operation is broken up, then completion percentage between the
+ start and end values may be passed to the provided Progress function. The
+ caller of this function is required to call the Progress function for the
+ start and end completion percentage values. This allows the Progress,
+ StartPercentage, and EndPercentage parameters to be ignored if the requested
+ flash write operation can not be broken up
+
+ @param[in] FirmwareType The type of firmware.
+ @param[in] FlashAddress The address of flash device to be accessed.
+ @param[in] FlashAddressType The type of flash device address.
+ @param[in] Buffer The pointer to the data buffer.
+ @param[in] Length The length of data buffer in bytes.
+ @param[in] Progress A function used report the progress of the
+ firmware update. This is an optional parameter
+ that may be NULL.
+ @param[in] StartPercentage The start completion percentage value that may
+ be used to report progress during the flash
+ write operation.
+ @param[in] EndPercentage The end completion percentage value that may
+ be used to report progress during the flash
+ write operation.
+
+ @retval EFI_SUCCESS The operation returns successfully.
+ @retval EFI_WRITE_PROTECTED The flash device is read only.
+ @retval EFI_UNSUPPORTED The flash device access is unsupported.
+ @retval EFI_INVALID_PARAMETER The input parameter is not valid.
+**/
+EFI_STATUS
+EFIAPI
+PerformFlashWriteWithProgress (
+ IN PLATFORM_FIRMWARE_TYPE FirmwareType,
+ IN EFI_PHYSICAL_ADDRESS FlashAddress,
+ IN FLASH_ADDRESS_TYPE FlashAddressType,
+ IN VOID *Buffer,
+ IN UINTN Length,
+ IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS Progress OPTIONAL,
+ IN UINTN StartPercentage,
+ IN UINTN EndPercentage
+ );
+
+#endif
diff --git a/OvmfPkg/RiscVVirt/Feature/Capsule/Library/PlatformFlashAccessLib/PlatformFlashAccessLib.inf b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/PlatformFlashAccessLib/PlatformFlashAccessLib.inf
new file mode 100644
index 000000000000..1c632f2fde52
--- /dev/null
+++ b/OvmfPkg/RiscVVirt/Feature/Capsule/Library/PlatformFlashAccessLib/PlatformFlashAccessLib.inf
@@ -0,0 +1,34 @@
+## @file
+# Platform Flash Access library.
+#
+# Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2025, Ventana Micro Systems Inc. All Rights Reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 1.30
+ BASE_NAME = PlatformFlashAccessLibDxe
+ FILE_GUID = 2FDAFAFE-0179-4047-90A4-40BC56CFBBAE
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PlatformFlashAccessLib
+ CONSTRUCTOR = PlatformFlashAccessLibConstructor
+
+[Sources]
+ PlatformFlashAccessLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ PcdLib
+ DebugLib
+ VirtNorFlashDeviceLib
+ UefiBootServicesTableLib
+ DxeServicesTableLib
diff --git a/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc b/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc
index 8e864e123f08..47e388ced4a2 100644
--- a/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc
+++ b/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc
@@ -56,10 +56,20 @@
!error "NETWORK_SNP_ENABLE is IA32/X64/EBC only"
!endif
+ #
+ # UPDATE/RECOVERY definition
+ #
+ DEFINE CAPSULE_ENABLE = FALSE
+ DEFINE FMP_SYSTEM_DEVICE = BCBACAC2-1D1D-4C14-89A3-5E27496B702D
!include MdePkg/MdeLibs.dsc.inc
!include NetworkPkg/Network.dsc.inc
+!if $(CAPSULE_ENABLE) == TRUE
+ POSTBUILD = python OvmfPkg/RiscVVirt/Feature/Capsule/GenerateCapsule/GenCapsule.py
+!include OvmfPkg/RiscVVirt/RiscVVirtSystemFW.dsc.inc
+!endif
+
[BuildOptions]
GCC:RELEASE_*_*_CC_FLAGS = -DMDEPKG_NDEBUG
!ifdef $(SOURCE_DEBUG_ENABLE)
@@ -118,6 +128,18 @@
TpmPlatformHierarchyLib|SecurityPkg/Library/PeiDxeTpmPlatformHierarchyLibNull/PeiDxeTpmPlatformHierarchyLib.inf
!endif
+!if $(CAPSULE_ENABLE) == TRUE
+ CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf
+ BmpSupportLib|MdeModulePkg/Library/BaseBmpSupportLib/BaseBmpSupportLib.inf
+ SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf
+ OpensslLib|CryptoPkg/Library/OpensslLib/OpensslLib.inf
+ IntrinsicLib|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf
+ BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
+ FmpAuthenticationLib|SecurityPkg/Library/FmpAuthenticationLibPkcs7/FmpAuthenticationLibPkcs7.inf
+ DisplayUpdateProgressLib|MdeModulePkg/Library/DisplayUpdateProgressLibText/DisplayUpdateProgressLibText.inf
+ RngLib|MdeModulePkg/Library/BaseRngLibTimerLib/BaseRngLibTimerLib.inf
+!endif
+
[LibraryClasses.common.DXE_DRIVER]
AcpiPlatformLib|OvmfPkg/Library/AcpiPlatformLib/DxeAcpiPlatformLib.inf
ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
@@ -131,6 +153,11 @@
UefiScsiLib|MdePkg/Library/UefiScsiLib/UefiScsiLib.inf
PciExpressLib|OvmfPkg/Library/BaseCachingPciExpressLib/BaseCachingPciExpressLib.inf
+[LibraryClasses.common.DXE_RUNTIME_DRIVER]
+!if $(CAPSULE_ENABLE) == TRUE
+ CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf
+!endif
+
################################################################################
#
# Pcd Section - list of all EDK II PCD Entries defined by this Platform.
@@ -230,6 +257,10 @@
gEfiSecurityPkgTokenSpaceGuid.PcdTpmBaseAddress|0x0
!endif
+!if $(CAPSULE_ENABLE) == TRUE
+ gEfiMdeModulePkgTokenSpaceGuid.PcdSystemFmpCapsuleImageTypeIdGuid|{GUID("$(FMP_SYSTEM_DEVICE)")}|VOID*|0x10
+!endif
+
[PcdsDynamicHii]
gUefiOvmfPkgTokenSpaceGuid.PcdForceNoAcpi|L"ForceNoAcpi"|gOvmfVariableGuid|0x0|FALSE|NV,BS
@@ -493,3 +524,11 @@
<LibraryClasses>
NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
}
+
+!if $(CAPSULE_ENABLE) == TRUE
+ MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxe.inf
+ MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf {
+ <LibraryClasses>
+ PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
+ }
+!endif
diff --git a/OvmfPkg/RiscVVirt/RiscVVirtQemu.fdf b/OvmfPkg/RiscVVirt/RiscVVirtQemu.fdf
index 4528a44d3b82..8cbd5711aa64 100644
--- a/OvmfPkg/RiscVVirt/RiscVVirtQemu.fdf
+++ b/OvmfPkg/RiscVVirt/RiscVVirtQemu.fdf
@@ -216,6 +216,11 @@ INF MdeModulePkg/Logo/LogoDxe.inf
#
INF MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
+!if $(CAPSULE_ENABLE) == TRUE
+INF MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxe.inf
+INF FILE_GUID = $(FMP_SYSTEM_DEVICE) FmpDevicePkg/FmpDxe/FmpDxe.inf
+!endif
+
################################################################################
[FV.FVMAIN_COMPACT]
diff --git a/OvmfPkg/RiscVVirt/RiscVVirtSystemFW.dsc.inc b/OvmfPkg/RiscVVirt/RiscVVirtSystemFW.dsc.inc
new file mode 100644
index 000000000000..644e894e03fc
--- /dev/null
+++ b/OvmfPkg/RiscVVirt/RiscVVirtSystemFW.dsc.inc
@@ -0,0 +1,61 @@
+## @file
+# FmpDxe driver for system firmware update.
+#
+# Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+FmpDevicePkg/FmpDxe/FmpDxe.inf {
+ <Defines>
+ #
+ # ESRT and FMP GUID for system device capsule update
+ #
+ FILE_GUID = $(FMP_SYSTEM_DEVICE)
+
+ <PcdsFixedAtBuild>
+ #
+ # Unicode name string that is used to populate FMP Image Descriptor for this capsule update module
+ #
+ gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceImageIdName|L"RISC-V VIRT System Firmware Device"
+
+ #
+ # ESRT and FMP Lowest Support Version for this capsule update module
+ # 000.000.000.000
+ #
+ gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceBuildTimeLowestSupportedVersion|0x00000001
+
+ gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceProgressWatchdogTimeInSeconds|2
+
+ #
+ # Capsule Update Progress Bar Color. Set to Green (RGB) (0, 255, 0)
+ #
+ gFmpDevicePkgTokenSpaceGuid.PcdFmpDeviceProgressColor|0x0000FF00
+
+ #
+ # Certificates used to authenticate capsule update image
+ # EDKII Test certificate
+ #
+ !include BaseTools/Source/Python/Pkcs7Sign/TestRoot.cer.gFmpDevicePkgTokenSpaceGuid.PcdFmpDevicePkcs7CertBufferXdr.inc
+
+ <LibraryClasses>
+ #
+ # Generic libraries that are used "as is" by all FMP modules
+ #
+ FmpPayloadHeaderLib|FmpDevicePkg/Library/FmpPayloadHeaderLibV1/FmpPayloadHeaderLibV1.inf
+ FmpAuthenticationLib|SecurityPkg/Library/FmpAuthenticationLibPkcs7/FmpAuthenticationLibPkcs7.inf
+ FmpDependencyLib|FmpDevicePkg/Library/FmpDependencyLib/FmpDependencyLib.inf
+ FmpDependencyCheckLib|FmpDevicePkg/Library/FmpDependencyCheckLibNull/FmpDependencyCheckLibNull.inf
+ FmpDependencyDeviceLib|FmpDevicePkg/Library/FmpDependencyDeviceLibNull/FmpDependencyDeviceLibNull.inf
+ #
+ # Platform specific capsule policy library
+ #
+ CapsuleUpdatePolicyLib|OvmfPkg/RiscVVirt/Feature/Capsule/Library/CapsuleUpdatePolicyLib/CapsuleUpdatePolicyLib.inf
+ #
+ # Device specific library that processes a capsule and updates the FW storage device
+ #
+ FmpDeviceLib|OvmfPkg/RiscVVirt/Feature/Capsule/Library/FmpDeviceLib/FmpDeviceLib.inf
+ VirtNorFlashDeviceLib|OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.inf
+ PlatformFlashAccessLib|OvmfPkg/RiscVVirt/Feature/Capsule/Library/PlatformFlashAccessLib/PlatformFlashAccessLib.inf
+ }
--
2.34.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#121278): https://edk2.groups.io/g/devel/message/121278
Mute This Topic: https://groups.io/mt/112379040/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply related [flat|nested] 4+ messages in thread