From a417d6c82a172012c0502cd9a0f0845ecf42a98f Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 27 Aug 2018 21:24:27 +0100 Subject: [PATCH] trivial: Allow setting the firmware min and max sizes from quirk files --- plugins/colorhug/colorhug.quirk | 6 ++ plugins/csr/fu-csr-device.c | 42 ++++++----- plugins/unifying/lu-device.c | 9 --- plugins/unifying/meson.build | 6 ++ plugins/unifying/unifying.quirk | 19 +++++ src/fu-device.c | 125 +++++++++++++++++++++++++++++++- src/fu-device.h | 12 ++- src/fu-quirks.h | 22 ++++++ 8 files changed, 212 insertions(+), 29 deletions(-) create mode 100644 plugins/unifying/unifying.quirk diff --git a/plugins/colorhug/colorhug.quirk b/plugins/colorhug/colorhug.quirk index 417c8d40d..d142fd010 100644 --- a/plugins/colorhug/colorhug.quirk +++ b/plugins/colorhug/colorhug.quirk @@ -3,6 +3,8 @@ Plugin = colorhug Flags = is-bootloader Guid = 40338ceb-b966-4eae-adae-9c32edfcc484 +FirmwareSizeMin = 0x2000 +FirmwareSizeMax = 0x8000 [DeviceInstanceId=USB\VID_273F&PID_1001] Plugin = colorhug Flags = none @@ -17,6 +19,8 @@ Flags = none Summary = An open source display colorimeter Icon = colorimeter-colorhug Guid = 2082b5e0-7a64-478a-b1b2-e3404fab6dad +FirmwareSizeMin = 0x2000 +FirmwareSizeMax = 0x8000 [DeviceInstanceId=USB\VID_273F&PID_1005] Plugin = colorhug Flags = is-bootloader @@ -28,6 +32,8 @@ Plugin = colorhug Flags = halfsize,none Summary = An open source ambient light sensor Guid = 84f40464-9272-4ef7-9399-cd95f12da696 +FirmwareSizeMin = 0x1000 +FirmwareSizeMax = 0x4000 [DeviceInstanceId=USB\VID_273F&PID_1006] Plugin = colorhug Flags = halfsize,is-bootloader diff --git a/plugins/csr/fu-csr-device.c b/plugins/csr/fu-csr-device.c index 933369d87..e49e558d6 100644 --- a/plugins/csr/fu-csr-device.c +++ b/plugins/csr/fu-csr-device.c @@ -437,25 +437,16 @@ _dfu_firmware_get_default_element_data (DfuFirmware *firmware) return dfu_element_get_contents (element); } -static gboolean -fu_csr_device_download (FuDevice *device, GBytes *blob, GError **error) +static GBytes * +fu_csr_device_prepare_firmware (FuDevice *device, GBytes *fw, GError **error) { - FuCsrDevice *self = FU_CSR_DEVICE (device); GBytes *blob_noftr; - const guint8 *data; - gsize sz = 0; - guint16 idx; g_autoptr(DfuFirmware) dfu_firmware = dfu_firmware_new (); - g_autoptr(GBytes) blob_empty = NULL; - g_autoptr(GPtrArray) chunks = NULL; - - /* notify UI */ - fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); /* parse the file */ - if (!dfu_firmware_parse_data (dfu_firmware, blob, + if (!dfu_firmware_parse_data (dfu_firmware, fw, DFU_FIRMWARE_PARSE_FLAG_NONE, error)) - return FALSE; + return NULL; if (g_getenv ("FWUPD_CSR_VERBOSE") != NULL) { g_autofree gchar *fw_str = NULL; fw_str = dfu_firmware_to_string (dfu_firmware); @@ -466,7 +457,7 @@ fu_csr_device_download (FuDevice *device, GBytes *blob, GError **error) FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "expected DFU firmware"); - return FALSE; + return NULL; } /* get the blob from the firmware file */ @@ -476,13 +467,27 @@ fu_csr_device_download (FuDevice *device, GBytes *blob, GError **error) FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "firmware contained no data"); - return FALSE; + return NULL; } + /* success */ + return g_bytes_ref (blob_noftr); +} + +static gboolean +fu_csr_device_download (FuDevice *device, GBytes *blob, GError **error) +{ + FuCsrDevice *self = FU_CSR_DEVICE (device); + guint16 idx; + g_autoptr(GBytes) blob_empty = NULL; + g_autoptr(GPtrArray) chunks = NULL; + + /* notify UI */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + /* create chunks */ - data = g_bytes_get_data (blob_noftr, &sz); - chunks = fu_chunk_array_new (data, (guint32) sz, 0x0, 0x0, - FU_CSR_PACKET_DATA_SIZE - FU_CSR_COMMAND_HEADER_SIZE); + chunks = fu_chunk_array_new_from_bytes (blob, 0x0, 0x0, + FU_CSR_PACKET_DATA_SIZE - FU_CSR_COMMAND_HEADER_SIZE); /* send to hardware */ for (idx = 0; idx < chunks->len; idx++) { @@ -576,6 +581,7 @@ fu_csr_device_class_init (FuCsrDeviceClass *klass) klass_device->to_string = fu_csr_device_to_string; klass_device->write_firmware = fu_csr_device_download; klass_device->read_firmware = fu_csr_device_upload; + klass_device->prepare_firmware = fu_csr_device_prepare_firmware; klass_device->attach = fu_csr_device_attach; klass_usb_device->open = fu_csr_device_open; klass_usb_device->close = fu_csr_device_close; diff --git a/plugins/unifying/lu-device.c b/plugins/unifying/lu-device.c index cec6ffd3e..b67e5051a 100644 --- a/plugins/unifying/lu-device.c +++ b/plugins/unifying/lu-device.c @@ -899,15 +899,6 @@ lu_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) g_return_val_if_fail (LU_IS_DEVICE (device), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - /* corrupt */ - if (g_bytes_get_size (fw) < 0x4000) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "firmware is too small"); - return FALSE; - } - /* call device-specific method */ if (klass->write_firmware == NULL) { g_set_error (error, diff --git a/plugins/unifying/meson.build b/plugins/unifying/meson.build index 8b7c0d841..262280c87 100644 --- a/plugins/unifying/meson.build +++ b/plugins/unifying/meson.build @@ -1,5 +1,11 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginUnifying"'] +install_data([ + 'unifying.quirk', + ], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + shared_module('fu_plugin_unifying', sources : [ 'fu-plugin-unifying.c', diff --git a/plugins/unifying/unifying.quirk b/plugins/unifying/unifying.quirk new file mode 100644 index 000000000..079c01b10 --- /dev/null +++ b/plugins/unifying/unifying.quirk @@ -0,0 +1,19 @@ +# Nordic +[DeviceInstanceId=USB\VID_046D&PID_AAAA] +Plugin = unifying +FirmwareSizeMin = 0x4000 + +# Nordic Pico +[DeviceInstanceId=USB\VID_046D&PID_AAAE] +Plugin = unifying +FirmwareSizeMin = 0x4000 + +# Nordic +[DeviceInstanceId=USB\VID_046D&PID_AAAC] +Plugin = unifying +FirmwareSizeMin = 0x4000 + +# Nordic Pico +[DeviceInstanceId=USB\VID_046D&PID_AAAD] +Plugin = unifying +FirmwareSizeMin = 0x4000 diff --git a/src/fu-device.c b/src/fu-device.c index 5ebe02856..40621e180 100644 --- a/src/fu-device.c +++ b/src/fu-device.c @@ -41,6 +41,8 @@ typedef struct { guint order; guint priority; gboolean done_probe; + guint64 size_min; + guint64 size_max; } FuDevicePrivate; enum { @@ -549,6 +551,14 @@ fu_device_add_guid_quirks (FuDevice *device, const gchar *guid) if (tmp != NULL) fu_device_add_parent_guid (device, tmp); + /* firmware size */ + tmp = fu_quirks_lookup_by_guid (priv->quirks, guid, FU_QUIRKS_FIRMWARE_SIZE_MIN); + if (tmp != NULL) + fu_device_set_firmware_size_min (device, fu_common_strtoull (tmp)); + tmp = fu_quirks_lookup_by_guid (priv->quirks, guid, FU_QUIRKS_FIRMWARE_SIZE_MAX); + if (tmp != NULL) + fu_device_set_firmware_size_max (device, fu_common_strtoull (tmp)); + /* children */ tmp = fu_quirks_lookup_by_guid (priv->quirks, guid, FU_QUIRKS_CHILDREN); if (tmp != NULL) { @@ -563,6 +573,40 @@ fu_device_add_guid_quirks (FuDevice *device, const gchar *guid) } } +/** + * fu_device_set_firmware_size_min: + * @device: A #FuDevice + * @size_min: Size in bytes + * + * Sets the minimum allowed size of the firmware blob. + * + * Since: 1.1.2 + **/ +void +fu_device_set_firmware_size_min (FuDevice *device, guint64 size_min) +{ + FuDevicePrivate *priv = GET_PRIVATE (device); + g_return_if_fail (FU_IS_DEVICE (device)); + priv->size_min = size_min; +} + +/** + * fu_device_set_firmware_size_max: + * @device: A #FuDevice + * @size_min: Size in bytes + * + * Sets the maximum allowed size of the firmware blob. + * + * Since: 1.1.2 + **/ +void +fu_device_set_firmware_size_max (FuDevice *device, guint64 size_max) +{ + FuDevicePrivate *priv = GET_PRIVATE (device); + g_return_if_fail (FU_IS_DEVICE (device)); + priv->size_max = size_max; +} + static void fu_device_add_guid_safe (FuDevice *device, const gchar *guid) { @@ -1169,6 +1213,14 @@ fu_device_to_string (FuDevice *device) fwupd_pad_kv_str (str, "AlternateId", priv->alternate_id); if (priv->equivalent_id != NULL) fwupd_pad_kv_str (str, "EquivalentId", priv->equivalent_id); + if (priv->size_min > 0) { + g_autofree gchar *sz = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->size_min); + fwupd_pad_kv_str (str, "FirmwareSizeMin", sz); + } + if (priv->size_max > 0) { + g_autofree gchar *sz = g_strdup_printf ("%" G_GUINT64_FORMAT, priv->size_max); + fwupd_pad_kv_str (str, "FirmwareSizeMax", sz); + } keys = g_hash_table_get_keys (priv->metadata); for (GList *l = keys; l != NULL; l = l->next) { const gchar *key = l->data; @@ -1258,6 +1310,7 @@ gboolean fu_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) { FuDeviceClass *klass = FU_DEVICE_GET_CLASS (device); + g_autoptr(GBytes) fw_new = NULL; g_return_val_if_fail (FU_IS_DEVICE (device), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); @@ -1271,8 +1324,78 @@ fu_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) return FALSE; } + /* prepare (e.g. decompress) firmware */ + fw_new = fu_device_prepare_firmware (device, fw, error); + if (fw_new == NULL) + return FALSE; + /* call vfunc */ - return klass->write_firmware (device, fw, error); + return klass->write_firmware (device, fw_new, error); +} + +/** + * fu_device_prepare_firmware: + * @device: A #FuDevice + * @fw: A #GBytes + * @error: A #GError + * + * Prepares the firmware by calling an optional device-specific vfunc for the + * device, which can do things like decompressing or parsing of the firmware + * data. + * + * For all firmware, this checks the size of the firmware if limits have been + * set using fu_device_set_firmware_size_min(), fu_device_set_firmware_size_max() + * or using a quirk entry. + * + * Returns: (return full): A new #GBytes, or %NULL for error + * + * Since: 1.1.2 + **/ +GBytes * +fu_device_prepare_firmware (FuDevice *device, GBytes *fw, GError **error) +{ + FuDeviceClass *klass = FU_DEVICE_GET_CLASS (device); + FuDevicePrivate *priv = GET_PRIVATE (device); + guint64 fw_sz; + g_autoptr(GBytes) fw_new = NULL; + + g_return_val_if_fail (FU_IS_DEVICE (device), FALSE); + g_return_val_if_fail (fw != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* optionally subclassed */ + if (klass->prepare_firmware != NULL) { + fw_new = klass->prepare_firmware (device, fw, error); + if (fw_new == NULL) + return FALSE; + } else { + fw_new = g_bytes_ref (fw); + } + + /* check size */ + fw_sz = (guint64) g_bytes_get_size (fw_new); + if (priv->size_max > 0 && fw_sz > priv->size_max) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware is %04x bytes larger than the allowed " + "maximum size of %04x bytes", + (guint) (fw_sz - priv->size_max), + (guint) priv->size_max); + return FALSE; + } + if (priv->size_min > 0 && fw_sz < priv->size_min) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware is %04x bytes smaller than the allowed " + "minimum size of %04x bytes", + (guint) (priv->size_min - fw_sz), + (guint) priv->size_max); + return FALSE; + } + + return g_steal_pointer (&fw_new); } /** diff --git a/src/fu-device.h b/src/fu-device.h index 773312d05..6862a0750 100644 --- a/src/fu-device.h +++ b/src/fu-device.h @@ -37,8 +37,11 @@ struct _FuDeviceClass GError **error); gboolean (*probe) (FuDevice *device, GError **error); + GBytes *(*prepare_firmware) (FuDevice *device, + GBytes *fw, + GError **error); /*< private >*/ - gpointer padding[25]; + gpointer padding[24]; }; /** @@ -157,6 +160,10 @@ void fu_device_set_remove_delay (FuDevice *device, FwupdStatus fu_device_get_status (FuDevice *device); void fu_device_set_status (FuDevice *device, FwupdStatus status); +void fu_device_set_firmware_size_min (FuDevice *device, + guint64 size_min); +void fu_device_set_firmware_size_max (FuDevice *device, + guint64 size_max); guint fu_device_get_progress (FuDevice *device); void fu_device_set_progress (FuDevice *device, guint progress); @@ -170,6 +177,9 @@ FwupdRelease *fu_device_get_release_default (FuDevice *device); gboolean fu_device_write_firmware (FuDevice *device, GBytes *fw, GError **error); +GBytes *fu_device_prepare_firmware (FuDevice *device, + GBytes *fw, + GError **error); GBytes *fu_device_read_firmware (FuDevice *device, GError **error); gboolean fu_device_attach (FuDevice *device, diff --git a/src/fu-quirks.h b/src/fu-quirks.h index e23925d75..63a2be55a 100644 --- a/src/fu-quirks.h +++ b/src/fu-quirks.h @@ -181,6 +181,28 @@ gchar *fu_quirks_lookup_by_guids (FuQuirks *self, */ #define FU_QUIRKS_VENDOR "Vendor" +/** + * FU_QUIRKS_FIRMWARE_SIZE_MIN: + * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` + * @value: the vendor, e.g. `0x10000` + * + * Sets the minimum allowed firmware size. + * + * Since: 1.1.2 + */ +#define FU_QUIRKS_FIRMWARE_SIZE_MIN "FirmwareSizeMin" + +/** + * FU_QUIRKS_FIRMWARE_SIZE_MAX: + * @key: the USB device ID, e.g. `DeviceInstanceId=USB\VID_0763&PID_2806` + * @value: the vendor, e.g. `0x10000` + * + * Sets the maximum allowed firmware size. + * + * Since: 1.1.2 + */ +#define FU_QUIRKS_FIRMWARE_SIZE_MAX "FirmwareSizeMax" + G_END_DECLS #endif /* __FU_QUIRKS_H */