| /** @file | |
| Process Capsule On Disk. | |
| Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "CapsuleApp.h" | |
| EFI_GUID mCapsuleOnDiskBootOptionGuid = { | |
| 0x4CC29BB7, 0x2413, 0x40A2, { 0xB0, 0x6D, 0x25, 0x3E, 0x37, 0x10, 0xF5, 0x32 } | |
| }; | |
| /** | |
| Get file name from file path. | |
| @param FilePath File path. | |
| @return Pointer to file name. | |
| **/ | |
| CHAR16 * | |
| GetFileNameFromPath ( | |
| CHAR16 *FilePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_SHELL_PROTOCOL *ShellProtocol; | |
| SHELL_FILE_HANDLE Handle; | |
| EFI_FILE_INFO *FileInfo; | |
| ShellProtocol = GetShellProtocol (); | |
| if (ShellProtocol == NULL) { | |
| return NULL; | |
| } | |
| // | |
| // Open file by FileName. | |
| // | |
| Status = ShellProtocol->OpenFileByName ( | |
| FilePath, | |
| &Handle, | |
| EFI_FILE_MODE_READ | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return NULL; | |
| } | |
| // | |
| // Get file name from EFI_FILE_INFO. | |
| // | |
| FileInfo = ShellProtocol->GetFileInfo (Handle); | |
| ShellProtocol->CloseFile (Handle); | |
| if (FileInfo == NULL) { | |
| return NULL; | |
| } | |
| return FileInfo->FileName; | |
| } | |
| /** | |
| Check if the device path is EFI system Partition. | |
| @param DevicePath The ESP device path. | |
| @retval TRUE DevicePath is a device path for ESP. | |
| @retval FALSE DevicePath is not a device path for ESP. | |
| **/ | |
| BOOLEAN | |
| IsEfiSysPartitionDevicePath ( | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; | |
| HARDDRIVE_DEVICE_PATH *Hd; | |
| EFI_HANDLE Handle; | |
| // | |
| // Check if the device path contains GPT node | |
| // | |
| TempDevicePath = DevicePath; | |
| while (!IsDevicePathEnd (TempDevicePath)) { | |
| if ((DevicePathType (TempDevicePath) == MEDIA_DEVICE_PATH) && | |
| (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP)) | |
| { | |
| Hd = (HARDDRIVE_DEVICE_PATH *)TempDevicePath; | |
| if (Hd->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER) { | |
| break; | |
| } | |
| } | |
| TempDevicePath = NextDevicePathNode (TempDevicePath); | |
| } | |
| if (!IsDevicePathEnd (TempDevicePath)) { | |
| // | |
| // Search for EFI system partition protocol on full device path in Boot Option | |
| // | |
| Status = gBS->LocateDevicePath (&gEfiPartTypeSystemPartGuid, &DevicePath, &Handle); | |
| return EFI_ERROR (Status) ? FALSE : TRUE; | |
| } else { | |
| return FALSE; | |
| } | |
| } | |
| /** | |
| Dump all EFI System Partition. | |
| **/ | |
| VOID | |
| DumpAllEfiSysPartition ( | |
| VOID | |
| ) | |
| { | |
| EFI_HANDLE *SimpleFileSystemHandles; | |
| UINTN NumberSimpleFileSystemHandles; | |
| UINTN Index; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| UINTN NumberEfiSystemPartitions; | |
| EFI_SHELL_PROTOCOL *ShellProtocol; | |
| NumberEfiSystemPartitions = 0; | |
| ShellProtocol = GetShellProtocol (); | |
| if (ShellProtocol == NULL) { | |
| Print (L"Get Shell Protocol Fail\n"); | |
| return; | |
| } | |
| Print (L"EFI System Partition list:\n"); | |
| gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| NULL, | |
| &NumberSimpleFileSystemHandles, | |
| &SimpleFileSystemHandles | |
| ); | |
| for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) { | |
| DevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]); | |
| if (IsEfiSysPartitionDevicePath (DevicePath)) { | |
| NumberEfiSystemPartitions++; | |
| Print (L" %s\n %s\n", ShellProtocol->GetMapFromDevicePath (&DevicePath), ConvertDevicePathToText (DevicePath, TRUE, TRUE)); | |
| } | |
| } | |
| if (NumberEfiSystemPartitions == 0) { | |
| Print (L" No ESP found.\n"); | |
| } | |
| } | |
| /** | |
| Check if capsule is provisioned. | |
| @retval TRUE Capsule is provisioned previously. | |
| @retval FALSE No capsule is provisioned. | |
| **/ | |
| BOOLEAN | |
| IsCapsuleProvisioned ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT64 OsIndication; | |
| UINTN DataSize; | |
| OsIndication = 0; | |
| DataSize = sizeof (UINT64); | |
| Status = gRT->GetVariable ( | |
| L"OsIndications", | |
| &gEfiGlobalVariableGuid, | |
| NULL, | |
| &DataSize, | |
| &OsIndication | |
| ); | |
| if (!EFI_ERROR (Status) && | |
| ((OsIndication & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) != 0)) | |
| { | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| Get one active Efi System Partition. | |
| @param[out] FsDevicePath The device path of Fs | |
| @param[out] Fs The file system within EfiSysPartition | |
| @retval EFI_SUCCESS Get file system successfully | |
| @retval EFI_NOT_FOUND No valid file system found | |
| **/ | |
| EFI_STATUS | |
| GetEfiSysPartition ( | |
| OUT EFI_DEVICE_PATH_PROTOCOL **FsDevicePath, | |
| OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs | |
| ) | |
| { | |
| EFI_HANDLE *SimpleFileSystemHandles; | |
| UINTN NumberSimpleFileSystemHandles; | |
| UINTN Index; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| EFI_STATUS Status; | |
| Status = gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| NULL, | |
| &NumberSimpleFileSystemHandles, | |
| &SimpleFileSystemHandles | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_NOT_FOUND; | |
| } | |
| for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) { | |
| DevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]); | |
| if (IsEfiSysPartitionDevicePath (DevicePath)) { | |
| Status = gBS->HandleProtocol (SimpleFileSystemHandles[Index], &gEfiSimpleFileSystemProtocolGuid, (VOID **)Fs); | |
| if (!EFI_ERROR (Status)) { | |
| *FsDevicePath = DevicePath; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| } | |
| return EFI_NOT_FOUND; | |
| } | |
| /** | |
| Check if Active Efi System Partition within GPT is in the device path. | |
| @param[in] DevicePath The device path | |
| @param[out] FsDevicePath The device path of Fs | |
| @param[out] Fs The file system within EfiSysPartition | |
| @retval EFI_SUCCESS Get file system successfully | |
| @retval EFI_NOT_FOUND No valid file system found | |
| @retval others Get file system failed | |
| **/ | |
| EFI_STATUS | |
| GetEfiSysPartitionFromDevPath ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
| OUT EFI_DEVICE_PATH_PROTOCOL **FsDevicePath, | |
| OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; | |
| HARDDRIVE_DEVICE_PATH *Hd; | |
| EFI_HANDLE Handle; | |
| // | |
| // Check if the device path contains GPT node | |
| // | |
| TempDevicePath = DevicePath; | |
| while (!IsDevicePathEnd (TempDevicePath)) { | |
| if ((DevicePathType (TempDevicePath) == MEDIA_DEVICE_PATH) && | |
| (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP)) | |
| { | |
| Hd = (HARDDRIVE_DEVICE_PATH *)TempDevicePath; | |
| if (Hd->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER) { | |
| break; | |
| } | |
| } | |
| TempDevicePath = NextDevicePathNode (TempDevicePath); | |
| } | |
| if (!IsDevicePathEnd (TempDevicePath)) { | |
| // | |
| // Search for EFI system partition protocol on full device path in Boot Option | |
| // | |
| Status = gBS->LocateDevicePath (&gEfiPartTypeSystemPartGuid, &DevicePath, &Handle); | |
| // | |
| // Search for simple file system on this handler | |
| // | |
| if (!EFI_ERROR (Status)) { | |
| Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)Fs); | |
| if (!EFI_ERROR (Status)) { | |
| *FsDevicePath = DevicePathFromHandle (Handle); | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| } | |
| return EFI_NOT_FOUND; | |
| } | |
| /** | |
| Get SimpleFileSystem from boot option file path. | |
| @param[in] DevicePath The file path of boot option | |
| @param[out] FullPath The full device path of boot device | |
| @param[out] Fs The file system within EfiSysPartition | |
| @retval EFI_SUCCESS Get file system successfully | |
| @retval EFI_NOT_FOUND No valid file system found | |
| @retval others Get file system failed | |
| **/ | |
| EFI_STATUS | |
| GetEfiSysPartitionFromBootOptionFilePath ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
| OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, | |
| OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_DEVICE_PATH_PROTOCOL *CurFullPath; | |
| EFI_DEVICE_PATH_PROTOCOL *PreFullPath; | |
| EFI_DEVICE_PATH_PROTOCOL *FsFullPath; | |
| CurFullPath = NULL; | |
| FsFullPath = NULL; | |
| // | |
| // Try every full device Path generated from bootoption | |
| // | |
| do { | |
| PreFullPath = CurFullPath; | |
| CurFullPath = EfiBootManagerGetNextLoadOptionDevicePath (DevicePath, CurFullPath); | |
| if (PreFullPath != NULL) { | |
| FreePool (PreFullPath); | |
| } | |
| if (CurFullPath == NULL) { | |
| // | |
| // No Active EFI system partition is found in BootOption device path | |
| // | |
| Status = EFI_NOT_FOUND; | |
| break; | |
| } | |
| DEBUG_CODE_BEGIN (); | |
| CHAR16 *DevicePathStr; | |
| DevicePathStr = ConvertDevicePathToText (CurFullPath, TRUE, TRUE); | |
| if (DevicePathStr != NULL) { | |
| DEBUG ((DEBUG_INFO, "Full device path %s\n", DevicePathStr)); | |
| FreePool (DevicePathStr); | |
| } | |
| DEBUG_CODE_END (); | |
| Status = GetEfiSysPartitionFromDevPath (CurFullPath, &FsFullPath, Fs); | |
| } while (EFI_ERROR (Status)); | |
| if (*Fs != NULL) { | |
| *FullPath = FsFullPath; | |
| return EFI_SUCCESS; | |
| } else { | |
| return EFI_NOT_FOUND; | |
| } | |
| } | |
| /** | |
| Get a valid SimpleFileSystem within EFI system partition. | |
| @param[in] Map The FS mapping capsule write to | |
| @param[out] BootNext The value of BootNext Variable | |
| @param[out] Fs The file system within EfiSysPartition | |
| @param[out] UpdateBootNext The flag to indicate whether update BootNext Variable | |
| @retval EFI_SUCCESS Get FS successfully | |
| @retval EFI_NOT_FOUND No valid FS found | |
| @retval others Get FS failed | |
| **/ | |
| EFI_STATUS | |
| GetUpdateFileSystem ( | |
| IN CHAR16 *Map, | |
| OUT UINT16 *BootNext, | |
| OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs, | |
| OUT BOOLEAN *UpdateBootNext | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| CHAR16 BootOptionName[20]; | |
| UINTN Index; | |
| CONST EFI_DEVICE_PATH_PROTOCOL *MappedDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *FullPath; | |
| UINT16 *BootNextData; | |
| EFI_BOOT_MANAGER_LOAD_OPTION BootNextOption; | |
| EFI_BOOT_MANAGER_LOAD_OPTION *BootOptionBuffer; | |
| UINTN BootOptionCount; | |
| EFI_SHELL_PROTOCOL *ShellProtocol; | |
| EFI_BOOT_MANAGER_LOAD_OPTION NewOption; | |
| MappedDevicePath = NULL; | |
| BootOptionBuffer = NULL; | |
| ShellProtocol = GetShellProtocol (); | |
| if (ShellProtocol == NULL) { | |
| Print (L"Get Shell Protocol Fail\n"); | |
| return EFI_NOT_FOUND; | |
| } | |
| // | |
| // 1. If Fs is not assigned and there are capsule provisioned before, | |
| // Get EFI system partition from BootNext. | |
| // | |
| if (IsCapsuleProvisioned () && (Map == NULL)) { | |
| Status = GetVariable2 ( | |
| L"BootNext", | |
| &gEfiGlobalVariableGuid, | |
| (VOID **)&BootNextData, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status) || (BootNextData == NULL)) { | |
| Print (L"Get Boot Next Data Fail. Status = %r\n", Status); | |
| return EFI_NOT_FOUND; | |
| } else { | |
| UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", *BootNextData); | |
| Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootNextOption); | |
| if (!EFI_ERROR (Status)) { | |
| DevicePath = BootNextOption.FilePath; | |
| Status = GetEfiSysPartitionFromBootOptionFilePath (DevicePath, &FullPath, Fs); | |
| if (!EFI_ERROR (Status)) { | |
| *UpdateBootNext = FALSE; | |
| Print (L"Get EFI system partition from BootNext : %s\n", BootNextOption.Description); | |
| Print (L"%s %s\n", ShellProtocol->GetMapFromDevicePath (&FullPath), ConvertDevicePathToText (FullPath, TRUE, TRUE)); | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| } | |
| } | |
| // | |
| // Check if Map is valid. | |
| // | |
| if (Map != NULL) { | |
| MappedDevicePath = ShellProtocol->GetDevicePathFromMap (Map); | |
| if (MappedDevicePath == NULL) { | |
| Print (L"'%s' is not a valid mapping.\n", Map); | |
| return EFI_INVALID_PARAMETER; | |
| } else if (!IsEfiSysPartitionDevicePath (DuplicateDevicePath (MappedDevicePath))) { | |
| Print (L"'%s' is not a EFI System Partition.\n", Map); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| } | |
| // | |
| // 2. Get EFI system partition form boot options. | |
| // | |
| BootOptionBuffer = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); | |
| if ((BootOptionBuffer == NULL) || | |
| ((BootOptionCount == 0) && (Map == NULL)) | |
| ) | |
| { | |
| return EFI_NOT_FOUND; | |
| } | |
| for (Index = 0; Index < BootOptionCount; Index++) { | |
| // | |
| // Get the boot option from the link list | |
| // | |
| DevicePath = BootOptionBuffer[Index].FilePath; | |
| // | |
| // Skip inactive or legacy boot options | |
| // | |
| if (((BootOptionBuffer[Index].Attributes & LOAD_OPTION_ACTIVE) == 0) || | |
| (DevicePathType (DevicePath) == BBS_DEVICE_PATH)) | |
| { | |
| continue; | |
| } | |
| DEBUG_CODE_BEGIN (); | |
| CHAR16 *DevicePathStr; | |
| DevicePathStr = ConvertDevicePathToText (DevicePath, TRUE, TRUE); | |
| if (DevicePathStr != NULL) { | |
| DEBUG ((DEBUG_INFO, "Try BootOption %s\n", DevicePathStr)); | |
| FreePool (DevicePathStr); | |
| } else { | |
| DEBUG ((DEBUG_INFO, "DevicePathToStr failed\n")); | |
| } | |
| DEBUG_CODE_END (); | |
| Status = GetEfiSysPartitionFromBootOptionFilePath (DevicePath, &FullPath, Fs); | |
| if (!EFI_ERROR (Status)) { | |
| if (Map == NULL) { | |
| *BootNext = (UINT16)BootOptionBuffer[Index].OptionNumber; | |
| *UpdateBootNext = TRUE; | |
| Print (L"Found EFI system partition on Boot%04x: %s\n", *BootNext, BootOptionBuffer[Index].Description); | |
| Print (L"%s %s\n", ShellProtocol->GetMapFromDevicePath (&FullPath), ConvertDevicePathToText (FullPath, TRUE, TRUE)); | |
| return EFI_SUCCESS; | |
| } | |
| if (StrnCmp (Map, ShellProtocol->GetMapFromDevicePath (&FullPath), StrLen (Map)) == 0) { | |
| *BootNext = (UINT16)BootOptionBuffer[Index].OptionNumber; | |
| *UpdateBootNext = TRUE; | |
| Print (L"Found Boot Option on %s : %s\n", Map, BootOptionBuffer[Index].Description); | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| } | |
| // | |
| // 3. If no ESP is found on boot option, try to find a ESP and create boot option for it. | |
| // | |
| if (Map != NULL) { | |
| // | |
| // If map is assigned, try to get ESP from mapped Fs. | |
| // | |
| DevicePath = DuplicateDevicePath (MappedDevicePath); | |
| Status = GetEfiSysPartitionFromDevPath (DevicePath, &FullPath, Fs); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"Error: Cannot get EFI system partition from '%s' - %r\n", Map, Status); | |
| return EFI_NOT_FOUND; | |
| } | |
| Print (L"Warning: Cannot find Boot Option on '%s'!\n", Map); | |
| } else { | |
| Status = GetEfiSysPartition (&DevicePath, Fs); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"Error: Cannot find a EFI system partition!\n"); | |
| return EFI_NOT_FOUND; | |
| } | |
| } | |
| Print (L"Create Boot option for capsule on disk:\n"); | |
| Status = EfiBootManagerInitializeLoadOption ( | |
| &NewOption, | |
| LoadOptionNumberUnassigned, | |
| LoadOptionTypeBoot, | |
| LOAD_OPTION_ACTIVE, | |
| L"UEFI Capsule On Disk", | |
| DevicePath, | |
| (UINT8 *)&mCapsuleOnDiskBootOptionGuid, | |
| sizeof (EFI_GUID) | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| Status = EfiBootManagerAddLoadOptionVariable (&NewOption, (UINTN)-1); | |
| { | |
| if (!EFI_ERROR (Status)) { | |
| *UpdateBootNext = TRUE; | |
| *BootNext = (UINT16)NewOption.OptionNumber; | |
| Print (L" Boot%04x: %s\n", *BootNext, ConvertDevicePathToText (DevicePath, TRUE, TRUE)); | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| } | |
| Print (L"ERROR: Cannot create boot option! - %r\n", Status); | |
| return EFI_NOT_FOUND; | |
| } | |
| /** | |
| Write files to a given SimpleFileSystem. | |
| @param[in] Buffer The buffer array | |
| @param[in] BufferSize The buffer size array | |
| @param[in] FileName The file name array | |
| @param[in] BufferNum The buffer number | |
| @param[in] Fs The SimpleFileSystem handle to be written | |
| @retval EFI_SUCCESS Write file successfully | |
| @retval EFI_NOT_FOUND SFS protocol not found | |
| @retval others Write file failed | |
| **/ | |
| EFI_STATUS | |
| WriteUpdateFile ( | |
| IN VOID **Buffer, | |
| IN UINTN *BufferSize, | |
| IN CHAR16 **FileName, | |
| IN UINTN BufferNum, | |
| IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_FILE *Root; | |
| EFI_FILE *FileHandle; | |
| EFI_FILE_PROTOCOL *DirHandle; | |
| UINT64 FileInfo; | |
| VOID *Filebuffer; | |
| UINTN FileSize; | |
| UINTN Index; | |
| DirHandle = NULL; | |
| FileHandle = NULL; | |
| Index = 0; | |
| // | |
| // Open Root from SFS | |
| // | |
| Status = Fs->OpenVolume (Fs, &Root); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"Cannot open volume. Status = %r\n", Status); | |
| return EFI_NOT_FOUND; | |
| } | |
| // | |
| // Ensure that efi and updatecapsule directories exist | |
| // | |
| Status = Root->Open (Root, &DirHandle, L"\\EFI", EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); | |
| if (EFI_ERROR (Status)) { | |
| Status = Root->Open (Root, &DirHandle, L"\\EFI", EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, EFI_FILE_DIRECTORY); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"Unable to create %s directory\n", L"\\EFI"); | |
| return EFI_NOT_FOUND; | |
| } | |
| } | |
| Status = Root->Open (Root, &DirHandle, EFI_CAPSULE_FILE_DIRECTORY, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); | |
| if (EFI_ERROR (Status)) { | |
| Status = Root->Open (Root, &DirHandle, EFI_CAPSULE_FILE_DIRECTORY, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, EFI_FILE_DIRECTORY); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"Unable to create %s directory\n", EFI_CAPSULE_FILE_DIRECTORY); | |
| return EFI_NOT_FOUND; | |
| } | |
| } | |
| for (Index = 0; Index < BufferNum; Index++) { | |
| FileHandle = NULL; | |
| // | |
| // Open UpdateCapsule file | |
| // | |
| Status = DirHandle->Open (DirHandle, &FileHandle, FileName[Index], EFI_FILE_MODE_CREATE | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ, 0); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"Unable to create %s file\n", FileName[Index]); | |
| return EFI_NOT_FOUND; | |
| } | |
| // | |
| // Empty the file contents | |
| // | |
| Status = FileHandleGetSize (FileHandle, &FileInfo); | |
| if (EFI_ERROR (Status)) { | |
| FileHandleClose (FileHandle); | |
| Print (L"Error Reading %s\n", FileName[Index]); | |
| return EFI_DEVICE_ERROR; | |
| } | |
| // | |
| // If the file size is already 0, then it has been empty. | |
| // | |
| if (FileInfo != 0) { | |
| // | |
| // Set the file size to 0. | |
| // | |
| FileInfo = 0; | |
| Status = FileHandleSetSize (FileHandle, FileInfo); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"Error Deleting %s\n", FileName[Index]); | |
| FileHandleClose (FileHandle); | |
| return Status; | |
| } | |
| } | |
| // | |
| // Write Filebuffer to file | |
| // | |
| Filebuffer = Buffer[Index]; | |
| FileSize = BufferSize[Index]; | |
| Status = FileHandleWrite (FileHandle, &FileSize, Filebuffer); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"Unable to write Capsule Update to %s, Status = %r\n", FileName[Index], Status); | |
| return EFI_NOT_FOUND; | |
| } | |
| Print (L"Succeed to write %s\n", FileName[Index]); | |
| FileHandleClose (FileHandle); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Set capsule status variable. | |
| @param[in] SetCap Set or clear the capsule flag. | |
| @retval EFI_SUCCESS Succeed to set SetCap variable. | |
| @retval others Fail to set the variable. | |
| **/ | |
| EFI_STATUS | |
| SetCapsuleStatusVariable ( | |
| BOOLEAN SetCap | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT64 OsIndication; | |
| UINTN DataSize; | |
| OsIndication = 0; | |
| DataSize = sizeof (UINT64); | |
| Status = gRT->GetVariable ( | |
| L"OsIndications", | |
| &gEfiGlobalVariableGuid, | |
| NULL, | |
| &DataSize, | |
| &OsIndication | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| OsIndication = 0; | |
| } | |
| if (SetCap) { | |
| OsIndication |= ((UINT64)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED); | |
| } else { | |
| OsIndication &= ~((UINT64)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED); | |
| } | |
| Status = gRT->SetVariable ( | |
| L"OsIndications", | |
| &gEfiGlobalVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| sizeof (UINT64), | |
| &OsIndication | |
| ); | |
| return Status; | |
| } | |
| /** | |
| Check if Capsule On Disk is supported. | |
| @retval TRUE Capsule On Disk is supported. | |
| @retval FALSE Capsule On Disk is not supported. | |
| **/ | |
| BOOLEAN | |
| IsCapsuleOnDiskSupported ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT64 OsIndicationsSupported; | |
| UINTN DataSize; | |
| DataSize = sizeof (UINT64); | |
| Status = gRT->GetVariable ( | |
| L"OsIndicationsSupported", | |
| &gEfiGlobalVariableGuid, | |
| NULL, | |
| &DataSize, | |
| &OsIndicationsSupported | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return FALSE; | |
| } | |
| if ((OsIndicationsSupported & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) != 0) { | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| Process Capsule On Disk. | |
| @param[in] CapsuleBuffer An array of pointer to capsule images | |
| @param[in] CapsuleBufferSize An array of UINTN to capsule images size | |
| @param[in] FilePath An array of capsule images file path | |
| @param[in] Map File system mapping string | |
| @param[in] CapsuleNum The count of capsule images | |
| @retval EFI_SUCCESS Capsule on disk success. | |
| @retval others Capsule on disk fail. | |
| **/ | |
| EFI_STATUS | |
| ProcessCapsuleOnDisk ( | |
| IN VOID **CapsuleBuffer, | |
| IN UINTN *CapsuleBufferSize, | |
| IN CHAR16 **FilePath, | |
| IN CHAR16 *Map, | |
| IN UINTN CapsuleNum | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT16 BootNext; | |
| EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs; | |
| BOOLEAN UpdateBootNext; | |
| CHAR16 *FileName[MAX_CAPSULE_NUM]; | |
| UINTN Index; | |
| // | |
| // Check if Capsule On Disk is supported | |
| // | |
| if (!IsCapsuleOnDiskSupported ()) { | |
| Print (L"CapsuleApp: Capsule On Disk is not supported.\n"); | |
| return EFI_UNSUPPORTED; | |
| } | |
| // | |
| // Get a valid file system from boot path | |
| // | |
| Fs = NULL; | |
| Status = GetUpdateFileSystem (Map, &BootNext, &Fs, &UpdateBootNext); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"CapsuleApp: cannot find a valid file system on boot devices. Status = %r\n", Status); | |
| return Status; | |
| } | |
| // | |
| // Get file name from file path | |
| // | |
| for (Index = 0; Index < CapsuleNum; Index++) { | |
| FileName[Index] = GetFileNameFromPath (FilePath[Index]); | |
| } | |
| // | |
| // Copy capsule image to '\efi\UpdateCapsule\' | |
| // | |
| Status = WriteUpdateFile (CapsuleBuffer, CapsuleBufferSize, FileName, CapsuleNum, Fs); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"CapsuleApp: capsule image could not be copied for update.\n"); | |
| return Status; | |
| } | |
| // | |
| // Set variable then reset | |
| // | |
| Status = SetCapsuleStatusVariable (TRUE); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"CapsuleApp: unable to set OSIndication variable.\n"); | |
| return Status; | |
| } | |
| if (UpdateBootNext) { | |
| Status = gRT->SetVariable ( | |
| L"BootNext", | |
| &gEfiGlobalVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| sizeof (UINT16), | |
| &BootNext | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Print (L"CapsuleApp: unable to set BootNext variable.\n"); | |
| return Status; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } |