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