]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashFvbDxe.c
EmbeddedPkg: introduce NvVarStoreFormattedLib
[mirror_edk2.git] / ArmPlatformPkg / Drivers / NorFlashDxe / NorFlashFvbDxe.c
CommitLineData
1e57a462 1/*++ @file NorFlashFvbDxe.c\r
2\r
2dff0c1a 3 Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>\r
1e57a462 4\r
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13 --*/\r
14\r
15#include <PiDxe.h>\r
16\r
17#include <Library/PcdLib.h>\r
18#include <Library/BaseLib.h>\r
19#include <Library/HobLib.h>\r
20#include <Library/UefiLib.h>\r
21#include <Library/BaseMemoryLib.h>\r
22#include <Library/MemoryAllocationLib.h>\r
1dbbfc17 23#include <Library/DxeServicesTableLib.h>\r
1e57a462 24#include <Library/UefiBootServicesTableLib.h>\r
25\r
26#include <Guid/VariableFormat.h>\r
27#include <Guid/SystemNvDataGuid.h>\r
28\r
29#include "NorFlashDxe.h"\r
30\r
1dbbfc17
OM
31STATIC EFI_EVENT mFvbVirtualAddrChangeEvent;\r
32STATIC UINTN mFlashNvStorageVariableBase;\r
1e57a462 33\r
34///\r
35/// The Firmware Volume Block Protocol is the low-level interface\r
36/// to a firmware volume. File-level access to a firmware volume\r
37/// should not be done using the Firmware Volume Block Protocol.\r
38/// Normal access to a firmware volume must use the Firmware\r
39/// Volume Protocol. Typically, only the file system driver that\r
40/// produces the Firmware Volume Protocol will bind to the\r
41/// Firmware Volume Block Protocol.\r
42///\r
43\r
44/**\r
45 Initialises the FV Header and Variable Store Header\r
46 to support variable operations.\r
47\r
48 @param[in] Ptr - Location to initialise the headers\r
49\r
50**/\r
51EFI_STATUS\r
52InitializeFvAndVariableStoreHeaders (\r
53 IN NOR_FLASH_INSTANCE *Instance\r
54 )\r
55{\r
56 EFI_STATUS Status;\r
57 VOID* Headers;\r
58 UINTN HeadersLength;\r
59 EFI_FIRMWARE_VOLUME_HEADER *FirmwareVolumeHeader;\r
60 VARIABLE_STORE_HEADER *VariableStoreHeader;\r
61\r
62 if (!Instance->Initialized && Instance->Initialize) {\r
63 Instance->Initialize (Instance);\r
64 }\r
65\r
66 HeadersLength = sizeof(EFI_FIRMWARE_VOLUME_HEADER) + sizeof(EFI_FV_BLOCK_MAP_ENTRY) + sizeof(VARIABLE_STORE_HEADER);\r
67 Headers = AllocateZeroPool(HeadersLength);\r
68\r
69 // FirmwareVolumeHeader->FvLength is declared to have the Variable area AND the FTW working area AND the FTW Spare contiguous.\r
70 ASSERT(PcdGet32(PcdFlashNvStorageVariableBase) + PcdGet32(PcdFlashNvStorageVariableSize) == PcdGet32(PcdFlashNvStorageFtwWorkingBase));\r
71 ASSERT(PcdGet32(PcdFlashNvStorageFtwWorkingBase) + PcdGet32(PcdFlashNvStorageFtwWorkingSize) == PcdGet32(PcdFlashNvStorageFtwSpareBase));\r
72\r
73 // Check if the size of the area is at least one block size\r
74 ASSERT((PcdGet32(PcdFlashNvStorageVariableSize) > 0) && (PcdGet32(PcdFlashNvStorageVariableSize) / Instance->Media.BlockSize > 0));\r
75 ASSERT((PcdGet32(PcdFlashNvStorageFtwWorkingSize) > 0) && (PcdGet32(PcdFlashNvStorageFtwWorkingSize) / Instance->Media.BlockSize > 0));\r
76 ASSERT((PcdGet32(PcdFlashNvStorageFtwSpareSize) > 0) && (PcdGet32(PcdFlashNvStorageFtwSpareSize) / Instance->Media.BlockSize > 0));\r
77\r
78 // Ensure the Variable area Base Addresses are aligned on a block size boundaries\r
79 ASSERT(PcdGet32(PcdFlashNvStorageVariableBase) % Instance->Media.BlockSize == 0);\r
80 ASSERT(PcdGet32(PcdFlashNvStorageFtwWorkingBase) % Instance->Media.BlockSize == 0);\r
81 ASSERT(PcdGet32(PcdFlashNvStorageFtwSpareBase) % Instance->Media.BlockSize == 0);\r
82\r
83 //\r
84 // EFI_FIRMWARE_VOLUME_HEADER\r
85 //\r
86 FirmwareVolumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)Headers;\r
87 CopyGuid (&FirmwareVolumeHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid);\r
88 FirmwareVolumeHeader->FvLength =\r
89 PcdGet32(PcdFlashNvStorageVariableSize) +\r
90 PcdGet32(PcdFlashNvStorageFtwWorkingSize) +\r
91 PcdGet32(PcdFlashNvStorageFtwSpareSize);\r
92 FirmwareVolumeHeader->Signature = EFI_FVH_SIGNATURE;\r
93 FirmwareVolumeHeader->Attributes = (EFI_FVB_ATTRIBUTES_2) (\r
94 EFI_FVB2_READ_ENABLED_CAP | // Reads may be enabled\r
95 EFI_FVB2_READ_STATUS | // Reads are currently enabled\r
96 EFI_FVB2_STICKY_WRITE | // A block erase is required to flip bits into EFI_FVB2_ERASE_POLARITY\r
97 EFI_FVB2_MEMORY_MAPPED | // It is memory mapped\r
98 EFI_FVB2_ERASE_POLARITY | // After erasure all bits take this value (i.e. '1')\r
99 EFI_FVB2_WRITE_STATUS | // Writes are currently enabled\r
100 EFI_FVB2_WRITE_ENABLED_CAP // Writes may be enabled\r
101 );\r
102 FirmwareVolumeHeader->HeaderLength = sizeof(EFI_FIRMWARE_VOLUME_HEADER) + sizeof(EFI_FV_BLOCK_MAP_ENTRY);\r
103 FirmwareVolumeHeader->Revision = EFI_FVH_REVISION;\r
104 FirmwareVolumeHeader->BlockMap[0].NumBlocks = Instance->Media.LastBlock + 1;\r
105 FirmwareVolumeHeader->BlockMap[0].Length = Instance->Media.BlockSize;\r
106 FirmwareVolumeHeader->BlockMap[1].NumBlocks = 0;\r
107 FirmwareVolumeHeader->BlockMap[1].Length = 0;\r
108 FirmwareVolumeHeader->Checksum = CalculateCheckSum16 ((UINT16*)FirmwareVolumeHeader,FirmwareVolumeHeader->HeaderLength);\r
109\r
110 //\r
111 // VARIABLE_STORE_HEADER\r
112 //\r
d5cd447b 113 VariableStoreHeader = (VARIABLE_STORE_HEADER*)((UINTN)Headers + FirmwareVolumeHeader->HeaderLength);\r
8753858f 114 CopyGuid (&VariableStoreHeader->Signature, &gEfiAuthenticatedVariableGuid);\r
1e57a462 115 VariableStoreHeader->Size = PcdGet32(PcdFlashNvStorageVariableSize) - FirmwareVolumeHeader->HeaderLength;\r
116 VariableStoreHeader->Format = VARIABLE_STORE_FORMATTED;\r
117 VariableStoreHeader->State = VARIABLE_STORE_HEALTHY;\r
118\r
119 // Install the combined super-header in the NorFlash\r
120 Status = FvbWrite (&Instance->FvbProtocol, 0, 0, &HeadersLength, Headers);\r
121\r
122 FreePool (Headers);\r
123 return Status;\r
124}\r
125\r
126/**\r
127 Check the integrity of firmware volume header.\r
128\r
129 @param[in] FwVolHeader - A pointer to a firmware volume header\r
130\r
131 @retval EFI_SUCCESS - The firmware volume is consistent\r
132 @retval EFI_NOT_FOUND - The firmware volume has been corrupted.\r
133\r
134**/\r
135EFI_STATUS\r
136ValidateFvHeader (\r
137 IN NOR_FLASH_INSTANCE *Instance\r
138 )\r
139{\r
140 UINT16 Checksum;\r
141 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;\r
142 VARIABLE_STORE_HEADER *VariableStoreHeader;\r
143 UINTN VariableStoreLength;\r
91c38d4e 144 UINTN FvLength;\r
1e57a462 145\r
146 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER*)Instance->RegionBaseAddress;\r
147\r
148 FvLength = PcdGet32(PcdFlashNvStorageVariableSize) + PcdGet32(PcdFlashNvStorageFtwWorkingSize) +\r
149 PcdGet32(PcdFlashNvStorageFtwSpareSize);\r
150\r
151 //\r
152 // Verify the header revision, header signature, length\r
153 // Length of FvBlock cannot be 2**64-1\r
154 // HeaderLength cannot be an odd number\r
155 //\r
156 if ( (FwVolHeader->Revision != EFI_FVH_REVISION)\r
157 || (FwVolHeader->Signature != EFI_FVH_SIGNATURE)\r
158 || (FwVolHeader->FvLength != FvLength)\r
159 )\r
160 {\r
582085eb
LE
161 DEBUG ((EFI_D_INFO, "%a: No Firmware Volume header present\n",\r
162 __FUNCTION__));\r
1e57a462 163 return EFI_NOT_FOUND;\r
164 }\r
165\r
166 // Check the Firmware Volume Guid\r
167 if( CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid) == FALSE ) {\r
582085eb
LE
168 DEBUG ((EFI_D_INFO, "%a: Firmware Volume Guid non-compatible\n",\r
169 __FUNCTION__));\r
1e57a462 170 return EFI_NOT_FOUND;\r
171 }\r
172\r
173 // Verify the header checksum\r
174 Checksum = CalculateSum16((UINT16*)FwVolHeader, FwVolHeader->HeaderLength);\r
175 if (Checksum != 0) {\r
582085eb
LE
176 DEBUG ((EFI_D_INFO, "%a: FV checksum is invalid (Checksum:0x%X)\n",\r
177 __FUNCTION__, Checksum));\r
1e57a462 178 return EFI_NOT_FOUND;\r
179 }\r
180\r
d5cd447b 181 VariableStoreHeader = (VARIABLE_STORE_HEADER*)((UINTN)FwVolHeader + FwVolHeader->HeaderLength);\r
1e57a462 182\r
183 // Check the Variable Store Guid\r
8753858f
AB
184 if (!CompareGuid (&VariableStoreHeader->Signature, &gEfiVariableGuid) &&\r
185 !CompareGuid (&VariableStoreHeader->Signature, &gEfiAuthenticatedVariableGuid)) {\r
582085eb
LE
186 DEBUG ((EFI_D_INFO, "%a: Variable Store Guid non-compatible\n",\r
187 __FUNCTION__));\r
1e57a462 188 return EFI_NOT_FOUND;\r
189 }\r
190\r
191 VariableStoreLength = PcdGet32 (PcdFlashNvStorageVariableSize) - FwVolHeader->HeaderLength;\r
192 if (VariableStoreHeader->Size != VariableStoreLength) {\r
582085eb
LE
193 DEBUG ((EFI_D_INFO, "%a: Variable Store Length does not match\n",\r
194 __FUNCTION__));\r
1e57a462 195 return EFI_NOT_FOUND;\r
196 }\r
197\r
198 return EFI_SUCCESS;\r
199}\r
200\r
201/**\r
202 The GetAttributes() function retrieves the attributes and\r
203 current settings of the block.\r
204\r
205 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.\r
206\r
207 @param Attributes Pointer to EFI_FVB_ATTRIBUTES_2 in which the attributes and\r
208 current settings are returned.\r
209 Type EFI_FVB_ATTRIBUTES_2 is defined in EFI_FIRMWARE_VOLUME_HEADER.\r
210\r
211 @retval EFI_SUCCESS The firmware volume attributes were returned.\r
212\r
213 **/\r
214EFI_STATUS\r
215EFIAPI\r
216FvbGetAttributes(\r
217 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,\r
218 OUT EFI_FVB_ATTRIBUTES_2 *Attributes\r
219 )\r
220{\r
221 EFI_FVB_ATTRIBUTES_2 FlashFvbAttributes;\r
222 NOR_FLASH_INSTANCE *Instance;\r
223\r
224 Instance = INSTANCE_FROM_FVB_THIS(This);\r
225\r
226 FlashFvbAttributes = (EFI_FVB_ATTRIBUTES_2) (\r
227\r
228 EFI_FVB2_READ_ENABLED_CAP | // Reads may be enabled\r
229 EFI_FVB2_READ_STATUS | // Reads are currently enabled\r
230 EFI_FVB2_STICKY_WRITE | // A block erase is required to flip bits into EFI_FVB2_ERASE_POLARITY\r
231 EFI_FVB2_MEMORY_MAPPED | // It is memory mapped\r
232 EFI_FVB2_ERASE_POLARITY // After erasure all bits take this value (i.e. '1')\r
233\r
234 );\r
235\r
236 // Check if it is write protected\r
237 if (Instance->Media.ReadOnly != TRUE) {\r
238\r
239 FlashFvbAttributes = FlashFvbAttributes |\r
240 EFI_FVB2_WRITE_STATUS | // Writes are currently enabled\r
241 EFI_FVB2_WRITE_ENABLED_CAP; // Writes may be enabled\r
242 }\r
243\r
244 *Attributes = FlashFvbAttributes;\r
245\r
246 DEBUG ((DEBUG_BLKIO, "FvbGetAttributes(0x%X)\n", *Attributes));\r
247\r
248 return EFI_SUCCESS;\r
249}\r
250\r
251/**\r
252 The SetAttributes() function sets configurable firmware volume attributes\r
253 and returns the new settings of the firmware volume.\r
254\r
255\r
256 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.\r
257\r
258 @param Attributes On input, Attributes is a pointer to EFI_FVB_ATTRIBUTES_2\r
259 that contains the desired firmware volume settings.\r
260 On successful return, it contains the new settings of\r
261 the firmware volume.\r
262 Type EFI_FVB_ATTRIBUTES_2 is defined in EFI_FIRMWARE_VOLUME_HEADER.\r
263\r
264 @retval EFI_SUCCESS The firmware volume attributes were returned.\r
265\r
266 @retval EFI_INVALID_PARAMETER The attributes requested are in conflict with the capabilities\r
267 as declared in the firmware volume header.\r
268\r
269 **/\r
270EFI_STATUS\r
271EFIAPI\r
272FvbSetAttributes(\r
273 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,\r
274 IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes\r
275 )\r
276{\r
277 DEBUG ((DEBUG_BLKIO, "FvbSetAttributes(0x%X) is not supported\n",*Attributes));\r
278 return EFI_UNSUPPORTED;\r
279}\r
280\r
281/**\r
282 The GetPhysicalAddress() function retrieves the base address of\r
283 a memory-mapped firmware volume. This function should be called\r
284 only for memory-mapped firmware volumes.\r
285\r
286 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.\r
287\r
288 @param Address Pointer to a caller-allocated\r
289 EFI_PHYSICAL_ADDRESS that, on successful\r
290 return from GetPhysicalAddress(), contains the\r
291 base address of the firmware volume.\r
292\r
293 @retval EFI_SUCCESS The firmware volume base address was returned.\r
294\r
295 @retval EFI_NOT_SUPPORTED The firmware volume is not memory mapped.\r
296\r
297 **/\r
298EFI_STATUS\r
299EFIAPI\r
300FvbGetPhysicalAddress (\r
301 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,\r
302 OUT EFI_PHYSICAL_ADDRESS *Address\r
303 )\r
304{\r
305 NOR_FLASH_INSTANCE *Instance;\r
306\r
307 Instance = INSTANCE_FROM_FVB_THIS(This);\r
308\r
309 DEBUG ((DEBUG_BLKIO, "FvbGetPhysicalAddress(BaseAddress=0x%08x)\n", Instance->RegionBaseAddress));\r
310\r
311 ASSERT(Address != NULL);\r
312\r
1dbbfc17 313 *Address = mFlashNvStorageVariableBase;\r
1e57a462 314 return EFI_SUCCESS;\r
315}\r
316\r
317/**\r
318 The GetBlockSize() function retrieves the size of the requested\r
319 block. It also returns the number of additional blocks with\r
320 the identical size. The GetBlockSize() function is used to\r
321 retrieve the block map (see EFI_FIRMWARE_VOLUME_HEADER).\r
322\r
323\r
324 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.\r
325\r
326 @param Lba Indicates the block for which to return the size.\r
327\r
328 @param BlockSize Pointer to a caller-allocated UINTN in which\r
329 the size of the block is returned.\r
330\r
331 @param NumberOfBlocks Pointer to a caller-allocated UINTN in\r
332 which the number of consecutive blocks,\r
333 starting with Lba, is returned. All\r
334 blocks in this range have a size of\r
335 BlockSize.\r
336\r
337\r
338 @retval EFI_SUCCESS The firmware volume base address was returned.\r
339\r
340 @retval EFI_INVALID_PARAMETER The requested LBA is out of range.\r
341\r
342 **/\r
343EFI_STATUS\r
344EFIAPI\r
345FvbGetBlockSize (\r
346 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,\r
347 IN EFI_LBA Lba,\r
348 OUT UINTN *BlockSize,\r
349 OUT UINTN *NumberOfBlocks\r
350 )\r
351{\r
352 EFI_STATUS Status;\r
353 NOR_FLASH_INSTANCE *Instance;\r
354\r
355 Instance = INSTANCE_FROM_FVB_THIS(This);\r
356\r
357 DEBUG ((DEBUG_BLKIO, "FvbGetBlockSize(Lba=%ld, BlockSize=0x%x, LastBlock=%ld)\n", Lba, Instance->Media.BlockSize, Instance->Media.LastBlock));\r
358\r
359 if (Lba > Instance->Media.LastBlock) {\r
360 DEBUG ((EFI_D_ERROR, "FvbGetBlockSize: ERROR - Parameter LBA %ld is beyond the last Lba (%ld).\n", Lba, Instance->Media.LastBlock));\r
361 Status = EFI_INVALID_PARAMETER;\r
362 } else {\r
363 // This is easy because in this platform each NorFlash device has equal sized blocks.\r
364 *BlockSize = (UINTN) Instance->Media.BlockSize;\r
365 *NumberOfBlocks = (UINTN) (Instance->Media.LastBlock - Lba + 1);\r
366\r
367 DEBUG ((DEBUG_BLKIO, "FvbGetBlockSize: *BlockSize=0x%x, *NumberOfBlocks=0x%x.\n", *BlockSize, *NumberOfBlocks));\r
368\r
369 Status = EFI_SUCCESS;\r
370 }\r
371\r
372 return Status;\r
373}\r
374\r
375/**\r
376 Reads the specified number of bytes into a buffer from the specified block.\r
377\r
378 The Read() function reads the requested number of bytes from the\r
379 requested block and stores them in the provided buffer.\r
380 Implementations should be mindful that the firmware volume\r
381 might be in the ReadDisabled state. If it is in this state,\r
382 the Read() function must return the status code\r
383 EFI_ACCESS_DENIED without modifying the contents of the\r
384 buffer. The Read() function must also prevent spanning block\r
385 boundaries. If a read is requested that would span a block\r
386 boundary, the read must read up to the boundary but not\r
387 beyond. The output parameter NumBytes must be set to correctly\r
388 indicate the number of bytes actually read. The caller must be\r
389 aware that a read may be partially completed.\r
390\r
391 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.\r
392\r
393 @param Lba The starting logical block index from which to read.\r
394\r
395 @param Offset Offset into the block at which to begin reading.\r
396\r
397 @param NumBytes Pointer to a UINTN.\r
398 At entry, *NumBytes contains the total size of the buffer.\r
399 At exit, *NumBytes contains the total number of bytes read.\r
400\r
401 @param Buffer Pointer to a caller-allocated buffer that will be used\r
402 to hold the data that is read.\r
403\r
404 @retval EFI_SUCCESS The firmware volume was read successfully, and contents are\r
405 in Buffer.\r
406\r
407 @retval EFI_BAD_BUFFER_SIZE Read attempted across an LBA boundary.\r
408 On output, NumBytes contains the total number of bytes\r
409 returned in Buffer.\r
410\r
411 @retval EFI_ACCESS_DENIED The firmware volume is in the ReadDisabled state.\r
412\r
413 @retval EFI_DEVICE_ERROR The block device is not functioning correctly and could not be read.\r
414\r
415 **/\r
416EFI_STATUS\r
417EFIAPI\r
418FvbRead (\r
419 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,\r
420 IN EFI_LBA Lba,\r
421 IN UINTN Offset,\r
422 IN OUT UINTN *NumBytes,\r
423 IN OUT UINT8 *Buffer\r
424 )\r
425{\r
1e57a462 426 EFI_STATUS TempStatus;\r
427 UINTN BlockSize;\r
1e57a462 428 NOR_FLASH_INSTANCE *Instance;\r
429\r
430 Instance = INSTANCE_FROM_FVB_THIS(This);\r
431\r
432 DEBUG ((DEBUG_BLKIO, "FvbRead(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Instance->StartLba + Lba, Offset, *NumBytes, Buffer));\r
433\r
434 if (!Instance->Initialized && Instance->Initialize) {\r
435 Instance->Initialize(Instance);\r
436 }\r
437\r
518c243d 438 TempStatus = EFI_SUCCESS;\r
1e57a462 439\r
440 // Cache the block size to avoid de-referencing pointers all the time\r
441 BlockSize = Instance->Media.BlockSize;\r
442\r
443 DEBUG ((DEBUG_BLKIO, "FvbRead: Check if (Offset=0x%x + NumBytes=0x%x) <= BlockSize=0x%x\n", Offset, *NumBytes, BlockSize ));\r
444\r
445 // The read must not span block boundaries.\r
446 // We need to check each variable individually because adding two large values together overflows.\r
447 if ((Offset >= BlockSize) ||\r
448 (*NumBytes > BlockSize) ||\r
449 ((Offset + *NumBytes) > BlockSize)) {\r
450 DEBUG ((EFI_D_ERROR, "FvbRead: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize ));\r
451 return EFI_BAD_BUFFER_SIZE;\r
452 }\r
453\r
454 // We must have some bytes to read\r
455 if (*NumBytes == 0) {\r
456 return EFI_BAD_BUFFER_SIZE;\r
457 }\r
458\r
518c243d
HL
459 // Decide if we are doing full block reads or not.\r
460 if (*NumBytes % BlockSize != 0) {\r
461 TempStatus = NorFlashRead (Instance, Instance->StartLba + Lba, Offset, *NumBytes, Buffer);\r
462 if (EFI_ERROR (TempStatus)) {\r
463 return EFI_DEVICE_ERROR;\r
464 }\r
465 } else {\r
466 // Read NOR Flash data into shadow buffer\r
467 TempStatus = NorFlashReadBlocks (Instance, Instance->StartLba + Lba, BlockSize, Buffer);\r
468 if (EFI_ERROR (TempStatus)) {\r
469 // Return one of the pre-approved error statuses\r
470 return EFI_DEVICE_ERROR;\r
471 }\r
1e57a462 472 }\r
518c243d 473 return EFI_SUCCESS;\r
1e57a462 474}\r
475\r
476/**\r
477 Writes the specified number of bytes from the input buffer to the block.\r
478\r
479 The Write() function writes the specified number of bytes from\r
480 the provided buffer to the specified block and offset. If the\r
481 firmware volume is sticky write, the caller must ensure that\r
482 all the bits of the specified range to write are in the\r
483 EFI_FVB_ERASE_POLARITY state before calling the Write()\r
484 function, or else the result will be unpredictable. This\r
485 unpredictability arises because, for a sticky-write firmware\r
486 volume, a write may negate a bit in the EFI_FVB_ERASE_POLARITY\r
487 state but cannot flip it back again. Before calling the\r
488 Write() function, it is recommended for the caller to first call\r
489 the EraseBlocks() function to erase the specified block to\r
490 write. A block erase cycle will transition bits from the\r
491 (NOT)EFI_FVB_ERASE_POLARITY state back to the\r
492 EFI_FVB_ERASE_POLARITY state. Implementations should be\r
493 mindful that the firmware volume might be in the WriteDisabled\r
494 state. If it is in this state, the Write() function must\r
495 return the status code EFI_ACCESS_DENIED without modifying the\r
496 contents of the firmware volume. The Write() function must\r
497 also prevent spanning block boundaries. If a write is\r
498 requested that spans a block boundary, the write must store up\r
499 to the boundary but not beyond. The output parameter NumBytes\r
500 must be set to correctly indicate the number of bytes actually\r
501 written. The caller must be aware that a write may be\r
502 partially completed. All writes, partial or otherwise, must be\r
503 fully flushed to the hardware before the Write() service\r
504 returns.\r
505\r
506 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.\r
507\r
508 @param Lba The starting logical block index to write to.\r
509\r
510 @param Offset Offset into the block at which to begin writing.\r
511\r
512 @param NumBytes The pointer to a UINTN.\r
513 At entry, *NumBytes contains the total size of the buffer.\r
514 At exit, *NumBytes contains the total number of bytes actually written.\r
515\r
516 @param Buffer The pointer to a caller-allocated buffer that contains the source for the write.\r
517\r
518 @retval EFI_SUCCESS The firmware volume was written successfully.\r
519\r
520 @retval EFI_BAD_BUFFER_SIZE The write was attempted across an LBA boundary.\r
521 On output, NumBytes contains the total number of bytes\r
522 actually written.\r
523\r
524 @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state.\r
525\r
526 @retval EFI_DEVICE_ERROR The block device is malfunctioning and could not be written.\r
527\r
528\r
529 **/\r
530EFI_STATUS\r
531EFIAPI\r
532FvbWrite (\r
533 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,\r
534 IN EFI_LBA Lba,\r
535 IN UINTN Offset,\r
536 IN OUT UINTN *NumBytes,\r
537 IN UINT8 *Buffer\r
538 )\r
539{\r
1e57a462 540 NOR_FLASH_INSTANCE *Instance;\r
518c243d 541\r
452a9ee1 542 Instance = INSTANCE_FROM_FVB_THIS (This);\r
1e57a462 543\r
36d66acf 544 return NorFlashWriteSingleBlock (Instance, Instance->StartLba + Lba, Offset, NumBytes, Buffer);\r
1e57a462 545}\r
546\r
547/**\r
548 Erases and initialises a firmware volume block.\r
549\r
550 The EraseBlocks() function erases one or more blocks as denoted\r
551 by the variable argument list. The entire parameter list of\r
552 blocks must be verified before erasing any blocks. If a block is\r
553 requested that does not exist within the associated firmware\r
554 volume (it has a larger index than the last block of the\r
555 firmware volume), the EraseBlocks() function must return the\r
556 status code EFI_INVALID_PARAMETER without modifying the contents\r
557 of the firmware volume. Implementations should be mindful that\r
558 the firmware volume might be in the WriteDisabled state. If it\r
559 is in this state, the EraseBlocks() function must return the\r
560 status code EFI_ACCESS_DENIED without modifying the contents of\r
561 the firmware volume. All calls to EraseBlocks() must be fully\r
562 flushed to the hardware before the EraseBlocks() service\r
563 returns.\r
564\r
565 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL\r
566 instance.\r
567\r
568 @param ... The variable argument list is a list of tuples.\r
569 Each tuple describes a range of LBAs to erase\r
570 and consists of the following:\r
571 - An EFI_LBA that indicates the starting LBA\r
572 - A UINTN that indicates the number of blocks to erase.\r
573\r
574 The list is terminated with an EFI_LBA_LIST_TERMINATOR.\r
575 For example, the following indicates that two ranges of blocks\r
576 (5-7 and 10-11) are to be erased:\r
577 EraseBlocks (This, 5, 3, 10, 2, EFI_LBA_LIST_TERMINATOR);\r
578\r
579 @retval EFI_SUCCESS The erase request successfully completed.\r
580\r
581 @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state.\r
582\r
583 @retval EFI_DEVICE_ERROR The block device is not functioning correctly and could not be written.\r
584 The firmware device may have been partially erased.\r
585\r
586 @retval EFI_INVALID_PARAMETER One or more of the LBAs listed in the variable argument list do\r
587 not exist in the firmware volume.\r
588\r
589 **/\r
590EFI_STATUS\r
591EFIAPI\r
592FvbEraseBlocks (\r
593 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,\r
594 ...\r
595 )\r
596{\r
597 EFI_STATUS Status;\r
598 VA_LIST Args;\r
599 UINTN BlockAddress; // Physical address of Lba to erase\r
600 EFI_LBA StartingLba; // Lba from which we start erasing\r
601 UINTN NumOfLba; // Number of Lba blocks to erase\r
602 NOR_FLASH_INSTANCE *Instance;\r
603\r
604 Instance = INSTANCE_FROM_FVB_THIS(This);\r
605\r
606 DEBUG ((DEBUG_BLKIO, "FvbEraseBlocks()\n"));\r
607\r
608 Status = EFI_SUCCESS;\r
609\r
610 // Detect WriteDisabled state\r
611 if (Instance->Media.ReadOnly == TRUE) {\r
612 // Firmware volume is in WriteDisabled state\r
613 DEBUG ((EFI_D_ERROR, "FvbEraseBlocks: ERROR - Device is in WriteDisabled state.\n"));\r
614 return EFI_ACCESS_DENIED;\r
615 }\r
616\r
617 // Before erasing, check the entire list of parameters to ensure all specified blocks are valid\r
618\r
619 VA_START (Args, This);\r
620 do {\r
621 // Get the Lba from which we start erasing\r
622 StartingLba = VA_ARG (Args, EFI_LBA);\r
623\r
624 // Have we reached the end of the list?\r
625 if (StartingLba == EFI_LBA_LIST_TERMINATOR) {\r
626 //Exit the while loop\r
627 break;\r
628 }\r
629\r
630 // How many Lba blocks are we requested to erase?\r
ce69cc77 631 NumOfLba = VA_ARG (Args, UINTN);\r
1e57a462 632\r
633 // All blocks must be within range\r
ce69cc77
LE
634 DEBUG ((\r
635 DEBUG_BLKIO,\r
636 "FvbEraseBlocks: Check if: ( StartingLba=%ld + NumOfLba=%Lu - 1 ) > LastBlock=%ld.\n",\r
637 Instance->StartLba + StartingLba,\r
638 (UINT64)NumOfLba,\r
639 Instance->Media.LastBlock\r
640 ));\r
1e57a462 641 if ((NumOfLba == 0) || ((Instance->StartLba + StartingLba + NumOfLba - 1) > Instance->Media.LastBlock)) {\r
642 VA_END (Args);\r
643 DEBUG ((EFI_D_ERROR, "FvbEraseBlocks: ERROR - Lba range goes past the last Lba.\n"));\r
644 Status = EFI_INVALID_PARAMETER;\r
645 goto EXIT;\r
646 }\r
647 } while (TRUE);\r
648 VA_END (Args);\r
649\r
650 //\r
651 // To get here, all must be ok, so start erasing\r
652 //\r
653 VA_START (Args, This);\r
654 do {\r
655 // Get the Lba from which we start erasing\r
656 StartingLba = VA_ARG (Args, EFI_LBA);\r
657\r
658 // Have we reached the end of the list?\r
659 if (StartingLba == EFI_LBA_LIST_TERMINATOR) {\r
660 // Exit the while loop\r
661 break;\r
662 }\r
663\r
664 // How many Lba blocks are we requested to erase?\r
ce69cc77 665 NumOfLba = VA_ARG (Args, UINTN);\r
1e57a462 666\r
667 // Go through each one and erase it\r
668 while (NumOfLba > 0) {\r
669\r
670 // Get the physical address of Lba to erase\r
671 BlockAddress = GET_NOR_BLOCK_ADDRESS (\r
672 Instance->RegionBaseAddress,\r
673 Instance->StartLba + StartingLba,\r
674 Instance->Media.BlockSize\r
675 );\r
676\r
677 // Erase it\r
678 DEBUG ((DEBUG_BLKIO, "FvbEraseBlocks: Erasing Lba=%ld @ 0x%08x.\n", Instance->StartLba + StartingLba, BlockAddress));\r
679 Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress);\r
680 if (EFI_ERROR(Status)) {\r
681 VA_END (Args);\r
682 Status = EFI_DEVICE_ERROR;\r
683 goto EXIT;\r
684 }\r
685\r
686 // Move to the next Lba\r
687 StartingLba++;\r
688 NumOfLba--;\r
689 }\r
690 } while (TRUE);\r
691 VA_END (Args);\r
692\r
693EXIT:\r
694 return Status;\r
695}\r
696\r
1dbbfc17
OM
697/**\r
698 Fixup internal data so that EFI can be call in virtual mode.\r
699 Call the passed in Child Notify event and convert any pointers in\r
700 lib to virtual mode.\r
701\r
702 @param[in] Event The Event that is being processed\r
703 @param[in] Context Event Context\r
704**/\r
705VOID\r
706EFIAPI\r
707FvbVirtualNotifyEvent (\r
708 IN EFI_EVENT Event,\r
709 IN VOID *Context\r
710 )\r
711{\r
712 EfiConvertPointer (0x0, (VOID**)&mFlashNvStorageVariableBase);\r
713 return;\r
714}\r
715\r
1e57a462 716EFI_STATUS\r
717EFIAPI\r
718NorFlashFvbInitialize (\r
719 IN NOR_FLASH_INSTANCE* Instance\r
720 )\r
721{\r
722 EFI_STATUS Status;\r
723 UINT32 FvbNumLba;\r
724 EFI_BOOT_MODE BootMode;\r
1dbbfc17 725 UINTN RuntimeMmioRegionSize;\r
1e57a462 726\r
727 DEBUG((DEBUG_BLKIO,"NorFlashFvbInitialize\n"));\r
90d1f671
AG
728 ASSERT((Instance != NULL));\r
729\r
730 //\r
731 // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME\r
732 //\r
733\r
734 // Note: all the NOR Flash region needs to be reserved into the UEFI Runtime memory;\r
735 // even if we only use the small block region at the top of the NOR Flash.\r
736 // The reason is when the NOR Flash memory is set into program mode, the command\r
737 // is written as the base of the flash region (ie: Instance->DeviceBaseAddress)\r
738 RuntimeMmioRegionSize = (Instance->RegionBaseAddress - Instance->DeviceBaseAddress) + Instance->Size;\r
739\r
740 Status = gDS->AddMemorySpace (\r
741 EfiGcdMemoryTypeMemoryMappedIo,\r
742 Instance->DeviceBaseAddress, RuntimeMmioRegionSize,\r
743 EFI_MEMORY_UC | EFI_MEMORY_RUNTIME\r
744 );\r
745 ASSERT_EFI_ERROR (Status);\r
746\r
747 Status = gDS->SetMemorySpaceAttributes (\r
748 Instance->DeviceBaseAddress, RuntimeMmioRegionSize,\r
749 EFI_MEMORY_UC | EFI_MEMORY_RUNTIME);\r
750 ASSERT_EFI_ERROR (Status);\r
1e57a462 751\r
752 Instance->Initialized = TRUE;\r
1dbbfc17 753 mFlashNvStorageVariableBase = FixedPcdGet32 (PcdFlashNvStorageVariableBase);\r
1e57a462 754\r
755 // Set the index of the first LBA for the FVB\r
756 Instance->StartLba = (PcdGet32 (PcdFlashNvStorageVariableBase) - Instance->RegionBaseAddress) / Instance->Media.BlockSize;\r
757\r
758 BootMode = GetBootModeHob ();\r
759 if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) {\r
760 Status = EFI_INVALID_PARAMETER;\r
761 } else {\r
762 // Determine if there is a valid header at the beginning of the NorFlash\r
763 Status = ValidateFvHeader (Instance);\r
764 }\r
765\r
3402aac7 766 // Install the Default FVB header if required\r
1e57a462 767 if (EFI_ERROR(Status)) {\r
768 // There is no valid header, so time to install one.\r
582085eb
LE
769 DEBUG ((EFI_D_INFO, "%a: The FVB Header is not valid.\n", __FUNCTION__));\r
770 DEBUG ((EFI_D_INFO, "%a: Installing a correct one for this volume.\n",\r
771 __FUNCTION__));\r
1e57a462 772\r
773 // Erase all the NorFlash that is reserved for variable storage\r
774 FvbNumLba = (PcdGet32(PcdFlashNvStorageVariableSize) + PcdGet32(PcdFlashNvStorageFtwWorkingSize) + PcdGet32(PcdFlashNvStorageFtwSpareSize)) / Instance->Media.BlockSize;\r
775\r
776 Status = FvbEraseBlocks (&Instance->FvbProtocol, (EFI_LBA)0, FvbNumLba, EFI_LBA_LIST_TERMINATOR);\r
777 if (EFI_ERROR(Status)) {\r
778 return Status;\r
779 }\r
780\r
781 // Install all appropriate headers\r
782 Status = InitializeFvAndVariableStoreHeaders (Instance);\r
783 if (EFI_ERROR(Status)) {\r
784 return Status;\r
785 }\r
786 }\r
1dbbfc17 787\r
1dbbfc17
OM
788 //\r
789 // Register for the virtual address change event\r
790 //\r
791 Status = gBS->CreateEventEx (\r
792 EVT_NOTIFY_SIGNAL,\r
793 TPL_NOTIFY,\r
794 FvbVirtualNotifyEvent,\r
795 NULL,\r
796 &gEfiEventVirtualAddressChangeGuid,\r
797 &mFvbVirtualAddrChangeEvent\r
798 );\r
799 ASSERT_EFI_ERROR (Status);\r
800\r
1e57a462 801 return Status;\r
802}\r