1 /** @file NorFlashDxe.c
3 Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #include <Library/UefiLib.h>
16 #include <Library/BaseMemoryLib.h>
17 #include <Library/MemoryAllocationLib.h>
18 #include <Library/UefiBootServicesTableLib.h>
19 #include <Library/PcdLib.h>
21 #include "NorFlashDxe.h"
23 STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent
;
26 // Global variable declarations
28 NOR_FLASH_INSTANCE
**mNorFlashInstances
;
29 UINT32 mNorFlashDeviceCount
;
31 NOR_FLASH_INSTANCE mNorFlashInstanceTemplate
= {
32 NOR_FLASH_SIGNATURE
, // Signature
33 NULL
, // Handle ... NEED TO BE FILLED
38 0, // DeviceBaseAddress ... NEED TO BE FILLED
39 0, // RegionBaseAddress ... NEED TO BE FILLED
40 0, // Size ... NEED TO BE FILLED
44 EFI_BLOCK_IO_PROTOCOL_REVISION2
, // Revision
45 NULL
, // Media ... NEED TO BE FILLED
46 NorFlashBlockIoReset
, // Reset;
47 NorFlashBlockIoReadBlocks
, // ReadBlocks
48 NorFlashBlockIoWriteBlocks
, // WriteBlocks
49 NorFlashBlockIoFlushBlocks
// FlushBlocks
53 0, // MediaId ... NEED TO BE FILLED
54 FALSE
, // RemovableMedia
56 FALSE
, // LogicalPartition
58 FALSE
, // WriteCaching;
59 0, // BlockSize ... NEED TO BE FILLED
61 0, // LastBlock ... NEED TO BE FILLED
62 0, // LowestAlignedLba
63 1, // LogicalBlocksPerPhysicalBlock
67 EFI_DISK_IO_PROTOCOL_REVISION
, // Revision
68 NorFlashDiskIoReadDisk
, // ReadDisk
69 NorFlashDiskIoWriteDisk
// WriteDisk
72 FALSE
, // SupportFvb ... NEED TO BE FILLED
74 FvbGetAttributes
, // GetAttributes
75 FvbSetAttributes
, // SetAttributes
76 FvbGetPhysicalAddress
, // GetPhysicalAddress
77 FvbGetBlockSize
, // GetBlockSize
80 FvbEraseBlocks
, // EraseBlocks
89 { (UINT8
)sizeof(VENDOR_DEVICE_PATH
), (UINT8
)((sizeof(VENDOR_DEVICE_PATH
)) >> 8) }
91 { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }, // GUID ... NEED TO BE FILLED
95 END_ENTIRE_DEVICE_PATH_SUBTYPE
,
96 { sizeof (EFI_DEVICE_PATH_PROTOCOL
), 0 }
102 NorFlashCreateInstance (
103 IN UINTN NorFlashDeviceBase
,
104 IN UINTN NorFlashRegionBase
,
105 IN UINTN NorFlashSize
,
108 IN BOOLEAN SupportFvb
,
109 IN CONST GUID
*NorFlashGuid
,
110 OUT NOR_FLASH_INSTANCE
** NorFlashInstance
114 NOR_FLASH_INSTANCE
* Instance
;
116 ASSERT(NorFlashInstance
!= NULL
);
118 Instance
= AllocateRuntimeCopyPool (sizeof(NOR_FLASH_INSTANCE
),&mNorFlashInstanceTemplate
);
119 if (Instance
== NULL
) {
120 return EFI_OUT_OF_RESOURCES
;
123 Instance
->DeviceBaseAddress
= NorFlashDeviceBase
;
124 Instance
->RegionBaseAddress
= NorFlashRegionBase
;
125 Instance
->Size
= NorFlashSize
;
127 Instance
->BlockIoProtocol
.Media
= &Instance
->Media
;
128 Instance
->Media
.MediaId
= MediaId
;
129 Instance
->Media
.BlockSize
= BlockSize
;
130 Instance
->Media
.LastBlock
= (NorFlashSize
/ BlockSize
)-1;
132 CopyGuid (&Instance
->DevicePath
.Vendor
.Guid
, NorFlashGuid
);
134 Instance
->ShadowBuffer
= AllocateRuntimePool (BlockSize
);;
135 if (Instance
->ShadowBuffer
== NULL
) {
136 return EFI_OUT_OF_RESOURCES
;
140 Instance
->SupportFvb
= TRUE
;
141 Instance
->Initialize
= NorFlashFvbInitialize
;
143 Status
= gBS
->InstallMultipleProtocolInterfaces (
145 &gEfiDevicePathProtocolGuid
, &Instance
->DevicePath
,
146 &gEfiBlockIoProtocolGuid
, &Instance
->BlockIoProtocol
,
147 &gEfiFirmwareVolumeBlockProtocolGuid
, &Instance
->FvbProtocol
,
150 if (EFI_ERROR(Status
)) {
155 Instance
->Initialized
= TRUE
;
157 Status
= gBS
->InstallMultipleProtocolInterfaces (
159 &gEfiDevicePathProtocolGuid
, &Instance
->DevicePath
,
160 &gEfiBlockIoProtocolGuid
, &Instance
->BlockIoProtocol
,
161 &gEfiDiskIoProtocolGuid
, &Instance
->DiskIoProtocol
,
164 if (EFI_ERROR(Status
)) {
170 *NorFlashInstance
= Instance
;
175 NorFlashReadStatusRegister (
176 IN NOR_FLASH_INSTANCE
*Instance
,
180 // Prepare to read the status register
181 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_STATUS_REGISTER
);
182 return MmioRead32 (Instance
->DeviceBaseAddress
);
187 NorFlashBlockIsLocked (
188 IN NOR_FLASH_INSTANCE
*Instance
,
189 IN UINTN BlockAddress
193 BOOLEAN BlockIsLocked
;
195 BlockIsLocked
= TRUE
;
197 // Send command for reading device id
198 SEND_NOR_COMMAND (BlockAddress
, 2, P30_CMD_READ_DEVICE_ID
);
200 // Read block lock status
201 LockStatus
= MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress
, 2));
203 // Decode block lock status
204 LockStatus
= FOLD_32BIT_INTO_16BIT(LockStatus
);
206 if ((LockStatus
& 0x2) != 0) {
207 DEBUG((EFI_D_ERROR
, "NorFlashBlockIsLocked: WARNING: Block LOCKED DOWN\n"));
210 if ((LockStatus
& 0x1) == 0) {
211 // This means the block is unlocked
212 DEBUG((DEBUG_BLKIO
, "UnlockSingleBlock: Block 0x%08x unlocked\n", BlockAddress
));
213 BlockIsLocked
= FALSE
;
216 return BlockIsLocked
;
221 NorFlashUnlockSingleBlock (
222 IN NOR_FLASH_INSTANCE
*Instance
,
223 IN UINTN BlockAddress
226 EFI_STATUS Status
= EFI_SUCCESS
;
229 // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations
230 // and to protect shared data structures.
232 if (FeaturePcdGet (PcdNorFlashCheckBlockLocked
) == TRUE
) {
234 // Request a lock setup
235 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_LOCK_BLOCK_SETUP
);
238 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_UNLOCK_BLOCK
);
240 // Send command for reading device id
241 SEND_NOR_COMMAND (BlockAddress
, 2, P30_CMD_READ_DEVICE_ID
);
243 // Read block lock status
244 LockStatus
= MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress
, 2));
246 // Decode block lock status
247 LockStatus
= FOLD_32BIT_INTO_16BIT(LockStatus
);
248 } while ((LockStatus
& 0x1) == 1);
250 // Request a lock setup
251 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_LOCK_BLOCK_SETUP
);
254 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_UNLOCK_BLOCK
);
256 // Wait until the status register gives us the all clear
258 LockStatus
= NorFlashReadStatusRegister (Instance
, BlockAddress
);
259 } while ((LockStatus
& P30_SR_BIT_WRITE
) != P30_SR_BIT_WRITE
);
262 // Put device back into Read Array mode
263 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_READ_ARRAY
);
265 DEBUG((DEBUG_BLKIO
, "UnlockSingleBlock: BlockAddress=0x%08x, Exit Status = \"%r\".\n", BlockAddress
, Status
));
272 NorFlashUnlockSingleBlockIfNecessary (
273 IN NOR_FLASH_INSTANCE
*Instance
,
274 IN UINTN BlockAddress
277 EFI_STATUS Status
= EFI_SUCCESS
;
279 if (NorFlashBlockIsLocked (Instance
, BlockAddress
) == TRUE
) {
280 Status
= NorFlashUnlockSingleBlock (Instance
, BlockAddress
);
288 * The following function presumes that the block has already been unlocked.
291 NorFlashEraseSingleBlock (
292 IN NOR_FLASH_INSTANCE
*Instance
,
293 IN UINTN BlockAddress
297 UINT32 StatusRegister
;
299 Status
= EFI_SUCCESS
;
301 // Request a block erase and then confirm it
302 SEND_NOR_COMMAND(BlockAddress
, 0, P30_CMD_BLOCK_ERASE_SETUP
);
303 SEND_NOR_COMMAND(BlockAddress
, 0, P30_CMD_BLOCK_ERASE_CONFIRM
);
305 // Wait until the status register gives us the all clear
307 StatusRegister
= NorFlashReadStatusRegister (Instance
, BlockAddress
);
308 } while ((StatusRegister
& P30_SR_BIT_WRITE
) != P30_SR_BIT_WRITE
);
310 if (StatusRegister
& P30_SR_BIT_VPP
) {
311 DEBUG((EFI_D_ERROR
,"EraseSingleBlock(BlockAddress=0x%08x: VPP Range Error\n", BlockAddress
));
312 Status
= EFI_DEVICE_ERROR
;
315 if ((StatusRegister
& (P30_SR_BIT_ERASE
| P30_SR_BIT_PROGRAM
)) == (P30_SR_BIT_ERASE
| P30_SR_BIT_PROGRAM
)) {
316 DEBUG((EFI_D_ERROR
,"EraseSingleBlock(BlockAddress=0x%08x: Command Sequence Error\n", BlockAddress
));
317 Status
= EFI_DEVICE_ERROR
;
320 if (StatusRegister
& P30_SR_BIT_ERASE
) {
321 DEBUG((EFI_D_ERROR
,"EraseSingleBlock(BlockAddress=0x%08x: Block Erase Error StatusRegister:0x%X\n", BlockAddress
, StatusRegister
));
322 Status
= EFI_DEVICE_ERROR
;
325 if (StatusRegister
& P30_SR_BIT_BLOCK_LOCKED
) {
326 // The debug level message has been reduced because a device lock might happen. In this case we just retry it ...
327 DEBUG((EFI_D_INFO
,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error\n", BlockAddress
));
328 Status
= EFI_WRITE_PROTECTED
;
331 if (EFI_ERROR(Status
)) {
332 // Clear the Status Register
333 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_CLEAR_STATUS_REGISTER
);
336 // Put device back into Read Array mode
337 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
343 * The following function presumes that the block has already been unlocked.
346 NorFlashUnlockAndEraseSingleBlock (
347 IN NOR_FLASH_INSTANCE
*Instance
,
348 IN UINTN BlockAddress
355 if (!EfiAtRuntime ()) {
356 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
357 OriginalTPL
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
359 // This initialization is only to prevent the compiler to complain about the
360 // use of uninitialized variables
361 OriginalTPL
= TPL_HIGH_LEVEL
;
365 // The block erase might fail a first time (SW bug ?). Retry it ...
367 // Unlock the block if we have to
368 Status
= NorFlashUnlockSingleBlockIfNecessary (Instance
, BlockAddress
);
369 if (!EFI_ERROR(Status
)) {
370 Status
= NorFlashEraseSingleBlock (Instance
, BlockAddress
);
373 } while ((Index
< NOR_FLASH_ERASE_RETRY
) && (Status
== EFI_WRITE_PROTECTED
));
375 if (Index
== NOR_FLASH_ERASE_RETRY
) {
376 DEBUG((EFI_D_ERROR
,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress
,Index
));
379 if (!EfiAtRuntime ()) {
380 // Interruptions can resume.
381 gBS
->RestoreTPL (OriginalTPL
);
390 NorFlashWriteSingleWord (
391 IN NOR_FLASH_INSTANCE
*Instance
,
392 IN UINTN WordAddress
,
397 UINT32 StatusRegister
;
399 Status
= EFI_SUCCESS
;
401 // Request a write single word command
402 SEND_NOR_COMMAND(WordAddress
, 0, P30_CMD_WORD_PROGRAM_SETUP
);
404 // Store the word into NOR Flash;
405 MmioWrite32 (WordAddress
, WriteData
);
407 // Wait for the write to complete and then check for any errors; i.e. check the Status Register
409 // Prepare to read the status register
410 StatusRegister
= NorFlashReadStatusRegister (Instance
, WordAddress
);
411 // The chip is busy while the WRITE bit is not asserted
412 } while ((StatusRegister
& P30_SR_BIT_WRITE
) != P30_SR_BIT_WRITE
);
415 // Perform a full status check:
416 // Mask the relevant bits of Status Register.
417 // Everything should be zero, if not, we have a problem
419 if (StatusRegister
& P30_SR_BIT_VPP
) {
420 DEBUG((EFI_D_ERROR
,"NorFlashWriteSingleWord(WordAddress:0x%X): VPP Range Error\n",WordAddress
));
421 Status
= EFI_DEVICE_ERROR
;
424 if (StatusRegister
& P30_SR_BIT_PROGRAM
) {
425 DEBUG((EFI_D_ERROR
,"NorFlashWriteSingleWord(WordAddress:0x%X): Program Error\n",WordAddress
));
426 Status
= EFI_DEVICE_ERROR
;
429 if (StatusRegister
& P30_SR_BIT_BLOCK_LOCKED
) {
430 DEBUG((EFI_D_ERROR
,"NorFlashWriteSingleWord(WordAddress:0x%X): Device Protect Error\n",WordAddress
));
431 Status
= EFI_DEVICE_ERROR
;
434 if (!EFI_ERROR(Status
)) {
435 // Clear the Status Register
436 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_CLEAR_STATUS_REGISTER
);
439 // Put device back into Read Array mode
440 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
446 * Writes data to the NOR Flash using the Buffered Programming method.
448 * The maximum size of the on-chip buffer is 32-words, because of hardware restrictions.
449 * Therefore this function will only handle buffers up to 32 words or 128 bytes.
450 * To deal with larger buffers, call this function again.
452 * This function presumes that both the TargetAddress and the TargetAddress+BufferSize
453 * exist entirely within the NOR Flash. Therefore these conditions will not be checked here.
455 * In buffered programming, if the target address not at the beginning of a 32-bit word boundary,
456 * then programming time is doubled and power consumption is increased.
457 * Therefore, it is a requirement to align buffer writes to 32-bit word boundaries.
458 * i.e. the last 4 bits of the target start address must be zero: 0x......00
461 NorFlashWriteBuffer (
462 IN NOR_FLASH_INSTANCE
*Instance
,
463 IN UINTN TargetAddress
,
464 IN UINTN BufferSizeInBytes
,
469 UINTN BufferSizeInWords
;
471 volatile UINT32
*Data
;
473 BOOLEAN BufferAvailable
;
474 UINT32 StatusRegister
;
476 WaitForBuffer
= MAX_BUFFERED_PROG_ITERATIONS
;
477 BufferAvailable
= FALSE
;
479 // Check that the target address does not cross a 32-word boundary.
480 if ((TargetAddress
& BOUNDARY_OF_32_WORDS
) != 0) {
481 return EFI_INVALID_PARAMETER
;
484 // Check there are some data to program
485 if (BufferSizeInBytes
== 0) {
486 return EFI_BUFFER_TOO_SMALL
;
489 // Check that the buffer size does not exceed the maximum hardware buffer size on chip.
490 if (BufferSizeInBytes
> P30_MAX_BUFFER_SIZE_IN_BYTES
) {
491 return EFI_BAD_BUFFER_SIZE
;
494 // Check that the buffer size is a multiple of 32-bit words
495 if ((BufferSizeInBytes
% 4) != 0) {
496 return EFI_BAD_BUFFER_SIZE
;
499 // Pre-programming conditions checked, now start the algorithm.
501 // Prepare the data destination address
502 Data
= (UINT32
*)TargetAddress
;
504 // Check the availability of the buffer
506 // Issue the Buffered Program Setup command
507 SEND_NOR_COMMAND(TargetAddress
, 0, P30_CMD_BUFFERED_PROGRAM_SETUP
);
509 // Read back the status register bit#7 from the same address
510 if (((*Data
) & P30_SR_BIT_WRITE
) == P30_SR_BIT_WRITE
) {
511 BufferAvailable
= TRUE
;
514 // Update the loop counter
517 } while ((WaitForBuffer
> 0) && (BufferAvailable
== FALSE
));
519 // The buffer was not available for writing
520 if (WaitForBuffer
== 0) {
521 Status
= EFI_DEVICE_ERROR
;
525 // From now on we work in 32-bit words
526 BufferSizeInWords
= BufferSizeInBytes
/ (UINTN
)4;
528 // Write the word count, which is (buffer_size_in_words - 1),
529 // because word count 0 means one word.
530 SEND_NOR_COMMAND(TargetAddress
, 0, (BufferSizeInWords
- 1));
532 // Write the data to the NOR Flash, advancing each address by 4 bytes
533 for(Count
=0; Count
< BufferSizeInWords
; Count
++, Data
++, Buffer
++) {
537 // Issue the Buffered Program Confirm command, to start the programming operation
538 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_BUFFERED_PROGRAM_CONFIRM
);
540 // Wait for the write to complete and then check for any errors; i.e. check the Status Register
542 StatusRegister
= NorFlashReadStatusRegister (Instance
, TargetAddress
);
543 // The chip is busy while the WRITE bit is not asserted
544 } while ((StatusRegister
& P30_SR_BIT_WRITE
) != P30_SR_BIT_WRITE
);
547 // Perform a full status check:
548 // Mask the relevant bits of Status Register.
549 // Everything should be zero, if not, we have a problem
551 Status
= EFI_SUCCESS
;
553 if (StatusRegister
& P30_SR_BIT_VPP
) {
554 DEBUG((EFI_D_ERROR
,"NorFlashWriteBuffer(TargetAddress:0x%X): VPP Range Error\n", TargetAddress
));
555 Status
= EFI_DEVICE_ERROR
;
558 if (StatusRegister
& P30_SR_BIT_PROGRAM
) {
559 DEBUG((EFI_D_ERROR
,"NorFlashWriteBuffer(TargetAddress:0x%X): Program Error\n", TargetAddress
));
560 Status
= EFI_DEVICE_ERROR
;
563 if (StatusRegister
& P30_SR_BIT_BLOCK_LOCKED
) {
564 DEBUG((EFI_D_ERROR
,"NorFlashWriteBuffer(TargetAddress:0x%X): Device Protect Error\n",TargetAddress
));
565 Status
= EFI_DEVICE_ERROR
;
568 if (!EFI_ERROR(Status
)) {
569 // Clear the Status Register
570 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_CLEAR_STATUS_REGISTER
);
574 // Put device back into Read Array mode
575 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
582 NorFlashWriteFullBlock (
583 IN NOR_FLASH_INSTANCE
*Instance
,
585 IN UINT32
*DataBuffer
,
586 IN UINT32 BlockSizeInWords
594 UINTN BuffersInBlock
;
595 UINTN RemainingWords
;
599 Status
= EFI_SUCCESS
;
601 // Get the physical address of the block
602 BlockAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
, Lba
, BlockSizeInWords
* 4);
604 // Start writing from the first address at the start of the block
605 WordAddress
= BlockAddress
;
607 if (!EfiAtRuntime ()) {
608 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
609 OriginalTPL
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
611 // This initialization is only to prevent the compiler to complain about the
612 // use of uninitialized variables
613 OriginalTPL
= TPL_HIGH_LEVEL
;
616 Status
= NorFlashUnlockAndEraseSingleBlock (Instance
, BlockAddress
);
617 if (EFI_ERROR(Status
)) {
618 DEBUG((EFI_D_ERROR
, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress
));
622 // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
624 // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero
625 if ((WordAddress
& BOUNDARY_OF_32_WORDS
) == 0x00) {
627 // First, break the entire block into buffer-sized chunks.
628 BuffersInBlock
= (UINTN
)(BlockSizeInWords
* 4) / P30_MAX_BUFFER_SIZE_IN_BYTES
;
630 // Then feed each buffer chunk to the NOR Flash
631 // If a buffer does not contain any data, don't write it.
633 BufferIndex
< BuffersInBlock
;
634 BufferIndex
++, WordAddress
+= P30_MAX_BUFFER_SIZE_IN_BYTES
, DataBuffer
+= P30_MAX_BUFFER_SIZE_IN_WORDS
636 // Check the buffer to see if it contains any data (not set all 1s).
637 for (Cnt
= 0; Cnt
< P30_MAX_BUFFER_SIZE_IN_WORDS
; Cnt
++) {
638 if (~DataBuffer
[Cnt
] != 0 ) {
639 // Some data found, write the buffer.
640 Status
= NorFlashWriteBuffer (Instance
, WordAddress
, P30_MAX_BUFFER_SIZE_IN_BYTES
,
642 if (EFI_ERROR(Status
)) {
650 // Finally, finish off any remaining words that are less than the maximum size of the buffer
651 RemainingWords
= BlockSizeInWords
% P30_MAX_BUFFER_SIZE_IN_WORDS
;
653 if(RemainingWords
!= 0) {
654 Status
= NorFlashWriteBuffer (Instance
, WordAddress
, (RemainingWords
* 4), DataBuffer
);
655 if (EFI_ERROR(Status
)) {
661 // For now, use the single word programming algorithm
662 // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,
663 // i.e. which ends in the range 0x......01 - 0x......7F.
664 for(WordIndex
=0; WordIndex
<BlockSizeInWords
; WordIndex
++, DataBuffer
++, WordAddress
= WordAddress
+ 4) {
665 Status
= NorFlashWriteSingleWord (Instance
, WordAddress
, *DataBuffer
);
666 if (EFI_ERROR(Status
)) {
673 if (!EfiAtRuntime ()) {
674 // Interruptions can resume.
675 gBS
->RestoreTPL (OriginalTPL
);
678 if (EFI_ERROR(Status
)) {
679 DEBUG((EFI_D_ERROR
, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress
, Status
));
686 NorFlashWriteBlocks (
687 IN NOR_FLASH_INSTANCE
*Instance
,
689 IN UINTN BufferSizeInBytes
,
693 UINT32
*pWriteBuffer
;
694 EFI_STATUS Status
= EFI_SUCCESS
;
695 EFI_LBA CurrentBlock
;
696 UINT32 BlockSizeInWords
;
700 // The buffer must be valid
701 if (Buffer
== NULL
) {
702 return EFI_INVALID_PARAMETER
;
705 if(Instance
->Media
.ReadOnly
== TRUE
) {
706 return EFI_WRITE_PROTECTED
;
709 // We must have some bytes to read
710 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes
));
711 if(BufferSizeInBytes
== 0) {
712 return EFI_BAD_BUFFER_SIZE
;
715 // The size of the buffer must be a multiple of the block size
716 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance
->Media
.BlockSize
));
717 if ((BufferSizeInBytes
% Instance
->Media
.BlockSize
) != 0) {
718 return EFI_BAD_BUFFER_SIZE
;
721 // All blocks must be within the device
722 NumBlocks
= ((UINT32
)BufferSizeInBytes
) / Instance
->Media
.BlockSize
;
724 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks
, Instance
->Media
.LastBlock
, Lba
));
726 if ((Lba
+ NumBlocks
) > (Instance
->Media
.LastBlock
+ 1)) {
727 DEBUG((EFI_D_ERROR
, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n"));
728 return EFI_INVALID_PARAMETER
;
731 BlockSizeInWords
= Instance
->Media
.BlockSize
/ 4;
733 // Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer
734 // to a proper data type, so use *ReadBuffer
735 pWriteBuffer
= (UINT32
*)Buffer
;
738 for (BlockCount
=0; BlockCount
< NumBlocks
; BlockCount
++, CurrentBlock
++, pWriteBuffer
= pWriteBuffer
+ BlockSizeInWords
) {
740 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN
)CurrentBlock
));
742 Status
= NorFlashWriteFullBlock (Instance
, CurrentBlock
, pWriteBuffer
, BlockSizeInWords
);
744 if (EFI_ERROR(Status
)) {
749 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status
));
755 IN NOR_FLASH_INSTANCE
*Instance
,
757 IN UINTN BufferSizeInBytes
,
764 DEBUG((DEBUG_BLKIO
, "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n",
765 BufferSizeInBytes
, Instance
->Media
.BlockSize
, Instance
->Media
.LastBlock
, Lba
));
767 // The buffer must be valid
768 if (Buffer
== NULL
) {
769 return EFI_INVALID_PARAMETER
;
772 // Return if we have not any byte to read
773 if (BufferSizeInBytes
== 0) {
777 // The size of the buffer must be a multiple of the block size
778 if ((BufferSizeInBytes
% Instance
->Media
.BlockSize
) != 0) {
779 return EFI_BAD_BUFFER_SIZE
;
782 // All blocks must be within the device
783 NumBlocks
= ((UINT32
)BufferSizeInBytes
) / Instance
->Media
.BlockSize
;
785 if ((Lba
+ NumBlocks
) > (Instance
->Media
.LastBlock
+ 1)) {
786 DEBUG((EFI_D_ERROR
, "NorFlashReadBlocks: ERROR - Read will exceed last block\n"));
787 return EFI_INVALID_PARAMETER
;
790 // Get the address to start reading from
791 StartAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
,
793 Instance
->Media
.BlockSize
796 // Put the device into Read Array mode
797 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
800 CopyMem(Buffer
, (UINTN
*)StartAddress
, BufferSizeInBytes
);
807 IN NOR_FLASH_INSTANCE
*Instance
,
810 IN UINTN BufferSizeInBytes
,
817 // The buffer must be valid
818 if (Buffer
== NULL
) {
819 return EFI_INVALID_PARAMETER
;
822 // Return if we have not any byte to read
823 if (BufferSizeInBytes
== 0) {
827 // All blocks must be within the device
828 NumBlocks
= ((UINT32
)BufferSizeInBytes
) / Instance
->Media
.BlockSize
;
830 if ((Lba
+ NumBlocks
) > (Instance
->Media
.LastBlock
+ 1)) {
831 DEBUG ((EFI_D_ERROR
, "NorFlashRead: ERROR - Read will exceed last block\n"));
832 return EFI_INVALID_PARAMETER
;
835 if (Offset
+ BufferSizeInBytes
>= Instance
->Size
) {
836 DEBUG ((EFI_D_ERROR
, "NorFlashRead: ERROR - Read will exceed device size.\n"));
837 return EFI_INVALID_PARAMETER
;
840 // Get the address to start reading from
841 StartAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
,
843 Instance
->Media
.BlockSize
846 // Put the device into Read Array mode
847 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
850 CopyMem (Buffer
, (UINTN
*)(StartAddress
+ Offset
), BufferSizeInBytes
);
856 Write a full or portion of a block. It must not span block boundaries; that is,
857 Offset + *NumBytes <= Instance->Media.BlockSize.
860 NorFlashWriteSingleBlock (
861 IN NOR_FLASH_INSTANCE
*Instance
,
864 IN OUT UINTN
*NumBytes
,
868 EFI_STATUS TempStatus
;
879 UINTN PrevBlockAddress
;
881 PrevBlockAddress
= 0;
883 if (!Instance
->Initialized
&& Instance
->Initialize
) {
884 Instance
->Initialize(Instance
);
887 DEBUG ((DEBUG_BLKIO
, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba
, Offset
, *NumBytes
, Buffer
));
889 // Detect WriteDisabled state
890 if (Instance
->Media
.ReadOnly
== TRUE
) {
891 DEBUG ((EFI_D_ERROR
, "NorFlashWriteSingleBlock: ERROR - Can not write: Device is in WriteDisabled state.\n"));
892 // It is in WriteDisabled state, return an error right away
893 return EFI_ACCESS_DENIED
;
896 // Cache the block size to avoid de-referencing pointers all the time
897 BlockSize
= Instance
->Media
.BlockSize
;
899 // The write must not span block boundaries.
900 // We need to check each variable individually because adding two large values together overflows.
901 if ( ( Offset
>= BlockSize
) ||
902 ( *NumBytes
> BlockSize
) ||
903 ( (Offset
+ *NumBytes
) > BlockSize
) ) {
904 DEBUG ((EFI_D_ERROR
, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset
, *NumBytes
, BlockSize
));
905 return EFI_BAD_BUFFER_SIZE
;
908 // We must have some bytes to write
909 if (*NumBytes
== 0) {
910 DEBUG ((EFI_D_ERROR
, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset
, *NumBytes
, BlockSize
));
911 return EFI_BAD_BUFFER_SIZE
;
914 // Pick 128bytes as a good start for word operations as opposed to erasing the
915 // block and writing the data regardless if an erase is really needed.
916 // It looks like most individual NV variable writes are smaller than 128bytes.
917 if (*NumBytes
<= 128) {
918 // Check to see if we need to erase before programming the data into NOR.
919 // If the destination bits are only changing from 1s to 0s we can just write.
920 // After a block is erased all bits in the block is set to 1.
921 // If any byte requires us to erase we just give up and rewrite all of it.
923 BytesToWrite
= *NumBytes
;
926 while (BytesToWrite
> 0) {
927 // Read full word from NOR, splice as required. A word is the smallest
928 // unit we can write.
929 TempStatus
= NorFlashRead (Instance
, Lba
, CurOffset
& ~(0x3), sizeof(Tmp
), &Tmp
);
930 if (EFI_ERROR (TempStatus
)) {
931 return EFI_DEVICE_ERROR
;
934 // Physical address of word in NOR to write.
935 WordAddr
= (CurOffset
& ~(0x3)) + GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
,
937 // The word of data that is to be written.
938 TmpBuf
= *((UINT32
*)(Buffer
+ (*NumBytes
- BytesToWrite
)));
940 // First do word aligned chunks.
941 if ((CurOffset
& 0x3) == 0) {
942 if (BytesToWrite
>= 4) {
943 // Is the destination still in 'erased' state?
945 // Check to see if we are only changing bits to zero.
946 if ((Tmp
^ TmpBuf
) & TmpBuf
) {
951 // Write this word to NOR
952 WordToWrite
= TmpBuf
;
953 CurOffset
+= sizeof(TmpBuf
);
954 BytesToWrite
-= sizeof(TmpBuf
);
956 // BytesToWrite < 4. Do small writes and left-overs
957 Mask
= ~((~0) << (BytesToWrite
* 8));
958 // Mask out the bytes we want.
960 // Is the destination still in 'erased' state?
961 if ((Tmp
& Mask
) != Mask
) {
962 // Check to see if we are only changing bits to zero.
963 if ((Tmp
^ TmpBuf
) & TmpBuf
) {
968 // Merge old and new data. Write merged word to NOR
969 WordToWrite
= (Tmp
& ~Mask
) | TmpBuf
;
970 CurOffset
+= BytesToWrite
;
974 // Do multiple words, but starting unaligned.
975 if (BytesToWrite
> (4 - (CurOffset
& 0x3))) {
976 Mask
= ((~0) << ((CurOffset
& 0x3) * 8));
977 // Mask out the bytes we want.
979 // Is the destination still in 'erased' state?
980 if ((Tmp
& Mask
) != Mask
) {
981 // Check to see if we are only changing bits to zero.
982 if ((Tmp
^ TmpBuf
) & TmpBuf
) {
987 // Merge old and new data. Write merged word to NOR
988 WordToWrite
= (Tmp
& ~Mask
) | TmpBuf
;
989 BytesToWrite
-= (4 - (CurOffset
& 0x3));
990 CurOffset
+= (4 - (CurOffset
& 0x3));
992 // Unaligned and fits in one word.
993 Mask
= (~((~0) << (BytesToWrite
* 8))) << ((CurOffset
& 0x3) * 8);
994 // Mask out the bytes we want.
995 TmpBuf
= (TmpBuf
<< ((CurOffset
& 0x3) * 8)) & Mask
;
996 // Is the destination still in 'erased' state?
997 if ((Tmp
& Mask
) != Mask
) {
998 // Check to see if we are only changing bits to zero.
999 if ((Tmp
^ TmpBuf
) & TmpBuf
) {
1004 // Merge old and new data. Write merged word to NOR
1005 WordToWrite
= (Tmp
& ~Mask
) | TmpBuf
;
1006 CurOffset
+= BytesToWrite
;
1012 // Write the word to NOR.
1015 BlockAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
, Lba
, BlockSize
);
1016 if (BlockAddress
!= PrevBlockAddress
) {
1017 TempStatus
= NorFlashUnlockSingleBlockIfNecessary (Instance
, BlockAddress
);
1018 if (EFI_ERROR (TempStatus
)) {
1019 return EFI_DEVICE_ERROR
;
1021 PrevBlockAddress
= BlockAddress
;
1023 TempStatus
= NorFlashWriteSingleWord (Instance
, WordAddr
, WordToWrite
);
1024 if (EFI_ERROR (TempStatus
)) {
1025 return EFI_DEVICE_ERROR
;
1028 // Exit if we got here and could write all the data. Otherwise do the
1029 // Erase-Write cycle.
1035 // Check we did get some memory. Buffer is BlockSize.
1036 if (Instance
->ShadowBuffer
== NULL
) {
1037 DEBUG ((EFI_D_ERROR
, "FvbWrite: ERROR - Buffer not ready\n"));
1038 return EFI_DEVICE_ERROR
;
1041 // Read NOR Flash data into shadow buffer
1042 TempStatus
= NorFlashReadBlocks (Instance
, Lba
, BlockSize
, Instance
->ShadowBuffer
);
1043 if (EFI_ERROR (TempStatus
)) {
1044 // Return one of the pre-approved error statuses
1045 return EFI_DEVICE_ERROR
;
1048 // Put the data at the appropriate location inside the buffer area
1049 CopyMem ((VOID
*)((UINTN
)Instance
->ShadowBuffer
+ Offset
), Buffer
, *NumBytes
);
1051 // Write the modified buffer back to the NorFlash
1052 TempStatus
= NorFlashWriteBlocks (Instance
, Lba
, BlockSize
, Instance
->ShadowBuffer
);
1053 if (EFI_ERROR (TempStatus
)) {
1054 // Return one of the pre-approved error statuses
1055 return EFI_DEVICE_ERROR
;
1062 Although DiskIoDxe will automatically install the DiskIO protocol whenever
1063 we install the BlockIO protocol, its implementation is sub-optimal as it reads
1064 and writes entire blocks using the BlockIO protocol. In fact we can access
1065 NOR flash with a finer granularity than that, so we can improve performance
1066 by directly producing the DiskIO protocol.
1070 Read BufferSize bytes from Offset into Buffer.
1072 @param This Protocol instance pointer.
1073 @param MediaId Id of the media, changes every time the media is replaced.
1074 @param Offset The starting byte offset to read from
1075 @param BufferSize Size of Buffer
1076 @param Buffer Buffer containing read data
1078 @retval EFI_SUCCESS The data was read correctly from the device.
1079 @retval EFI_DEVICE_ERROR The device reported an error while performing the read.
1080 @retval EFI_NO_MEDIA There is no media in the device.
1081 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
1082 @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not
1083 valid for the device.
1088 NorFlashDiskIoReadDisk (
1089 IN EFI_DISK_IO_PROTOCOL
*This
,
1091 IN UINT64 DiskOffset
,
1092 IN UINTN BufferSize
,
1096 NOR_FLASH_INSTANCE
*Instance
;
1101 Instance
= INSTANCE_FROM_DISKIO_THIS(This
);
1103 if (MediaId
!= Instance
->Media
.MediaId
) {
1104 return EFI_MEDIA_CHANGED
;
1107 BlockSize
= Instance
->Media
.BlockSize
;
1108 Lba
= (EFI_LBA
) DivU64x32Remainder (DiskOffset
, BlockSize
, &BlockOffset
);
1110 return NorFlashRead (Instance
, Lba
, BlockOffset
, BufferSize
, Buffer
);
1114 Writes a specified number of bytes to a device.
1116 @param This Indicates a pointer to the calling context.
1117 @param MediaId ID of the medium to be written.
1118 @param Offset The starting byte offset on the logical block I/O device to write.
1119 @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device.
1120 @param Buffer A pointer to the buffer containing the data to be written.
1122 @retval EFI_SUCCESS The data was written correctly to the device.
1123 @retval EFI_WRITE_PROTECTED The device can not be written to.
1124 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
1125 @retval EFI_NO_MEDIA There is no media in the device.
1126 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
1127 @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not
1128 valid for the device.
1133 NorFlashDiskIoWriteDisk (
1134 IN EFI_DISK_IO_PROTOCOL
*This
,
1136 IN UINT64 DiskOffset
,
1137 IN UINTN BufferSize
,
1141 NOR_FLASH_INSTANCE
*Instance
;
1145 UINTN RemainingBytes
;
1149 Instance
= INSTANCE_FROM_DISKIO_THIS(This
);
1151 if (MediaId
!= Instance
->Media
.MediaId
) {
1152 return EFI_MEDIA_CHANGED
;
1155 BlockSize
= Instance
->Media
.BlockSize
;
1156 Lba
= (EFI_LBA
) DivU64x32Remainder (DiskOffset
, BlockSize
, &BlockOffset
);
1158 RemainingBytes
= BufferSize
;
1160 // Write either all the remaining bytes, or the number of bytes that bring
1161 // us up to a block boundary, whichever is less.
1162 // (DiskOffset | (BlockSize - 1)) + 1) rounds DiskOffset up to the next
1163 // block boundary (even if it is already on one).
1164 WriteSize
= MIN (RemainingBytes
, ((DiskOffset
| (BlockSize
- 1)) + 1) - DiskOffset
);
1167 if (WriteSize
== BlockSize
) {
1168 // Write a full block
1169 Status
= NorFlashWriteFullBlock (Instance
, Lba
, Buffer
, BlockSize
/ sizeof (UINT32
));
1171 // Write a partial block
1172 Status
= NorFlashWriteSingleBlock (Instance
, Lba
, BlockOffset
, &WriteSize
, Buffer
);
1174 if (EFI_ERROR (Status
)) {
1177 // Now continue writing either all the remaining bytes or single blocks.
1178 RemainingBytes
-= WriteSize
;
1179 Buffer
= (UINT8
*) Buffer
+ WriteSize
;
1182 WriteSize
= MIN (RemainingBytes
, BlockSize
);
1183 } while (RemainingBytes
);
1190 IN NOR_FLASH_INSTANCE
*Instance
1193 // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode
1194 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
1199 Fixup internal data so that EFI can be call in virtual mode.
1200 Call the passed in Child Notify event and convert any pointers in
1201 lib to virtual mode.
1203 @param[in] Event The Event that is being processed
1204 @param[in] Context Event Context
1208 NorFlashVirtualNotifyEvent (
1215 for (Index
= 0; Index
< mNorFlashDeviceCount
; Index
++) {
1216 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->DeviceBaseAddress
);
1217 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->RegionBaseAddress
);
1219 // Convert BlockIo protocol
1220 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->BlockIoProtocol
.FlushBlocks
);
1221 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->BlockIoProtocol
.ReadBlocks
);
1222 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->BlockIoProtocol
.Reset
);
1223 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->BlockIoProtocol
.WriteBlocks
);
1226 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.EraseBlocks
);
1227 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.GetAttributes
);
1228 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.GetBlockSize
);
1229 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.GetPhysicalAddress
);
1230 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.Read
);
1231 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.SetAttributes
);
1232 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.Write
);
1234 if (mNorFlashInstances
[Index
]->ShadowBuffer
!= NULL
) {
1235 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->ShadowBuffer
);
1244 NorFlashInitialise (
1245 IN EFI_HANDLE ImageHandle
,
1246 IN EFI_SYSTEM_TABLE
*SystemTable
1251 NOR_FLASH_DESCRIPTION
* NorFlashDevices
;
1252 BOOLEAN ContainVariableStorage
;
1254 Status
= NorFlashPlatformInitialization ();
1255 if (EFI_ERROR(Status
)) {
1256 DEBUG((EFI_D_ERROR
,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
1260 Status
= NorFlashPlatformGetDevices (&NorFlashDevices
, &mNorFlashDeviceCount
);
1261 if (EFI_ERROR(Status
)) {
1262 DEBUG((EFI_D_ERROR
,"NorFlashInitialise: Fail to get Nor Flash devices\n"));
1266 mNorFlashInstances
= AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE
*) * mNorFlashDeviceCount
);
1268 for (Index
= 0; Index
< mNorFlashDeviceCount
; Index
++) {
1269 // Check if this NOR Flash device contain the variable storage region
1270 ContainVariableStorage
=
1271 (NorFlashDevices
[Index
].RegionBaseAddress
<= PcdGet32 (PcdFlashNvStorageVariableBase
)) &&
1272 (PcdGet32 (PcdFlashNvStorageVariableBase
) + PcdGet32 (PcdFlashNvStorageVariableSize
) <= NorFlashDevices
[Index
].RegionBaseAddress
+ NorFlashDevices
[Index
].Size
);
1274 Status
= NorFlashCreateInstance (
1275 NorFlashDevices
[Index
].DeviceBaseAddress
,
1276 NorFlashDevices
[Index
].RegionBaseAddress
,
1277 NorFlashDevices
[Index
].Size
,
1279 NorFlashDevices
[Index
].BlockSize
,
1280 ContainVariableStorage
,
1281 &NorFlashDevices
[Index
].Guid
,
1282 &mNorFlashInstances
[Index
]
1284 if (EFI_ERROR(Status
)) {
1285 DEBUG((EFI_D_ERROR
,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index
));
1290 // Register for the virtual address change event
1292 Status
= gBS
->CreateEventEx (
1295 NorFlashVirtualNotifyEvent
,
1297 &gEfiEventVirtualAddressChangeGuid
,
1298 &mNorFlashVirtualAddrChangeEvent
1300 ASSERT_EFI_ERROR (Status
);