mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-31 15:54:57 +00:00

That giant uint64_t isn't looking so big now, and we'll want to add even more to it in the future. Split out some private flags that are never useful to the client, although the #defines will have to remain until we break API again.
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_internal_flag (dev, FU_DEVICE_INTERNAL_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;
|
|
}
|