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