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