mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-17 19:04:13 +00:00

1) Drop the path at the end of the title (\fwupdx64.efi)
2) Linux-Firmware-Updater to Linux Firmware Updater
The behavior that required the hacky name has been fixed since shim 10
which is in all the relevant distros now.
3322257e61
1336 lines
32 KiB
C
1336 lines
32 KiB
C
/*
|
|
* Copyright (C) 2014-2018 Red Hat, Inc.
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include <efi.h>
|
|
#include <efilib.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include "fwup-efi.h"
|
|
#include "hexdump.h"
|
|
|
|
#define UNUSED __attribute__((__unused__))
|
|
|
|
EFI_GUID empty_guid = {0x0,0x0,0x0,{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0}};
|
|
EFI_GUID fwupdate_guid =
|
|
{0x0abba7dc,0xe516,0x4167,{0xbb,0xf5,0x4d,0x9d,0x1c,0x73,0x94,0x16}};
|
|
EFI_GUID ux_capsule_guid =
|
|
{0x3b8c8162,0x188c,0x46a4,{0xae,0xc9,0xbe,0x43,0xf1,0xd6,0x56,0x97}};
|
|
EFI_GUID fmp_capsule_guid =
|
|
{0x6dcbd5ed,0xe82d,0x4c44,{0xbd,0xa1,0x71,0x94,0x19,0x9a,0xd9,0x2a}};
|
|
EFI_GUID global_variable_guid = EFI_GLOBAL_VARIABLE;
|
|
|
|
|
|
typedef struct update_table_s {
|
|
CHAR16 *name;
|
|
UINT32 attributes;
|
|
UINTN size;
|
|
update_info *info;
|
|
} update_table;
|
|
|
|
static EFI_STATUS
|
|
delete_variable(CHAR16 *name, EFI_GUID guid, UINT32 attributes);
|
|
static EFI_STATUS
|
|
set_variable(CHAR16 *name, EFI_GUID guid, VOID *data, UINTN size,
|
|
UINT32 attrs);
|
|
|
|
static int debugging;
|
|
|
|
#define SECONDS 1000000
|
|
|
|
static VOID
|
|
msleep(unsigned long msecs)
|
|
{
|
|
BS->Stall(msecs);
|
|
}
|
|
|
|
|
|
/*
|
|
* I'm not actually sure when these appear, but they're present in the
|
|
* version in front of me.
|
|
*/
|
|
#if defined(__GNUC__) && defined(__GNUC_MINOR__)
|
|
#if __GNUC__ >= 5 && __GNUC_MINOR__ >= 1
|
|
#define uintn_mult(a, b, c) __builtin_mul_overflow(a, b, c)
|
|
#endif
|
|
#endif
|
|
#ifndef uintn_mult
|
|
#define uintn_mult(a, b, c) ({ \
|
|
const UINTN _limit = ~0UL; \
|
|
int _ret = 1; \
|
|
if ((a) != 0 && (b) != 0) { \
|
|
_ret = _limit / (a) < (b); \
|
|
} \
|
|
if (!_ret) \
|
|
*(c) = ((a) * (b)); \
|
|
_ret; \
|
|
})
|
|
#endif
|
|
|
|
int
|
|
debug_print(const char *func, const char *file, const int line,
|
|
CHAR16 *fmt, ...)
|
|
{
|
|
va_list args0, args1;
|
|
CHAR16 *out0, *out1;
|
|
UINT32 attrs = EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
|
EFI_VARIABLE_RUNTIME_ACCESS;
|
|
CHAR16 *name = L"FWUPDATE_DEBUG_LOG";
|
|
static bool once = true;
|
|
|
|
va_start(args0, fmt);
|
|
out0 = VPoolPrint(fmt, args0);
|
|
va_end(args0);
|
|
if (!out0) {
|
|
if (debugging) {
|
|
va_start(args1, fmt);
|
|
VPrint(fmt, args1);
|
|
va_end(args1);
|
|
msleep(200000);
|
|
}
|
|
Print(L"fwupdate: Allocation for debug log failed!\n");
|
|
return debugging;
|
|
}
|
|
if (debugging)
|
|
Print(L"%s", out0);
|
|
out1 = PoolPrint(L"%a:%d:%a(): %s", file, line, func, out0);
|
|
FreePool(out0);
|
|
if (!out1) {
|
|
Print(L"fwupdate: Allocation for debug log failed!\n");
|
|
return debugging;
|
|
}
|
|
|
|
if (once) {
|
|
once = false;
|
|
delete_variable(name, fwupdate_guid, attrs);
|
|
} else {
|
|
attrs |= EFI_VARIABLE_APPEND_WRITE;
|
|
}
|
|
set_variable(name, fwupdate_guid, out1, StrSize(out1) - sizeof (CHAR16), attrs);
|
|
|
|
FreePool(out1);
|
|
|
|
return debugging;
|
|
}
|
|
|
|
#define dprint(fmt, args...) debug_print(__func__, __FILE__, __LINE__, fmt, ## args )
|
|
#define print(fmt, args...) ({ if (!dprint(fmt, ## args)) Print(fmt, ## args); })
|
|
|
|
/*
|
|
* Allocate some raw pages that aren't part of the pool allocator.
|
|
*/
|
|
static EFI_STATUS
|
|
allocate(void **addr, UINTN size)
|
|
{
|
|
/*
|
|
* We're actually guaranteed that page size is 4096 by UEFI.
|
|
*/
|
|
UINTN pages = size / 4096 + ((size % 4096) ? 1 : 0);
|
|
EFI_STATUS rc;
|
|
EFI_PHYSICAL_ADDRESS pageaddr = 0;
|
|
EFI_ALLOCATE_TYPE type = AllocateAnyPages;
|
|
|
|
if (sizeof (VOID *) == 4) {
|
|
pageaddr = 0xffffffffULL - 8192;
|
|
type = AllocateMaxAddress;
|
|
}
|
|
|
|
rc = uefi_call_wrapper(BS->AllocatePages, 4, type,
|
|
EfiLoaderData, pages,
|
|
&pageaddr);
|
|
if (EFI_ERROR(rc))
|
|
return rc;
|
|
if (sizeof (VOID *) == 4 && pageaddr > 0xffffffffULL) {
|
|
uefi_call_wrapper(BS->FreePages, 2, pageaddr, pages);
|
|
print(L"Got bad allocation at 0x%016x\n", (UINT64)pageaddr);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
*addr = (void *)(UINTN)pageaddr;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Free our raw page allocations.
|
|
*/
|
|
static EFI_STATUS
|
|
free(void *addr, UINTN size)
|
|
{
|
|
UINTN pages = size / 4096 + ((size % 4096) ? 1 : 0);
|
|
EFI_STATUS rc;
|
|
|
|
rc = uefi_call_wrapper(BS->FreePages, 2,
|
|
(EFI_PHYSICAL_ADDRESS)(UINTN)addr,
|
|
pages);
|
|
return rc;
|
|
}
|
|
|
|
static inline int
|
|
guid_cmp(efi_guid_t *a, efi_guid_t *b)
|
|
{
|
|
return CompareMem(a, b, sizeof (*a));
|
|
}
|
|
|
|
EFI_STATUS
|
|
read_file(EFI_FILE_HANDLE fh, UINT8 **buf_out, UINTN *buf_size_out)
|
|
{
|
|
UINT8 *b = NULL;
|
|
const UINTN bs = 512;
|
|
UINTN n_blocks = 4096;
|
|
UINTN i = 0;
|
|
EFI_STATUS rc;
|
|
|
|
while (1) {
|
|
void *newb = NULL;
|
|
UINTN news = 0;
|
|
if (uintn_mult(bs * 2, n_blocks, &news)) {
|
|
if (b)
|
|
free(b, bs * n_blocks);
|
|
print(L"allocation %d * %d would overflow size\n",
|
|
bs * 2, n_blocks);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
rc = allocate(&newb, news);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Tried to allocate %d\n",
|
|
bs * n_blocks * 2);
|
|
print(L"Could not allocate memory.\n");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
if (b) {
|
|
CopyMem(newb, b, bs * n_blocks);
|
|
free(b, bs * n_blocks);
|
|
}
|
|
b = newb;
|
|
n_blocks *= 2;
|
|
|
|
for (; i < n_blocks; i++) {
|
|
EFI_STATUS rc;
|
|
UINTN sz = bs;
|
|
|
|
rc = uefi_call_wrapper(fh->Read, 3, fh, &sz,
|
|
&b[i * bs]);
|
|
if (EFI_ERROR(rc)) {
|
|
free(b, bs * n_blocks);
|
|
print(L"Could not read file: %r\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
if (sz != bs) {
|
|
*buf_size_out = bs * i + sz;
|
|
*buf_out = b;
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static EFI_STATUS
|
|
delete_variable(CHAR16 *name, EFI_GUID guid, UINT32 attributes)
|
|
{
|
|
return uefi_call_wrapper(RT->SetVariable, 5, name, &guid, attributes,
|
|
0, NULL);
|
|
}
|
|
|
|
static EFI_STATUS
|
|
set_variable(CHAR16 *name, EFI_GUID guid, VOID *data, UINTN size,
|
|
UINT32 attrs)
|
|
{
|
|
return uefi_call_wrapper(RT->SetVariable, 5, name, &guid, attrs,
|
|
size, data);
|
|
}
|
|
|
|
static EFI_STATUS
|
|
read_variable(CHAR16 *name, EFI_GUID guid, void **buf_out, UINTN *buf_size_out,
|
|
UINT32 *attributes_out)
|
|
{
|
|
EFI_STATUS rc;
|
|
UINT32 attributes;
|
|
UINTN size = 0;
|
|
void *buf = NULL;
|
|
|
|
rc = uefi_call_wrapper(RT->GetVariable, 5, name,
|
|
&guid, &attributes, &size, NULL);
|
|
if (EFI_ERROR(rc)) {
|
|
if (rc == EFI_BUFFER_TOO_SMALL) {
|
|
buf = AllocatePool(size);
|
|
if (!buf) {
|
|
print(L"Tried to allocate %d\n", size);
|
|
print(L"Could not allocate memory.\n");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
} else if (rc != EFI_NOT_FOUND) {
|
|
print(L"Could not get variable \"%s\": %r\n", name, rc);
|
|
return rc;
|
|
}
|
|
} else {
|
|
print(L"GetVariable(%s) succeeded with size=0.\n", name);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
rc = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, &attributes,
|
|
&size, buf);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Could not get variable \"%s\": %r\n", name, rc);
|
|
FreePool(buf);
|
|
return rc;
|
|
}
|
|
*buf_out = buf;
|
|
*buf_size_out = size;
|
|
*attributes_out = attributes;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static INTN
|
|
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
|
|
get_info(CHAR16 *name, update_table *info_out)
|
|
{
|
|
EFI_STATUS rc;
|
|
update_info *info = NULL;
|
|
UINTN info_size = 0;
|
|
UINT32 attributes = 0;
|
|
void *info_ptr = NULL;
|
|
|
|
rc = read_variable(name, fwupdate_guid, &info_ptr, &info_size,
|
|
&attributes);
|
|
if (EFI_ERROR(rc))
|
|
return rc;
|
|
info = (update_info *)info_ptr;
|
|
|
|
if (info_size < sizeof (*info)) {
|
|
print(L"Update \"%s\" is is too small.\n", name);
|
|
delete_variable(name, fwupdate_guid, attributes);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (info_size - sizeof (EFI_DEVICE_PATH) <= sizeof (*info)) {
|
|
print(L"Update \"%s\" is malformed, "
|
|
L"and cannot hold a file path.\n", name);
|
|
delete_variable(name, fwupdate_guid, attributes);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
EFI_DEVICE_PATH *hdr = (EFI_DEVICE_PATH *)&info->dp;
|
|
INTN is = EFI_FIELD_OFFSET(update_info, dp);
|
|
if (is > (INTN)info_size) {
|
|
print(L"Update \"%s\" has an invalid file path.\n"
|
|
L"Device path offset is %d, but total size is %d\n",
|
|
name, is, info_size);
|
|
delete_variable(name, fwupdate_guid, attributes);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
is = info_size - is;
|
|
INTN sz = dp_size(hdr, info_size);
|
|
if (sz < 0 || is < 0) {
|
|
invalid_size:
|
|
print(L"Update \"%s\" has an invalid file path.\n"
|
|
L"update info size: %d dp size: %d size for dp: %d\n",
|
|
name, info_size, sz, is);
|
|
delete_variable(name, fwupdate_guid, attributes);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
if (is > (INTN)info_size)
|
|
goto invalid_size;
|
|
if (is != sz)
|
|
goto invalid_size;
|
|
|
|
info_out->info = info;
|
|
info_out->size = info_size;
|
|
info_out->attributes = attributes;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static EFI_STATUS
|
|
find_updates(UINTN *n_updates_out, update_table ***updates_out)
|
|
{
|
|
EFI_STATUS rc;
|
|
update_table **updates = NULL;
|
|
UINTN n_updates = 0;
|
|
UINTN n_updates_allocated = 128;
|
|
EFI_STATUS ret = EFI_OUT_OF_RESOURCES;
|
|
|
|
#define GNVN_BUF_SIZE 1024
|
|
UINTN variable_name_allocation = GNVN_BUF_SIZE;
|
|
UINTN variable_name_size = 0;
|
|
CHAR16 *variable_name;
|
|
EFI_GUID vendor_guid = empty_guid;
|
|
UINTN mult_res;
|
|
|
|
if (uintn_mult(sizeof (update_table *), n_updates_allocated,
|
|
&mult_res)) {
|
|
print(L"Allocation %d * %d would overflow size\n",
|
|
sizeof (update_table *), n_updates_allocated);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
updates = AllocateZeroPool(mult_res);
|
|
if (!updates) {
|
|
print(L"Tried to allocate %d\n", mult_res);
|
|
print(L"Could not allocate memory.\n");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
/* How much do we trust "size of the VariableName buffer" to mean
|
|
* sizeof(vn) and not sizeof(vn)/sizeof(vn[0]) ? */
|
|
variable_name = AllocateZeroPool(GNVN_BUF_SIZE * 2);
|
|
if (!variable_name) {
|
|
print(L"Tried to allocate %d\n", GNVN_BUF_SIZE * 2);
|
|
print(L"Could not allocate memory.\n");
|
|
FreePool(updates);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
while (1) {
|
|
variable_name_size = variable_name_allocation;
|
|
rc = uefi_call_wrapper(RT->GetNextVariableName, 3,
|
|
&variable_name_size, variable_name,
|
|
&vendor_guid);
|
|
if (rc == EFI_BUFFER_TOO_SMALL) {
|
|
/* If we don't have a big enough buffer to hold the
|
|
* name, allocate a bigger one and try again */
|
|
UINTN new_allocation;
|
|
CHAR16 *new_name;
|
|
|
|
new_allocation = variable_name_size;
|
|
if (uintn_mult(new_allocation, 2, &mult_res)) {
|
|
print(L"%d * 2 would overflow size\n",
|
|
new_allocation);
|
|
ret = EFI_OUT_OF_RESOURCES;
|
|
goto err;
|
|
}
|
|
new_name = AllocatePool(new_allocation * 2);
|
|
if (!new_name) {
|
|
print(L"Tried to allocate %d\n",
|
|
new_allocation * 2);
|
|
print(L"Could not allocate memory.\n");
|
|
ret = EFI_OUT_OF_RESOURCES;
|
|
goto err;
|
|
}
|
|
CopyMem(new_name, variable_name,
|
|
variable_name_allocation);
|
|
variable_name_allocation = new_allocation;
|
|
FreePool(variable_name);
|
|
variable_name = new_name;
|
|
continue;
|
|
} else if (rc == EFI_NOT_FOUND) {
|
|
break;
|
|
} else if (EFI_ERROR(rc)) {
|
|
print(L"Could not get variable name: %r\n", rc);
|
|
ret = rc;
|
|
goto err;
|
|
}
|
|
|
|
/*
|
|
* If it's not one of our state variables, keep going.
|
|
*/
|
|
if (guid_cmp(&vendor_guid, &fwupdate_guid))
|
|
continue;
|
|
|
|
/*
|
|
* Don't delete our debugging settings.
|
|
*/
|
|
if (StrCmp(variable_name, L"FWUPDATE_VERBOSE") == 0 ||
|
|
StrCmp(variable_name, L"FWUPDATE_DEBUG_LOG") == 0)
|
|
continue;
|
|
|
|
UINTN vns = StrLen(variable_name);
|
|
CHAR16 vn[vns + 1];
|
|
CopyMem(vn, variable_name, vns * sizeof (vn[0]));
|
|
vn[vns] = L'\0';
|
|
print(L"Found update %s\n", vn);
|
|
|
|
if (n_updates == n_updates_allocated) {
|
|
update_table **new_ups;
|
|
UINTN mul_a, mul_b;
|
|
if (uintn_mult(n_updates_allocated, 2, &mult_res)) {
|
|
mul_a = n_updates_allocated;
|
|
mul_b = 2;
|
|
mult_err:
|
|
print(L"Allocation %d * %d would overflow size\n",
|
|
mul_a, mul_b);
|
|
ret = EFI_OUT_OF_RESOURCES;
|
|
goto err;
|
|
}
|
|
if (uintn_mult(mult_res, sizeof (update_table *),
|
|
&mult_res)) {
|
|
mul_a = mult_res;
|
|
mul_b = sizeof (update_table *);
|
|
goto mult_err;
|
|
}
|
|
|
|
new_ups = AllocateZeroPool(mult_res);
|
|
if (!new_ups) {
|
|
print(L"Tried to allocate %d\n", mult_res);
|
|
print(L"Could not allocate memory.\n");
|
|
ret = EFI_OUT_OF_RESOURCES;
|
|
goto err;
|
|
}
|
|
CopyMem(new_ups, updates, mult_res);
|
|
n_updates_allocated *= 2;
|
|
FreePool(updates);
|
|
updates = new_ups;
|
|
}
|
|
|
|
update_table *update = AllocatePool(sizeof (update_table));
|
|
if (!update) {
|
|
print(L"Tried to allocate %d\n", sizeof (update_table));
|
|
ret = EFI_OUT_OF_RESOURCES;
|
|
goto err;
|
|
}
|
|
|
|
update->name = StrDuplicate(vn);
|
|
if (!update->name) {
|
|
print(L"Tried to allocate %d\n", StrSize(vn));
|
|
ret = EFI_OUT_OF_RESOURCES;
|
|
FreePool(update);
|
|
goto err;
|
|
}
|
|
|
|
rc = get_info(vn, update);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Could not get update info for \"%s\", aborting.\n", vn);
|
|
ret = rc;
|
|
FreePool(update->name);
|
|
FreePool(update);
|
|
goto err;
|
|
}
|
|
if (update->info->status & FWUPDATE_ATTEMPT_UPDATE) {
|
|
EFI_TIME_CAPABILITIES timecaps = { 0, };
|
|
|
|
uefi_call_wrapper(RT->GetTime, 2,
|
|
&update->info->time_attempted,
|
|
&timecaps);
|
|
update->info->status = FWUPDATE_ATTEMPTED;
|
|
updates[n_updates++] = update;
|
|
} else {
|
|
FreePool(update->info);
|
|
FreePool(update->name);
|
|
FreePool(update);
|
|
}
|
|
}
|
|
|
|
FreePool(variable_name);
|
|
|
|
*n_updates_out = n_updates;
|
|
*updates_out = updates;
|
|
|
|
return EFI_SUCCESS;
|
|
err:
|
|
FreePool(variable_name);
|
|
|
|
for (unsigned int i = 0; i < n_updates; i++) {
|
|
FreePool(updates[i]->name);
|
|
FreePool(updates[i]->info);
|
|
FreePool(updates[i]);
|
|
}
|
|
|
|
FreePool(updates);
|
|
return ret;
|
|
}
|
|
|
|
static EFI_STATUS
|
|
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;
|
|
EFI_FILE_HANDLE *devices;
|
|
UINTN i, n_handles, count;
|
|
EFI_STATUS rc;
|
|
|
|
rc = uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &sfsp,
|
|
NULL, &n_handles, (EFI_HANDLE **)&devices);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Could not find handles.\n");
|
|
return rc;
|
|
}
|
|
|
|
dp = *file_dp;
|
|
|
|
if (debugging)
|
|
print(L"Searching Device Path: %s ...\n", DevicePathToStr(dp));
|
|
|
|
parent_dp = DuplicateDevicePath(dp);
|
|
if (!parent_dp) {
|
|
rc = EFI_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
dp = parent_dp;
|
|
count = 0;
|
|
while (1) {
|
|
if (IsDevicePathEnd(dp)) {
|
|
rc = EFI_INVALID_PARAMETER;
|
|
goto out;
|
|
}
|
|
|
|
if (DevicePathType(dp) == MEDIA_DEVICE_PATH &&
|
|
DevicePathSubType(dp) == MEDIA_FILEPATH_DP)
|
|
break;
|
|
|
|
dp = NextDevicePathNode(dp);
|
|
++count;
|
|
}
|
|
|
|
SetDevicePathEndNode(dp);
|
|
|
|
if (debugging)
|
|
print(L"Device Path prepared: %s\n",
|
|
DevicePathToStr(parent_dp));
|
|
|
|
for (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;
|
|
|
|
if (debugging)
|
|
print(L"Device supporting SFSP: %s\n",
|
|
DevicePathToStr(path));
|
|
|
|
rc = EFI_UNSUPPORTED;
|
|
while (!IsDevicePathEnd(path)) {
|
|
if (debugging)
|
|
print(L"Comparing: %s and %s\n",
|
|
DevicePathToStr(parent_dp),
|
|
DevicePathToStr(path));
|
|
|
|
if (LibMatchDevicePaths(path, parent_dp) == TRUE) {
|
|
*fh = devices[i];
|
|
for (i = 0; i < count; i++)
|
|
*file_dp = NextDevicePathNode(*file_dp);
|
|
rc = EFI_SUCCESS;
|
|
|
|
if (debugging)
|
|
print(L"Match up! Returning %s\n",
|
|
DevicePathToStr(*file_dp));
|
|
|
|
goto out;
|
|
}
|
|
|
|
path = NextDevicePathNode(path);
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (!EFI_ERROR(rc))
|
|
print(L"File %s searched\n", DevicePathToStr(*file_dp));
|
|
|
|
uefi_call_wrapper(BS->FreePool, 1, devices);
|
|
return rc;
|
|
}
|
|
|
|
static EFI_STATUS
|
|
open_file(EFI_DEVICE_PATH *dp, EFI_FILE_HANDLE *fh)
|
|
{
|
|
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 = search_file(&file_dp, &device);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Could not locate device handle: %r\n", rc);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
if (DevicePathType(file_dp) != MEDIA_DEVICE_PATH ||
|
|
DevicePathSubType(file_dp) != MEDIA_FILEPATH_DP) {
|
|
print(L"Could not find appropriate device.\n");
|
|
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) {
|
|
print(L"Invalid file device path.\n");
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
sz /= sizeof (CHAR16);
|
|
/*
|
|
* check against some arbitrary limit to avoid having a stack
|
|
* overflow here.
|
|
*/
|
|
if (sz > 1024) {
|
|
print(L"Invalid file device path.\n");
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
CHAR16 filename[sz+1];
|
|
CopyMem(filename, (UINT8 *)file_dp + 4, sz * sizeof (CHAR16));
|
|
filename[sz] = L'\0';
|
|
|
|
rc = uefi_call_wrapper(BS->HandleProtocol, 3, device, &sfsp,
|
|
(void **)&drive);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Could not open device interface: %r.\n", rc);
|
|
return rc;
|
|
}
|
|
dprint(L"Found device\n");
|
|
|
|
rc = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Could not open volume: %r.\n", rc);
|
|
return rc;
|
|
}
|
|
dprint(L"Found volume\n");
|
|
|
|
rc = uefi_call_wrapper(root->Open, 5, root, fh, filename,
|
|
EFI_FILE_MODE_READ, 0);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Could not open file \"%s\": %r.\n", filename, rc);
|
|
return rc;
|
|
}
|
|
dprint(L"Found file\n");
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static EFI_STATUS
|
|
delete_boot_order(CHAR16 *name, EFI_GUID guid)
|
|
{
|
|
|
|
UINTN i;
|
|
UINT16 boot_num;
|
|
EFI_STATUS rc;
|
|
UINTN info_size = 0;
|
|
UINT32 attributes = 0;
|
|
void *info_ptr = NULL;
|
|
UINT16 *new_info_ptr = NULL;
|
|
BOOLEAN num_found = FALSE;
|
|
UINTN new_list_num = 0;
|
|
|
|
/* get boot hex number */
|
|
boot_num = xtoi((CHAR16 *)((UINT8 *)name + sizeof(L"Boot")));
|
|
|
|
rc = read_variable(L"BootOrder", guid, &info_ptr, &info_size,
|
|
&attributes);
|
|
if (EFI_ERROR(rc))
|
|
return rc;
|
|
|
|
new_info_ptr = AllocatePool(info_size);
|
|
if (!new_info_ptr) {
|
|
print(L"Tried to allocate %d\n", info_size);
|
|
print(L"Could not allocate memory.\n");
|
|
FreePool(info_ptr);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
for (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) {
|
|
rc = EFI_SUCCESS;
|
|
goto out;
|
|
}
|
|
|
|
rc = uefi_call_wrapper(RT->SetVariable, 5, L"BootOrder", &guid,
|
|
attributes, new_list_num * sizeof(UINT16),
|
|
new_info_ptr);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Could not update variable status for \"%s\": %r\n",
|
|
name, rc);
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
|
|
FreePool(info_ptr);
|
|
FreePool(new_info_ptr);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static EFI_STATUS
|
|
delete_boot_entry(void)
|
|
{
|
|
EFI_STATUS rc;
|
|
|
|
UINTN variable_name_allocation = GNVN_BUF_SIZE;
|
|
UINTN variable_name_size = 0;
|
|
CHAR16 *variable_name;
|
|
EFI_GUID vendor_guid = empty_guid;
|
|
UINTN mult_res;
|
|
EFI_STATUS ret = EFI_OUT_OF_RESOURCES;
|
|
|
|
variable_name = AllocateZeroPool(GNVN_BUF_SIZE * 2);
|
|
if (!variable_name) {
|
|
print(L"Tried to allocate %d\n", GNVN_BUF_SIZE * 2);
|
|
print(L"Could not allocate memory.\n");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
while (1) {
|
|
variable_name_size = variable_name_allocation;
|
|
rc = uefi_call_wrapper(RT->GetNextVariableName, 3,
|
|
&variable_name_size, variable_name,
|
|
&vendor_guid);
|
|
if (rc == EFI_BUFFER_TOO_SMALL) {
|
|
|
|
UINTN new_allocation;
|
|
CHAR16 *new_name;
|
|
|
|
new_allocation = variable_name_size;
|
|
if (uintn_mult(new_allocation, 2, &mult_res)) {
|
|
print(L"%d * 2 would overflow size\n",
|
|
new_allocation);
|
|
ret = EFI_OUT_OF_RESOURCES;
|
|
goto err;
|
|
}
|
|
new_name = AllocatePool(new_allocation * 2);
|
|
if (!new_name) {
|
|
print(L"Tried to allocate %d\n",
|
|
new_allocation * 2);
|
|
print(L"Could not allocate memory.\n");
|
|
ret = EFI_OUT_OF_RESOURCES;
|
|
goto err;
|
|
}
|
|
CopyMem(new_name, variable_name,
|
|
variable_name_allocation);
|
|
variable_name_allocation = new_allocation;
|
|
FreePool(variable_name);
|
|
variable_name = new_name;
|
|
continue;
|
|
} else if (rc == EFI_NOT_FOUND) {
|
|
break;
|
|
} else if (EFI_ERROR(rc)) {
|
|
print(L"Could not get variable name: %r\n", rc);
|
|
ret = rc;
|
|
goto err;
|
|
}
|
|
|
|
/* check if the variable name is Boot#### */
|
|
UINTN vns = StrLen(variable_name);
|
|
if (!guid_cmp(&vendor_guid, &global_variable_guid)
|
|
&& vns == 8 && CompareMem(variable_name, L"Boot", 8) == 0) {
|
|
UINTN info_size = 0;
|
|
UINT32 attributes = 0;
|
|
void *info_ptr = NULL;
|
|
CHAR16 *load_op_description = NULL;
|
|
CHAR16 target[] = L"Linux Firmware Updater";
|
|
|
|
rc = read_variable(variable_name, vendor_guid,
|
|
&info_ptr, &info_size, &attributes);
|
|
if (EFI_ERROR(rc)) {
|
|
ret = rc;
|
|
goto err;
|
|
}
|
|
|
|
/*
|
|
* check if the boot path created by fwupdate,
|
|
* check with EFI_LOAD_OPTION decription
|
|
*/
|
|
load_op_description = (CHAR16 *)
|
|
((UINT8 *)info_ptr
|
|
+ sizeof(UINT32)
|
|
+ sizeof(UINT16));
|
|
|
|
if (CompareMem(load_op_description, target,
|
|
sizeof(target) - 2) == 0) {
|
|
/* delete the boot path from BootOrder list */
|
|
rc = delete_boot_order(variable_name,
|
|
vendor_guid);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Failed to delete the Linux Firmware Updater boot entry from BootOrder.\n");
|
|
FreePool(info_ptr);
|
|
ret = rc;
|
|
goto err;
|
|
}
|
|
|
|
rc = delete_variable(variable_name,
|
|
vendor_guid, attributes);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Failed to delete the Linux Firmware Updater boot entry.\n");
|
|
FreePool(info_ptr);
|
|
ret = rc;
|
|
goto err;
|
|
}
|
|
|
|
FreePool(info_ptr);
|
|
break;
|
|
}
|
|
|
|
FreePool(info_ptr);
|
|
}
|
|
}
|
|
|
|
ret = EFI_SUCCESS;
|
|
err:
|
|
FreePool(variable_name);
|
|
return ret;
|
|
}
|
|
|
|
static EFI_STATUS
|
|
get_gop_mode(UINT32 *mode, EFI_HANDLE loaded_image)
|
|
{
|
|
EFI_HANDLE *handles, gop_handle;
|
|
UINTN num_handles, i;
|
|
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 || num_handles == 0)
|
|
return EFI_UNSUPPORTED;
|
|
|
|
for (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 UINT32 csum(UINT8 *buf, UINTN size)
|
|
{
|
|
UINT32 sum = 0;
|
|
UINTN i;
|
|
|
|
dprint(L"checksumming %d bytes at 0x%08x\n", size, buf);
|
|
|
|
for (i = 0; i < size; i++) {
|
|
sum += buf[i];
|
|
if (debugging)
|
|
Print(L"\rpos:%08lx csum:%d", buf+i, sum);
|
|
}
|
|
if (debugging)
|
|
Print(L"\n");
|
|
|
|
return sum;
|
|
}
|
|
|
|
static EFI_STATUS
|
|
do_ux_csum(EFI_HANDLE loaded_image, UINT8 *buf, UINTN size)
|
|
{
|
|
ux_capsule_header_t *payload_hdr;
|
|
EFI_CAPSULE_HEADER *capsule;
|
|
EFI_STATUS rc;
|
|
UINTN sum = 0;
|
|
|
|
UINT8 *current = buf;
|
|
UINTN left = size;
|
|
|
|
if (size < sizeof(*capsule)) {
|
|
dprint(L"Invalid capsule size %d\n", size);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
capsule = (EFI_CAPSULE_HEADER *)buf;
|
|
|
|
if (debugging)
|
|
hexdump(buf, size <= 0x40 ? size : 0x40);
|
|
|
|
dprint(L"size: %d\n", size);
|
|
dprint(L"&HeaderSize: 0x%08lx\n", &capsule->HeaderSize);
|
|
dprint(L"HeaderSize: %d\n", capsule->HeaderSize);
|
|
dprint(L"&CapsuleImageSize: 0x%08lx\n", &capsule->CapsuleImageSize);
|
|
dprint(L"CapsuleImageSize: %d\n", capsule->CapsuleImageSize);
|
|
|
|
if (size < capsule->HeaderSize) {
|
|
dprint(L"Invalid capsule header size %d\n", size);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
sum += csum(current, capsule->HeaderSize);
|
|
current += capsule->HeaderSize;
|
|
left -= capsule->HeaderSize;
|
|
|
|
payload_hdr = (ux_capsule_header_t *)(buf) + capsule->HeaderSize;
|
|
dprint(L"&PayloadHeader: 0x%08lx\n", payload_hdr);
|
|
dprint(L"PayloadHeader Size: %d\n", sizeof (*payload_hdr));
|
|
rc = get_gop_mode(&payload_hdr->mode, loaded_image);
|
|
if (EFI_ERROR(rc))
|
|
return EFI_UNSUPPORTED;
|
|
|
|
payload_hdr->checksum = 0;
|
|
sum += csum(current, sizeof(*payload_hdr));
|
|
current += sizeof(*payload_hdr);
|
|
left -= sizeof(*payload_hdr);
|
|
|
|
sum += csum(current, left);
|
|
dprint(L"sum is 0x%02hhx; setting ->checksum to 0x%02hhx\n",
|
|
sum & 0xff, (uint8_t)((int8_t)(0 - (sum & 0xff))));
|
|
|
|
payload_hdr->checksum = (uint8_t)((int8_t)(0 - sum));
|
|
dprint(L"checksum is set...\n");
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
#define is_ux_capsule(guid) (guid_cmp(guid, &ux_capsule_guid) == 0)
|
|
#define is_fmp_capsule(guid) (guid_cmp(guid, &fmp_capsule_guid) == 0)
|
|
|
|
static EFI_STATUS
|
|
add_capsule(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 = open_file((EFI_DEVICE_PATH *)update->info->dp_buf, &fh);
|
|
if (EFI_ERROR(rc))
|
|
return rc;
|
|
|
|
rc = read_file(fh, &fbuf, &fsize);
|
|
if (EFI_ERROR(rc))
|
|
return rc;
|
|
|
|
uefi_call_wrapper(fh->Close, 1, fh);
|
|
|
|
dprint(L"Read file; %d bytes\n", fsize);
|
|
dprint(L"updates guid: %g\n", &update->info->guid);
|
|
dprint(L"File guid: %g\n", fbuf);
|
|
|
|
/*
|
|
* See if it has the capsule header, and if not, add one.
|
|
*
|
|
* Unfortunately there's not a good way to do this, so we're just
|
|
* checking if the capsule has the fw_class guid at the right place.
|
|
*/
|
|
if ((guid_cmp(&update->info->guid, (efi_guid_t *)fbuf) == 0 ||
|
|
is_fmp_capsule((efi_guid_t *)fbuf)) &&
|
|
/*
|
|
* We're ignoring things that are 40 bytes here, because that's
|
|
* the size of the variables used in the test code I wrote for
|
|
* edk2 - It's basically a capsule header with no payload, so
|
|
* there's nothing real it can do anyway.
|
|
*
|
|
* At some point I'll update that to be slightly different and
|
|
* take the exception out, but it's not pressing.
|
|
*/
|
|
fsize != 40) {
|
|
dprint(L"Image has capsule image embedded\n");
|
|
cbd_len = fsize;
|
|
cbd_data = (EFI_PHYSICAL_ADDRESS)(UINTN)fbuf;
|
|
capsule = cap_out = (EFI_CAPSULE_HEADER *)fbuf;
|
|
if (!cap_out->Flags && !is_ux_capsule(&update->info->guid)) {
|
|
#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
|
|
}
|
|
} else {
|
|
dprint(L"Image does not have embedded header\n");
|
|
dprint(L"Allocating %d for capsule header.\n",
|
|
sizeof (*capsule)+fsize);
|
|
rc = allocate((void **)&capsule, sizeof (*capsule) + fsize);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Tried to allocate %d\n",
|
|
sizeof (*capsule) + fsize);
|
|
print(L"Could not allocate space for update: %r.\n",
|
|
rc);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
capsule->CapsuleGuid = update->info->guid;
|
|
capsule->HeaderSize = sizeof (*capsule);
|
|
if (!is_ux_capsule(&update->info->guid)) {
|
|
#if defined(__aarch64__)
|
|
capsule->Flags |= update->info->capsule_flags;
|
|
#else
|
|
capsule->Flags = update->info->capsule_flags |
|
|
CAPSULE_FLAGS_PERSIST_ACROSS_RESET |
|
|
CAPSULE_FLAGS_INITIATE_RESET;
|
|
#endif
|
|
}
|
|
capsule->CapsuleImageSize = fsize + sizeof (*capsule);
|
|
|
|
UINT8 *buffer = (UINT8 *)capsule + capsule->HeaderSize;
|
|
CopyMem(buffer, fbuf, fsize);
|
|
cbd_len = capsule->CapsuleImageSize;
|
|
cbd_data = (EFI_PHYSICAL_ADDRESS)(UINTN)capsule;
|
|
cap_out = capsule;
|
|
free(fbuf, fsize);
|
|
}
|
|
|
|
if (is_ux_capsule(&update->info->guid)) {
|
|
dprint(L"Checksumming ux capsule\n");
|
|
rc = do_ux_csum(loaded_image, (UINT8 *)capsule, cbd_len);
|
|
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
|
|
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 = delete_boot_entry();
|
|
if (EFI_ERROR(rc)) {
|
|
/*
|
|
* Print out deleting boot entry error, but still try to apply
|
|
* capsule.
|
|
*/
|
|
dprint(L"Could not delete boot entry: %r\n",
|
|
__FILE__, __func__, __LINE__, rc);
|
|
}
|
|
|
|
rc = uefi_call_wrapper(RT->QueryCapsuleCapabilities, 4, capsules,
|
|
num_updates, &max_capsule_size, reset);
|
|
dprint(L"QueryCapsuleCapabilities: %r max: %ld reset:%d\n",
|
|
rc, max_capsule_size, *reset);
|
|
dprint(L"Capsules: %d\n", num_updates);
|
|
|
|
uefi_call_wrapper(BS->Stall, 1, 1 * SECONDS);
|
|
rc = uefi_call_wrapper(RT->UpdateCapsule, 3, capsules, num_updates,
|
|
(EFI_PHYSICAL_ADDRESS)(UINTN)cbd);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Could not apply capsule update: %r\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static
|
|
EFI_STATUS
|
|
set_statuses(UINTN n_updates, update_table **updates)
|
|
{
|
|
EFI_STATUS rc;
|
|
for (UINTN i = 0; i < n_updates; i++) {
|
|
rc = uefi_call_wrapper(RT->SetVariable, 5, updates[i]->name,
|
|
&fwupdate_guid, updates[i]->attributes,
|
|
updates[i]->size, updates[i]->info);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Coould not update variable status for \"%s\": %r\n",
|
|
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")))
|
|
debug_hook(void)
|
|
{
|
|
EFI_GUID guid = SHIM_LOCK_GUID;
|
|
UINTN data = 0;
|
|
UINTN data_size = 1;
|
|
EFI_STATUS efi_status;
|
|
UINT32 attributes;
|
|
register volatile int x = 0;
|
|
extern char _text UNUSED, _data UNUSED;
|
|
|
|
/*
|
|
* If SHIM_DEBUG is set, we're going to assume shim has done whatever
|
|
* is needed to get a debugger attached, and we just need to explain
|
|
* who and where we are, and also enable our debugging output.
|
|
*/
|
|
efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"SHIM_DEBUG",
|
|
&guid, &attributes, &data_size, &data);
|
|
if (EFI_ERROR(efi_status) || data != 1) {
|
|
efi_status = uefi_call_wrapper(RT->GetVariable, 5,
|
|
L"FWUPDATE_VERBOSE",
|
|
&fwupdate_guid, &attributes,
|
|
&data_size, &data);
|
|
if (EFI_ERROR(efi_status) || data != 1) {
|
|
return;
|
|
}
|
|
debugging = 1;
|
|
return;
|
|
}
|
|
|
|
debugging = 1;
|
|
if (x)
|
|
return;
|
|
|
|
x = 1;
|
|
print(L"add-symbol-file "DEBUGDIR
|
|
L"fwupdate.efi.debug %p -s .data %p\n",
|
|
&_text, &_data);
|
|
}
|
|
|
|
EFI_STATUS
|
|
efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
|
|
{
|
|
EFI_STATUS rc;
|
|
update_table **updates = NULL;
|
|
UINTN n_updates = 0;
|
|
EFI_RESET_TYPE reset_type = EfiResetWarm;
|
|
|
|
InitializeLib(image, systab);
|
|
|
|
/*
|
|
* if SHIM_DEBUG is set, print info for our attached debugger.
|
|
*/
|
|
debug_hook();
|
|
|
|
/*
|
|
* Basically the workflow here is:
|
|
* 1) find and validate any update state variables with the right GUID
|
|
* 2) allocate our capsule data structures and add the capsules
|
|
* #1 described
|
|
* 3) update status variables
|
|
* 4) apply the capsule updates
|
|
* 5) reboot
|
|
*/
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
rc = find_updates(&n_updates, &updates);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"fwupdate: Could not find updates: %r\n", rc);
|
|
return rc;
|
|
}
|
|
if (n_updates == 0) {
|
|
print(L"fwupdate: No updates to process. Called in error?\n");
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
/*
|
|
* Step 2: Build our data structure and add the capsules to it.
|
|
*/
|
|
EFI_CAPSULE_HEADER *capsules[n_updates + 1];
|
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *cbd_data;
|
|
UINTN i, j;
|
|
rc = allocate((void **)&cbd_data,
|
|
sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR)*(n_updates+1));
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"Tried to allocate %d\n",
|
|
sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR)*(n_updates+1));
|
|
print(L"fwupdate: Could not allocate memory: %r.\n",rc);
|
|
return rc;
|
|
}
|
|
for (i = 0, j = 0; i < n_updates; i++) {
|
|
dprint(L"Adding new capsule\n");
|
|
rc = add_capsule(updates[i], &capsules[j], &cbd_data[j],
|
|
image);
|
|
if (EFI_ERROR(rc)) {
|
|
if (rc == EFI_UNSUPPORTED &&
|
|
is_ux_capsule(&updates[i]->info->guid))
|
|
continue;
|
|
dprint(L"fwupdate: Could not build update list: %r\n",
|
|
rc);
|
|
return rc;
|
|
}
|
|
j++;
|
|
}
|
|
n_updates = j;
|
|
dprint(L"n_updates: %d\n", n_updates);
|
|
|
|
cbd_data[i].Length = 0;
|
|
cbd_data[i].Union.ContinuationPointer = 0;
|
|
|
|
/*
|
|
* Step 3: update the state variables.
|
|
*/
|
|
rc = set_statuses(n_updates, updates);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"fwupdate: Could not set update status: %r\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Step 4: apply the capsules.
|
|
*/
|
|
rc = apply_capsules(capsules, cbd_data, n_updates, &reset_type);
|
|
if (EFI_ERROR(rc)) {
|
|
print(L"fwupdate: Could not apply capsules: %r\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Step 5: if #4 didn't reboot us, do it manually.
|
|
*/
|
|
dprint(L"fwupdate: Reset System\n");
|
|
if (debugging)
|
|
uefi_call_wrapper(BS->Stall, 1, 5 * SECONDS);
|
|
|
|
uefi_call_wrapper(BS->Stall, 1, 5 * SECONDS);
|
|
uefi_call_wrapper(RT->ResetSystem, 4, reset_type, EFI_SUCCESS,
|
|
0, NULL);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|