]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Application/CapsuleApp/CapsuleOnDisk.c
MdeModulePkg/CapsuleApp: Add functions to support Capsule-on-Disk
[mirror_edk2.git] / MdeModulePkg / Application / CapsuleApp / CapsuleOnDisk.c
CommitLineData
d67ade09
CC
1/** @file\r
2 Process Capsule On Disk.\r
3\r
4 Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14#include <Uefi.h>\r
15#include <Library/BaseLib.h>\r
16#include <Library/DebugLib.h>\r
17#include <Library/BaseMemoryLib.h>\r
18#include <Library/MemoryAllocationLib.h>\r
19#include <Library/UefiBootServicesTableLib.h>\r
20#include <Library/UefiRuntimeServicesTableLib.h>\r
21#include <Library/UefiLib.h>\r
22#include <Library/PrintLib.h>\r
23#include <Library/DevicePathLib.h>\r
24#include <Library/FileHandleLib.h>\r
25#include <Library/UefiBootManagerLib.h>\r
26#include <Protocol/SimpleFileSystem.h>\r
27#include <Protocol/Shell.h>\r
28#include <Guid/FileInfo.h>\r
29#include <Guid/GlobalVariable.h>\r
30#include <Guid/Gpt.h>\r
31\r
32EFI_GUID mCapsuleOnDiskBootOptionGuid = { 0x4CC29BB7, 0x2413, 0x40A2, { 0xB0, 0x6D, 0x25, 0x3E, 0x37, 0x10, 0xF5, 0x32 } };\r
33\r
34/**\r
35 Get shell protocol.\r
36\r
37 @return Pointer to shell protocol.\r
38\r
39**/\r
40EFI_SHELL_PROTOCOL *\r
41GetShellProtocol (\r
42 VOID\r
43 );\r
44\r
45/**\r
46 Get file name from file path.\r
47\r
48 @param FilePath File path.\r
49\r
50 @return Pointer to file name.\r
51\r
52**/\r
53CHAR16 *\r
54GetFileNameFromPath (\r
55 CHAR16 *FilePath\r
56 )\r
57{\r
58 EFI_STATUS Status;\r
59 EFI_SHELL_PROTOCOL *ShellProtocol;\r
60 SHELL_FILE_HANDLE Handle;\r
61 EFI_FILE_INFO *FileInfo;\r
62\r
63 ShellProtocol = GetShellProtocol ();\r
64 if (ShellProtocol == NULL) {\r
65 return NULL;\r
66 }\r
67\r
68 //\r
69 // Open file by FileName.\r
70 //\r
71 Status = ShellProtocol->OpenFileByName (\r
72 FilePath,\r
73 &Handle,\r
74 EFI_FILE_MODE_READ\r
75 );\r
76 if (EFI_ERROR (Status)) {\r
77 return NULL;\r
78 }\r
79\r
80 //\r
81 // Get file name from EFI_FILE_INFO.\r
82 //\r
83 FileInfo = ShellProtocol->GetFileInfo (Handle);\r
84 ShellProtocol->CloseFile (Handle);\r
85 if (FileInfo == NULL) {\r
86 return NULL;\r
87 }\r
88\r
89 return FileInfo->FileName;\r
90}\r
91\r
92/**\r
93 Check if the device path is EFI system Partition.\r
94\r
95 @param DevicePath The ESP device path.\r
96\r
97 @retval TRUE DevicePath is a device path for ESP.\r
98 @retval FALSE DevicePath is not a device path for ESP.\r
99\r
100**/\r
101BOOLEAN\r
102IsEfiSysPartitionDevicePath (\r
103 EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
104 )\r
105{\r
106 EFI_STATUS Status;\r
107 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
108 HARDDRIVE_DEVICE_PATH *Hd;\r
109 EFI_HANDLE Handle;\r
110\r
111 //\r
112 // Check if the device path contains GPT node\r
113 //\r
114 TempDevicePath = DevicePath;\r
115\r
116 while (!IsDevicePathEnd (TempDevicePath)) {\r
117 if ((DevicePathType (TempDevicePath) == MEDIA_DEVICE_PATH) &&\r
118 (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP)) {\r
119 Hd = (HARDDRIVE_DEVICE_PATH *)TempDevicePath;\r
120 if (Hd->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER) {\r
121 break;\r
122 }\r
123 }\r
124 TempDevicePath = NextDevicePathNode (TempDevicePath);\r
125 }\r
126\r
127 if (!IsDevicePathEnd (TempDevicePath)) {\r
128 //\r
129 // Search for EFI system partition protocol on full device path in Boot Option\r
130 //\r
131 Status = gBS->LocateDevicePath (&gEfiPartTypeSystemPartGuid, &DevicePath, &Handle);\r
132 return EFI_ERROR (Status) ? FALSE : TRUE;\r
133 } else {\r
134 return FALSE;\r
135 }\r
136}\r
137\r
138/**\r
139 Dump all EFI System Partition.\r
140\r
141**/\r
142VOID\r
143DumpAllEfiSysPartition (\r
144 VOID\r
145 )\r
146{\r
147 EFI_HANDLE *SimpleFileSystemHandles;\r
148 UINTN NumberSimpleFileSystemHandles;\r
149 UINTN Index;\r
150 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
151 UINTN NumberEfiSystemPartitions;\r
152 EFI_SHELL_PROTOCOL *ShellProtocol;\r
153\r
154 ShellProtocol = GetShellProtocol ();\r
155 NumberEfiSystemPartitions = 0;\r
156\r
157 Print (L"EFI System Partition list:\n");\r
158\r
159 gBS->LocateHandleBuffer (\r
160 ByProtocol,\r
161 &gEfiSimpleFileSystemProtocolGuid,\r
162 NULL,\r
163 &NumberSimpleFileSystemHandles,\r
164 &SimpleFileSystemHandles\r
165 );\r
166\r
167 for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {\r
168 DevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);\r
169 if (IsEfiSysPartitionDevicePath (DevicePath)) {\r
170 NumberEfiSystemPartitions++;\r
171 Print(L" %s\n %s\n", ShellProtocol->GetMapFromDevicePath (&DevicePath), ConvertDevicePathToText (DevicePath, TRUE, TRUE));\r
172 }\r
173 }\r
174\r
175 if (NumberEfiSystemPartitions == 0) {\r
176 Print(L" No ESP found.\n");\r
177 }\r
178}\r
179\r
180/**\r
181 Check if capsule is provisioned.\r
182\r
183 @retval TRUE Capsule is provisioned previously.\r
184 @retval FALSE No capsule is provisioned.\r
185\r
186**/\r
187BOOLEAN\r
188IsCapsuleProvisioned (\r
189 VOID\r
190 )\r
191{\r
192 EFI_STATUS Status;\r
193 UINT64 OsIndication;\r
194 UINTN DataSize;\r
195\r
196 OsIndication = 0;\r
197 DataSize = sizeof(UINT64);\r
198 Status = gRT->GetVariable (\r
199 L"OsIndications",\r
200 &gEfiGlobalVariableGuid,\r
201 NULL,\r
202 &DataSize,\r
203 &OsIndication\r
204 );\r
205 if (!EFI_ERROR (Status) &&\r
206 (OsIndication & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) != 0) {\r
207 return TRUE;\r
208 }\r
209\r
210 return FALSE;\r
211}\r
212\r
213/**\r
214 Get one active Efi System Partition.\r
215\r
216 @param[out] FsDevicePath The device path of Fs\r
217 @param[out] Fs The file system within EfiSysPartition\r
218\r
219 @retval EFI_SUCCESS Get file system successfully\r
220 @retval EFI_NOT_FOUND No valid file system found\r
221\r
222**/\r
223EFI_STATUS\r
224GetEfiSysPartition (\r
225 OUT EFI_DEVICE_PATH_PROTOCOL **FsDevicePath,\r
226 OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs\r
227 )\r
228{\r
229 EFI_HANDLE *SimpleFileSystemHandles;\r
230 UINTN NumberSimpleFileSystemHandles;\r
231 UINTN Index;\r
232 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
233 EFI_STATUS Status;\r
234\r
235 Status = gBS->LocateHandleBuffer (\r
236 ByProtocol,\r
237 &gEfiSimpleFileSystemProtocolGuid,\r
238 NULL,\r
239 &NumberSimpleFileSystemHandles,\r
240 &SimpleFileSystemHandles\r
241 );\r
242\r
243 if (EFI_ERROR (Status)) {\r
244 return EFI_NOT_FOUND;\r
245 }\r
246\r
247 for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {\r
248 DevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);\r
249 if (IsEfiSysPartitionDevicePath (DevicePath)) {\r
250 Status = gBS->HandleProtocol (SimpleFileSystemHandles[Index], &gEfiSimpleFileSystemProtocolGuid, (VOID **)Fs);\r
251 if (!EFI_ERROR (Status)) {\r
252 *FsDevicePath = DevicePath;\r
253 return EFI_SUCCESS;\r
254 }\r
255 }\r
256 }\r
257\r
258 return EFI_NOT_FOUND;\r
259}\r
260\r
261/**\r
262 Check if Active Efi System Partition within GPT is in the device path.\r
263\r
264 @param[in] DevicePath The device path\r
265 @param[out] FsDevicePath The device path of Fs\r
266 @param[out] Fs The file system within EfiSysPartition\r
267\r
268 @retval EFI_SUCCESS Get file system successfully\r
269 @retval EFI_NOT_FOUND No valid file system found\r
270 @retval others Get file system failed\r
271\r
272**/\r
273EFI_STATUS\r
274GetEfiSysPartitionFromDevPath (\r
275 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
276 OUT EFI_DEVICE_PATH_PROTOCOL **FsDevicePath,\r
277 OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs\r
278 )\r
279{\r
280 EFI_STATUS Status;\r
281 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
282 HARDDRIVE_DEVICE_PATH *Hd;\r
283 EFI_HANDLE Handle;\r
284\r
285 //\r
286 // Check if the device path contains GPT node\r
287 //\r
288 TempDevicePath = DevicePath;\r
289 while (!IsDevicePathEnd (TempDevicePath)) {\r
290 if ((DevicePathType (TempDevicePath) == MEDIA_DEVICE_PATH) &&\r
291 (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP)) {\r
292 Hd = (HARDDRIVE_DEVICE_PATH *)TempDevicePath;\r
293 if (Hd->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER) {\r
294 break;\r
295 }\r
296 }\r
297 TempDevicePath = NextDevicePathNode (TempDevicePath);\r
298 }\r
299\r
300 if (!IsDevicePathEnd (TempDevicePath)) {\r
301 //\r
302 // Search for EFI system partition protocol on full device path in Boot Option\r
303 //\r
304 Status = gBS->LocateDevicePath (&gEfiPartTypeSystemPartGuid, &DevicePath, &Handle);\r
305\r
306 //\r
307 // Search for simple file system on this handler\r
308 //\r
309 if (!EFI_ERROR (Status)) {\r
310 Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)Fs);\r
311 if (!EFI_ERROR (Status)) {\r
312 *FsDevicePath = DevicePathFromHandle (Handle);\r
313 return EFI_SUCCESS;\r
314 }\r
315 }\r
316 }\r
317\r
318 return EFI_NOT_FOUND;\r
319}\r
320\r
321/**\r
322 Get SimpleFileSystem from boot option file path.\r
323\r
324 @param[in] DevicePath The file path of boot option\r
325 @param[out] FullPath The full device path of boot device\r
326 @param[out] Fs The file system within EfiSysPartition\r
327\r
328 @retval EFI_SUCCESS Get file system successfully\r
329 @retval EFI_NOT_FOUND No valid file system found\r
330 @retval others Get file system failed\r
331\r
332**/\r
333EFI_STATUS\r
334EFIAPI\r
335GetEfiSysPartitionFromBootOptionFilePath (\r
336 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
337 OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,\r
338 OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs\r
339 )\r
340{\r
341 EFI_STATUS Status;\r
342 EFI_DEVICE_PATH_PROTOCOL *CurFullPath;\r
343 EFI_DEVICE_PATH_PROTOCOL *PreFullPath;\r
344 EFI_DEVICE_PATH_PROTOCOL *FsFullPath;\r
345\r
346 CurFullPath = NULL;\r
347 FsFullPath = NULL;\r
348 //\r
349 // Try every full device Path generated from bootoption\r
350 //\r
351 do {\r
352 PreFullPath = CurFullPath;\r
353 CurFullPath = EfiBootManagerGetNextFullDevicePath (DevicePath, CurFullPath);\r
354\r
355 if (PreFullPath != NULL) {\r
356 FreePool (PreFullPath);\r
357 }\r
358\r
359 if (CurFullPath == NULL) {\r
360 //\r
361 // No Active EFI system partition is found in BootOption device path\r
362 //\r
363 Status = EFI_NOT_FOUND;\r
364 break;\r
365 }\r
366\r
367 DEBUG_CODE (\r
368 CHAR16 *DevicePathStr;\r
369\r
370 DevicePathStr = ConvertDevicePathToText (CurFullPath, TRUE, TRUE);\r
371 if (DevicePathStr != NULL){\r
372 DEBUG ((DEBUG_INFO, "Full device path %s\n", DevicePathStr));\r
373 FreePool (DevicePathStr);\r
374 }\r
375 );\r
376\r
377 Status = GetEfiSysPartitionFromDevPath (CurFullPath, &FsFullPath, Fs);\r
378 } while (EFI_ERROR (Status));\r
379\r
380 if (*Fs != NULL) {\r
381 *FullPath = FsFullPath;\r
382 return EFI_SUCCESS;\r
383 } else {\r
384 return EFI_NOT_FOUND;\r
385 }\r
386}\r
387\r
388/**\r
389 Get a valid SimpleFileSystem within EFI system partition.\r
390\r
391 @param[in] Map The FS mapping capsule write to\r
392 @param[out] BootNext The value of BootNext Variable\r
393 @param[out] Fs The file system within EfiSysPartition\r
394 @param[out] UpdateBootNext The flag to indicate whether update BootNext Variable\r
395\r
396 @retval EFI_SUCCESS Get FS successfully\r
397 @retval EFI_NOT_FOUND No valid FS found\r
398 @retval others Get FS failed\r
399\r
400**/\r
401EFI_STATUS\r
402EFIAPI\r
403GetUpdateFileSystem (\r
404 IN CHAR16 *Map,\r
405 OUT UINT16 *BootNext,\r
406 OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs,\r
407 OUT BOOLEAN *UpdateBootNext\r
408)\r
409{\r
410 EFI_STATUS Status;\r
411 CHAR16 BootOptionName[20];\r
412 UINTN Index;\r
413 CONST EFI_DEVICE_PATH_PROTOCOL *MappedDevicePath;\r
414 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
415 EFI_DEVICE_PATH_PROTOCOL *FullPath;\r
416 UINT16 *BootNextData;\r
417 EFI_BOOT_MANAGER_LOAD_OPTION BootNextOption;\r
418 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptionBuffer;\r
419 UINTN BootOptionCount;\r
420 EFI_SHELL_PROTOCOL *ShellProtocol;\r
421 EFI_BOOT_MANAGER_LOAD_OPTION NewOption;\r
422\r
423 MappedDevicePath = NULL;\r
424 ShellProtocol = GetShellProtocol ();\r
425\r
426 //\r
427 // 1. If Fs is not assigned and there are capsule provisioned before,\r
428 // Get EFI system partition from BootNext.\r
429 //\r
430 if (IsCapsuleProvisioned () && Map == NULL) {\r
431 Status = GetVariable2 (\r
432 L"BootNext",\r
433 &gEfiGlobalVariableGuid,\r
434 (VOID **)&BootNextData,\r
435 NULL\r
436 );\r
437 if (!EFI_ERROR (Status)) {\r
438 UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", *BootNextData);\r
439 Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootNextOption);\r
440 if (!EFI_ERROR (Status)) {\r
441 DevicePath = BootNextOption.FilePath;\r
442 Status = GetEfiSysPartitionFromBootOptionFilePath (DevicePath, &FullPath, Fs);\r
443 if (!EFI_ERROR (Status)) {\r
444 *UpdateBootNext = FALSE;\r
445 Print(L"Get EFI system partition from BootNext : %s\n", BootNextOption.Description);\r
446 Print(L"%s %s\n", ShellProtocol->GetMapFromDevicePath (&FullPath), ConvertDevicePathToText (FullPath, TRUE, TRUE));\r
447 return EFI_SUCCESS;\r
448 }\r
449 }\r
450 }\r
451 }\r
452\r
453 //\r
454 // Check if Map is valid.\r
455 //\r
456 if (Map != NULL) {\r
457 MappedDevicePath = ShellProtocol->GetDevicePathFromMap (Map);\r
458 if (MappedDevicePath == NULL) {\r
459 Print(L"'%s' is not a valid mapping.\n", Map);\r
460 return EFI_INVALID_PARAMETER;\r
461 } else if (!IsEfiSysPartitionDevicePath (DuplicateDevicePath (MappedDevicePath))) {\r
462 Print(L"'%s' is not a EFI System Partition.\n", Map);\r
463 return EFI_INVALID_PARAMETER;\r
464 }\r
465 }\r
466\r
467 //\r
468 // 2. Get EFI system partition form boot options.\r
469 //\r
470 BootOptionBuffer = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
471 if (BootOptionCount == 0 && Map == NULL) {\r
472 return EFI_NOT_FOUND;\r
473 }\r
474\r
475 for (Index = 0; Index < BootOptionCount; Index++) {\r
476 //\r
477 // Get the boot option from the link list\r
478 //\r
479 DevicePath = BootOptionBuffer[Index].FilePath;\r
480\r
481 //\r
482 // Skip inactive or legacy boot options\r
483 //\r
484 if ((BootOptionBuffer[Index].Attributes & LOAD_OPTION_ACTIVE) == 0 ||\r
485 DevicePathType (DevicePath) == BBS_DEVICE_PATH) {\r
486 continue;\r
487 }\r
488\r
489 DEBUG_CODE (\r
490 CHAR16 *DevicePathStr;\r
491\r
492 DevicePathStr = ConvertDevicePathToText (DevicePath, TRUE, TRUE);\r
493 if (DevicePathStr != NULL){\r
494 DEBUG ((DEBUG_INFO, "Try BootOption %s\n", DevicePathStr));\r
495 FreePool (DevicePathStr);\r
496 } else {\r
497 DEBUG ((DEBUG_INFO, "DevicePathToStr failed\n"));\r
498 }\r
499 );\r
500\r
501 Status = GetEfiSysPartitionFromBootOptionFilePath (DevicePath, &FullPath, Fs);\r
502 if (!EFI_ERROR (Status)) {\r
503 if (Map == NULL) {\r
504 *BootNext = (UINT16) BootOptionBuffer[Index].OptionNumber;\r
505 *UpdateBootNext = TRUE;\r
506 Print (L"Found EFI system partition on Boot%04x: %s\n", *BootNext, BootOptionBuffer[Index].Description);\r
507 Print (L"%s %s\n", ShellProtocol->GetMapFromDevicePath (&FullPath), ConvertDevicePathToText (FullPath, TRUE, TRUE));\r
508 return EFI_SUCCESS;\r
509 }\r
510\r
511 if (StrnCmp (Map, ShellProtocol->GetMapFromDevicePath (&FullPath), StrLen (Map)) == 0) {\r
512 *BootNext = (UINT16) BootOptionBuffer[Index].OptionNumber;\r
513 *UpdateBootNext = TRUE;\r
514 Print (L"Found Boot Option on %s : %s\n", Map, BootOptionBuffer[Index].Description);\r
515 return EFI_SUCCESS;\r
516 }\r
517 }\r
518 }\r
519\r
520 //\r
521 // 3. If no ESP is found on boot option, try to find a ESP and create boot option for it.\r
522 //\r
523 if (Map != NULL) {\r
524 //\r
525 // If map is assigned, try to get ESP from mapped Fs.\r
526 //\r
527 DevicePath = DuplicateDevicePath (MappedDevicePath);\r
528 Status = GetEfiSysPartitionFromDevPath (DevicePath, &FullPath, Fs);\r
529 if (EFI_ERROR (Status)) {\r
530 Print (L"Error: Cannot get EFI system partiion from '%s' - %r\n", Map, Status);\r
531 return EFI_NOT_FOUND;\r
532 }\r
533 Print (L"Warning: Cannot find Boot Option on '%s'!\n", Map);\r
534 } else {\r
535 Status = GetEfiSysPartition (&DevicePath, Fs);\r
536 if (EFI_ERROR (Status)) {\r
537 Print (L"Error: Cannot find a EFI system partition!\n");\r
538 return EFI_NOT_FOUND;\r
539 }\r
540 }\r
541\r
542 Print (L"Create Boot option for capsule on disk:\n");\r
543 Status = EfiBootManagerInitializeLoadOption (\r
544 &NewOption,\r
545 LoadOptionNumberUnassigned,\r
546 LoadOptionTypeBoot,\r
547 LOAD_OPTION_ACTIVE,\r
548 L"UEFI Capsule On Disk",\r
549 DevicePath,\r
550 (UINT8 *) &mCapsuleOnDiskBootOptionGuid,\r
551 sizeof(EFI_GUID)\r
552 );\r
553 if (!EFI_ERROR (Status)) {\r
554 Status = EfiBootManagerAddLoadOptionVariable (&NewOption, (UINTN) -1); {\r
555 if (!EFI_ERROR (Status)) {\r
556 *UpdateBootNext = TRUE;\r
557 *BootNext = (UINT16) NewOption.OptionNumber;\r
558 Print (L" Boot%04x: %s\n", *BootNext, ConvertDevicePathToText(DevicePath, TRUE, TRUE));\r
559 return EFI_SUCCESS;\r
560 }\r
561 }\r
562 }\r
563\r
564 Print (L"ERROR: Cannot create boot option! - %r\n", Status);\r
565\r
566 return EFI_NOT_FOUND;\r
567}\r
568\r
569/**\r
570 Write files to a given SimpleFileSystem.\r
571\r
572 @param[in] Buffer The buffer array\r
573 @param[in] BufferSize The buffer size array\r
574 @param[in] FileName The file name array\r
575 @param[in] BufferNum The buffer number\r
576 @param[in] Fs The SimpleFileSystem handle to be written\r
577\r
578 @retval EFI_SUCCESS Write file successfully\r
579 @retval EFI_NOT_FOUND SFS protocol not found\r
580 @retval others Write file failed\r
581\r
582**/\r
583EFI_STATUS\r
584WriteUpdateFile (\r
585 IN VOID **Buffer,\r
586 IN UINTN *BufferSize,\r
587 IN CHAR16 **FileName,\r
588 IN UINTN BufferNum,\r
589 IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs\r
590)\r
591{\r
592 EFI_STATUS Status;\r
593 EFI_FILE *Root;\r
594 EFI_FILE *FileHandle;\r
595 EFI_FILE_PROTOCOL *DirHandle;\r
596 UINT64 FileInfo;\r
597 VOID *Filebuffer;\r
598 UINTN FileSize;\r
599 UINTN Index;\r
600\r
601 DirHandle = NULL;\r
602 FileHandle = NULL;\r
603 Index = 0;\r
604\r
605 //\r
606 // Open Root from SFS\r
607 //\r
608 Status = Fs->OpenVolume (Fs, &Root);\r
609 if (EFI_ERROR (Status)) {\r
610 Print (L"Cannot open volume. Status = %r\n", Status);\r
611 return EFI_NOT_FOUND;\r
612 }\r
613\r
614 //\r
615 // Ensure that efi and updatecapsule directories exist\r
616 //\r
617 Status = Root->Open (Root, &DirHandle, L"\\EFI", EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0);\r
618 if (EFI_ERROR (Status)) {\r
619 Status = Root->Open (Root, &DirHandle, L"\\EFI", EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, EFI_FILE_DIRECTORY);\r
620 if (EFI_ERROR (Status)) {\r
621 Print(L"Unable to create %s directory\n", L"\\EFI");\r
622 return EFI_NOT_FOUND;\r
623 }\r
624 }\r
625 Status = Root->Open (Root, &DirHandle, EFI_CAPSULE_FILE_DIRECTORY, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE , 0);\r
626 if (EFI_ERROR (Status)) {\r
627 Status = Root->Open (Root, &DirHandle, EFI_CAPSULE_FILE_DIRECTORY, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, EFI_FILE_DIRECTORY);\r
628 if (EFI_ERROR (Status)) {\r
629 Print(L"Unable to create %s directory\n", EFI_CAPSULE_FILE_DIRECTORY);\r
630 return EFI_NOT_FOUND;\r
631 }\r
632 }\r
633\r
634 for (Index = 0; Index < BufferNum; Index++) {\r
635 FileHandle = NULL;\r
636\r
637 //\r
638 // Open UpdateCapsule file\r
639 //\r
640 Status = DirHandle->Open (DirHandle, &FileHandle, FileName[Index], EFI_FILE_MODE_CREATE | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ, 0);\r
641 if (EFI_ERROR (Status)) {\r
642 Print (L"Unable to create %s file\n", FileName[Index]);\r
643 return EFI_NOT_FOUND;\r
644 }\r
645\r
646 //\r
647 // Empty the file contents\r
648 //\r
649 Status = FileHandleGetSize (FileHandle, &FileInfo);\r
650 if (EFI_ERROR (Status)) {\r
651 FileHandleClose (FileHandle);\r
652 Print (L"Error Reading %s\n", FileName[Index]);\r
653 return EFI_DEVICE_ERROR;\r
654 }\r
655\r
656 //\r
657 // If the file size is already 0, then it has been empty.\r
658 //\r
659 if (FileInfo != 0) {\r
660 //\r
661 // Set the file size to 0.\r
662 //\r
663 FileInfo = 0;\r
664 Status = FileHandleSetSize (FileHandle, FileInfo);\r
665 if (EFI_ERROR (Status)) {\r
666 Print (L"Error Deleting %s\n", FileName[Index]);\r
667 FileHandleClose (FileHandle);\r
668 return Status;\r
669 }\r
670 }\r
671\r
672 //\r
673 // Write Filebuffer to file\r
674 //\r
675 Filebuffer = Buffer[Index];\r
676 FileSize = BufferSize[Index];\r
677 Status = FileHandleWrite (FileHandle, &FileSize, Filebuffer);\r
678 if (EFI_ERROR (Status)) {\r
679 Print (L"Unable to write Capsule Update to %s, Status = %r\n", FileName[Index], Status);\r
680 return EFI_NOT_FOUND;\r
681 }\r
682\r
683 Print (L"Succeed to write %s\n", FileName[Index]);\r
684 FileHandleClose (FileHandle);\r
685 }\r
686\r
687 return EFI_SUCCESS;\r
688}\r
689\r
690/**\r
691 Set capsule status variable.\r
692\r
693 @param[in] SetCap Set or clear the capsule flag.\r
694\r
695 @retval EFI_SUCCESS Succeed to set SetCap variable.\r
696 @retval others Fail to set the variable.\r
697\r
698**/\r
699EFI_STATUS\r
700SetCapsuleStatusVariable (\r
701 BOOLEAN SetCap\r
702 )\r
703{\r
704 EFI_STATUS Status;\r
705 UINT64 OsIndication;\r
706 UINTN DataSize;\r
707\r
708 OsIndication = 0;\r
709 DataSize = sizeof(UINT64);\r
710 Status = gRT->GetVariable (\r
711 L"OsIndications",\r
712 &gEfiGlobalVariableGuid,\r
713 NULL,\r
714 &DataSize,\r
715 &OsIndication\r
716 );\r
717 if (EFI_ERROR (Status)) {\r
718 OsIndication = 0;\r
719 }\r
720 if (SetCap) {\r
721 OsIndication |= ((UINT64)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED);\r
722 }\r
723 else {\r
724 OsIndication &= ~((UINT64)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED);\r
725 }\r
726 Status = gRT->SetVariable (\r
727 L"OsIndications",\r
728 &gEfiGlobalVariableGuid,\r
729 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
730 sizeof(UINT64),\r
731 &OsIndication\r
732 );\r
733\r
734 return Status;\r
735}\r
736\r
737/**\r
738 Process Capsule On Disk.\r
739\r
740 @param[in] CapsuleBuffer An array of pointer to capsule images\r
741 @param[in] CapsuleBufferSize An array of UINTN to capsule images size\r
742 @param[in] FilePath An array of capsule images file path\r
743 @param[in] Map File system mapping string\r
744 @param[in] CapsuleNum The count of capsule images\r
745\r
746 @retval EFI_SUCCESS Capsule on disk success.\r
747 @retval others Capsule on disk fail.\r
748\r
749**/\r
750EFI_STATUS\r
751ProcessCapsuleOnDisk (\r
752 IN VOID **CapsuleBuffer,\r
753 IN UINTN *CapsuleBufferSize,\r
754 IN CHAR16 **FilePath,\r
755 IN CHAR16 *Map,\r
756 IN UINTN CapsuleNum\r
757 )\r
758{\r
759 EFI_STATUS Status;\r
760 UINT16 BootNext;\r
761 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
762 BOOLEAN UpdateBootNext;\r
763\r
764 //\r
765 // Get a valid file system from boot path\r
766 //\r
767 Fs = NULL;\r
768\r
769 Status = GetUpdateFileSystem (Map, &BootNext, &Fs, &UpdateBootNext);\r
770 if (EFI_ERROR (Status)) {\r
771 Print (L"CapsuleApp: cannot find a valid file system on boot devies. Status = %r\n", Status);\r
772 return Status;\r
773 }\r
774\r
775 //\r
776 // Copy capsule image to '\efi\UpdateCapsule\'\r
777 //\r
778 Status = WriteUpdateFile (CapsuleBuffer, CapsuleBufferSize, FilePath, CapsuleNum, Fs);\r
779 if (EFI_ERROR (Status)) {\r
780 Print (L"CapsuleApp: capsule image could not be copied for update.\n");\r
781 return Status;\r
782 }\r
783\r
784 //\r
785 // Set variable then reset\r
786 //\r
787 Status = SetCapsuleStatusVariable (TRUE);\r
788 if (EFI_ERROR (Status)) {\r
789 Print (L"CapsuleApp: unable to set OSIndication variable.\n");\r
790 return Status;\r
791 }\r
792\r
793 if (UpdateBootNext) {\r
794 Status = gRT->SetVariable (\r
795 L"BootNext",\r
796 &gEfiGlobalVariableGuid,\r
797 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
798 sizeof(UINT16),\r
799 &BootNext\r
800 );\r
801 if (EFI_ERROR (Status)){\r
802 Print (L"CapsuleApp: unable to set BootNext variable.\n");\r
803 return Status;\r
804 }\r
805 }\r
806\r
807 return EFI_SUCCESS;\r
808}\r