mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-18 06:52:00 +00:00

Deleting boot entries from EFI sometimes triggers problems on some firmware. We don't actually need to do it from the EFI binary, and it's perfectly safe to leave it in the boot list. It also means when doing multiple updates over several months we're not creating, deleting, creating, deleting and can just re-use the same BootXXXX number each time. It also makes the EFI binary simpler, which is good.
620 lines
16 KiB
C
620 lines
16 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_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;
|
|
|
|
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;
|
|
}
|