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