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
194 // Send command for reading device id
195 SEND_NOR_COMMAND (BlockAddress
, 2, P30_CMD_READ_DEVICE_ID
);
197 // Read block lock status
198 LockStatus
= MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress
, 2));
200 // Decode block lock status
201 LockStatus
= FOLD_32BIT_INTO_16BIT(LockStatus
);
203 if ((LockStatus
& 0x2) != 0) {
204 DEBUG((EFI_D_ERROR
, "NorFlashBlockIsLocked: WARNING: Block LOCKED DOWN\n"));
207 return ((LockStatus
& 0x1) != 0);
212 NorFlashUnlockSingleBlock (
213 IN NOR_FLASH_INSTANCE
*Instance
,
214 IN UINTN BlockAddress
219 // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations
220 // and to protect shared data structures.
222 if (FeaturePcdGet (PcdNorFlashCheckBlockLocked
) == TRUE
) {
224 // Request a lock setup
225 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_LOCK_BLOCK_SETUP
);
228 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_UNLOCK_BLOCK
);
230 // Send command for reading device id
231 SEND_NOR_COMMAND (BlockAddress
, 2, P30_CMD_READ_DEVICE_ID
);
233 // Read block lock status
234 LockStatus
= MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress
, 2));
236 // Decode block lock status
237 LockStatus
= FOLD_32BIT_INTO_16BIT(LockStatus
);
238 } while ((LockStatus
& 0x1) == 1);
240 // Request a lock setup
241 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_LOCK_BLOCK_SETUP
);
244 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_UNLOCK_BLOCK
);
246 // Wait until the status register gives us the all clear
248 LockStatus
= NorFlashReadStatusRegister (Instance
, BlockAddress
);
249 } while ((LockStatus
& P30_SR_BIT_WRITE
) != P30_SR_BIT_WRITE
);
252 // Put device back into Read Array mode
253 SEND_NOR_COMMAND (BlockAddress
, 0, P30_CMD_READ_ARRAY
);
255 DEBUG((DEBUG_BLKIO
, "UnlockSingleBlock: BlockAddress=0x%08x\n", BlockAddress
));
262 NorFlashUnlockSingleBlockIfNecessary (
263 IN NOR_FLASH_INSTANCE
*Instance
,
264 IN UINTN BlockAddress
269 Status
= EFI_SUCCESS
;
271 if (NorFlashBlockIsLocked (Instance
, BlockAddress
) == TRUE
) {
272 Status
= NorFlashUnlockSingleBlock (Instance
, BlockAddress
);
280 * The following function presumes that the block has already been unlocked.
284 NorFlashEraseSingleBlock (
285 IN NOR_FLASH_INSTANCE
*Instance
,
286 IN UINTN BlockAddress
290 UINT32 StatusRegister
;
292 Status
= EFI_SUCCESS
;
294 // Request a block erase and then confirm it
295 SEND_NOR_COMMAND(BlockAddress
, 0, P30_CMD_BLOCK_ERASE_SETUP
);
296 SEND_NOR_COMMAND(BlockAddress
, 0, P30_CMD_BLOCK_ERASE_CONFIRM
);
298 // Wait until the status register gives us the all clear
300 StatusRegister
= NorFlashReadStatusRegister (Instance
, BlockAddress
);
301 } while ((StatusRegister
& P30_SR_BIT_WRITE
) != P30_SR_BIT_WRITE
);
303 if (StatusRegister
& P30_SR_BIT_VPP
) {
304 DEBUG((EFI_D_ERROR
,"EraseSingleBlock(BlockAddress=0x%08x: VPP Range Error\n", BlockAddress
));
305 Status
= EFI_DEVICE_ERROR
;
308 if ((StatusRegister
& (P30_SR_BIT_ERASE
| P30_SR_BIT_PROGRAM
)) == (P30_SR_BIT_ERASE
| P30_SR_BIT_PROGRAM
)) {
309 DEBUG((EFI_D_ERROR
,"EraseSingleBlock(BlockAddress=0x%08x: Command Sequence Error\n", BlockAddress
));
310 Status
= EFI_DEVICE_ERROR
;
313 if (StatusRegister
& P30_SR_BIT_ERASE
) {
314 DEBUG((EFI_D_ERROR
,"EraseSingleBlock(BlockAddress=0x%08x: Block Erase Error StatusRegister:0x%X\n", BlockAddress
, StatusRegister
));
315 Status
= EFI_DEVICE_ERROR
;
318 if (StatusRegister
& P30_SR_BIT_BLOCK_LOCKED
) {
319 // The debug level message has been reduced because a device lock might happen. In this case we just retry it ...
320 DEBUG((EFI_D_INFO
,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error\n", BlockAddress
));
321 Status
= EFI_WRITE_PROTECTED
;
324 if (EFI_ERROR(Status
)) {
325 // Clear the Status Register
326 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_CLEAR_STATUS_REGISTER
);
329 // Put device back into Read Array mode
330 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
336 * This function unlock and erase an entire NOR Flash block.
339 NorFlashUnlockAndEraseSingleBlock (
340 IN NOR_FLASH_INSTANCE
*Instance
,
341 IN UINTN BlockAddress
348 if (!EfiAtRuntime ()) {
349 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
350 OriginalTPL
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
352 // This initialization is only to prevent the compiler to complain about the
353 // use of uninitialized variables
354 OriginalTPL
= TPL_HIGH_LEVEL
;
358 // The block erase might fail a first time (SW bug ?). Retry it ...
360 // Unlock the block if we have to
361 Status
= NorFlashUnlockSingleBlockIfNecessary (Instance
, BlockAddress
);
362 if (EFI_ERROR (Status
)) {
365 Status
= NorFlashEraseSingleBlock (Instance
, BlockAddress
);
367 } while ((Index
< NOR_FLASH_ERASE_RETRY
) && (Status
== EFI_WRITE_PROTECTED
));
369 if (Index
== NOR_FLASH_ERASE_RETRY
) {
370 DEBUG((EFI_D_ERROR
,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress
,Index
));
373 if (!EfiAtRuntime ()) {
374 // Interruptions can resume.
375 gBS
->RestoreTPL (OriginalTPL
);
384 NorFlashWriteSingleWord (
385 IN NOR_FLASH_INSTANCE
*Instance
,
386 IN UINTN WordAddress
,
391 UINT32 StatusRegister
;
393 Status
= EFI_SUCCESS
;
395 // Request a write single word command
396 SEND_NOR_COMMAND(WordAddress
, 0, P30_CMD_WORD_PROGRAM_SETUP
);
398 // Store the word into NOR Flash;
399 MmioWrite32 (WordAddress
, WriteData
);
401 // Wait for the write to complete and then check for any errors; i.e. check the Status Register
403 // Prepare to read the status register
404 StatusRegister
= NorFlashReadStatusRegister (Instance
, WordAddress
);
405 // The chip is busy while the WRITE bit is not asserted
406 } while ((StatusRegister
& P30_SR_BIT_WRITE
) != P30_SR_BIT_WRITE
);
409 // Perform a full status check:
410 // Mask the relevant bits of Status Register.
411 // Everything should be zero, if not, we have a problem
413 if (StatusRegister
& P30_SR_BIT_VPP
) {
414 DEBUG((EFI_D_ERROR
,"NorFlashWriteSingleWord(WordAddress:0x%X): VPP Range Error\n",WordAddress
));
415 Status
= EFI_DEVICE_ERROR
;
418 if (StatusRegister
& P30_SR_BIT_PROGRAM
) {
419 DEBUG((EFI_D_ERROR
,"NorFlashWriteSingleWord(WordAddress:0x%X): Program Error\n",WordAddress
));
420 Status
= EFI_DEVICE_ERROR
;
423 if (StatusRegister
& P30_SR_BIT_BLOCK_LOCKED
) {
424 DEBUG((EFI_D_ERROR
,"NorFlashWriteSingleWord(WordAddress:0x%X): Device Protect Error\n",WordAddress
));
425 Status
= EFI_DEVICE_ERROR
;
428 if (!EFI_ERROR(Status
)) {
429 // Clear the Status Register
430 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_CLEAR_STATUS_REGISTER
);
433 // Put device back into Read Array mode
434 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
440 * Writes data to the NOR Flash using the Buffered Programming method.
442 * The maximum size of the on-chip buffer is 32-words, because of hardware restrictions.
443 * Therefore this function will only handle buffers up to 32 words or 128 bytes.
444 * To deal with larger buffers, call this function again.
446 * This function presumes that both the TargetAddress and the TargetAddress+BufferSize
447 * exist entirely within the NOR Flash. Therefore these conditions will not be checked here.
449 * In buffered programming, if the target address not at the beginning of a 32-bit word boundary,
450 * then programming time is doubled and power consumption is increased.
451 * Therefore, it is a requirement to align buffer writes to 32-bit word boundaries.
452 * i.e. the last 4 bits of the target start address must be zero: 0x......00
455 NorFlashWriteBuffer (
456 IN NOR_FLASH_INSTANCE
*Instance
,
457 IN UINTN TargetAddress
,
458 IN UINTN BufferSizeInBytes
,
463 UINTN BufferSizeInWords
;
465 volatile UINT32
*Data
;
467 BOOLEAN BufferAvailable
;
468 UINT32 StatusRegister
;
470 WaitForBuffer
= MAX_BUFFERED_PROG_ITERATIONS
;
471 BufferAvailable
= FALSE
;
473 // Check that the target address does not cross a 32-word boundary.
474 if ((TargetAddress
& BOUNDARY_OF_32_WORDS
) != 0) {
475 return EFI_INVALID_PARAMETER
;
478 // Check there are some data to program
479 if (BufferSizeInBytes
== 0) {
480 return EFI_BUFFER_TOO_SMALL
;
483 // Check that the buffer size does not exceed the maximum hardware buffer size on chip.
484 if (BufferSizeInBytes
> P30_MAX_BUFFER_SIZE_IN_BYTES
) {
485 return EFI_BAD_BUFFER_SIZE
;
488 // Check that the buffer size is a multiple of 32-bit words
489 if ((BufferSizeInBytes
% 4) != 0) {
490 return EFI_BAD_BUFFER_SIZE
;
493 // Pre-programming conditions checked, now start the algorithm.
495 // Prepare the data destination address
496 Data
= (UINT32
*)TargetAddress
;
498 // Check the availability of the buffer
500 // Issue the Buffered Program Setup command
501 SEND_NOR_COMMAND(TargetAddress
, 0, P30_CMD_BUFFERED_PROGRAM_SETUP
);
503 // Read back the status register bit#7 from the same address
504 if (((*Data
) & P30_SR_BIT_WRITE
) == P30_SR_BIT_WRITE
) {
505 BufferAvailable
= TRUE
;
508 // Update the loop counter
511 } while ((WaitForBuffer
> 0) && (BufferAvailable
== FALSE
));
513 // The buffer was not available for writing
514 if (WaitForBuffer
== 0) {
515 Status
= EFI_DEVICE_ERROR
;
519 // From now on we work in 32-bit words
520 BufferSizeInWords
= BufferSizeInBytes
/ (UINTN
)4;
522 // Write the word count, which is (buffer_size_in_words - 1),
523 // because word count 0 means one word.
524 SEND_NOR_COMMAND(TargetAddress
, 0, (BufferSizeInWords
- 1));
526 // Write the data to the NOR Flash, advancing each address by 4 bytes
527 for(Count
=0; Count
< BufferSizeInWords
; Count
++, Data
++, Buffer
++) {
528 MmioWrite32 ((UINTN
)Data
, *Buffer
);
531 // Issue the Buffered Program Confirm command, to start the programming operation
532 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_BUFFERED_PROGRAM_CONFIRM
);
534 // Wait for the write to complete and then check for any errors; i.e. check the Status Register
536 StatusRegister
= NorFlashReadStatusRegister (Instance
, TargetAddress
);
537 // The chip is busy while the WRITE bit is not asserted
538 } while ((StatusRegister
& P30_SR_BIT_WRITE
) != P30_SR_BIT_WRITE
);
541 // Perform a full status check:
542 // Mask the relevant bits of Status Register.
543 // Everything should be zero, if not, we have a problem
545 Status
= EFI_SUCCESS
;
547 if (StatusRegister
& P30_SR_BIT_VPP
) {
548 DEBUG((EFI_D_ERROR
,"NorFlashWriteBuffer(TargetAddress:0x%X): VPP Range Error\n", TargetAddress
));
549 Status
= EFI_DEVICE_ERROR
;
552 if (StatusRegister
& P30_SR_BIT_PROGRAM
) {
553 DEBUG((EFI_D_ERROR
,"NorFlashWriteBuffer(TargetAddress:0x%X): Program Error\n", TargetAddress
));
554 Status
= EFI_DEVICE_ERROR
;
557 if (StatusRegister
& P30_SR_BIT_BLOCK_LOCKED
) {
558 DEBUG((EFI_D_ERROR
,"NorFlashWriteBuffer(TargetAddress:0x%X): Device Protect Error\n",TargetAddress
));
559 Status
= EFI_DEVICE_ERROR
;
562 if (!EFI_ERROR(Status
)) {
563 // Clear the Status Register
564 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_CLEAR_STATUS_REGISTER
);
568 // Put device back into Read Array mode
569 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
576 NorFlashWriteFullBlock (
577 IN NOR_FLASH_INSTANCE
*Instance
,
579 IN UINT32
*DataBuffer
,
580 IN UINT32 BlockSizeInWords
588 UINTN BuffersInBlock
;
589 UINTN RemainingWords
;
593 Status
= EFI_SUCCESS
;
595 // Get the physical address of the block
596 BlockAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
, Lba
, BlockSizeInWords
* 4);
598 // Start writing from the first address at the start of the block
599 WordAddress
= BlockAddress
;
601 if (!EfiAtRuntime ()) {
602 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
603 OriginalTPL
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
605 // This initialization is only to prevent the compiler to complain about the
606 // use of uninitialized variables
607 OriginalTPL
= TPL_HIGH_LEVEL
;
610 Status
= NorFlashUnlockAndEraseSingleBlock (Instance
, BlockAddress
);
611 if (EFI_ERROR(Status
)) {
612 DEBUG((EFI_D_ERROR
, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress
));
616 // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
618 // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero
619 if ((WordAddress
& BOUNDARY_OF_32_WORDS
) == 0x00) {
621 // First, break the entire block into buffer-sized chunks.
622 BuffersInBlock
= (UINTN
)(BlockSizeInWords
* 4) / P30_MAX_BUFFER_SIZE_IN_BYTES
;
624 // Then feed each buffer chunk to the NOR Flash
625 // If a buffer does not contain any data, don't write it.
627 BufferIndex
< BuffersInBlock
;
628 BufferIndex
++, WordAddress
+= P30_MAX_BUFFER_SIZE_IN_BYTES
, DataBuffer
+= P30_MAX_BUFFER_SIZE_IN_WORDS
630 // Check the buffer to see if it contains any data (not set all 1s).
631 for (Cnt
= 0; Cnt
< P30_MAX_BUFFER_SIZE_IN_WORDS
; Cnt
++) {
632 if (~DataBuffer
[Cnt
] != 0 ) {
633 // Some data found, write the buffer.
634 Status
= NorFlashWriteBuffer (Instance
, WordAddress
, P30_MAX_BUFFER_SIZE_IN_BYTES
,
636 if (EFI_ERROR(Status
)) {
644 // Finally, finish off any remaining words that are less than the maximum size of the buffer
645 RemainingWords
= BlockSizeInWords
% P30_MAX_BUFFER_SIZE_IN_WORDS
;
647 if(RemainingWords
!= 0) {
648 Status
= NorFlashWriteBuffer (Instance
, WordAddress
, (RemainingWords
* 4), DataBuffer
);
649 if (EFI_ERROR(Status
)) {
655 // For now, use the single word programming algorithm
656 // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,
657 // i.e. which ends in the range 0x......01 - 0x......7F.
658 for(WordIndex
=0; WordIndex
<BlockSizeInWords
; WordIndex
++, DataBuffer
++, WordAddress
= WordAddress
+ 4) {
659 Status
= NorFlashWriteSingleWord (Instance
, WordAddress
, *DataBuffer
);
660 if (EFI_ERROR(Status
)) {
667 if (!EfiAtRuntime ()) {
668 // Interruptions can resume.
669 gBS
->RestoreTPL (OriginalTPL
);
672 if (EFI_ERROR(Status
)) {
673 DEBUG((EFI_D_ERROR
, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress
, Status
));
680 NorFlashWriteBlocks (
681 IN NOR_FLASH_INSTANCE
*Instance
,
683 IN UINTN BufferSizeInBytes
,
687 UINT32
*pWriteBuffer
;
688 EFI_STATUS Status
= EFI_SUCCESS
;
689 EFI_LBA CurrentBlock
;
690 UINT32 BlockSizeInWords
;
694 // The buffer must be valid
695 if (Buffer
== NULL
) {
696 return EFI_INVALID_PARAMETER
;
699 if(Instance
->Media
.ReadOnly
== TRUE
) {
700 return EFI_WRITE_PROTECTED
;
703 // We must have some bytes to read
704 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes
));
705 if(BufferSizeInBytes
== 0) {
706 return EFI_BAD_BUFFER_SIZE
;
709 // The size of the buffer must be a multiple of the block size
710 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance
->Media
.BlockSize
));
711 if ((BufferSizeInBytes
% Instance
->Media
.BlockSize
) != 0) {
712 return EFI_BAD_BUFFER_SIZE
;
715 // All blocks must be within the device
716 NumBlocks
= ((UINT32
)BufferSizeInBytes
) / Instance
->Media
.BlockSize
;
718 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks
, Instance
->Media
.LastBlock
, Lba
));
720 if ((Lba
+ NumBlocks
) > (Instance
->Media
.LastBlock
+ 1)) {
721 DEBUG((EFI_D_ERROR
, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n"));
722 return EFI_INVALID_PARAMETER
;
725 BlockSizeInWords
= Instance
->Media
.BlockSize
/ 4;
727 // Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer
728 // to a proper data type, so use *ReadBuffer
729 pWriteBuffer
= (UINT32
*)Buffer
;
732 for (BlockCount
=0; BlockCount
< NumBlocks
; BlockCount
++, CurrentBlock
++, pWriteBuffer
= pWriteBuffer
+ BlockSizeInWords
) {
734 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN
)CurrentBlock
));
736 Status
= NorFlashWriteFullBlock (Instance
, CurrentBlock
, pWriteBuffer
, BlockSizeInWords
);
738 if (EFI_ERROR(Status
)) {
743 DEBUG((DEBUG_BLKIO
, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status
));
749 IN NOR_FLASH_INSTANCE
*Instance
,
751 IN UINTN BufferSizeInBytes
,
758 DEBUG((DEBUG_BLKIO
, "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n",
759 BufferSizeInBytes
, Instance
->Media
.BlockSize
, Instance
->Media
.LastBlock
, Lba
));
761 // The buffer must be valid
762 if (Buffer
== NULL
) {
763 return EFI_INVALID_PARAMETER
;
766 // Return if we have not any byte to read
767 if (BufferSizeInBytes
== 0) {
771 // The size of the buffer must be a multiple of the block size
772 if ((BufferSizeInBytes
% Instance
->Media
.BlockSize
) != 0) {
773 return EFI_BAD_BUFFER_SIZE
;
776 // All blocks must be within the device
777 NumBlocks
= ((UINT32
)BufferSizeInBytes
) / Instance
->Media
.BlockSize
;
779 if ((Lba
+ NumBlocks
) > (Instance
->Media
.LastBlock
+ 1)) {
780 DEBUG((EFI_D_ERROR
, "NorFlashReadBlocks: ERROR - Read will exceed last block\n"));
781 return EFI_INVALID_PARAMETER
;
784 // Get the address to start reading from
785 StartAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
,
787 Instance
->Media
.BlockSize
790 // Put the device into Read Array mode
791 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
794 CopyMem(Buffer
, (UINTN
*)StartAddress
, BufferSizeInBytes
);
801 IN NOR_FLASH_INSTANCE
*Instance
,
804 IN UINTN BufferSizeInBytes
,
810 // The buffer must be valid
811 if (Buffer
== NULL
) {
812 return EFI_INVALID_PARAMETER
;
815 // Return if we have not any byte to read
816 if (BufferSizeInBytes
== 0) {
820 if (((Lba
* Instance
->Media
.BlockSize
) + Offset
+ BufferSizeInBytes
) > Instance
->Size
) {
821 DEBUG ((EFI_D_ERROR
, "NorFlashRead: ERROR - Read will exceed device size.\n"));
822 return EFI_INVALID_PARAMETER
;
825 // Get the address to start reading from
826 StartAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
,
828 Instance
->Media
.BlockSize
831 // Put the device into Read Array mode
832 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
835 CopyMem (Buffer
, (UINTN
*)(StartAddress
+ Offset
), BufferSizeInBytes
);
841 Write a full or portion of a block. It must not span block boundaries; that is,
842 Offset + *NumBytes <= Instance->Media.BlockSize.
845 NorFlashWriteSingleBlock (
846 IN NOR_FLASH_INSTANCE
*Instance
,
849 IN OUT UINTN
*NumBytes
,
853 EFI_STATUS TempStatus
;
864 UINTN PrevBlockAddress
;
866 PrevBlockAddress
= 0;
868 if (!Instance
->Initialized
&& Instance
->Initialize
) {
869 Instance
->Initialize(Instance
);
872 DEBUG ((DEBUG_BLKIO
, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba
, Offset
, *NumBytes
, Buffer
));
874 // Detect WriteDisabled state
875 if (Instance
->Media
.ReadOnly
== TRUE
) {
876 DEBUG ((EFI_D_ERROR
, "NorFlashWriteSingleBlock: ERROR - Can not write: Device is in WriteDisabled state.\n"));
877 // It is in WriteDisabled state, return an error right away
878 return EFI_ACCESS_DENIED
;
881 // Cache the block size to avoid de-referencing pointers all the time
882 BlockSize
= Instance
->Media
.BlockSize
;
884 // The write must not span block boundaries.
885 // We need to check each variable individually because adding two large values together overflows.
886 if ( ( Offset
>= BlockSize
) ||
887 ( *NumBytes
> BlockSize
) ||
888 ( (Offset
+ *NumBytes
) > BlockSize
) ) {
889 DEBUG ((EFI_D_ERROR
, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset
, *NumBytes
, BlockSize
));
890 return EFI_BAD_BUFFER_SIZE
;
893 // We must have some bytes to write
894 if (*NumBytes
== 0) {
895 DEBUG ((EFI_D_ERROR
, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset
, *NumBytes
, BlockSize
));
896 return EFI_BAD_BUFFER_SIZE
;
899 // Pick 128bytes as a good start for word operations as opposed to erasing the
900 // block and writing the data regardless if an erase is really needed.
901 // It looks like most individual NV variable writes are smaller than 128bytes.
902 if (*NumBytes
<= 128) {
903 // Check to see if we need to erase before programming the data into NOR.
904 // If the destination bits are only changing from 1s to 0s we can just write.
905 // After a block is erased all bits in the block is set to 1.
906 // If any byte requires us to erase we just give up and rewrite all of it.
908 BytesToWrite
= *NumBytes
;
911 while (BytesToWrite
> 0) {
912 // Read full word from NOR, splice as required. A word is the smallest
913 // unit we can write.
914 TempStatus
= NorFlashRead (Instance
, Lba
, CurOffset
& ~(0x3), sizeof(Tmp
), &Tmp
);
915 if (EFI_ERROR (TempStatus
)) {
916 return EFI_DEVICE_ERROR
;
919 // Physical address of word in NOR to write.
920 WordAddr
= (CurOffset
& ~(0x3)) + GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
,
922 // The word of data that is to be written.
923 TmpBuf
= *((UINT32
*)(Buffer
+ (*NumBytes
- BytesToWrite
)));
925 // First do word aligned chunks.
926 if ((CurOffset
& 0x3) == 0) {
927 if (BytesToWrite
>= 4) {
928 // Is the destination still in 'erased' state?
930 // Check to see if we are only changing bits to zero.
931 if ((Tmp
^ TmpBuf
) & TmpBuf
) {
936 // Write this word to NOR
937 WordToWrite
= TmpBuf
;
938 CurOffset
+= sizeof(TmpBuf
);
939 BytesToWrite
-= sizeof(TmpBuf
);
941 // BytesToWrite < 4. Do small writes and left-overs
942 Mask
= ~((~0) << (BytesToWrite
* 8));
943 // Mask out the bytes we want.
945 // Is the destination still in 'erased' state?
946 if ((Tmp
& Mask
) != Mask
) {
947 // Check to see if we are only changing bits to zero.
948 if ((Tmp
^ TmpBuf
) & TmpBuf
) {
953 // Merge old and new data. Write merged word to NOR
954 WordToWrite
= (Tmp
& ~Mask
) | TmpBuf
;
955 CurOffset
+= BytesToWrite
;
959 // Do multiple words, but starting unaligned.
960 if (BytesToWrite
> (4 - (CurOffset
& 0x3))) {
961 Mask
= ((~0) << ((CurOffset
& 0x3) * 8));
962 // Mask out the bytes we want.
964 // Is the destination still in 'erased' state?
965 if ((Tmp
& Mask
) != Mask
) {
966 // Check to see if we are only changing bits to zero.
967 if ((Tmp
^ TmpBuf
) & TmpBuf
) {
972 // Merge old and new data. Write merged word to NOR
973 WordToWrite
= (Tmp
& ~Mask
) | TmpBuf
;
974 BytesToWrite
-= (4 - (CurOffset
& 0x3));
975 CurOffset
+= (4 - (CurOffset
& 0x3));
977 // Unaligned and fits in one word.
978 Mask
= (~((~0) << (BytesToWrite
* 8))) << ((CurOffset
& 0x3) * 8);
979 // Mask out the bytes we want.
980 TmpBuf
= (TmpBuf
<< ((CurOffset
& 0x3) * 8)) & Mask
;
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 CurOffset
+= BytesToWrite
;
997 // Write the word to NOR.
1000 BlockAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
, Lba
, BlockSize
);
1001 if (BlockAddress
!= PrevBlockAddress
) {
1002 TempStatus
= NorFlashUnlockSingleBlockIfNecessary (Instance
, BlockAddress
);
1003 if (EFI_ERROR (TempStatus
)) {
1004 return EFI_DEVICE_ERROR
;
1006 PrevBlockAddress
= BlockAddress
;
1008 TempStatus
= NorFlashWriteSingleWord (Instance
, WordAddr
, WordToWrite
);
1009 if (EFI_ERROR (TempStatus
)) {
1010 return EFI_DEVICE_ERROR
;
1013 // Exit if we got here and could write all the data. Otherwise do the
1014 // Erase-Write cycle.
1020 // Check we did get some memory. Buffer is BlockSize.
1021 if (Instance
->ShadowBuffer
== NULL
) {
1022 DEBUG ((EFI_D_ERROR
, "FvbWrite: ERROR - Buffer not ready\n"));
1023 return EFI_DEVICE_ERROR
;
1026 // Read NOR Flash data into shadow buffer
1027 TempStatus
= NorFlashReadBlocks (Instance
, Lba
, BlockSize
, Instance
->ShadowBuffer
);
1028 if (EFI_ERROR (TempStatus
)) {
1029 // Return one of the pre-approved error statuses
1030 return EFI_DEVICE_ERROR
;
1033 // Put the data at the appropriate location inside the buffer area
1034 CopyMem ((VOID
*)((UINTN
)Instance
->ShadowBuffer
+ Offset
), Buffer
, *NumBytes
);
1036 // Write the modified buffer back to the NorFlash
1037 TempStatus
= NorFlashWriteBlocks (Instance
, Lba
, BlockSize
, Instance
->ShadowBuffer
);
1038 if (EFI_ERROR (TempStatus
)) {
1039 // Return one of the pre-approved error statuses
1040 return EFI_DEVICE_ERROR
;
1047 Although DiskIoDxe will automatically install the DiskIO protocol whenever
1048 we install the BlockIO protocol, its implementation is sub-optimal as it reads
1049 and writes entire blocks using the BlockIO protocol. In fact we can access
1050 NOR flash with a finer granularity than that, so we can improve performance
1051 by directly producing the DiskIO protocol.
1055 Read BufferSize bytes from Offset into Buffer.
1057 @param This Protocol instance pointer.
1058 @param MediaId Id of the media, changes every time the media is replaced.
1059 @param Offset The starting byte offset to read from
1060 @param BufferSize Size of Buffer
1061 @param Buffer Buffer containing read data
1063 @retval EFI_SUCCESS The data was read correctly from the device.
1064 @retval EFI_DEVICE_ERROR The device reported an error while performing the read.
1065 @retval EFI_NO_MEDIA There is no media in the device.
1066 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
1067 @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not
1068 valid for the device.
1073 NorFlashDiskIoReadDisk (
1074 IN EFI_DISK_IO_PROTOCOL
*This
,
1076 IN UINT64 DiskOffset
,
1077 IN UINTN BufferSize
,
1081 NOR_FLASH_INSTANCE
*Instance
;
1086 Instance
= INSTANCE_FROM_DISKIO_THIS(This
);
1088 if (MediaId
!= Instance
->Media
.MediaId
) {
1089 return EFI_MEDIA_CHANGED
;
1092 BlockSize
= Instance
->Media
.BlockSize
;
1093 Lba
= (EFI_LBA
) DivU64x32Remainder (DiskOffset
, BlockSize
, &BlockOffset
);
1095 return NorFlashRead (Instance
, Lba
, BlockOffset
, BufferSize
, Buffer
);
1099 Writes a specified number of bytes to a device.
1101 @param This Indicates a pointer to the calling context.
1102 @param MediaId ID of the medium to be written.
1103 @param Offset The starting byte offset on the logical block I/O device to write.
1104 @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device.
1105 @param Buffer A pointer to the buffer containing the data to be written.
1107 @retval EFI_SUCCESS The data was written correctly to the device.
1108 @retval EFI_WRITE_PROTECTED The device can not be written to.
1109 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
1110 @retval EFI_NO_MEDIA There is no media in the device.
1111 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
1112 @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not
1113 valid for the device.
1118 NorFlashDiskIoWriteDisk (
1119 IN EFI_DISK_IO_PROTOCOL
*This
,
1121 IN UINT64 DiskOffset
,
1122 IN UINTN BufferSize
,
1126 NOR_FLASH_INSTANCE
*Instance
;
1130 UINTN RemainingBytes
;
1134 Instance
= INSTANCE_FROM_DISKIO_THIS(This
);
1136 if (MediaId
!= Instance
->Media
.MediaId
) {
1137 return EFI_MEDIA_CHANGED
;
1140 BlockSize
= Instance
->Media
.BlockSize
;
1141 Lba
= (EFI_LBA
) DivU64x32Remainder (DiskOffset
, BlockSize
, &BlockOffset
);
1143 RemainingBytes
= BufferSize
;
1145 // Write either all the remaining bytes, or the number of bytes that bring
1146 // us up to a block boundary, whichever is less.
1147 // (DiskOffset | (BlockSize - 1)) + 1) rounds DiskOffset up to the next
1148 // block boundary (even if it is already on one).
1149 WriteSize
= MIN (RemainingBytes
, ((DiskOffset
| (BlockSize
- 1)) + 1) - DiskOffset
);
1152 if (WriteSize
== BlockSize
) {
1153 // Write a full block
1154 Status
= NorFlashWriteFullBlock (Instance
, Lba
, Buffer
, BlockSize
/ sizeof (UINT32
));
1156 // Write a partial block
1157 Status
= NorFlashWriteSingleBlock (Instance
, Lba
, BlockOffset
, &WriteSize
, Buffer
);
1159 if (EFI_ERROR (Status
)) {
1162 // Now continue writing either all the remaining bytes or single blocks.
1163 RemainingBytes
-= WriteSize
;
1164 Buffer
= (UINT8
*) Buffer
+ WriteSize
;
1167 WriteSize
= MIN (RemainingBytes
, BlockSize
);
1168 } while (RemainingBytes
);
1175 IN NOR_FLASH_INSTANCE
*Instance
1178 // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode
1179 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
1184 Fixup internal data so that EFI can be call in virtual mode.
1185 Call the passed in Child Notify event and convert any pointers in
1186 lib to virtual mode.
1188 @param[in] Event The Event that is being processed
1189 @param[in] Context Event Context
1193 NorFlashVirtualNotifyEvent (
1200 for (Index
= 0; Index
< mNorFlashDeviceCount
; Index
++) {
1201 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->DeviceBaseAddress
);
1202 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->RegionBaseAddress
);
1204 // Convert BlockIo protocol
1205 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->BlockIoProtocol
.FlushBlocks
);
1206 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->BlockIoProtocol
.ReadBlocks
);
1207 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->BlockIoProtocol
.Reset
);
1208 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->BlockIoProtocol
.WriteBlocks
);
1211 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.EraseBlocks
);
1212 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.GetAttributes
);
1213 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.GetBlockSize
);
1214 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.GetPhysicalAddress
);
1215 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.Read
);
1216 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.SetAttributes
);
1217 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->FvbProtocol
.Write
);
1219 if (mNorFlashInstances
[Index
]->ShadowBuffer
!= NULL
) {
1220 EfiConvertPointer (0x0, (VOID
**)&mNorFlashInstances
[Index
]->ShadowBuffer
);
1229 NorFlashInitialise (
1230 IN EFI_HANDLE ImageHandle
,
1231 IN EFI_SYSTEM_TABLE
*SystemTable
1236 NOR_FLASH_DESCRIPTION
* NorFlashDevices
;
1237 BOOLEAN ContainVariableStorage
;
1239 Status
= NorFlashPlatformInitialization ();
1240 if (EFI_ERROR(Status
)) {
1241 DEBUG((EFI_D_ERROR
,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
1245 Status
= NorFlashPlatformGetDevices (&NorFlashDevices
, &mNorFlashDeviceCount
);
1246 if (EFI_ERROR(Status
)) {
1247 DEBUG((EFI_D_ERROR
,"NorFlashInitialise: Fail to get Nor Flash devices\n"));
1251 mNorFlashInstances
= AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE
*) * mNorFlashDeviceCount
);
1253 for (Index
= 0; Index
< mNorFlashDeviceCount
; Index
++) {
1254 // Check if this NOR Flash device contain the variable storage region
1255 ContainVariableStorage
=
1256 (NorFlashDevices
[Index
].RegionBaseAddress
<= PcdGet32 (PcdFlashNvStorageVariableBase
)) &&
1257 (PcdGet32 (PcdFlashNvStorageVariableBase
) + PcdGet32 (PcdFlashNvStorageVariableSize
) <= NorFlashDevices
[Index
].RegionBaseAddress
+ NorFlashDevices
[Index
].Size
);
1259 Status
= NorFlashCreateInstance (
1260 NorFlashDevices
[Index
].DeviceBaseAddress
,
1261 NorFlashDevices
[Index
].RegionBaseAddress
,
1262 NorFlashDevices
[Index
].Size
,
1264 NorFlashDevices
[Index
].BlockSize
,
1265 ContainVariableStorage
,
1266 &NorFlashDevices
[Index
].Guid
,
1267 &mNorFlashInstances
[Index
]
1269 if (EFI_ERROR(Status
)) {
1270 DEBUG((EFI_D_ERROR
,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index
));
1275 // Register for the virtual address change event
1277 Status
= gBS
->CreateEventEx (
1280 NorFlashVirtualNotifyEvent
,
1282 &gEfiEventVirtualAddressChangeGuid
,
1283 &mNorFlashVirtualAddrChangeEvent
1285 ASSERT_EFI_ERROR (Status
);