mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-11 01:17:17 +00:00

Asking the user for the UID mapping isn't working very well, as it requires lots of manual handholding. It also doesn't work very well when the device vendor does not actually have a PCI ID or if the vendor has split into two entities. Just use the OUI address as an additional VendorID and match any of the device IDs against any of the metadata-supplied values.
269 lines
8.0 KiB
C
269 lines
8.0 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
|
*
|
|
* Copyright (C) 2017 Richard Hughes <richard@hughsie.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-hash.h"
|
|
#include "libflashrom.h"
|
|
|
|
#define SELFCHECK_TRUE 1
|
|
|
|
struct FuPluginData {
|
|
gsize flash_size;
|
|
struct flashrom_flashctx *flashctx;
|
|
struct flashrom_layout *layout;
|
|
struct flashrom_programmer *flashprog;
|
|
};
|
|
|
|
void
|
|
fu_plugin_init (FuPlugin *plugin)
|
|
{
|
|
fu_plugin_set_build_hash (plugin, FU_BUILD_HASH);
|
|
fu_plugin_alloc_data (plugin, sizeof (FuPluginData));
|
|
}
|
|
|
|
void
|
|
fu_plugin_destroy (FuPlugin *plugin)
|
|
{
|
|
FuPluginData *data = fu_plugin_get_data (plugin);
|
|
flashrom_layout_release (data->layout);
|
|
flashrom_programmer_shutdown (data->flashprog);
|
|
flashrom_flash_release (data->flashctx);
|
|
}
|
|
|
|
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
|
|
switch (lvl) {
|
|
case FLASHROM_MSG_ERROR:
|
|
case FLASHROM_MSG_WARN:
|
|
g_warning ("%s", tmp);
|
|
break;
|
|
case FLASHROM_MSG_INFO:
|
|
g_debug ("%s", tmp);
|
|
break;
|
|
case FLASHROM_MSG_DEBUG:
|
|
case FLASHROM_MSG_DEBUG2:
|
|
if (g_getenv ("FWUPD_FLASHROM_VERBOSE") != NULL)
|
|
g_debug ("%s", tmp);
|
|
break;
|
|
case FLASHROM_MSG_SPEW:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
gboolean
|
|
fu_plugin_coldplug (FuPlugin *plugin, GError **error)
|
|
{
|
|
FuPluginData *data = fu_plugin_get_data (plugin);
|
|
GPtrArray *hwids = fu_plugin_get_hwids (plugin);
|
|
const gchar *dmi_vendor;
|
|
g_autoptr(GPtrArray) devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
|
|
|
|
dmi_vendor = fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_BIOS_VENDOR);
|
|
for (guint i = 0; i < hwids->len; i++) {
|
|
const gchar *guid = g_ptr_array_index (hwids, i);
|
|
const gchar *quirk_str;
|
|
g_autofree gchar *quirk_key_prefixed = NULL;
|
|
quirk_key_prefixed = g_strdup_printf ("HwId=%s", guid);
|
|
quirk_str = fu_plugin_lookup_quirk_by_id (plugin,
|
|
quirk_key_prefixed,
|
|
"DeviceId");
|
|
if (quirk_str != NULL) {
|
|
g_autofree gchar *device_id = g_strdup_printf ("flashrom-%s", quirk_str);
|
|
g_autoptr(FuDevice) dev = fu_device_new ();
|
|
fu_device_set_id (dev, device_id);
|
|
fu_device_set_quirks (dev, fu_plugin_get_quirks (plugin));
|
|
fu_device_set_protocol (dev, "org.flashrom");
|
|
fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_INTERNAL);
|
|
fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE);
|
|
fu_device_set_name (dev, fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_PRODUCT_NAME));
|
|
fu_device_set_vendor (dev, fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_MANUFACTURER));
|
|
fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_ENSURE_SEMVER);
|
|
fu_device_set_version (dev, fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_BIOS_VERSION));
|
|
fu_device_add_guid (dev, guid);
|
|
if (dmi_vendor != NULL) {
|
|
g_autofree gchar *vendor_id = g_strdup_printf ("DMI:%s", dmi_vendor);
|
|
fu_device_add_vendor_id (FU_DEVICE (dev), vendor_id);
|
|
}
|
|
g_ptr_array_add (devices, g_steal_pointer (&dev));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* nothing to do, so don't bother initializing flashrom */
|
|
if (devices->len == 0)
|
|
return TRUE;
|
|
|
|
/* actually probe hardware to check for support */
|
|
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);
|
|
if (flashrom_programmer_init (&data->flashprog, "internal", NULL)) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"programmer initialization failed");
|
|
return FALSE;
|
|
}
|
|
if (flashrom_flash_probe (&data->flashctx, data->flashprog, NULL)) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"flash probe failed");
|
|
return FALSE;
|
|
}
|
|
data->flash_size = flashrom_flash_getsize (data->flashctx);
|
|
if (data->flash_size == 0) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"flash size zero");
|
|
return FALSE;
|
|
}
|
|
|
|
/* add devices */
|
|
for (guint i = 0; i < devices->len; i++) {
|
|
FuDevice *dev = g_ptr_array_index (devices, i);
|
|
fu_plugin_device_add (plugin, dev);
|
|
fu_plugin_cache_add (plugin, fu_device_get_id (dev), dev);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fu_plugin_update_prepare (FuPlugin *plugin,
|
|
FwupdInstallFlags flags,
|
|
FuDevice *device,
|
|
GError **error)
|
|
{
|
|
FuPluginData *data = fu_plugin_get_data (plugin);
|
|
g_autofree gchar *firmware_orig = NULL;
|
|
g_autofree gchar *basename = NULL;
|
|
|
|
/* not us */
|
|
if (fu_plugin_cache_lookup (plugin, fu_device_get_id (device)) == NULL)
|
|
return TRUE;
|
|
|
|
/* if the original firmware doesn't exist, grab it now */
|
|
basename = g_strdup_printf ("flashrom-%s.bin", fu_device_get_id (device));
|
|
firmware_orig = g_build_filename (FWUPD_LOCALSTATEDIR, "lib", "fwupd",
|
|
"builder", basename, NULL);
|
|
if (!fu_common_mkdir_parent (firmware_orig, error))
|
|
return FALSE;
|
|
if (!g_file_test (firmware_orig, G_FILE_TEST_EXISTS)) {
|
|
g_autofree guint8 *newcontents = g_malloc0 (data->flash_size);
|
|
g_autoptr(GBytes) buf = NULL;
|
|
|
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_READ);
|
|
if (flashrom_image_read (data->flashctx, newcontents, data->flash_size)) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_READ,
|
|
"failed to back up original firmware");
|
|
return FALSE;
|
|
}
|
|
buf = g_bytes_new_static (newcontents, data->flash_size);
|
|
if (!fu_common_set_contents_bytes (firmware_orig, buf, error))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
fu_plugin_update (FuPlugin *plugin,
|
|
FuDevice *device,
|
|
GBytes *blob_fw,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
FuPluginData *data = fu_plugin_get_data (plugin);
|
|
gsize sz = 0;
|
|
gint rc;
|
|
const guint8 *buf = g_bytes_get_data (blob_fw, &sz);
|
|
|
|
if (flashrom_layout_read_from_ifd (&data->layout, data->flashctx, NULL, 0)) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_READ,
|
|
"failed to read layout from Intel ICH descriptor");
|
|
return FALSE;
|
|
}
|
|
|
|
/* include bios region for safety reasons */
|
|
if (flashrom_layout_include_region (data->layout, "bios")) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"invalid region name");
|
|
return FALSE;
|
|
}
|
|
|
|
/* write region */
|
|
flashrom_layout_set (data->flashctx, data->layout);
|
|
if (sz != data->flash_size) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"invalid image size 0x%x, expected 0x%x",
|
|
(guint) sz, (guint) data->flash_size);
|
|
return FALSE;
|
|
}
|
|
|
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE);
|
|
rc = flashrom_image_write (data->flashctx, (void *) buf, sz, NULL /* refbuffer */);
|
|
if (rc != 0) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_WRITE,
|
|
"image write failed, err=%i", rc);
|
|
return FALSE;
|
|
}
|
|
|
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_VERIFY);
|
|
if (flashrom_image_verify (data->flashctx, (void *) buf, sz)) {
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_WRITE,
|
|
"image verify failed");
|
|
return FALSE;
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|