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