fwupd/plugins/flashrom/fu-plugin-flashrom.c
Richard Hughes b333e0045c Split out a shared system context
There is a lot of code in fwupd that just assigns a shared object type to
a FuPlugin, and then for each device on that plugin assigns that same shared
object to each FuDevice.

Rather than proxy several kinds of information stores over two different levels
of abstraction create a 'context' which contains the shared *system* state
between the daemon, the plugins and the daemon.

This will allow us to hold other per-machine state in the future, for instance
the system battery level or AC state.
2021-04-01 21:11:29 +01:00

204 lines
6.2 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
* Copyright (C) 2019 9elements Agency GmbH <patrick.rudolph@9elements.com>
*
* Licensed under the GNU General Public License Version 2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <string.h>
#include "fu-plugin-vfuncs.h"
#include "fu-flashrom-device.h"
#include <libflashrom.h>
#define SELFCHECK_TRUE 1
void
fu_plugin_init (FuPlugin *plugin)
{
fu_plugin_set_build_hash (plugin, FU_BUILD_HASH);
fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "linux_lockdown");
fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_CONFLICTS, "coreboot"); /* obsoleted */
fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_REQUIRE_HWID);
}
static int
fu_plugin_flashrom_debug_cb (enum flashrom_log_level lvl, const char *fmt, va_list args)
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
g_autofree gchar *tmp = g_strdup_vprintf (fmt, args);
#pragma clang diagnostic pop
g_autofree gchar *str = fu_common_strstrip (tmp);
if (g_strcmp0 (str, "OK.") == 0 || g_strcmp0 (str, ".") == 0)
return 0;
switch (lvl) {
case FLASHROM_MSG_ERROR:
case FLASHROM_MSG_WARN:
g_warning ("%s", str);
break;
case FLASHROM_MSG_INFO:
g_debug ("%s", str);
break;
case FLASHROM_MSG_DEBUG:
case FLASHROM_MSG_DEBUG2:
if (g_getenv ("FWUPD_FLASHROM_VERBOSE") != NULL)
g_debug ("%s", str);
break;
case FLASHROM_MSG_SPEW:
break;
default:
break;
}
return 0;
}
static void
fu_plugin_flashrom_device_set_version (FuPlugin *plugin, FuDevice *device)
{
FuContext *ctx = fu_plugin_get_context (plugin);
const gchar *version;
const gchar *version_major;
const gchar *version_minor;
/* as-is */
version = fu_context_get_hwid_value (ctx, FU_HWIDS_KEY_BIOS_VERSION);
if (version != NULL) {
/* some Lenovo hardware requires a specific prefix for the EC,
* so strip it before we use ensure-semver */
if (strlen (version) > 9 && g_str_has_prefix (version, "CBET"))
version += 9;
/* this may not "stick" if there are no numeric chars */
fu_device_set_version (device, version);
if (fu_device_get_version (device) != NULL)
return;
}
/* component parts only */
version_major = fu_context_get_hwid_value (ctx, FU_HWIDS_KEY_BIOS_MAJOR_RELEASE);
version_minor = fu_context_get_hwid_value (ctx, FU_HWIDS_KEY_BIOS_MINOR_RELEASE);
if (version_major != NULL && version_minor != NULL) {
g_autofree gchar *tmp = g_strdup_printf ("%s.%s.0",
version_major,
version_minor);
fu_device_set_version (device, tmp);
return;
}
}
static void
fu_plugin_flashrom_device_set_bios_info (FuPlugin *plugin, FuDevice *device)
{
FuContext *ctx = fu_plugin_get_context (plugin);
const guint8 *buf;
gsize bufsz;
guint32 bios_char = 0x0;
guint8 bios_sz = 0x0;
g_autoptr(GBytes) bios_table = NULL;
/* get SMBIOS info */
bios_table = fu_context_get_smbios_data (ctx, FU_SMBIOS_STRUCTURE_TYPE_BIOS);
if (bios_table == NULL)
return;
/* ROM size */
buf = g_bytes_get_data (bios_table, &bufsz);
if (fu_common_read_uint8_safe (buf, bufsz, 0x9, &bios_sz, NULL)) {
guint64 firmware_size = (bios_sz + 1) * 64 * 1024;
fu_device_set_firmware_size_max (device, firmware_size);
}
/* BIOS characteristics */
if (fu_common_read_uint32_safe (buf, bufsz, 0xa, &bios_char, G_LITTLE_ENDIAN, NULL)) {
if ((bios_char & (1 << 11)) == 0)
fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE);
}
}
static void
fu_plugin_flashrom_device_set_hwids (FuPlugin *plugin, FuDevice *device)
{
FuContext *ctx = fu_plugin_get_context (plugin);
static const gchar *hwids[] = {
"HardwareID-3",
"HardwareID-4",
"HardwareID-5",
"HardwareID-6",
"HardwareID-10",
/* a more useful one for coreboot branch detection */
FU_HWIDS_KEY_MANUFACTURER "&"
FU_HWIDS_KEY_FAMILY "&"
FU_HWIDS_KEY_PRODUCT_NAME "&"
FU_HWIDS_KEY_PRODUCT_SKU "&"
FU_HWIDS_KEY_BIOS_VENDOR,
};
/* don't include FU_HWIDS_KEY_BIOS_VERSION */
for (guint i = 0; i < G_N_ELEMENTS (hwids); i++) {
g_autofree gchar *str = NULL;
str = fu_context_get_hwid_replace_value (ctx, hwids[i], NULL);
if (str != NULL)
fu_device_add_instance_id (device, str);
}
}
gboolean
fu_plugin_coldplug (FuPlugin *plugin, GError **error)
{
FuContext *ctx = fu_plugin_get_context (plugin);
const gchar *dmi_vendor;
g_autoptr(FuDevice) device = fu_flashrom_device_new ();
fu_device_set_context (device, ctx);
fu_device_set_name (device, fu_context_get_hwid_value (ctx, FU_HWIDS_KEY_PRODUCT_NAME));
fu_device_set_vendor (device, fu_context_get_hwid_value (ctx, FU_HWIDS_KEY_MANUFACTURER));
/* use same VendorID logic as with UEFI */
dmi_vendor = fu_context_get_hwid_value (ctx, FU_HWIDS_KEY_BIOS_VENDOR);
if (dmi_vendor != NULL) {
g_autofree gchar *vendor_id = g_strdup_printf ("DMI:%s", dmi_vendor);
fu_device_add_vendor_id (FU_DEVICE (device), vendor_id);
}
fu_plugin_flashrom_device_set_version (plugin, device);
fu_plugin_flashrom_device_set_hwids (plugin, device);
fu_plugin_flashrom_device_set_bios_info (plugin, device);
if (!fu_device_setup (device, error))
return FALSE;
/* success */
fu_plugin_device_add (plugin, device);
fu_plugin_cache_add (plugin, fu_device_get_id (device), device);
return TRUE;
}
gboolean
fu_plugin_startup (FuPlugin *plugin, GError **error)
{
if (flashrom_init (SELFCHECK_TRUE)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"flashrom initialization error");
return FALSE;
}
flashrom_set_log_callback (fu_plugin_flashrom_debug_cb);
return TRUE;
}