mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-14 04:33:08 +00:00
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:
parent
a1f8096c53
commit
3747e245e5
@ -53,6 +53,7 @@ _fwupdtool_opts=(
|
||||
'--prepare'
|
||||
'--cleanup'
|
||||
'--filter'
|
||||
'--method'
|
||||
'--disable-ssl-strict'
|
||||
'--no-safety-check'
|
||||
'--ignore-checksum'
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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 ();
|
||||
|
@ -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);
|
||||
}
|
||||
|
18
plugins/uefi-capsule/fu-uefi-backend-freebsd.h
Normal file
18
plugins/uefi-capsule/fu-uefi-backend-freebsd.h
Normal 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)
|
@ -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);
|
||||
}
|
||||
|
18
plugins/uefi-capsule/fu-uefi-backend-linux.h
Normal file
18
plugins/uefi-capsule/fu-uefi-backend-linux.h
Normal 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)
|
83
plugins/uefi-capsule/fu-uefi-backend.c
Normal file
83
plugins/uefi-capsule/fu-uefi-backend.c
Normal 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)
|
||||
{
|
||||
}
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
235
plugins/uefi-capsule/fu-uefi-cod-device.c
Normal file
235
plugins/uefi-capsule/fu-uefi-cod-device.c
Normal 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;
|
||||
}
|
18
plugins/uefi-capsule/fu-uefi-cod-device.h
Normal file
18
plugins/uefi-capsule/fu-uefi-cod-device.h
Normal 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;
|
||||
};
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
132
plugins/uefi-capsule/fu-uefi-nvram-device.c
Normal file
132
plugins/uefi-capsule/fu-uefi-nvram-device.c
Normal 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;
|
||||
}
|
18
plugins/uefi-capsule/fu-uefi-nvram-device.h
Normal file
18
plugins/uefi-capsule/fu-uefi-nvram-device.h
Normal 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;
|
||||
};
|
@ -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);
|
||||
|
@ -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',
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user