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
) ),
90 (UINT8
)((sizeof(VENDOR_DEVICE_PATH
)) >> 8),
92 { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, // GUID ... NEED TO BE FILLED
96 END_ENTIRE_DEVICE_PATH_SUBTYPE
,
97 sizeof (EFI_DEVICE_PATH_PROTOCOL
),
104 NorFlashCreateInstance (
105 IN UINTN NorFlashDeviceBase
,
106 IN UINTN NorFlashRegionBase
,
107 IN UINTN NorFlashSize
,
110 IN BOOLEAN SupportFvb
,
111 IN CONST GUID
*NorFlashGuid
,
112 OUT NOR_FLASH_INSTANCE
** NorFlashInstance
116 NOR_FLASH_INSTANCE
* Instance
;
118 ASSERT(NorFlashInstance
!= NULL
);
120 Instance
= AllocateRuntimeCopyPool (sizeof(NOR_FLASH_INSTANCE
),&mNorFlashInstanceTemplate
);
121 if (Instance
== NULL
) {
122 return EFI_OUT_OF_RESOURCES
;
125 Instance
->DeviceBaseAddress
= NorFlashDeviceBase
;
126 Instance
->RegionBaseAddress
= NorFlashRegionBase
;
127 Instance
->Size
= NorFlashSize
;
129 Instance
->BlockIoProtocol
.Media
= &Instance
->Media
;
130 Instance
->Media
.MediaId
= MediaId
;
131 Instance
->Media
.BlockSize
= BlockSize
;
132 Instance
->Media
.LastBlock
= (NorFlashSize
/ BlockSize
)-1;
134 CopyGuid (&Instance
->DevicePath
.Vendor
.Guid
, NorFlashGuid
);
136 Instance
->ShadowBuffer
= AllocateRuntimePool (BlockSize
);;
137 if (Instance
->ShadowBuffer
== NULL
) {
138 return EFI_OUT_OF_RESOURCES
;
142 Instance
->SupportFvb
= TRUE
;
143 Instance
->Initialize
= NorFlashFvbInitialize
;
145 Status
= gBS
->InstallMultipleProtocolInterfaces (
147 &gEfiDevicePathProtocolGuid
, &Instance
->DevicePath
,
148 &gEfiBlockIoProtocolGuid
, &Instance
->BlockIoProtocol
,
149 &gEfiFirmwareVolumeBlockProtocolGuid
, &Instance
->FvbProtocol
,
152 if (EFI_ERROR(Status
)) {
157 Instance
->Initialized
= TRUE
;
159 Status
= gBS
->InstallMultipleProtocolInterfaces (
161 &gEfiDevicePathProtocolGuid
, &Instance
->DevicePath
,
162 &gEfiBlockIoProtocolGuid
, &Instance
->BlockIoProtocol
,
163 &gEfiDiskIoProtocolGuid
, &Instance
->DiskIoProtocol
,
166 if (EFI_ERROR(Status
)) {
172 *NorFlashInstance
= Instance
;
177 NorFlashReadStatusRegister (
178 IN NOR_FLASH_INSTANCE
*Instance
,
182 // Prepare to read the status register
183 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_STATUS_REGISTER
);
184 return MmioRead32 (Instance
->DeviceBaseAddress
);
189 NorFlashBlockIsLocked (
190 IN NOR_FLASH_INSTANCE
*Instance
,
191 IN UINTN BlockAddress
195 BOOLEAN BlockIsLocked
;
197 BlockIsLocked
= TRUE
;
199 // Send command for reading device id
200 SEND_NOR_COMMAND (BlockAddress
, 2, P30_CMD_READ_DEVICE_ID
);
202 // Read block lock status
203 LockStatus
= MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress
, 2));
205 // Decode block lock status
206 LockStatus
= FOLD_32BIT_INTO_16BIT(LockStatus
);
208 if ((LockStatus
& 0x2) != 0) {
209 DEBUG((EFI_D_ERROR
, "NorFlashBlockIsLocked: WARNING: Block LOCKED DOWN\n"));
212 if ((LockStatus
& 0x1) == 0) {
213 // This means the block is unlocked
214 DEBUG((DEBUG_BLKIO
, "UnlockSingleBlock: Block 0x%08x unlocked\n", BlockAddress
));
215 BlockIsLocked
= FALSE
;
218 return BlockIsLocked
;
223 NorFlashUnlockSingleBlock (
224 IN NOR_FLASH_INSTANCE
*Instance
,
225 IN UINTN BlockAddress
228 EFI_STATUS Status
= EFI_SUCCESS
;
231 // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations
232 // and to protect shared data structures.
234 if (FeaturePcdGet (PcdNorFlashCheckBlockLocked
) == TRUE
) {
236 // Request a lock setup
237 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_LOCK_BLOCK_SETUP
);
240 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_UNLOCK_BLOCK
);
242 // Send command for reading device id
243 SEND_NOR_COMMAND (BlockAddress
, 2, P30_CMD_READ_DEVICE_ID
);
245 // Read block lock status
246 LockStatus
= MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress
, 2));
248 // Decode block lock status
249 LockStatus
= FOLD_32BIT_INTO_16BIT(LockStatus
);
250 } while ((LockStatus
& 0x1) == 1);
252 // Request a lock setup
253 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_LOCK_BLOCK_SETUP
);
256 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_UNLOCK_BLOCK
);
258 // Wait until the status register gives us the all clear
260 LockStatus
= NorFlashReadStatusRegister (Instance
, BlockAddress
);
261 } while ((LockStatus
& P30_SR_BIT_WRITE
) != P30_SR_BIT_WRITE
);
264 // Put device back into Read Array mode
265 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_READ_ARRAY
);
267 DEBUG((DEBUG_BLKIO
, "UnlockSingleBlock: BlockAddress=0x%08x, Exit Status = \"%r\".\n", BlockAddress
, Status
));
274 NorFlashUnlockSingleBlockIfNecessary (
275 IN NOR_FLASH_INSTANCE
*Instance
,
276 IN UINTN BlockAddress
279 EFI_STATUS Status
= EFI_SUCCESS
;
281 if (NorFlashBlockIsLocked (Instance
, BlockAddress
) == TRUE
) {
282 Status
= NorFlashUnlockSingleBlock (Instance
, BlockAddress
);
290 * The following function presumes that the block has already been unlocked.
293 NorFlashEraseSingleBlock (
294 IN NOR_FLASH_INSTANCE
*Instance
,
295 IN UINTN BlockAddress
299 UINT32 StatusRegister
;
301 Status
= EFI_SUCCESS
;
303 // Request a block erase and then confirm it
304 SEND_NOR_COMMAND(BlockAddress
, 0, P30_CMD_BLOCK_ERASE_SETUP
);
305 SEND_NOR_COMMAND(BlockAddress
, 0, P30_CMD_BLOCK_ERASE_CONFIRM
);
307 // Wait until the status register gives us the all clear
309 StatusRegister
= NorFlashReadStatusRegister (Instance
, BlockAddress
);
310 } while ((StatusRegister
& P30_SR_BIT_WRITE
) != P30_SR_BIT_WRITE
);
312 if (StatusRegister
& P30_SR_BIT_VPP
) {
313 DEBUG((EFI_D_ERROR
,"EraseSingleBlock(BlockAddress=0x%08x: VPP Range Error\n", BlockAddress
));
314 Status
= EFI_DEVICE_ERROR
;
317 if ((StatusRegister
& (P30_SR_BIT_ERASE
| P30_SR_BIT_PROGRAM
)) == (P30_SR_BIT_ERASE
| P30_SR_BIT_PROGRAM
)) {
318 DEBUG((EFI_D_ERROR
,"EraseSingleBlock(BlockAddress=0x%08x: Command Sequence Error\n", BlockAddress
));
319 Status
= EFI_DEVICE_ERROR
;
322 if (StatusRegister
& P30_SR_BIT_ERASE
) {
323 DEBUG((EFI_D_ERROR
,"EraseSingleBlock(BlockAddress=0x%08x: Block Erase Error StatusRegister:0x%X\n", BlockAddress
, StatusRegister
));
324 Status
= EFI_DEVICE_ERROR
;
327 if (StatusRegister
& P30_SR_BIT_BLOCK_LOCKED
) {
328 // The debug level message has been reduced because a device lock might happen. In this case we just retry it ...
329 DEBUG((EFI_D_INFO
,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error\n", BlockAddress
));
330 Status
= EFI_WRITE_PROTECTED
;
333 if (EFI_ERROR(Status
)) {
334 // Clear the Status Register
335 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_CLEAR_STATUS_REGISTER
);
338 // Put device back into Read Array mode
339 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
345 * The following function presumes that the block has already been unlocked.
348 NorFlashUnlockAndEraseSingleBlock (
349 IN NOR_FLASH_INSTANCE
*Instance
,
350 IN UINTN BlockAddress
357 if (!EfiAtRuntime ()) {
358 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
359 OriginalTPL
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
361 // This initialization is only to prevent the compiler to complain about the
362 // use of uninitialized variables
363 OriginalTPL
= TPL_HIGH_LEVEL
;
367 // The block erase might fail a first time (SW bug ?). Retry it ...
369 // Unlock the block if we have to
370 Status
= NorFlashUnlockSingleBlockIfNecessary (Instance
, BlockAddress
);
371 if (!EFI_ERROR(Status
)) {
372 Status
= NorFlashEraseSingleBlock (Instance
, BlockAddress
);
375 } while ((Index
< NOR_FLASH_ERASE_RETRY
) && (Status
== EFI_WRITE_PROTECTED
));
377 if (Index
== NOR_FLASH_ERASE_RETRY
) {
378 DEBUG((EFI_D_ERROR
,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress
,Index
));
381 if (!EfiAtRuntime ()) {
382 // Interruptions can resume.
383 gBS
->RestoreTPL (OriginalTPL
);
392 NorFlashWriteSingleWord (
393 IN NOR_FLASH_INSTANCE
*Instance
,
394 IN UINTN WordAddress
,
399 UINT32 StatusRegister
;
401 Status
= EFI_SUCCESS
;
403 // Request a write single word command
404 SEND_NOR_COMMAND(WordAddress
, 0, P30_CMD_WORD_PROGRAM_SETUP
);
406 // Store the word into NOR Flash;
407 MmioWrite32 (WordAddress
, WriteData
);
409 // Wait for the write to complete and then check for any errors; i.e. check the Status Register
411 // Prepare to read the status register
412 StatusRegister
= NorFlashReadStatusRegister (Instance
, WordAddress
);
413 // The chip is busy while the WRITE bit is not asserted
414 } while ((StatusRegister
& P30_SR_BIT_WRITE
) != P30_SR_BIT_WRITE
);
417 // Perform a full status check:
418 // Mask the relevant bits of Status Register.
419 // Everything should be zero, if not, we have a problem
421 if (StatusRegister
& P30_SR_BIT_VPP
) {
422 DEBUG((EFI_D_ERROR
,"NorFlashWriteSingleWord(WordAddress:0x%X): VPP Range Error\n",WordAddress
));
423 Status
= EFI_DEVICE_ERROR
;
426 if (StatusRegister
& P30_SR_BIT_PROGRAM
) {
427 DEBUG((EFI_D_ERROR
,"NorFlashWriteSingleWord(WordAddress:0x%X): Program Error\n",WordAddress
));
428 Status
= EFI_DEVICE_ERROR
;
431 if (StatusRegister
& P30_SR_BIT_BLOCK_LOCKED
) {
432 DEBUG((EFI_D_ERROR
,"NorFlashWriteSingleWord(WordAddress:0x%X): Device Protect Error\n",WordAddress
));
433 Status
= EFI_DEVICE_ERROR
;
436 if (!EFI_ERROR(Status
)) {
437 // Clear the Status Register
438 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_CLEAR_STATUS_REGISTER
);
441 // Put device back into Read Array mode
442 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
448 * Writes data to the NOR Flash using the Buffered Programming method.
450 * The maximum size of the on-chip buffer is 32-words, because of hardware restrictions.
451 * Therefore this function will only handle buffers up to 32 words or 128 bytes.
452 * To deal with larger buffers, call this function again.
454 * This function presumes that both the TargetAddress and the TargetAddress+BufferSize
455 * exist entirely within the NOR Flash. Therefore these conditions will not be checked here.
457 * In buffered programming, if the target address not at the beginning of a 32-bit word boundary,
458 * then programming time is doubled and power consumption is increased.
459 * Therefore, it is a requirement to align buffer writes to 32-bit word boundaries.
460 * i.e. the last 4 bits of the target start address must be zero: 0x......00
463 NorFlashWriteBuffer (
464 IN NOR_FLASH_INSTANCE
*Instance
,
465 IN UINTN TargetAddress
,
466 IN UINTN BufferSizeInBytes
,
471 UINTN BufferSizeInWords
;
473 volatile UINT32
*Data
;
475 BOOLEAN BufferAvailable
;
476 UINT32 StatusRegister
;
478 WaitForBuffer
= MAX_BUFFERED_PROG_ITERATIONS
;
479 BufferAvailable
= FALSE
;
481 // Check that the target address does not cross a 32-word boundary.
482 if ((TargetAddress
& BOUNDARY_OF_32_WORDS
) != 0) {
483 return EFI_INVALID_PARAMETER
;
486 // Check there are some data to program
487 if (BufferSizeInBytes
== 0) {
488 return EFI_BUFFER_TOO_SMALL
;
491 // Check that the buffer size does not exceed the maximum hardware buffer size on chip.
492 if (BufferSizeInBytes
> P30_MAX_BUFFER_SIZE_IN_BYTES
) {
493 return EFI_BAD_BUFFER_SIZE
;
496 // Check that the buffer size is a multiple of 32-bit words
497 if ((BufferSizeInBytes
% 4) != 0) {
498 return EFI_BAD_BUFFER_SIZE
;
501 // Pre-programming conditions checked, now start the algorithm.
503 // Prepare the data destination address
504 Data
= (UINT32
*)TargetAddress
;
506 // Check the availability of the buffer
508 // Issue the Buffered Program Setup command
509 SEND_NOR_COMMAND(TargetAddress
, 0, P30_CMD_BUFFERED_PROGRAM_SETUP
);
511 // Read back the status register bit#7 from the same address
512 if (((*Data
) & P30_SR_BIT_WRITE
) == P30_SR_BIT_WRITE
) {
513 BufferAvailable
= TRUE
;
516 // Update the loop counter
519 } while ((WaitForBuffer
> 0) && (BufferAvailable
== FALSE
));
521 // The buffer was not available for writing
522 if (WaitForBuffer
== 0) {
523 Status
= EFI_DEVICE_ERROR
;
527 // From now on we work in 32-bit words
528 BufferSizeInWords
= BufferSizeInBytes
/ (UINTN
)4;
530 // Write the word count, which is (buffer_size_in_words - 1),
531 // because word count 0 means one word.
532 SEND_NOR_COMMAND(TargetAddress
, 0, (BufferSizeInWords
- 1));
534 // Write the data to the NOR Flash, advancing each address by 4 bytes
535 for(Count
=0; Count
< BufferSizeInWords
; Count
++, Data
++, Buffer
++) {
539 // Issue the Buffered Program Confirm command, to start the programming operation
540 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_BUFFERED_PROGRAM_CONFIRM
);
542 // Wait for the write to complete and then check for any errors; i.e. check the Status Register
544 StatusRegister
= NorFlashReadStatusRegister (Instance
, TargetAddress
);
545 // The chip is busy while the WRITE bit is not asserted
546 } while ((StatusRegister
& P30_SR_BIT_WRITE
) != P30_SR_BIT_WRITE
);
549 // Perform a full status check:
550 // Mask the relevant bits of Status Register.
551 // Everything should be zero, if not, we have a problem
553 Status
= EFI_SUCCESS
;
555 if (StatusRegister
& P30_SR_BIT_VPP
) {
556 DEBUG((EFI_D_ERROR
,"NorFlashWriteBuffer(TargetAddress:0x%X): VPP Range Error\n", TargetAddress
));
557 Status
= EFI_DEVICE_ERROR
;
560 if (StatusRegister
& P30_SR_BIT_PROGRAM
) {
561 DEBUG((EFI_D_ERROR
,"NorFlashWriteBuffer(TargetAddress:0x%X): Program Error\n", TargetAddress
));
562 Status
= EFI_DEVICE_ERROR
;
565 if (StatusRegister
& P30_SR_BIT_BLOCK_LOCKED
) {
566 DEBUG((EFI_D_ERROR
,"NorFlashWriteBuffer(TargetAddress:0x%X): Device Protect Error\n",TargetAddress
));
567 Status
= EFI_DEVICE_ERROR
;
570 if (!EFI_ERROR(Status
)) {
571 // Clear the Status Register
572 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_CLEAR_STATUS_REGISTER
);
576 // Put device back into Read Array mode
577 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
584 NorFlashWriteFullBlock (
585 IN NOR_FLASH_INSTANCE
*Instance
,
587 IN UINT32
*DataBuffer
,
588 IN UINT32 BlockSizeInWords
596 UINTN BuffersInBlock
;
597 UINTN RemainingWords
;
601 Status
= EFI_SUCCESS
;
603 // Get the physical address of the block
604 BlockAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
, Lba
, BlockSizeInWords
* 4);
606 // Start writing from the first address at the start of the block
607 WordAddress
= BlockAddress
;
609 if (!EfiAtRuntime ()) {
610 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
611 OriginalTPL
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
613 // This initialization is only to prevent the compiler to complain about the
614 // use of uninitialized variables
615 OriginalTPL
= TPL_HIGH_LEVEL
;
618 Status
= NorFlashUnlockAndEraseSingleBlock (Instance
, BlockAddress
);
619 if (EFI_ERROR(Status
)) {
620 DEBUG((EFI_D_ERROR
, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress
));
624 // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
626 // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero
627 if ((WordAddress
& BOUNDARY_OF_32_WORDS
) == 0x00) {
629 // First, break the entire block into buffer-sized chunks.
630 BuffersInBlock
= (UINTN
)(BlockSizeInWords
* 4) / P30_MAX_BUFFER_SIZE_IN_BYTES
;
632 // Then feed each buffer chunk to the NOR Flash
633 // If a buffer does not contain any data, don't write it.
635 BufferIndex
< BuffersInBlock
;
636 BufferIndex
++, WordAddress
+= P30_MAX_BUFFER_SIZE_IN_BYTES
, DataBuffer
+= P30_MAX_BUFFER_SIZE_IN_WORDS
638 // Check the buffer to see if it contains any data (not set all 1s).
639 for (Cnt
= 0; Cnt
< P30_MAX_BUFFER_SIZE_IN_WORDS
; Cnt
++) {
640 if (~DataBuffer
[Cnt
] != 0 ) {
641 // Some data found, write the buffer.
642 Status
= NorFlashWriteBuffer (Instance
, WordAddress
, P30_MAX_BUFFER_SIZE_IN_BYTES
,
644 if (EFI_ERROR(Status
)) {
652 // Finally, finish off any remaining words that are less than the maximum size of the buffer
653 RemainingWords
= BlockSizeInWords
% P30_MAX_BUFFER_SIZE_IN_WORDS
;
655 if(RemainingWords
!= 0) {
656 Status
= NorFlashWriteBuffer (Instance
, WordAddress
, (RemainingWords
* 4), DataBuffer
);
657 if (EFI_ERROR(Status
)) {
663 // For now, use the single word programming algorithm
664 // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,
665 // i.e. which ends in the range 0x......01 - 0x......7F.
666 for(WordIndex
=0; WordIndex
<BlockSizeInWords
; WordIndex
++, DataBuffer
++, WordAddress
= WordAddress
+ 4) {
667 Status
= NorFlashWriteSingleWord (Instance
, WordAddress
, *DataBuffer
);
668 if (EFI_ERROR(Status
)) {
675 if (!EfiAtRuntime ()) {
676 // Interruptions can resume.
677 gBS
->RestoreTPL (OriginalTPL
);
680 if (EFI_ERROR(Status
)) {
681 DEBUG((EFI_D_ERROR
, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress
, Status
));
688 NorFlashWriteBlocks (
689 IN NOR_FLASH_INSTANCE
*Instance
,
691 IN UINTN BufferSizeInBytes
,
695 UINT32
*pWriteBuffer
;
696 EFI_STATUS Status
= EFI_SUCCESS
;
697 EFI_LBA CurrentBlock
;
698 UINT32 BlockSizeInWords
;
702 // The buffer must be valid
703 if (Buffer
== NULL
) {
704 return EFI_INVALID_PARAMETER
;
707 if(Instance
->Media
.ReadOnly
== TRUE
) {
708 return EFI_WRITE_PROTECTED
;
711 // We must have some bytes to read
712 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes
));
713 if(BufferSizeInBytes
== 0) {
714 return EFI_BAD_BUFFER_SIZE
;
717 // The size of the buffer must be a multiple of the block size
718 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance
->Media
.BlockSize
));
719 if ((BufferSizeInBytes
% Instance
->Media
.BlockSize
) != 0) {
720 return EFI_BAD_BUFFER_SIZE
;
723 // All blocks must be within the device
724 NumBlocks
= ((UINT32
)BufferSizeInBytes
) / Instance
->Media
.BlockSize
;
726 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks
, Instance
->Media
.LastBlock
, Lba
));
728 if ((Lba
+ NumBlocks
) > (Instance
->Media
.LastBlock
+ 1)) {
729 DEBUG((EFI_D_ERROR
, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n"));
730 return EFI_INVALID_PARAMETER
;
733 BlockSizeInWords
= Instance
->Media
.BlockSize
/ 4;
735 // Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer
736 // to a proper data type, so use *ReadBuffer
737 pWriteBuffer
= (UINT32
*)Buffer
;
740 for (BlockCount
=0; BlockCount
< NumBlocks
; BlockCount
++, CurrentBlock
++, pWriteBuffer
= pWriteBuffer
+ BlockSizeInWords
) {
742 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN
)CurrentBlock
));
744 Status
= NorFlashWriteFullBlock (Instance
, CurrentBlock
, pWriteBuffer
, BlockSizeInWords
);
746 if (EFI_ERROR(Status
)) {
751 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status
));
757 IN NOR_FLASH_INSTANCE
*Instance
,
759 IN UINTN BufferSizeInBytes
,
766 DEBUG((DEBUG_BLKIO
, "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n",
767 BufferSizeInBytes
, Instance
->Media
.BlockSize
, Instance
->Media
.LastBlock
, Lba
));
769 // The buffer must be valid
770 if (Buffer
== NULL
) {
771 return EFI_INVALID_PARAMETER
;
774 // Return if we have not any byte to read
775 if (BufferSizeInBytes
== 0) {
779 // The size of the buffer must be a multiple of the block size
780 if ((BufferSizeInBytes
% Instance
->Media
.BlockSize
) != 0) {
781 return EFI_BAD_BUFFER_SIZE
;
784 // All blocks must be within the device
785 NumBlocks
= ((UINT32
)BufferSizeInBytes
) / Instance
->Media
.BlockSize
;
787 if ((Lba
+ NumBlocks
) > (Instance
->Media
.LastBlock
+ 1)) {
788 DEBUG((EFI_D_ERROR
, "NorFlashReadBlocks: ERROR - Read will exceed last block\n"));
789 return EFI_INVALID_PARAMETER
;
792 // Get the address to start reading from
793 StartAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
,
795 Instance
->Media
.BlockSize
798 // Put the device into Read Array mode
799 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
802 CopyMem(Buffer
, (UINTN
*)StartAddress
, BufferSizeInBytes
);
809 IN NOR_FLASH_INSTANCE
*Instance
,
812 IN UINTN BufferSizeInBytes
,
819 // The buffer must be valid
820 if (Buffer
== NULL
) {
821 return EFI_INVALID_PARAMETER
;
824 // Return if we have not any byte to read
825 if (BufferSizeInBytes
== 0) {
829 // All blocks must be within the device
830 NumBlocks
= ((UINT32
)BufferSizeInBytes
) / Instance
->Media
.BlockSize
;
832 if ((Lba
+ NumBlocks
) > (Instance
->Media
.LastBlock
+ 1)) {
833 DEBUG ((EFI_D_ERROR
, "NorFlashRead: ERROR - Read will exceed last block\n"));
834 return EFI_INVALID_PARAMETER
;
837 if (Offset
+ BufferSizeInBytes
>= Instance
->Size
) {
838 DEBUG ((EFI_D_ERROR
, "NorFlashRead: ERROR - Read will exceed device size.\n"));
839 return EFI_INVALID_PARAMETER
;
842 // Get the address to start reading from
843 StartAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
,
845 Instance
->Media
.BlockSize
848 // Put the device into Read Array mode
849 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
852 CopyMem (Buffer
, (UINTN
*)(StartAddress
+ Offset
), BufferSizeInBytes
);
858 Write a full or portion of a block. It must not span block boundaries; that is,
859 Offset + *NumBytes <= Instance->Media.BlockSize.
862 NorFlashWriteSingleBlock (
863 IN NOR_FLASH_INSTANCE
*Instance
,
866 IN OUT UINTN
*NumBytes
,
870 EFI_STATUS TempStatus
;
881 UINTN PrevBlockAddress
;
883 PrevBlockAddress
= 0;
885 if (!Instance
->Initialized
&& Instance
->Initialize
) {
886 Instance
->Initialize(Instance
);
889 DEBUG ((DEBUG_BLKIO
, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba
, Offset
, *NumBytes
, Buffer
));
891 // Detect WriteDisabled state
892 if (Instance
->Media
.ReadOnly
== TRUE
) {
893 DEBUG ((EFI_D_ERROR
, "NorFlashWriteSingleBlock: ERROR - Can not write: Device is in WriteDisabled state.\n"));
894 // It is in WriteDisabled state, return an error right away
895 return EFI_ACCESS_DENIED
;
898 // Cache the block size to avoid de-referencing pointers all the time
899 BlockSize
= Instance
->Media
.BlockSize
;
901 // The write must not span block boundaries.
902 // We need to check each variable individually because adding two large values together overflows.
903 if ( ( Offset
>= BlockSize
) ||
904 ( *NumBytes
> BlockSize
) ||
905 ( (Offset
+ *NumBytes
) > BlockSize
) ) {
906 DEBUG ((EFI_D_ERROR
, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset
, *NumBytes
, BlockSize
));
907 return EFI_BAD_BUFFER_SIZE
;
910 // We must have some bytes to write
911 if (*NumBytes
== 0) {
912 DEBUG ((EFI_D_ERROR
, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset
, *NumBytes
, BlockSize
));
913 return EFI_BAD_BUFFER_SIZE
;
916 // Pick 128bytes as a good start for word operations as opposed to erasing the
917 // block and writing the data regardless if an erase is really needed.
918 // It looks like most individual NV variable writes are smaller than 128bytes.
919 if (*NumBytes
<= 128) {
920 // Check to see if we need to erase before programming the data into NOR.
921 // If the destination bits are only changing from 1s to 0s we can just write.
922 // After a block is erased all bits in the block is set to 1.
923 // If any byte requires us to erase we just give up and rewrite all of it.
925 BytesToWrite
= *NumBytes
;
928 while (BytesToWrite
> 0) {
929 // Read full word from NOR, splice as required. A word is the smallest
930 // unit we can write.
931 TempStatus
= NorFlashRead (Instance
, Lba
, CurOffset
& ~(0x3), sizeof(Tmp
), &Tmp
);
932 if (EFI_ERROR (TempStatus
)) {
933 return EFI_DEVICE_ERROR
;
936 // Physical address of word in NOR to write.
937 WordAddr
= (CurOffset
& ~(0x3)) + GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
,
939 // The word of data that is to be written.
940 TmpBuf
= *((UINT32
*)(Buffer
+ (*NumBytes
- BytesToWrite
)));
942 // First do word aligned chunks.
943 if ((CurOffset
& 0x3) == 0) {
944 if (BytesToWrite
>= 4) {
945 // Is the destination still in 'erased' state?
947 // Check to see if we are only changing bits to zero.
948 if ((Tmp
^ TmpBuf
) & TmpBuf
) {
953 // Write this word to NOR
954 WordToWrite
= TmpBuf
;
955 CurOffset
+= sizeof(TmpBuf
);
956 BytesToWrite
-= sizeof(TmpBuf
);
958 // BytesToWrite < 4. Do small writes and left-overs
959 Mask
= ~((~0) << (BytesToWrite
* 8));
960 // Mask out the bytes we want.
962 // Is the destination still in 'erased' state?
963 if ((Tmp
& Mask
) != Mask
) {
964 // Check to see if we are only changing bits to zero.
965 if ((Tmp
^ TmpBuf
) & TmpBuf
) {
970 // Merge old and new data. Write merged word to NOR
971 WordToWrite
= (Tmp
& ~Mask
) | TmpBuf
;
972 CurOffset
+= BytesToWrite
;
976 // Do multiple words, but starting unaligned.
977 if (BytesToWrite
> (4 - (CurOffset
& 0x3))) {
978 Mask
= ((~0) << ((CurOffset
& 0x3) * 8));
979 // Mask out the bytes we want.
981 // Is the destination still in 'erased' state?
982 if ((Tmp
& Mask
) != Mask
) {
983 // Check to see if we are only changing bits to zero.
984 if ((Tmp
^ TmpBuf
) & TmpBuf
) {
989 // Merge old and new data. Write merged word to NOR
990 WordToWrite
= (Tmp
& ~Mask
) | TmpBuf
;
991 BytesToWrite
-= (4 - (CurOffset
& 0x3));
992 CurOffset
+= (4 - (CurOffset
& 0x3));
994 // Unaligned and fits in one word.
995 Mask
= (~((~0) << (BytesToWrite
* 8))) << ((CurOffset
& 0x3) * 8);
996 // Mask out the bytes we want.
997 TmpBuf
= (TmpBuf
<< ((CurOffset
& 0x3) * 8)) & Mask
;
998 // Is the destination still in 'erased' state?
999 if ((Tmp
& Mask
) != Mask
) {
1000 // Check to see if we are only changing bits to zero.
1001 if ((Tmp
^ TmpBuf
) & TmpBuf
) {
1006 // Merge old and new data. Write merged word to NOR
1007 WordToWrite
= (Tmp
& ~Mask
) | TmpBuf
;
1008 CurOffset
+= BytesToWrite
;
1014 // Write the word to NOR.
1017 BlockAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
, Lba
, BlockSize
);
1018 if (BlockAddress
!= PrevBlockAddress
) {
1019 TempStatus
= NorFlashUnlockSingleBlockIfNecessary (Instance
, BlockAddress
);
1020 if (EFI_ERROR (TempStatus
)) {
1021 return EFI_DEVICE_ERROR
;
1023 PrevBlockAddress
= BlockAddress
;
1025 TempStatus
= NorFlashWriteSingleWord (Instance
, WordAddr
, WordToWrite
);
1026 if (EFI_ERROR (TempStatus
)) {
1027 return EFI_DEVICE_ERROR
;
1030 // Exit if we got here and could write all the data. Otherwise do the
1031 // Erase-Write cycle.
1037 // Check we did get some memory. Buffer is BlockSize.
1038 if (Instance
->ShadowBuffer
== NULL
) {
1039 DEBUG ((EFI_D_ERROR
, "FvbWrite: ERROR - Buffer not ready\n"));
1040 return EFI_DEVICE_ERROR
;
1043 // Read NOR Flash data into shadow buffer
1044 TempStatus
= NorFlashReadBlocks (Instance
, Lba
, BlockSize
, Instance
->ShadowBuffer
);
1045 if (EFI_ERROR (TempStatus
)) {
1046 // Return one of the pre-approved error statuses
1047 return EFI_DEVICE_ERROR
;
1050 // Put the data at the appropriate location inside the buffer area
1051 CopyMem ((VOID
*)((UINTN
)Instance
->ShadowBuffer
+ Offset
), Buffer
, *NumBytes
);
1053 // Write the modified buffer back to the NorFlash
1054 TempStatus
= NorFlashWriteBlocks (Instance
, Lba
, BlockSize
, Instance
->ShadowBuffer
);
1055 if (EFI_ERROR (TempStatus
)) {
1056 // Return one of the pre-approved error statuses
1057 return EFI_DEVICE_ERROR
;
1064 Although DiskIoDxe will automatically install the DiskIO protocol whenever
1065 we install the BlockIO protocol, its implementation is sub-optimal as it reads
1066 and writes entire blocks using the BlockIO protocol. In fact we can access
1067 NOR flash with a finer granularity than that, so we can improve performance
1068 by directly producing the DiskIO protocol.
1072 Read BufferSize bytes from Offset into Buffer.
1074 @param This Protocol instance pointer.
1075 @param MediaId Id of the media, changes every time the media is replaced.
1076 @param Offset The starting byte offset to read from
1077 @param BufferSize Size of Buffer
1078 @param Buffer Buffer containing read data
1080 @retval EFI_SUCCESS The data was read correctly from the device.
1081 @retval EFI_DEVICE_ERROR The device reported an error while performing the read.
1082 @retval EFI_NO_MEDIA There is no media in the device.
1083 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
1084 @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not
1085 valid for the device.
1090 NorFlashDiskIoReadDisk (
1091 IN EFI_DISK_IO_PROTOCOL
*This
,
1093 IN UINT64 DiskOffset
,
1094 IN UINTN BufferSize
,
1098 NOR_FLASH_INSTANCE
*Instance
;
1103 Instance
= INSTANCE_FROM_DISKIO_THIS(This
);
1105 if (MediaId
!= Instance
->Media
.MediaId
) {
1106 return EFI_MEDIA_CHANGED
;
1109 BlockSize
= Instance
->Media
.BlockSize
;
1110 Lba
= (EFI_LBA
) DivU64x32Remainder (DiskOffset
, BlockSize
, &BlockOffset
);
1112 return NorFlashRead (Instance
, Lba
, BlockOffset
, BufferSize
, Buffer
);
1116 Writes a specified number of bytes to a device.
1118 @param This Indicates a pointer to the calling context.
1119 @param MediaId ID of the medium to be written.
1120 @param Offset The starting byte offset on the logical block I/O device to write.
1121 @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device.
1122 @param Buffer A pointer to the buffer containing the data to be written.
1124 @retval EFI_SUCCESS The data was written correctly to the device.
1125 @retval EFI_WRITE_PROTECTED The device can not be written to.
1126 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
1127 @retval EFI_NO_MEDIA There is no media in the device.
1128 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
1129 @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not
1130 valid for the device.
1135 NorFlashDiskIoWriteDisk (
1136 IN EFI_DISK_IO_PROTOCOL
*This
,
1138 IN UINT64 DiskOffset
,
1139 IN UINTN BufferSize
,
1143 NOR_FLASH_INSTANCE
*Instance
;
1147 UINTN RemainingBytes
;
1151 Instance
= INSTANCE_FROM_DISKIO_THIS(This
);
1153 if (MediaId
!= Instance
->Media
.MediaId
) {
1154 return EFI_MEDIA_CHANGED
;
1157 BlockSize
= Instance
->Media
.BlockSize
;
1158 Lba
= (EFI_LBA
) DivU64x32Remainder (DiskOffset
, BlockSize
, &BlockOffset
);
1160 RemainingBytes
= BufferSize
;
1162 // Write either all the remaining bytes, or the number of bytes that bring
1163 // us up to a block boundary, whichever is less.
1164 // (DiskOffset | (BlockSize - 1)) + 1) rounds DiskOffset up to the next
1165 // block boundary (even if it is already on one).
1166 WriteSize
= MIN (RemainingBytes
, ((DiskOffset
| (BlockSize
- 1)) + 1) - DiskOffset
);
1169 if (WriteSize
== BlockSize
) {
1170 // Write a full block
1171 Status
= NorFlashWriteFullBlock (Instance
, Lba
, Buffer
, BlockSize
/ sizeof (UINT32
));
1173 // Write a partial block
1174 Status
= NorFlashWriteSingleBlock (Instance
, Lba
, BlockOffset
, &WriteSize
, Buffer
);
1176 if (EFI_ERROR (Status
)) {
1179 // Now continue writing either all the remaining bytes or single blocks.
1180 RemainingBytes
-= WriteSize
;
1181 Buffer
= (UINT8
*) Buffer
+ WriteSize
;
1184 WriteSize
= MIN (RemainingBytes
, BlockSize
);
1185 } while (RemainingBytes
);
1192 IN NOR_FLASH_INSTANCE
*Instance
1195 // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode
1196 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
1201 Fixup internal data so that EFI can be call in virtual mode.
1202 Call the passed in Child Notify event and convert any pointers in
1203 lib to virtual mode.
1205 @param[in] Event The Event that is being processed
1206 @param[in] Context Event Context
1210 NorFlashVirtualNotifyEvent (
1217 for (Index
= 0; Index
< mNorFlashDeviceCount
; Index
++) {
1218 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->DeviceBaseAddress
);
1219 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->RegionBaseAddress
);
1221 // Convert BlockIo protocol
1222 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->BlockIoProtocol
.FlushBlocks
);
1223 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->BlockIoProtocol
.ReadBlocks
);
1224 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->BlockIoProtocol
.Reset
);
1225 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->BlockIoProtocol
.WriteBlocks
);
1228 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.EraseBlocks
);
1229 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.GetAttributes
);
1230 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.GetBlockSize
);
1231 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.GetPhysicalAddress
);
1232 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.Read
);
1233 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.SetAttributes
);
1234 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.Write
);
1236 if (mNorFlashInstances
[Index
]->ShadowBuffer
!= NULL
) {
1237 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->ShadowBuffer
);
1246 NorFlashInitialise (
1247 IN EFI_HANDLE ImageHandle
,
1248 IN EFI_SYSTEM_TABLE
*SystemTable
1253 NOR_FLASH_DESCRIPTION
* NorFlashDevices
;
1254 BOOLEAN ContainVariableStorage
;
1256 Status
= NorFlashPlatformInitialization ();
1257 if (EFI_ERROR(Status
)) {
1258 DEBUG((EFI_D_ERROR
,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
1262 Status
= NorFlashPlatformGetDevices (&NorFlashDevices
, &mNorFlashDeviceCount
);
1263 if (EFI_ERROR(Status
)) {
1264 DEBUG((EFI_D_ERROR
,"NorFlashInitialise: Fail to get Nor Flash devices\n"));
1268 mNorFlashInstances
= AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE
*) * mNorFlashDeviceCount
);
1270 for (Index
= 0; Index
< mNorFlashDeviceCount
; Index
++) {
1271 // Check if this NOR Flash device contain the variable storage region
1272 ContainVariableStorage
=
1273 (NorFlashDevices
[Index
].RegionBaseAddress
<= PcdGet32 (PcdFlashNvStorageVariableBase
)) &&
1274 (PcdGet32 (PcdFlashNvStorageVariableBase
) + PcdGet32 (PcdFlashNvStorageVariableSize
) <= NorFlashDevices
[Index
].RegionBaseAddress
+ NorFlashDevices
[Index
].Size
);
1276 Status
= NorFlashCreateInstance (
1277 NorFlashDevices
[Index
].DeviceBaseAddress
,
1278 NorFlashDevices
[Index
].RegionBaseAddress
,
1279 NorFlashDevices
[Index
].Size
,
1281 NorFlashDevices
[Index
].BlockSize
,
1282 ContainVariableStorage
,
1283 &NorFlashDevices
[Index
].Guid
,
1284 &mNorFlashInstances
[Index
]
1286 if (EFI_ERROR(Status
)) {
1287 DEBUG((EFI_D_ERROR
,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index
));
1292 // Register for the virtual address change event
1294 Status
= gBS
->CreateEventEx (
1297 NorFlashVirtualNotifyEvent
,
1299 &gEfiEventVirtualAddressChangeGuid
,
1300 &mNorFlashVirtualAddrChangeEvent
1302 ASSERT_EFI_ERROR (Status
);