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