From 6fff3da2254b09ca922949b9d861244bd9f1e4fc Mon Sep 17 00:00:00 2001 From: Tuan Phan Date: Tue, 27 May 2025 14:04:21 -0700 Subject: [PATCH] OvmfPkg/VirtNorFlash: Move low level NOR flash functions into library 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 --- .../Include/Library/VirtNorFlashDeviceLib.h | 126 +++++ .../VirtNorFlashDeviceLib.c} | 500 +++++++++++++----- .../VirtNorFlashDeviceLib.inf | 30 ++ OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc | 1 + OvmfPkg/OvmfPkg.dec | 4 + OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc | 1 + OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h | 345 ------------ OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.c | 144 +---- OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.h | 156 ++++++ OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf | 3 +- OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c | 38 +- 11 files changed, 737 insertions(+), 611 deletions(-) create mode 100644 OvmfPkg/Include/Library/VirtNorFlashDeviceLib.h rename OvmfPkg/{VirtNorFlashDxe/VirtNorFlash.c => Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.c} (56%) 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 0000000000..d6a6816fd9 --- /dev/null +++ b/OvmfPkg/Include/Library/VirtNorFlashDeviceLib.h @@ -0,0 +1,126 @@ +/** @file VirtNorFlashDeviceLib.h + + Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
+ Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef VIRT_NOR_FLASH_DEVICE_LIB_H +#define VIRT_NOR_FLASH_DEVICE_LIB_H + +// 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) ) + +EFI_STATUS +EFIAPI +NorFlashWriteBuffer ( + IN UINTN DeviceBaseAddress, + IN UINTN TargetAddress, + IN UINTN BufferSizeInBytes, + IN UINT32 *Buffer + ); + +EFI_STATUS +EFIAPI +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 +EFIAPI +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 +EFIAPI +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 +EFIAPI +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 +EFIAPI +NorFlashEraseSingleBlock ( + IN UINTN DeviceBaseAddress, + IN UINTN BlockAddress + ); + +EFI_STATUS +EFIAPI +NorFlashUnlockSingleBlockIfNecessary ( + IN UINTN DeviceBaseAddress, + IN UINTN BlockAddress + ); + +EFI_STATUS +EFIAPI +NorFlashWriteSingleWord ( + IN UINTN DeviceBaseAddress, + IN UINTN WordAddress, + IN UINT32 WriteData + ); + +EFI_STATUS +EFIAPI +NorFlashWriteFullBlock ( + IN UINTN DeviceBaseAddress, + IN UINTN RegionBaseAddress, + IN EFI_LBA Lba, + IN UINT32 *DataBuffer, + IN UINT32 BlockSizeInWords + ); + +EFI_STATUS +EFIAPI +NorFlashUnlockAndEraseSingleBlock ( + IN UINTN DeviceBaseAddress, + IN UINTN BlockAddress + ); + +EFI_STATUS +EFIAPI +NorFlashReset ( + IN UINTN DeviceBaseAddress + ); + +#endif /* VIRT_NOR_FLASH_DEVICE_LIB_H */ diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c b/OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.c similarity index 56% rename from OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c rename to OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.c index e6aaed27ce..731af75b73 100644 --- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c +++ b/OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.c @@ -1,4 +1,4 @@ -/** @file NorFlash.c +/** @file VirtNorFlashDeviceLib.c Copyright (c) 2011 - 2020, Arm Limited. All rights reserved.
Copyright (c) 2020, Linaro, Ltd. All rights reserved.
@@ -7,32 +7,108 @@ **/ +#include +#include + #include +#include +#include +#include -#include "VirtNorFlash.h" +#define NOR_FLASH_ERASE_RETRY 10 -// -// Global variable declarations -// -extern NOR_FLASH_INSTANCE **mNorFlashInstances; -extern UINT32 mNorFlashDeviceCount; +// 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 ) ) + +// 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 + +STATIC 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 +132,8 @@ NorFlashBlockIsLocked ( STATIC EFI_STATUS NorFlashUnlockSingleBlock ( - IN NOR_FLASH_INSTANCE *Instance, - IN UINTN BlockAddress + IN UINTN DeviceBaseAddress, + IN UINTN BlockAddress ) { UINT32 LockStatus; @@ -73,7 +149,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 @@ -85,17 +161,18 @@ NorFlashUnlockSingleBlock ( } EFI_STATUS +EFIAPI 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; @@ -105,9 +182,10 @@ NorFlashUnlockSingleBlockIfNecessary ( * The following function presumes that the block has already been unlocked. **/ EFI_STATUS +EFIAPI NorFlashEraseSingleBlock ( - IN NOR_FLASH_INSTANCE *Instance, - IN UINTN BlockAddress + IN UINTN DeviceBaseAddress, + IN UINTN BlockAddress ) { EFI_STATUS Status; @@ -121,7 +199,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 +225,21 @@ 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 +EFIAPI 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 +256,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 +281,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; @@ -224,11 +303,12 @@ NorFlashWriteSingleWord ( * i.e. the last 4 bits of the target start address must be zero: 0x......00 */ EFI_STATUS +EFIAPI 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 +379,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,18 +410,150 @@ 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; +} + +EFI_STATUS +EFIAPI 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 +577,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 +602,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; @@ -402,11 +620,15 @@ NorFlashWriteBlocks ( } EFI_STATUS +EFIAPI 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 +638,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 +654,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); @@ -461,12 +683,16 @@ NorFlashReadBlocks ( } EFI_STATUS +EFIAPI 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 +707,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 +731,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; @@ -539,17 +785,22 @@ NorFlashWriteSingleBlockWithErase ( Offset + *NumBytes <= Instance->BlockSize. */ EFI_STATUS +EFIAPI 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 +809,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 +868,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 +883,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 +891,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 +910,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 +921,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 +932,33 @@ 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 +EFIAPI 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 0000000000..befa57034d --- /dev/null +++ b/OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.inf @@ -0,0 +1,30 @@ +#/** @file +# +# This library enables low-level access to Virt Flash devices +# +# Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.
+# +# 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 948047e085..3f16e20764 100644 --- a/OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc +++ b/OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc @@ -204,6 +204,7 @@ DebugLib | MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf PeiServicesLib | MdePkg/Library/PeiServicesLib/PeiServicesLib.inf VariableFlashInfoLib | MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf + VirtNorFlashDeviceLib | OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.inf VirtNorFlashPlatformLib | OvmfPkg/Library/FdtNorFlashQemuLib/FdtNorFlashQemuLib.inf [LibraryClasses.common.SEC] diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index edc72a98fe..0a1a09fb70 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 ca211b3db3..aeda34247b 100644 --- a/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc +++ b/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc @@ -90,6 +90,7 @@ QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf TimerLib|UefiCpuPkg/Library/BaseRiscV64CpuTimerLib/BaseRiscV64CpuTimerLib.inf + VirtNorFlashDeviceLib|OvmfPkg/Library/VirtNorFlashDeviceLib/VirtNorFlashDeviceLib.inf VirtNorFlashPlatformLib|OvmfPkg/RiscVVirt/Library/VirtNorFlashPlatformLib/VirtNorFlashDeviceTreeLib.inf CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h b/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h deleted file mode 100644 index 455eafacc2..0000000000 --- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.h +++ /dev/null @@ -1,345 +0,0 @@ -/** @file NorFlash.h - - Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
- - SPDX-License-Identifier: BSD-2-Clause-Patent - -**/ - -#ifndef __VIRT_NOR_FLASH__ -#define __VIRT_NOR_FLASH__ - -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include - -#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 0ff8e5c9c6..5eec1c1398 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.
@@ -14,7 +14,7 @@ #include #include -#include "VirtNorFlash.h" +#include "VirtNorFlashDxe.h" STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent; @@ -138,131 +138,35 @@ NorFlashCreateInstance ( } /** - * This function unlock and erase an entire NOR Flash block. - **/ -EFI_STATUS -NorFlashUnlockAndEraseSingleBlock ( - IN NOR_FLASH_INSTANCE *Instance, - IN UINTN BlockAddress + @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 Index; + 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 (Instance, BlockAddress); - if (EFI_ERROR (Status)) { - break; - } + for (Index = 0; Index < mNorFlashDeviceCount; Index++) { + EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->DeviceBaseAddress); + EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->RegionBaseAddress); - Status = NorFlashEraseSingleBlock (Instance, BlockAddress); - Index++; - } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED)); + // 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 (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 NOR_FLASH_INSTANCE *Instance, - 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 (Instance->RegionBaseAddress, Lba, BlockSizeInWords * 4); - - // Start writing from the first address at the start of the block - WordAddress = BlockAddress; - - 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; - - 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; - } + 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 (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status)); - } - - return Status; } EFI_STATUS diff --git a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.h b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.h new file mode 100644 index 0000000000..3c7f509415 --- /dev/null +++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.h @@ -0,0 +1,156 @@ +/** @file VirtNorFlashDxe.h + + Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.
+ Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __VIRT_NOR_FLASH_DXE__ +#define __VIRT_NOR_FLASH_DXE__ + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 f549400280..6d6d615a62 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 59e5f432bc..2632e199b7 100644 --- a/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c +++ b/OvmfPkg/VirtNorFlashDxe/VirtNorFlashFvb.c @@ -19,7 +19,7 @@ #include #include -#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; @@ -748,7 +765,18 @@ FvbWrite ( OriginalTPL = TPL_HIGH_LEVEL; } - Status = NorFlashWriteSingleBlock (Instance, Instance->StartLba + Lba, Offset, NumBytes, Buffer); + Status = NorFlashWriteSingleBlock ( + Instance->DeviceBaseAddress, + Instance->RegionBaseAddress, + Instance->StartLba + Lba, + Instance->LastBlock, + Instance->BlockSize, + Instance->Size, + Offset, + NumBytes, + Buffer, + Instance->ShadowBuffer + ); if (!EfiAtRuntime ()) { // Interruptions can resume. @@ -893,7 +921,7 @@ FvbEraseBlocks ( OriginalTPL = TPL_HIGH_LEVEL; } - Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress); + Status = NorFlashUnlockAndEraseSingleBlock (Instance->DeviceBaseAddress, BlockAddress); if (!EfiAtRuntime ()) { // Interruptions can resume. gBS->RestoreTPL (OriginalTPL);