Files
system76-edk2/OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c
Ard Biesheuvel 789a723285 OvmfPkg/VirtNorFlashDxe: use EFI_MEMORY_WC and drop AlignedCopyMem()
NOR flash emulation under KVM involves switching between two modes,
where array mode is backed by a read-only memslot, and programming mode
is fully emulated, i.e., the memory region is not backed by anything,
and the faulting accesses are forwarded to the VMM by the hypervisor,
which translates them into NOR flash programming commands.

Normally, we are limited to the use of device attributes when mapping
such regions, given that the programming mode has MMIO semantics.
However, when running under KVM, the chosen memory attributes only take
effect when in array mode, since no memory mapping exists otherwise.

This means we can tune the memory mapping so it behaves a bit more like
a ROM, by switching to EFI_MEMORY_WC attributes. This means we no longer
need a special CopyMem() implementation that avoids unaligned accesses
at all cost.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: Sunil V L <sunilvl@ventanamicro.com>
2022-10-27 16:52:01 +00:00

698 lines
22 KiB
C

/** @file NorFlash.c
Copyright (c) 2011 - 2020, Arm Limited. All rights reserved.<BR>
Copyright (c) 2020, Linaro, Ltd. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Library/BaseMemoryLib.h>
#include "VirtNorFlash.h"
//
// Global variable declarations
//
extern NOR_FLASH_INSTANCE **mNorFlashInstances;
extern UINT32 mNorFlashDeviceCount;
UINT32
NorFlashReadStatusRegister (
IN NOR_FLASH_INSTANCE *Instance,
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);
}
STATIC
BOOLEAN
NorFlashBlockIsLocked (
IN NOR_FLASH_INSTANCE *Instance,
IN UINTN BlockAddress
)
{
UINT32 LockStatus;
// Send command for reading device id
SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID);
// Read block lock status
LockStatus = MmioRead32 (CREATE_NOR_ADDRESS (BlockAddress, 2));
// Decode block lock status
LockStatus = FOLD_32BIT_INTO_16BIT (LockStatus);
if ((LockStatus & 0x2) != 0) {
DEBUG ((DEBUG_ERROR, "NorFlashBlockIsLocked: WARNING: Block LOCKED DOWN\n"));
}
return ((LockStatus & 0x1) != 0);
}
STATIC
EFI_STATUS
NorFlashUnlockSingleBlock (
IN NOR_FLASH_INSTANCE *Instance,
IN UINTN BlockAddress
)
{
UINT32 LockStatus;
// Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations
// and to protect shared data structures.
// Request a lock setup
SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP);
// Request an unlock
SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK);
// Wait until the status register gives us the all clear
do {
LockStatus = NorFlashReadStatusRegister (Instance, BlockAddress);
} while ((LockStatus & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
// Put device back into Read Array mode
SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_READ_ARRAY);
DEBUG ((DEBUG_BLKIO, "UnlockSingleBlock: BlockAddress=0x%08x\n", BlockAddress));
return EFI_SUCCESS;
}
EFI_STATUS
NorFlashUnlockSingleBlockIfNecessary (
IN NOR_FLASH_INSTANCE *Instance,
IN UINTN BlockAddress
)
{
EFI_STATUS Status;
Status = EFI_SUCCESS;
if (NorFlashBlockIsLocked (Instance, BlockAddress)) {
Status = NorFlashUnlockSingleBlock (Instance, BlockAddress);
}
return Status;
}
/**
* The following function presumes that the block has already been unlocked.
**/
EFI_STATUS
NorFlashEraseSingleBlock (
IN NOR_FLASH_INSTANCE *Instance,
IN UINTN BlockAddress
)
{
EFI_STATUS Status;
UINT32 StatusRegister;
Status = EFI_SUCCESS;
// Request a block erase and then confirm it
SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_BLOCK_ERASE_SETUP);
SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_BLOCK_ERASE_CONFIRM);
// Wait until the status register gives us the all clear
do {
StatusRegister = NorFlashReadStatusRegister (Instance, BlockAddress);
} while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
if (StatusRegister & P30_SR_BIT_VPP) {
DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: VPP Range Error\n", BlockAddress));
Status = EFI_DEVICE_ERROR;
}
if ((StatusRegister & (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) == (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) {
DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: Command Sequence Error\n", BlockAddress));
Status = EFI_DEVICE_ERROR;
}
if (StatusRegister & P30_SR_BIT_ERASE) {
DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: Block Erase Error StatusRegister:0x%X\n", BlockAddress, StatusRegister));
Status = EFI_DEVICE_ERROR;
}
if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
// The debug level message has been reduced because a device lock might happen. In this case we just retry it ...
DEBUG ((DEBUG_INFO, "EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error\n", BlockAddress));
Status = EFI_WRITE_PROTECTED;
}
if (EFI_ERROR (Status)) {
// Clear the Status Register
SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
}
// Put device back into Read Array mode
SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
return Status;
}
EFI_STATUS
NorFlashWriteSingleWord (
IN NOR_FLASH_INSTANCE *Instance,
IN UINTN WordAddress,
IN UINT32 WriteData
)
{
EFI_STATUS Status;
UINT32 StatusRegister;
Status = EFI_SUCCESS;
// Request a write single word command
SEND_NOR_COMMAND (WordAddress, 0, P30_CMD_WORD_PROGRAM_SETUP);
// Store the word into NOR Flash;
MmioWrite32 (WordAddress, WriteData);
// 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);
// The chip is busy while the WRITE bit is not asserted
} while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
// Perform a full status check:
// Mask the relevant bits of Status Register.
// Everything should be zero, if not, we have a problem
if (StatusRegister & P30_SR_BIT_VPP) {
DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): VPP Range Error\n", WordAddress));
Status = EFI_DEVICE_ERROR;
}
if (StatusRegister & P30_SR_BIT_PROGRAM) {
DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): Program Error\n", WordAddress));
Status = EFI_DEVICE_ERROR;
}
if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): Device Protect Error\n", WordAddress));
Status = EFI_DEVICE_ERROR;
}
if (!EFI_ERROR (Status)) {
// Clear the Status Register
SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
}
return Status;
}
/*
* Writes data to the NOR Flash using the Buffered Programming method.
*
* The maximum size of the on-chip buffer is 32-words, because of hardware restrictions.
* Therefore this function will only handle buffers up to 32 words or 128 bytes.
* To deal with larger buffers, call this function again.
*
* This function presumes that both the TargetAddress and the TargetAddress+BufferSize
* exist entirely within the NOR Flash. Therefore these conditions will not be checked here.
*
* In buffered programming, if the target address not at the beginning of a 32-bit word boundary,
* then programming time is doubled and power consumption is increased.
* Therefore, it is a requirement to align buffer writes to 32-bit word boundaries.
* i.e. the last 4 bits of the target start address must be zero: 0x......00
*/
EFI_STATUS
NorFlashWriteBuffer (
IN NOR_FLASH_INSTANCE *Instance,
IN UINTN TargetAddress,
IN UINTN BufferSizeInBytes,
IN UINT32 *Buffer
)
{
EFI_STATUS Status;
UINTN BufferSizeInWords;
UINTN Count;
volatile UINT32 *Data;
UINTN WaitForBuffer;
BOOLEAN BufferAvailable;
UINT32 StatusRegister;
WaitForBuffer = MAX_BUFFERED_PROG_ITERATIONS;
BufferAvailable = FALSE;
// Check that the target address does not cross a 32-word boundary.
if ((TargetAddress & BOUNDARY_OF_32_WORDS) != 0) {
return EFI_INVALID_PARAMETER;
}
// Check there are some data to program
if (BufferSizeInBytes == 0) {
return EFI_BUFFER_TOO_SMALL;
}
// Check that the buffer size does not exceed the maximum hardware buffer size on chip.
if (BufferSizeInBytes > P30_MAX_BUFFER_SIZE_IN_BYTES) {
return EFI_BAD_BUFFER_SIZE;
}
// Check that the buffer size is a multiple of 32-bit words
if ((BufferSizeInBytes % 4) != 0) {
return EFI_BAD_BUFFER_SIZE;
}
// Pre-programming conditions checked, now start the algorithm.
// Prepare the data destination address
Data = (UINT32 *)TargetAddress;
// Check the availability of the buffer
do {
// Issue the Buffered Program Setup command
SEND_NOR_COMMAND (TargetAddress, 0, P30_CMD_BUFFERED_PROGRAM_SETUP);
// Read back the status register bit#7 from the same address
if (((*Data) & P30_SR_BIT_WRITE) == P30_SR_BIT_WRITE) {
BufferAvailable = TRUE;
}
// Update the loop counter
WaitForBuffer--;
} while ((WaitForBuffer > 0) && (BufferAvailable == FALSE));
// The buffer was not available for writing
if (WaitForBuffer == 0) {
return EFI_DEVICE_ERROR;
}
// From now on we work in 32-bit words
BufferSizeInWords = BufferSizeInBytes / (UINTN)4;
// Write the word count, which is (buffer_size_in_words - 1),
// because word count 0 means one word.
SEND_NOR_COMMAND (TargetAddress, 0, (BufferSizeInWords - 1));
// Write the data to the NOR Flash, advancing each address by 4 bytes
for (Count = 0; Count < BufferSizeInWords; Count++, Data++, Buffer++) {
MmioWrite32 ((UINTN)Data, *Buffer);
}
// Issue the Buffered Program Confirm command, to start the programming operation
SEND_NOR_COMMAND (Instance->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);
// The chip is busy while the WRITE bit is not asserted
} while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
// Perform a full status check:
// Mask the relevant bits of Status Register.
// Everything should be zero, if not, we have a problem
Status = EFI_SUCCESS;
if (StatusRegister & P30_SR_BIT_VPP) {
DEBUG ((DEBUG_ERROR, "NorFlashWriteBuffer(TargetAddress:0x%X): VPP Range Error\n", TargetAddress));
Status = EFI_DEVICE_ERROR;
}
if (StatusRegister & P30_SR_BIT_PROGRAM) {
DEBUG ((DEBUG_ERROR, "NorFlashWriteBuffer(TargetAddress:0x%X): Program Error\n", TargetAddress));
Status = EFI_DEVICE_ERROR;
}
if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
DEBUG ((DEBUG_ERROR, "NorFlashWriteBuffer(TargetAddress:0x%X): Device Protect Error\n", TargetAddress));
Status = EFI_DEVICE_ERROR;
}
if (!EFI_ERROR (Status)) {
// Clear the Status Register
SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
}
return Status;
}
EFI_STATUS
NorFlashWriteBlocks (
IN NOR_FLASH_INSTANCE *Instance,
IN EFI_LBA Lba,
IN UINTN BufferSizeInBytes,
IN VOID *Buffer
)
{
UINT32 *pWriteBuffer;
EFI_STATUS Status;
EFI_LBA CurrentBlock;
UINT32 BlockSizeInWords;
UINT32 NumBlocks;
UINT32 BlockCount;
Status = EFI_SUCCESS;
// The buffer must be valid
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
// We must have some bytes to read
DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes));
if (BufferSizeInBytes == 0) {
return EFI_BAD_BUFFER_SIZE;
}
// 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) {
return EFI_BAD_BUFFER_SIZE;
}
// All blocks must be within the device
NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->BlockSize;
DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->LastBlock, Lba));
if ((Lba + NumBlocks) > (Instance->LastBlock + 1)) {
DEBUG ((DEBUG_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n"));
return EFI_INVALID_PARAMETER;
}
BlockSizeInWords = Instance->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
pWriteBuffer = (UINT32 *)Buffer;
CurrentBlock = Lba;
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);
if (EFI_ERROR (Status)) {
break;
}
}
DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status));
return Status;
}
EFI_STATUS
NorFlashReadBlocks (
IN NOR_FLASH_INSTANCE *Instance,
IN EFI_LBA Lba,
IN UINTN BufferSizeInBytes,
OUT VOID *Buffer
)
{
UINT32 NumBlocks;
UINTN StartAddress;
DEBUG ((
DEBUG_BLKIO,
"NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n",
BufferSizeInBytes,
Instance->BlockSize,
Instance->LastBlock,
Lba
));
// The buffer must be valid
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
// Return if we have not any byte to read
if (BufferSizeInBytes == 0) {
return EFI_SUCCESS;
}
// The size of the buffer must be a multiple of the block size
if ((BufferSizeInBytes % Instance->BlockSize) != 0) {
return EFI_BAD_BUFFER_SIZE;
}
// All blocks must be within the device
NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->BlockSize;
if ((Lba + NumBlocks) > (Instance->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,
Lba,
Instance->BlockSize
);
// Put the device into Read Array mode
SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
// Readout the data
CopyMem (Buffer, (VOID *)StartAddress, BufferSizeInBytes);
return EFI_SUCCESS;
}
EFI_STATUS
NorFlashRead (
IN NOR_FLASH_INSTANCE *Instance,
IN EFI_LBA Lba,
IN UINTN Offset,
IN UINTN BufferSizeInBytes,
OUT VOID *Buffer
)
{
UINTN StartAddress;
// The buffer must be valid
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
// Return if we have not any byte to read
if (BufferSizeInBytes == 0) {
return EFI_SUCCESS;
}
if (((Lba * Instance->BlockSize) + Offset + BufferSizeInBytes) > Instance->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,
Lba,
Instance->BlockSize
);
// Put the device into Read Array mode
SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
// Readout the data
CopyMem (Buffer, (VOID *)(StartAddress + Offset), BufferSizeInBytes);
return EFI_SUCCESS;
}
/*
Write a full or portion of a block. It must not span block boundaries; that is,
Offset + *NumBytes <= Instance->BlockSize.
*/
EFI_STATUS
NorFlashWriteSingleBlock (
IN NOR_FLASH_INSTANCE *Instance,
IN EFI_LBA Lba,
IN UINTN Offset,
IN OUT UINTN *NumBytes,
IN UINT8 *Buffer
)
{
EFI_STATUS Status;
UINTN CurOffset;
UINTN BlockSize;
UINTN BlockAddress;
UINT8 *OrigData;
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) {
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) ||
(*NumBytes > BlockSize) ||
((Offset + *NumBytes) > BlockSize))
{
DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize));
return EFI_BAD_BUFFER_SIZE;
}
// We must have some bytes to write
if (*NumBytes == 0) {
DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize));
return EFI_BAD_BUFFER_SIZE;
}
// Pick P30_MAX_BUFFER_SIZE_IN_BYTES (== 128 bytes) as a good start for word
// operations as opposed to erasing the block and writing the data regardless
// if an erase is really needed. It looks like most individual NV variable
// writes are smaller than 128 bytes.
// To avoid pathological cases were a 2 byte write is disregarded because it
// occurs right at a 128 byte buffered write alignment boundary, permit up to
// twice the max buffer size, and perform two writes if needed.
if ((*NumBytes + (Offset & BOUNDARY_OF_32_WORDS)) <= (2 * P30_MAX_BUFFER_SIZE_IN_BYTES)) {
// Check to see if we need to erase before programming the data into NOR.
// If the destination bits are only changing from 1s to 0s we can just write.
// After a block is erased all bits in the block is set to 1.
// If any byte requires us to erase we just give up and rewrite all of it.
// Read the old version of the data into the shadow buffer
Status = NorFlashRead (
Instance,
Lba,
Offset & ~BOUNDARY_OF_32_WORDS,
(*NumBytes | BOUNDARY_OF_32_WORDS) + 1,
Instance->ShadowBuffer
);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
// 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);
// Update the buffer containing the old version of the data with the new
// contents, while checking whether the old version had any bits cleared
// that we want to set. In that case, we will need to erase the block first.
for (CurOffset = 0; CurOffset < *NumBytes; CurOffset++) {
if (~OrigData[CurOffset] & Buffer[CurOffset]) {
goto DoErase;
}
OrigData[CurOffset] = Buffer[CurOffset];
}
//
// Write the updated buffer to NOR.
//
BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize);
// Unlock the block if we have to
Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
if (EFI_ERROR (Status)) {
goto Exit;
}
Status = NorFlashWriteBuffer (
Instance,
BlockAddress + (Offset & ~BOUNDARY_OF_32_WORDS),
P30_MAX_BUFFER_SIZE_IN_BYTES,
Instance->ShadowBuffer
);
if (EFI_ERROR (Status)) {
goto Exit;
}
if ((*NumBytes + (Offset & BOUNDARY_OF_32_WORDS)) > P30_MAX_BUFFER_SIZE_IN_BYTES) {
BlockAddress += P30_MAX_BUFFER_SIZE_IN_BYTES;
Status = NorFlashWriteBuffer (
Instance,
BlockAddress + (Offset & ~BOUNDARY_OF_32_WORDS),
P30_MAX_BUFFER_SIZE_IN_BYTES,
Instance->ShadowBuffer + P30_MAX_BUFFER_SIZE_IN_BYTES
);
}
Exit:
// Put device back into Read Array mode
SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
return Status;
}
DoErase:
// Read NOR Flash data into shadow buffer
Status = NorFlashReadBlocks (Instance, Lba, BlockSize, Instance->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);
// Write the modified buffer back to the NorFlash
Status = NorFlashWriteBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
if (EFI_ERROR (Status)) {
// Return one of the pre-approved error statuses
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
EFI_STATUS
NorFlashReset (
IN NOR_FLASH_INSTANCE *Instance
)
{
// 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);
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;
}