1 /** @file NorFlashDxe.c
3 Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include <Library/UefiLib.h>
10 #include <Library/BaseMemoryLib.h>
11 #include <Library/MemoryAllocationLib.h>
12 #include <Library/UefiBootServicesTableLib.h>
13 #include <Library/PcdLib.h>
14 #include <Library/HobLib.h>
15 #include <Library/DxeServicesTableLib.h>
19 STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent
;
22 // Global variable declarations
24 NOR_FLASH_INSTANCE
**mNorFlashInstances
;
25 UINT32 mNorFlashDeviceCount
;
26 UINTN mFlashNvStorageVariableBase
;
27 EFI_EVENT mFvbVirtualAddrChangeEvent
;
29 NOR_FLASH_INSTANCE mNorFlashInstanceTemplate
= {
30 NOR_FLASH_SIGNATURE
, // Signature
31 NULL
, // Handle ... NEED TO BE FILLED
33 0, // DeviceBaseAddress ... NEED TO BE FILLED
34 0, // RegionBaseAddress ... NEED TO BE FILLED
35 0, // Size ... NEED TO BE FILLED
39 EFI_BLOCK_IO_PROTOCOL_REVISION2
, // Revision
40 NULL
, // Media ... NEED TO BE FILLED
41 NorFlashBlockIoReset
, // Reset;
42 NorFlashBlockIoReadBlocks
, // ReadBlocks
43 NorFlashBlockIoWriteBlocks
, // WriteBlocks
44 NorFlashBlockIoFlushBlocks
// FlushBlocks
48 0, // MediaId ... NEED TO BE FILLED
49 FALSE
, // RemovableMedia
51 FALSE
, // LogicalPartition
53 FALSE
, // WriteCaching;
54 0, // BlockSize ... NEED TO BE FILLED
56 0, // LastBlock ... NEED TO BE FILLED
57 0, // LowestAlignedLba
58 1, // LogicalBlocksPerPhysicalBlock
62 EFI_DISK_IO_PROTOCOL_REVISION
, // Revision
63 NorFlashDiskIoReadDisk
, // ReadDisk
64 NorFlashDiskIoWriteDisk
// WriteDisk
68 FvbGetAttributes
, // GetAttributes
69 FvbSetAttributes
, // SetAttributes
70 FvbGetPhysicalAddress
, // GetPhysicalAddress
71 FvbGetBlockSize
, // GetBlockSize
74 FvbEraseBlocks
, // EraseBlocks
84 (UINT8
)(OFFSET_OF (NOR_FLASH_DEVICE_PATH
, End
)),
85 (UINT8
)(OFFSET_OF (NOR_FLASH_DEVICE_PATH
, End
) >> 8)
88 { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
89 }, // GUID ... NEED TO BE FILLED
94 END_ENTIRE_DEVICE_PATH_SUBTYPE
,
95 { sizeof (EFI_DEVICE_PATH_PROTOCOL
), 0 }
101 NorFlashCreateInstance (
102 IN UINTN NorFlashDeviceBase
,
103 IN UINTN NorFlashRegionBase
,
104 IN UINTN NorFlashSize
,
107 IN BOOLEAN SupportFvb
,
108 OUT NOR_FLASH_INSTANCE
**NorFlashInstance
112 NOR_FLASH_INSTANCE
*Instance
;
114 ASSERT (NorFlashInstance
!= NULL
);
116 Instance
= AllocateRuntimeCopyPool (sizeof (NOR_FLASH_INSTANCE
), &mNorFlashInstanceTemplate
);
117 if (Instance
== NULL
) {
118 return EFI_OUT_OF_RESOURCES
;
121 Instance
->DeviceBaseAddress
= NorFlashDeviceBase
;
122 Instance
->RegionBaseAddress
= NorFlashRegionBase
;
123 Instance
->Size
= NorFlashSize
;
125 Instance
->BlockIoProtocol
.Media
= &Instance
->Media
;
126 Instance
->Media
.MediaId
= Index
;
127 Instance
->Media
.BlockSize
= BlockSize
;
128 Instance
->Media
.LastBlock
= (NorFlashSize
/ BlockSize
)-1;
130 CopyGuid (&Instance
->DevicePath
.Vendor
.Guid
, &gEfiCallerIdGuid
);
131 Instance
->DevicePath
.Index
= (UINT8
)Index
;
133 Instance
->ShadowBuffer
= AllocateRuntimePool (BlockSize
);
134 if (Instance
->ShadowBuffer
== NULL
) {
135 return EFI_OUT_OF_RESOURCES
;
139 NorFlashFvbInitialize (Instance
);
141 Status
= gBS
->InstallMultipleProtocolInterfaces (
143 &gEfiDevicePathProtocolGuid
,
144 &Instance
->DevicePath
,
145 &gEfiBlockIoProtocolGuid
,
146 &Instance
->BlockIoProtocol
,
147 &gEfiFirmwareVolumeBlockProtocolGuid
,
148 &Instance
->FvbProtocol
,
151 if (EFI_ERROR (Status
)) {
156 Status
= gBS
->InstallMultipleProtocolInterfaces (
158 &gEfiDevicePathProtocolGuid
,
159 &Instance
->DevicePath
,
160 &gEfiBlockIoProtocolGuid
,
161 &Instance
->BlockIoProtocol
,
162 &gEfiDiskIoProtocolGuid
,
163 &Instance
->DiskIoProtocol
,
166 if (EFI_ERROR (Status
)) {
172 *NorFlashInstance
= Instance
;
177 * This function unlock and erase an entire NOR Flash block.
180 NorFlashUnlockAndEraseSingleBlock (
181 IN NOR_FLASH_INSTANCE
*Instance
,
182 IN UINTN BlockAddress
189 if (!EfiAtRuntime ()) {
190 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
191 OriginalTPL
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
193 // This initialization is only to prevent the compiler to complain about the
194 // use of uninitialized variables
195 OriginalTPL
= TPL_HIGH_LEVEL
;
199 // The block erase might fail a first time (SW bug ?). Retry it ...
201 // Unlock the block if we have to
202 Status
= NorFlashUnlockSingleBlockIfNecessary (Instance
, BlockAddress
);
203 if (EFI_ERROR (Status
)) {
207 Status
= NorFlashEraseSingleBlock (Instance
, BlockAddress
);
209 } while ((Index
< NOR_FLASH_ERASE_RETRY
) && (Status
== EFI_WRITE_PROTECTED
));
211 if (Index
== NOR_FLASH_ERASE_RETRY
) {
212 DEBUG ((DEBUG_ERROR
, "EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress
, Index
));
215 if (!EfiAtRuntime ()) {
216 // Interruptions can resume.
217 gBS
->RestoreTPL (OriginalTPL
);
224 NorFlashWriteFullBlock (
225 IN NOR_FLASH_INSTANCE
*Instance
,
227 IN UINT32
*DataBuffer
,
228 IN UINT32 BlockSizeInWords
236 UINTN BuffersInBlock
;
237 UINTN RemainingWords
;
241 Status
= EFI_SUCCESS
;
243 // Get the physical address of the block
244 BlockAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
, Lba
, BlockSizeInWords
* 4);
246 // Start writing from the first address at the start of the block
247 WordAddress
= BlockAddress
;
249 if (!EfiAtRuntime ()) {
250 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
251 OriginalTPL
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
253 // This initialization is only to prevent the compiler to complain about the
254 // use of uninitialized variables
255 OriginalTPL
= TPL_HIGH_LEVEL
;
258 Status
= NorFlashUnlockAndEraseSingleBlock (Instance
, BlockAddress
);
259 if (EFI_ERROR (Status
)) {
260 DEBUG ((DEBUG_ERROR
, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress
));
264 // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
266 // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero
267 if ((WordAddress
& BOUNDARY_OF_32_WORDS
) == 0x00) {
268 // First, break the entire block into buffer-sized chunks.
269 BuffersInBlock
= (UINTN
)(BlockSizeInWords
* 4) / P30_MAX_BUFFER_SIZE_IN_BYTES
;
271 // Then feed each buffer chunk to the NOR Flash
272 // If a buffer does not contain any data, don't write it.
273 for (BufferIndex
= 0;
274 BufferIndex
< BuffersInBlock
;
275 BufferIndex
++, WordAddress
+= P30_MAX_BUFFER_SIZE_IN_BYTES
, DataBuffer
+= P30_MAX_BUFFER_SIZE_IN_WORDS
278 // Check the buffer to see if it contains any data (not set all 1s).
279 for (Cnt
= 0; Cnt
< P30_MAX_BUFFER_SIZE_IN_WORDS
; Cnt
++) {
280 if (~DataBuffer
[Cnt
] != 0 ) {
281 // Some data found, write the buffer.
282 Status
= NorFlashWriteBuffer (
285 P30_MAX_BUFFER_SIZE_IN_BYTES
,
288 if (EFI_ERROR (Status
)) {
297 // Finally, finish off any remaining words that are less than the maximum size of the buffer
298 RemainingWords
= BlockSizeInWords
% P30_MAX_BUFFER_SIZE_IN_WORDS
;
300 if (RemainingWords
!= 0) {
301 Status
= NorFlashWriteBuffer (Instance
, WordAddress
, (RemainingWords
* 4), DataBuffer
);
302 if (EFI_ERROR (Status
)) {
307 // For now, use the single word programming algorithm
308 // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,
309 // i.e. which ends in the range 0x......01 - 0x......7F.
310 for (WordIndex
= 0; WordIndex
< BlockSizeInWords
; WordIndex
++, DataBuffer
++, WordAddress
= WordAddress
+ 4) {
311 Status
= NorFlashWriteSingleWord (Instance
, WordAddress
, *DataBuffer
);
312 if (EFI_ERROR (Status
)) {
319 if (!EfiAtRuntime ()) {
320 // Interruptions can resume.
321 gBS
->RestoreTPL (OriginalTPL
);
324 if (EFI_ERROR (Status
)) {
325 DEBUG ((DEBUG_ERROR
, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress
, Status
));
334 IN EFI_HANDLE ImageHandle
,
335 IN EFI_SYSTEM_TABLE
*SystemTable
340 NOR_FLASH_DESCRIPTION
*NorFlashDevices
;
341 BOOLEAN ContainVariableStorage
;
343 Status
= NorFlashPlatformInitialization ();
344 if (EFI_ERROR (Status
)) {
345 DEBUG ((DEBUG_ERROR
, "NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
349 Status
= NorFlashPlatformGetDevices (&NorFlashDevices
, &mNorFlashDeviceCount
);
350 if (EFI_ERROR (Status
)) {
351 DEBUG ((DEBUG_ERROR
, "NorFlashInitialise: Fail to get Nor Flash devices\n"));
355 mNorFlashInstances
= AllocateRuntimePool (sizeof (NOR_FLASH_INSTANCE
*) * mNorFlashDeviceCount
);
357 for (Index
= 0; Index
< mNorFlashDeviceCount
; Index
++) {
358 // Check if this NOR Flash device contain the variable storage region
360 if (PcdGet64 (PcdFlashNvStorageVariableBase64
) != 0) {
361 ContainVariableStorage
=
362 (NorFlashDevices
[Index
].RegionBaseAddress
<= PcdGet64 (PcdFlashNvStorageVariableBase64
)) &&
363 (PcdGet64 (PcdFlashNvStorageVariableBase64
) + PcdGet32 (PcdFlashNvStorageVariableSize
) <=
364 NorFlashDevices
[Index
].RegionBaseAddress
+ NorFlashDevices
[Index
].Size
);
366 ContainVariableStorage
=
367 (NorFlashDevices
[Index
].RegionBaseAddress
<= PcdGet32 (PcdFlashNvStorageVariableBase
)) &&
368 (PcdGet32 (PcdFlashNvStorageVariableBase
) + PcdGet32 (PcdFlashNvStorageVariableSize
) <=
369 NorFlashDevices
[Index
].RegionBaseAddress
+ NorFlashDevices
[Index
].Size
);
372 Status
= NorFlashCreateInstance (
373 NorFlashDevices
[Index
].DeviceBaseAddress
,
374 NorFlashDevices
[Index
].RegionBaseAddress
,
375 NorFlashDevices
[Index
].Size
,
377 NorFlashDevices
[Index
].BlockSize
,
378 ContainVariableStorage
,
379 &mNorFlashInstances
[Index
]
381 if (EFI_ERROR (Status
)) {
382 DEBUG ((DEBUG_ERROR
, "NorFlashInitialise: Fail to create instance for NorFlash[%d]\n", Index
));
387 // Register for the virtual address change event
389 Status
= gBS
->CreateEventEx (
392 NorFlashVirtualNotifyEvent
,
394 &gEfiEventVirtualAddressChangeGuid
,
395 &mNorFlashVirtualAddrChangeEvent
397 ASSERT_EFI_ERROR (Status
);
404 NorFlashFvbInitialize (
405 IN NOR_FLASH_INSTANCE
*Instance
410 EFI_BOOT_MODE BootMode
;
411 UINTN RuntimeMmioRegionSize
;
413 DEBUG ((DEBUG_BLKIO
, "NorFlashFvbInitialize\n"));
414 ASSERT ((Instance
!= NULL
));
417 // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME
420 // Note: all the NOR Flash region needs to be reserved into the UEFI Runtime memory;
421 // even if we only use the small block region at the top of the NOR Flash.
422 // The reason is when the NOR Flash memory is set into program mode, the command
423 // is written as the base of the flash region (ie: Instance->DeviceBaseAddress)
424 RuntimeMmioRegionSize
= (Instance
->RegionBaseAddress
- Instance
->DeviceBaseAddress
) + Instance
->Size
;
426 Status
= gDS
->AddMemorySpace (
427 EfiGcdMemoryTypeMemoryMappedIo
,
428 Instance
->DeviceBaseAddress
,
429 RuntimeMmioRegionSize
,
430 EFI_MEMORY_UC
| EFI_MEMORY_RUNTIME
432 ASSERT_EFI_ERROR (Status
);
434 Status
= gDS
->SetMemorySpaceAttributes (
435 Instance
->DeviceBaseAddress
,
436 RuntimeMmioRegionSize
,
437 EFI_MEMORY_UC
| EFI_MEMORY_RUNTIME
439 ASSERT_EFI_ERROR (Status
);
441 mFlashNvStorageVariableBase
= (PcdGet64 (PcdFlashNvStorageVariableBase64
) != 0) ?
442 PcdGet64 (PcdFlashNvStorageVariableBase64
) : PcdGet32 (PcdFlashNvStorageVariableBase
);
444 // Set the index of the first LBA for the FVB
445 Instance
->StartLba
= (mFlashNvStorageVariableBase
- Instance
->RegionBaseAddress
) / Instance
->Media
.BlockSize
;
447 BootMode
= GetBootModeHob ();
448 if (BootMode
== BOOT_WITH_DEFAULT_SETTINGS
) {
449 Status
= EFI_INVALID_PARAMETER
;
451 // Determine if there is a valid header at the beginning of the NorFlash
452 Status
= ValidateFvHeader (Instance
);
455 // Install the Default FVB header if required
456 if (EFI_ERROR (Status
)) {
457 // There is no valid header, so time to install one.
458 DEBUG ((DEBUG_INFO
, "%a: The FVB Header is not valid.\n", __FUNCTION__
));
461 "%a: Installing a correct one for this volume.\n",
465 // Erase all the NorFlash that is reserved for variable storage
466 FvbNumLba
= (PcdGet32 (PcdFlashNvStorageVariableSize
) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize
) + PcdGet32 (PcdFlashNvStorageFtwSpareSize
)) / Instance
->Media
.BlockSize
;
468 Status
= FvbEraseBlocks (&Instance
->FvbProtocol
, (EFI_LBA
)0, FvbNumLba
, EFI_LBA_LIST_TERMINATOR
);
469 if (EFI_ERROR (Status
)) {
473 // Install all appropriate headers
474 Status
= InitializeFvAndVariableStoreHeaders (Instance
);
475 if (EFI_ERROR (Status
)) {
481 // The driver implementing the variable read service can now be dispatched;
482 // the varstore headers are in place.
484 Status
= gBS
->InstallProtocolInterface (
486 &gEdkiiNvVarStoreFormattedGuid
,
487 EFI_NATIVE_INTERFACE
,
490 ASSERT_EFI_ERROR (Status
);
493 // Register for the virtual address change event
495 Status
= gBS
->CreateEventEx (
498 FvbVirtualNotifyEvent
,
500 &gEfiEventVirtualAddressChangeGuid
,
501 &mFvbVirtualAddrChangeEvent
503 ASSERT_EFI_ERROR (Status
);