]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashFvbDxe.c
ArmPlatformPkg/NorFlashDxe: Fixed driver to support UEFI Runtime mode
[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
1e57a462 114 CopyGuid (&VariableStoreHeader->Signature, &gEfiVariableGuid);\r
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
144 UINTN FvLength;\r
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
161 DEBUG ((EFI_D_ERROR, "ValidateFvHeader: No Firmware Volume header present\n"));\r
162 return EFI_NOT_FOUND;\r
163 }\r
164\r
165 // Check the Firmware Volume Guid\r
166 if( CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid) == FALSE ) {\r
167 DEBUG ((EFI_D_ERROR, "ValidateFvHeader: Firmware Volume Guid non-compatible\n"));\r
168 return EFI_NOT_FOUND;\r
169 }\r
170\r
171 // Verify the header checksum\r
172 Checksum = CalculateSum16((UINT16*)FwVolHeader, FwVolHeader->HeaderLength);\r
173 if (Checksum != 0) {\r
174 DEBUG ((EFI_D_ERROR, "ValidateFvHeader: FV checksum is invalid (Checksum:0x%X)\n",Checksum));\r
175 return EFI_NOT_FOUND;\r
176 }\r
177\r
d5cd447b 178 VariableStoreHeader = (VARIABLE_STORE_HEADER*)((UINTN)FwVolHeader + FwVolHeader->HeaderLength);\r
1e57a462 179\r
180 // Check the Variable Store Guid\r
181 if( CompareGuid (&VariableStoreHeader->Signature, &gEfiVariableGuid) == FALSE ) {\r
182 DEBUG ((EFI_D_ERROR, "ValidateFvHeader: Variable Store Guid non-compatible\n"));\r
183 return EFI_NOT_FOUND;\r
184 }\r
185\r
186 VariableStoreLength = PcdGet32 (PcdFlashNvStorageVariableSize) - FwVolHeader->HeaderLength;\r
187 if (VariableStoreHeader->Size != VariableStoreLength) {\r
188 DEBUG ((EFI_D_ERROR, "ValidateFvHeader: Variable Store Length does not match\n"));\r
189 return EFI_NOT_FOUND;\r
190 }\r
191\r
192 return EFI_SUCCESS;\r
193}\r
194\r
195/**\r
196 The GetAttributes() function retrieves the attributes and\r
197 current settings of the block.\r
198\r
199 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.\r
200\r
201 @param Attributes Pointer to EFI_FVB_ATTRIBUTES_2 in which the attributes and\r
202 current settings are returned.\r
203 Type EFI_FVB_ATTRIBUTES_2 is defined in EFI_FIRMWARE_VOLUME_HEADER.\r
204\r
205 @retval EFI_SUCCESS The firmware volume attributes were returned.\r
206\r
207 **/\r
208EFI_STATUS\r
209EFIAPI\r
210FvbGetAttributes(\r
211 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,\r
212 OUT EFI_FVB_ATTRIBUTES_2 *Attributes\r
213 )\r
214{\r
215 EFI_FVB_ATTRIBUTES_2 FlashFvbAttributes;\r
216 NOR_FLASH_INSTANCE *Instance;\r
217\r
218 Instance = INSTANCE_FROM_FVB_THIS(This);\r
219\r
220 FlashFvbAttributes = (EFI_FVB_ATTRIBUTES_2) (\r
221\r
222 EFI_FVB2_READ_ENABLED_CAP | // Reads may be enabled\r
223 EFI_FVB2_READ_STATUS | // Reads are currently enabled\r
224 EFI_FVB2_STICKY_WRITE | // A block erase is required to flip bits into EFI_FVB2_ERASE_POLARITY\r
225 EFI_FVB2_MEMORY_MAPPED | // It is memory mapped\r
226 EFI_FVB2_ERASE_POLARITY // After erasure all bits take this value (i.e. '1')\r
227\r
228 );\r
229\r
230 // Check if it is write protected\r
231 if (Instance->Media.ReadOnly != TRUE) {\r
232\r
233 FlashFvbAttributes = FlashFvbAttributes |\r
234 EFI_FVB2_WRITE_STATUS | // Writes are currently enabled\r
235 EFI_FVB2_WRITE_ENABLED_CAP; // Writes may be enabled\r
236 }\r
237\r
238 *Attributes = FlashFvbAttributes;\r
239\r
240 DEBUG ((DEBUG_BLKIO, "FvbGetAttributes(0x%X)\n", *Attributes));\r
241\r
242 return EFI_SUCCESS;\r
243}\r
244\r
245/**\r
246 The SetAttributes() function sets configurable firmware volume attributes\r
247 and returns the new settings of the firmware volume.\r
248\r
249\r
250 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.\r
251\r
252 @param Attributes On input, Attributes is a pointer to EFI_FVB_ATTRIBUTES_2\r
253 that contains the desired firmware volume settings.\r
254 On successful return, it contains the new settings of\r
255 the firmware volume.\r
256 Type EFI_FVB_ATTRIBUTES_2 is defined in EFI_FIRMWARE_VOLUME_HEADER.\r
257\r
258 @retval EFI_SUCCESS The firmware volume attributes were returned.\r
259\r
260 @retval EFI_INVALID_PARAMETER The attributes requested are in conflict with the capabilities\r
261 as declared in the firmware volume header.\r
262\r
263 **/\r
264EFI_STATUS\r
265EFIAPI\r
266FvbSetAttributes(\r
267 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,\r
268 IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes\r
269 )\r
270{\r
271 DEBUG ((DEBUG_BLKIO, "FvbSetAttributes(0x%X) is not supported\n",*Attributes));\r
272 return EFI_UNSUPPORTED;\r
273}\r
274\r
275/**\r
276 The GetPhysicalAddress() function retrieves the base address of\r
277 a memory-mapped firmware volume. This function should be called\r
278 only for memory-mapped firmware volumes.\r
279\r
280 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.\r
281\r
282 @param Address Pointer to a caller-allocated\r
283 EFI_PHYSICAL_ADDRESS that, on successful\r
284 return from GetPhysicalAddress(), contains the\r
285 base address of the firmware volume.\r
286\r
287 @retval EFI_SUCCESS The firmware volume base address was returned.\r
288\r
289 @retval EFI_NOT_SUPPORTED The firmware volume is not memory mapped.\r
290\r
291 **/\r
292EFI_STATUS\r
293EFIAPI\r
294FvbGetPhysicalAddress (\r
295 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,\r
296 OUT EFI_PHYSICAL_ADDRESS *Address\r
297 )\r
298{\r
299 NOR_FLASH_INSTANCE *Instance;\r
300\r
301 Instance = INSTANCE_FROM_FVB_THIS(This);\r
302\r
303 DEBUG ((DEBUG_BLKIO, "FvbGetPhysicalAddress(BaseAddress=0x%08x)\n", Instance->RegionBaseAddress));\r
304\r
305 ASSERT(Address != NULL);\r
306\r
1dbbfc17 307 *Address = mFlashNvStorageVariableBase;\r
1e57a462 308 return EFI_SUCCESS;\r
309}\r
310\r
311/**\r
312 The GetBlockSize() function retrieves the size of the requested\r
313 block. It also returns the number of additional blocks with\r
314 the identical size. The GetBlockSize() function is used to\r
315 retrieve the block map (see EFI_FIRMWARE_VOLUME_HEADER).\r
316\r
317\r
318 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.\r
319\r
320 @param Lba Indicates the block for which to return the size.\r
321\r
322 @param BlockSize Pointer to a caller-allocated UINTN in which\r
323 the size of the block is returned.\r
324\r
325 @param NumberOfBlocks Pointer to a caller-allocated UINTN in\r
326 which the number of consecutive blocks,\r
327 starting with Lba, is returned. All\r
328 blocks in this range have a size of\r
329 BlockSize.\r
330\r
331\r
332 @retval EFI_SUCCESS The firmware volume base address was returned.\r
333\r
334 @retval EFI_INVALID_PARAMETER The requested LBA is out of range.\r
335\r
336 **/\r
337EFI_STATUS\r
338EFIAPI\r
339FvbGetBlockSize (\r
340 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,\r
341 IN EFI_LBA Lba,\r
342 OUT UINTN *BlockSize,\r
343 OUT UINTN *NumberOfBlocks\r
344 )\r
345{\r
346 EFI_STATUS Status;\r
347 NOR_FLASH_INSTANCE *Instance;\r
348\r
349 Instance = INSTANCE_FROM_FVB_THIS(This);\r
350\r
351 DEBUG ((DEBUG_BLKIO, "FvbGetBlockSize(Lba=%ld, BlockSize=0x%x, LastBlock=%ld)\n", Lba, Instance->Media.BlockSize, Instance->Media.LastBlock));\r
352\r
353 if (Lba > Instance->Media.LastBlock) {\r
354 DEBUG ((EFI_D_ERROR, "FvbGetBlockSize: ERROR - Parameter LBA %ld is beyond the last Lba (%ld).\n", Lba, Instance->Media.LastBlock));\r
355 Status = EFI_INVALID_PARAMETER;\r
356 } else {\r
357 // This is easy because in this platform each NorFlash device has equal sized blocks.\r
358 *BlockSize = (UINTN) Instance->Media.BlockSize;\r
359 *NumberOfBlocks = (UINTN) (Instance->Media.LastBlock - Lba + 1);\r
360\r
361 DEBUG ((DEBUG_BLKIO, "FvbGetBlockSize: *BlockSize=0x%x, *NumberOfBlocks=0x%x.\n", *BlockSize, *NumberOfBlocks));\r
362\r
363 Status = EFI_SUCCESS;\r
364 }\r
365\r
366 return Status;\r
367}\r
368\r
369/**\r
370 Reads the specified number of bytes into a buffer from the specified block.\r
371\r
372 The Read() function reads the requested number of bytes from the\r
373 requested block and stores them in the provided buffer.\r
374 Implementations should be mindful that the firmware volume\r
375 might be in the ReadDisabled state. If it is in this state,\r
376 the Read() function must return the status code\r
377 EFI_ACCESS_DENIED without modifying the contents of the\r
378 buffer. The Read() function must also prevent spanning block\r
379 boundaries. If a read is requested that would span a block\r
380 boundary, the read must read up to the boundary but not\r
381 beyond. The output parameter NumBytes must be set to correctly\r
382 indicate the number of bytes actually read. The caller must be\r
383 aware that a read may be partially completed.\r
384\r
385 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.\r
386\r
387 @param Lba The starting logical block index from which to read.\r
388\r
389 @param Offset Offset into the block at which to begin reading.\r
390\r
391 @param NumBytes Pointer to a UINTN.\r
392 At entry, *NumBytes contains the total size of the buffer.\r
393 At exit, *NumBytes contains the total number of bytes read.\r
394\r
395 @param Buffer Pointer to a caller-allocated buffer that will be used\r
396 to hold the data that is read.\r
397\r
398 @retval EFI_SUCCESS The firmware volume was read successfully, and contents are\r
399 in Buffer.\r
400\r
401 @retval EFI_BAD_BUFFER_SIZE Read attempted across an LBA boundary.\r
402 On output, NumBytes contains the total number of bytes\r
403 returned in Buffer.\r
404\r
405 @retval EFI_ACCESS_DENIED The firmware volume is in the ReadDisabled state.\r
406\r
407 @retval EFI_DEVICE_ERROR The block device is not functioning correctly and could not be read.\r
408\r
409 **/\r
410EFI_STATUS\r
411EFIAPI\r
412FvbRead (\r
413 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,\r
414 IN EFI_LBA Lba,\r
415 IN UINTN Offset,\r
416 IN OUT UINTN *NumBytes,\r
417 IN OUT UINT8 *Buffer\r
418 )\r
419{\r
420 EFI_STATUS Status;\r
421 EFI_STATUS TempStatus;\r
422 UINTN BlockSize;\r
1e57a462 423 NOR_FLASH_INSTANCE *Instance;\r
424\r
425 Instance = INSTANCE_FROM_FVB_THIS(This);\r
426\r
427 DEBUG ((DEBUG_BLKIO, "FvbRead(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Instance->StartLba + Lba, Offset, *NumBytes, Buffer));\r
428\r
429 if (!Instance->Initialized && Instance->Initialize) {\r
430 Instance->Initialize(Instance);\r
431 }\r
432\r
433 Status = EFI_SUCCESS;\r
434 TempStatus = Status;\r
435\r
436 // Cache the block size to avoid de-referencing pointers all the time\r
437 BlockSize = Instance->Media.BlockSize;\r
438\r
439 DEBUG ((DEBUG_BLKIO, "FvbRead: Check if (Offset=0x%x + NumBytes=0x%x) <= BlockSize=0x%x\n", Offset, *NumBytes, BlockSize ));\r
440\r
441 // The read must not span block boundaries.\r
442 // We need to check each variable individually because adding two large values together overflows.\r
443 if ((Offset >= BlockSize) ||\r
444 (*NumBytes > BlockSize) ||\r
445 ((Offset + *NumBytes) > BlockSize)) {\r
446 DEBUG ((EFI_D_ERROR, "FvbRead: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize ));\r
447 return EFI_BAD_BUFFER_SIZE;\r
448 }\r
449\r
450 // We must have some bytes to read\r
451 if (*NumBytes == 0) {\r
452 return EFI_BAD_BUFFER_SIZE;\r
453 }\r
454\r
2dff0c1a
OM
455 // Check we did get some memory\r
456 if (Instance->FvbBuffer == NULL) {\r
457 DEBUG ((EFI_D_ERROR, "FvbRead: ERROR - Buffer not ready\n"));\r
1e57a462 458 return EFI_DEVICE_ERROR;\r
459 }\r
460\r
461 // Read NOR Flash data into shadow buffer\r
2dff0c1a 462 TempStatus = NorFlashReadBlocks (Instance, Instance->StartLba + Lba, BlockSize, Instance->FvbBuffer);\r
1e57a462 463 if (EFI_ERROR (TempStatus)) {\r
464 // Return one of the pre-approved error statuses\r
2dff0c1a 465 return EFI_DEVICE_ERROR;\r
1e57a462 466 }\r
467\r
468 // Put the data at the appropriate location inside the buffer area\r
2dff0c1a 469 DEBUG ((DEBUG_BLKIO, "FvbRead: CopyMem( Dst=0x%08x, Src=0x%08x, Size=0x%x ).\n", Buffer, (UINTN)Instance->FvbBuffer + Offset, *NumBytes));\r
1e57a462 470\r
2dff0c1a 471 CopyMem (Buffer, (VOID*)((UINTN)Instance->FvbBuffer + Offset), *NumBytes);\r
1e57a462 472\r
1e57a462 473 return Status;\r
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
540 EFI_STATUS Status;\r
541 EFI_STATUS TempStatus;\r
542 UINTN BlockSize;\r
1e57a462 543 NOR_FLASH_INSTANCE *Instance;\r
544\r
545 Instance = INSTANCE_FROM_FVB_THIS(This);\r
546\r
547 if (!Instance->Initialized && Instance->Initialize) {\r
548 Instance->Initialize(Instance);\r
549 }\r
550\r
551 DEBUG ((DEBUG_BLKIO, "FvbWrite(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Instance->StartLba + Lba, Offset, *NumBytes, Buffer));\r
552\r
553 Status = EFI_SUCCESS;\r
554 TempStatus = Status;\r
555\r
556 // Detect WriteDisabled state\r
557 if (Instance->Media.ReadOnly == TRUE) {\r
558 DEBUG ((EFI_D_ERROR, "FvbWrite: ERROR - Can not write: Device is in WriteDisabled state.\n"));\r
559 // It is in WriteDisabled state, return an error right away\r
560 return EFI_ACCESS_DENIED;\r
561 }\r
562\r
563 // Cache the block size to avoid de-referencing pointers all the time\r
564 BlockSize = Instance->Media.BlockSize;\r
565\r
566 // The write must not span block boundaries.\r
567 // We need to check each variable individually because adding two large values together overflows.\r
568 if ( ( Offset >= BlockSize ) ||\r
569 ( *NumBytes > BlockSize ) ||\r
570 ( (Offset + *NumBytes) > BlockSize ) ) {\r
571 DEBUG ((EFI_D_ERROR, "FvbWrite: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize ));\r
572 return EFI_BAD_BUFFER_SIZE;\r
573 }\r
574\r
575 // We must have some bytes to write\r
576 if (*NumBytes == 0) {\r
577 DEBUG ((EFI_D_ERROR, "FvbWrite: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize ));\r
578 return EFI_BAD_BUFFER_SIZE;\r
579 }\r
580\r
1e57a462 581 // Check we did get some memory\r
2dff0c1a
OM
582 if (Instance->FvbBuffer == NULL) {\r
583 DEBUG ((EFI_D_ERROR, "FvbWrite: ERROR - Buffer not ready\n"));\r
1e57a462 584 return EFI_DEVICE_ERROR;\r
585 }\r
586\r
587 // Read NOR Flash data into shadow buffer\r
2dff0c1a 588 TempStatus = NorFlashReadBlocks (Instance, Instance->StartLba + Lba, BlockSize, Instance->FvbBuffer);\r
1e57a462 589 if (EFI_ERROR (TempStatus)) {\r
590 // Return one of the pre-approved error statuses\r
2dff0c1a 591 return EFI_DEVICE_ERROR;\r
1e57a462 592 }\r
593\r
594 // Put the data at the appropriate location inside the buffer area\r
2dff0c1a 595 CopyMem ((VOID*)((UINTN)Instance->FvbBuffer + Offset), Buffer, *NumBytes);\r
1e57a462 596\r
597 // Write the modified buffer back to the NorFlash\r
2dff0c1a 598 TempStatus = NorFlashWriteBlocks (Instance, Instance->StartLba + Lba, BlockSize, Instance->FvbBuffer);\r
1e57a462 599 if (EFI_ERROR (TempStatus)) {\r
600 // Return one of the pre-approved error statuses\r
2dff0c1a 601 return EFI_DEVICE_ERROR;\r
1e57a462 602 }\r
603\r
1e57a462 604 return Status;\r
605}\r
606\r
607/**\r
608 Erases and initialises a firmware volume block.\r
609\r
610 The EraseBlocks() function erases one or more blocks as denoted\r
611 by the variable argument list. The entire parameter list of\r
612 blocks must be verified before erasing any blocks. If a block is\r
613 requested that does not exist within the associated firmware\r
614 volume (it has a larger index than the last block of the\r
615 firmware volume), the EraseBlocks() function must return the\r
616 status code EFI_INVALID_PARAMETER without modifying the contents\r
617 of the firmware volume. Implementations should be mindful that\r
618 the firmware volume might be in the WriteDisabled state. If it\r
619 is in this state, the EraseBlocks() function must return the\r
620 status code EFI_ACCESS_DENIED without modifying the contents of\r
621 the firmware volume. All calls to EraseBlocks() must be fully\r
622 flushed to the hardware before the EraseBlocks() service\r
623 returns.\r
624\r
625 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL\r
626 instance.\r
627\r
628 @param ... The variable argument list is a list of tuples.\r
629 Each tuple describes a range of LBAs to erase\r
630 and consists of the following:\r
631 - An EFI_LBA that indicates the starting LBA\r
632 - A UINTN that indicates the number of blocks to erase.\r
633\r
634 The list is terminated with an EFI_LBA_LIST_TERMINATOR.\r
635 For example, the following indicates that two ranges of blocks\r
636 (5-7 and 10-11) are to be erased:\r
637 EraseBlocks (This, 5, 3, 10, 2, EFI_LBA_LIST_TERMINATOR);\r
638\r
639 @retval EFI_SUCCESS The erase request successfully completed.\r
640\r
641 @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state.\r
642\r
643 @retval EFI_DEVICE_ERROR The block device is not functioning correctly and could not be written.\r
644 The firmware device may have been partially erased.\r
645\r
646 @retval EFI_INVALID_PARAMETER One or more of the LBAs listed in the variable argument list do\r
647 not exist in the firmware volume.\r
648\r
649 **/\r
650EFI_STATUS\r
651EFIAPI\r
652FvbEraseBlocks (\r
653 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,\r
654 ...\r
655 )\r
656{\r
657 EFI_STATUS Status;\r
658 VA_LIST Args;\r
659 UINTN BlockAddress; // Physical address of Lba to erase\r
660 EFI_LBA StartingLba; // Lba from which we start erasing\r
661 UINTN NumOfLba; // Number of Lba blocks to erase\r
662 NOR_FLASH_INSTANCE *Instance;\r
663\r
664 Instance = INSTANCE_FROM_FVB_THIS(This);\r
665\r
666 DEBUG ((DEBUG_BLKIO, "FvbEraseBlocks()\n"));\r
667\r
668 Status = EFI_SUCCESS;\r
669\r
670 // Detect WriteDisabled state\r
671 if (Instance->Media.ReadOnly == TRUE) {\r
672 // Firmware volume is in WriteDisabled state\r
673 DEBUG ((EFI_D_ERROR, "FvbEraseBlocks: ERROR - Device is in WriteDisabled state.\n"));\r
674 return EFI_ACCESS_DENIED;\r
675 }\r
676\r
677 // Before erasing, check the entire list of parameters to ensure all specified blocks are valid\r
678\r
679 VA_START (Args, This);\r
680 do {\r
681 // Get the Lba from which we start erasing\r
682 StartingLba = VA_ARG (Args, EFI_LBA);\r
683\r
684 // Have we reached the end of the list?\r
685 if (StartingLba == EFI_LBA_LIST_TERMINATOR) {\r
686 //Exit the while loop\r
687 break;\r
688 }\r
689\r
690 // How many Lba blocks are we requested to erase?\r
691 NumOfLba = VA_ARG (Args, UINT32);\r
692\r
693 // All blocks must be within range\r
694 DEBUG ((DEBUG_BLKIO, "FvbEraseBlocks: Check if: ( StartingLba=%ld + NumOfLba=%d - 1 ) > LastBlock=%ld.\n", Instance->StartLba + StartingLba, NumOfLba, Instance->Media.LastBlock));\r
695 if ((NumOfLba == 0) || ((Instance->StartLba + StartingLba + NumOfLba - 1) > Instance->Media.LastBlock)) {\r
696 VA_END (Args);\r
697 DEBUG ((EFI_D_ERROR, "FvbEraseBlocks: ERROR - Lba range goes past the last Lba.\n"));\r
698 Status = EFI_INVALID_PARAMETER;\r
699 goto EXIT;\r
700 }\r
701 } while (TRUE);\r
702 VA_END (Args);\r
703\r
704 //\r
705 // To get here, all must be ok, so start erasing\r
706 //\r
707 VA_START (Args, This);\r
708 do {\r
709 // Get the Lba from which we start erasing\r
710 StartingLba = VA_ARG (Args, EFI_LBA);\r
711\r
712 // Have we reached the end of the list?\r
713 if (StartingLba == EFI_LBA_LIST_TERMINATOR) {\r
714 // Exit the while loop\r
715 break;\r
716 }\r
717\r
718 // How many Lba blocks are we requested to erase?\r
719 NumOfLba = VA_ARG (Args, UINT32);\r
720\r
721 // Go through each one and erase it\r
722 while (NumOfLba > 0) {\r
723\r
724 // Get the physical address of Lba to erase\r
725 BlockAddress = GET_NOR_BLOCK_ADDRESS (\r
726 Instance->RegionBaseAddress,\r
727 Instance->StartLba + StartingLba,\r
728 Instance->Media.BlockSize\r
729 );\r
730\r
731 // Erase it\r
732 DEBUG ((DEBUG_BLKIO, "FvbEraseBlocks: Erasing Lba=%ld @ 0x%08x.\n", Instance->StartLba + StartingLba, BlockAddress));\r
733 Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress);\r
734 if (EFI_ERROR(Status)) {\r
735 VA_END (Args);\r
736 Status = EFI_DEVICE_ERROR;\r
737 goto EXIT;\r
738 }\r
739\r
740 // Move to the next Lba\r
741 StartingLba++;\r
742 NumOfLba--;\r
743 }\r
744 } while (TRUE);\r
745 VA_END (Args);\r
746\r
747EXIT:\r
748 return Status;\r
749}\r
750\r
1dbbfc17
OM
751/**\r
752 Fixup internal data so that EFI can be call in virtual mode.\r
753 Call the passed in Child Notify event and convert any pointers in\r
754 lib to virtual mode.\r
755\r
756 @param[in] Event The Event that is being processed\r
757 @param[in] Context Event Context\r
758**/\r
759VOID\r
760EFIAPI\r
761FvbVirtualNotifyEvent (\r
762 IN EFI_EVENT Event,\r
763 IN VOID *Context\r
764 )\r
765{\r
766 EfiConvertPointer (0x0, (VOID**)&mFlashNvStorageVariableBase);\r
767 return;\r
768}\r
769\r
1e57a462 770EFI_STATUS\r
771EFIAPI\r
772NorFlashFvbInitialize (\r
773 IN NOR_FLASH_INSTANCE* Instance\r
774 )\r
775{\r
776 EFI_STATUS Status;\r
777 UINT32 FvbNumLba;\r
778 EFI_BOOT_MODE BootMode;\r
1dbbfc17 779 UINTN RuntimeMmioRegionSize;\r
1e57a462 780\r
781 DEBUG((DEBUG_BLKIO,"NorFlashFvbInitialize\n"));\r
782\r
783 Instance->Initialized = TRUE;\r
1dbbfc17 784 mFlashNvStorageVariableBase = FixedPcdGet32 (PcdFlashNvStorageVariableBase);\r
1e57a462 785\r
786 // Set the index of the first LBA for the FVB\r
787 Instance->StartLba = (PcdGet32 (PcdFlashNvStorageVariableBase) - Instance->RegionBaseAddress) / Instance->Media.BlockSize;\r
788\r
789 BootMode = GetBootModeHob ();\r
790 if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) {\r
791 Status = EFI_INVALID_PARAMETER;\r
792 } else {\r
793 // Determine if there is a valid header at the beginning of the NorFlash\r
794 Status = ValidateFvHeader (Instance);\r
795 }\r
796\r
797 // Install the Default FVB header if required \r
798 if (EFI_ERROR(Status)) {\r
799 // There is no valid header, so time to install one.\r
800 DEBUG((EFI_D_ERROR,"NorFlashFvbInitialize: ERROR - The FVB Header is not valid. Installing a correct one for this volume.\n"));\r
801\r
802 // Erase all the NorFlash that is reserved for variable storage\r
803 FvbNumLba = (PcdGet32(PcdFlashNvStorageVariableSize) + PcdGet32(PcdFlashNvStorageFtwWorkingSize) + PcdGet32(PcdFlashNvStorageFtwSpareSize)) / Instance->Media.BlockSize;\r
804\r
805 Status = FvbEraseBlocks (&Instance->FvbProtocol, (EFI_LBA)0, FvbNumLba, EFI_LBA_LIST_TERMINATOR);\r
806 if (EFI_ERROR(Status)) {\r
807 return Status;\r
808 }\r
809\r
810 // Install all appropriate headers\r
811 Status = InitializeFvAndVariableStoreHeaders (Instance);\r
812 if (EFI_ERROR(Status)) {\r
813 return Status;\r
814 }\r
815 }\r
1dbbfc17
OM
816\r
817 //\r
818 // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME\r
819 //\r
820\r
821 // Note: all the NOR Flash region needs to be reserved into the UEFI Runtime memory;\r
822 // even if we only use the small block region at the top of the NOR Flash.\r
823 // The reason is when the NOR Flash memory is set into program mode, the command\r
824 // is written as the base of the flash region (ie: Instance->DeviceBaseAddress)\r
825 RuntimeMmioRegionSize = (Instance->RegionBaseAddress - Instance->DeviceBaseAddress) + Instance->Size;\r
826\r
827 Status = gDS->AddMemorySpace (\r
828 EfiGcdMemoryTypeMemoryMappedIo,\r
829 Instance->DeviceBaseAddress, RuntimeMmioRegionSize,\r
830 EFI_MEMORY_UC | EFI_MEMORY_RUNTIME\r
831 );\r
832 ASSERT_EFI_ERROR (Status);\r
833\r
834 Status = gDS->SetMemorySpaceAttributes (\r
835 Instance->DeviceBaseAddress, RuntimeMmioRegionSize,\r
836 EFI_MEMORY_UC | EFI_MEMORY_RUNTIME);\r
837 ASSERT_EFI_ERROR (Status);\r
838\r
839 //\r
840 // Register for the virtual address change event\r
841 //\r
842 Status = gBS->CreateEventEx (\r
843 EVT_NOTIFY_SIGNAL,\r
844 TPL_NOTIFY,\r
845 FvbVirtualNotifyEvent,\r
846 NULL,\r
847 &gEfiEventVirtualAddressChangeGuid,\r
848 &mFvbVirtualAddrChangeEvent\r
849 );\r
850 ASSERT_EFI_ERROR (Status);\r
851\r
1e57a462 852 return Status;\r
853}\r