]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.c
ArmPlatformPkg: Apply uncrustify changes
[mirror_edk2.git] / ArmPlatformPkg / Drivers / NorFlashDxe / NorFlashDxe.c
... / ...
CommitLineData
1/** @file NorFlashDxe.c\r
2\r
3 Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.<BR>\r
4\r
5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
6\r
7**/\r
8\r
9#include <Library/UefiLib.h>\r
10#include <Library/BaseMemoryLib.h>\r
11#include <Library/MemoryAllocationLib.h>\r
12#include <Library/UefiBootServicesTableLib.h>\r
13#include <Library/PcdLib.h>\r
14#include <Library/HobLib.h>\r
15#include <Library/DxeServicesTableLib.h>\r
16\r
17#include "NorFlash.h"\r
18\r
19STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent;\r
20\r
21//\r
22// Global variable declarations\r
23//\r
24NOR_FLASH_INSTANCE **mNorFlashInstances;\r
25UINT32 mNorFlashDeviceCount;\r
26UINTN mFlashNvStorageVariableBase;\r
27EFI_EVENT mFvbVirtualAddrChangeEvent;\r
28\r
29NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = {\r
30 NOR_FLASH_SIGNATURE, // Signature\r
31 NULL, // Handle ... NEED TO BE FILLED\r
32\r
33 0, // DeviceBaseAddress ... NEED TO BE FILLED\r
34 0, // RegionBaseAddress ... NEED TO BE FILLED\r
35 0, // Size ... NEED TO BE FILLED\r
36 0, // StartLba\r
37\r
38 {\r
39 EFI_BLOCK_IO_PROTOCOL_REVISION2, // Revision\r
40 NULL, // Media ... NEED TO BE FILLED\r
41 NorFlashBlockIoReset, // Reset;\r
42 NorFlashBlockIoReadBlocks, // ReadBlocks\r
43 NorFlashBlockIoWriteBlocks, // WriteBlocks\r
44 NorFlashBlockIoFlushBlocks // FlushBlocks\r
45 }, // BlockIoProtocol\r
46\r
47 {\r
48 0, // MediaId ... NEED TO BE FILLED\r
49 FALSE, // RemovableMedia\r
50 TRUE, // MediaPresent\r
51 FALSE, // LogicalPartition\r
52 FALSE, // ReadOnly\r
53 FALSE, // WriteCaching;\r
54 0, // BlockSize ... NEED TO BE FILLED\r
55 4, // IoAlign\r
56 0, // LastBlock ... NEED TO BE FILLED\r
57 0, // LowestAlignedLba\r
58 1, // LogicalBlocksPerPhysicalBlock\r
59 }, // Media;\r
60\r
61 {\r
62 EFI_DISK_IO_PROTOCOL_REVISION, // Revision\r
63 NorFlashDiskIoReadDisk, // ReadDisk\r
64 NorFlashDiskIoWriteDisk // WriteDisk\r
65 },\r
66\r
67 {\r
68 FvbGetAttributes, // GetAttributes\r
69 FvbSetAttributes, // SetAttributes\r
70 FvbGetPhysicalAddress, // GetPhysicalAddress\r
71 FvbGetBlockSize, // GetBlockSize\r
72 FvbRead, // Read\r
73 FvbWrite, // Write\r
74 FvbEraseBlocks, // EraseBlocks\r
75 NULL, // ParentHandle\r
76 }, // FvbProtoccol;\r
77 NULL, // ShadowBuffer\r
78 {\r
79 {\r
80 {\r
81 HARDWARE_DEVICE_PATH,\r
82 HW_VENDOR_DP,\r
83 {\r
84 (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End)),\r
85 (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End) >> 8)\r
86 }\r
87 },\r
88 { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }\r
89 }, // GUID ... NEED TO BE FILLED\r
90 },\r
91 0, // Index\r
92 {\r
93 END_DEVICE_PATH_TYPE,\r
94 END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
95 { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }\r
96 }\r
97 } // DevicePath\r
98};\r
99\r
100EFI_STATUS\r
101NorFlashCreateInstance (\r
102 IN UINTN NorFlashDeviceBase,\r
103 IN UINTN NorFlashRegionBase,\r
104 IN UINTN NorFlashSize,\r
105 IN UINT32 Index,\r
106 IN UINT32 BlockSize,\r
107 IN BOOLEAN SupportFvb,\r
108 OUT NOR_FLASH_INSTANCE **NorFlashInstance\r
109 )\r
110{\r
111 EFI_STATUS Status;\r
112 NOR_FLASH_INSTANCE *Instance;\r
113\r
114 ASSERT (NorFlashInstance != NULL);\r
115\r
116 Instance = AllocateRuntimeCopyPool (sizeof (NOR_FLASH_INSTANCE), &mNorFlashInstanceTemplate);\r
117 if (Instance == NULL) {\r
118 return EFI_OUT_OF_RESOURCES;\r
119 }\r
120\r
121 Instance->DeviceBaseAddress = NorFlashDeviceBase;\r
122 Instance->RegionBaseAddress = NorFlashRegionBase;\r
123 Instance->Size = NorFlashSize;\r
124\r
125 Instance->BlockIoProtocol.Media = &Instance->Media;\r
126 Instance->Media.MediaId = Index;\r
127 Instance->Media.BlockSize = BlockSize;\r
128 Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1;\r
129\r
130 CopyGuid (&Instance->DevicePath.Vendor.Guid, &gEfiCallerIdGuid);\r
131 Instance->DevicePath.Index = (UINT8)Index;\r
132\r
133 Instance->ShadowBuffer = AllocateRuntimePool (BlockSize);\r
134 if (Instance->ShadowBuffer == NULL) {\r
135 return EFI_OUT_OF_RESOURCES;\r
136 }\r
137\r
138 if (SupportFvb) {\r
139 NorFlashFvbInitialize (Instance);\r
140\r
141 Status = gBS->InstallMultipleProtocolInterfaces (\r
142 &Instance->Handle,\r
143 &gEfiDevicePathProtocolGuid,\r
144 &Instance->DevicePath,\r
145 &gEfiBlockIoProtocolGuid,\r
146 &Instance->BlockIoProtocol,\r
147 &gEfiFirmwareVolumeBlockProtocolGuid,\r
148 &Instance->FvbProtocol,\r
149 NULL\r
150 );\r
151 if (EFI_ERROR (Status)) {\r
152 FreePool (Instance);\r
153 return Status;\r
154 }\r
155 } else {\r
156 Status = gBS->InstallMultipleProtocolInterfaces (\r
157 &Instance->Handle,\r
158 &gEfiDevicePathProtocolGuid,\r
159 &Instance->DevicePath,\r
160 &gEfiBlockIoProtocolGuid,\r
161 &Instance->BlockIoProtocol,\r
162 &gEfiDiskIoProtocolGuid,\r
163 &Instance->DiskIoProtocol,\r
164 NULL\r
165 );\r
166 if (EFI_ERROR (Status)) {\r
167 FreePool (Instance);\r
168 return Status;\r
169 }\r
170 }\r
171\r
172 *NorFlashInstance = Instance;\r
173 return Status;\r
174}\r
175\r
176/**\r
177 * This function unlock and erase an entire NOR Flash block.\r
178 **/\r
179EFI_STATUS\r
180NorFlashUnlockAndEraseSingleBlock (\r
181 IN NOR_FLASH_INSTANCE *Instance,\r
182 IN UINTN BlockAddress\r
183 )\r
184{\r
185 EFI_STATUS Status;\r
186 UINTN Index;\r
187 EFI_TPL OriginalTPL;\r
188\r
189 if (!EfiAtRuntime ()) {\r
190 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.\r
191 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
192 } else {\r
193 // This initialization is only to prevent the compiler to complain about the\r
194 // use of uninitialized variables\r
195 OriginalTPL = TPL_HIGH_LEVEL;\r
196 }\r
197\r
198 Index = 0;\r
199 // The block erase might fail a first time (SW bug ?). Retry it ...\r
200 do {\r
201 // Unlock the block if we have to\r
202 Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);\r
203 if (EFI_ERROR (Status)) {\r
204 break;\r
205 }\r
206\r
207 Status = NorFlashEraseSingleBlock (Instance, BlockAddress);\r
208 Index++;\r
209 } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED));\r
210\r
211 if (Index == NOR_FLASH_ERASE_RETRY) {\r
212 DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress, Index));\r
213 }\r
214\r
215 if (!EfiAtRuntime ()) {\r
216 // Interruptions can resume.\r
217 gBS->RestoreTPL (OriginalTPL);\r
218 }\r
219\r
220 return Status;\r
221}\r
222\r
223EFI_STATUS\r
224NorFlashWriteFullBlock (\r
225 IN NOR_FLASH_INSTANCE *Instance,\r
226 IN EFI_LBA Lba,\r
227 IN UINT32 *DataBuffer,\r
228 IN UINT32 BlockSizeInWords\r
229 )\r
230{\r
231 EFI_STATUS Status;\r
232 UINTN WordAddress;\r
233 UINT32 WordIndex;\r
234 UINTN BufferIndex;\r
235 UINTN BlockAddress;\r
236 UINTN BuffersInBlock;\r
237 UINTN RemainingWords;\r
238 EFI_TPL OriginalTPL;\r
239 UINTN Cnt;\r
240\r
241 Status = EFI_SUCCESS;\r
242\r
243 // Get the physical address of the block\r
244 BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSizeInWords * 4);\r
245\r
246 // Start writing from the first address at the start of the block\r
247 WordAddress = BlockAddress;\r
248\r
249 if (!EfiAtRuntime ()) {\r
250 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.\r
251 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
252 } else {\r
253 // This initialization is only to prevent the compiler to complain about the\r
254 // use of uninitialized variables\r
255 OriginalTPL = TPL_HIGH_LEVEL;\r
256 }\r
257\r
258 Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress);\r
259 if (EFI_ERROR (Status)) {\r
260 DEBUG ((DEBUG_ERROR, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress));\r
261 goto EXIT;\r
262 }\r
263\r
264 // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.\r
265\r
266 // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero\r
267 if ((WordAddress & BOUNDARY_OF_32_WORDS) == 0x00) {\r
268 // First, break the entire block into buffer-sized chunks.\r
269 BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES;\r
270\r
271 // Then feed each buffer chunk to the NOR Flash\r
272 // If a buffer does not contain any data, don't write it.\r
273 for (BufferIndex = 0;\r
274 BufferIndex < BuffersInBlock;\r
275 BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS\r
276 )\r
277 {\r
278 // Check the buffer to see if it contains any data (not set all 1s).\r
279 for (Cnt = 0; Cnt < P30_MAX_BUFFER_SIZE_IN_WORDS; Cnt++) {\r
280 if (~DataBuffer[Cnt] != 0 ) {\r
281 // Some data found, write the buffer.\r
282 Status = NorFlashWriteBuffer (\r
283 Instance,\r
284 WordAddress,\r
285 P30_MAX_BUFFER_SIZE_IN_BYTES,\r
286 DataBuffer\r
287 );\r
288 if (EFI_ERROR (Status)) {\r
289 goto EXIT;\r
290 }\r
291\r
292 break;\r
293 }\r
294 }\r
295 }\r
296\r
297 // Finally, finish off any remaining words that are less than the maximum size of the buffer\r
298 RemainingWords = BlockSizeInWords % P30_MAX_BUFFER_SIZE_IN_WORDS;\r
299\r
300 if (RemainingWords != 0) {\r
301 Status = NorFlashWriteBuffer (Instance, WordAddress, (RemainingWords * 4), DataBuffer);\r
302 if (EFI_ERROR (Status)) {\r
303 goto EXIT;\r
304 }\r
305 }\r
306 } else {\r
307 // For now, use the single word programming algorithm\r
308 // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,\r
309 // i.e. which ends in the range 0x......01 - 0x......7F.\r
310 for (WordIndex = 0; WordIndex < BlockSizeInWords; WordIndex++, DataBuffer++, WordAddress = WordAddress + 4) {\r
311 Status = NorFlashWriteSingleWord (Instance, WordAddress, *DataBuffer);\r
312 if (EFI_ERROR (Status)) {\r
313 goto EXIT;\r
314 }\r
315 }\r
316 }\r
317\r
318EXIT:\r
319 if (!EfiAtRuntime ()) {\r
320 // Interruptions can resume.\r
321 gBS->RestoreTPL (OriginalTPL);\r
322 }\r
323\r
324 if (EFI_ERROR (Status)) {\r
325 DEBUG ((DEBUG_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status));\r
326 }\r
327\r
328 return Status;\r
329}\r
330\r
331EFI_STATUS\r
332EFIAPI\r
333NorFlashInitialise (\r
334 IN EFI_HANDLE ImageHandle,\r
335 IN EFI_SYSTEM_TABLE *SystemTable\r
336 )\r
337{\r
338 EFI_STATUS Status;\r
339 UINT32 Index;\r
340 NOR_FLASH_DESCRIPTION *NorFlashDevices;\r
341 BOOLEAN ContainVariableStorage;\r
342\r
343 Status = NorFlashPlatformInitialization ();\r
344 if (EFI_ERROR (Status)) {\r
345 DEBUG ((DEBUG_ERROR, "NorFlashInitialise: Fail to initialize Nor Flash devices\n"));\r
346 return Status;\r
347 }\r
348\r
349 Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);\r
350 if (EFI_ERROR (Status)) {\r
351 DEBUG ((DEBUG_ERROR, "NorFlashInitialise: Fail to get Nor Flash devices\n"));\r
352 return Status;\r
353 }\r
354\r
355 mNorFlashInstances = AllocateRuntimePool (sizeof (NOR_FLASH_INSTANCE *) * mNorFlashDeviceCount);\r
356\r
357 for (Index = 0; Index < mNorFlashDeviceCount; Index++) {\r
358 // Check if this NOR Flash device contain the variable storage region\r
359\r
360 if (PcdGet64 (PcdFlashNvStorageVariableBase64) != 0) {\r
361 ContainVariableStorage =\r
362 (NorFlashDevices[Index].RegionBaseAddress <= PcdGet64 (PcdFlashNvStorageVariableBase64)) &&\r
363 (PcdGet64 (PcdFlashNvStorageVariableBase64) + PcdGet32 (PcdFlashNvStorageVariableSize) <=\r
364 NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);\r
365 } else {\r
366 ContainVariableStorage =\r
367 (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) &&\r
368 (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <=\r
369 NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);\r
370 }\r
371\r
372 Status = NorFlashCreateInstance (\r
373 NorFlashDevices[Index].DeviceBaseAddress,\r
374 NorFlashDevices[Index].RegionBaseAddress,\r
375 NorFlashDevices[Index].Size,\r
376 Index,\r
377 NorFlashDevices[Index].BlockSize,\r
378 ContainVariableStorage,\r
379 &mNorFlashInstances[Index]\r
380 );\r
381 if (EFI_ERROR (Status)) {\r
382 DEBUG ((DEBUG_ERROR, "NorFlashInitialise: Fail to create instance for NorFlash[%d]\n", Index));\r
383 }\r
384 }\r
385\r
386 //\r
387 // Register for the virtual address change event\r
388 //\r
389 Status = gBS->CreateEventEx (\r
390 EVT_NOTIFY_SIGNAL,\r
391 TPL_NOTIFY,\r
392 NorFlashVirtualNotifyEvent,\r
393 NULL,\r
394 &gEfiEventVirtualAddressChangeGuid,\r
395 &mNorFlashVirtualAddrChangeEvent\r
396 );\r
397 ASSERT_EFI_ERROR (Status);\r
398\r
399 return Status;\r
400}\r
401\r
402EFI_STATUS\r
403EFIAPI\r
404NorFlashFvbInitialize (\r
405 IN NOR_FLASH_INSTANCE *Instance\r
406 )\r
407{\r
408 EFI_STATUS Status;\r
409 UINT32 FvbNumLba;\r
410 EFI_BOOT_MODE BootMode;\r
411 UINTN RuntimeMmioRegionSize;\r
412\r
413 DEBUG ((DEBUG_BLKIO, "NorFlashFvbInitialize\n"));\r
414 ASSERT ((Instance != NULL));\r
415\r
416 //\r
417 // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME\r
418 //\r
419\r
420 // Note: all the NOR Flash region needs to be reserved into the UEFI Runtime memory;\r
421 // even if we only use the small block region at the top of the NOR Flash.\r
422 // The reason is when the NOR Flash memory is set into program mode, the command\r
423 // is written as the base of the flash region (ie: Instance->DeviceBaseAddress)\r
424 RuntimeMmioRegionSize = (Instance->RegionBaseAddress - Instance->DeviceBaseAddress) + Instance->Size;\r
425\r
426 Status = gDS->AddMemorySpace (\r
427 EfiGcdMemoryTypeMemoryMappedIo,\r
428 Instance->DeviceBaseAddress,\r
429 RuntimeMmioRegionSize,\r
430 EFI_MEMORY_UC | EFI_MEMORY_RUNTIME\r
431 );\r
432 ASSERT_EFI_ERROR (Status);\r
433\r
434 Status = gDS->SetMemorySpaceAttributes (\r
435 Instance->DeviceBaseAddress,\r
436 RuntimeMmioRegionSize,\r
437 EFI_MEMORY_UC | EFI_MEMORY_RUNTIME\r
438 );\r
439 ASSERT_EFI_ERROR (Status);\r
440\r
441 mFlashNvStorageVariableBase = (PcdGet64 (PcdFlashNvStorageVariableBase64) != 0) ?\r
442 PcdGet64 (PcdFlashNvStorageVariableBase64) : PcdGet32 (PcdFlashNvStorageVariableBase);\r
443\r
444 // Set the index of the first LBA for the FVB\r
445 Instance->StartLba = (mFlashNvStorageVariableBase - Instance->RegionBaseAddress) / Instance->Media.BlockSize;\r
446\r
447 BootMode = GetBootModeHob ();\r
448 if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) {\r
449 Status = EFI_INVALID_PARAMETER;\r
450 } else {\r
451 // Determine if there is a valid header at the beginning of the NorFlash\r
452 Status = ValidateFvHeader (Instance);\r
453 }\r
454\r
455 // Install the Default FVB header if required\r
456 if (EFI_ERROR (Status)) {\r
457 // There is no valid header, so time to install one.\r
458 DEBUG ((DEBUG_INFO, "%a: The FVB Header is not valid.\n", __FUNCTION__));\r
459 DEBUG ((\r
460 DEBUG_INFO,\r
461 "%a: Installing a correct one for this volume.\n",\r
462 __FUNCTION__\r
463 ));\r
464\r
465 // Erase all the NorFlash that is reserved for variable storage\r
466 FvbNumLba = (PcdGet32 (PcdFlashNvStorageVariableSize) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + PcdGet32 (PcdFlashNvStorageFtwSpareSize)) / Instance->Media.BlockSize;\r
467\r
468 Status = FvbEraseBlocks (&Instance->FvbProtocol, (EFI_LBA)0, FvbNumLba, EFI_LBA_LIST_TERMINATOR);\r
469 if (EFI_ERROR (Status)) {\r
470 return Status;\r
471 }\r
472\r
473 // Install all appropriate headers\r
474 Status = InitializeFvAndVariableStoreHeaders (Instance);\r
475 if (EFI_ERROR (Status)) {\r
476 return Status;\r
477 }\r
478 }\r
479\r
480 //\r
481 // The driver implementing the variable read service can now be dispatched;\r
482 // the varstore headers are in place.\r
483 //\r
484 Status = gBS->InstallProtocolInterface (\r
485 &gImageHandle,\r
486 &gEdkiiNvVarStoreFormattedGuid,\r
487 EFI_NATIVE_INTERFACE,\r
488 NULL\r
489 );\r
490 ASSERT_EFI_ERROR (Status);\r
491\r
492 //\r
493 // Register for the virtual address change event\r
494 //\r
495 Status = gBS->CreateEventEx (\r
496 EVT_NOTIFY_SIGNAL,\r
497 TPL_NOTIFY,\r
498 FvbVirtualNotifyEvent,\r
499 NULL,\r
500 &gEfiEventVirtualAddressChangeGuid,\r
501 &mFvbVirtualAddrChangeEvent\r
502 );\r
503 ASSERT_EFI_ERROR (Status);\r
504\r
505 return Status;\r
506}\r