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