1 /** @file NorFlashDxe.c
3 Copyright (c) 2011 - 2020, 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 } }, // GUID ... NEED TO BE FILLED
93 END_ENTIRE_DEVICE_PATH_SUBTYPE
,
94 { sizeof (EFI_DEVICE_PATH_PROTOCOL
), 0 }
100 NorFlashCreateInstance (
101 IN UINTN NorFlashDeviceBase
,
102 IN UINTN NorFlashRegionBase
,
103 IN UINTN NorFlashSize
,
106 IN BOOLEAN SupportFvb
,
107 OUT NOR_FLASH_INSTANCE
** NorFlashInstance
111 NOR_FLASH_INSTANCE
* Instance
;
113 ASSERT(NorFlashInstance
!= NULL
);
115 Instance
= AllocateRuntimeCopyPool (sizeof(NOR_FLASH_INSTANCE
),&mNorFlashInstanceTemplate
);
116 if (Instance
== NULL
) {
117 return EFI_OUT_OF_RESOURCES
;
120 Instance
->DeviceBaseAddress
= NorFlashDeviceBase
;
121 Instance
->RegionBaseAddress
= NorFlashRegionBase
;
122 Instance
->Size
= NorFlashSize
;
124 Instance
->BlockIoProtocol
.Media
= &Instance
->Media
;
125 Instance
->Media
.MediaId
= Index
;
126 Instance
->Media
.BlockSize
= BlockSize
;
127 Instance
->Media
.LastBlock
= (NorFlashSize
/ BlockSize
)-1;
129 CopyGuid (&Instance
->DevicePath
.Vendor
.Guid
, &gEfiCallerIdGuid
);
130 Instance
->DevicePath
.Index
= (UINT8
)Index
;
132 Instance
->ShadowBuffer
= AllocateRuntimePool (BlockSize
);;
133 if (Instance
->ShadowBuffer
== NULL
) {
134 return EFI_OUT_OF_RESOURCES
;
138 NorFlashFvbInitialize (Instance
);
140 Status
= gBS
->InstallMultipleProtocolInterfaces (
142 &gEfiDevicePathProtocolGuid
, &Instance
->DevicePath
,
143 &gEfiBlockIoProtocolGuid
, &Instance
->BlockIoProtocol
,
144 &gEfiFirmwareVolumeBlockProtocolGuid
, &Instance
->FvbProtocol
,
147 if (EFI_ERROR(Status
)) {
152 Status
= gBS
->InstallMultipleProtocolInterfaces (
154 &gEfiDevicePathProtocolGuid
, &Instance
->DevicePath
,
155 &gEfiBlockIoProtocolGuid
, &Instance
->BlockIoProtocol
,
156 &gEfiDiskIoProtocolGuid
, &Instance
->DiskIoProtocol
,
159 if (EFI_ERROR(Status
)) {
165 *NorFlashInstance
= Instance
;
170 * This function unlock and erase an entire NOR Flash block.
173 NorFlashUnlockAndEraseSingleBlock (
174 IN NOR_FLASH_INSTANCE
*Instance
,
175 IN UINTN BlockAddress
182 if (!EfiAtRuntime ()) {
183 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
184 OriginalTPL
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
186 // This initialization is only to prevent the compiler to complain about the
187 // use of uninitialized variables
188 OriginalTPL
= TPL_HIGH_LEVEL
;
192 // The block erase might fail a first time (SW bug ?). Retry it ...
194 // Unlock the block if we have to
195 Status
= NorFlashUnlockSingleBlockIfNecessary (Instance
, BlockAddress
);
196 if (EFI_ERROR (Status
)) {
199 Status
= NorFlashEraseSingleBlock (Instance
, BlockAddress
);
201 } while ((Index
< NOR_FLASH_ERASE_RETRY
) && (Status
== EFI_WRITE_PROTECTED
));
203 if (Index
== NOR_FLASH_ERASE_RETRY
) {
204 DEBUG((EFI_D_ERROR
,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress
,Index
));
207 if (!EfiAtRuntime ()) {
208 // Interruptions can resume.
209 gBS
->RestoreTPL (OriginalTPL
);
216 NorFlashWriteFullBlock (
217 IN NOR_FLASH_INSTANCE
*Instance
,
219 IN UINT32
*DataBuffer
,
220 IN UINT32 BlockSizeInWords
228 UINTN BuffersInBlock
;
229 UINTN RemainingWords
;
233 Status
= EFI_SUCCESS
;
235 // Get the physical address of the block
236 BlockAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
, Lba
, BlockSizeInWords
* 4);
238 // Start writing from the first address at the start of the block
239 WordAddress
= BlockAddress
;
241 if (!EfiAtRuntime ()) {
242 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
243 OriginalTPL
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
245 // This initialization is only to prevent the compiler to complain about the
246 // use of uninitialized variables
247 OriginalTPL
= TPL_HIGH_LEVEL
;
250 Status
= NorFlashUnlockAndEraseSingleBlock (Instance
, BlockAddress
);
251 if (EFI_ERROR(Status
)) {
252 DEBUG((EFI_D_ERROR
, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress
));
256 // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
258 // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero
259 if ((WordAddress
& BOUNDARY_OF_32_WORDS
) == 0x00) {
261 // First, break the entire block into buffer-sized chunks.
262 BuffersInBlock
= (UINTN
)(BlockSizeInWords
* 4) / P30_MAX_BUFFER_SIZE_IN_BYTES
;
264 // Then feed each buffer chunk to the NOR Flash
265 // If a buffer does not contain any data, don't write it.
267 BufferIndex
< BuffersInBlock
;
268 BufferIndex
++, WordAddress
+= P30_MAX_BUFFER_SIZE_IN_BYTES
, DataBuffer
+= P30_MAX_BUFFER_SIZE_IN_WORDS
270 // Check the buffer to see if it contains any data (not set all 1s).
271 for (Cnt
= 0; Cnt
< P30_MAX_BUFFER_SIZE_IN_WORDS
; Cnt
++) {
272 if (~DataBuffer
[Cnt
] != 0 ) {
273 // Some data found, write the buffer.
274 Status
= NorFlashWriteBuffer (Instance
, WordAddress
, P30_MAX_BUFFER_SIZE_IN_BYTES
,
276 if (EFI_ERROR(Status
)) {
284 // Finally, finish off any remaining words that are less than the maximum size of the buffer
285 RemainingWords
= BlockSizeInWords
% P30_MAX_BUFFER_SIZE_IN_WORDS
;
287 if(RemainingWords
!= 0) {
288 Status
= NorFlashWriteBuffer (Instance
, WordAddress
, (RemainingWords
* 4), DataBuffer
);
289 if (EFI_ERROR(Status
)) {
295 // For now, use the single word programming algorithm
296 // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,
297 // i.e. which ends in the range 0x......01 - 0x......7F.
298 for(WordIndex
=0; WordIndex
<BlockSizeInWords
; WordIndex
++, DataBuffer
++, WordAddress
= WordAddress
+ 4) {
299 Status
= NorFlashWriteSingleWord (Instance
, WordAddress
, *DataBuffer
);
300 if (EFI_ERROR(Status
)) {
307 if (!EfiAtRuntime ()) {
308 // Interruptions can resume.
309 gBS
->RestoreTPL (OriginalTPL
);
312 if (EFI_ERROR(Status
)) {
313 DEBUG((EFI_D_ERROR
, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress
, Status
));
321 IN EFI_HANDLE ImageHandle
,
322 IN EFI_SYSTEM_TABLE
*SystemTable
327 NOR_FLASH_DESCRIPTION
* NorFlashDevices
;
328 BOOLEAN ContainVariableStorage
;
330 Status
= NorFlashPlatformInitialization ();
331 if (EFI_ERROR(Status
)) {
332 DEBUG((EFI_D_ERROR
,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
336 Status
= NorFlashPlatformGetDevices (&NorFlashDevices
, &mNorFlashDeviceCount
);
337 if (EFI_ERROR(Status
)) {
338 DEBUG((EFI_D_ERROR
,"NorFlashInitialise: Fail to get Nor Flash devices\n"));
342 mNorFlashInstances
= AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE
*) * mNorFlashDeviceCount
);
344 for (Index
= 0; Index
< mNorFlashDeviceCount
; Index
++) {
345 // Check if this NOR Flash device contain the variable storage region
346 ContainVariableStorage
=
347 (NorFlashDevices
[Index
].RegionBaseAddress
<= PcdGet32 (PcdFlashNvStorageVariableBase
)) &&
348 (PcdGet32 (PcdFlashNvStorageVariableBase
) + PcdGet32 (PcdFlashNvStorageVariableSize
) <= NorFlashDevices
[Index
].RegionBaseAddress
+ NorFlashDevices
[Index
].Size
);
350 Status
= NorFlashCreateInstance (
351 NorFlashDevices
[Index
].DeviceBaseAddress
,
352 NorFlashDevices
[Index
].RegionBaseAddress
,
353 NorFlashDevices
[Index
].Size
,
355 NorFlashDevices
[Index
].BlockSize
,
356 ContainVariableStorage
,
357 &mNorFlashInstances
[Index
]
359 if (EFI_ERROR(Status
)) {
360 DEBUG((EFI_D_ERROR
,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index
));
365 // Register for the virtual address change event
367 Status
= gBS
->CreateEventEx (
370 NorFlashVirtualNotifyEvent
,
372 &gEfiEventVirtualAddressChangeGuid
,
373 &mNorFlashVirtualAddrChangeEvent
375 ASSERT_EFI_ERROR (Status
);
382 NorFlashFvbInitialize (
383 IN NOR_FLASH_INSTANCE
* Instance
388 EFI_BOOT_MODE BootMode
;
389 UINTN RuntimeMmioRegionSize
;
391 DEBUG((DEBUG_BLKIO
,"NorFlashFvbInitialize\n"));
392 ASSERT((Instance
!= NULL
));
395 // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME
398 // Note: all the NOR Flash region needs to be reserved into the UEFI Runtime memory;
399 // even if we only use the small block region at the top of the NOR Flash.
400 // The reason is when the NOR Flash memory is set into program mode, the command
401 // is written as the base of the flash region (ie: Instance->DeviceBaseAddress)
402 RuntimeMmioRegionSize
= (Instance
->RegionBaseAddress
- Instance
->DeviceBaseAddress
) + Instance
->Size
;
404 Status
= gDS
->AddMemorySpace (
405 EfiGcdMemoryTypeMemoryMappedIo
,
406 Instance
->DeviceBaseAddress
, RuntimeMmioRegionSize
,
407 EFI_MEMORY_UC
| EFI_MEMORY_RUNTIME
409 ASSERT_EFI_ERROR (Status
);
411 Status
= gDS
->SetMemorySpaceAttributes (
412 Instance
->DeviceBaseAddress
, RuntimeMmioRegionSize
,
413 EFI_MEMORY_UC
| EFI_MEMORY_RUNTIME
);
414 ASSERT_EFI_ERROR (Status
);
416 mFlashNvStorageVariableBase
= PcdGet32 (PcdFlashNvStorageVariableBase
);
418 // Set the index of the first LBA for the FVB
419 Instance
->StartLba
= (PcdGet32 (PcdFlashNvStorageVariableBase
) - Instance
->RegionBaseAddress
) / Instance
->Media
.BlockSize
;
421 BootMode
= GetBootModeHob ();
422 if (BootMode
== BOOT_WITH_DEFAULT_SETTINGS
) {
423 Status
= EFI_INVALID_PARAMETER
;
425 // Determine if there is a valid header at the beginning of the NorFlash
426 Status
= ValidateFvHeader (Instance
);
429 // Install the Default FVB header if required
430 if (EFI_ERROR(Status
)) {
431 // There is no valid header, so time to install one.
432 DEBUG ((DEBUG_INFO
, "%a: The FVB Header is not valid.\n", __FUNCTION__
));
433 DEBUG ((DEBUG_INFO
, "%a: Installing a correct one for this volume.\n",
436 // Erase all the NorFlash that is reserved for variable storage
437 FvbNumLba
= (PcdGet32(PcdFlashNvStorageVariableSize
) + PcdGet32(PcdFlashNvStorageFtwWorkingSize
) + PcdGet32(PcdFlashNvStorageFtwSpareSize
)) / Instance
->Media
.BlockSize
;
439 Status
= FvbEraseBlocks (&Instance
->FvbProtocol
, (EFI_LBA
)0, FvbNumLba
, EFI_LBA_LIST_TERMINATOR
);
440 if (EFI_ERROR(Status
)) {
444 // Install all appropriate headers
445 Status
= InitializeFvAndVariableStoreHeaders (Instance
);
446 if (EFI_ERROR(Status
)) {
452 // The driver implementing the variable read service can now be dispatched;
453 // the varstore headers are in place.
455 Status
= gBS
->InstallProtocolInterface (
457 &gEdkiiNvVarStoreFormattedGuid
,
458 EFI_NATIVE_INTERFACE
,
461 ASSERT_EFI_ERROR (Status
);
464 // Register for the virtual address change event
466 Status
= gBS
->CreateEventEx (
469 FvbVirtualNotifyEvent
,
471 &gEfiEventVirtualAddressChangeGuid
,
472 &mFvbVirtualAddrChangeEvent
474 ASSERT_EFI_ERROR (Status
);