]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/Dxe/FwVolBlock/FwVolBlock.c
Code Scrub for Dxe Core.
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / FwVolBlock / FwVolBlock.c
1 /** @file
2 Implementations for Firmware Volume Block protocol.
3
4 It consumes FV HOBs and creates read-lonly Firmare Volume Block protocol
5 instances for each of them.
6
7 Copyright (c) 2006 - 2008, Intel Corporation. <BR>
8 All rights reserved. This program and the accompanying materials
9 are licensed and made available under the terms and conditions of the BSD License
10 which accompanies this distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
12
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15
16 **/
17
18 #include <DxeMain.h>
19
20
21 EFI_FW_VOL_BLOCK_DEVICE mFwVolBlock = {
22 FVB_DEVICE_SIGNATURE,
23 NULL,
24 {
25 {
26 {
27 HARDWARE_DEVICE_PATH,
28 HW_MEMMAP_DP,
29 {
30 (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
31 (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)
32 }
33 },
34 EfiMemoryMappedIO,
35 (EFI_PHYSICAL_ADDRESS) 0,
36 (EFI_PHYSICAL_ADDRESS) 0,
37 },
38 {
39 END_DEVICE_PATH_TYPE,
40 END_ENTIRE_DEVICE_PATH_SUBTYPE,
41 {
42 END_DEVICE_PATH_LENGTH,
43 0
44 }
45 },
46 },
47 {
48 FwVolBlockGetAttributes,
49 (EFI_FVB_SET_ATTRIBUTES)FwVolBlockSetAttributes,
50 FwVolBlockGetPhysicalAddress,
51 FwVolBlockGetBlockSize,
52 FwVolBlockReadBlock,
53 (EFI_FVB_WRITE)FwVolBlockWriteBlock,
54 (EFI_FVB_ERASE_BLOCKS)FwVolBlockEraseBlock,
55 NULL
56 },
57 0,
58 NULL,
59 0,
60 0
61 };
62
63
64
65 /**
66 Retrieves Volume attributes. No polarity translations are done.
67
68 @param This Calling context
69 @param Attributes output buffer which contains attributes
70
71 @retval EFI_SUCCESS The firmware volume attributes were returned.
72
73 **/
74 EFI_STATUS
75 EFIAPI
76 FwVolBlockGetAttributes (
77 IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
78 OUT EFI_FVB_ATTRIBUTES *Attributes
79 )
80 {
81 EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
82
83 FvbDevice = FVB_DEVICE_FROM_THIS (This);
84
85 //
86 // Since we are read only, it's safe to get attributes data from our in-memory copy.
87 //
88 *Attributes = FvbDevice->FvbAttributes;
89
90 return EFI_SUCCESS;
91 }
92
93
94
95 /**
96 Modifies the current settings of the firmware volume according to the input parameter.
97
98 @param This Calling context
99 @param Attributes input buffer which contains attributes
100
101 @retval EFI_SUCCESS The firmware volume attributes were returned.
102 @retval EFI_INVALID_PARAMETER The attributes requested are in conflict with
103 the capabilities as declared in the firmware
104 volume header.
105 @retval EFI_UNSUPPORTED Not supported.
106
107 **/
108 EFI_STATUS
109 EFIAPI
110 FwVolBlockSetAttributes (
111 IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
112 IN CONST EFI_FVB_ATTRIBUTES *Attributes
113 )
114 {
115 return EFI_UNSUPPORTED;
116 }
117
118
119
120 /**
121 The EraseBlock() function erases one or more blocks as denoted by the
122 variable argument list. The entire parameter list of blocks must be verified
123 prior to erasing any blocks. If a block is requested that does not exist
124 within the associated firmware volume (it has a larger index than the last
125 block of the firmware volume), the EraseBlock() function must return
126 EFI_INVALID_PARAMETER without modifying the contents of the firmware volume.
127
128 @param This Calling context
129 @param ... Starting LBA followed by Number of Lba to erase.
130 a -1 to terminate the list.
131
132 @retval EFI_SUCCESS The erase request was successfully completed.
133 @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled
134 state.
135 @retval EFI_DEVICE_ERROR The block device is not functioning correctly
136 and could not be written. The firmware device
137 may have been partially erased.
138 @retval EFI_INVALID_PARAMETER One or more of the LBAs listed in the variable
139 argument list do
140 @retval EFI_UNSUPPORTED Not supported.
141
142 **/
143 EFI_STATUS
144 EFIAPI
145 FwVolBlockEraseBlock (
146 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
147 ...
148 )
149 {
150 return EFI_UNSUPPORTED;
151 }
152
153
154
155 /**
156 Read the specified number of bytes from the block to the input buffer.
157
158 @param This Indicates the calling context.
159 @param Lba The starting logical block index to read.
160 @param Offset Offset into the block at which to begin reading.
161 @param NumBytes Pointer to a UINT32. At entry, *NumBytes
162 contains the total size of the buffer. At exit,
163 *NumBytes contains the total number of bytes
164 actually read.
165 @param Buffer Pinter to a caller-allocated buffer that
166 contains the destine for the read.
167
168 @retval EFI_SUCCESS The firmware volume was read successfully.
169 @retval EFI_BAD_BUFFER_SIZE The read was attempted across an LBA boundary.
170 @retval EFI_ACCESS_DENIED Access denied.
171 @retval EFI_DEVICE_ERROR The block device is malfunctioning and could not
172 be read.
173
174 **/
175 EFI_STATUS
176 EFIAPI
177 FwVolBlockReadBlock (
178 IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
179 IN CONST EFI_LBA Lba,
180 IN CONST UINTN Offset,
181 IN OUT UINTN *NumBytes,
182 IN OUT UINT8 *Buffer
183 )
184 {
185 EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
186 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
187 UINT8 *LbaOffset;
188 UINTN LbaStart;
189 UINTN NumOfBytesRead;
190 UINTN LbaIndex;
191
192 FvbDevice = FVB_DEVICE_FROM_THIS (This);
193
194 //
195 // Check if This FW can be read
196 //
197 if ((FvbDevice->FvbAttributes & EFI_FVB2_READ_STATUS) == 0) {
198 return EFI_ACCESS_DENIED;
199 }
200
201 LbaIndex = (UINTN) Lba;
202 if (LbaIndex >= FvbDevice->NumBlocks) {
203 //
204 // Invalid Lba, read nothing.
205 //
206 *NumBytes = 0;
207 return EFI_BAD_BUFFER_SIZE;
208 }
209
210 if (Offset > FvbDevice->LbaCache[LbaIndex].Length) {
211 //
212 // all exceed boundry, read nothing.
213 //
214 *NumBytes = 0;
215 return EFI_BAD_BUFFER_SIZE;
216 }
217
218 NumOfBytesRead = *NumBytes;
219 if (Offset + NumOfBytesRead > FvbDevice->LbaCache[LbaIndex].Length) {
220 //
221 // partial exceed boundry, read data from current postion to end.
222 //
223 NumOfBytesRead = FvbDevice->LbaCache[LbaIndex].Length - Offset;
224 }
225
226 LbaStart = FvbDevice->LbaCache[LbaIndex].Base;
227 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)((UINTN) FvbDevice->BaseAddress);
228 LbaOffset = (UINT8 *) FwVolHeader + LbaStart + Offset;
229
230 //
231 // Perform read operation
232 //
233 CopyMem (Buffer, LbaOffset, NumOfBytesRead);
234
235 if (NumOfBytesRead == *NumBytes) {
236 return EFI_SUCCESS;
237 }
238
239 *NumBytes = NumOfBytesRead;
240 return EFI_BAD_BUFFER_SIZE;
241 }
242
243
244
245 /**
246 Writes the specified number of bytes from the input buffer to the block.
247
248 @param This Indicates the calling context.
249 @param Lba The starting logical block index to write to.
250 @param Offset Offset into the block at which to begin writing.
251 @param NumBytes Pointer to a UINT32. At entry, *NumBytes
252 contains the total size of the buffer. At exit,
253 *NumBytes contains the total number of bytes
254 actually written.
255 @param Buffer Pinter to a caller-allocated buffer that
256 contains the source for the write.
257
258 @retval EFI_SUCCESS The firmware volume was written successfully.
259 @retval EFI_BAD_BUFFER_SIZE The write was attempted across an LBA boundary.
260 On output, NumBytes contains the total number of
261 bytes actually written.
262 @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled
263 state.
264 @retval EFI_DEVICE_ERROR The block device is malfunctioning and could not
265 be written.
266 @retval EFI_UNSUPPORTED Not supported.
267
268 **/
269 EFI_STATUS
270 EFIAPI
271 FwVolBlockWriteBlock (
272 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
273 IN EFI_LBA Lba,
274 IN UINTN Offset,
275 IN OUT UINTN *NumBytes,
276 IN UINT8 *Buffer
277 )
278 {
279 return EFI_UNSUPPORTED;
280 }
281
282
283
284 /**
285 Get Fvb's base address.
286
287 @param This Indicates the calling context.
288 @param Address Fvb device base address.
289
290 @retval EFI_SUCCESS Successfully got Fvb's base address.
291 @retval EFI_UNSUPPORTED Not supported.
292
293 **/
294 EFI_STATUS
295 EFIAPI
296 FwVolBlockGetPhysicalAddress (
297 IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
298 OUT EFI_PHYSICAL_ADDRESS *Address
299 )
300 {
301 EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
302
303 FvbDevice = FVB_DEVICE_FROM_THIS (This);
304
305 if (FvbDevice->FvbAttributes & EFI_FVB2_MEMORY_MAPPED) {
306 *Address = FvbDevice->BaseAddress;
307 return EFI_SUCCESS;
308 }
309
310 return EFI_UNSUPPORTED;
311 }
312
313
314
315 /**
316 Retrieves the size in bytes of a specific block within a firmware volume.
317
318 @param This Indicates the calling context.
319 @param Lba Indicates the block for which to return the
320 size.
321 @param BlockSize Pointer to a caller-allocated UINTN in which the
322 size of the block is returned.
323 @param NumberOfBlocks Pointer to a caller-allocated UINTN in which the
324 number of consecutive blocks starting with Lba
325 is returned. All blocks in this range have a
326 size of BlockSize.
327
328 @retval EFI_SUCCESS The firmware volume base address is returned.
329 @retval EFI_INVALID_PARAMETER The requested LBA is out of range.
330
331 **/
332 EFI_STATUS
333 EFIAPI
334 FwVolBlockGetBlockSize (
335 IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
336 IN CONST EFI_LBA Lba,
337 IN OUT UINTN *BlockSize,
338 IN OUT UINTN *NumberOfBlocks
339 )
340 {
341 UINTN TotalBlocks;
342 EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
343 EFI_FV_BLOCK_MAP_ENTRY *PtrBlockMapEntry;
344 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
345
346 FvbDevice = FVB_DEVICE_FROM_THIS (This);
347
348 //
349 // Do parameter checking
350 //
351 if (Lba >= FvbDevice->NumBlocks) {
352 return EFI_INVALID_PARAMETER;
353 }
354
355 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)((UINTN)FvbDevice->BaseAddress);
356
357 PtrBlockMapEntry = FwVolHeader->BlockMap;
358
359 //
360 // Search the block map for the given block
361 //
362 TotalBlocks = 0;
363 while ((PtrBlockMapEntry->NumBlocks != 0) || (PtrBlockMapEntry->Length !=0 )) {
364 TotalBlocks += PtrBlockMapEntry->NumBlocks;
365 if (Lba < TotalBlocks) {
366 //
367 // We find the range
368 //
369 break;
370 }
371
372 PtrBlockMapEntry++;
373 }
374
375 *BlockSize = PtrBlockMapEntry->Length;
376 *NumberOfBlocks = TotalBlocks - (UINTN)Lba;
377
378 return EFI_SUCCESS;
379 }
380
381
382
383 /**
384 This routine produces a firmware volume block protocol on a given
385 buffer.
386
387 @param BaseAddress base address of the firmware volume image
388 @param Length length of the firmware volume image
389 @param ParentHandle handle of parent firmware volume, if this image
390 came from an FV image file in another firmware
391 volume (ala capsules)
392 @param FvProtocol Firmware volume block protocol produced.
393
394 @retval EFI_VOLUME_CORRUPTED Volume corrupted.
395 @retval EFI_OUT_OF_RESOURCES No enough buffer to be allocated.
396 @retval EFI_SUCCESS Successfully produced a FVB protocol on given
397 buffer.
398
399 **/
400 EFI_STATUS
401 ProduceFVBProtocolOnBuffer (
402 IN EFI_PHYSICAL_ADDRESS BaseAddress,
403 IN UINT64 Length,
404 IN EFI_HANDLE ParentHandle,
405 OUT EFI_HANDLE *FvProtocol OPTIONAL
406 )
407 {
408 EFI_STATUS Status;
409 EFI_FW_VOL_BLOCK_DEVICE *FvbDev;
410 EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
411 UINTN BlockIndex;
412 UINTN BlockIndex2;
413 UINTN LinearOffset;
414 UINT32 FvAlignment;
415 EFI_FV_BLOCK_MAP_ENTRY *PtrBlockMapEntry;
416
417 FvAlignment = 0;
418 FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN) BaseAddress;
419 //
420 // Validate FV Header, if not as expected, return
421 //
422 if (FwVolHeader->Signature != EFI_FVH_SIGNATURE) {
423 return EFI_VOLUME_CORRUPTED;
424 }
425 //
426 // Get FvHeader alignment
427 //
428 FvAlignment = 1 << ((FwVolHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16);
429 if (FvAlignment < 8) {
430 FvAlignment = 8;
431 }
432 if ((UINTN)BaseAddress % FvAlignment != 0) {
433 //
434 // FvImage buffer is not at its required alignment.
435 //
436 return EFI_VOLUME_CORRUPTED;
437 }
438 //
439 // Allocate EFI_FW_VOL_BLOCK_DEVICE
440 //
441 FvbDev = CoreAllocateCopyPool (sizeof (EFI_FW_VOL_BLOCK_DEVICE), &mFwVolBlock);
442 if (FvbDev == NULL) {
443 return EFI_OUT_OF_RESOURCES;
444 }
445
446 FvbDev->BaseAddress = BaseAddress;
447 FvbDev->FvbAttributes = FwVolHeader->Attributes;
448 FvbDev->FwVolBlockInstance.ParentHandle = ParentHandle;
449
450 //
451 // Init the block caching fields of the device
452 // First, count the number of blocks
453 //
454 FvbDev->NumBlocks = 0;
455 for (PtrBlockMapEntry = FwVolHeader->BlockMap;
456 PtrBlockMapEntry->NumBlocks != 0;
457 PtrBlockMapEntry++) {
458 FvbDev->NumBlocks += PtrBlockMapEntry->NumBlocks;
459 }
460 //
461 // Second, allocate the cache
462 //
463 FvbDev->LbaCache = CoreAllocateBootServicesPool (FvbDev->NumBlocks * sizeof (LBA_CACHE));
464 if (FvbDev->LbaCache == NULL) {
465 CoreFreePool (FvbDev);
466 return EFI_OUT_OF_RESOURCES;
467 }
468 //
469 // Last, fill in the cache with the linear address of the blocks
470 //
471 BlockIndex = 0;
472 LinearOffset = 0;
473 for (PtrBlockMapEntry = FwVolHeader->BlockMap;
474 PtrBlockMapEntry->NumBlocks != 0; PtrBlockMapEntry++) {
475 for (BlockIndex2 = 0; BlockIndex2 < PtrBlockMapEntry->NumBlocks; BlockIndex2++) {
476 FvbDev->LbaCache[BlockIndex].Base = LinearOffset;
477 FvbDev->LbaCache[BlockIndex].Length = PtrBlockMapEntry->Length;
478 LinearOffset += PtrBlockMapEntry->Length;
479 BlockIndex++;
480 }
481 }
482
483 //
484 // Set up the devicepath
485 //
486 FvbDev->DevicePath.MemMapDevPath.StartingAddress = BaseAddress;
487 FvbDev->DevicePath.MemMapDevPath.EndingAddress = BaseAddress + FwVolHeader->FvLength - 1;
488
489 //
490 //
491 // Attach FvVolBlock Protocol to new handle
492 //
493 Status = CoreInstallMultipleProtocolInterfaces (
494 &FvbDev->Handle,
495 &gEfiFirmwareVolumeBlockProtocolGuid, &FvbDev->FwVolBlockInstance,
496 &gEfiDevicePathProtocolGuid, &FvbDev->DevicePath,
497 &gEfiFirmwareVolumeDispatchProtocolGuid, NULL,
498 NULL
499 );
500
501 //
502 // If they want the handle back, set it.
503 //
504 if (FvProtocol != NULL) {
505 *FvProtocol = FvbDev->Handle;
506 }
507
508 return Status;
509 }
510
511
512
513 /**
514 This routine is the driver initialization entry point. It initializes the
515 libraries, consumes FV hobs and NT_NON_MM_FV environment variable and
516 produces instances of FW_VOL_BLOCK_PROTOCOL as appropriate.
517
518 @param ImageHandle The image handle.
519 @param SystemTable The system table.
520
521 @retval EFI_SUCCESS Successfully initialized firmware volume block
522 driver.
523
524 **/
525 EFI_STATUS
526 EFIAPI
527 FwVolBlockDriverInit (
528 IN EFI_HANDLE ImageHandle,
529 IN EFI_SYSTEM_TABLE *SystemTable
530 )
531 {
532 EFI_PEI_HOB_POINTERS FvHob;
533
534 //
535 // Core Needs Firmware Volumes to function
536 //
537 FvHob.Raw = GetHobList ();
538 while ((FvHob.Raw = GetNextHob (EFI_HOB_TYPE_FV, FvHob.Raw)) != NULL) {
539 //
540 // Produce an FVB protocol for it
541 //
542 ProduceFVBProtocolOnBuffer (FvHob.FirmwareVolume->BaseAddress, FvHob.FirmwareVolume->Length, NULL, NULL);
543 FvHob.Raw = GET_NEXT_HOB (FvHob);
544 }
545
546 return EFI_SUCCESS;
547 }
548
549
550
551 /**
552 This DXE service routine is used to process a firmware volume. In
553 particular, it can be called by BDS to process a single firmware
554 volume found in a capsule.
555
556 @param FvHeader pointer to a firmware volume header
557 @param Size the size of the buffer pointed to by FvHeader
558 @param FVProtocolHandle the handle on which a firmware volume protocol
559 was produced for the firmware volume passed in.
560
561 @retval EFI_OUT_OF_RESOURCES if an FVB could not be produced due to lack of
562 system resources
563 @retval EFI_VOLUME_CORRUPTED if the volume was corrupted
564 @retval EFI_SUCCESS a firmware volume protocol was produced for the
565 firmware volume
566
567 **/
568 EFI_STATUS
569 CoreProcessFirmwareVolume (
570 IN VOID *FvHeader,
571 IN UINTN Size,
572 OUT EFI_HANDLE *FVProtocolHandle
573 )
574 {
575 VOID *Ptr;
576 EFI_STATUS Status;
577
578 *FVProtocolHandle = NULL;
579 Status = ProduceFVBProtocolOnBuffer (
580 (EFI_PHYSICAL_ADDRESS) (UINTN) FvHeader,
581 (UINT64)Size,
582 NULL,
583 FVProtocolHandle
584 );
585 //
586 // Since in our implementation we use register-protocol-notify to put a
587 // FV protocol on the FVB protocol handle, we can't directly verify that
588 // the FV protocol was produced. Therefore here we will check the handle
589 // and make sure an FV protocol is on it. This indicates that all went
590 // well. Otherwise we have to assume that the volume was corrupted
591 // somehow.
592 //
593 if (!EFI_ERROR(Status)) {
594 Ptr = NULL;
595 Status = CoreHandleProtocol (*FVProtocolHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **) &Ptr);
596 if (EFI_ERROR(Status) || (Ptr == NULL)) {
597 return EFI_VOLUME_CORRUPTED;
598 }
599 return EFI_SUCCESS;
600 }
601 return Status;
602 }
603
604
605