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