fwupd/plugins/uefi/efi/fwupdate.c
2019-04-08 12:47:53 +01:00

755 lines
19 KiB
C

/*
* Copyright (C) 2014-2018 Red Hat, Inc.
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include <efi.h>
#include <efilib.h>
#include "fwup-cleanups.h"
#include "fwup-common.h"
#include "fwup-efi.h"
#include "fwup-debug.h"
#define UNUSED __attribute__((__unused__))
#define GNVN_BUF_SIZE 1024
#define FWUP_NUM_CAPSULE_UPDATES_MAX 128
typedef struct {
CHAR16 *name;
UINT32 attrs;
UINTN size;
FWUP_UPDATE_INFO *info;
} FWUP_UPDATE_TABLE;
static VOID
fwup_update_table_free(FWUP_UPDATE_TABLE *update)
{
FreePool(update->info);
FreePool(update->name);
FreePool(update);
}
_DEFINE_CLEANUP_FUNCTION0(FWUP_UPDATE_TABLE *, _fwup_update_table_free_p, fwup_update_table_free)
#define _cleanup_update_table __attribute__ ((cleanup(_fwup_update_table_free_p)))
#define SECONDS 1000000
static INTN
fwup_dp_size(EFI_DEVICE_PATH *dp, INTN limit)
{
INTN ret = 0;
while (1) {
if (limit < 4)
break;
INTN nodelen = DevicePathNodeLength(dp);
if (nodelen > limit)
break;
limit -= nodelen;
ret += nodelen;
if (IsDevicePathEnd(dp))
return ret;
dp = NextDevicePathNode(dp);
}
return -1;
}
static EFI_STATUS
fwup_populate_update_info(CHAR16 *name, FWUP_UPDATE_TABLE *info_out)
{
EFI_STATUS rc;
FWUP_UPDATE_INFO *info = NULL;
UINTN info_size = 0;
UINT32 attrs = 0;
VOID *info_ptr = NULL;
rc = fwup_get_variable(name, &fwupdate_guid, &info_ptr, &info_size, &attrs);
if (EFI_ERROR(rc))
return rc;
info = (FWUP_UPDATE_INFO *)info_ptr;
if (info_size < sizeof(*info)) {
fwup_warning(L"Update '%s' is is too small", name);
return EFI_INVALID_PARAMETER;
}
if (info_size - sizeof(EFI_DEVICE_PATH) <= sizeof(*info)) {
fwup_warning(L"Update '%s' is malformed, "
L"and cannot hold a file path", name);
return EFI_INVALID_PARAMETER;
}
EFI_DEVICE_PATH *hdr = (EFI_DEVICE_PATH *)&info->dp;
INTN is = EFI_FIELD_OFFSET(FWUP_UPDATE_INFO, dp);
if (is > (INTN)info_size) {
fwup_warning(L"Update '%s' has an invalid file path, "
L"device path offset is %d, but total size is %d",
name, is, info_size);
return EFI_INVALID_PARAMETER;
}
is = info_size - is;
INTN sz = fwup_dp_size(hdr, info_size);
if (sz < 0 || is > (INTN)info_size || is != sz) {
fwup_warning(L"Update '%s' has an invalid file path, "
L"update info size: %d dp size: %d size for dp: %d",
name, info_size, sz, is);
return EFI_INVALID_PARAMETER;
}
info_out->info = info;
info_out->size = info_size;
info_out->attrs = attrs;
info_out->name = StrDuplicate(name);
if (info_out->name == NULL) {
fwup_warning(L"Could not allocate %d", StrSize(name));
return EFI_OUT_OF_RESOURCES;
}
return EFI_SUCCESS;
}
static EFI_STATUS
fwup_populate_update_table(FWUP_UPDATE_TABLE **updates, UINTN *n_updates_out)
{
EFI_GUID vendor_guid = empty_guid;
EFI_STATUS rc;
UINTN n_updates = 0;
_cleanup_free CHAR16 *variable_name = NULL;
/* How much do we trust "size of the VariableName buffer" to mean
* sizeof(vn) and not sizeof(vn)/sizeof(vn[0]) ? */
variable_name = fwup_malloc0(GNVN_BUF_SIZE * 2);
if (variable_name == NULL)
return EFI_OUT_OF_RESOURCES;
while (1) {
UINTN variable_name_size = GNVN_BUF_SIZE;
rc = uefi_call_wrapper(RT->GetNextVariableName, 3,
&variable_name_size, variable_name,
&vendor_guid);
if (rc == EFI_NOT_FOUND)
break;
/* ignore any huge names */
if (rc == EFI_BUFFER_TOO_SMALL)
continue;
if (EFI_ERROR(rc)) {
fwup_warning(L"Could not get variable name: %r", rc);
return rc;
}
/* not one of our state variables */
if (CompareGuid(&vendor_guid, &fwupdate_guid))
continue;
/* ignore debugging settings */
if (StrCmp(variable_name, L"FWUPDATE_VERBOSE") == 0 ||
StrCmp(variable_name, L"FWUPDATE_DEBUG_LOG") == 0)
continue;
if (n_updates > FWUP_NUM_CAPSULE_UPDATES_MAX) {
fwup_warning(L"Ignoring update %s", variable_name);
continue;
}
fwup_info(L"Found update %s", variable_name);
_cleanup_update_table FWUP_UPDATE_TABLE *update = fwup_malloc0(sizeof(FWUP_UPDATE_TABLE));
if (update == NULL)
return EFI_OUT_OF_RESOURCES;
rc = fwup_populate_update_info(variable_name, update);
if (EFI_ERROR(rc)) {
fwup_delete_variable(variable_name, &fwupdate_guid);
fwup_warning(L"Could not populate update info for '%s'", variable_name);
return rc;
}
if (update->info->status & FWUPDATE_ATTEMPT_UPDATE) {
fwup_time(&update->info->time_attempted);
update->info->status = FWUPDATE_ATTEMPTED;
updates[n_updates++] = _steal_pointer(&update);
}
}
*n_updates_out = n_updates;
return EFI_SUCCESS;
}
static EFI_STATUS
fwup_search_file(EFI_DEVICE_PATH **file_dp, EFI_FILE_HANDLE *fh)
{
EFI_DEVICE_PATH *dp, *parent_dp;
EFI_GUID sfsp = SIMPLE_FILE_SYSTEM_PROTOCOL;
EFI_GUID dpp = DEVICE_PATH_PROTOCOL;
UINTN n_handles, count;
EFI_STATUS rc;
_cleanup_free EFI_FILE_HANDLE *devices = NULL;
rc = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &sfsp,
NULL, &n_handles, (EFI_HANDLE **)&devices);
if (EFI_ERROR(rc)) {
fwup_warning(L"Could not find handles");
return rc;
}
dp = *file_dp;
fwup_debug(L"Searching Device Path: %s...", DevicePathToStr(dp));
parent_dp = DuplicateDevicePath(dp);
if (parent_dp == NULL)
return EFI_INVALID_PARAMETER;
dp = parent_dp;
count = 0;
while (1) {
if (IsDevicePathEnd(dp))
return EFI_INVALID_PARAMETER;
if (DevicePathType(dp) == MEDIA_DEVICE_PATH &&
DevicePathSubType(dp) == MEDIA_FILEPATH_DP)
break;
dp = NextDevicePathNode(dp);
++count;
}
SetDevicePathEndNode(dp);
fwup_debug(L"Device Path prepared: %s", DevicePathToStr(parent_dp));
for (UINTN i = 0; i < n_handles; i++) {
EFI_DEVICE_PATH *path;
rc = uefi_call_wrapper(BS->HandleProtocol, 3, devices[i], &dpp,
(VOID **)&path);
if (EFI_ERROR(rc))
continue;
fwup_debug(L"Device supporting SFSP: %s", DevicePathToStr(path));
while (!IsDevicePathEnd(path)) {
fwup_debug(L"Comparing: %s and %s",
DevicePathToStr(parent_dp),
DevicePathToStr(path));
if (LibMatchDevicePaths(path, parent_dp) == TRUE) {
*fh = devices[i];
for (UINTN j = 0; j < count; j++)
*file_dp = NextDevicePathNode(*file_dp);
fwup_debug(L"Match up! Returning %s",
DevicePathToStr(*file_dp));
return EFI_SUCCESS;
}
path = NextDevicePathNode(path);
}
}
fwup_warning(L"Failed to find '%s' DevicePath", DevicePathToStr(*file_dp));
return EFI_UNSUPPORTED;
}
static EFI_STATUS
fwup_open_file(EFI_DEVICE_PATH *dp, EFI_FILE_HANDLE *fh)
{
CONST UINTN devpath_max_size = 1024; /* arbitrary limit */
EFI_DEVICE_PATH *file_dp = dp;
EFI_GUID sfsp = SIMPLE_FILE_SYSTEM_PROTOCOL;
EFI_FILE_HANDLE device;
EFI_FILE_IO_INTERFACE *drive;
EFI_FILE_HANDLE root;
EFI_STATUS rc;
rc = uefi_call_wrapper(BS->LocateDevicePath, 3, &sfsp, &file_dp,
(EFI_HANDLE *)&device);
if (EFI_ERROR(rc)) {
rc = fwup_search_file(&file_dp, &device);
if (EFI_ERROR(rc)) {
fwup_warning(L"Could not locate device handle: %r", rc);
return rc;
}
}
if (DevicePathType(file_dp) != MEDIA_DEVICE_PATH ||
DevicePathSubType(file_dp) != MEDIA_FILEPATH_DP) {
fwup_warning(L"Could not find appropriate device");
return EFI_UNSUPPORTED;
}
UINT16 sz16;
UINTN sz;
CopyMem(&sz16, &file_dp->Length[0], sizeof(sz16));
sz = sz16;
sz -= 4;
if (sz <= 6 || sz % 2 != 0 ||
sz > devpath_max_size * sizeof(CHAR16)) {
fwup_warning(L"Invalid file device path of size %d", sz);
return EFI_INVALID_PARAMETER;
}
_cleanup_free CHAR16 *filename = fwup_malloc0(sz + sizeof(CHAR16));
CopyMem(filename, (UINT8 *)file_dp + 4, sz);
rc = uefi_call_wrapper(BS->HandleProtocol, 3, device, &sfsp,
(VOID **)&drive);
if (EFI_ERROR(rc)) {
fwup_warning(L"Could not open device interface: %r", rc);
return rc;
}
fwup_debug(L"Found device");
rc = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root);
if (EFI_ERROR(rc)) {
fwup_warning(L"Could not open volume: %r", rc);
return rc;
}
fwup_debug(L"Found volume");
rc = uefi_call_wrapper(root->Open, 5, root, fh, filename,
EFI_FILE_MODE_READ, 0);
if (EFI_ERROR(rc)) {
fwup_warning(L"Could not open file '%s': %r", filename, rc);
return rc;
}
fwup_debug(L"Found file");
return EFI_SUCCESS;
}
static EFI_STATUS
fwup_delete_boot_order(CHAR16 *name, EFI_GUID guid)
{
UINT16 boot_num;
EFI_STATUS rc;
UINTN info_size = 0;
UINT32 attrs = 0;
_cleanup_free VOID *info_ptr = NULL;
_cleanup_free UINT16 *new_info_ptr = NULL;
UINT8 num_found = FALSE;
UINTN new_list_num = 0;
/* get boot hex number */
boot_num = xtoi((CHAR16 *)((UINT8 *)name + sizeof(L"Boot")));
rc = fwup_get_variable(L"BootOrder", &guid, &info_ptr, &info_size, &attrs);
if (EFI_ERROR(rc))
return rc;
new_info_ptr = fwup_malloc(info_size);
if (new_info_ptr == NULL)
return EFI_OUT_OF_RESOURCES;
for (UINTN i = 0; i < (info_size / sizeof(UINT16)) ; i++) {
if (((UINT16 *)info_ptr)[i] != boot_num) {
new_info_ptr[i] = ((UINT16 *)info_ptr)[i];
new_list_num++;
} else {
num_found = TRUE;
}
}
/* if not in the BootOrder list, do not update BootOrder */
if (!num_found)
return EFI_SUCCESS;
rc = uefi_call_wrapper(RT->SetVariable, 5, L"BootOrder", &guid,
attrs, new_list_num * sizeof(UINT16),
new_info_ptr);
if (EFI_ERROR(rc)) {
fwup_warning(L"Could not update variable status for '%s': %r",
name, rc);
return rc;
}
return rc;
}
/* TODO: move to gnu-efi: https://github.com/vathpela/gnu-efi/issues/7 */
static BOOLEAN
_StrHasPrefix(IN CONST CHAR16 *s1, IN CONST CHAR16 *s2)
{
while (*s2) {
if (*s1 == L'\0' || *s1 != *s2)
return FALSE;
s1 += 1;
s2 += 1;
}
return TRUE;
}
static EFI_STATUS
fwup_delete_boot_entry(VOID)
{
EFI_STATUS rc;
UINTN variable_name_size = 0;
_cleanup_free CHAR16 *variable_name = NULL;
EFI_GUID vendor_guid = empty_guid;
variable_name = fwup_malloc0(GNVN_BUF_SIZE * 2);
if (variable_name == NULL)
return EFI_OUT_OF_RESOURCES;
while (1) {
variable_name_size = GNVN_BUF_SIZE;
rc = uefi_call_wrapper(RT->GetNextVariableName, 3,
&variable_name_size, variable_name,
&vendor_guid);
if (rc == EFI_NOT_FOUND)
break;
/* ignore any huge names */
if (rc == EFI_BUFFER_TOO_SMALL)
continue;
if (EFI_ERROR(rc)) {
fwup_warning(L"Could not get variable name: %r", rc);
return rc;
}
/* check if the variable name is Boot#### */
if (CompareGuid(&vendor_guid, &global_variable_guid) != 0)
continue;
if (StrCmp(variable_name, L"Boot") != 0)
continue;
UINTN info_size = 0;
_cleanup_free VOID *info_ptr = NULL;
/* get the data */
rc = fwup_get_variable(variable_name, &vendor_guid,
&info_ptr, &info_size, NULL);
if (EFI_ERROR(rc))
return rc;
if (info_size < sizeof(EFI_LOAD_OPTION))
continue;
/*
* check if the boot path created by fwupdate,
* check with EFI_LOAD_OPTION description
*/
EFI_LOAD_OPTION *load_op = (EFI_LOAD_OPTION *) info_ptr;
if (_StrHasPrefix(load_op->description, L"Linux Firmware Updater") ||
_StrHasPrefix(load_op->description, L"Linux-Firmware-Updater")) {
/* delete the boot path from BootOrder list */
rc = fwup_delete_boot_order(variable_name, vendor_guid);
if (EFI_ERROR(rc)) {
fwup_warning(L"Failed to delete boot entry from BootOrder");
return rc;
}
rc = fwup_delete_variable(variable_name, &vendor_guid);
if (EFI_ERROR(rc)) {
fwup_warning(L"Failed to delete boot entry");
return rc;
}
break;
}
}
return EFI_SUCCESS;
}
static EFI_STATUS
fwup_get_gop_mode(UINT32 *mode, EFI_HANDLE loaded_image)
{
EFI_HANDLE *handles, gop_handle;
UINTN num_handles;
EFI_STATUS status;
EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
VOID *iface;
status = LibLocateHandle(ByProtocol, &gop_guid, NULL, &num_handles,
&handles);
if (EFI_ERROR(status))
return status;
if (handles == NULL || num_handles == 0)
return EFI_UNSUPPORTED;
for (UINTN i = 0; i < num_handles; i++) {
gop_handle = handles[i];
status = uefi_call_wrapper(BS->OpenProtocol, 6,
gop_handle, &gop_guid, &iface,
loaded_image, 0,
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (EFI_ERROR(status))
continue;
gop = (EFI_GRAPHICS_OUTPUT_PROTOCOL *)iface;
*mode = gop->Mode->Mode;
return EFI_SUCCESS;
}
return EFI_UNSUPPORTED;
}
static inline void
fwup_update_ux_capsule_checksum(UX_CAPSULE_HEADER *payload_hdr)
{
UINT8 *buf = (UINT8 *)payload_hdr;
UINT8 sum = 0;
payload_hdr->checksum = 0;
for (UINTN i = 0; i < sizeof(*payload_hdr); i++)
sum = (UINT8) (sum + buf[i]);
payload_hdr->checksum = sum;
}
static EFI_STATUS
fwup_check_gop_for_ux_capsule(EFI_HANDLE loaded_image,
EFI_CAPSULE_HEADER *capsule)
{
UX_CAPSULE_HEADER *payload_hdr;
EFI_STATUS rc;
payload_hdr = (UX_CAPSULE_HEADER *) (((UINT8 *) capsule) + capsule->HeaderSize);
rc = fwup_get_gop_mode(&payload_hdr->mode, loaded_image);
if (EFI_ERROR(rc))
return EFI_UNSUPPORTED;
fwup_update_ux_capsule_checksum(payload_hdr);
return EFI_SUCCESS;
}
static EFI_STATUS
fwup_add_update_capsule(FWUP_UPDATE_TABLE *update, EFI_CAPSULE_HEADER **capsule_out,
EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd_out, EFI_HANDLE loaded_image)
{
EFI_STATUS rc;
EFI_FILE_HANDLE fh = NULL;
UINT8 *fbuf = NULL;
UINTN fsize = 0;
EFI_CAPSULE_HEADER *capsule;
UINTN cbd_len;
EFI_PHYSICAL_ADDRESS cbd_data;
EFI_CAPSULE_HEADER *cap_out;
rc = fwup_open_file((EFI_DEVICE_PATH *)update->info->dp_buf, &fh);
if (EFI_ERROR(rc))
return rc;
rc = fwup_read_file(fh, &fbuf, &fsize);
if (EFI_ERROR(rc))
return rc;
uefi_call_wrapper(fh->Close, 1, fh);
if (fsize < sizeof(EFI_CAPSULE_HEADER)) {
fwup_warning(L"Invalid capsule size %d", fsize);
return EFI_INVALID_PARAMETER;
}
fwup_debug(L"Read file; %d bytes", fsize);
fwup_debug(L"updates guid: %g", &update->info->guid);
fwup_debug(L"File guid: %g", fbuf);
cbd_len = fsize;
cbd_data = (EFI_PHYSICAL_ADDRESS)(UINTN)fbuf;
capsule = cap_out = (EFI_CAPSULE_HEADER *)fbuf;
if (cap_out->Flags == 0 &&
CompareGuid(&update->info->guid, &ux_capsule_guid) != 0) {
#if defined(__aarch64__)
cap_out->Flags |= update->info->capsule_flags;
#else
cap_out->Flags |= update->info->capsule_flags |
CAPSULE_FLAGS_PERSIST_ACROSS_RESET |
CAPSULE_FLAGS_INITIATE_RESET;
#endif
}
if (CompareGuid(&update->info->guid, &ux_capsule_guid) == 0) {
fwup_debug(L"Checking GOP for ux capsule");
rc = fwup_check_gop_for_ux_capsule(loaded_image, capsule);
if (EFI_ERROR(rc))
return EFI_UNSUPPORTED;
}
cbd_out->Length = cbd_len;
cbd_out->Union.DataBlock = cbd_data;
*capsule_out = cap_out;
return EFI_SUCCESS;
}
static EFI_STATUS
fwup_apply_capsules(EFI_CAPSULE_HEADER **capsules,
EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd,
UINTN num_updates, EFI_RESET_TYPE *reset)
{
UINT64 max_capsule_size;
EFI_STATUS rc;
/* still try to apply capsule on failure */
rc = fwup_delete_boot_entry();
if (EFI_ERROR(rc))
fwup_warning(L"Could not delete boot entry: %r", rc);
rc = uefi_call_wrapper(RT->QueryCapsuleCapabilities, 4, capsules,
num_updates, &max_capsule_size, reset);
if (EFI_ERROR(rc)) {
fwup_warning(L"Could not query capsule capabilities: %r", rc);
return rc;
}
fwup_debug(L"QueryCapsuleCapabilities: %r max: %ld reset:%d",
rc, max_capsule_size, *reset);
fwup_debug(L"Capsules: %d", num_updates);
fwup_msleep(1 * SECONDS);
rc = uefi_call_wrapper(RT->UpdateCapsule, 3, capsules, num_updates,
(EFI_PHYSICAL_ADDRESS)(UINTN)cbd);
if (EFI_ERROR(rc)) {
fwup_warning(L"Could not apply capsule update: %r", rc);
return rc;
}
return EFI_SUCCESS;
}
static EFI_STATUS
fwup_set_update_statuses(FWUP_UPDATE_TABLE **updates)
{
EFI_STATUS rc;
for (UINTN i = 0; i < FWUP_NUM_CAPSULE_UPDATES_MAX; i++) {
if (updates[i] == NULL || updates[i]->name == NULL)
break;
rc = fwup_set_variable(updates[i]->name, &fwupdate_guid,
updates[i]->info, updates[i]->size,
updates[i]->attrs);
if (EFI_ERROR(rc)) {
fwup_warning(L"Could not update variable status for '%s': %r",
updates[i]->name, rc);
return rc;
}
}
return EFI_SUCCESS;
}
EFI_GUID SHIM_LOCK_GUID =
{0x605dab50,0xe046,0x4300,{0xab,0xb6,0x3d,0xd8,0x10,0xdd,0x8b,0x23}};
static VOID
__attribute__((__optimize__("0")))
fwup_debug_hook(VOID)
{
EFI_GUID guid = SHIM_LOCK_GUID;
UINTN data = 0;
UINTN data_size = 1;
EFI_STATUS efi_status;
UINT32 attrs;
register volatile int x = 0;
extern char _text UNUSED, _data UNUSED;
/* shim has done whatever is needed to get a debugger attached */
efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"SHIM_DEBUG",
&guid, &attrs, &data_size, &data);
if (EFI_ERROR(efi_status) || data != 1) {
efi_status = uefi_call_wrapper(RT->GetVariable, 5,
L"FWUPDATE_VERBOSE",
&fwupdate_guid, &attrs,
&data_size, &data);
if (EFI_ERROR(efi_status) || data != 1)
return;
fwup_debug_set_enabled(TRUE);
return;
}
fwup_debug_set_enabled(TRUE);
if (x)
return;
x = 1;
fwup_info(L"add-symbol-file "DEBUGDIR
L"fwupdate.efi.debug %p -s .data %p",
&_text, &_data);
}
EFI_STATUS
efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
{
EFI_STATUS rc;
UINTN i, n_updates = 0;
EFI_RESET_TYPE reset_type = EfiResetWarm;
_cleanup_free FWUP_UPDATE_TABLE **updates = NULL;
InitializeLib(image, systab);
/* if SHIM_DEBUG is set, fwup_info info for our attached debugger */
fwup_debug_hook();
/* step 1: find and validate update state variables */
/* XXX TODO:
* 1) survey the reset types first, and separate into groups
* according to them
* 2) if there's more than one, mirror BootCurrent back into BootNext
* so we can do multiple runs
* 3) only select the ones from one type for the first go
*/
updates = fwup_new0(FWUP_UPDATE_TABLE *, FWUP_NUM_CAPSULE_UPDATES_MAX);
if (updates == NULL)
return EFI_OUT_OF_RESOURCES;
rc = fwup_populate_update_table(updates, &n_updates);
if (EFI_ERROR(rc)) {
fwup_warning(L"Could not find updates: %r", rc);
return rc;
}
if (n_updates == 0) {
fwup_warning(L"No updates to process. Called in error?");
return EFI_INVALID_PARAMETER;
}
/* step 2: Build our data structure and add the capsules to it */
_cleanup_free EFI_CAPSULE_HEADER **capsules = NULL;
capsules = fwup_new0(EFI_CAPSULE_HEADER *, n_updates + 1);
EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd_data;
UINTN j = 0;
cbd_data = fwup_malloc_raw(sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR)*(n_updates+1));
if (cbd_data == NULL)
return EFI_OUT_OF_RESOURCES;
for (i = 0; i < n_updates; i++) {
fwup_info(L"Adding new capsule");
rc = fwup_add_update_capsule(updates[i], &capsules[j], &cbd_data[j], image);
if (EFI_ERROR(rc)) {
/* ignore a failing UX capsule */
if (rc == EFI_UNSUPPORTED &&
CompareGuid(&updates[i]->info->guid, &ux_capsule_guid) == 0) {
fwup_debug(L"GOP unsuitable: %r", rc);
continue;
}
fwup_warning(L"Could not build update list: %r", rc);
return rc;
}
j++;
}
n_updates = j;
fwup_debug(L"n_updates: %d", n_updates);
cbd_data[i].Length = 0;
cbd_data[i].Union.ContinuationPointer = 0;
/* step 3: update the state variables */
rc = fwup_set_update_statuses(updates);
if (EFI_ERROR(rc)) {
fwup_warning(L"Could not set update status: %r", rc);
return rc;
}
/* step 4: apply the capsules */
rc = fwup_apply_capsules(capsules, cbd_data, n_updates, &reset_type);
if (EFI_ERROR(rc)) {
fwup_warning(L"Could not apply capsules: %r", rc);
return rc;
}
/* step 5: if #4 didn't reboot us, do it manually */
fwup_info(L"Reset System");
fwup_msleep(5 * SECONDS);
if (fwup_debug_get_enabled())
fwup_msleep(30 * SECONDS);
uefi_call_wrapper(RT->ResetSystem, 4, reset_type, EFI_SUCCESS, 0, NULL);
return EFI_SUCCESS;
}