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> |
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); |
6acb379f |
63 | Headers = AllocateZeroPool(HeadersLength); |
1d5d0ae9 |
64 | |
65 | // |
66 | // EFI_FIRMWARE_VOLUME_HEADER |
67 | // |
68 | FirmwareVolumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)Headers; |
69 | CopyGuid (&FirmwareVolumeHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid); |
70 | FirmwareVolumeHeader->FvLength = Instance->Media.BlockSize * (Instance->Media.LastBlock + 1); |
71 | FirmwareVolumeHeader->Signature = EFI_FVH_SIGNATURE; |
72 | FirmwareVolumeHeader->Attributes = (EFI_FVB_ATTRIBUTES_2) ( |
73 | EFI_FVB2_READ_ENABLED_CAP | // Reads may be enabled |
74 | EFI_FVB2_READ_STATUS | // Reads are currently enabled |
75 | EFI_FVB2_STICKY_WRITE | // A block erase is required to flip bits into EFI_FVB2_ERASE_POLARITY |
76 | EFI_FVB2_MEMORY_MAPPED | // It is memory mapped |
77 | EFI_FVB2_ERASE_POLARITY | // After erasure all bits take this value (i.e. '1') |
78 | EFI_FVB2_WRITE_STATUS | // Writes are currently enabled |
79 | EFI_FVB2_WRITE_ENABLED_CAP // Writes may be enabled |
80 | ); |
81 | FirmwareVolumeHeader->HeaderLength = sizeof(EFI_FIRMWARE_VOLUME_HEADER) + sizeof(EFI_FV_BLOCK_MAP_ENTRY); |
82 | FirmwareVolumeHeader->Revision = EFI_FVH_REVISION; |
83 | FirmwareVolumeHeader->BlockMap[0].NumBlocks = Instance->Media.LastBlock + 1; |
84 | FirmwareVolumeHeader->BlockMap[0].Length = Instance->Media.BlockSize; |
85 | FirmwareVolumeHeader->BlockMap[1].NumBlocks = 0; |
86 | FirmwareVolumeHeader->BlockMap[1].Length = 0; |
6acb379f |
87 | FirmwareVolumeHeader->Checksum = CalculateCheckSum16 ((UINT16*)FirmwareVolumeHeader,FirmwareVolumeHeader->HeaderLength); |
1d5d0ae9 |
88 | |
89 | // |
90 | // VARIABLE_STORE_HEADER |
91 | // |
92 | VariableStoreHeader = (VARIABLE_STORE_HEADER*)((UINT32)Headers + FirmwareVolumeHeader->HeaderLength); |
93 | CopyGuid (&VariableStoreHeader->Signature, &gEfiVariableGuid); |
94 | VariableStoreHeader->Size = PcdGet32(PcdFlashNvStorageVariableSize) - FirmwareVolumeHeader->HeaderLength; |
95 | VariableStoreHeader->Format = VARIABLE_STORE_FORMATTED; |
96 | VariableStoreHeader->State = VARIABLE_STORE_HEALTHY; |
97 | |
98 | // Install the combined super-header in the NorFlash |
6acb379f |
99 | Status = FvbWrite(&Instance->FvbProtocol, 0, 0, &HeadersLength, Headers ); |
1d5d0ae9 |
100 | |
101 | FreePool(Headers); |
102 | return Status; |
103 | } |
104 | |
105 | /** |
106 | Check the integrity of firmware volume header. |
107 | |
108 | @param[in] FwVolHeader - A pointer to a firmware volume header |
109 | |
110 | @retval EFI_SUCCESS - The firmware volume is consistent |
111 | @retval EFI_NOT_FOUND - The firmware volume has been corrupted. |
112 | |
113 | **/ |
114 | EFI_STATUS |
115 | ValidateFvHeader ( |
116 | IN NOR_FLASH_INSTANCE *Instance |
117 | ) |
118 | { |
119 | UINT16 Checksum; |
120 | EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; |
121 | VARIABLE_STORE_HEADER *VariableStoreHeader; |
122 | UINTN VariableStoreLength; |
123 | |
124 | FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER*)Instance->BaseAddress; |
125 | |
126 | // |
127 | // Verify the header revision, header signature, length |
128 | // Length of FvBlock cannot be 2**64-1 |
129 | // HeaderLength cannot be an odd number |
130 | // |
131 | if ( ( FwVolHeader->Revision != EFI_FVH_REVISION ) |
132 | || ( FwVolHeader->Signature != EFI_FVH_SIGNATURE ) |
133 | || ( FwVolHeader->FvLength != Instance->Media.BlockSize * (Instance->Media.LastBlock + 1) ) |
134 | ) { |
6acb379f |
135 | DEBUG ((EFI_D_ERROR, "ValidateFvHeader: No Firmware Volume header present\n")); |
1d5d0ae9 |
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 |
6acb379f |
146 | Checksum = CalculateSum16((UINT16*)FwVolHeader, FwVolHeader->HeaderLength); |
1d5d0ae9 |
147 | if (Checksum != 0) { |
148 | DEBUG ((EFI_D_ERROR, "ValidateFvHeader: FV checksum is invalid (Checksum:0x%X)\n",Checksum)); |
149 | return EFI_NOT_FOUND; |
6acb379f |
150 | } |
1d5d0ae9 |
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 |
6acb379f |
386 | FvbRead ( |
1d5d0ae9 |
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 | |
1d5d0ae9 |
411 | // Cache the block size to avoid de-referencing pointers all the time |
412 | BlockSize = Instance->Media.BlockSize; |
413 | |
414 | DEBUG ((DEBUG_BLKIO, "FvbRead: Check if (Offset=0x%x + NumBytes=0x%x) <= BlockSize=0x%x\n", Offset, *NumBytes, BlockSize )); |
415 | |
416 | // The read must not span block boundaries. |
417 | // We need to check each variable individually because adding two large values together overflows. |
418 | if ( ( Offset >= BlockSize ) || |
419 | ( *NumBytes > BlockSize ) || |
420 | ( (Offset + *NumBytes) > BlockSize ) ) { |
421 | DEBUG ((EFI_D_ERROR, "FvbRead: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize )); |
422 | return EFI_BAD_BUFFER_SIZE; |
423 | } |
424 | |
425 | // We must have some bytes to read |
426 | if (*NumBytes == 0) { |
427 | return EFI_BAD_BUFFER_SIZE; |
428 | } |
429 | |
430 | // FixMe: Allow an arbitrary number of bytes to be read out, not just a multiple of block size. |
431 | |
432 | // Allocate runtime memory to read in the NOR Flash data. Variable Services are runtime. |
433 | BlockBuffer = AllocateRuntimePool(BlockSize); |
434 | |
435 | // Check if the memory allocation was successful |
436 | if( BlockBuffer == NULL ) { |
437 | DEBUG ((EFI_D_ERROR, "FvbRead: ERROR - Could not allocate BlockBuffer @ 0x%08x.\n", BlockBuffer)); |
438 | return EFI_DEVICE_ERROR; |
439 | } |
440 | |
441 | // Read NOR Flash data into shadow buffer |
442 | TempStatus = NorFlashReadBlocks(Instance, Lba, BlockSize, BlockBuffer); |
443 | if (EFI_ERROR (TempStatus)) { |
444 | // Return one of the pre-approved error statuses |
445 | Status = EFI_DEVICE_ERROR; |
446 | goto FREE_MEMORY; |
447 | } |
448 | |
449 | // Put the data at the appropriate location inside the buffer area |
450 | DEBUG ((DEBUG_BLKIO, "FvbRead: CopyMem( Dst=0x%08x, Src=0x%08x, Size=0x%x ).\n", Buffer, BlockBuffer + Offset, *NumBytes)); |
451 | |
452 | CopyMem(Buffer, BlockBuffer + Offset, *NumBytes); |
453 | |
454 | FREE_MEMORY: |
455 | FreePool(BlockBuffer); |
456 | |
457 | DEBUG ((DEBUG_BLKIO, "FvbRead - end\n")); |
458 | return Status; |
459 | } |
460 | |
461 | /** |
462 | Writes the specified number of bytes from the input buffer to the block. |
463 | |
464 | The Write() function writes the specified number of bytes from |
465 | the provided buffer to the specified block and offset. If the |
466 | firmware volume is sticky write, the caller must ensure that |
467 | all the bits of the specified range to write are in the |
468 | EFI_FVB_ERASE_POLARITY state before calling the Write() |
469 | function, or else the result will be unpredictable. This |
470 | unpredictability arises because, for a sticky-write firmware |
471 | volume, a write may negate a bit in the EFI_FVB_ERASE_POLARITY |
472 | state but cannot flip it back again. Before calling the |
473 | Write() function, it is recommended for the caller to first call |
474 | the EraseBlocks() function to erase the specified block to |
475 | write. A block erase cycle will transition bits from the |
476 | (NOT)EFI_FVB_ERASE_POLARITY state back to the |
477 | EFI_FVB_ERASE_POLARITY state. Implementations should be |
478 | mindful that the firmware volume might be in the WriteDisabled |
479 | state. If it is in this state, the Write() function must |
480 | return the status code EFI_ACCESS_DENIED without modifying the |
481 | contents of the firmware volume. The Write() function must |
482 | also prevent spanning block boundaries. If a write is |
483 | requested that spans a block boundary, the write must store up |
484 | to the boundary but not beyond. The output parameter NumBytes |
485 | must be set to correctly indicate the number of bytes actually |
486 | written. The caller must be aware that a write may be |
487 | partially completed. All writes, partial or otherwise, must be |
488 | fully flushed to the hardware before the Write() service |
489 | returns. |
490 | |
491 | @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. |
492 | |
493 | @param Lba The starting logical block index to write to. |
494 | |
495 | @param Offset Offset into the block at which to begin writing. |
496 | |
497 | @param NumBytes The pointer to a UINTN. |
498 | At entry, *NumBytes contains the total size of the buffer. |
499 | At exit, *NumBytes contains the total number of bytes actually written. |
500 | |
501 | @param Buffer The pointer to a caller-allocated buffer that contains the source for the write. |
502 | |
503 | @retval EFI_SUCCESS The firmware volume was written successfully. |
504 | |
505 | @retval EFI_BAD_BUFFER_SIZE The write was attempted across an LBA boundary. |
506 | On output, NumBytes contains the total number of bytes |
507 | actually written. |
508 | |
509 | @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state. |
510 | |
511 | @retval EFI_DEVICE_ERROR The block device is malfunctioning and could not be written. |
512 | |
513 | |
514 | **/ |
515 | EFI_STATUS |
516 | EFIAPI |
6acb379f |
517 | FvbWrite ( |
1d5d0ae9 |
518 | IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
519 | IN EFI_LBA Lba, |
520 | IN UINTN Offset, |
521 | IN OUT UINTN *NumBytes, |
522 | IN UINT8 *Buffer |
523 | ) |
524 | { |
525 | EFI_STATUS Status; |
526 | EFI_STATUS TempStatus; |
527 | UINTN BlockSize; |
528 | UINT8 *BlockBuffer; |
529 | NOR_FLASH_INSTANCE *Instance; |
530 | |
531 | Instance = INSTANCE_FROM_FVB_THIS(This); |
532 | |
533 | if (!Instance->Initialized) { |
534 | Instance->Initialize(Instance); |
535 | } |
536 | |
537 | DEBUG ((DEBUG_BLKIO, "FvbWrite(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer)); |
538 | |
539 | Status = EFI_SUCCESS; |
540 | TempStatus = Status; |
541 | |
542 | // Detect WriteDisabled state |
543 | if (Instance->Media.ReadOnly == TRUE) { |
544 | DEBUG ((EFI_D_ERROR, "FvbWrite: ERROR - Can not write: Device is in WriteDisabled state.\n")); |
545 | // It is in WriteDisabled state, return an error right away |
546 | return EFI_ACCESS_DENIED; |
547 | } |
548 | |
549 | // Cache the block size to avoid de-referencing pointers all the time |
550 | BlockSize = Instance->Media.BlockSize; |
551 | |
552 | // The write must not span block boundaries. |
553 | // We need to check each variable individually because adding two large values together overflows. |
554 | if ( ( Offset >= BlockSize ) || |
555 | ( *NumBytes > BlockSize ) || |
556 | ( (Offset + *NumBytes) > BlockSize ) ) { |
557 | DEBUG ((EFI_D_ERROR, "FvbWrite: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize )); |
558 | return EFI_BAD_BUFFER_SIZE; |
559 | } |
560 | |
561 | // We must have some bytes to write |
562 | if (*NumBytes == 0) { |
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 | // Allocate runtime memory to read in the NOR Flash data. |
568 | // Since the intention is to use this with Variable Services and since these are runtime, |
569 | // allocate the memory from the runtime pool. |
570 | BlockBuffer = AllocateRuntimePool(BlockSize); |
571 | |
572 | // Check we did get some memory |
573 | if( BlockBuffer == NULL ) { |
574 | DEBUG ((EFI_D_ERROR, "FvbWrite: ERROR - Can not allocate BlockBuffer @ 0x%08x.\n", BlockBuffer)); |
575 | return EFI_DEVICE_ERROR; |
576 | } |
577 | |
578 | // Read NOR Flash data into shadow buffer |
579 | TempStatus = NorFlashReadBlocks(Instance, Lba, BlockSize, BlockBuffer); |
580 | if (EFI_ERROR (TempStatus)) { |
581 | // Return one of the pre-approved error statuses |
582 | Status = EFI_DEVICE_ERROR; |
583 | goto FREE_MEMORY; |
584 | } |
585 | |
586 | // Put the data at the appropriate location inside the buffer area |
587 | CopyMem((BlockBuffer + Offset), Buffer, *NumBytes); |
588 | |
589 | // Write the modified buffer back to the NorFlash |
590 | Status = NorFlashWriteBlocks(Instance, Lba, BlockSize, BlockBuffer); |
591 | if (EFI_ERROR (TempStatus)) { |
592 | // Return one of the pre-approved error statuses |
593 | Status = EFI_DEVICE_ERROR; |
594 | goto FREE_MEMORY; |
595 | } |
596 | |
597 | FREE_MEMORY: |
598 | FreePool(BlockBuffer); |
599 | return Status; |
600 | } |
601 | |
602 | /** |
603 | Erases and initialises a firmware volume block. |
604 | |
605 | The EraseBlocks() function erases one or more blocks as denoted |
606 | by the variable argument list. The entire parameter list of |
607 | blocks must be verified before erasing any blocks. If a block is |
608 | requested that does not exist within the associated firmware |
609 | volume (it has a larger index than the last block of the |
610 | firmware volume), the EraseBlocks() function must return the |
611 | status code EFI_INVALID_PARAMETER without modifying the contents |
612 | of the firmware volume. Implementations should be mindful that |
613 | the firmware volume might be in the WriteDisabled state. If it |
614 | is in this state, the EraseBlocks() function must return the |
615 | status code EFI_ACCESS_DENIED without modifying the contents of |
616 | the firmware volume. All calls to EraseBlocks() must be fully |
617 | flushed to the hardware before the EraseBlocks() service |
618 | returns. |
619 | |
620 | @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL |
621 | instance. |
622 | |
623 | @param ... The variable argument list is a list of tuples. |
624 | Each tuple describes a range of LBAs to erase |
625 | and consists of the following: |
626 | - An EFI_LBA that indicates the starting LBA |
627 | - A UINTN that indicates the number of blocks to erase. |
628 | |
629 | The list is terminated with an EFI_LBA_LIST_TERMINATOR. |
630 | For example, the following indicates that two ranges of blocks |
631 | (5-7 and 10-11) are to be erased: |
632 | EraseBlocks (This, 5, 3, 10, 2, EFI_LBA_LIST_TERMINATOR); |
633 | |
634 | @retval EFI_SUCCESS The erase request successfully completed. |
635 | |
636 | @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state. |
637 | |
638 | @retval EFI_DEVICE_ERROR The block device is not functioning correctly and could not be written. |
639 | The firmware device may have been partially erased. |
640 | |
641 | @retval EFI_INVALID_PARAMETER One or more of the LBAs listed in the variable argument list do |
642 | not exist in the firmware volume. |
643 | |
644 | **/ |
645 | EFI_STATUS |
646 | EFIAPI |
6acb379f |
647 | FvbEraseBlocks ( |
1d5d0ae9 |
648 | IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
649 | ... |
650 | ) |
651 | { |
652 | EFI_STATUS Status; |
653 | VA_LIST args; |
654 | UINTN BlockAddress; // Physical address of Lba to erase |
655 | EFI_LBA StartingLba; // Lba from which we start erasing |
656 | UINTN NumOfLba; // Number of Lba blocks to erase |
657 | NOR_FLASH_INSTANCE *Instance; |
658 | |
659 | Instance = INSTANCE_FROM_FVB_THIS(This); |
660 | |
661 | DEBUG ((DEBUG_BLKIO, "FvbEraseBlocks()\n")); |
662 | |
663 | Status = EFI_SUCCESS; |
664 | |
665 | // Detect WriteDisabled state |
666 | if (Instance->Media.ReadOnly == TRUE) { |
667 | // Firmware volume is in WriteDisabled state |
668 | DEBUG ((EFI_D_ERROR, "FvbEraseBlocks: ERROR - Device is in WriteDisabled state.\n")); |
669 | return EFI_ACCESS_DENIED; |
670 | } |
671 | |
672 | // Before erasing, check the entire list of parameters to ensure all specified blocks are valid |
673 | |
674 | VA_START (args, This); |
675 | |
676 | do { |
677 | |
678 | // Get the Lba from which we start erasing |
679 | StartingLba = VA_ARG (args, EFI_LBA); |
680 | |
681 | // Have we reached the end of the list? |
682 | if (StartingLba == EFI_LBA_LIST_TERMINATOR) { |
683 | //Exit the while loop |
684 | break; |
685 | } |
686 | |
687 | // How many Lba blocks are we requested to erase? |
688 | NumOfLba = VA_ARG (args, UINT32); |
689 | |
690 | // All blocks must be within range |
691 | DEBUG ((DEBUG_BLKIO, "FvbEraseBlocks: Check if: ( StartingLba=%ld + NumOfLba=%d - 1 ) > LastBlock=%ld.\n", StartingLba, NumOfLba, Instance->Media.LastBlock)); |
692 | if ((NumOfLba == 0) || ((StartingLba + NumOfLba - 1) > Instance->Media.LastBlock)) { |
693 | VA_END (args); |
694 | DEBUG ((DEBUG_BLKIO, "FvbEraseBlocks: Check if: ( StartingLba=%ld + NumOfLba=%d - 1 ) > LastBlock=%ld.\n", StartingLba, NumOfLba, Instance->Media.LastBlock)); |
695 | DEBUG ((EFI_D_ERROR, "FvbEraseBlocks: ERROR - Lba range goes past the last Lba.\n")); |
696 | Status = EFI_INVALID_PARAMETER; |
697 | goto EXIT; |
698 | } |
699 | |
700 | } while (TRUE); |
701 | |
702 | VA_END (args); |
703 | |
704 | // To get here, all must be ok, so start erasing |
705 | |
706 | VA_START (args, This); |
707 | |
708 | do { |
709 | |
710 | // Get the Lba from which we start erasing |
711 | StartingLba = VA_ARG (args, EFI_LBA); |
712 | |
713 | // Have we reached the end of the list? |
714 | if (StartingLba == EFI_LBA_LIST_TERMINATOR) { |
715 | // Exit the while loop |
716 | break; |
717 | } |
718 | |
719 | // How many Lba blocks are we requested to erase? |
720 | NumOfLba = VA_ARG (args, UINT32); |
721 | |
722 | // Go through each one and erase it |
723 | while (NumOfLba > 0) { |
724 | |
725 | // Get the physical address of Lba to erase |
6acb379f |
726 | BlockAddress = GET_NOR_BLOCK_ADDRESS ( |
1d5d0ae9 |
727 | Instance->BaseAddress, |
728 | StartingLba, |
729 | Instance->Media.BlockSize |
730 | ); |
731 | |
732 | // Erase it |
733 | DEBUG ((DEBUG_BLKIO, "FvbEraseBlocks: Erasing Lba=%ld @ 0x%08x.\n", StartingLba, BlockAddress)); |
6acb379f |
734 | Status = NorFlashUnlockAndEraseSingleBlock (BlockAddress); |
1d5d0ae9 |
735 | if (EFI_ERROR(Status)) { |
736 | VA_END (args); |
737 | Status = EFI_DEVICE_ERROR; |
738 | goto EXIT; |
739 | } |
740 | |
741 | // Move to the next Lba |
742 | StartingLba++; |
743 | NumOfLba--; |
744 | } |
745 | |
746 | } while (TRUE); |
747 | |
748 | VA_END (args); |
749 | |
750 | EXIT: |
751 | return Status; |
752 | } |
753 | |
754 | EFI_STATUS |
755 | EFIAPI |
756 | NorFlashFvbInitialize ( |
757 | IN NOR_FLASH_INSTANCE* Instance |
758 | ) { |
759 | EFI_STATUS Status; |
760 | |
761 | DEBUG((DEBUG_BLKIO,"NorFlashFvbInitialize\n")); |
762 | |
763 | Status = NorFlashBlkIoInitialize(Instance); |
764 | if (EFI_ERROR(Status)) { |
765 | DEBUG((EFI_D_ERROR,"NorFlashFvbInitialize: ERROR - Failed to initialize FVB\n")); |
766 | return Status; |
767 | } |
768 | Instance->Initialized = TRUE; |
769 | |
770 | // Determine if there is a valid header at the beginning of the NorFlash |
771 | Status = ValidateFvHeader (Instance); |
772 | if (EFI_ERROR(Status)) { |
773 | // There is no valid header, so time to install one. |
6acb379f |
774 | DEBUG((EFI_D_ERROR,"NorFlashFvbInitialize: ERROR - The FVB Header is not valid. Installing a correct one for this volume.\n")); |
1d5d0ae9 |
775 | |
776 | // Erase all the NorFlash that is reserved for variable storage |
777 | Status = FvbEraseBlocks ( &Instance->FvbProtocol, (EFI_LBA)0, (UINT32)(Instance->Media.LastBlock + 1), EFI_LBA_LIST_TERMINATOR ); |
778 | if (EFI_ERROR(Status)) { |
779 | return Status; |
780 | } |
781 | |
782 | // Install all appropriate headers |
783 | InitializeFvAndVariableStoreHeaders ( Instance ); |
784 | if (EFI_ERROR(Status)) { |
785 | return Status; |
786 | } |
787 | } |
788 | |
789 | return Status; |
790 | } |