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