uefi-capsule: Add support for CapsuleOnDisk

Based on a patch by Ilias Apalodimas <ilias.apalodimas@linaro.org>,
many thanks.

Fixes https://github.com/fwupd/fwupd/issues/2900
This commit is contained in:
Richard Hughes 2021-07-24 11:16:31 +01:00
parent a1f8096c53
commit 3747e245e5
25 changed files with 1053 additions and 328 deletions

View File

@ -53,6 +53,7 @@ _fwupdtool_opts=(
'--prepare'
'--cleanup'
'--filter'
'--method'
'--disable-ssl-strict'
'--no-safety-check'
'--ignore-checksum'

View File

@ -14,6 +14,7 @@
#define FU_EFIVAR_GUID_UX_CAPSULE "3b8c8162-188c-46a4-aec9-be43f1d65697"
#define FU_EFIVAR_GUID_SECURITY_DATABASE "d719b2cb-3d3a-4596-a3bc-dad00e67656f"
#define FU_EFIVAR_GUID_UX_CAPSULE "3b8c8162-188c-46a4-aec9-be43f1d65697"
#define FU_EFIVAR_GUID_EFI_CAPSULE_REPORT "39b68c46-f7fb-441b-b6ec-16b0f69821f3"
#define FU_EFIVAR_ATTR_NON_VOLATILE (1 << 0)
#define FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS (1 << 1)

View File

@ -8,14 +8,15 @@
#include "config.h"
#include <fwupdplugin.h>
#include <fcntl.h>
#include <glib/gi18n.h>
#include "fu-uefi-backend.h"
#include "fu-uefi-bgrt.h"
#include "fu-uefi-bootmgr.h"
#include "fu-uefi-cod-device.h"
#include "fu-uefi-common.h"
#include "fu-uefi-device.h"
struct FuPluginData {
FuUefiBgrt *bgrt;
@ -386,10 +387,11 @@ static void
fu_plugin_uefi_capsule_register_proxy_device (FuPlugin *plugin, FuDevice *device)
{
FuPluginData *data = fu_plugin_get_data (plugin);
g_autoptr(FuUefiDevice) dev = fu_uefi_device_new_from_dev (device);
g_autoptr(FuUefiDevice) dev = NULL;
g_autoptr(GError) error_local = NULL;
/* load all configuration variables */
dev = fu_uefi_backend_device_new_from_dev(FU_UEFI_BACKEND(data->backend), device);
fu_plugin_uefi_capsule_load_config (plugin, FU_DEVICE (dev));
if (data->esp == NULL)
data->esp = fu_common_get_esp_default (&error_local);
@ -662,6 +664,36 @@ fu_plugin_uefi_update_state_notify_cb (GObject *object,
}
}
static gboolean
fu_backend_uefi_check_cod_support(GError **error)
{
gsize bufsz = 0;
guint64 value = 0;
g_autofree guint8 *buf = NULL;
if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_GLOBAL,
"OsIndicationsSupported",
&buf,
&bufsz,
NULL,
error)) {
g_prefix_error(error, "failed to read EFI variable: ");
return FALSE;
}
if (!fu_common_read_uint64_safe(buf, bufsz, 0x0, &value, G_LITTLE_ENDIAN, error))
return FALSE;
if ((value & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) == 0) {
g_set_error_literal(error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"Capsule-on-Disk is not supported");
return FALSE;
}
/* success */
return TRUE;
}
gboolean
fu_plugin_coldplug (FuPlugin *plugin, GError **error)
{
@ -682,6 +714,17 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error)
}
}
/* firmware may lie */
if (!fu_plugin_get_config_value_boolean(plugin, "DisableCapsuleUpdateOnDisk")) {
g_autoptr(GError) error_cod = NULL;
if (!fu_backend_uefi_check_cod_support(&error_cod)) {
g_debug("not using CapsuleOnDisk support: %s", error_cod->message);
} else {
fu_uefi_backend_set_device_gtype(FU_UEFI_BACKEND(data->backend),
FU_TYPE_UEFI_COD_DEVICE);
}
}
/* add each device */
if (!fu_backend_coldplug (data->backend, error))
return FALSE;

View File

@ -9,12 +9,11 @@
#include <fwupdplugin.h>
#include "fu-context-private.h"
#include "fu-ucs2.h"
#include "fu-uefi-backend.h"
#include "fu-uefi-bgrt.h"
#include "fu-uefi-cod-device.h"
#include "fu-uefi-common.h"
#include "fu-uefi-device.h"
#include "fu-uefi-pcrs.h"
static void
@ -145,6 +144,103 @@ fu_uefi_bitmap_func (void)
g_assert_cmpint (height, ==, 24);
}
static GByteArray *
fu_uefi_cod_device_build_efi_string(const gchar *text)
{
GByteArray *array = g_byte_array_new();
glong items_written = 0;
g_autofree gunichar2 *test_utf16 = NULL;
g_autoptr(GError) error = NULL;
fu_byte_array_append_uint32(array, 0x0, G_LITTLE_ENDIAN); /* attrs */
test_utf16 = g_utf8_to_utf16(text, -1, NULL, &items_written, &error);
g_assert_no_error(error);
g_assert_nonnull(test_utf16);
g_byte_array_append(array, (const guint8 *)test_utf16, items_written * 2);
return array;
}
static GByteArray *
fu_uefi_cod_device_build_efi_result(const gchar *guidstr)
{
GByteArray *array = g_byte_array_new();
fwupd_guid_t guid = {0x0};
gboolean ret;
guint8 timestamp[16] = {0x0};
g_autoptr(GError) error = NULL;
fu_byte_array_append_uint32(array, 0x0, G_LITTLE_ENDIAN); /* attrs */
fu_byte_array_append_uint32(array, 0x3A, G_LITTLE_ENDIAN); /* VariableTotalSize */
fu_byte_array_append_uint32(array, 0xFF, G_LITTLE_ENDIAN); /* Reserved */
ret = fwupd_guid_from_string(guidstr, &guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_byte_array_append(array, guid, sizeof(guid)); /* CapsuleGuid */
g_byte_array_append(array, timestamp, sizeof(timestamp)); /* CapsuleProcessed */
fu_byte_array_append_uint32(array,
FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_BATT,
G_LITTLE_ENDIAN); /* Status */
return array;
}
static void
fu_uefi_cod_device_write_efi_name(const gchar *name, GByteArray *array)
{
gboolean ret;
g_autoptr(GError) error = NULL;
g_autofree gchar *fn = g_strdup_printf("%s-%s", name, FU_EFIVAR_GUID_EFI_CAPSULE_REPORT);
g_autofree gchar *path = g_build_filename(TESTDATADIR, "efi", "efivars", fn, NULL);
ret = g_file_set_contents(path, (gchar *)array->data, array->len, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
fu_uefi_cod_device_func(void)
{
gboolean ret;
g_autoptr(FuDevice) dev = NULL;
g_autoptr(GError) error = NULL;
g_autofree gchar *str = NULL;
/* these are checked into git and so are not required */
if (g_getenv("FU_UEFI_CAPSULE_RECREATE_COD_SELF_TEST_DATA") != NULL) {
g_autoptr(GByteArray) cap0 = NULL;
g_autoptr(GByteArray) cap1 = NULL;
g_autoptr(GByteArray) last = NULL;
g_autoptr(GByteArray) max = NULL;
last = fu_uefi_cod_device_build_efi_string("Capsule0001");
max = fu_uefi_cod_device_build_efi_string("Capsule9999");
cap0 = fu_uefi_cod_device_build_efi_result("99999999-bf9d-540b-b92b-172ce31013c1");
cap1 = fu_uefi_cod_device_build_efi_result("cc4cbfa9-bf9d-540b-b92b-172ce31013c1");
fu_uefi_cod_device_write_efi_name("CapsuleLast", last);
fu_uefi_cod_device_write_efi_name("CapsuleMax", max);
fu_uefi_cod_device_write_efi_name("Capsule0000", cap0);
fu_uefi_cod_device_write_efi_name("Capsule0001", cap1);
}
/* create device */
dev = g_object_new(FU_TYPE_UEFI_COD_DEVICE,
"fw-class",
"cc4cbfa9-bf9d-540b-b92b-172ce31013c1",
NULL);
ret = fu_device_get_results(dev, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* debug */
str = fu_device_to_string(dev);
g_debug("%s", str);
g_assert_cmpint(fu_device_get_update_state(dev), ==, FWUPD_UPDATE_STATE_FAILED_TRANSIENT);
g_assert_cmpstr(fu_device_get_update_error(dev),
==,
"failed to update to 0: battery level is too low");
g_assert_cmpint(fu_uefi_device_get_status(FU_UEFI_DEVICE(dev)),
==,
FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_BATT);
}
static void
fu_uefi_device_func (void)
{
@ -258,6 +354,7 @@ main (int argc, char **argv)
g_test_init (&argc, &argv, NULL);
g_setenv ("FWUPD_SYSFSFWDIR", TESTDATADIR, TRUE);
g_setenv ("FWUPD_SYSFSDRIVERDIR", TESTDATADIR, TRUE);
g_setenv("FWUPD_UEFI_TEST", "1", TRUE);
/* only critical and error are fatal */
g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
@ -271,6 +368,7 @@ main (int argc, char **argv)
g_test_add_func ("/uefi/framebuffer", fu_uefi_framebuffer_func);
g_test_add_func ("/uefi/bitmap", fu_uefi_bitmap_func);
g_test_add_func ("/uefi/device", fu_uefi_device_func);
g_test_add_func("/uefi/cod-device", fu_uefi_cod_device_func);
g_test_add_func ("/uefi/update-info", fu_uefi_update_info_func);
g_test_add_func ("/uefi/plugin", fu_uefi_plugin_func);
return g_test_run ();

View File

@ -20,20 +20,23 @@
#include <glib/gstdio.h>
#include "fu-uefi-backend-freebsd.h"
#include "fu-uefi-common.h"
#include "fu-uefi-device.h"
#include "fu-uefi-backend.h"
struct _FuUefiBackend {
FuBackend parent_instance;
struct _FuUefiBackendFreebsd {
FuUefiBackend parent_instance;
};
G_DEFINE_TYPE (FuUefiBackend, fu_uefi_backend, FU_TYPE_BACKEND)
G_DEFINE_TYPE(FuUefiBackendFreebsd, fu_uefi_backend_freebsd, FU_TYPE_UEFI_BACKEND)
#ifdef HAVE_FREEBSD_ESRT
static FuUefiDevice *
fu_uefi_backend_device_new (struct efi_esrt_entry_v1 *entry, guint64 idx, GError **error)
fu_uefi_backend_device_new(FuUefiBackend *self,
struct efi_esrt_entry_v1 *entry,
guint64 idx,
GError **error)
{
g_autoptr(FuUefiDevice) dev = NULL;
g_autofree gchar *fw_class = NULL;
@ -50,17 +53,26 @@ fu_uefi_backend_device_new (struct efi_esrt_entry_v1 *entry, guint64 idx, GError
}
/* create object */
dev = g_object_new (FU_TYPE_UEFI_DEVICE,
"fw-class", fw_class,
"capsule-flags", entry->capsule_flags,
"kind", entry->fw_type,
"fw-version", entry->fw_version,
"last-attempt-status", entry->last_attempt_status,
"last-attempt-version", entry->last_attempt_version,
"fw-version-lowest", entry->lowest_supported_fw_version,
"fmp-hardware-instance", (guint64) 0x0,
"version-format", FWUPD_VERSION_FORMAT_NUMBER,
NULL);
dev = g_object_new(fu_uefi_backend_get_device_gtype(self),
"fw-class",
fw_class,
"capsule-flags",
entry->capsule_flags,
"kind",
entry->fw_type,
"fw-version",
entry->fw_version,
"last-attempt-status",
entry->last_attempt_status,
"last-attempt-version",
entry->last_attempt_version,
"fw-version-lowest",
entry->lowest_supported_fw_version,
"fmp-hardware-instance",
(guint64)0x0,
"version-format",
FWUPD_VERSION_FORMAT_NUMBER,
NULL);
/* set ID */
phys_id = g_strdup_printf ("ESRT/%u", (guint)idx);
@ -71,7 +83,7 @@ fu_uefi_backend_device_new (struct efi_esrt_entry_v1 *entry, guint64 idx, GError
#endif
static gboolean
fu_uefi_backend_setup (FuBackend *backend, GError **error)
fu_uefi_backend_freebsd_setup(FuBackend *backend, GError **error)
{
g_autofree gchar *efi_ver = fu_kenv_get_string ("efi-version", error);
if (efi_ver == NULL) {
@ -90,9 +102,10 @@ fu_uefi_backend_setup (FuBackend *backend, GError **error)
}
static gboolean
fu_uefi_backend_coldplug (FuBackend *backend, GError **error)
fu_uefi_backend_freebsd_coldplug(FuBackend *backend, GError **error)
{
#ifdef HAVE_FREEBSD_ESRT
FuUefiBackend *self = FU_UEFI_BACKEND(backend);
struct efi_get_table_ioc table = {
.uuid = EFI_TABLE_ESRT
};
@ -141,9 +154,8 @@ fu_uefi_backend_coldplug (FuBackend *backend, GError **error)
entries = (struct efi_esrt_entry_v1 *)esrt->entries;
for (guint i = 0; i < esrt->fw_resource_count; i++) {
g_autoptr(FuUefiDevice) dev = fu_uefi_backend_device_new (&entries[i],
i,
error);
g_autoptr(FuUefiDevice) dev = NULL;
dev = fu_uefi_backend_device_new(self, &entries[i], i, error);
if (dev == NULL)
return FALSE;
@ -161,24 +173,26 @@ fu_uefi_backend_coldplug (FuBackend *backend, GError **error)
#endif
}
static void
fu_uefi_backend_init (FuUefiBackend *self)
void
fu_uefi_backend_freebsd_set_device_gtype(FuBackend *backend, GType device_gtype)
{
}
static void
fu_uefi_backend_class_init (FuUefiBackendClass *klass)
fu_uefi_backend_freebsd_init(FuUefiBackendFreebsd *self)
{
}
static void
fu_uefi_backend_freebsd_class_init(FuUefiBackendFreebsdClass *klass)
{
FuBackendClass *klass_backend = FU_BACKEND_CLASS (klass);
klass_backend->setup = fu_uefi_backend_setup;
klass_backend->coldplug = fu_uefi_backend_coldplug;
klass_backend->setup = fu_uefi_backend_freebsd_setup;
klass_backend->coldplug = fu_uefi_backend_freebsd_coldplug;
}
FuBackend *
fu_uefi_backend_new (FuContext *ctx)
{
return FU_BACKEND (g_object_new (FU_TYPE_UEFI_BACKEND,
"name", "uefi",
"context", ctx,
NULL));
return g_object_new(FU_TYPE_UEFI_BACKEND_FREEBSD, "name", "uefi", "context", ctx, NULL);
}

View File

@ -0,0 +1,18 @@
/*
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <fwupdplugin.h>
#include "fu-uefi-backend.h"
#define FU_TYPE_UEFI_BACKEND_FREEBSD (fu_uefi_backend_freebsd_get_type())
G_DECLARE_FINAL_TYPE(FuUefiBackendFreebsd,
fu_uefi_backend_freebsd,
FU,
UEFI_BACKEND_FREEBSD,
FuUefiBackend)

View File

@ -7,11 +7,13 @@
#include "config.h"
#include <fwupdplugin.h>
#include <gio/gunixmounts.h>
#include "fu-uefi-backend-linux.h"
#include "fu-uefi-cod-device.h"
#include "fu-uefi-common.h"
#include "fu-uefi-device.h"
#include "fu-uefi-backend.h"
#include "fu-uefi-nvram-device.h"
#ifndef HAVE_GIO_2_55_0
#pragma clang diagnostic push
@ -20,21 +22,22 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUnixMountEntry, g_unix_mount_free)
#pragma clang diagnostic pop
#endif
struct _FuUefiBackend {
FuBackend parent_instance;
struct _FuUefiBackendLinux {
FuUefiBackend parent_instance;
gboolean use_rt_set_variable;
};
G_DEFINE_TYPE (FuUefiBackend, fu_uefi_backend, FU_TYPE_BACKEND)
G_DEFINE_TYPE(FuUefiBackendLinux, fu_uefi_backend_linux, FU_TYPE_UEFI_BACKEND)
/* yes, unsized uint_t */
static guint
fu_uefi_backend_read (const gchar *path, const gchar *filename)
fu_uefi_backend_linux_read(const gchar *path, const gchar *filename)
{
return fu_uefi_read_file_as_uint64 (path, filename);
}
static FuUefiDevice *
fu_uefi_backend_device_new (const gchar *path)
fu_uefi_backend_linux_device_new(FuUefiBackendLinux *self, const gchar *path)
{
g_autoptr(FuUefiDevice) dev = NULL;
g_autofree gchar *fw_class = NULL;
@ -53,17 +56,30 @@ fu_uefi_backend_device_new (const gchar *path)
* The hardware instance is not in the ESRT table and we should really
* write the EFI stub to query with FMP -- but we still have not ever
* seen a PCIe device with FMP support... */
dev = g_object_new (FU_TYPE_UEFI_DEVICE,
"fw-class", fw_class,
"capsule-flags", fu_uefi_backend_read (path, "capsule_flags"),
"kind", fu_uefi_backend_read (path, "fw_type"),
"fw-version", fu_uefi_backend_read (path, "fw_version"),
"last-attempt-status", fu_uefi_backend_read (path, "last_attempt_status"),
"last-attempt-version", fu_uefi_backend_read (path, "last_attempt_version"),
"fw-version-lowest", fu_uefi_backend_read (path, "lowest_supported_fw_version"),
"fmp-hardware-instance", (guint64) 0x0,
"version-format", FWUPD_VERSION_FORMAT_NUMBER,
NULL);
dev = g_object_new(fu_uefi_backend_get_device_gtype(FU_UEFI_BACKEND(self)),
"fw-class",
fw_class,
"capsule-flags",
fu_uefi_backend_linux_read(path, "capsule_flags"),
"kind",
fu_uefi_backend_linux_read(path, "fw_type"),
"fw-version",
fu_uefi_backend_linux_read(path, "fw_version"),
"last-attempt-status",
fu_uefi_backend_linux_read(path, "last_attempt_status"),
"last-attempt-version",
fu_uefi_backend_linux_read(path, "last_attempt_version"),
"fw-version-lowest",
fu_uefi_backend_linux_read(path, "lowest_supported_fw_version"),
"fmp-hardware-instance",
(guint64)0x0,
"version-format",
FWUPD_VERSION_FORMAT_NUMBER,
NULL);
/* u-boot for instance */
if (!self->use_rt_set_variable)
fu_device_add_private_flag(FU_DEVICE(dev), FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE);
/* set ID */
fu_device_set_physical_id (FU_DEVICE (dev), path);
@ -71,14 +87,56 @@ fu_uefi_backend_device_new (const gchar *path)
}
static gboolean
fu_uefi_backend_coldplug (FuBackend *backend, GError **error)
fu_uefi_backend_linux_check_efivarfs(FuUefiBackendLinux *self, GError **error)
{
g_autofree gchar *sysfsfwdir = fu_common_get_path(FU_PATH_KIND_SYSFSDIR_FW);
g_autofree gchar *sysfsefivardir = g_build_filename(sysfsfwdir, "efi", "efivars", NULL);
g_autoptr(GUnixMountEntry) mount = g_unix_mount_at(sysfsefivardir, NULL);
/* in the self tests */
if (g_getenv("FWUPD_UEFI_TEST") != NULL)
return TRUE;
if (mount == NULL) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"%s was not mounted",
sysfsefivardir);
return FALSE;
}
if (g_unix_mount_is_readonly(mount)) {
GType gtype = fu_uefi_backend_get_device_gtype(FU_UEFI_BACKEND(self));
if (gtype != FU_TYPE_UEFI_COD_DEVICE) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_WRITE,
"%s is read only and no CoD",
sysfsefivardir);
return FALSE;
}
/* this is fine! just do not use SetVariable... */
self->use_rt_set_variable = FALSE;
}
return TRUE;
}
static gboolean
fu_uefi_backend_linux_coldplug(FuBackend *backend, GError **error)
{
FuUefiBackendLinux *self = FU_UEFI_BACKEND_LINUX(backend);
const gchar *fn;
g_autofree gchar *esrt_entries = NULL;
g_autofree gchar *esrt_path = NULL;
g_autofree gchar *sysfsfwdir = NULL;
g_autoptr(GDir) dir = NULL;
/* make sure that efivarfs is suitable */
if (!fu_uefi_backend_linux_check_efivarfs(self, error))
return FALSE;
/* get the directory of ESRT entries */
sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW);
esrt_path = g_build_filename (sysfsfwdir, "efi", "esrt", NULL);
@ -90,7 +148,7 @@ fu_uefi_backend_coldplug (FuBackend *backend, GError **error)
/* add each device */
while ((fn = g_dir_read_name (dir)) != NULL) {
g_autofree gchar *path = g_build_filename (esrt_entries, fn, NULL);
g_autoptr(FuUefiDevice) dev = fu_uefi_backend_device_new (path);
g_autoptr(FuUefiDevice) dev = fu_uefi_backend_linux_device_new(self, path);
fu_backend_device_added (backend, FU_DEVICE (dev));
}
@ -99,7 +157,7 @@ fu_uefi_backend_coldplug (FuBackend *backend, GError **error)
}
static gboolean
fu_uefi_backend_check_smbios_enabled (FuContext *ctx, GError **error)
fu_uefi_backend_linux_check_smbios_enabled(FuContext *ctx, GError **error)
{
const guint8 *data;
gsize sz;
@ -140,32 +198,7 @@ fu_uefi_backend_check_smbios_enabled (FuContext *ctx, GError **error)
}
static gboolean
fu_uefi_backend_check_efivarfs_rw (GError **error)
{
g_autofree gchar *sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW);
g_autofree gchar *sysfsefivardir = g_build_filename (sysfsfwdir, "efi", "efivars", NULL);
g_autoptr(GUnixMountEntry) mount = g_unix_mount_at (sysfsefivardir, NULL);
if (mount == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"%s was not mounted", sysfsefivardir);
return FALSE;
}
if (g_unix_mount_is_readonly (mount)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_WRITE,
"%s is read only", sysfsefivardir);
return FALSE;
}
return TRUE;
}
static gboolean
fu_uefi_backend_setup (FuBackend *backend, GError **error)
fu_uefi_backend_linux_setup(FuBackend *backend, GError **error)
{
g_autoptr(GError) error_local = NULL;
@ -173,12 +206,9 @@ fu_uefi_backend_setup (FuBackend *backend, GError **error)
if (g_getenv ("FWUPD_SYSFSFWDIR") != NULL)
return TRUE;
/* make sure that efivarfs is rw */
if (!fu_uefi_backend_check_efivarfs_rw (error))
return FALSE;
/* check SMBIOS for 'UEFI Specification is supported' */
if (!fu_uefi_backend_check_smbios_enabled (fu_backend_get_context (backend), &error_local)) {
if (!fu_uefi_backend_linux_check_smbios_enabled(fu_backend_get_context(backend),
&error_local)) {
g_autofree gchar *fw = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW);
g_autofree gchar *fn = g_build_filename (fw, "efi", NULL);
if (g_file_test (fn, G_FILE_TEST_EXISTS)) {
@ -194,23 +224,21 @@ fu_uefi_backend_setup (FuBackend *backend, GError **error)
}
static void
fu_uefi_backend_init (FuUefiBackend *self)
fu_uefi_backend_linux_init(FuUefiBackendLinux *self)
{
self->use_rt_set_variable = TRUE;
}
static void
fu_uefi_backend_class_init (FuUefiBackendClass *klass)
fu_uefi_backend_linux_class_init(FuUefiBackendLinuxClass *klass)
{
FuBackendClass *klass_backend = FU_BACKEND_CLASS (klass);
klass_backend->coldplug = fu_uefi_backend_coldplug;
klass_backend->setup = fu_uefi_backend_setup;
klass_backend->coldplug = fu_uefi_backend_linux_coldplug;
klass_backend->setup = fu_uefi_backend_linux_setup;
}
FuBackend *
fu_uefi_backend_new (FuContext *ctx)
{
return FU_BACKEND (g_object_new (FU_TYPE_UEFI_BACKEND,
"name", "uefi",
"context", ctx,
NULL));
return g_object_new(FU_TYPE_UEFI_BACKEND_LINUX, "name", "uefi", "context", ctx, NULL);
}

View File

@ -0,0 +1,18 @@
/*
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <fwupdplugin.h>
#include "fu-uefi-backend.h"
#define FU_TYPE_UEFI_BACKEND_LINUX (fu_uefi_backend_linux_get_type())
G_DECLARE_FINAL_TYPE(FuUefiBackendLinux,
fu_uefi_backend_linux,
FU,
UEFI_BACKEND_LINUX,
FuUefiBackend)

View File

@ -0,0 +1,83 @@
/*
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include "fu-uefi-backend.h"
#include "fu-uefi-nvram-device.h"
typedef struct {
GType device_gtype;
} FuUefiBackendPrivate;
G_DEFINE_TYPE_WITH_PRIVATE(FuUefiBackend, fu_uefi_backend, FU_TYPE_BACKEND)
#define GET_PRIVATE(o) (fu_uefi_backend_get_instance_private(o))
void
fu_uefi_backend_set_device_gtype(FuUefiBackend *self, GType device_gtype)
{
FuUefiBackendPrivate *priv = GET_PRIVATE(self);
priv->device_gtype = device_gtype;
}
GType
fu_uefi_backend_get_device_gtype(FuUefiBackend *self)
{
FuUefiBackendPrivate *priv = GET_PRIVATE(self);
return priv->device_gtype;
}
/* create virtual object not backed by an ESRT entry */
FuUefiDevice *
fu_uefi_backend_device_new_from_dev(FuUefiBackend *self, FuDevice *dev)
{
FuUefiDevice *device;
FuUefiBackendPrivate *priv = GET_PRIVATE(self);
const gchar *tmp;
g_return_val_if_fail(fu_device_get_guid_default(dev) != NULL, NULL);
tmp = fu_device_get_metadata(dev, FU_DEVICE_METADATA_UEFI_DEVICE_KIND);
device =
g_object_new(priv->device_gtype,
"fw-class",
fu_device_get_guid_default(dev),
"kind",
fu_uefi_device_kind_from_string(tmp),
"capsule-flags",
fu_device_get_metadata_integer(dev, FU_DEVICE_METADATA_UEFI_CAPSULE_FLAGS),
"fw-version",
fu_device_get_metadata_integer(dev, FU_DEVICE_METADATA_UEFI_FW_VERSION),
NULL);
fu_device_incorporate(FU_DEVICE(device), dev);
return device;
}
FuUefiDevice *
fu_uefi_backend_device_new_from_guid(FuUefiBackend *self, const gchar *guid)
{
FuUefiDevice *device;
FuUefiBackendPrivate *priv = GET_PRIVATE(self);
g_return_val_if_fail(guid != NULL, NULL);
device = g_object_new(priv->device_gtype, "fw-class", guid, NULL);
fu_device_set_version_format(FU_DEVICE(device), FWUPD_VERSION_FORMAT_NUMBER);
return device;
}
static void
fu_uefi_backend_init(FuUefiBackend *self)
{
FuUefiBackendPrivate *priv = GET_PRIVATE(self);
priv->device_gtype = FU_TYPE_UEFI_NVRAM_DEVICE;
}
static void
fu_uefi_backend_class_init(FuUefiBackendClass *klass)
{
}

View File

@ -8,7 +8,22 @@
#include <fwupdplugin.h>
#include "fu-uefi-device.h"
#define FU_TYPE_UEFI_BACKEND (fu_uefi_backend_get_type ())
G_DECLARE_FINAL_TYPE (FuUefiBackend, fu_uefi_backend, FU, UEFI_BACKEND, FuBackend)
G_DECLARE_DERIVABLE_TYPE(FuUefiBackend, fu_uefi_backend, FU, UEFI_BACKEND, FuBackend)
struct _FuUefiBackendClass {
FuBackendClass parent_class;
};
FuBackend *fu_uefi_backend_new (FuContext *ctx);
void
fu_uefi_backend_set_device_gtype(FuUefiBackend *self, GType device_gtype);
GType
fu_uefi_backend_get_device_gtype(FuUefiBackend *self);
FuUefiDevice *
fu_uefi_backend_device_new_from_guid(FuUefiBackend *self, const gchar *guid);
FuUefiDevice *
fu_uefi_backend_device_new_from_dev(FuUefiBackend *self, FuDevice *dev);

View File

@ -248,7 +248,7 @@ fu_uefi_setup_bootnext_with_dp (const guint8 *dp_buf, guint8 *opt, gssize opt_si
}
static gboolean
fu_uefi_cmp_asset (const gchar *source, const gchar *target)
fu_uefi_cmp_asset(const gchar *source, const gchar *target)
{
gsize len = 0;
g_autofree gchar *source_checksum = NULL;
@ -257,36 +257,35 @@ fu_uefi_cmp_asset (const gchar *source, const gchar *target)
g_autofree gchar *target_data = NULL;
/* nothing in target yet */
if (!g_file_test (target, G_FILE_TEST_EXISTS))
if (!g_file_test(target, G_FILE_TEST_EXISTS))
return FALSE;
/* test if the file needs to be updated */
if (!g_file_get_contents (source, &source_data, &len, NULL))
if (!g_file_get_contents(source, &source_data, &len, NULL))
return FALSE;
source_checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA256,
(guchar *) source_data, len);
if (!g_file_get_contents (target, &target_data, &len, NULL))
source_checksum =
g_compute_checksum_for_data(G_CHECKSUM_SHA256, (guchar *)source_data, len);
if (!g_file_get_contents(target, &target_data, &len, NULL))
return FALSE;
target_checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA256,
(guchar *) target_data, len);
return g_strcmp0 (target_checksum, source_checksum) == 0;
target_checksum =
g_compute_checksum_for_data(G_CHECKSUM_SHA256, (guchar *)target_data, len);
return g_strcmp0(target_checksum, source_checksum) == 0;
}
static gboolean
fu_uefi_copy_asset (const gchar *source, const gchar *target, GError **error)
fu_uefi_copy_asset(const gchar *source, const gchar *target, GError **error)
{
g_autoptr(GFile) source_file = g_file_new_for_path (source);
g_autoptr(GFile) target_file = g_file_new_for_path (target);
g_autoptr(GFile) source_file = g_file_new_for_path(source);
g_autoptr(GFile) target_file = g_file_new_for_path(target);
if (!g_file_copy (source_file,
target_file,
G_FILE_COPY_OVERWRITE,
NULL,
NULL,
NULL,
error)) {
g_prefix_error (error, "Failed to copy %s to %s: ",
source, target);
if (!g_file_copy(source_file,
target_file,
G_FILE_COPY_OVERWRITE,
NULL,
NULL,
NULL,
error)) {
g_prefix_error(error, "Failed to copy %s to %s: ", source, target);
return FALSE;
}

View File

@ -0,0 +1,235 @@
/*
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupdplugin.h>
#include <string.h>
#include "fu-uefi-cod-device.h"
#include "fu-uefi-common.h"
struct _FuUefiCodDevice {
FuUefiDevice parent_instance;
};
G_DEFINE_TYPE(FuUefiCodDevice, fu_uefi_cod_device, FU_TYPE_UEFI_DEVICE)
static gboolean
fu_uefi_cod_device_get_results_for_idx(FuDevice *device, guint idx, GError **error)
{
FuUefiDevice *device_uefi = FU_UEFI_DEVICE(device);
fwupd_guid_t guid = {0x0};
gsize bufsz = 0;
guint32 status = 0;
guint32 total_size = 0;
g_autofree gchar *guidstr = NULL;
g_autofree gchar *name = NULL;
g_autofree guint8 *buf = NULL;
/* read out result */
name = g_strdup_printf("Capsule%04u", idx);
if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_CAPSULE_REPORT,
name,
&buf,
&bufsz,
NULL,
error)) {
g_prefix_error(error, "failed to read %s: ", name);
return FALSE;
}
/* sanity check */
if (!fu_common_read_uint32_safe(buf, bufsz, 0x00, &total_size, G_LITTLE_ENDIAN, error))
return FALSE;
if (total_size < 0x3A) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"EFI_CAPSULE_RESULT_VARIABLE_HEADER too small");
return FALSE;
}
/* verify guid */
if (!fu_memcpy_safe(guid,
sizeof(guid),
0x0, /* dst */
buf,
bufsz,
0x08, /* src */
sizeof(guid),
error))
return FALSE;
guidstr = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN);
if (g_strcmp0(guidstr, fu_uefi_device_get_guid(device_uefi)) != 0) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"wrong GUID, expected %s, got %s",
fu_uefi_device_get_guid(device_uefi),
guidstr);
return FALSE;
}
/* get status */
if (!fu_common_read_uint32_safe(buf, bufsz, 0x28, &status, G_LITTLE_ENDIAN, error))
return FALSE;
fu_uefi_device_set_status(device_uefi, status);
return TRUE;
}
#define VARIABLE_IDX_SIZE 11 /* of CHAR16 */
static gboolean
fu_uefi_cod_device_get_variable_idx(const gchar *name, guint *value, GError **error)
{
gsize bufsz = 0;
g_autofree guint8 *buf = NULL;
g_autofree gchar *str = NULL;
g_autoptr(GError) error_local = NULL;
gunichar2 buf16[VARIABLE_IDX_SIZE] = {0x0};
if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_CAPSULE_REPORT, name, &buf, &bufsz, NULL, error))
return FALSE;
if (!fu_memcpy_safe((guint8 *)buf16,
sizeof(buf16),
0x0, /* dst */
buf,
bufsz,
0x0, /* src */
sizeof(buf16),
error))
return FALSE;
/* parse the value */
str = g_utf16_to_utf8(buf16, VARIABLE_IDX_SIZE, NULL, NULL, error);
if (str == NULL)
return FALSE;
if (!g_str_has_prefix(str, "Capsule")) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"wrong contents, got %s",
str);
return FALSE;
}
if (value != NULL)
*value = fu_common_strtoull(str + strlen("Capsule"));
return TRUE;
}
static gboolean
fu_uefi_cod_device_get_results(FuDevice *device, GError **error)
{
guint capsule_last = 1024;
/* tell us where to stop */
if (!fu_uefi_cod_device_get_variable_idx("CapsuleLast", &capsule_last, error))
return FALSE;
for (guint i = 0; i <= capsule_last; i++) {
g_autoptr(GError) error_local = NULL;
if (fu_uefi_cod_device_get_results_for_idx(device, i, &error_local))
return TRUE;
if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND) &&
!g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
g_propagate_error(error, g_steal_pointer(&error_local));
return FALSE;
}
}
/* nothing found */
return TRUE;
}
static gboolean
fu_uefi_cod_device_write_firmware(FuDevice *device,
FuFirmware *firmware,
FwupdInstallFlags flags,
GError **error)
{
FuUefiDevice *self = FU_UEFI_DEVICE(device);
g_autofree gchar *basename = NULL;
g_autofree gchar *cod_path = NULL;
g_autofree gchar *esp_path = fu_uefi_device_get_esp_path(self);
g_autoptr(GBytes) fw = NULL;
/* ensure we have the existing state */
if (fu_uefi_device_get_guid(self) == NULL) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"cannot update device info with no GUID");
return FALSE;
}
/* copy the capsule */
fw = fu_firmware_get_bytes(firmware, error);
if (fw == NULL)
return FALSE;
basename = g_strdup_printf("fwupd-%s.cap", fu_uefi_device_get_guid(self));
cod_path = g_build_filename(esp_path, "EFI", "UpdateCapsule", basename, NULL);
if (!fu_common_mkdir_parent(cod_path, error))
return FALSE;
if (!fu_common_set_contents_bytes(cod_path, fw, error))
return FALSE;
/*
* NOTE: The EFI spec requires setting OsIndications!
* RT->SetVariable is not supported for all hardware, and so when using
* U-Boot, it applies the capsule even if OsIndications isn't set.
* The capsule is then deleted by U-Boot after it has been deployed.
*/
if (!fu_device_has_private_flag(device, FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE)) {
gsize bufsz = 0;
guint64 os_indications = 0;
g_autofree guint8 *buf = NULL;
if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_GLOBAL,
"OsIndications",
&buf,
&bufsz,
NULL,
error)) {
g_prefix_error(error, "failed to read EFI variable: ");
return FALSE;
}
if (!fu_common_read_uint64_safe(buf,
bufsz,
0x0,
&os_indications,
G_LITTLE_ENDIAN,
error))
return FALSE;
os_indications |= EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED;
if (!fu_efivar_set_data(FU_EFIVAR_GUID_EFI_GLOBAL,
"OsIndications",
(guint8 *)&os_indications,
sizeof(os_indications),
FU_EFIVAR_ATTR_NON_VOLATILE |
FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS |
FU_EFIVAR_ATTR_RUNTIME_ACCESS,
error)) {
g_prefix_error(error, "Could not set OsIndications: ");
return FALSE;
}
}
/* success */
return TRUE;
}
static void
fu_uefi_cod_device_init(FuUefiCodDevice *self)
{
}
static void
fu_uefi_cod_device_class_init(FuUefiCodDeviceClass *klass)
{
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
klass_device->write_firmware = fu_uefi_cod_device_write_firmware;
klass_device->get_results = fu_uefi_cod_device_get_results;
}

View File

@ -0,0 +1,18 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <fwupdplugin.h>
#include "fu-uefi-device.h"
#define FU_TYPE_UEFI_COD_DEVICE (fu_uefi_cod_device_get_type())
G_DECLARE_FINAL_TYPE(FuUefiCodDevice, fu_uefi_cod_device, FU, UEFI_COD_DEVICE, FuUefiDevice)
struct _FuUefiCodDeviceClass {
FuUefiDeviceClass parent_class;
};

View File

@ -14,6 +14,8 @@
#define EFI_CAPSULE_HEADER_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000
#define EFI_CAPSULE_HEADER_FLAGS_INITIATE_RESET 0x00040000
#define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED 0x0000000000000004ULL
typedef struct __attribute__((__packed__)) {
guint16 year;
guint8 month;

View File

@ -15,7 +15,6 @@
#include "fu-uefi-common.h"
#include "fu-uefi-device.h"
#include "fu-uefi-devpath.h"
#include "fu-uefi-bootmgr.h"
#include "fu-uefi-pcrs.h"
typedef struct {
@ -78,8 +77,8 @@ fu_uefi_device_kind_to_string (FuUefiDeviceKind kind)
return NULL;
}
static FuUefiDeviceKind
fu_uefi_device_kind_from_string (const gchar *kind)
FuUefiDeviceKind
fu_uefi_device_kind_from_string(const gchar *kind)
{
if (g_strcmp0 (kind, "system-firmware") == 0)
return FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE;
@ -217,6 +216,42 @@ fu_uefi_device_get_status (FuUefiDevice *self)
return priv->last_attempt_status;
}
void
fu_uefi_device_set_status(FuUefiDevice *self, FuUefiDeviceStatus status)
{
FuUefiDevicePrivate *priv = GET_PRIVATE(self);
const gchar *tmp;
g_autofree gchar *err_msg = NULL;
g_autofree gchar *version_str = NULL;
g_return_if_fail(FU_IS_UEFI_DEVICE(self));
/* cache for later */
priv->last_attempt_status = status;
/* all good */
if (status == FU_UEFI_DEVICE_STATUS_SUCCESS) {
fu_device_set_update_state(FU_DEVICE(self), FWUPD_UPDATE_STATE_SUCCESS);
return;
}
/* something went wrong */
if (status == FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_AC ||
status == FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_BATT) {
fu_device_set_update_state(FU_DEVICE(self), FWUPD_UPDATE_STATE_FAILED_TRANSIENT);
} else {
fu_device_set_update_state(FU_DEVICE(self), FWUPD_UPDATE_STATE_FAILED);
}
version_str = g_strdup_printf("%u", priv->last_attempt_version);
tmp = fu_uefi_device_status_to_string(status);
if (tmp == NULL) {
err_msg = g_strdup_printf("failed to update to %s", version_str);
} else {
err_msg = g_strdup_printf("failed to update to %s: %s", version_str, tmp);
}
fu_device_set_update_error(FU_DEVICE(self), err_msg);
}
guint32
fu_uefi_device_get_capsule_flags (FuUefiDevice *self)
{
@ -233,8 +268,8 @@ fu_uefi_device_get_guid (FuUefiDevice *self)
return priv->fw_class;
}
static gchar *
fu_uefi_device_build_varname (FuUefiDevice *self)
gchar *
fu_uefi_device_build_varname(FuUefiDevice *self)
{
FuUefiDevicePrivate *priv = GET_PRIVATE (self);
return g_strdup_printf ("fwupd-%s-%"G_GUINT64_FORMAT,
@ -356,10 +391,9 @@ fu_uefi_device_build_dp_buf (const gchar *path, gsize *bufsz, GError **error)
return g_steal_pointer (&dp_buf);
}
static GBytes *
fu_uefi_device_fixup_firmware (FuDevice *device, GBytes *fw, GError **error)
GBytes *
fu_uefi_device_fixup_firmware(FuUefiDevice *self, GBytes *fw, GError **error)
{
FuUefiDevice *self = FU_UEFI_DEVICE (device);
FuUefiDevicePrivate *priv = GET_PRIVATE (self);
gsize fw_length;
const guint8 *data = g_bytes_get_data (fw, &fw_length);
@ -555,82 +589,6 @@ fu_uefi_device_cleanup (FuDevice *device,
return TRUE;
}
static gboolean
fu_uefi_device_write_firmware (FuDevice *device,
FuFirmware *firmware,
FwupdInstallFlags install_flags,
GError **error)
{
FuUefiDevice *self = FU_UEFI_DEVICE (device);
FuUefiDevicePrivate *priv = GET_PRIVATE (self);
FuUefiBootmgrFlags flags = FU_UEFI_BOOTMGR_FLAG_NONE;
const gchar *bootmgr_desc = "Linux Firmware Updater";
g_autofree gchar *esp_path = fu_volume_get_mount_point (priv->esp);
g_autoptr(GBytes) fixed_fw = NULL;
g_autoptr(GBytes) fw = NULL;
g_autofree gchar *basename = NULL;
g_autofree gchar *directory = NULL;
g_autofree gchar *fn = NULL;
g_autofree gchar *varname = fu_uefi_device_build_varname (self);
/* ensure we have the existing state */
if (priv->fw_class == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"cannot update device info with no GUID");
return FALSE;
}
/* get default image */
fw = fu_firmware_get_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* save the blob to the ESP */
directory = fu_uefi_get_esp_path_for_os (device, esp_path);
basename = g_strdup_printf ("fwupd-%s.cap", priv->fw_class);
fn = g_build_filename (directory, "fw", basename, NULL);
if (!fu_common_mkdir_parent (fn, error))
return FALSE;
fixed_fw = fu_uefi_device_fixup_firmware (device, fw, error);
if (fixed_fw == NULL)
return FALSE;
if (!fu_common_set_contents_bytes (fn, fixed_fw, error))
return FALSE;
/* delete the logs to save space; use fwupdate to debug the EFI binary */
if (fu_efivar_exists (FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_VERBOSE")) {
if (!fu_efivar_delete (FU_EFIVAR_GUID_FWUPDATE,
"FWUPDATE_VERBOSE", error))
return FALSE;
}
if (fu_efivar_exists (FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_DEBUG_LOG")) {
if (!fu_efivar_delete (FU_EFIVAR_GUID_FWUPDATE,
"FWUPDATE_DEBUG_LOG", error))
return FALSE;
}
/* set the blob header shared with fwupd.efi */
if (!fu_uefi_device_write_update_info (self, fn, varname, priv->fw_class, error))
return FALSE;
/* update the firmware before the bootloader runs */
if (fu_device_has_private_flag (device, FU_UEFI_DEVICE_FLAG_USE_SHIM_FOR_SB))
flags |= FU_UEFI_BOOTMGR_FLAG_USE_SHIM_FOR_SB;
if (fu_device_has_private_flag (device, FU_UEFI_DEVICE_FLAG_USE_SHIM_UNIQUE))
flags |= FU_UEFI_BOOTMGR_FLAG_USE_SHIM_UNIQUE;
/* some legacy devices use the old name to deduplicate boot entries */
if (fu_device_has_private_flag (device, FU_UEFI_DEVICE_FLAG_USE_LEGACY_BOOTMGR_DESC))
bootmgr_desc = "Linux-Firmware-Updater";
if (!fu_uefi_bootmgr_bootnext (device, esp_path, bootmgr_desc, flags, error))
return FALSE;
/* success! */
return TRUE;
}
static gboolean
fu_uefi_device_add_system_checksum (FuDevice *device, GError **error)
{
@ -759,54 +717,21 @@ fu_uefi_device_probe (FuDevice *device, GError **error)
static gboolean
fu_uefi_device_get_results (FuDevice *device, GError **error)
{
FuUefiDevice *device_uefi = FU_UEFI_DEVICE (device);
FuUefiDeviceStatus status = fu_uefi_device_get_status (device_uefi);
const gchar *tmp;
g_autofree gchar *err_msg = NULL;
g_autofree gchar *version_str = NULL;
g_autoptr(GError) error_local = NULL;
FuUefiDevice *self = FU_UEFI_DEVICE(device);
FuUefiDevicePrivate *priv = GET_PRIVATE(self);
/* trivial case */
if (status == FU_UEFI_DEVICE_STATUS_SUCCESS) {
fu_device_set_update_state (device, FWUPD_UPDATE_STATE_SUCCESS);
return TRUE;
}
/* check if something rudely removed our BOOTXXXX entry */
if (!fu_uefi_bootmgr_verify_fwupd (&error_local)) {
if (fu_device_has_private_flag (device, FU_UEFI_DEVICE_FLAG_SUPPORTS_BOOT_ORDER_LOCK)) {
g_prefix_error (&error_local,
"boot entry missing; "
"perhaps 'Boot Order Lock' enabled in the BIOS: ");
fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT);
} else {
g_prefix_error (&error_local, "boot entry missing: ");
fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED);
}
fu_device_set_update_error (device, error_local->message);
return TRUE;
}
/* something went wrong */
if (status == FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_AC ||
status == FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_BATT) {
fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT);
} else {
fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED);
}
version_str = g_strdup_printf ("%u", fu_uefi_device_get_version_error (device_uefi));
tmp = fu_uefi_device_status_to_string (status);
if (tmp == NULL) {
err_msg = g_strdup_printf ("failed to update to %s",
version_str);
} else {
err_msg = g_strdup_printf ("failed to update to %s: %s",
version_str, tmp);
}
fu_device_set_update_error (device, err_msg);
/* just set the update error */
fu_uefi_device_set_status(self, priv->last_attempt_status);
return TRUE;
}
gchar *
fu_uefi_device_get_esp_path(FuUefiDevice *self)
{
FuUefiDevicePrivate *priv = GET_PRIVATE(self);
return fu_volume_get_mount_point(priv->esp);
}
static void
fu_uefi_device_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
@ -830,7 +755,7 @@ fu_uefi_device_set_property (GObject *object, guint prop_id,
priv->fw_version_lowest = g_value_get_uint (value);
break;
case PROP_LAST_ATTEMPT_STATUS:
priv->last_attempt_status = g_value_get_uint (value);
fu_uefi_device_set_status(self, g_value_get_uint(value));
break;
case PROP_LAST_ATTEMPT_VERSION:
priv->last_attempt_version = g_value_get_uint (value);
@ -867,6 +792,9 @@ fu_uefi_device_init (FuUefiDevice *self)
fu_device_register_private_flag (FU_DEVICE (self),
FU_UEFI_DEVICE_FLAG_FALLBACK_TO_REMOVABLE_PATH,
"fallback-to-removable-path");
fu_device_register_private_flag(FU_DEVICE(self),
FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE,
"no-rt-set-variable");
}
static void
@ -896,7 +824,6 @@ fu_uefi_device_class_init (FuUefiDeviceClass *klass)
klass_device->to_string = fu_uefi_device_to_string;
klass_device->probe = fu_uefi_device_probe;
klass_device->prepare = fu_uefi_device_prepare;
klass_device->write_firmware = fu_uefi_device_write_firmware;
klass_device->cleanup = fu_uefi_device_cleanup;
klass_device->report_metadata_pre = fu_uefi_device_report_metadata_pre;
klass_device->report_metadata_post = fu_uefi_device_report_metadata_post;
@ -954,37 +881,3 @@ fu_uefi_device_class_init (FuUefiDeviceClass *klass)
G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_FMP_HARDWARE_INSTANCE, pspec);
}
FuUefiDevice *
fu_uefi_device_new_from_dev (FuDevice *dev)
{
const gchar *tmp;
FuUefiDevice *self;
FuUefiDevicePrivate *priv;
g_return_val_if_fail (fu_device_get_guid_default (dev) != NULL, NULL);
/* create virtual object not backed by an ESRT entry */
self = g_object_new (FU_TYPE_UEFI_DEVICE, NULL);
fu_device_incorporate (FU_DEVICE (self), dev);
priv = GET_PRIVATE (self);
priv->fw_class = g_strdup (fu_device_get_guid_default (dev));
tmp = fu_device_get_metadata (dev, FU_DEVICE_METADATA_UEFI_DEVICE_KIND);
priv->kind = fu_uefi_device_kind_from_string (tmp);
priv->capsule_flags = fu_device_get_metadata_integer (dev, FU_DEVICE_METADATA_UEFI_CAPSULE_FLAGS);
priv->fw_version = fu_device_get_metadata_integer (dev, FU_DEVICE_METADATA_UEFI_FW_VERSION);
g_assert (priv->fw_class != NULL);
return self;
}
FuUefiDevice *
fu_uefi_device_new_from_guid (const gchar *guid)
{
FuUefiDevice *self;
FuUefiDevicePrivate *priv;
self = g_object_new (FU_TYPE_UEFI_DEVICE, NULL);
priv = GET_PRIVATE (self);
priv->fw_class = g_strdup (guid);
fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_NUMBER);
return self;
}

View File

@ -79,15 +79,26 @@ typedef enum {
* Use shim to load fwupdx64.efi when SecureBoot is turned on.
*/
#define FU_UEFI_DEVICE_FLAG_USE_SHIM_FOR_SB (1 << 5)
/**
* FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE:
*
* Do not use RT->SetVariable.
*/
#define FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE (1 << 6)
FuUefiDeviceKind
fu_uefi_device_kind_from_string(const gchar *kind);
FuUefiDevice *fu_uefi_device_new_from_guid (const gchar *guid);
FuUefiDevice *fu_uefi_device_new_from_dev (FuDevice *dev);
void fu_uefi_device_set_esp (FuUefiDevice *self,
FuVolume *esp);
gboolean fu_uefi_device_clear_status (FuUefiDevice *self,
GError **error);
FuUefiDeviceKind fu_uefi_device_get_kind (FuUefiDevice *self);
const gchar *fu_uefi_device_get_guid (FuUefiDevice *self);
gchar *
fu_uefi_device_get_esp_path(FuUefiDevice *self);
gchar *
fu_uefi_device_build_varname(FuUefiDevice *self);
guint32 fu_uefi_device_get_version (FuUefiDevice *self);
guint32 fu_uefi_device_get_version_lowest (FuUefiDevice *self);
guint32 fu_uefi_device_get_version_error (FuUefiDevice *self);
@ -103,3 +114,7 @@ gboolean fu_uefi_device_write_update_info (FuUefiDevice *self,
const gchar *varname,
const gchar *guid,
GError **error);
GBytes *
fu_uefi_device_fixup_firmware(FuUefiDevice *self, GBytes *fw, GError **error);
void
fu_uefi_device_set_status(FuUefiDevice *self, FuUefiDeviceStatus status);

View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
* Copyright (C) 2018 Mario Limonciello <mario.limonciello@amd.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupdplugin.h>
#include "fu-uefi-bootmgr.h"
#include "fu-uefi-common.h"
#include "fu-uefi-nvram-device.h"
struct _FuUefiNvramDevice {
FuUefiDevice parent_instance;
};
G_DEFINE_TYPE(FuUefiNvramDevice, fu_uefi_nvram_device, FU_TYPE_UEFI_DEVICE)
static gboolean
fu_uefi_nvram_device_get_results(FuDevice *device, GError **error)
{
g_autoptr(GError) error_local = NULL;
/* check if something rudely removed our BOOTXXXX entry */
if (!fu_uefi_bootmgr_verify_fwupd(&error_local)) {
if (fu_device_has_private_flag(device,
FU_UEFI_DEVICE_FLAG_SUPPORTS_BOOT_ORDER_LOCK)) {
g_prefix_error(&error_local,
"boot entry missing; "
"perhaps 'Boot Order Lock' enabled in the BIOS: ");
fu_device_set_update_state(device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT);
} else {
g_prefix_error(&error_local, "boot entry missing: ");
fu_device_set_update_state(device, FWUPD_UPDATE_STATE_FAILED);
}
fu_device_set_update_error(device, error_local->message);
return TRUE;
}
/* parent */
return FU_DEVICE_CLASS(fu_uefi_nvram_device_parent_class)->get_results(device, error);
}
static gboolean
fu_uefi_nvram_device_write_firmware(FuDevice *device,
FuFirmware *firmware,
FwupdInstallFlags flags,
GError **error)
{
FuUefiDevice *self = FU_UEFI_DEVICE(device);
FuUefiBootmgrFlags bootmgr_flags = FU_UEFI_BOOTMGR_FLAG_NONE;
const gchar *bootmgr_desc = "Linux Firmware Updater";
const gchar *fw_class = fu_uefi_device_get_guid(self);
g_autofree gchar *esp_path = fu_uefi_device_get_esp_path(self);
g_autoptr(GBytes) fixed_fw = NULL;
g_autoptr(GBytes) fw = NULL;
g_autofree gchar *basename = NULL;
g_autofree gchar *directory = NULL;
g_autofree gchar *fn = NULL;
g_autofree gchar *varname = fu_uefi_device_build_varname(self);
/* ensure we have the existing state */
if (fw_class == NULL) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"cannot update device info with no GUID");
return FALSE;
}
/* get default image */
fw = fu_firmware_get_bytes(firmware, error);
if (fw == NULL)
return FALSE;
/* save the blob to the ESP */
directory = fu_uefi_get_esp_path_for_os(device, esp_path);
basename = g_strdup_printf("fwupd-%s.cap", fw_class);
fn = g_build_filename(directory, "fw", basename, NULL);
if (!fu_common_mkdir_parent(fn, error))
return FALSE;
fixed_fw = fu_uefi_device_fixup_firmware(self, fw, error);
if (fixed_fw == NULL)
return FALSE;
if (!fu_common_set_contents_bytes(fn, fixed_fw, error))
return FALSE;
/* delete the logs to save space; use fwupdate to debug the EFI binary */
if (fu_efivar_exists(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_VERBOSE")) {
if (!fu_efivar_delete(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_VERBOSE", error))
return FALSE;
}
if (fu_efivar_exists(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_DEBUG_LOG")) {
if (!fu_efivar_delete(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_DEBUG_LOG", error))
return FALSE;
}
/* set the blob header shared with fwupd.efi */
if (!fu_uefi_device_write_update_info(self, fn, varname, fw_class, error))
return FALSE;
/* update the firmware before the bootloader runs */
if (fu_device_has_private_flag(device, FU_UEFI_DEVICE_FLAG_USE_SHIM_FOR_SB))
bootmgr_flags |= FU_UEFI_BOOTMGR_FLAG_USE_SHIM_FOR_SB;
if (fu_device_has_private_flag(device, FU_UEFI_DEVICE_FLAG_USE_SHIM_UNIQUE))
bootmgr_flags |= FU_UEFI_BOOTMGR_FLAG_USE_SHIM_UNIQUE;
/* some legacy devices use the old name to deduplicate boot entries */
if (fu_device_has_private_flag(device, FU_UEFI_DEVICE_FLAG_USE_LEGACY_BOOTMGR_DESC))
bootmgr_desc = "Linux-Firmware-Updater";
if (!fu_uefi_bootmgr_bootnext(device, esp_path, bootmgr_desc, bootmgr_flags, error))
return FALSE;
/* success! */
return TRUE;
}
static void
fu_uefi_nvram_device_init(FuUefiNvramDevice *self)
{
}
static void
fu_uefi_nvram_device_class_init(FuUefiNvramDeviceClass *klass)
{
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
klass_device->get_results = fu_uefi_nvram_device_get_results;
klass_device->write_firmware = fu_uefi_nvram_device_write_firmware;
}

View File

@ -0,0 +1,18 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <fwupdplugin.h>
#include "fu-uefi-device.h"
#define FU_TYPE_UEFI_NVRAM_DEVICE (fu_uefi_nvram_device_get_type())
G_DECLARE_FINAL_TYPE(FuUefiNvramDevice, fu_uefi_nvram_device, FU, UEFI_NVRAM_DEVICE, FuUefiDevice)
struct _FuUefiNvramDeviceClass {
FuUefiDeviceClass parent_class;
};

View File

@ -7,8 +7,9 @@
#include "config.h"
#include <fwupdplugin.h>
#include <glib/gi18n.h>
#include <glib-unix.h>
#include <glib/gi18n.h>
#include <locale.h>
#include <stdlib.h>
#include <unistd.h>
@ -16,8 +17,9 @@
#include "fu-context-private.h"
#include "fu-ucs2.h"
#include "fu-uefi-backend.h"
#include "fu-uefi-cod-device.h"
#include "fu-uefi-common.h"
#include "fu-uefi-device.h"
#include "fu-uefi-nvram-device.h"
#include "fu-uefi-update-info.h"
/* custom return code */
@ -62,6 +64,7 @@ main (int argc, char *argv[])
gboolean ret;
gboolean verbose = FALSE;
g_autofree gchar *apply = FALSE;
g_autofree gchar *type = FALSE;
g_autofree gchar *esp_path = NULL;
g_autofree gchar *flags = FALSE;
g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
@ -69,48 +72,113 @@ main (int argc, char *argv[])
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(FuVolume) esp = NULL;
const GOptionEntry options[] = {
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
/* TRANSLATORS: command line option */
_("Show extra debugging information"), NULL },
{ "version", '\0', 0, G_OPTION_ARG_NONE, &action_version,
/* TRANSLATORS: command line option */
_("Display version"), NULL },
{ "log", 'L', 0, G_OPTION_ARG_NONE, &action_log,
/* TRANSLATORS: command line option */
_("Show the debug log from the last attempted update"), NULL },
{ "list", 'l', 0, G_OPTION_ARG_NONE, &action_list,
/* TRANSLATORS: command line option */
_("List supported firmware updates"), NULL },
{ "supported", 's', 0, G_OPTION_ARG_NONE, &action_supported,
/* TRANSLATORS: command line option */
_("Query for firmware update support"), NULL },
{ "info", 'i', 0, G_OPTION_ARG_NONE, &action_info,
/* TRANSLATORS: command line option */
_("Show the information of firmware update status"), NULL },
{ "enable", 'e', 0, G_OPTION_ARG_NONE, &action_enable,
/* TRANSLATORS: command line option */
_("Enable firmware update support on supported systems"), NULL },
{ "esp-path", 'p', 0, G_OPTION_ARG_STRING, &esp_path,
/* TRANSLATORS: command line option */
_("Override the default ESP path"),
/* TRANSLATORS: command argument: uppercase, spaces->dashes */
_("PATH") },
{ "set-debug", 'd', 0, G_OPTION_ARG_NONE, &action_set_debug,
/* TRANSLATORS: command line option */
_("Set the debugging flag during update"), NULL },
{ "unset-debug", 'D', 0, G_OPTION_ARG_NONE, &action_unset_debug,
/* TRANSLATORS: command line option */
_("Unset the debugging flag during update"), NULL },
{ "apply", 'a', 0, G_OPTION_ARG_STRING, &apply,
/* TRANSLATORS: command line option */
_("Apply firmware updates"),
/* TRANSLATORS: command argument: uppercase, spaces->dashes */
C_("A single GUID", "GUID") },
{ "flags", 'f', 0, G_OPTION_ARG_STRING, &flags,
/* TRANSLATORS: command line option */
_("Use quirk flags when installing firmware"), NULL },
{ NULL}
};
{"verbose",
'v',
G_OPTION_FLAG_NONE,
G_OPTION_ARG_NONE,
&verbose,
/* TRANSLATORS: command line option */
_("Show extra debugging information"),
NULL},
{"version",
'\0',
G_OPTION_FLAG_NONE,
G_OPTION_ARG_NONE,
&action_version,
/* TRANSLATORS: command line option */
_("Display version"),
NULL},
{"log",
'L',
G_OPTION_FLAG_NONE,
G_OPTION_ARG_NONE,
&action_log,
/* TRANSLATORS: command line option */
_("Show the debug log from the last attempted update"),
NULL},
{"list",
'l',
G_OPTION_FLAG_NONE,
G_OPTION_ARG_NONE,
&action_list,
/* TRANSLATORS: command line option */
_("List supported firmware updates"),
NULL},
{"supported",
's',
G_OPTION_FLAG_NONE,
G_OPTION_ARG_NONE,
&action_supported,
/* TRANSLATORS: command line option */
_("Query for firmware update support"),
NULL},
{"info",
'i',
G_OPTION_FLAG_NONE,
G_OPTION_ARG_NONE,
&action_info,
/* TRANSLATORS: command line option */
_("Show the information of firmware update status"),
NULL},
{"enable",
'e',
G_OPTION_FLAG_NONE,
G_OPTION_ARG_NONE,
&action_enable,
/* TRANSLATORS: command line option */
_("Enable firmware update support on supported systems"),
NULL},
{"esp-path",
'p',
G_OPTION_FLAG_NONE,
G_OPTION_ARG_STRING,
&esp_path,
/* TRANSLATORS: command line option */
_("Override the default ESP path"),
/* TRANSLATORS: command argument: uppercase, spaces->dashes */
_("PATH")},
{"set-debug",
'd',
G_OPTION_FLAG_NONE,
G_OPTION_ARG_NONE,
&action_set_debug,
/* TRANSLATORS: command line option */
_("Set the debugging flag during update"),
NULL},
{"unset-debug",
'D',
G_OPTION_FLAG_NONE,
G_OPTION_ARG_NONE,
&action_unset_debug,
/* TRANSLATORS: command line option */
_("Unset the debugging flag during update"),
NULL},
{"apply",
'a',
G_OPTION_FLAG_NONE,
G_OPTION_ARG_STRING,
&apply,
/* TRANSLATORS: command line option */
_("Apply firmware updates"),
/* TRANSLATORS: command argument: uppercase, spaces->dashes */
C_("A single GUID", "GUID")},
{"method",
'm',
G_OPTION_FLAG_NONE,
G_OPTION_ARG_STRING,
&type,
/* TRANSLATORS: command line option */
_("Device update method"),
"nvram|cod"},
{"flags",
'f',
G_OPTION_FLAG_NONE,
G_OPTION_ARG_STRING,
&flags,
/* TRANSLATORS: command line option */
_("Use quirk flags when installing firmware"),
NULL},
{NULL}};
setlocale (LC_ALL, "");
@ -316,10 +384,26 @@ main (int argc, char *argv[])
/* apply firmware updates */
if (apply != NULL) {
g_autoptr(FuUefiDevice) dev = fu_uefi_device_new_from_guid (apply);
g_autoptr(FuContext) ctx = fu_context_new();
g_autoptr(FuBackend) backend = fu_uefi_backend_new(ctx);
g_autoptr(FuUefiDevice) dev = NULL;
g_autoptr(GError) error_local = NULL;
g_autoptr(GBytes) fw = NULL;
/* type is specified, otherwise use default */
if (type != NULL) {
if (g_strcmp0(type, "nvram") == 0) {
fu_uefi_backend_set_device_gtype(FU_UEFI_BACKEND(backend),
FU_TYPE_UEFI_NVRAM_DEVICE);
} else if (g_strcmp0(type, "cod") == 0) {
fu_uefi_backend_set_device_gtype(FU_UEFI_BACKEND(backend),
FU_TYPE_UEFI_COD_DEVICE);
} else {
g_printerr("invalid type specified\n");
return EXIT_FAILURE;
}
}
if (argv[1] == NULL) {
g_printerr ("capsule filename required\n");
return EXIT_FAILURE;
@ -329,6 +413,7 @@ main (int argc, char *argv[])
g_printerr ("failed: %s\n", error_local->message);
return EXIT_FAILURE;
}
dev = fu_uefi_backend_device_new_from_guid(FU_UEFI_BACKEND(backend), apply);
fu_uefi_device_set_esp (dev, esp);
if (flags != NULL)
fu_device_set_custom_flags (FU_DEVICE (dev), flags);

View File

@ -10,7 +10,7 @@ endif
install_data(['uefi-capsule.quirk'],
install_dir: join_paths(datadir, 'fwupd', 'quirks.d'))
backend_srcs = []
backend_srcs = ['fu-uefi-backend.c']
if host_machine.system() == 'linux'
backend_srcs += 'fu-uefi-backend-linux.c'
elif host_machine.system() == 'freebsd'
@ -27,6 +27,8 @@ shared_module('fu_plugin_uefi_capsule',
'fu-ucs2.c',
'fu-uefi-bootmgr.c',
'fu-uefi-common.c',
'fu-uefi-cod-device.c',
'fu-uefi-nvram-device.c',
'fu-uefi-device.c',
'fu-uefi-devpath.c',
'fu-uefi-pcrs.c',
@ -64,6 +66,8 @@ fwupdate = executable(
'fu-uefi-bootmgr.c',
'fu-uefi-common.c',
'fu-uefi-device.c',
'fu-uefi-cod-device.c',
'fu-uefi-nvram-device.c',
'fu-uefi-devpath.c',
'fu-uefi-pcrs.c',
'fu-uefi-update-info.c',
@ -145,6 +149,8 @@ if get_option('tests')
'fu-uefi-bootmgr.c',
'fu-uefi-common.c',
'fu-uefi-device.c',
'fu-uefi-cod-device.c',
'fu-uefi-nvram-device.c',
'fu-uefi-devpath.c',
'fu-uefi-pcrs.c',
'fu-uefi-update-info.c',

View File

@ -14,3 +14,6 @@
# with the UEFI removable path enabled, the default esp path is set to /EFI/boot
# the shim EFI binary and presumably this is $ESP/EFI/boot/bootx64.efi
#FallbacktoRemovablePath=false
# allow ignoring the CapsuleOnDisk support advertised by the firmware
#DisableCapsuleUpdateOnDisk=true