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/BaseMemoryLib.h>
10 #include <Library/DxeServicesTableLib.h>
11 #include <Library/HobLib.h>
12 #include <Library/MemoryAllocationLib.h>
13 #include <Library/PcdLib.h>
14 #include <Library/UefiBootServicesTableLib.h>
15 #include <Library/UefiLib.h>
17 #include "VirtNorFlash.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
41 FvbGetAttributes
, // GetAttributes
42 FvbSetAttributes
, // SetAttributes
43 FvbGetPhysicalAddress
, // GetPhysicalAddress
44 FvbGetBlockSize
, // GetBlockSize
47 FvbEraseBlocks
, // EraseBlocks
57 (UINT8
)(OFFSET_OF (NOR_FLASH_DEVICE_PATH
, End
)),
58 (UINT8
)(OFFSET_OF (NOR_FLASH_DEVICE_PATH
, End
) >> 8)
61 { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
62 }, // GUID ... NEED TO BE FILLED
67 END_ENTIRE_DEVICE_PATH_SUBTYPE
,
68 { sizeof (EFI_DEVICE_PATH_PROTOCOL
), 0 }
74 NorFlashCreateInstance (
75 IN UINTN NorFlashDeviceBase
,
76 IN UINTN NorFlashRegionBase
,
77 IN UINTN NorFlashSize
,
80 IN BOOLEAN SupportFvb
,
81 OUT NOR_FLASH_INSTANCE
**NorFlashInstance
85 NOR_FLASH_INSTANCE
*Instance
;
87 ASSERT (NorFlashInstance
!= NULL
);
89 Instance
= AllocateRuntimeCopyPool (sizeof (NOR_FLASH_INSTANCE
), &mNorFlashInstanceTemplate
);
90 if (Instance
== NULL
) {
91 return EFI_OUT_OF_RESOURCES
;
94 Instance
->DeviceBaseAddress
= NorFlashDeviceBase
;
95 Instance
->RegionBaseAddress
= NorFlashRegionBase
;
96 Instance
->Size
= NorFlashSize
;
97 Instance
->BlockSize
= BlockSize
;
98 Instance
->LastBlock
= (NorFlashSize
/ BlockSize
) - 1;
100 CopyGuid (&Instance
->DevicePath
.Vendor
.Guid
, &gEfiCallerIdGuid
);
101 Instance
->DevicePath
.Index
= (UINT8
)Index
;
103 Instance
->ShadowBuffer
= AllocateRuntimePool (BlockSize
);
104 if (Instance
->ShadowBuffer
== NULL
) {
105 return EFI_OUT_OF_RESOURCES
;
109 NorFlashFvbInitialize (Instance
);
111 Status
= gBS
->InstallMultipleProtocolInterfaces (
113 &gEfiDevicePathProtocolGuid
,
114 &Instance
->DevicePath
,
115 &gEfiFirmwareVolumeBlockProtocolGuid
,
116 &Instance
->FvbProtocol
,
119 if (EFI_ERROR (Status
)) {
124 Status
= gBS
->InstallMultipleProtocolInterfaces (
126 &gEfiDevicePathProtocolGuid
,
127 &Instance
->DevicePath
,
130 if (EFI_ERROR (Status
)) {
136 *NorFlashInstance
= Instance
;
141 * This function unlock and erase an entire NOR Flash block.
144 NorFlashUnlockAndEraseSingleBlock (
145 IN NOR_FLASH_INSTANCE
*Instance
,
146 IN UINTN BlockAddress
153 if (!EfiAtRuntime ()) {
154 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
155 OriginalTPL
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
157 // This initialization is only to prevent the compiler to complain about the
158 // use of uninitialized variables
159 OriginalTPL
= TPL_HIGH_LEVEL
;
163 // The block erase might fail a first time (SW bug ?). Retry it ...
165 // Unlock the block if we have to
166 Status
= NorFlashUnlockSingleBlockIfNecessary (Instance
, BlockAddress
);
167 if (EFI_ERROR (Status
)) {
171 Status
= NorFlashEraseSingleBlock (Instance
, BlockAddress
);
173 } while ((Index
< NOR_FLASH_ERASE_RETRY
) && (Status
== EFI_WRITE_PROTECTED
));
175 if (Index
== NOR_FLASH_ERASE_RETRY
) {
176 DEBUG ((DEBUG_ERROR
, "EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress
, Index
));
179 if (!EfiAtRuntime ()) {
180 // Interruptions can resume.
181 gBS
->RestoreTPL (OriginalTPL
);
188 NorFlashWriteFullBlock (
189 IN NOR_FLASH_INSTANCE
*Instance
,
191 IN UINT32
*DataBuffer
,
192 IN UINT32 BlockSizeInWords
200 UINTN BuffersInBlock
;
201 UINTN RemainingWords
;
205 Status
= EFI_SUCCESS
;
207 // Get the physical address of the block
208 BlockAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
, Lba
, BlockSizeInWords
* 4);
210 // Start writing from the first address at the start of the block
211 WordAddress
= BlockAddress
;
213 if (!EfiAtRuntime ()) {
214 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
215 OriginalTPL
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
217 // This initialization is only to prevent the compiler to complain about the
218 // use of uninitialized variables
219 OriginalTPL
= TPL_HIGH_LEVEL
;
222 Status
= NorFlashUnlockAndEraseSingleBlock (Instance
, BlockAddress
);
223 if (EFI_ERROR (Status
)) {
224 DEBUG ((DEBUG_ERROR
, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress
));
228 // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
230 // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero
231 if ((WordAddress
& BOUNDARY_OF_32_WORDS
) == 0x00) {
232 // First, break the entire block into buffer-sized chunks.
233 BuffersInBlock
= (UINTN
)(BlockSizeInWords
* 4) / P30_MAX_BUFFER_SIZE_IN_BYTES
;
235 // Then feed each buffer chunk to the NOR Flash
236 // If a buffer does not contain any data, don't write it.
237 for (BufferIndex
= 0;
238 BufferIndex
< BuffersInBlock
;
239 BufferIndex
++, WordAddress
+= P30_MAX_BUFFER_SIZE_IN_BYTES
, DataBuffer
+= P30_MAX_BUFFER_SIZE_IN_WORDS
242 // Check the buffer to see if it contains any data (not set all 1s).
243 for (Cnt
= 0; Cnt
< P30_MAX_BUFFER_SIZE_IN_WORDS
; Cnt
++) {
244 if (~DataBuffer
[Cnt
] != 0 ) {
245 // Some data found, write the buffer.
246 Status
= NorFlashWriteBuffer (
249 P30_MAX_BUFFER_SIZE_IN_BYTES
,
252 if (EFI_ERROR (Status
)) {
261 // Finally, finish off any remaining words that are less than the maximum size of the buffer
262 RemainingWords
= BlockSizeInWords
% P30_MAX_BUFFER_SIZE_IN_WORDS
;
264 if (RemainingWords
!= 0) {
265 Status
= NorFlashWriteBuffer (Instance
, WordAddress
, (RemainingWords
* 4), DataBuffer
);
266 if (EFI_ERROR (Status
)) {
271 // For now, use the single word programming algorithm
272 // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,
273 // i.e. which ends in the range 0x......01 - 0x......7F.
274 for (WordIndex
= 0; WordIndex
< BlockSizeInWords
; WordIndex
++, DataBuffer
++, WordAddress
= WordAddress
+ 4) {
275 Status
= NorFlashWriteSingleWord (Instance
, WordAddress
, *DataBuffer
);
276 if (EFI_ERROR (Status
)) {
283 // Put device back into Read Array mode
284 SEND_NOR_COMMAND (Instance
->DeviceBaseAddress
, 0, P30_CMD_READ_ARRAY
);
286 if (!EfiAtRuntime ()) {
287 // Interruptions can resume.
288 gBS
->RestoreTPL (OriginalTPL
);
291 if (EFI_ERROR (Status
)) {
292 DEBUG ((DEBUG_ERROR
, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress
, Status
));
301 IN EFI_HANDLE ImageHandle
,
302 IN EFI_SYSTEM_TABLE
*SystemTable
307 VIRT_NOR_FLASH_DESCRIPTION
*NorFlashDevices
;
308 BOOLEAN ContainVariableStorage
;
310 Status
= VirtNorFlashPlatformInitialization ();
311 if (EFI_ERROR (Status
)) {
312 DEBUG ((DEBUG_ERROR
, "NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
316 Status
= VirtNorFlashPlatformGetDevices (&NorFlashDevices
, &mNorFlashDeviceCount
);
317 if (EFI_ERROR (Status
)) {
318 DEBUG ((DEBUG_ERROR
, "NorFlashInitialise: Fail to get Nor Flash devices\n"));
322 mNorFlashInstances
= AllocateRuntimePool (sizeof (NOR_FLASH_INSTANCE
*) * mNorFlashDeviceCount
);
324 for (Index
= 0; Index
< mNorFlashDeviceCount
; Index
++) {
325 // Check if this NOR Flash device contain the variable storage region
327 if (PcdGet64 (PcdFlashNvStorageVariableBase64
) != 0) {
328 ContainVariableStorage
=
329 (NorFlashDevices
[Index
].RegionBaseAddress
<= PcdGet64 (PcdFlashNvStorageVariableBase64
)) &&
330 (PcdGet64 (PcdFlashNvStorageVariableBase64
) + PcdGet32 (PcdFlashNvStorageVariableSize
) <=
331 NorFlashDevices
[Index
].RegionBaseAddress
+ NorFlashDevices
[Index
].Size
);
333 ContainVariableStorage
=
334 (NorFlashDevices
[Index
].RegionBaseAddress
<= PcdGet32 (PcdFlashNvStorageVariableBase
)) &&
335 (PcdGet32 (PcdFlashNvStorageVariableBase
) + PcdGet32 (PcdFlashNvStorageVariableSize
) <=
336 NorFlashDevices
[Index
].RegionBaseAddress
+ NorFlashDevices
[Index
].Size
);
339 Status
= NorFlashCreateInstance (
340 NorFlashDevices
[Index
].DeviceBaseAddress
,
341 NorFlashDevices
[Index
].RegionBaseAddress
,
342 NorFlashDevices
[Index
].Size
,
344 NorFlashDevices
[Index
].BlockSize
,
345 ContainVariableStorage
,
346 &mNorFlashInstances
[Index
]
348 if (EFI_ERROR (Status
)) {
349 DEBUG ((DEBUG_ERROR
, "NorFlashInitialise: Fail to create instance for NorFlash[%d]\n", Index
));
354 // Register for the virtual address change event
356 Status
= gBS
->CreateEventEx (
359 NorFlashVirtualNotifyEvent
,
361 &gEfiEventVirtualAddressChangeGuid
,
362 &mNorFlashVirtualAddrChangeEvent
364 ASSERT_EFI_ERROR (Status
);
371 NorFlashFvbInitialize (
372 IN NOR_FLASH_INSTANCE
*Instance
377 EFI_BOOT_MODE BootMode
;
378 UINTN RuntimeMmioRegionSize
;
380 DEBUG ((DEBUG_BLKIO
, "NorFlashFvbInitialize\n"));
381 ASSERT ((Instance
!= NULL
));
384 // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME
387 // Note: all the NOR Flash region needs to be reserved into the UEFI Runtime memory;
388 // even if we only use the small block region at the top of the NOR Flash.
389 // The reason is when the NOR Flash memory is set into program mode, the command
390 // is written as the base of the flash region (ie: Instance->DeviceBaseAddress)
391 RuntimeMmioRegionSize
= (Instance
->RegionBaseAddress
- Instance
->DeviceBaseAddress
) + Instance
->Size
;
393 Status
= gDS
->AddMemorySpace (
394 EfiGcdMemoryTypeMemoryMappedIo
,
395 Instance
->DeviceBaseAddress
,
396 RuntimeMmioRegionSize
,
397 EFI_MEMORY_UC
| EFI_MEMORY_RUNTIME
399 ASSERT_EFI_ERROR (Status
);
401 Status
= gDS
->SetMemorySpaceAttributes (
402 Instance
->DeviceBaseAddress
,
403 RuntimeMmioRegionSize
,
404 EFI_MEMORY_UC
| EFI_MEMORY_RUNTIME
406 ASSERT_EFI_ERROR (Status
);
408 mFlashNvStorageVariableBase
= (PcdGet64 (PcdFlashNvStorageVariableBase64
) != 0) ?
409 PcdGet64 (PcdFlashNvStorageVariableBase64
) : PcdGet32 (PcdFlashNvStorageVariableBase
);
411 // Set the index of the first LBA for the FVB
412 Instance
->StartLba
= (mFlashNvStorageVariableBase
- Instance
->RegionBaseAddress
) / Instance
->BlockSize
;
414 BootMode
= GetBootModeHob ();
415 if (BootMode
== BOOT_WITH_DEFAULT_SETTINGS
) {
416 Status
= EFI_INVALID_PARAMETER
;
418 // Determine if there is a valid header at the beginning of the NorFlash
419 Status
= ValidateFvHeader (Instance
);
422 // Install the Default FVB header if required
423 if (EFI_ERROR (Status
)) {
424 // There is no valid header, so time to install one.
425 DEBUG ((DEBUG_INFO
, "%a: The FVB Header is not valid.\n", __FUNCTION__
));
428 "%a: Installing a correct one for this volume.\n",
432 // Erase all the NorFlash that is reserved for variable storage
433 FvbNumLba
= (PcdGet32 (PcdFlashNvStorageVariableSize
) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize
) + PcdGet32 (PcdFlashNvStorageFtwSpareSize
)) / Instance
->BlockSize
;
435 Status
= FvbEraseBlocks (&Instance
->FvbProtocol
, (EFI_LBA
)0, FvbNumLba
, EFI_LBA_LIST_TERMINATOR
);
436 if (EFI_ERROR (Status
)) {
440 // Install all appropriate headers
441 Status
= InitializeFvAndVariableStoreHeaders (Instance
);
442 if (EFI_ERROR (Status
)) {
448 // The driver implementing the variable read service can now be dispatched;
449 // the varstore headers are in place.
451 Status
= gBS
->InstallProtocolInterface (
453 &gEdkiiNvVarStoreFormattedGuid
,
454 EFI_NATIVE_INTERFACE
,
457 ASSERT_EFI_ERROR (Status
);
460 // Register for the virtual address change event
462 Status
= gBS
->CreateEventEx (
465 FvbVirtualNotifyEvent
,
467 &gEfiEventVirtualAddressChangeGuid
,
468 &mFvbVirtualAddrChangeEvent
470 ASSERT_EFI_ERROR (Status
);