X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=ArmPlatformPkg%2FDrivers%2FNorFlashDxe%2FNorFlashDxe.c;h=53753a4721ac980f202e690b596a1ad31e7250c6;hp=a9c589b744c56363332dc32476b0c575d0817ce3;hb=4ef113583978c6a385c82cbbe1ed9cc754a5ffdc;hpb=8274521330e956ada5b9fc2a21aa81bd9d872f4c diff --git a/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.c b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.c index a9c589b744..53753a4721 100644 --- a/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.c +++ b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.c @@ -1,6 +1,6 @@ /** @file NorFlashDxe.c - Copyright (c) 2011-2012, ARM Ltd. All rights reserved.
+ Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -20,19 +20,18 @@ #include "NorFlashDxe.h" +STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent; // // Global variable declarations // NOR_FLASH_INSTANCE **mNorFlashInstances; +UINT32 mNorFlashDeviceCount; NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = { NOR_FLASH_SIGNATURE, // Signature NULL, // Handle ... NEED TO BE FILLED - FALSE, // Initialized - NULL, // Initialize - 0, // DeviceBaseAddress ... NEED TO BE FILLED 0, // RegionBaseAddress ... NEED TO BE FILLED 0, // Size ... NEED TO BE FILLED @@ -61,7 +60,12 @@ NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = { 1, // LogicalBlocksPerPhysicalBlock }, //Media; - FALSE, // SupportFvb ... NEED TO BE FILLED + { + EFI_DISK_IO_PROTOCOL_REVISION, // Revision + NorFlashDiskIoReadDisk, // ReadDisk + NorFlashDiskIoWriteDisk // WriteDisk + }, + { FvbGetAttributes, // GetAttributes FvbSetAttributes, // SetAttributes @@ -72,22 +76,23 @@ NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = { FvbEraseBlocks, // EraseBlocks NULL, //ParentHandle }, // FvbProtoccol; - + NULL, // ShadowBuffer { { { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, - (UINT8)( sizeof(VENDOR_DEVICE_PATH) ), - (UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8), + { + (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End)), + (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End) >> 8) + } }, - { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, // GUID ... NEED TO BE FILLED + { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }, // GUID ... NEED TO BE FILLED }, { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, - sizeof (EFI_DEVICE_PATH_PROTOCOL), - 0 + { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 } } } // DevicePath }; @@ -97,7 +102,7 @@ NorFlashCreateInstance ( IN UINTN NorFlashDeviceBase, IN UINTN NorFlashRegionBase, IN UINTN NorFlashSize, - IN UINT32 MediaId, + IN UINT32 Index, IN UINT32 BlockSize, IN BOOLEAN SupportFvb, IN CONST GUID *NorFlashGuid, @@ -109,7 +114,7 @@ NorFlashCreateInstance ( ASSERT(NorFlashInstance != NULL); - Instance = AllocateCopyPool (sizeof(NOR_FLASH_INSTANCE),&mNorFlashInstanceTemplate); + Instance = AllocateRuntimeCopyPool (sizeof(NOR_FLASH_INSTANCE),&mNorFlashInstanceTemplate); if (Instance == NULL) { return EFI_OUT_OF_RESOURCES; } @@ -119,15 +124,19 @@ NorFlashCreateInstance ( Instance->Size = NorFlashSize; Instance->BlockIoProtocol.Media = &Instance->Media; - Instance->Media.MediaId = MediaId; + Instance->Media.MediaId = Index; Instance->Media.BlockSize = BlockSize; Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1; - CopyGuid (&Instance->DevicePath.Vendor.Guid,NorFlashGuid); + CopyGuid (&Instance->DevicePath.Vendor.Guid, NorFlashGuid); + + Instance->ShadowBuffer = AllocateRuntimePool (BlockSize);; + if (Instance->ShadowBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } if (SupportFvb) { - Instance->SupportFvb = TRUE; - Instance->Initialize = NorFlashFvbInitialize; + NorFlashFvbInitialize (Instance); Status = gBS->InstallMultipleProtocolInterfaces ( &Instance->Handle, @@ -137,20 +146,19 @@ NorFlashCreateInstance ( NULL ); if (EFI_ERROR(Status)) { - FreePool(Instance); + FreePool (Instance); return Status; } } else { - Instance->Initialized = TRUE; - Status = gBS->InstallMultipleProtocolInterfaces ( &Instance->Handle, &gEfiDevicePathProtocolGuid, &Instance->DevicePath, &gEfiBlockIoProtocolGuid, &Instance->BlockIoProtocol, + &gEfiDiskIoProtocolGuid, &Instance->DiskIoProtocol, NULL ); if (EFI_ERROR(Status)) { - FreePool(Instance); + FreePool (Instance); return Status; } } @@ -170,7 +178,7 @@ NorFlashReadStatusRegister ( return MmioRead32 (Instance->DeviceBaseAddress); } - +STATIC BOOLEAN NorFlashBlockIsLocked ( IN NOR_FLASH_INSTANCE *Instance, @@ -178,9 +186,6 @@ NorFlashBlockIsLocked ( ) { UINT32 LockStatus; - BOOLEAN BlockIsLocked; - - BlockIsLocked = TRUE; // Send command for reading device id SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID); @@ -195,23 +200,16 @@ NorFlashBlockIsLocked ( DEBUG((EFI_D_ERROR, "NorFlashBlockIsLocked: WARNING: Block LOCKED DOWN\n")); } - if ((LockStatus & 0x1) == 0) { - // This means the block is unlocked - DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: Block 0x%08x unlocked\n", BlockAddress)); - BlockIsLocked = FALSE; - } - - return BlockIsLocked; + return ((LockStatus & 0x1) != 0); } - +STATIC EFI_STATUS NorFlashUnlockSingleBlock ( IN NOR_FLASH_INSTANCE *Instance, IN UINTN BlockAddress ) { - EFI_STATUS Status = EFI_SUCCESS; UINT32 LockStatus; // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations @@ -250,19 +248,21 @@ NorFlashUnlockSingleBlock ( // Put device back into Read Array mode SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_READ_ARRAY); - DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: BlockAddress=0x%08x, Exit Status = \"%r\".\n", BlockAddress, Status)); + DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: BlockAddress=0x%08x\n", BlockAddress)); - return Status; + return EFI_SUCCESS; } - +STATIC EFI_STATUS NorFlashUnlockSingleBlockIfNecessary ( IN NOR_FLASH_INSTANCE *Instance, IN UINTN BlockAddress ) { - EFI_STATUS Status = EFI_SUCCESS; + EFI_STATUS Status; + + Status = EFI_SUCCESS; if (NorFlashBlockIsLocked (Instance, BlockAddress) == TRUE) { Status = NorFlashUnlockSingleBlock (Instance, BlockAddress); @@ -275,6 +275,7 @@ NorFlashUnlockSingleBlockIfNecessary ( /** * The following function presumes that the block has already been unlocked. **/ +STATIC EFI_STATUS NorFlashEraseSingleBlock ( IN NOR_FLASH_INSTANCE *Instance, @@ -328,7 +329,7 @@ NorFlashEraseSingleBlock ( } /** - * The following function presumes that the block has already been unlocked. + * This function unlock and erase an entire NOR Flash block. **/ EFI_STATUS NorFlashUnlockAndEraseSingleBlock ( @@ -340,17 +341,24 @@ NorFlashUnlockAndEraseSingleBlock ( UINTN Index; EFI_TPL OriginalTPL; - // Raise TPL to TPL_HIGH to stop anyone from interrupting us. - OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL); + 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; + } 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)) { - Status = NorFlashEraseSingleBlock (Instance, BlockAddress); + if (EFI_ERROR (Status)) { + break; } + Status = NorFlashEraseSingleBlock (Instance, BlockAddress); Index++; } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED)); @@ -358,13 +366,16 @@ NorFlashUnlockAndEraseSingleBlock ( DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress,Index)); } - // Interruptions can resume. - gBS->RestoreTPL (OriginalTPL); + if (!EfiAtRuntime ()) { + // Interruptions can resume. + gBS->RestoreTPL (OriginalTPL); + } return Status; } +STATIC EFI_STATUS NorFlashWriteSingleWord ( IN NOR_FLASH_INSTANCE *Instance, @@ -510,7 +521,7 @@ NorFlashWriteBuffer ( // Write the data to the NOR Flash, advancing each address by 4 bytes for(Count=0; Count < BufferSizeInWords; Count++, Data++, Buffer++) { - *Data = *Buffer; + MmioWrite32 ((UINTN)Data, *Buffer); } // Issue the Buffered Program Confirm command, to start the programming operation @@ -556,8 +567,9 @@ EXIT: return Status; } +STATIC EFI_STATUS -NorFlashWriteSingleBlock ( +NorFlashWriteFullBlock ( IN NOR_FLASH_INSTANCE *Instance, IN EFI_LBA Lba, IN UINT32 *DataBuffer, @@ -572,6 +584,7 @@ NorFlashWriteSingleBlock ( UINTN BuffersInBlock; UINTN RemainingWords; EFI_TPL OriginalTPL; + UINTN Cnt; Status = EFI_SUCCESS; @@ -581,8 +594,14 @@ NorFlashWriteSingleBlock ( // Start writing from the first address at the start of the block WordAddress = BlockAddress; - // Raise TPL to TPL_HIGH to stop anyone from interrupting us. - OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL); + 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)) { @@ -599,13 +618,22 @@ NorFlashWriteSingleBlock ( 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 ) { - Status = NorFlashWriteBuffer (Instance, WordAddress, P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer); - if (EFI_ERROR(Status)) { - goto EXIT; + // 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; + } } } @@ -632,8 +660,10 @@ NorFlashWriteSingleBlock ( } EXIT: - // Interruptions can resume. - gBS->RestoreTPL (OriginalTPL); + if (!EfiAtRuntime ()) { + // Interruptions can resume. + gBS->RestoreTPL (OriginalTPL); + } if (EFI_ERROR(Status)) { DEBUG((EFI_D_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status)); @@ -699,7 +729,7 @@ NorFlashWriteBlocks ( DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN)CurrentBlock)); - Status = NorFlashWriteSingleBlock (Instance, CurrentBlock, pWriteBuffer, BlockSizeInWords); + Status = NorFlashWriteFullBlock (Instance, CurrentBlock, pWriteBuffer, BlockSizeInWords); if (EFI_ERROR(Status)) { break; @@ -710,6 +740,65 @@ NorFlashWriteBlocks ( return Status; } +#define BOTH_ALIGNED(a, b, align) ((((UINTN)(a) | (UINTN)(b)) & ((align) - 1)) == 0) + +/** + Copy Length bytes from Source to Destination, using aligned accesses only. + Note that this implementation uses memcpy() semantics rather then memmove() + semantics, i.e., SourceBuffer and DestinationBuffer should not overlap. + + @param DestinationBuffer The target of the copy request. + @param SourceBuffer The place to copy from. + @param Length The number of bytes to copy. + + @return Destination + +**/ +STATIC +VOID * +AlignedCopyMem ( + OUT VOID *DestinationBuffer, + IN CONST VOID *SourceBuffer, + IN UINTN Length + ) +{ + UINT8 *Destination8; + CONST UINT8 *Source8; + UINT32 *Destination32; + CONST UINT32 *Source32; + UINT64 *Destination64; + CONST UINT64 *Source64; + + if (BOTH_ALIGNED(DestinationBuffer, SourceBuffer, 8) && Length >= 8) { + Destination64 = DestinationBuffer; + Source64 = SourceBuffer; + while (Length >= 8) { + *Destination64++ = *Source64++; + Length -= 8; + } + + Destination8 = (UINT8 *)Destination64; + Source8 = (CONST UINT8 *)Source64; + } else if (BOTH_ALIGNED(DestinationBuffer, SourceBuffer, 4) && Length >= 4) { + Destination32 = DestinationBuffer; + Source32 = SourceBuffer; + while (Length >= 4) { + *Destination32++ = *Source32++; + Length -= 4; + } + + Destination8 = (UINT8 *)Destination32; + Source8 = (CONST UINT8 *)Source32; + } else { + Destination8 = DestinationBuffer; + Source8 = SourceBuffer; + } + while (Length-- != 0) { + *Destination8++ = *Source8++; + } + return DestinationBuffer; +} + EFI_STATUS NorFlashReadBlocks ( IN NOR_FLASH_INSTANCE *Instance, @@ -721,19 +810,20 @@ NorFlashReadBlocks ( UINT32 NumBlocks; UINTN StartAddress; + DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n", + BufferSizeInBytes, Instance->Media.BlockSize, Instance->Media.LastBlock, Lba)); + // The buffer must be valid if (Buffer == NULL) { return EFI_INVALID_PARAMETER; } - // We must have some bytes to read - DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: BufferSize=0x%x bytes.\n", BufferSizeInBytes)); - if(BufferSizeInBytes == 0) { - return EFI_BAD_BUFFER_SIZE; + // 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 - DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: BlockSize=0x%x bytes.\n", Instance->Media.BlockSize)); if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) { return EFI_BAD_BUFFER_SIZE; } @@ -741,8 +831,6 @@ NorFlashReadBlocks ( // All blocks must be within the device NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ; - DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld\n", NumBlocks, Instance->Media.LastBlock, Lba)); - if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) { DEBUG((EFI_D_ERROR, "NorFlashReadBlocks: ERROR - Read will exceed last block\n")); return EFI_INVALID_PARAMETER; @@ -758,11 +846,381 @@ NorFlashReadBlocks ( SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); // Readout the data - CopyMem(Buffer, (UINTN *)StartAddress, BufferSizeInBytes); + AlignedCopyMem (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->Media.BlockSize) + Offset + BufferSizeInBytes) > Instance->Size) { + DEBUG ((EFI_D_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->Media.BlockSize + ); + + // Put the device into Read Array mode + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); + + // Readout the data + AlignedCopyMem (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->Media.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 TempStatus; + UINT32 Tmp; + UINT32 TmpBuf; + UINT32 WordToWrite; + UINT32 Mask; + BOOLEAN DoErase; + UINTN BytesToWrite; + UINTN CurOffset; + UINTN WordAddr; + UINTN BlockSize; + UINTN BlockAddress; + UINTN PrevBlockAddress; + + PrevBlockAddress = 0; + + DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer)); + + // Detect WriteDisabled state + if (Instance->Media.ReadOnly == TRUE) { + DEBUG ((EFI_D_ERROR, "NorFlashWriteSingleBlock: ERROR - Can not write: Device is in WriteDisabled state.\n")); + // It is in WriteDisabled state, return an error right away + return EFI_ACCESS_DENIED; + } + + // Cache the block size to avoid de-referencing pointers all the time + BlockSize = Instance->Media.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 ((EFI_D_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 ((EFI_D_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 128bytes 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 128bytes. + if (*NumBytes <= 128) { + // 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. + DoErase = FALSE; + BytesToWrite = *NumBytes; + CurOffset = Offset; + + while (BytesToWrite > 0) { + // Read full word from NOR, splice as required. A word is the smallest + // unit we can write. + TempStatus = NorFlashRead (Instance, Lba, CurOffset & ~(0x3), sizeof(Tmp), &Tmp); + if (EFI_ERROR (TempStatus)) { + return EFI_DEVICE_ERROR; + } + + // Physical address of word in NOR to write. + WordAddr = (CurOffset & ~(0x3)) + GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, + Lba, BlockSize); + // The word of data that is to be written. + TmpBuf = *((UINT32*)(Buffer + (*NumBytes - BytesToWrite))); + + // First do word aligned chunks. + if ((CurOffset & 0x3) == 0) { + if (BytesToWrite >= 4) { + // Is the destination still in 'erased' state? + if (~Tmp != 0) { + // Check to see if we are only changing bits to zero. + if ((Tmp ^ TmpBuf) & TmpBuf) { + DoErase = TRUE; + break; + } + } + // Write this word to NOR + WordToWrite = TmpBuf; + CurOffset += sizeof(TmpBuf); + BytesToWrite -= sizeof(TmpBuf); + } else { + // BytesToWrite < 4. Do small writes and left-overs + Mask = ~((~0) << (BytesToWrite * 8)); + // Mask out the bytes we want. + TmpBuf &= Mask; + // Is the destination still in 'erased' state? + if ((Tmp & Mask) != Mask) { + // Check to see if we are only changing bits to zero. + if ((Tmp ^ TmpBuf) & TmpBuf) { + DoErase = TRUE; + break; + } + } + // Merge old and new data. Write merged word to NOR + WordToWrite = (Tmp & ~Mask) | TmpBuf; + CurOffset += BytesToWrite; + BytesToWrite = 0; + } + } else { + // Do multiple words, but starting unaligned. + if (BytesToWrite > (4 - (CurOffset & 0x3))) { + Mask = ((~0) << ((CurOffset & 0x3) * 8)); + // Mask out the bytes we want. + TmpBuf &= Mask; + // Is the destination still in 'erased' state? + if ((Tmp & Mask) != Mask) { + // Check to see if we are only changing bits to zero. + if ((Tmp ^ TmpBuf) & TmpBuf) { + DoErase = TRUE; + break; + } + } + // Merge old and new data. Write merged word to NOR + WordToWrite = (Tmp & ~Mask) | TmpBuf; + BytesToWrite -= (4 - (CurOffset & 0x3)); + CurOffset += (4 - (CurOffset & 0x3)); + } else { + // Unaligned and fits in one word. + Mask = (~((~0) << (BytesToWrite * 8))) << ((CurOffset & 0x3) * 8); + // Mask out the bytes we want. + TmpBuf = (TmpBuf << ((CurOffset & 0x3) * 8)) & Mask; + // Is the destination still in 'erased' state? + if ((Tmp & Mask) != Mask) { + // Check to see if we are only changing bits to zero. + if ((Tmp ^ TmpBuf) & TmpBuf) { + DoErase = TRUE; + break; + } + } + // Merge old and new data. Write merged word to NOR + WordToWrite = (Tmp & ~Mask) | TmpBuf; + CurOffset += BytesToWrite; + BytesToWrite = 0; + } + } + + // + // Write the word to NOR. + // + + BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize); + if (BlockAddress != PrevBlockAddress) { + TempStatus = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress); + if (EFI_ERROR (TempStatus)) { + return EFI_DEVICE_ERROR; + } + PrevBlockAddress = BlockAddress; + } + TempStatus = NorFlashWriteSingleWord (Instance, WordAddr, WordToWrite); + if (EFI_ERROR (TempStatus)) { + return EFI_DEVICE_ERROR; + } + } + // Exit if we got here and could write all the data. Otherwise do the + // Erase-Write cycle. + if (!DoErase) { + return EFI_SUCCESS; + } + } + + // Check we did get some memory. Buffer is BlockSize. + if (Instance->ShadowBuffer == NULL) { + DEBUG ((EFI_D_ERROR, "FvbWrite: ERROR - Buffer not ready\n")); + return EFI_DEVICE_ERROR; + } + + // Read NOR Flash data into shadow buffer + TempStatus = NorFlashReadBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer); + if (EFI_ERROR (TempStatus)) { + // 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 + TempStatus = NorFlashWriteBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer); + if (EFI_ERROR (TempStatus)) { + // Return one of the pre-approved error statuses + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/* + Although DiskIoDxe will automatically install the DiskIO protocol whenever + we install the BlockIO protocol, its implementation is sub-optimal as it reads + and writes entire blocks using the BlockIO protocol. In fact we can access + NOR flash with a finer granularity than that, so we can improve performance + by directly producing the DiskIO protocol. +*/ + +/** + Read BufferSize bytes from Offset into Buffer. + + @param This Protocol instance pointer. + @param MediaId Id of the media, changes every time the media is replaced. + @param Offset The starting byte offset to read from + @param BufferSize Size of Buffer + @param Buffer Buffer containing read data + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not + valid for the device. + +**/ +EFI_STATUS +EFIAPI +NorFlashDiskIoReadDisk ( + IN EFI_DISK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 DiskOffset, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + NOR_FLASH_INSTANCE *Instance; + UINT32 BlockSize; + UINT32 BlockOffset; + EFI_LBA Lba; + + Instance = INSTANCE_FROM_DISKIO_THIS(This); + + if (MediaId != Instance->Media.MediaId) { + return EFI_MEDIA_CHANGED; + } + + BlockSize = Instance->Media.BlockSize; + Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset); + + return NorFlashRead (Instance, Lba, BlockOffset, BufferSize, Buffer); +} + +/** + Writes a specified number of bytes to a device. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to be written. + @param Offset The starting byte offset on the logical block I/O device to write. + @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device. + @param Buffer A pointer to the buffer containing the data to be written. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not + valid for the device. + +**/ +EFI_STATUS +EFIAPI +NorFlashDiskIoWriteDisk ( + IN EFI_DISK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 DiskOffset, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + NOR_FLASH_INSTANCE *Instance; + UINT32 BlockSize; + UINT32 BlockOffset; + EFI_LBA Lba; + UINTN RemainingBytes; + UINTN WriteSize; + EFI_STATUS Status; + + Instance = INSTANCE_FROM_DISKIO_THIS(This); + + if (MediaId != Instance->Media.MediaId) { + return EFI_MEDIA_CHANGED; + } + + BlockSize = Instance->Media.BlockSize; + Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset); + + RemainingBytes = BufferSize; + + // Write either all the remaining bytes, or the number of bytes that bring + // us up to a block boundary, whichever is less. + // (DiskOffset | (BlockSize - 1)) + 1) rounds DiskOffset up to the next + // block boundary (even if it is already on one). + WriteSize = MIN (RemainingBytes, ((DiskOffset | (BlockSize - 1)) + 1) - DiskOffset); + + do { + if (WriteSize == BlockSize) { + // Write a full block + Status = NorFlashWriteFullBlock (Instance, Lba, Buffer, BlockSize / sizeof (UINT32)); + } else { + // Write a partial block + Status = NorFlashWriteSingleBlock (Instance, Lba, BlockOffset, &WriteSize, Buffer); + } + if (EFI_ERROR (Status)) { + return Status; + } + // Now continue writing either all the remaining bytes or single blocks. + RemainingBytes -= WriteSize; + Buffer = (UINT8 *) Buffer + WriteSize; + Lba++; + BlockOffset = 0; + WriteSize = MIN (RemainingBytes, BlockSize); + } while (RemainingBytes); + + return Status; +} + EFI_STATUS NorFlashReset ( IN NOR_FLASH_INSTANCE *Instance @@ -773,6 +1231,50 @@ NorFlashReset ( 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 BlockIo protocol + EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.FlushBlocks); + EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.ReadBlocks); + EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.Reset); + EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.WriteBlocks); + + // 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; +} + EFI_STATUS EFIAPI NorFlashInitialise ( @@ -783,7 +1285,6 @@ NorFlashInitialise ( EFI_STATUS Status; UINT32 Index; NOR_FLASH_DESCRIPTION* NorFlashDevices; - UINT32 NorFlashDeviceCount; BOOLEAN ContainVariableStorage; Status = NorFlashPlatformInitialization (); @@ -792,15 +1293,15 @@ NorFlashInitialise ( return Status; } - Status = NorFlashPlatformGetDevices (&NorFlashDevices,&NorFlashDeviceCount); + Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount); if (EFI_ERROR(Status)) { DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n")); return Status; } - mNorFlashInstances = AllocatePool (sizeof(NOR_FLASH_INSTANCE*) * NorFlashDeviceCount); + mNorFlashInstances = AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE*) * mNorFlashDeviceCount); - for (Index = 0; Index < NorFlashDeviceCount; Index++) { + for (Index = 0; Index < mNorFlashDeviceCount; Index++) { // Check if this NOR Flash device contain the variable storage region ContainVariableStorage = (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) && @@ -821,5 +1322,18 @@ NorFlashInitialise ( } } + // + // Register for the virtual address change event + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + NorFlashVirtualNotifyEvent, + NULL, + &gEfiEventVirtualAddressChangeGuid, + &mNorFlashVirtualAddrChangeEvent + ); + ASSERT_EFI_ERROR (Status); + return Status; }