mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-10 08:07:35 +00:00

Future metadata from the LVFS will set the protocol the firmware is expected to use. As vendors love to re-use common terms like DFU for incompatible protocols, namespace them with the controlling company ID with an approximate reverse DNS namespace. This also allows more than one plugin to define support for the same protocol, for instance rts54hid+rts54hub and synapticsmst+dell-dock.
204 lines
6.2 KiB
C
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>
|
|
*
|
|
* 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"
|
|
|
|
struct FuPluginData {
|
|
gchar *flashrom_fn;
|
|
};
|
|
|
|
void
|
|
fu_plugin_init (FuPlugin *plugin)
|
|
{
|
|
fu_plugin_alloc_data (plugin, sizeof (FuPluginData));
|
|
fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "org.flashrom");
|
|
}
|
|
|
|
void
|
|
fu_plugin_destroy (FuPlugin *plugin)
|
|
{
|
|
FuPluginData *data = fu_plugin_get_data (plugin);
|
|
g_free (data->flashrom_fn);
|
|
}
|
|
|
|
gboolean
|
|
fu_plugin_startup (FuPlugin *plugin, GError **error)
|
|
{
|
|
FuPluginData *data = fu_plugin_get_data (plugin);
|
|
GPtrArray *hwids;
|
|
g_autoptr(GError) error_local = NULL;
|
|
|
|
/* we need flashrom from the host system */
|
|
data->flashrom_fn = fu_common_find_program_in_path ("flashrom", &error_local);
|
|
|
|
/* search for devices */
|
|
hwids = fu_plugin_get_hwids (plugin);
|
|
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_add_flag (dev, FWUPD_DEVICE_FLAG_INTERNAL);
|
|
if (data->flashrom_fn != NULL) {
|
|
fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE);
|
|
} else {
|
|
fu_device_set_update_error (dev, error_local->message);
|
|
}
|
|
fu_device_add_guid (dev, guid);
|
|
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_set_version (dev, fu_plugin_get_dmi_value (plugin, FU_HWIDS_KEY_BIOS_VERSION));
|
|
fu_plugin_device_add (plugin, dev);
|
|
fu_plugin_cache_add (plugin, device_id, dev);
|
|
break;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static guint
|
|
fu_plugin_flashrom_parse_percentage (const gchar *lines_verbose)
|
|
{
|
|
const guint64 addr_highest = 0x800000;
|
|
guint64 addr_best = 0x0;
|
|
g_auto(GStrv) chunks = NULL;
|
|
|
|
/* parse 0x000000-0x000fff:S, 0x001000-0x001fff:S */
|
|
chunks = g_strsplit_set (lines_verbose, "x-:S, \n\r", -1);
|
|
for (guint i = 0; chunks[i] != NULL; i++) {
|
|
guint64 addr_tmp;
|
|
if (strlen (chunks[i]) != 6)
|
|
continue;
|
|
addr_tmp = g_ascii_strtoull (chunks[i], NULL, 16);
|
|
if (addr_tmp > addr_best)
|
|
addr_best = addr_tmp;
|
|
}
|
|
return (addr_best * 100) / addr_highest;
|
|
}
|
|
|
|
static void
|
|
fu_plugin_flashrom_read_cb (const gchar *line, gpointer user_data)
|
|
{
|
|
FuDevice *device = FU_DEVICE (user_data);
|
|
if (g_strcmp0 (line, "Reading flash...") == 0)
|
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_VERIFY);
|
|
fu_device_set_progress (device, fu_plugin_flashrom_parse_percentage (line));
|
|
}
|
|
|
|
static void
|
|
fu_plugin_flashrom_write_cb (const gchar *line, gpointer user_data)
|
|
{
|
|
FuDevice *device = FU_DEVICE (user_data);
|
|
if (g_strcmp0 (line, "Writing flash...") == 0)
|
|
fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE);
|
|
fu_device_set_progress (device, fu_plugin_flashrom_parse_percentage (line));
|
|
}
|
|
|
|
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 (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)) {
|
|
const gchar *argv[] = {
|
|
data->flashrom_fn,
|
|
"--programmer", "internal:laptop=force_I_want_a_brick",
|
|
"--read", firmware_orig,
|
|
"--verbose", NULL };
|
|
if (!fu_common_spawn_sync ((const gchar * const *) argv,
|
|
fu_plugin_flashrom_read_cb, device,
|
|
NULL, error)) {
|
|
g_prefix_error (error, "failed to get original firmware: ");
|
|
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);
|
|
g_autofree gchar *firmware_fn = NULL;
|
|
g_autofree gchar *tmpdir = NULL;
|
|
const gchar *argv[] = {
|
|
data->flashrom_fn,
|
|
"--programmer", "internal:laptop=force_I_want_a_brick",
|
|
"--write", "xxx",
|
|
"--verbose", NULL };
|
|
|
|
/* write blob to temp location */
|
|
tmpdir = g_dir_make_tmp ("fwupd-XXXXXX", error);
|
|
if (tmpdir == NULL)
|
|
return FALSE;
|
|
firmware_fn = g_build_filename (tmpdir, "flashrom-firmware.bin", NULL);
|
|
if (!fu_common_set_contents_bytes (firmware_fn, blob_fw, error))
|
|
return FALSE;
|
|
|
|
/* use flashrom to write image */
|
|
argv[4] = firmware_fn;
|
|
if (!fu_common_spawn_sync ((const gchar * const *) argv,
|
|
fu_plugin_flashrom_write_cb, device,
|
|
NULL, error)) {
|
|
g_prefix_error (error, "failed to write firmware: ");
|
|
return FALSE;
|
|
}
|
|
|
|
/* delete temp location */
|
|
if (!fu_common_rmtree (tmpdir, error))
|
|
return FALSE;
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|