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