]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.c
ArmPlatformPkg: Change use of EFI_D_* to DEBUG_*
[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 } }, // GUID ... NEED TO BE FILLED\r
89 },\r
90 0, // Index\r
91 {\r
92 END_DEVICE_PATH_TYPE,\r
93 END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
94 { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }\r
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
104 IN UINT32 Index,\r
105 IN UINT32 BlockSize,\r
106 IN BOOLEAN SupportFvb,\r
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
115 Instance = AllocateRuntimeCopyPool (sizeof(NOR_FLASH_INSTANCE),&mNorFlashInstanceTemplate);\r
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
125 Instance->Media.MediaId = Index;\r
126 Instance->Media.BlockSize = BlockSize;\r
127 Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1;\r
128\r
129 CopyGuid (&Instance->DevicePath.Vendor.Guid, &gEfiCallerIdGuid);\r
130 Instance->DevicePath.Index = (UINT8)Index;\r
131\r
132 Instance->ShadowBuffer = AllocateRuntimePool (BlockSize);;\r
133 if (Instance->ShadowBuffer == NULL) {\r
134 return EFI_OUT_OF_RESOURCES;\r
135 }\r
136\r
137 if (SupportFvb) {\r
138 NorFlashFvbInitialize (Instance);\r
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
148 FreePool (Instance);\r
149 return Status;\r
150 }\r
151 } else {\r
152 Status = gBS->InstallMultipleProtocolInterfaces (\r
153 &Instance->Handle,\r
154 &gEfiDevicePathProtocolGuid, &Instance->DevicePath,\r
155 &gEfiBlockIoProtocolGuid, &Instance->BlockIoProtocol,\r
156 &gEfiDiskIoProtocolGuid, &Instance->DiskIoProtocol,\r
157 NULL\r
158 );\r
159 if (EFI_ERROR(Status)) {\r
160 FreePool (Instance);\r
161 return Status;\r
162 }\r
163 }\r
164\r
165 *NorFlashInstance = Instance;\r
166 return Status;\r
167}\r
168\r
169/**\r
170 * This function unlock and erase an entire NOR Flash block.\r
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
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
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
196 if (EFI_ERROR (Status)) {\r
197 break;\r
198 }\r
199 Status = NorFlashEraseSingleBlock (Instance, BlockAddress);\r
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((DEBUG_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress,Index));\r
205 }\r
206\r
207 if (!EfiAtRuntime ()) {\r
208 // Interruptions can resume.\r
209 gBS->RestoreTPL (OriginalTPL);\r
210 }\r
211\r
212 return Status;\r
213}\r
214\r
215EFI_STATUS\r
216NorFlashWriteFullBlock (\r
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
231 UINTN Cnt;\r
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
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
249\r
250 Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress);\r
251 if (EFI_ERROR(Status)) {\r
252 DEBUG((DEBUG_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
262 BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES;\r
263\r
264 // Then feed each buffer chunk to the NOR Flash\r
265 // If a buffer does not contain any data, don't write it.\r
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
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
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
307 if (!EfiAtRuntime ()) {\r
308 // Interruptions can resume.\r
309 gBS->RestoreTPL (OriginalTPL);\r
310 }\r
311\r
312 if (EFI_ERROR(Status)) {\r
313 DEBUG((DEBUG_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
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
328 BOOLEAN ContainVariableStorage;\r
329\r
330 Status = NorFlashPlatformInitialization ();\r
331 if (EFI_ERROR(Status)) {\r
332 DEBUG((DEBUG_ERROR,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));\r
333 return Status;\r
334 }\r
335\r
336 Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);\r
337 if (EFI_ERROR(Status)) {\r
338 DEBUG((DEBUG_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n"));\r
339 return Status;\r
340 }\r
341\r
342 mNorFlashInstances = AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE*) * mNorFlashDeviceCount);\r
343\r
344 for (Index = 0; Index < mNorFlashDeviceCount; Index++) {\r
345 // Check if this NOR Flash device contain the variable storage region\r
346\r
347 if (PcdGet64 (PcdFlashNvStorageVariableBase64) != 0) {\r
348 ContainVariableStorage =\r
349 (NorFlashDevices[Index].RegionBaseAddress <= PcdGet64 (PcdFlashNvStorageVariableBase64)) &&\r
350 (PcdGet64 (PcdFlashNvStorageVariableBase64) + PcdGet32 (PcdFlashNvStorageVariableSize) <=\r
351 NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);\r
352 } else {\r
353 ContainVariableStorage =\r
354 (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) &&\r
355 (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <=\r
356 NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);\r
357 }\r
358\r
359 Status = NorFlashCreateInstance (\r
360 NorFlashDevices[Index].DeviceBaseAddress,\r
361 NorFlashDevices[Index].RegionBaseAddress,\r
362 NorFlashDevices[Index].Size,\r
363 Index,\r
364 NorFlashDevices[Index].BlockSize,\r
365 ContainVariableStorage,\r
366 &mNorFlashInstances[Index]\r
367 );\r
368 if (EFI_ERROR(Status)) {\r
369 DEBUG((DEBUG_ERROR,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index));\r
370 }\r
371 }\r
372\r
373 //\r
374 // Register for the virtual address change event\r
375 //\r
376 Status = gBS->CreateEventEx (\r
377 EVT_NOTIFY_SIGNAL,\r
378 TPL_NOTIFY,\r
379 NorFlashVirtualNotifyEvent,\r
380 NULL,\r
381 &gEfiEventVirtualAddressChangeGuid,\r
382 &mNorFlashVirtualAddrChangeEvent\r
383 );\r
384 ASSERT_EFI_ERROR (Status);\r
385\r
386 return Status;\r
387}\r
388\r
389EFI_STATUS\r
390EFIAPI\r
391NorFlashFvbInitialize (\r
392 IN NOR_FLASH_INSTANCE* Instance\r
393 )\r
394{\r
395 EFI_STATUS Status;\r
396 UINT32 FvbNumLba;\r
397 EFI_BOOT_MODE BootMode;\r
398 UINTN RuntimeMmioRegionSize;\r
399\r
400 DEBUG((DEBUG_BLKIO,"NorFlashFvbInitialize\n"));\r
401 ASSERT((Instance != NULL));\r
402\r
403 //\r
404 // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME\r
405 //\r
406\r
407 // Note: all the NOR Flash region needs to be reserved into the UEFI Runtime memory;\r
408 // even if we only use the small block region at the top of the NOR Flash.\r
409 // The reason is when the NOR Flash memory is set into program mode, the command\r
410 // is written as the base of the flash region (ie: Instance->DeviceBaseAddress)\r
411 RuntimeMmioRegionSize = (Instance->RegionBaseAddress - Instance->DeviceBaseAddress) + Instance->Size;\r
412\r
413 Status = gDS->AddMemorySpace (\r
414 EfiGcdMemoryTypeMemoryMappedIo,\r
415 Instance->DeviceBaseAddress, RuntimeMmioRegionSize,\r
416 EFI_MEMORY_UC | EFI_MEMORY_RUNTIME\r
417 );\r
418 ASSERT_EFI_ERROR (Status);\r
419\r
420 Status = gDS->SetMemorySpaceAttributes (\r
421 Instance->DeviceBaseAddress, RuntimeMmioRegionSize,\r
422 EFI_MEMORY_UC | EFI_MEMORY_RUNTIME);\r
423 ASSERT_EFI_ERROR (Status);\r
424\r
425 mFlashNvStorageVariableBase = (PcdGet64 (PcdFlashNvStorageVariableBase64) != 0) ?\r
426 PcdGet64 (PcdFlashNvStorageVariableBase64) : PcdGet32 (PcdFlashNvStorageVariableBase);\r
427\r
428 // Set the index of the first LBA for the FVB\r
429 Instance->StartLba = (mFlashNvStorageVariableBase - Instance->RegionBaseAddress) / Instance->Media.BlockSize;\r
430\r
431 BootMode = GetBootModeHob ();\r
432 if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) {\r
433 Status = EFI_INVALID_PARAMETER;\r
434 } else {\r
435 // Determine if there is a valid header at the beginning of the NorFlash\r
436 Status = ValidateFvHeader (Instance);\r
437 }\r
438\r
439 // Install the Default FVB header if required\r
440 if (EFI_ERROR(Status)) {\r
441 // There is no valid header, so time to install one.\r
442 DEBUG ((DEBUG_INFO, "%a: The FVB Header is not valid.\n", __FUNCTION__));\r
443 DEBUG ((DEBUG_INFO, "%a: Installing a correct one for this volume.\n",\r
444 __FUNCTION__));\r
445\r
446 // Erase all the NorFlash that is reserved for variable storage\r
447 FvbNumLba = (PcdGet32(PcdFlashNvStorageVariableSize) + PcdGet32(PcdFlashNvStorageFtwWorkingSize) + PcdGet32(PcdFlashNvStorageFtwSpareSize)) / Instance->Media.BlockSize;\r
448\r
449 Status = FvbEraseBlocks (&Instance->FvbProtocol, (EFI_LBA)0, FvbNumLba, EFI_LBA_LIST_TERMINATOR);\r
450 if (EFI_ERROR(Status)) {\r
451 return Status;\r
452 }\r
453\r
454 // Install all appropriate headers\r
455 Status = InitializeFvAndVariableStoreHeaders (Instance);\r
456 if (EFI_ERROR(Status)) {\r
457 return Status;\r
458 }\r
459 }\r
460\r
461 //\r
462 // The driver implementing the variable read service can now be dispatched;\r
463 // the varstore headers are in place.\r
464 //\r
465 Status = gBS->InstallProtocolInterface (\r
466 &gImageHandle,\r
467 &gEdkiiNvVarStoreFormattedGuid,\r
468 EFI_NATIVE_INTERFACE,\r
469 NULL\r
470 );\r
471 ASSERT_EFI_ERROR (Status);\r
472\r
473 //\r
474 // Register for the virtual address change event\r
475 //\r
476 Status = gBS->CreateEventEx (\r
477 EVT_NOTIFY_SIGNAL,\r
478 TPL_NOTIFY,\r
479 FvbVirtualNotifyEvent,\r
480 NULL,\r
481 &gEfiEventVirtualAddressChangeGuid,\r
482 &mFvbVirtualAddrChangeEvent\r
483 );\r
484 ASSERT_EFI_ERROR (Status);\r
485\r
486 return Status;\r
487}\r