From a58510b2460e780f8488e399d83252290b7f97ce Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 30 Oct 2019 10:03:12 +0000 Subject: [PATCH] Allow verifying the device image itself from fwupdtool To debug flashing failures it's sometimes requried to get a SPI dump of the hardware to analysis. Add a debug-only command that lets us dump the device from the engine. --- data/bash-completion/fwupdtool.in | 3 +- src/fu-engine.c | 29 +++++++++++++ src/fu-engine.h | 4 ++ src/fu-tool.c | 68 +++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 1 deletion(-) diff --git a/data/bash-completion/fwupdtool.in b/data/bash-completion/fwupdtool.in index c059e677e..384b811f9 100644 --- a/data/bash-completion/fwupdtool.in +++ b/data/bash-completion/fwupdtool.in @@ -21,6 +21,7 @@ _fwupdtool_cmd_list=( 'smbios-dump' 'attach' 'detach' + 'firmware-read' 'verify-update' 'watch' ) @@ -86,7 +87,7 @@ _fwupdtool() esac case $command in - get-details|install|install-blob) + get-details|install|install-blob|firmware-read) #find files if [[ "$prev" = "$command" ]]; then _filedir diff --git a/src/fu-engine.c b/src/fu-engine.c index e4173cd7e..977cf30bd 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -2082,6 +2082,35 @@ fu_engine_update (FuEngine *self, return TRUE; } +GBytes * +fu_engine_firmware_read (FuEngine *self, + FuDevice *device, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuFirmware) firmware = NULL; + + /* open, detach, read, attach, serialize */ + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return NULL; + if (!fu_device_detach (device, error)) + return NULL; + firmware = fu_device_read_firmware (device, error); + if (firmware == NULL) { + g_autoptr(GError) error_local = NULL; + if (!fu_device_attach (device, &error_local)) { + g_warning ("failed to attach after read image failure: %s", + error_local->message); + } + return NULL; + } + if (!fu_device_attach (device, error)) + return NULL; + return fu_firmware_write (firmware, error); +} + gboolean fu_engine_install_blob (FuEngine *self, FuDevice *device, diff --git a/src/fu-engine.h b/src/fu-engine.h index e0749a8b6..fb14fd743 100644 --- a/src/fu-engine.h +++ b/src/fu-engine.h @@ -96,6 +96,10 @@ gboolean fu_engine_verify (FuEngine *self, gboolean fu_engine_verify_update (FuEngine *self, const gchar *device_id, GError **error); +GBytes *fu_engine_firmware_read (FuEngine *self, + FuDevice *device, + FwupdInstallFlags flags, + GError **error); gboolean fu_engine_modify_remote (FuEngine *self, const gchar *remote_id, const gchar *key, diff --git a/src/fu-tool.c b/src/fu-tool.c index 156c1a6ab..daff08177 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -39,6 +39,7 @@ typedef enum { FU_UTIL_OPERATION_UNKNOWN, FU_UTIL_OPERATION_UPDATE, FU_UTIL_OPERATION_INSTALL, + FU_UTIL_OPERATION_READ, FU_UTIL_OPERATION_LAST } FuUtilOperation; @@ -558,6 +559,11 @@ fu_util_update_device_changed_cb (FwupdClient *client, str = g_strdup_printf (_("Installing on %s…"), fwupd_device_get_name (device)); fu_progressbar_set_title (priv->progressbar, str); + } else if (priv->current_operation == FU_UTIL_OPERATION_READ) { + /* TRANSLATORS: %1 is a device name */ + str = g_strdup_printf (_("Reading from %s…"), + fwupd_device_get_name (device)); + fu_progressbar_set_title (priv->progressbar, str); } else { g_warning ("no FuUtilOperation set"); } @@ -660,6 +666,62 @@ fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error) return fu_util_prompt_complete (priv->completion_flags, TRUE, error); } +static gboolean +fu_util_firmware_read (FuUtilPrivate *priv, gchar **values, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0); + g_autoptr(GBytes) blob_fw = NULL; + + /* invalid args */ + if (g_strv_length (values) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* file already exists */ + if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + g_file_test (values[0], G_FILE_TEST_EXISTS)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Filename already exists"); + return FALSE; + } + + /* write a zero lenth file to ensure the destination is writable to + * avoid failing at the end of a potentially lengthy operation */ + if (!fu_common_set_contents_bytes (values[0], blob_empty, error)) + return FALSE; + + /* load engine */ + if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error)) + return FALSE; + + /* get device */ + if (g_strv_length (values) >= 2) { + device = fu_engine_get_device (priv->engine, values[1], error); + if (device == NULL) + return FALSE; + } else { + device = fu_util_prompt_for_device (priv, error); + if (device == NULL) + return FALSE; + } + priv->current_operation = FU_UTIL_OPERATION_READ; + g_signal_connect (priv->engine, "device-changed", + G_CALLBACK (fu_util_update_device_changed_cb), priv); + + /* dump firmware */ + blob_fw = fu_engine_firmware_read (priv->engine, device, priv->flags, error); + if (blob_fw == NULL) + return FALSE; + return fu_common_set_contents_bytes (values[0], blob_fw, error); +} + static gint fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b) { @@ -1685,6 +1747,12 @@ main (int argc, char *argv[]) /* TRANSLATORS: command description */ _("Update the stored metadata with current contents"), fu_util_verify_update); + fu_util_cmd_array_add (cmd_array, + "firmware-read", + "FILENAME [DEVICE-ID]", + /* TRANSLATORS: command description */ + _("Read a firmware blob from a device"), + fu_util_firmware_read); fu_util_cmd_array_add (cmd_array, "firmware-parse", "FILENAME [FIRMWARE_TYPE]",