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