diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index b4e5db19c..f5c01a2a4 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -262,6 +262,7 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %endif %{_libdir}/fwupd-plugins-3/libfu_plugin_dfu.so %{_libdir}/fwupd-plugins-3/libfu_plugin_ebitdo.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_flashrom.so %{_libdir}/fwupd-plugins-3/libfu_plugin_nitrokey.so %{_libdir}/fwupd-plugins-3/libfu_plugin_nvme.so %if 0%{?have_redfish} diff --git a/plugins/flashrom/README.md b/plugins/flashrom/README.md new file mode 100644 index 000000000..4df977c38 --- /dev/null +++ b/plugins/flashrom/README.md @@ -0,0 +1,7 @@ +Flashrom +======== + +Introduction +------------ + +This plugin uses `flashrom` to update the system firmware. diff --git a/plugins/flashrom/example/build.sh b/plugins/flashrom/example/build.sh new file mode 100755 index 000000000..83b9d0c42 --- /dev/null +++ b/plugins/flashrom/example/build.sh @@ -0,0 +1,4 @@ +#/bin/sh +appstream-util validate-relax com.Flashrom.Laptop.metainfo.xml +tar -cf firmware.tar startup.sh random-tool +gcab --create --nopath Flashrom-Laptop-1.2.3.cab firmware.tar com.Flashrom.Laptop.metainfo.xml diff --git a/plugins/flashrom/example/com.Flashrom.Laptop.metainfo.xml b/plugins/flashrom/example/com.Flashrom.Laptop.metainfo.xml new file mode 100644 index 000000000..711c70f9f --- /dev/null +++ b/plugins/flashrom/example/com.Flashrom.Laptop.metainfo.xml @@ -0,0 +1,42 @@ + + + + com.Flashrom.Laptop.firmware + Flashrom Laptop Firmware + System firmware for a Flashrom laptop + + + The laptop can be updated using flashrom. + + + + + a0ce5085-2dea-5086-ae72-45810a186ad0 + + http://www.bbc.co.uk/ + CC0-1.0 + Proprietary + Flashrom + + + + + + This release updates a frobnicator to frob faster. + + + + + + + + startup.sh + firmware.bin + + + + + org.freedesktop.fwupd + + + diff --git a/plugins/flashrom/example/random-tool b/plugins/flashrom/example/random-tool new file mode 100755 index 000000000..039cc8db7 --- /dev/null +++ b/plugins/flashrom/example/random-tool @@ -0,0 +1,2 @@ +#/bin/sh +echo "hello from the sandbox" diff --git a/plugins/flashrom/example/startup.sh b/plugins/flashrom/example/startup.sh new file mode 100755 index 000000000..22742b96e --- /dev/null +++ b/plugins/flashrom/example/startup.sh @@ -0,0 +1,10 @@ +#/bin/sh + +# do something with the old firmware +sha1sum /boot/flashrom-librem15v3.bin + +# run a random tool +./random-tool + +# this is the deliverable +cp /boot/flashrom-librem15v3.bin firmware.bin diff --git a/plugins/flashrom/flashrom.quirk b/plugins/flashrom/flashrom.quirk new file mode 100644 index 000000000..a5d833f49 --- /dev/null +++ b/plugins/flashrom/flashrom.quirk @@ -0,0 +1,3 @@ +# Purism +[HwId=a0ce5085-2dea-5086-ae72-45810a186ad0] +DeviceId=librem15v3 diff --git a/plugins/flashrom/fu-plugin-flashrom.c b/plugins/flashrom/fu-plugin-flashrom.c new file mode 100644 index 000000000..b26025568 --- /dev/null +++ b/plugins/flashrom/fu-plugin-flashrom.c @@ -0,0 +1,203 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2017 Richard Hughes + * + * 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 + +#include "fu-plugin.h" +#include "fu-plugin-vfuncs.h" + +struct FuPluginData { + gchar *flashrom_fn; +}; + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); +} + +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; + + /* we need flashrom from the host system */ + data->flashrom_fn = g_find_program_in_path ("flashrom"); + + /* 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_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, "flashrom binary not found"); + } + 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 (FuPlugin *plugin, 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) +{ + FuPlugin *plugin = FU_PLUGIN (user_data); + if (g_strcmp0 (line, "Reading flash...") == 0) + fu_plugin_set_status (plugin, FWUPD_STATUS_DEVICE_VERIFY); + fu_plugin_set_percentage (plugin, fu_plugin_flashrom_parse_percentage (plugin, line)); +} + +static void +fu_plugin_flashrom_write_cb (const gchar *line, gpointer user_data) +{ + FuPlugin *plugin = FU_PLUGIN (user_data); + if (g_strcmp0 (line, "Writing flash...") == 0) + fu_plugin_set_status (plugin, FWUPD_STATUS_DEVICE_WRITE); + fu_plugin_set_percentage (plugin, fu_plugin_flashrom_parse_percentage (plugin, 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, plugin, + NULL, error)) { + g_prefix_error (error, "failed to get original firmware: "); + return FALSE; + } + fu_plugin_set_status (plugin, FWUPD_STATUS_IDLE); + } + + 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, plugin, + NULL, error)) { + g_prefix_error (error, "failed to write firmware: "); + return FALSE; + } + + /* delete temp location */ + if (!fu_common_rmtree (tmpdir, error)) + return FALSE; + + /* success */ + fu_plugin_set_status (plugin, FWUPD_STATUS_IDLE); + return TRUE; +} diff --git a/plugins/flashrom/meson.build b/plugins/flashrom/meson.build new file mode 100644 index 000000000..1b4250f03 --- /dev/null +++ b/plugins/flashrom/meson.build @@ -0,0 +1,25 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginFlashrom"'] + +install_data(['flashrom.quirk'], + install_dir: join_paths(get_option('datadir'), 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_flashrom', + sources : [ + 'fu-plugin-flashrom.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + c_args : [ + cargs, + '-DLOCALSTATEDIR="' + localstatedir + '"', + ], + dependencies : [ + plugin_deps, + ], +) diff --git a/plugins/meson.build b/plugins/meson.build index 41770922a..b504b2118 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -1,6 +1,7 @@ subdir('dfu') subdir('colorhug') subdir('ebitdo') +subdir('flashrom') subdir('steelseries') subdir('nitrokey') subdir('test')
+ The laptop can be updated using flashrom. +
+ This release updates a frobnicator to frob faster. +