]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Library/DxeCapsuleLibFmp/CapsuleOnDisk.c
MdePkg: Add Generic Initiator Affinity Structure definitions to SRAT
[mirror_edk2.git] / MdeModulePkg / Library / DxeCapsuleLibFmp / CapsuleOnDisk.c
CommitLineData
8636f70b
WX
1/** @file\r
2 The implementation supports Capusle on Disk.\r
3\r
4 Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
6\r
7**/\r
8\r
9#include "CapsuleOnDisk.h"\r
10\r
11/**\r
12 Return if this capsule is a capsule name capsule, based upon CapsuleHeader.\r
13\r
14 @param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER\r
15\r
16 @retval TRUE It is a capsule name capsule.\r
17 @retval FALSE It is not a capsule name capsule.\r
18**/\r
19BOOLEAN\r
20IsCapsuleNameCapsule (\r
21 IN EFI_CAPSULE_HEADER *CapsuleHeader\r
22 );\r
23\r
24/**\r
25 Check the integrity of the capsule name capsule.\r
26 If the capsule is vaild, return the physical address of each capsule name string.\r
27\r
28 @param[in] CapsuleHeader Pointer to the capsule header of a capsule name capsule.\r
29 @param[out] CapsuleNameNum Number of capsule name.\r
30\r
31 @retval NULL Capsule name capsule is not valid.\r
32 @retval CapsuleNameBuf Array of capsule name physical address.\r
33\r
34**/\r
35EFI_PHYSICAL_ADDRESS *\r
36ValidateCapsuleNameCapsuleIntegrity (\r
37 IN EFI_CAPSULE_HEADER *CapsuleHeader,\r
38 OUT UINTN *CapsuleNameNum\r
39 )\r
40{\r
41 UINT8 *CapsuleNamePtr;\r
42 UINT8 *CapsuleNameBufStart;\r
43 UINT8 *CapsuleNameBufEnd;\r
44 UINTN Index;\r
45 UINTN StringSize;\r
46 EFI_PHYSICAL_ADDRESS *CapsuleNameBuf;\r
47\r
48 if (!IsCapsuleNameCapsule (CapsuleHeader)) {\r
49 return NULL;\r
50 }\r
51\r
52 //\r
53 // Total string size must be even.\r
54 //\r
55 if (((CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize) & BIT0) != 0) {\r
56 return NULL;\r
57 }\r
58\r
59 *CapsuleNameNum = 0;\r
60 Index = 0;\r
61 CapsuleNameBufStart = (UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize;\r
62\r
63 //\r
64 // If strings are not aligned on a 16-bit boundary, reallocate memory for it.\r
65 //\r
66 if (((UINTN) CapsuleNameBufStart & BIT0) != 0) {\r
67 CapsuleNameBufStart = AllocateCopyPool (CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize, CapsuleNameBufStart);\r
68 }\r
69\r
70 CapsuleNameBufEnd = CapsuleNameBufStart + CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize;\r
71\r
72 CapsuleNamePtr = CapsuleNameBufStart;\r
73 while (CapsuleNamePtr < CapsuleNameBufEnd) {\r
74 StringSize= StrnSizeS ((CHAR16 *) CapsuleNamePtr, (CapsuleNameBufEnd - CapsuleNamePtr)/sizeof(CHAR16));\r
75 CapsuleNamePtr += StringSize;\r
76 (*CapsuleNameNum) ++;\r
77 }\r
78\r
79 //\r
80 // Integrity check.\r
81 //\r
82 if (CapsuleNamePtr != CapsuleNameBufEnd) {\r
83 if (CapsuleNameBufStart != (UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize) {\r
84 FreePool (CapsuleNameBufStart);\r
85 }\r
86 return NULL;\r
87 }\r
88\r
89 CapsuleNameBuf = AllocatePool (*CapsuleNameNum * sizeof (EFI_PHYSICAL_ADDRESS));\r
90 if (CapsuleNameBuf == NULL) {\r
91 if (CapsuleNameBufStart != (UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize) {\r
92 FreePool (CapsuleNameBufStart);\r
93 }\r
94 return NULL;\r
95 }\r
96\r
97 CapsuleNamePtr = CapsuleNameBufStart;\r
98 while (CapsuleNamePtr < CapsuleNameBufEnd) {\r
99 StringSize= StrnSizeS ((CHAR16 *) CapsuleNamePtr, (CapsuleNameBufEnd - CapsuleNamePtr)/sizeof(CHAR16));\r
100 CapsuleNameBuf[Index] = (EFI_PHYSICAL_ADDRESS)(UINTN) CapsuleNamePtr;\r
101 CapsuleNamePtr += StringSize;\r
102 Index ++;\r
103 }\r
104\r
105 return CapsuleNameBuf;\r
106}\r
107\r
108/**\r
109 This routine is called to upper case given unicode string.\r
110\r
111 @param[in] Str String to upper case\r
112\r
113 @retval upper cased string after process\r
114\r
115**/\r
116static\r
117CHAR16 *\r
118UpperCaseString (\r
119 IN CHAR16 *Str\r
120 )\r
121{\r
122 CHAR16 *Cptr;\r
123\r
124 for (Cptr = Str; *Cptr; Cptr++) {\r
125 if (L'a' <= *Cptr && *Cptr <= L'z') {\r
126 *Cptr = *Cptr - L'a' + L'A';\r
127 }\r
128 }\r
129\r
130 return Str;\r
131}\r
132\r
133/**\r
134 This routine is used to return substring before period '.' or '\0'\r
135 Caller should respsonsible of substr space allocation & free\r
136\r
137 @param[in] Str String to check\r
138 @param[out] SubStr First part of string before period or '\0'\r
139 @param[out] SubStrLen Length of first part of string\r
140\r
141**/\r
142static\r
143VOID\r
144GetSubStringBeforePeriod (\r
145 IN CHAR16 *Str,\r
146 OUT CHAR16 *SubStr,\r
147 OUT UINTN *SubStrLen\r
148 )\r
149{\r
150 UINTN Index;\r
151 for (Index = 0; Str[Index] != L'.' && Str[Index] != L'\0'; Index++) {\r
152 SubStr[Index] = Str[Index];\r
153 }\r
154\r
155 SubStr[Index] = L'\0';\r
156 *SubStrLen = Index;\r
157}\r
158\r
159/**\r
160 This routine pad the string in tail with input character.\r
161\r
162 @param[in] StrBuf Str buffer to be padded, should be enough room for\r
163 @param[in] PadLen Expected padding length\r
164 @param[in] Character Character used to pad\r
165\r
166**/\r
167static\r
168VOID\r
169PadStrInTail (\r
170 IN CHAR16 *StrBuf,\r
171 IN UINTN PadLen,\r
172 IN CHAR16 Character\r
173 )\r
174{\r
175 UINTN Index;\r
176\r
177 for (Index = 0; StrBuf[Index] != L'\0'; Index++);\r
178\r
179 while(PadLen != 0) {\r
180 StrBuf[Index] = Character;\r
181 Index++;\r
182 PadLen--;\r
183 }\r
184\r
185 StrBuf[Index] = L'\0';\r
186}\r
187\r
188/**\r
189 This routine find the offset of the last period '.' of string. If No period exists\r
190 function FileNameExtension is set to L'\0'\r
191\r
192 @param[in] FileName File name to split between last period\r
193 @param[out] FileNameFirst First FileName before last period\r
194 @param[out] FileNameExtension FileName after last period\r
195\r
196**/\r
197static\r
198VOID\r
199SplitFileNameExtension (\r
200 IN CHAR16 *FileName,\r
201 OUT CHAR16 *FileNameFirst,\r
202 OUT CHAR16 *FileNameExtension\r
203 )\r
204{\r
205 UINTN Index;\r
206 UINTN StringLen;\r
207\r
208 StringLen = StrnLenS(FileName, MAX_FILE_NAME_SIZE);\r
209 for (Index = StringLen; Index > 0 && FileName[Index] != L'.'; Index--);\r
210\r
211 //\r
212 // No period exists. No FileName Extension\r
213 //\r
214 if (Index == 0 && FileName[Index] != L'.') {\r
215 FileNameExtension[0] = L'\0';\r
216 Index = StringLen;\r
217 } else {\r
218 StrCpyS(FileNameExtension, MAX_FILE_NAME_SIZE, &FileName[Index+1]);\r
219 }\r
220\r
221 //\r
222 // Copy First file name\r
223 //\r
224 StrnCpyS(FileNameFirst, MAX_FILE_NAME_SIZE, FileName, Index);\r
225 FileNameFirst[Index] = L'\0';\r
226}\r
227\r
228/**\r
229 This routine is called to get all boot options in the order determnined by:\r
230 1. "OptionBuf"\r
231 2. "BootOrder"\r
232\r
233 @param[out] OptionBuf BootList buffer to all boot options returned\r
234 @param[out] OptionCount BootList count of all boot options returned\r
235\r
236 @retval EFI_SUCCESS There is no error when processing capsule\r
237\r
238**/\r
239EFI_STATUS\r
240GetBootOptionInOrder(\r
241 OUT EFI_BOOT_MANAGER_LOAD_OPTION **OptionBuf,\r
242 OUT UINTN *OptionCount\r
243 )\r
244{\r
245 EFI_STATUS Status;\r
246 UINTN DataSize;\r
247 UINT16 BootNext;\r
248 CHAR16 BootOptionName[20];\r
249 EFI_BOOT_MANAGER_LOAD_OPTION *BootOrderOptionBuf;\r
250 UINTN BootOrderCount;\r
251 EFI_BOOT_MANAGER_LOAD_OPTION BootNextOptionEntry;\r
252 UINTN BootNextCount;\r
253 EFI_BOOT_MANAGER_LOAD_OPTION *TempBuf;\r
254\r
255 BootOrderOptionBuf = NULL;\r
256 TempBuf = NULL;\r
257 BootNextCount = 0;\r
258 BootOrderCount = 0;\r
259 *OptionBuf = NULL;\r
260 *OptionCount = 0;\r
261\r
262 //\r
263 // First Get BootOption from "BootNext"\r
264 //\r
265 DataSize = sizeof(BootNext);\r
266 Status = gRT->GetVariable (\r
267 EFI_BOOT_NEXT_VARIABLE_NAME,\r
268 &gEfiGlobalVariableGuid,\r
269 NULL,\r
270 &DataSize,\r
271 (VOID *)&BootNext\r
272 );\r
273 //\r
274 // BootNext variable is a single UINT16\r
275 //\r
276 if (!EFI_ERROR(Status) && DataSize == sizeof(UINT16)) {\r
277 //\r
278 // Add the boot next boot option\r
279 //\r
280 UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", BootNext);\r
281 ZeroMem(&BootNextOptionEntry, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION));\r
282 Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootNextOptionEntry);\r
283\r
284 if (!EFI_ERROR(Status)) {\r
285 BootNextCount = 1;\r
286 }\r
287 }\r
288\r
289 //\r
290 // Second get BootOption from "BootOrder"\r
291 //\r
292 BootOrderOptionBuf = EfiBootManagerGetLoadOptions (&BootOrderCount, LoadOptionTypeBoot);\r
293 if (BootNextCount == 0 && BootOrderCount == 0) {\r
294 return EFI_NOT_FOUND;\r
295 }\r
296\r
297 //\r
298 // At least one BootOption is found\r
299 //\r
300 TempBuf = AllocatePool(sizeof(EFI_BOOT_MANAGER_LOAD_OPTION) * (BootNextCount + BootOrderCount));\r
301 if (TempBuf != NULL) {\r
302 if (BootNextCount == 1) {\r
303 CopyMem(TempBuf, &BootNextOptionEntry, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION));\r
304 }\r
305\r
306 if (BootOrderCount > 0) {\r
307 CopyMem(TempBuf + BootNextCount, BootOrderOptionBuf, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION) * BootOrderCount);\r
308 }\r
309\r
310 *OptionBuf = TempBuf;\r
311 *OptionCount = BootNextCount + BootOrderCount;\r
312 Status = EFI_SUCCESS;\r
313 } else {\r
314 Status = EFI_OUT_OF_RESOURCES;\r
315 }\r
316\r
317 FreePool(BootOrderOptionBuf);\r
318\r
319 return Status;\r
320}\r
321\r
322/**\r
323 This routine is called to get boot option by OptionNumber.\r
324\r
325 @param[in] Number The OptionNumber of boot option\r
326 @param[out] OptionBuf BootList buffer to all boot options returned\r
327\r
328 @retval EFI_SUCCESS There is no error when getting boot option\r
329\r
330**/\r
331EFI_STATUS\r
332GetBootOptionByNumber(\r
333 IN UINT16 Number,\r
334 OUT EFI_BOOT_MANAGER_LOAD_OPTION **OptionBuf\r
335 )\r
336{\r
337 EFI_STATUS Status;\r
338 CHAR16 BootOptionName[20];\r
339 EFI_BOOT_MANAGER_LOAD_OPTION BootOption;\r
340\r
341 UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", Number);\r
342 ZeroMem (&BootOption, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));\r
343 Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootOption);\r
344\r
345 if (!EFI_ERROR (Status)) {\r
346 *OptionBuf = AllocatePool (sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));\r
347 CopyMem (*OptionBuf, &BootOption, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));\r
348 return EFI_SUCCESS;\r
349 }\r
350\r
351 return Status;\r
352}\r
353\r
354/**\r
355 Get Active EFI System Partition within GPT based on device path.\r
356\r
357 @param[in] DevicePath Device path to find a active EFI System Partition\r
358 @param[out] FsHandle BootList points to all boot options returned\r
359\r
360 @retval EFI_SUCCESS Active EFI System Partition is succesfully found\r
361 @retval EFI_NOT_FOUND No Active EFI System Partition is found\r
362\r
363**/\r
364EFI_STATUS\r
365GetEfiSysPartitionFromDevPath(\r
366 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
367 OUT EFI_HANDLE *FsHandle\r
368 )\r
369{\r
370 EFI_STATUS Status;\r
371 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
372 HARDDRIVE_DEVICE_PATH *Hd;\r
373 EFI_HANDLE Handle;\r
374 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
375\r
376 //\r
377 // Check if the device path contains GPT node\r
378 //\r
379 TempDevicePath = DevicePath;\r
380 while (!IsDevicePathEnd (TempDevicePath)) {\r
381 if ((DevicePathType (TempDevicePath) == MEDIA_DEVICE_PATH) &&\r
382 (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP)) {\r
383 Hd = (HARDDRIVE_DEVICE_PATH *)TempDevicePath;\r
384 if (Hd->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER) {\r
385 break;\r
386 }\r
387 }\r
388 TempDevicePath = NextDevicePathNode (TempDevicePath);\r
389 }\r
390\r
391 if (!IsDevicePathEnd (TempDevicePath)) {\r
392 //\r
393 // Search for EFI system partition protocol on full device path in Boot Option\r
394 //\r
395 Status = gBS->LocateDevicePath (&gEfiPartTypeSystemPartGuid, &DevicePath, &Handle);\r
396\r
397 //\r
398 // Search for simple file system on this handler\r
399 //\r
400 if (!EFI_ERROR(Status)) {\r
401 Status = gBS->HandleProtocol(Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);\r
402 if (!EFI_ERROR(Status)) {\r
403 *FsHandle = Handle;\r
404 return EFI_SUCCESS;\r
405 }\r
406 }\r
407 }\r
408\r
409 return EFI_NOT_FOUND;\r
410}\r
411\r
412/**\r
413 This routine is called to get Simple File System protocol on the first EFI system partition found in\r
414 active boot option. The boot option list is detemined in order by\r
415 1. "BootNext"\r
416 2. "BootOrder"\r
417\r
418 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure\r
419 device like USB can get enumerated.\r
420 @param[in, out] LoadOptionNumber On input, specify the boot option to get EFI system partition.\r
421 On output, return the OptionNumber of the boot option where EFI\r
422 system partition is got from.\r
423 @param[out] FsFsHandle Simple File System Protocol found on first active EFI system partition\r
424\r
425 @retval EFI_SUCCESS Simple File System protocol found for EFI system partition\r
426 @retval EFI_NOT_FOUND No Simple File System protocol found for EFI system partition\r
427\r
428**/\r
429EFI_STATUS\r
430GetEfiSysPartitionFromActiveBootOption(\r
431 IN UINTN MaxRetry,\r
432 IN OUT UINT16 **LoadOptionNumber,\r
433 OUT EFI_HANDLE *FsHandle\r
434 )\r
435{\r
436 EFI_STATUS Status;\r
437 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptionBuf;\r
438 UINTN BootOptionNum;\r
439 UINTN Index;\r
440 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
441 EFI_DEVICE_PATH_PROTOCOL *CurFullPath;\r
442 EFI_DEVICE_PATH_PROTOCOL *PreFullPath;\r
443\r
444 *FsHandle = NULL;\r
445\r
446 if (*LoadOptionNumber != NULL) {\r
447 BootOptionNum = 1;\r
448 Status = GetBootOptionByNumber(**LoadOptionNumber, &BootOptionBuf);\r
449 if (EFI_ERROR(Status)) {\r
450 DEBUG ((DEBUG_ERROR, "GetBootOptionByIndex Failed %x! No BootOption available for connection\n", Status));\r
451 return Status;\r
452 }\r
453 } else {\r
454 Status = GetBootOptionInOrder(&BootOptionBuf, &BootOptionNum);\r
455 if (EFI_ERROR(Status)) {\r
456 DEBUG ((DEBUG_ERROR, "GetBootOptionInOrder Failed %x! No BootOption available for connection\n", Status));\r
457 return Status;\r
458 }\r
459 }\r
460\r
461 //\r
462 // Search BootOptionList to check if it is an active boot option with EFI system partition\r
463 // 1. Connect device path\r
464 // 2. expend short/plug in devicepath\r
465 // 3. LoadImage\r
466 //\r
467 for (Index = 0; Index < BootOptionNum; Index++) {\r
468 //\r
469 // Get the boot option from the link list\r
470 //\r
471 DevicePath = BootOptionBuf[Index].FilePath;\r
472\r
473 //\r
474 // Skip inactive or legacy boot options\r
475 //\r
476 if ((BootOptionBuf[Index].Attributes & LOAD_OPTION_ACTIVE) == 0 ||\r
477 DevicePathType (DevicePath) == BBS_DEVICE_PATH) {\r
478 continue;\r
479 }\r
480\r
481 DEBUG_CODE (\r
482 CHAR16 *DevicePathStr;\r
483\r
484 DevicePathStr = ConvertDevicePathToText(DevicePath, TRUE, TRUE);\r
485 if (DevicePathStr != NULL){\r
486 DEBUG((DEBUG_INFO, "Try BootOption %s\n", DevicePathStr));\r
487 FreePool(DevicePathStr);\r
488 } else {\r
489 DEBUG((DEBUG_INFO, "DevicePathToStr failed\n"));\r
490 }\r
491 );\r
492\r
493 CurFullPath = NULL;\r
494 //\r
495 // Try every full device Path generated from bootoption\r
496 //\r
497 do {\r
498 PreFullPath = CurFullPath;\r
499 CurFullPath = EfiBootManagerGetNextLoadOptionDevicePath(DevicePath, CurFullPath);\r
500\r
501 if (PreFullPath != NULL) {\r
502 FreePool (PreFullPath);\r
503 }\r
504\r
505 if (CurFullPath == NULL) {\r
506 //\r
507 // No Active EFI system partition is found in BootOption device path\r
508 //\r
509 Status = EFI_NOT_FOUND;\r
510 break;\r
511 }\r
512\r
513 DEBUG_CODE (\r
514 CHAR16 *DevicePathStr1;\r
515\r
516 DevicePathStr1 = ConvertDevicePathToText(CurFullPath, TRUE, TRUE);\r
517 if (DevicePathStr1 != NULL){\r
518 DEBUG((DEBUG_INFO, "Full device path %s\n", DevicePathStr1));\r
519 FreePool(DevicePathStr1);\r
520 }\r
521 );\r
522\r
523 //\r
524 // Make sure the boot option device path connected.\r
525 // Only handle first device in boot option. Other optional device paths are described as OSV specific\r
526 // FullDevice could contain extra directory & file info. So don't check connection status here.\r
527 //\r
528 EfiBootManagerConnectDevicePath (CurFullPath, NULL);\r
529 Status = GetEfiSysPartitionFromDevPath(CurFullPath, FsHandle);\r
530\r
531 //\r
532 // Some relocation device like USB need more time to get enumerated\r
533 //\r
534 while (EFI_ERROR(Status) && MaxRetry > 0) {\r
535 EfiBootManagerConnectDevicePath(CurFullPath, NULL);\r
536\r
537 //\r
538 // Search for EFI system partition protocol on full device path in Boot Option\r
539 //\r
540 Status = GetEfiSysPartitionFromDevPath(CurFullPath, FsHandle);\r
541 if (!EFI_ERROR(Status)) {\r
542 break;\r
543 }\r
544 DEBUG((DEBUG_ERROR, "GetEfiSysPartitionFromDevPath Loop %x\n", Status));\r
545 //\r
546 // Stall 100ms if connection failed to ensure USB stack is ready\r
547 //\r
548 gBS->Stall(100000);\r
549 MaxRetry --;\r
550 }\r
551 } while(EFI_ERROR(Status));\r
552\r
553 //\r
554 // Find a qualified Simple File System\r
555 //\r
556 if (!EFI_ERROR(Status)) {\r
557 break;\r
558 }\r
559\r
560 }\r
561\r
562 //\r
563 // Return the OptionNumber of the boot option where EFI system partition is got from\r
564 //\r
565 if (*LoadOptionNumber == NULL) {\r
566 *LoadOptionNumber = AllocateCopyPool (sizeof(UINT16), (UINT16 *) &BootOptionBuf[Index].OptionNumber);\r
567 }\r
568\r
569 //\r
570 // No qualified EFI system partition found\r
571 //\r
572 if (*FsHandle == NULL) {\r
573 Status = EFI_NOT_FOUND;\r
574 }\r
575\r
576 DEBUG_CODE (\r
577 CHAR16 *DevicePathStr2;\r
578 if (*FsHandle != NULL) {\r
579 DevicePathStr2 = ConvertDevicePathToText(CurFullPath, TRUE, TRUE);\r
580 if (DevicePathStr2 != NULL){\r
581 DEBUG((DEBUG_INFO, "Found Active EFI System Partion on %s\n", DevicePathStr2));\r
582 FreePool(DevicePathStr2);\r
583 }\r
584 } else {\r
585 DEBUG((DEBUG_INFO, "Failed to found Active EFI System Partion\n"));\r
586 }\r
587 );\r
588\r
589 if (CurFullPath != NULL) {\r
590 FreePool(CurFullPath);\r
591 }\r
592\r
593 //\r
594 // Free BootOption Buffer\r
595 //\r
596 for (Index = 0; Index < BootOptionNum; Index++) {\r
597 if (BootOptionBuf[Index].Description != NULL) {\r
598 FreePool(BootOptionBuf[Index].Description);\r
599 }\r
600\r
601 if (BootOptionBuf[Index].FilePath != NULL) {\r
602 FreePool(BootOptionBuf[Index].FilePath);\r
603 }\r
604\r
605 if (BootOptionBuf[Index].OptionalData != NULL) {\r
606 FreePool(BootOptionBuf[Index].OptionalData);\r
607 }\r
608 }\r
609\r
610 FreePool(BootOptionBuf);\r
611\r
612 return Status;\r
613}\r
614\r
615\r
616/**\r
617 This routine is called to get all file infos with in a given dir & with given file attribute, the file info is listed in\r
618 alphabetical order described in UEFI spec.\r
619\r
620 @param[in] Dir Directory file handler\r
621 @param[in] FileAttr Attribute of file to be red from directory\r
622 @param[out] FileInfoList File images info list red from directory\r
623 @param[out] FileNum File images number red from directory\r
624\r
625 @retval EFI_SUCCESS File FileInfo list in the given\r
626\r
627**/\r
628EFI_STATUS\r
629GetFileInfoListInAlphabetFromDir(\r
630 IN EFI_FILE_HANDLE Dir,\r
631 IN UINT64 FileAttr,\r
632 OUT LIST_ENTRY *FileInfoList,\r
633 OUT UINTN *FileNum\r
634 )\r
635{\r
636 EFI_STATUS Status;\r
637 FILE_INFO_ENTRY *NewFileInfoEntry;\r
638 FILE_INFO_ENTRY *TempFileInfoEntry;\r
639 EFI_FILE_INFO *FileInfo;\r
640 CHAR16 *NewFileName;\r
641 CHAR16 *ListedFileName;\r
642 CHAR16 *NewFileNameExtension;\r
643 CHAR16 *ListedFileNameExtension;\r
644 CHAR16 *TempNewSubStr;\r
645 CHAR16 *TempListedSubStr;\r
646 LIST_ENTRY *Link;\r
647 BOOLEAN NoFile;\r
648 UINTN FileCount;\r
649 UINTN IndexNew;\r
650 UINTN IndexListed;\r
651 UINTN NewSubStrLen;\r
652 UINTN ListedSubStrLen;\r
653 INTN SubStrCmpResult;\r
654\r
655 Status = EFI_SUCCESS;\r
656 NewFileName = NULL;\r
657 ListedFileName = NULL;\r
658 NewFileNameExtension = NULL;\r
659 ListedFileNameExtension = NULL;\r
660 TempNewSubStr = NULL;\r
661 TempListedSubStr = NULL;\r
662 NoFile = FALSE;\r
663 FileCount = 0;\r
664\r
665 InitializeListHead(FileInfoList);\r
666\r
667 TempNewSubStr = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);\r
668 TempListedSubStr = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);\r
669\r
670 if (TempNewSubStr == NULL || TempListedSubStr == NULL ) {\r
671 Status = EFI_OUT_OF_RESOURCES;\r
672 goto EXIT;\r
673 }\r
674\r
675 for ( Status = FileHandleFindFirstFile(Dir, &FileInfo)\r
676 ; !EFI_ERROR(Status) && !NoFile\r
677 ; Status = FileHandleFindNextFile(Dir, FileInfo, &NoFile)\r
678 ){\r
679\r
680 //\r
681 // Skip file with mismatching File attribute\r
682 //\r
683 if ((FileInfo->Attribute & (FileAttr)) == 0) {\r
684 continue;\r
685 }\r
686\r
687 NewFileInfoEntry = NULL;\r
688 NewFileInfoEntry = (FILE_INFO_ENTRY*)AllocateZeroPool(sizeof(FILE_INFO_ENTRY));\r
689 if (NewFileInfoEntry == NULL) {\r
690 Status = EFI_OUT_OF_RESOURCES;\r
691 goto EXIT;\r
692 }\r
693 NewFileInfoEntry->Signature = FILE_INFO_SIGNATURE;\r
694 NewFileInfoEntry->FileInfo = AllocateCopyPool((UINTN) FileInfo->Size, FileInfo);\r
695 if (NewFileInfoEntry->FileInfo == NULL) {\r
696 FreePool(NewFileInfoEntry);\r
697 Status = EFI_OUT_OF_RESOURCES;\r
698 goto EXIT;\r
699 }\r
700\r
701 NewFileInfoEntry->FileNameFirstPart = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);\r
702 if (NewFileInfoEntry->FileNameFirstPart == NULL) {\r
703 FreePool(NewFileInfoEntry->FileInfo);\r
704 FreePool(NewFileInfoEntry);\r
705 Status = EFI_OUT_OF_RESOURCES;\r
706 goto EXIT;\r
707 }\r
708 NewFileInfoEntry->FileNameSecondPart = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);\r
709 if (NewFileInfoEntry->FileNameSecondPart == NULL) {\r
710 FreePool(NewFileInfoEntry->FileInfo);\r
711 FreePool(NewFileInfoEntry->FileNameFirstPart);\r
712 FreePool(NewFileInfoEntry);\r
713 Status = EFI_OUT_OF_RESOURCES;\r
714 goto EXIT;\r
715 }\r
716\r
717 //\r
718 // Splitter the whole New file name into 2 parts between the last period L'.' into NewFileName NewFileExtension\r
719 // If no period in the whole file name. NewFileExtension is set to L'\0'\r
720 //\r
721 NewFileName = NewFileInfoEntry->FileNameFirstPart;\r
722 NewFileNameExtension = NewFileInfoEntry->FileNameSecondPart;\r
723 SplitFileNameExtension(FileInfo->FileName, NewFileName, NewFileNameExtension);\r
724 UpperCaseString(NewFileName);\r
725 UpperCaseString(NewFileNameExtension);\r
726\r
727 //\r
728 // Insert capsule file in alphabetical ordered list\r
729 //\r
730 for (Link = FileInfoList->ForwardLink; Link != FileInfoList; Link = Link->ForwardLink) {\r
731 //\r
732 // Get the FileInfo from the link list\r
733 //\r
734 TempFileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
735 ListedFileName = TempFileInfoEntry->FileNameFirstPart;\r
736 ListedFileNameExtension = TempFileInfoEntry->FileNameSecondPart;\r
737\r
738 //\r
739 // Follow rule in UEFI spec 8.5.5 to compare file name\r
740 //\r
741 IndexListed = 0;\r
742 IndexNew = 0;\r
743 while (TRUE){\r
744 //\r
745 // First compare each substrings in NewFileName & ListedFileName between periods\r
746 //\r
747 GetSubStringBeforePeriod(&NewFileName[IndexNew], TempNewSubStr, &NewSubStrLen);\r
748 GetSubStringBeforePeriod(&ListedFileName[IndexListed], TempListedSubStr, &ListedSubStrLen);\r
749 if (NewSubStrLen > ListedSubStrLen) {\r
750 //\r
751 // Substr in NewFileName is longer. Pad tail with SPACE\r
752 //\r
753 PadStrInTail(TempListedSubStr, NewSubStrLen - ListedSubStrLen, L' ');\r
754 } else if (NewSubStrLen < ListedSubStrLen){\r
755 //\r
756 // Substr in ListedFileName is longer. Pad tail with SPACE\r
757 //\r
758 PadStrInTail(TempNewSubStr, ListedSubStrLen - NewSubStrLen, L' ');\r
759 }\r
760\r
761 SubStrCmpResult = StrnCmp(TempNewSubStr, TempListedSubStr, MAX_FILE_NAME_LEN);\r
762 if (SubStrCmpResult != 0) {\r
763 break;\r
764 }\r
765\r
766 //\r
767 // Move to skip this substring\r
768 //\r
769 IndexNew += NewSubStrLen;\r
770 IndexListed += ListedSubStrLen;\r
771 //\r
772 // Reach File First Name end\r
773 //\r
774 if (NewFileName[IndexNew] == L'\0' || ListedFileName[IndexListed] == L'\0') {\r
775 break;\r
776 }\r
777\r
778 //\r
779 // Skip the period L'.'\r
780 //\r
781 IndexNew++;\r
782 IndexListed++;\r
783 }\r
784\r
785 if (SubStrCmpResult < 0) {\r
786 //\r
787 // NewFileName is smaller. Find the right place to insert New file\r
788 //\r
789 break;\r
790 } else if (SubStrCmpResult == 0) {\r
791 //\r
792 // 2 cases whole NewFileName is smaller than ListedFileName\r
793 // 1. if NewFileName == ListedFileName. Continue to compare FileNameExtension\r
794 // 2. if NewFileName is shorter than ListedFileName\r
795 //\r
796 if (NewFileName[IndexNew] == L'\0') {\r
797 if (ListedFileName[IndexListed] != L'\0' || (StrnCmp(NewFileNameExtension, ListedFileNameExtension, MAX_FILE_NAME_LEN) < 0)) {\r
798 break;\r
799 }\r
800 }\r
801 }\r
802\r
803 //\r
804 // Other case, ListedFileName is smaller. Continue to compare the next file in the list\r
805 //\r
806 }\r
807\r
808 //\r
809 // If Find an entry in the list whose name is bigger than new FileInfo in alphabet order\r
810 // Insert it before this entry\r
811 // else\r
812 // Insert at the tail of this list (Link = FileInfoList)\r
813 //\r
814 InsertTailList(Link, &NewFileInfoEntry->Link);\r
815\r
816 FileCount++;\r
817 }\r
818\r
819 *FileNum = FileCount;\r
820\r
821EXIT:\r
822\r
823 if (TempNewSubStr != NULL) {\r
824 FreePool(TempNewSubStr);\r
825 }\r
826\r
827 if (TempListedSubStr != NULL) {\r
828 FreePool(TempListedSubStr);\r
829 }\r
830\r
831 if (EFI_ERROR(Status)) {\r
832 while(!IsListEmpty(FileInfoList)) {\r
833 Link = FileInfoList->ForwardLink;\r
834 RemoveEntryList(Link);\r
835\r
836 TempFileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
837\r
838 FreePool(TempFileInfoEntry->FileInfo);\r
839 FreePool(TempFileInfoEntry->FileNameFirstPart);\r
840 FreePool(TempFileInfoEntry->FileNameSecondPart);\r
841 FreePool(TempFileInfoEntry);\r
842 }\r
843 *FileNum = 0;\r
844 }\r
845\r
846 return Status;\r
847}\r
848\r
849\r
850/**\r
851 This routine is called to get all qualified image from file from an given directory\r
852 in alphabetic order. All the file image is copied to allocated boottime memory.\r
853 Caller should free these memory\r
854\r
855 @param[in] Dir Directory file handler\r
856 @param[in] FileAttr Attribute of file to be red from directory\r
857 @param[out] FilePtr File images Info buffer red from directory\r
858 @param[out] FileNum File images number red from directory\r
859\r
860 @retval EFI_SUCCESS Succeed to get all capsules in alphabetic order.\r
861\r
862**/\r
863EFI_STATUS\r
864GetFileImageInAlphabetFromDir(\r
865 IN EFI_FILE_HANDLE Dir,\r
866 IN UINT64 FileAttr,\r
867 OUT IMAGE_INFO **FilePtr,\r
868 OUT UINTN *FileNum\r
869 )\r
870{\r
871 EFI_STATUS Status;\r
872 LIST_ENTRY *Link;\r
873 EFI_FILE_HANDLE FileHandle;\r
874 FILE_INFO_ENTRY *FileInfoEntry;\r
875 EFI_FILE_INFO *FileInfo;\r
876 UINTN FileCount;\r
877 IMAGE_INFO *TempFilePtrBuf;\r
878 UINTN Size;\r
879 LIST_ENTRY FileInfoList;\r
880\r
881 FileHandle = NULL;\r
882 FileCount = 0;\r
883 TempFilePtrBuf = NULL;\r
884 *FilePtr = NULL;\r
885\r
886 //\r
887 // Get file list in Dir in alphabetical order\r
888 //\r
889 Status = GetFileInfoListInAlphabetFromDir(\r
890 Dir,\r
891 FileAttr,\r
892 &FileInfoList,\r
893 &FileCount\r
894 );\r
895 if (EFI_ERROR(Status)) {\r
896 DEBUG ((DEBUG_ERROR, "GetFileInfoListInAlphabetFromDir Failed!\n"));\r
897 goto EXIT;\r
898 }\r
899\r
900 if (FileCount == 0) {\r
901 DEBUG ((DEBUG_ERROR, "No file found in Dir!\n"));\r
902 Status = EFI_NOT_FOUND;\r
903 goto EXIT;\r
904 }\r
905\r
906 TempFilePtrBuf = (IMAGE_INFO *)AllocateZeroPool(sizeof(IMAGE_INFO) * FileCount);\r
907 if (TempFilePtrBuf == NULL) {\r
908 Status = EFI_OUT_OF_RESOURCES;\r
909 goto EXIT;\r
910 }\r
911\r
912 //\r
913 // Read all files from FileInfoList to BS memory\r
914 //\r
915 FileCount = 0;\r
916 for (Link = FileInfoList.ForwardLink; Link != &FileInfoList; Link = Link->ForwardLink) {\r
917 //\r
918 // Get FileInfo from the link list\r
919 //\r
920 FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
921 FileInfo = FileInfoEntry->FileInfo;\r
922\r
923 Status = Dir->Open(\r
924 Dir,\r
925 &FileHandle,\r
926 FileInfo->FileName,\r
927 EFI_FILE_MODE_READ,\r
928 0\r
929 );\r
930 if (EFI_ERROR(Status)){\r
931 continue;\r
932 }\r
933\r
934 Size = (UINTN)FileInfo->FileSize;\r
935 TempFilePtrBuf[FileCount].ImageAddress = AllocateZeroPool(Size);\r
936 if (TempFilePtrBuf[FileCount].ImageAddress == NULL) {\r
937 DEBUG((DEBUG_ERROR, "Fail to allocate memory for capsule. Stop processing the rest.\n"));\r
938 break;\r
939 }\r
940\r
941 Status = FileHandle->Read(\r
942 FileHandle,\r
943 &Size,\r
944 TempFilePtrBuf[FileCount].ImageAddress\r
945 );\r
946\r
947 FileHandle->Close(FileHandle);\r
948\r
949 //\r
950 // Skip read error file\r
951 //\r
952 if (EFI_ERROR(Status) || Size != (UINTN)FileInfo->FileSize) {\r
953 //\r
954 // Remove this error file info accordingly\r
955 // & move Link to BackLink\r
956 //\r
957 Link = RemoveEntryList(Link);\r
958 Link = Link->BackLink;\r
959\r
960 FreePool(FileInfoEntry->FileInfo);\r
961 FreePool(FileInfoEntry->FileNameFirstPart);\r
962 FreePool(FileInfoEntry->FileNameSecondPart);\r
963 FreePool(FileInfoEntry);\r
964\r
965 FreePool(TempFilePtrBuf[FileCount].ImageAddress);\r
966 TempFilePtrBuf[FileCount].ImageAddress = NULL;\r
967 TempFilePtrBuf[FileCount].FileInfo = NULL;\r
968\r
969 continue;\r
970 }\r
971 TempFilePtrBuf[FileCount].FileInfo = FileInfo;\r
972 FileCount++;\r
973 }\r
974\r
975 DEBUG_CODE (\r
976 for (Link = FileInfoList.ForwardLink; Link != &FileInfoList; Link = Link->ForwardLink) {\r
977 FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
978 FileInfo = FileInfoEntry->FileInfo;\r
979 DEBUG((DEBUG_INFO, "Successfully read capsule file %s from disk.\n", FileInfo->FileName));\r
980 }\r
981 );\r
982\r
983EXIT:\r
984\r
985 *FilePtr = TempFilePtrBuf;\r
986 *FileNum = FileCount;\r
987\r
988 //\r
989 // FileInfo will be freed by Calller\r
990 //\r
991 while(!IsListEmpty(&FileInfoList)) {\r
992 Link = FileInfoList.ForwardLink;\r
993 RemoveEntryList(Link);\r
994\r
995 FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
996\r
997 FreePool(FileInfoEntry->FileNameFirstPart);\r
998 FreePool(FileInfoEntry->FileNameSecondPart);\r
999 FreePool(FileInfoEntry);\r
1000 }\r
1001\r
1002 return Status;\r
1003}\r
1004\r
1005/**\r
1006 This routine is called to remove all qualified image from file from an given directory.\r
1007\r
1008 @param[in] Dir Directory file handler\r
1009 @param[in] FileAttr Attribute of files to be deleted\r
1010\r
1011 @retval EFI_SUCCESS Succeed to remove all files from an given directory.\r
1012\r
1013**/\r
1014EFI_STATUS\r
1015RemoveFileFromDir(\r
1016 IN EFI_FILE_HANDLE Dir,\r
1017 IN UINT64 FileAttr\r
1018 )\r
1019{\r
1020 EFI_STATUS Status;\r
1021 LIST_ENTRY *Link;\r
1022 LIST_ENTRY FileInfoList;\r
1023 EFI_FILE_HANDLE FileHandle;\r
1024 FILE_INFO_ENTRY *FileInfoEntry;\r
1025 EFI_FILE_INFO *FileInfo;\r
1026 UINTN FileCount;\r
1027\r
1028 FileHandle = NULL;\r
1029\r
1030 //\r
1031 // Get file list in Dir in alphabetical order\r
1032 //\r
1033 Status = GetFileInfoListInAlphabetFromDir(\r
1034 Dir,\r
1035 FileAttr,\r
1036 &FileInfoList,\r
1037 &FileCount\r
1038 );\r
1039 if (EFI_ERROR(Status)) {\r
1040 DEBUG ((DEBUG_ERROR, "GetFileInfoListInAlphabetFromDir Failed!\n"));\r
1041 goto EXIT;\r
1042 }\r
1043\r
1044 if (FileCount == 0) {\r
1045 DEBUG ((DEBUG_ERROR, "No file found in Dir!\n"));\r
1046 Status = EFI_NOT_FOUND;\r
1047 goto EXIT;\r
1048 }\r
1049\r
1050 //\r
1051 // Delete all files with given attribute in Dir\r
1052 //\r
1053 for (Link = FileInfoList.ForwardLink; Link != &(FileInfoList); Link = Link->ForwardLink) {\r
1054 //\r
1055 // Get FileInfo from the link list\r
1056 //\r
1057 FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
1058 FileInfo = FileInfoEntry->FileInfo;\r
1059\r
1060 Status = Dir->Open(\r
1061 Dir,\r
1062 &FileHandle,\r
1063 FileInfo->FileName,\r
1064 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,\r
1065 0\r
1066 );\r
1067 if (EFI_ERROR(Status)){\r
1068 continue;\r
1069 }\r
1070\r
1071 Status = FileHandle->Delete(FileHandle);\r
1072 }\r
1073\r
1074EXIT:\r
1075\r
1076 while(!IsListEmpty(&FileInfoList)) {\r
1077 Link = FileInfoList.ForwardLink;\r
1078 RemoveEntryList(Link);\r
1079\r
1080 FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
1081\r
1082 FreePool(FileInfoEntry->FileInfo);\r
1083 FreePool(FileInfoEntry);\r
1084 }\r
1085\r
1086 return Status;\r
1087}\r
1088\r
1089/**\r
1090 This routine is called to get all caspules from file. The capsule file image is\r
1091 copied to BS memory. Caller is responsible to free them.\r
1092\r
1093 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure\r
1094 devices like USB can get enumerated.\r
1095 @param[out] CapsulePtr Copied Capsule file Image Info buffer\r
1096 @param[out] CapsuleNum CapsuleNumber\r
1097 @param[out] FsHandle File system handle\r
1098 @param[out] LoadOptionNumber OptionNumber of boot option\r
1099\r
1100 @retval EFI_SUCCESS Succeed to get all capsules.\r
1101\r
1102**/\r
1103EFI_STATUS\r
1104GetAllCapsuleOnDisk(\r
1105 IN UINTN MaxRetry,\r
1106 OUT IMAGE_INFO **CapsulePtr,\r
1107 OUT UINTN *CapsuleNum,\r
1108 OUT EFI_HANDLE *FsHandle,\r
1109 OUT UINT16 *LoadOptionNumber\r
1110 )\r
1111{\r
1112 EFI_STATUS Status;\r
1113 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
1114 EFI_FILE_HANDLE RootDir;\r
1115 EFI_FILE_HANDLE FileDir;\r
1116 UINT16 *TempOptionNumber;\r
1117\r
1118 Fs = NULL;\r
1119 RootDir = NULL;\r
1120 FileDir = NULL;\r
1121 TempOptionNumber = NULL;\r
1122 *CapsuleNum = 0;\r
1123\r
1124 Status = GetEfiSysPartitionFromActiveBootOption(MaxRetry, &TempOptionNumber, FsHandle);\r
1125 if (EFI_ERROR(Status)) {\r
1126 return Status;\r
1127 }\r
1128\r
1129 Status = gBS->HandleProtocol(*FsHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);\r
1130 if (EFI_ERROR(Status)) {\r
1131 return Status;\r
1132 }\r
1133\r
1134 Status = Fs->OpenVolume(Fs, &RootDir);\r
1135 if (EFI_ERROR(Status)) {\r
1136 return Status;\r
1137 }\r
1138\r
1139 Status = RootDir->Open(\r
1140 RootDir,\r
1141 &FileDir,\r
1142 EFI_CAPSULE_FILE_DIRECTORY,\r
1143 EFI_FILE_MODE_READ,\r
1144 0\r
1145 );\r
1146 if (EFI_ERROR(Status)) {\r
1147 DEBUG((DEBUG_ERROR, "CodLibGetAllCapsuleOnDisk fail to open RootDir!\n"));\r
1148 goto EXIT;\r
1149 }\r
1150\r
1151 //\r
1152 // Only Load files with EFI_FILE_SYSTEM or EFI_FILE_ARCHIVE attribute\r
1153 // ignore EFI_FILE_READ_ONLY, EFI_FILE_HIDDEN, EFI_FILE_RESERVED, EFI_FILE_DIRECTORY\r
1154 //\r
1155 Status = GetFileImageInAlphabetFromDir(\r
1156 FileDir,\r
1157 EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE,\r
1158 CapsulePtr,\r
1159 CapsuleNum\r
1160 );\r
1161 DEBUG((DEBUG_INFO, "GetFileImageInAlphabetFromDir status %x\n", Status));\r
1162\r
1163 //\r
1164 // Always remove file to avoid deadloop in capsule process\r
1165 //\r
1166 Status = RemoveFileFromDir(FileDir, EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE);\r
1167 DEBUG((DEBUG_INFO, "RemoveFileFromDir status %x\n", Status));\r
1168\r
1169 if (LoadOptionNumber != NULL) {\r
1170 *LoadOptionNumber = *TempOptionNumber;\r
1171 }\r
1172\r
1173EXIT:\r
1174\r
1175 if (FileDir != NULL) {\r
1176 FileDir->Close (FileDir);\r
1177 }\r
1178\r
1179 if (RootDir != NULL) {\r
1180 RootDir->Close (RootDir);\r
1181 }\r
1182\r
1183 return Status;\r
1184}\r
1185\r
1186/**\r
1187 Build Gather list for a list of capsule images.\r
1188\r
1189 @param[in] CapsuleBuffer An array of pointer to capsule images\r
1190 @param[in] CapsuleSize An array of UINTN to capsule images size\r
1191 @param[in] CapsuleNum The count of capsule images\r
1192 @param[out] BlockDescriptors The block descriptors for the capsule images\r
1193\r
1194 @retval EFI_SUCCESS The block descriptors for the capsule images are constructed.\r
1195\r
1196**/\r
1197EFI_STATUS\r
1198BuildGatherList (\r
1199 IN VOID **CapsuleBuffer,\r
1200 IN UINTN *CapsuleSize,\r
1201 IN UINTN CapsuleNum,\r
1202 OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockDescriptors\r
1203 )\r
1204{\r
1205 EFI_STATUS Status;\r
1206 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors1;\r
1207 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorPre;\r
1208 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorsHeader;\r
1209 UINTN Index;\r
1210\r
1211 BlockDescriptors1 = NULL;\r
1212 BlockDescriptorPre = NULL;\r
1213 BlockDescriptorsHeader = NULL;\r
1214\r
1215 for (Index = 0; Index < CapsuleNum; Index++) {\r
1216 //\r
1217 // Allocate memory for the descriptors.\r
1218 //\r
1219 BlockDescriptors1 = AllocateZeroPool (2 * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR));\r
1220 if (BlockDescriptors1 == NULL) {\r
1221 DEBUG ((DEBUG_ERROR, "BuildGatherList: failed to allocate memory for descriptors\n"));\r
1222 Status = EFI_OUT_OF_RESOURCES;\r
1223 goto ERREXIT;\r
1224 } else {\r
1225 DEBUG ((DEBUG_INFO, "BuildGatherList: creating capsule descriptors at 0x%X\n", (UINTN) BlockDescriptors1));\r
1226 }\r
1227\r
1228 //\r
1229 // Record descirptor header\r
1230 //\r
1231 if (Index == 0) {\r
1232 BlockDescriptorsHeader = BlockDescriptors1;\r
1233 }\r
1234\r
1235 if (BlockDescriptorPre != NULL) {\r
1236 BlockDescriptorPre->Union.ContinuationPointer = (UINTN) BlockDescriptors1;\r
1237 BlockDescriptorPre->Length = 0;\r
1238 }\r
1239\r
1240 BlockDescriptors1->Union.DataBlock = (UINTN) CapsuleBuffer[Index];\r
1241 BlockDescriptors1->Length = CapsuleSize[Index];\r
1242\r
1243 BlockDescriptorPre = BlockDescriptors1 + 1;\r
1244 BlockDescriptors1 = NULL;\r
1245 }\r
1246\r
1247 //\r
1248 // Null-terminate.\r
1249 //\r
1250 if (BlockDescriptorPre != NULL) {\r
1251 BlockDescriptorPre->Union.ContinuationPointer = (UINTN)NULL;\r
1252 BlockDescriptorPre->Length = 0;\r
1253 *BlockDescriptors = BlockDescriptorsHeader;\r
1254 }\r
1255\r
1256 return EFI_SUCCESS;\r
1257\r
1258ERREXIT:\r
1259 if (BlockDescriptors1 != NULL) {\r
1260 FreePool (BlockDescriptors1);\r
1261 }\r
1262\r
1263 return Status;\r
1264}\r
1265\r
1266/**\r
1267 This routine is called to check if CapsuleOnDisk flag in OsIndications Variable\r
1268 is enabled.\r
1269\r
1270 @retval TRUE Flag is enabled\r
1271 @retval FALSE Flag is not enabled\r
1272\r
1273**/\r
1274BOOLEAN\r
1275EFIAPI\r
1276CoDCheckCapsuleOnDiskFlag(\r
1277 VOID\r
1278 )\r
1279{\r
1280 EFI_STATUS Status;\r
1281 UINT64 OsIndication;\r
1282 UINTN DataSize;\r
1283\r
1284 //\r
1285 // Check File Capsule Delivery Supported Flag in OsIndication variable\r
1286 //\r
1287 OsIndication = 0;\r
1288 DataSize = sizeof(UINT64);\r
1289 Status = gRT->GetVariable (\r
1290 EFI_OS_INDICATIONS_VARIABLE_NAME,\r
1291 &gEfiGlobalVariableGuid,\r
1292 NULL,\r
1293 &DataSize,\r
1294 &OsIndication\r
1295 );\r
1296 if (!EFI_ERROR(Status) &&\r
1297 (OsIndication & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) != 0) {\r
1298 return TRUE;\r
1299 }\r
1300\r
1301 return FALSE;\r
1302}\r
1303\r
1304\r
1305/**\r
1306 This routine is called to clear CapsuleOnDisk flags including OsIndications and BootNext variable.\r
1307\r
1308 @retval EFI_SUCCESS All Capsule On Disk flags are cleared\r
1309\r
1310**/\r
1311EFI_STATUS\r
1312EFIAPI\r
1313CoDClearCapsuleOnDiskFlag(\r
1314 VOID\r
1315 )\r
1316{\r
1317 EFI_STATUS Status;\r
1318 UINT64 OsIndication;\r
1319 UINTN DataSize;\r
1320\r
1321 //\r
1322 // Reset File Capsule Delivery Supported Flag in OsIndication variable\r
1323 //\r
1324 OsIndication = 0;\r
1325 DataSize = sizeof(UINT64);\r
1326 Status = gRT->GetVariable (\r
1327 EFI_OS_INDICATIONS_VARIABLE_NAME,\r
1328 &gEfiGlobalVariableGuid,\r
1329 NULL,\r
1330 &DataSize,\r
1331 &OsIndication\r
1332 );\r
1333 if (EFI_ERROR(Status) ||\r
1334 (OsIndication & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) == 0) {\r
1335 return Status;\r
1336 }\r
1337\r
1338 OsIndication &= ~((UINT64)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED);\r
1339 Status = gRT->SetVariable (\r
1340 EFI_OS_INDICATIONS_VARIABLE_NAME,\r
1341 &gEfiGlobalVariableGuid,\r
1342 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
1343 sizeof(UINT64),\r
1344 &OsIndication\r
1345 );\r
1346 ASSERT(!EFI_ERROR(Status));\r
1347\r
1348 //\r
1349 // Delete BootNext variable. Capsule Process may reset system, so can't rely on Bds to clear this variable\r
1350 //\r
1351 Status = gRT->SetVariable (\r
1352 EFI_BOOT_NEXT_VARIABLE_NAME,\r
1353 &gEfiGlobalVariableGuid,\r
1354 0,\r
1355 0,\r
1356 NULL\r
1357 );\r
1358 ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);\r
1359\r
1360 return EFI_SUCCESS;\r
1361}\r
1362\r
1363/**\r
1364 This routine is called to clear CapsuleOnDisk Relocation Info variable.\r
1365 Total Capsule On Disk length is recorded in this variable\r
1366\r
1367 @retval EFI_SUCCESS Capsule On Disk flags are cleared\r
1368\r
1369**/\r
1370EFI_STATUS\r
1371CoDClearCapsuleRelocationInfo(\r
1372 VOID\r
1373 )\r
1374{\r
1375 return gRT->SetVariable (\r
1376 COD_RELOCATION_INFO_VAR_NAME,\r
1377 &gEfiCapsuleVendorGuid,\r
1378 0,\r
1379 0,\r
1380 NULL\r
1381 );\r
1382}\r
1383\r
1384/**\r
1385 Relocate Capsule on Disk from EFI system partition to a platform-specific NV storage device\r
1386 with BlockIo protocol. Relocation device path, identified by PcdCodRelocationDevPath, must\r
1387 be a full device path.\r
1388 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
1389 Function will stall 100ms between each retry.\r
1390\r
1391 Side Effects:\r
1392 Content corruption. Block IO write directly touches low level write. Orignal partitions, file systems\r
1393 of the relocation device will be corrupted.\r
1394\r
1395 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure\r
1396 devices like USB can get enumerated.\r
1397\r
1398 @retval EFI_SUCCESS Capsule on Disk images are sucessfully relocated to the platform-specific device.\r
1399\r
1400**/\r
1401EFI_STATUS\r
1402RelocateCapsuleToDisk(\r
1403 UINTN MaxRetry\r
1404 )\r
1405{\r
1406 EFI_STATUS Status;\r
1407 UINTN CapsuleOnDiskNum;\r
1408 UINTN Index;\r
1409 UINTN DataSize;\r
1410 UINT64 TotalImageSize;\r
1411 UINT64 TotalImageNameSize;\r
1412 IMAGE_INFO *CapsuleOnDiskBuf;\r
1413 EFI_HANDLE Handle;\r
1414 EFI_HANDLE TempHandle;\r
1415 EFI_HANDLE *HandleBuffer;\r
1416 UINTN NumberOfHandles;\r
1417 EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
1418 UINT8 *CapsuleDataBuf;\r
1419 UINT8 *CapsulePtr;\r
1420 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
1421 EFI_FILE_HANDLE RootDir;\r
1422 EFI_FILE_HANDLE TempCodFile;\r
1423 UINT64 TempCodFileSize;\r
1424 EFI_DEVICE_PATH *TempDevicePath;\r
1425 BOOLEAN RelocationInfo;\r
1426 UINT16 LoadOptionNumber;\r
1427 EFI_CAPSULE_HEADER FileNameCapsuleHeader;\r
1428\r
1429 RootDir = NULL;\r
1430 TempCodFile = NULL;\r
1431 HandleBuffer = NULL;\r
1432 CapsuleDataBuf = NULL;\r
1433 CapsuleOnDiskBuf = NULL;\r
1434 NumberOfHandles = 0;\r
1435\r
1436 DEBUG ((DEBUG_INFO, "CapsuleOnDisk RelocateCapsule Enter\n"));\r
1437\r
1438 //\r
1439 // 1. Load all Capsule On Disks in to memory\r
1440 //\r
1441 Status = GetAllCapsuleOnDisk(MaxRetry, &CapsuleOnDiskBuf, &CapsuleOnDiskNum, &Handle, &LoadOptionNumber);\r
1442 if (EFI_ERROR(Status) || CapsuleOnDiskNum == 0) {\r
1443 DEBUG ((DEBUG_INFO, "RelocateCapsule: GetAllCapsuleOnDisk Status - 0x%x\n", Status));\r
1444 return EFI_NOT_FOUND;\r
1445 }\r
1446\r
1447 //\r
1448 // 2. Connect platform special device path as relocation device.\r
1449 // If no platform special device path specified or the device path is invalid, use the EFI system partition where\r
1450 // stores the capsules as relocation device.\r
1451 //\r
1452 if (IsDevicePathValid ((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), PcdGetSize(PcdCodRelocationDevPath))) {\r
1453 Status = EfiBootManagerConnectDevicePath ((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), &TempHandle);\r
1454 if (EFI_ERROR(Status)) {\r
1455 DEBUG ((DEBUG_ERROR, "RelocateCapsule: EfiBootManagerConnectDevicePath Status - 0x%x\n", Status));\r
1456 goto EXIT;\r
1457 }\r
1458\r
1459 //\r
1460 // Connect all the child handle. Partition & FAT drivers are allowed in this case\r
1461 //\r
1462 gBS->ConnectController (TempHandle, NULL, NULL, TRUE);\r
1463 Status = gBS->LocateHandleBuffer(\r
1464 ByProtocol,\r
1465 &gEfiSimpleFileSystemProtocolGuid,\r
1466 NULL,\r
1467 &NumberOfHandles,\r
1468 &HandleBuffer\r
1469 );\r
1470 if (EFI_ERROR(Status)) {\r
1471 DEBUG ((DEBUG_ERROR, "RelocateCapsule: LocateHandleBuffer Status - 0x%x\n", Status));\r
1472 goto EXIT;\r
1473 }\r
1474\r
1475 //\r
1476 // Find first Simple File System Handle which can match PcdCodRelocationDevPath\r
1477 //\r
1478 for (Index = 0; Index < NumberOfHandles; Index++) {\r
1479 Status = gBS->HandleProtocol(HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&TempDevicePath);\r
1480 if (EFI_ERROR(Status)) {\r
1481 continue;\r
1482 }\r
1483\r
1484 DataSize = GetDevicePathSize((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath)) - sizeof(EFI_DEVICE_PATH);\r
1485 if (0 == CompareMem((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), TempDevicePath, DataSize)) {\r
1486 Handle = HandleBuffer[Index];\r
1487 break;\r
1488 }\r
1489 }\r
1490\r
1491 FreePool(HandleBuffer);\r
1492\r
1493 if (Index == NumberOfHandles) {\r
1494 DEBUG ((DEBUG_ERROR, "RelocateCapsule: No simple file system protocol found.\n"));\r
1495 Status = EFI_NOT_FOUND;\r
1496 }\r
1497 }\r
1498\r
1499 Status = gBS->HandleProtocol(Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);\r
1500 if (EFI_ERROR(Status) || BlockIo->Media->ReadOnly) {\r
1501 DEBUG((DEBUG_ERROR, "Fail to find Capsule on Disk relocation BlockIo device or device is ReadOnly!\n"));\r
1502 goto EXIT;\r
1503 }\r
1504\r
1505 Status = gBS->HandleProtocol(Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);\r
1506 if (EFI_ERROR(Status)) {\r
1507 goto EXIT;\r
1508 }\r
1509\r
1510 //\r
1511 // Check if device used to relocate Capsule On Disk is big enough\r
1512 //\r
1513 TotalImageSize = 0;\r
1514 TotalImageNameSize = 0;\r
1515 for (Index = 0; Index < CapsuleOnDiskNum; Index++) {\r
1516 //\r
1517 // Overflow check\r
1518 //\r
1519 if (MAX_ADDRESS - (UINTN)TotalImageSize <= CapsuleOnDiskBuf[Index].FileInfo->FileSize) {\r
1520 Status = EFI_INVALID_PARAMETER;\r
1521 goto EXIT;\r
1522 }\r
1523\r
1524 if (MAX_ADDRESS - (UINTN)TotalImageNameSize <= StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName)) {\r
1525 Status = EFI_INVALID_PARAMETER;\r
1526 goto EXIT;\r
1527 }\r
1528\r
1529 TotalImageSize += CapsuleOnDiskBuf[Index].FileInfo->FileSize;\r
1530 TotalImageNameSize += StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName);\r
1531 DEBUG((DEBUG_INFO, "RelocateCapsule: %x Size %x\n",CapsuleOnDiskBuf[Index].FileInfo->FileName, CapsuleOnDiskBuf[Index].FileInfo->FileSize));\r
1532 }\r
1533\r
1534 DEBUG((DEBUG_INFO, "RelocateCapsule: TotalImageSize %x\n", TotalImageSize));\r
1535 DEBUG((DEBUG_INFO, "RelocateCapsule: TotalImageNameSize %x\n", TotalImageNameSize));\r
1536\r
1537 if (MAX_ADDRESS - (UINTN)TotalImageNameSize <= sizeof(UINT64) * 2 ||\r
1538 MAX_ADDRESS - (UINTN)TotalImageSize <= (UINTN)TotalImageNameSize + sizeof(UINT64) * 2) {\r
1539 Status = EFI_INVALID_PARAMETER;\r
1540 goto EXIT;\r
1541 }\r
1542\r
1543 TempCodFileSize = sizeof(UINT64) + TotalImageSize + sizeof(EFI_CAPSULE_HEADER) + TotalImageNameSize;\r
1544\r
1545 //\r
1546 // Check if CapsuleTotalSize. There could be reminder, so use LastBlock number directly\r
1547 //\r
1548 if (DivU64x32(TempCodFileSize, BlockIo->Media->BlockSize) > BlockIo->Media->LastBlock) {\r
1549 DEBUG((DEBUG_ERROR, "RelocateCapsule: Relocation device isn't big enough to hold all Capsule on Disk!\n"));\r
1550 DEBUG((DEBUG_ERROR, "TotalImageSize = %x\n", TotalImageSize));\r
1551 DEBUG((DEBUG_ERROR, "TotalImageNameSize = %x\n", TotalImageNameSize));\r
1552 DEBUG((DEBUG_ERROR, "RelocationDev BlockSize = %x LastBlock = %x\n", BlockIo->Media->BlockSize, BlockIo->Media->LastBlock));\r
1553 Status = EFI_OUT_OF_RESOURCES;\r
1554 goto EXIT;\r
1555 }\r
1556\r
1557 CapsuleDataBuf = AllocatePool((UINTN) TempCodFileSize);\r
1558 if (CapsuleDataBuf == NULL) {\r
1559 Status = EFI_OUT_OF_RESOURCES;\r
1560 goto EXIT;\r
1561 }\r
1562\r
1563 //\r
1564 // First UINT64 reserved for total image size, including capsule name capsule.\r
1565 //\r
1566 *(UINT64 *) CapsuleDataBuf = TotalImageSize + sizeof(EFI_CAPSULE_HEADER) + TotalImageNameSize;\r
1567\r
1568 //\r
1569 // Line up all the Capsule on Disk and write to relocation disk at one time. It could save some time in disk write\r
1570 //\r
1571 for (Index = 0, CapsulePtr = CapsuleDataBuf + sizeof(UINT64); Index < CapsuleOnDiskNum; Index++) {\r
1572 CopyMem(CapsulePtr, CapsuleOnDiskBuf[Index].ImageAddress, (UINTN) CapsuleOnDiskBuf[Index].FileInfo->FileSize);\r
1573 CapsulePtr += CapsuleOnDiskBuf[Index].FileInfo->FileSize;\r
1574 }\r
1575\r
1576 //\r
1577 // Line the capsule header for capsule name capsule.\r
1578 //\r
1579 CopyGuid(&FileNameCapsuleHeader.CapsuleGuid, &gEdkiiCapsuleOnDiskNameGuid);\r
1580 FileNameCapsuleHeader.CapsuleImageSize = (UINT32) TotalImageNameSize + sizeof(EFI_CAPSULE_HEADER);\r
1581 FileNameCapsuleHeader.Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;\r
1582 FileNameCapsuleHeader.HeaderSize = sizeof(EFI_CAPSULE_HEADER);\r
1583 CopyMem(CapsulePtr, &FileNameCapsuleHeader, FileNameCapsuleHeader.HeaderSize);\r
1584 CapsulePtr += FileNameCapsuleHeader.HeaderSize;\r
1585\r
1586 //\r
1587 // Line up all the Capsule file names.\r
1588 //\r
1589 for (Index = 0; Index < CapsuleOnDiskNum; Index++) {\r
1590 CopyMem(CapsulePtr, CapsuleOnDiskBuf[Index].FileInfo->FileName, StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName));\r
1591 CapsulePtr += StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName);\r
1592 }\r
1593\r
1594 //\r
1595 // 5. Flash all Capsules on Disk to TempCoD.tmp under RootDir\r
1596 //\r
1597 Status = Fs->OpenVolume(Fs, &RootDir);\r
1598 if (EFI_ERROR(Status)) {\r
1599 DEBUG((DEBUG_ERROR, "RelocateCapsule: OpenVolume error. %x\n", Status));\r
1600 goto EXIT;\r
1601 }\r
1602\r
1603 Status = RootDir->Open(\r
1604 RootDir,\r
1605 &TempCodFile,\r
1606 (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),\r
1607 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,\r
1608 0\r
1609 );\r
1610 if (!EFI_ERROR(Status)) {\r
1611 //\r
1612 // Error handling code to prevent malicious code to hold this file to block capsule on disk\r
1613 //\r
1614 TempCodFile->Delete(TempCodFile);\r
1615 }\r
1616 Status = RootDir->Open(\r
1617 RootDir,\r
1618 &TempCodFile,\r
1619 (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),\r
1620 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE,\r
1621 0\r
1622 );\r
1623 if (EFI_ERROR(Status)) {\r
1624 DEBUG((DEBUG_ERROR, "RelocateCapsule: Open TemCoD.tmp error. %x\n", Status));\r
1625 goto EXIT;\r
1626 }\r
1627\r
1628 //\r
1629 // Always write at the begining of TempCap file\r
1630 //\r
1631 DataSize = (UINTN) TempCodFileSize;\r
1632 Status = TempCodFile->Write(\r
1633 TempCodFile,\r
1634 &DataSize,\r
1635 CapsuleDataBuf\r
1636 );\r
1637 if (EFI_ERROR(Status)) {\r
1638 DEBUG((DEBUG_ERROR, "RelocateCapsule: Write TemCoD.tmp error. %x\n", Status));\r
1639 goto EXIT;\r
1640 }\r
1641\r
1642 if (DataSize != TempCodFileSize) {\r
1643 Status = EFI_DEVICE_ERROR;\r
1644 goto EXIT;\r
1645 }\r
1646\r
1647 //\r
1648 // Save Capsule On Disk relocation info to "CodRelocationInfo" Var\r
1649 // It is used in next reboot by TCB\r
1650 //\r
1651 RelocationInfo = TRUE;\r
1652 Status = gRT->SetVariable(\r
1653 COD_RELOCATION_INFO_VAR_NAME,\r
1654 &gEfiCapsuleVendorGuid,\r
1655 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
1656 sizeof (BOOLEAN),\r
1657 &RelocationInfo\r
1658 );\r
1659 //\r
1660 // Save the LoadOptionNumber of the boot option, where the capsule is relocated,\r
1661 // into "CodRelocationLoadOption" var. It is used in next reboot after capsule is\r
1662 // updated out of TCB to remove the TempCoDFile.\r
1663 //\r
1664 Status = gRT->SetVariable(\r
1665 COD_RELOCATION_LOAD_OPTION_VAR_NAME,\r
1666 &gEfiCapsuleVendorGuid,\r
1667 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
1668 sizeof (UINT16),\r
1669 &LoadOptionNumber\r
1670 );\r
1671\r
1672EXIT:\r
1673\r
1674 if (CapsuleDataBuf != NULL) {\r
1675 FreePool(CapsuleDataBuf);\r
1676 }\r
1677\r
1678 if (CapsuleOnDiskBuf != NULL) {\r
1679 //\r
1680 // Free resources allocated by CodLibGetAllCapsuleOnDisk\r
1681 //\r
1682 for (Index = 0; Index < CapsuleOnDiskNum; Index++ ) {\r
1683 FreePool(CapsuleOnDiskBuf[Index].ImageAddress);\r
1684 FreePool(CapsuleOnDiskBuf[Index].FileInfo);\r
1685 }\r
1686 FreePool(CapsuleOnDiskBuf);\r
1687 }\r
1688\r
1689 if (TempCodFile != NULL) {\r
1690 if (EFI_ERROR(Status)) {\r
1691 TempCodFile->Delete (TempCodFile);\r
1692 } else {\r
1693 TempCodFile->Close (TempCodFile);\r
1694 }\r
1695 }\r
1696\r
1697 if (RootDir != NULL) {\r
1698 RootDir->Close (RootDir);\r
1699 }\r
1700\r
1701 return Status;\r
1702}\r
1703\r
1704/**\r
1705 For the platforms that support Capsule In Ram, reuse the Capsule In Ram to deliver capsule.\r
1706 Relocate Capsule On Disk to memory and call UpdateCapsule().\r
1707 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
1708 Function will stall 100ms between each retry.\r
1709\r
1710 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure\r
1711 devices like USB can get enumerated.\r
1712\r
1713 @retval EFI_SUCCESS Deliver capsule through Capsule In Ram successfully.\r
1714\r
1715**/\r
1716EFI_STATUS\r
1717RelocateCapsuleToRam (\r
1718 UINTN MaxRetry\r
1719 )\r
1720{\r
1721 EFI_STATUS Status;\r
1722 UINTN CapsuleOnDiskNum;\r
1723 IMAGE_INFO *CapsuleOnDiskBuf;\r
1724 EFI_HANDLE Handle;\r
1725 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors;\r
1726 VOID **CapsuleBuffer;\r
1727 UINTN *CapsuleSize;\r
1728 EFI_CAPSULE_HEADER *FileNameCapsule;\r
1729 UINTN Index;\r
1730 UINT8 *StringBuf;\r
1731 UINTN StringSize;\r
1732 UINTN TotalStringSize;\r
1733\r
1734 CapsuleOnDiskBuf = NULL;\r
1735 BlockDescriptors = NULL;\r
1736 CapsuleBuffer = NULL;\r
1737 CapsuleSize = NULL;\r
1738 FileNameCapsule = NULL;\r
1739 TotalStringSize = 0;\r
1740\r
1741 //\r
1742 // 1. Load all Capsule On Disks into memory\r
1743 //\r
1744 Status = GetAllCapsuleOnDisk (MaxRetry, &CapsuleOnDiskBuf, &CapsuleOnDiskNum, &Handle, NULL);\r
1745 if (EFI_ERROR (Status) || CapsuleOnDiskNum == 0) {\r
1746 DEBUG ((DEBUG_ERROR, "GetAllCapsuleOnDisk Status - 0x%x\n", Status));\r
1747 return EFI_NOT_FOUND;\r
1748 }\r
1749\r
1750 //\r
1751 // 2. Add a capsule for Capsule file name strings\r
1752 //\r
1753 CapsuleBuffer = AllocateZeroPool ((CapsuleOnDiskNum + 1) * sizeof (VOID *));\r
1754 if (CapsuleBuffer == NULL) {\r
1755 DEBUG ((DEBUG_ERROR, "Fail to allocate memory for capsules.\n"));\r
1756 return EFI_OUT_OF_RESOURCES;\r
1757 }\r
1758\r
1759 CapsuleSize = AllocateZeroPool ((CapsuleOnDiskNum + 1) * sizeof (UINTN));\r
1760 if (CapsuleSize == NULL) {\r
1761 DEBUG ((DEBUG_ERROR, "Fail to allocate memory for capsules.\n"));\r
1762 FreePool (CapsuleBuffer);\r
1763 return EFI_OUT_OF_RESOURCES;\r
1764 }\r
1765\r
1766 for (Index = 0; Index < CapsuleOnDiskNum; Index++) {\r
1767 CapsuleBuffer[Index] = (VOID *)(UINTN) CapsuleOnDiskBuf[Index].ImageAddress;\r
1768 CapsuleSize[Index] = (UINTN) CapsuleOnDiskBuf[Index].FileInfo->FileSize;\r
1769 TotalStringSize += StrSize (CapsuleOnDiskBuf[Index].FileInfo->FileName);\r
1770 }\r
1771\r
1772 FileNameCapsule = AllocateZeroPool (sizeof (EFI_CAPSULE_HEADER) + TotalStringSize);\r
1773 if (FileNameCapsule == NULL) {\r
1774 DEBUG ((DEBUG_ERROR, "Fail to allocate memory for name capsule.\n"));\r
1775 FreePool (CapsuleBuffer);\r
1776 FreePool (CapsuleSize);\r
1777 return EFI_OUT_OF_RESOURCES;\r
1778 }\r
1779\r
1780 FileNameCapsule->CapsuleImageSize = (UINT32) (sizeof (EFI_CAPSULE_HEADER) + TotalStringSize);\r
1781 FileNameCapsule->Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;\r
1782 FileNameCapsule->HeaderSize = sizeof (EFI_CAPSULE_HEADER);\r
1783 CopyGuid (&(FileNameCapsule->CapsuleGuid), &gEdkiiCapsuleOnDiskNameGuid);\r
1784\r
1785 StringBuf = (UINT8 *)FileNameCapsule + FileNameCapsule->HeaderSize;\r
1786 for (Index = 0; Index < CapsuleOnDiskNum; Index ++) {\r
1787 StringSize = StrSize (CapsuleOnDiskBuf[Index].FileInfo->FileName);\r
1788 CopyMem (StringBuf, CapsuleOnDiskBuf[Index].FileInfo->FileName, StringSize);\r
1789 StringBuf += StringSize;\r
1790 }\r
1791\r
1792 CapsuleBuffer[CapsuleOnDiskNum] = FileNameCapsule;\r
1793 CapsuleSize[CapsuleOnDiskNum] = TotalStringSize + sizeof (EFI_CAPSULE_HEADER);\r
1794\r
1795 //\r
1796 // 3. Build Gather list for the capsules\r
1797 //\r
1798 Status = BuildGatherList (CapsuleBuffer, CapsuleSize, CapsuleOnDiskNum + 1, &BlockDescriptors);\r
1799 if (EFI_ERROR (Status) || BlockDescriptors == NULL) {\r
1800 FreePool (CapsuleBuffer);\r
1801 FreePool (CapsuleSize);\r
1802 FreePool (FileNameCapsule);\r
1803 return EFI_OUT_OF_RESOURCES;\r
1804 }\r
1805\r
1806 //\r
1807 // 4. Call UpdateCapsule() service\r
1808 //\r
1809 Status = gRT->UpdateCapsule((EFI_CAPSULE_HEADER **) CapsuleBuffer, CapsuleOnDiskNum + 1, (UINTN) BlockDescriptors);\r
1810\r
1811 return Status;\r
1812}\r
1813\r
1814/**\r
1815 Relocate Capsule on Disk from EFI system partition.\r
1816\r
1817 Two solution to deliver Capsule On Disk:\r
1818 Solution A: If PcdCapsuleInRamSupport is enabled, relocate Capsule On Disk to memory and call UpdateCapsule().\r
1819 Solution B: If PcdCapsuleInRamSupport is disabled, relocate Capsule On Disk to a platform-specific NV storage\r
1820 device with BlockIo protocol.\r
1821\r
1822 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
1823 Function will stall 100ms between each retry.\r
1824\r
1825 Side Effects:\r
1826 Capsule Delivery Supported Flag in OsIndication variable and BootNext variable will be cleared.\r
1827 Solution B: Content corruption. Block IO write directly touches low level write. Orignal partitions, file\r
1828 systems of the relocation device will be corrupted.\r
1829\r
1830 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure\r
1831 devices like USB can get enumerated. Input 0 means no retry.\r
1832\r
1833 @retval EFI_SUCCESS Capsule on Disk images are successfully relocated.\r
1834\r
1835**/\r
1836EFI_STATUS\r
1837EFIAPI\r
1838CoDRelocateCapsule(\r
1839 UINTN MaxRetry\r
1840 )\r
1841{\r
1842 if (!PcdGetBool (PcdCapsuleOnDiskSupport)) {\r
1843 return EFI_UNSUPPORTED;\r
1844 }\r
1845\r
1846 //\r
1847 // Clear CapsuleOnDisk Flag firstly.\r
1848 //\r
1849 CoDClearCapsuleOnDiskFlag ();\r
1850\r
1851 //\r
1852 // If Capsule In Ram is supported, delivery capsules through memory\r
1853 //\r
1854 if (PcdGetBool (PcdCapsuleInRamSupport)) {\r
1855 DEBUG ((DEBUG_INFO, "Capsule In Ram is supported, call gRT->UpdateCapsule().\n"));\r
1856 return RelocateCapsuleToRam (MaxRetry);\r
1857 } else {\r
1858 DEBUG ((DEBUG_INFO, "Reallcoate all Capsule on Disks to %s in RootDir.\n", (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName)));\r
1859 return RelocateCapsuleToDisk (MaxRetry);\r
1860 }\r
1861}\r
1862\r
1863/**\r
1864 Remove the temp file from the root of EFI System Partition.\r
1865 Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
1866 Function will stall 100ms between each retry.\r
1867\r
1868 @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure\r
1869 devices like USB can get enumerated. Input 0 means no retry.\r
1870\r
1871 @retval EFI_SUCCESS Remove the temp file successfully.\r
1872\r
1873**/\r
1874EFI_STATUS\r
1875EFIAPI\r
1876CoDRemoveTempFile (\r
1877 UINTN MaxRetry\r
1878 )\r
1879{\r
1880 EFI_STATUS Status;\r
1881 UINTN DataSize;\r
1882 UINT16 *LoadOptionNumber;\r
1883 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
1884 EFI_HANDLE FsHandle;\r
1885 EFI_FILE_HANDLE RootDir;\r
1886 EFI_FILE_HANDLE TempCodFile;\r
1887\r
1888 RootDir = NULL;\r
1889 TempCodFile = NULL;\r
1890 DataSize = sizeof(UINT16);\r
1891\r
1892 LoadOptionNumber = AllocatePool (sizeof(UINT16));\r
1893 if (LoadOptionNumber == NULL) {\r
1894 return EFI_OUT_OF_RESOURCES;\r
1895 }\r
1896\r
1897 //\r
1898 // Check if capsule files are relocated\r
1899 //\r
1900 Status = gRT->GetVariable (\r
1901 COD_RELOCATION_LOAD_OPTION_VAR_NAME,\r
1902 &gEfiCapsuleVendorGuid,\r
1903 NULL,\r
1904 &DataSize,\r
1905 (VOID *)LoadOptionNumber\r
1906 );\r
1907 if (EFI_ERROR(Status) || DataSize != sizeof(UINT16)) {\r
1908 goto EXIT;\r
1909 }\r
1910\r
1911 //\r
1912 // Get the EFI file system from the boot option where the capsules are relocated\r
1913 //\r
1914 Status = GetEfiSysPartitionFromActiveBootOption(MaxRetry, &LoadOptionNumber, &FsHandle);\r
1915 if (EFI_ERROR(Status)) {\r
1916 goto EXIT;\r
1917 }\r
1918\r
1919 Status = gBS->HandleProtocol(FsHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);\r
1920 if (EFI_ERROR(Status)) {\r
1921 goto EXIT;\r
1922 }\r
1923\r
1924 Status = Fs->OpenVolume(Fs, &RootDir);\r
1925 if (EFI_ERROR(Status)) {\r
1926 goto EXIT;\r
1927 }\r
1928\r
1929 //\r
1930 // Delete the TempCoDFile\r
1931 //\r
1932 Status = RootDir->Open(\r
1933 RootDir,\r
1934 &TempCodFile,\r
1935 (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),\r
1936 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,\r
1937 0\r
1938 );\r
1939 if (EFI_ERROR(Status)) {\r
1940 goto EXIT;\r
1941 }\r
1942\r
1943 TempCodFile->Delete(TempCodFile);\r
1944\r
1945 //\r
1946 // Clear "CoDRelocationLoadOption" variable\r
1947 //\r
1948 Status = gRT->SetVariable (\r
1949 COD_RELOCATION_LOAD_OPTION_VAR_NAME,\r
1950 &gEfiCapsuleVendorGuid,\r
1951 0,\r
1952 0,\r
1953 NULL\r
1954 );\r
1955\r
1956EXIT:\r
1957 if (LoadOptionNumber != NULL) {\r
1958 FreePool (LoadOptionNumber);\r
1959 }\r
1960\r
1961 if (RootDir != NULL) {\r
1962 RootDir->Close(RootDir);\r
1963 }\r
1964\r
1965 return Status;\r
1966}\r