diff --git a/libfwupdplugin/fu-firmware-image.c b/libfwupdplugin/fu-firmware-image.c index 63a28df39..bfef23a28 100644 --- a/libfwupdplugin/fu-firmware-image.c +++ b/libfwupdplugin/fu-firmware-image.c @@ -259,6 +259,63 @@ fu_firmware_image_parse (FuFirmwareImage *self, return TRUE; } +/** + * fu_firmware_image_build: + * @self: A #FuFirmwareImage + * @n: A #XbNode + * @error: A #GError, or %NULL + * + * Builds a firmware image from an XML manifest. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fu_firmware_image_build (FuFirmwareImage *self, XbNode *n, GError **error) +{ + guint64 tmpval; + const gchar *tmp; + + g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), FALSE); + g_return_val_if_fail (XB_IS_NODE (n), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + tmp = xb_node_query_text (n, "version", NULL); + if (tmp != NULL) + fu_firmware_image_set_version (self, tmp); + tmp = xb_node_query_text (n, "id", NULL); + if (tmp != NULL) + fu_firmware_image_set_id (self, tmp); + tmpval = xb_node_query_text_as_uint (n, "idx", NULL); + if (tmpval != G_MAXUINT64) + fu_firmware_image_set_idx (self, tmpval); + tmpval = xb_node_query_text_as_uint (n, "addr", NULL); + if (tmpval != G_MAXUINT64) + fu_firmware_image_set_addr (self, tmpval); + tmp = xb_node_query_text (n, "filename", NULL); + if (tmp != NULL) { + g_autoptr(GBytes) blob = NULL; + blob = fu_common_get_contents_bytes (tmp, error); + if (blob == NULL) + return FALSE; + fu_firmware_image_set_bytes (self, blob); + fu_firmware_image_set_filename (self, tmp); + } + tmp = xb_node_query_text (n, "data", NULL); + if (tmp != NULL) { + gsize bufsz = 0; + g_autofree guchar *buf = NULL; + g_autoptr(GBytes) blob = NULL; + buf = g_base64_decode (tmp, &bufsz); + blob = g_bytes_new (buf, bufsz); + fu_firmware_image_set_bytes (self, blob); + } + + /* success */ + return TRUE; +} + /** * fu_firmware_image_write: * @self: a #FuPlugin diff --git a/libfwupdplugin/fu-firmware-image.h b/libfwupdplugin/fu-firmware-image.h index 97be313d1..7310a3b8e 100644 --- a/libfwupdplugin/fu-firmware-image.h +++ b/libfwupdplugin/fu-firmware-image.h @@ -8,6 +8,7 @@ #include #include +#include #define FU_TYPE_FIRMWARE_IMAGE (fu_firmware_image_get_type ()) G_DECLARE_DERIVABLE_TYPE (FuFirmwareImage, fu_firmware_image, FU, FIRMWARE_IMAGE, GObject) @@ -56,6 +57,9 @@ gboolean fu_firmware_image_parse (FuFirmwareImage *self, GBytes *fw, FwupdInstallFlags flags, GError **error); +gboolean fu_firmware_image_build (FuFirmwareImage *self, + XbNode *n, + GError **error); GBytes *fu_firmware_image_write (FuFirmwareImage *self, GError **error); GBytes *fu_firmware_image_write_chunk (FuFirmwareImage *self, diff --git a/libfwupdplugin/fu-firmware.c b/libfwupdplugin/fu-firmware.c index 375d060c3..5e5b60bab 100644 --- a/libfwupdplugin/fu-firmware.c +++ b/libfwupdplugin/fu-firmware.c @@ -239,6 +239,56 @@ fu_firmware_parse (FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError return fu_firmware_parse_full (self, fw, 0x0, 0x0, flags, error); } +/** + * fu_firmware_build: + * @self: A #FuFirmware + * @n: A #XbNode + * @error: A #GError, or %NULL + * + * Builds a firmware from an XML manifest. + * + * Returns: %TRUE for success + * + * Since: 1.5.0 + **/ +gboolean +fu_firmware_build (FuFirmware *self, XbNode *n, GError **error) +{ + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS (self); + const gchar *tmp; + g_autoptr(GPtrArray) xb_images = NULL; + + g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE); + g_return_val_if_fail (XB_IS_NODE (n), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* set attributes */ + tmp = xb_node_query_text (n, "version", NULL); + if (tmp != NULL) + fu_firmware_set_version (self, tmp); + + /* parse images */ + xb_images = xb_node_query (n, "image", 0, NULL); + if (xb_images != NULL) { + for (guint i = 0; i < xb_images->len; i++) { + XbNode *xb_image = g_ptr_array_index (xb_images, i); + g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (NULL); + if (!fu_firmware_image_build (img, xb_image, error)) + return FALSE; + fu_firmware_add_image (self, img); + } + } + + /* subclassed */ + if (klass->build != NULL) { + if (!klass->build (self, n, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + /** * fu_firmware_parse_file: * @self: A #FuFirmware diff --git a/libfwupdplugin/fu-firmware.h b/libfwupdplugin/fu-firmware.h index 798787aef..119de9a82 100644 --- a/libfwupdplugin/fu-firmware.h +++ b/libfwupdplugin/fu-firmware.h @@ -32,8 +32,11 @@ struct _FuFirmwareClass GBytes *fw, FwupdInstallFlags flags, GError **error); + gboolean (*build) (FuFirmware *self, + XbNode *n, + GError **error); /*< private >*/ - gpointer padding[28]; + gpointer padding[27]; }; /** @@ -67,6 +70,9 @@ gboolean fu_firmware_tokenize (FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError **error); +gboolean fu_firmware_build (FuFirmware *self, + XbNode *n, + GError **error); gboolean fu_firmware_parse (FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, diff --git a/libfwupdplugin/fu-self-test.c b/libfwupdplugin/fu-self-test.c index 7db112b8e..238a5755a 100644 --- a/libfwupdplugin/fu-self-test.c +++ b/libfwupdplugin/fu-self-test.c @@ -1539,6 +1539,75 @@ fu_firmware_srec_tokenization_func (void) g_assert_cmpint (rcd->buf->data[0], ==, 0x50); } +static void +fu_firmware_build_func (void) +{ + gboolean ret; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) firmware = fu_firmware_new (); + g_autoptr(FuFirmwareImage) img = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) blob2 = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); + g_autoptr(XbBuilderSource) source = xb_builder_source_new (); + g_autoptr(XbNode) n = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *buf = + "\n" + "\n" + " 1.2.3\n" + " \n" + " 4.5.6\n" + " header\n" + " 456\n" + " 0x456\n" + " aGVsbG8=\n" + " \n" + " \n" + " 7.8.9\n" + " header\n" + " 789\n" + " 0x789\n" + " \n" + "\n"; + blob = g_bytes_new_static (buf, strlen (buf)); + g_assert_no_error (error); + g_assert_nonnull (blob); + + /* parse XML */ + ret = xb_builder_source_load_bytes (source, blob, XB_BUILDER_SOURCE_FLAG_NONE, &error); + g_assert_no_error (error); + g_assert (ret); + xb_builder_import_source (builder, source); + silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); + g_assert_no_error (error); + g_assert_nonnull (silo); + n = xb_silo_query_first (silo, "firmware", &error); + g_assert_no_error (error); + g_assert_nonnull (n); + + /* build object */ + ret = fu_firmware_build (firmware, n, &error); + g_assert_no_error (error); + g_assert (ret); + g_assert_cmpstr (fu_firmware_get_version (firmware), ==, "1.2.3"); + + /* verify image */ + img = fu_firmware_get_image_by_id (firmware, "header", &error); + g_assert_no_error (error); + g_assert_nonnull (img); + g_assert_cmpstr (fu_firmware_image_get_version (img), ==, "4.5.6"); + g_assert_cmpint (fu_firmware_image_get_idx (img), ==, 456); + g_assert_cmpint (fu_firmware_image_get_addr (img), ==, 0x456); + blob2 = fu_firmware_image_write (img, &error); + g_assert_no_error (error); + g_assert_nonnull (blob2); + g_assert_cmpint (g_bytes_get_size (blob2), ==, 5); + str = g_strndup (g_bytes_get_data (blob2, NULL), g_bytes_get_size (blob2)); + g_assert_cmpstr (str, ==, "hello"); +} + static void fu_firmware_dfu_func (void) { @@ -1976,6 +2045,7 @@ main (int argc, char **argv) g_test_add_func ("/fwupd/smbios3", fu_smbios3_func); g_test_add_func ("/fwupd/firmware", fu_firmware_func); g_test_add_func ("/fwupd/firmware{dedupe}", fu_firmware_dedupe_func); + g_test_add_func ("/fwupd/firmware{build}", fu_firmware_build_func); g_test_add_func ("/fwupd/firmware{ihex}", fu_firmware_ihex_func); g_test_add_func ("/fwupd/firmware{ihex-offset}", fu_firmware_ihex_offset_func); g_test_add_func ("/fwupd/firmware{ihex-signed}", fu_firmware_ihex_signed_func); diff --git a/libfwupdplugin/fwupdplugin.map b/libfwupdplugin/fwupdplugin.map index f79829e75..89353c287 100644 --- a/libfwupdplugin/fwupdplugin.map +++ b/libfwupdplugin/fwupdplugin.map @@ -618,9 +618,11 @@ LIBFWUPDPLUGIN_1.5.0 { fu_device_report_metadata_post; fu_device_report_metadata_pre; fu_firmware_add_flag; + fu_firmware_build; fu_firmware_flag_from_string; fu_firmware_flag_to_string; fu_firmware_has_flag; + fu_firmware_image_build; fu_firmware_image_get_filename; fu_firmware_image_parse; fu_firmware_image_set_filename; diff --git a/plugins/dfu/meson.build b/plugins/dfu/meson.build index 0a5a9a4e2..4fca2246f 100644 --- a/plugins/dfu/meson.build +++ b/plugins/dfu/meson.build @@ -24,6 +24,7 @@ dfu = static_library( dependencies : [ giounix, libm, + libxmlb, gusb, gudev, ], diff --git a/plugins/synaptics-prometheus/meson.build b/plugins/synaptics-prometheus/meson.build index b7b5d0a65..fed45214c 100644 --- a/plugins/synaptics-prometheus/meson.build +++ b/plugins/synaptics-prometheus/meson.build @@ -76,6 +76,7 @@ if get_option('tests') ], dependencies : [ gio, + libxmlb, ], link_with : [ fwupd, diff --git a/plugins/synaptics-rmi/meson.build b/plugins/synaptics-rmi/meson.build index 3e71f911c..3272fad87 100644 --- a/plugins/synaptics-rmi/meson.build +++ b/plugins/synaptics-rmi/meson.build @@ -48,6 +48,7 @@ if get_option('tests') ], dependencies : [ gio, + libxmlb, ], link_with : [ fwupd, diff --git a/src/fu-tool.c b/src/fu-tool.c index fdb78f27e..0694f3f9f 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -1639,12 +1639,16 @@ fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error) } static gchar * -fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error) +fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, gboolean add_builder, GError **error) { g_autoptr(GPtrArray) firmware_types = NULL; guint idx; firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine); + /* add fake entry */ + if (add_builder) + g_ptr_array_add (firmware_types, g_strdup ("builder")); + /* TRANSLATORS: get interactive prompt */ g_print ("%s\n", _("Choose a firmware type:")); /* TRANSLATORS: this is to abort the interactive prompt */ @@ -1697,7 +1701,7 @@ fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error) /* find the GType to use */ if (firmware_type == NULL) - firmware_type = fu_util_prompt_for_firmware_type (priv, error); + firmware_type = fu_util_prompt_for_firmware_type (priv, TRUE, error); if (firmware_type == NULL) return FALSE; gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type); @@ -1716,6 +1720,53 @@ fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } +static FuFirmware * +fu_util_firmware_builder_new (FuUtilPrivate *priv, GBytes *fw, GError **error) +{ + const gchar *tmp; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new (); + g_autoptr(XbBuilderSource) source = xb_builder_source_new (); + g_autoptr(XbNode) n = NULL; + g_autoptr(XbSilo) silo = NULL; + + /* parse XML */ + if (!xb_builder_source_load_bytes (source, fw, + XB_BUILDER_SOURCE_FLAG_NONE, + error)) { + g_prefix_error (error, "could not parse XML: "); + return NULL; + } + xb_builder_import_source (builder, source); + silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error); + if (silo == NULL) + return NULL; + + /* create FuFirmware of specific GType */ + n = xb_silo_query_first (silo, "firmware", error); + if (n == NULL) + return FALSE; + tmp = xb_node_get_attr (n, "gtype"); + if (tmp != NULL) { + GType gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, tmp); + if (gtype == G_TYPE_INVALID) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not supported", tmp); + return NULL; + } + firmware = g_object_new (gtype, NULL); + } else { + firmware = fu_firmware_new (); + } + if (!fu_firmware_build (firmware, n, error)) + return NULL; + + /* success */ + return g_steal_pointer (&firmware); +} + static gboolean fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error) { @@ -1756,20 +1807,29 @@ fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error) /* find the GType to use */ if (firmware_type_src == NULL) - firmware_type_src = fu_util_prompt_for_firmware_type (priv, error); + firmware_type_src = fu_util_prompt_for_firmware_type (priv, TRUE, error); if (firmware_type_src == NULL) return FALSE; if (firmware_type_dst == NULL) - firmware_type_dst = fu_util_prompt_for_firmware_type (priv, error); + firmware_type_dst = fu_util_prompt_for_firmware_type (priv, FALSE, error); if (firmware_type_dst == NULL) return FALSE; - gtype_src = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_src); - if (gtype_src == G_TYPE_INVALID) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_NOT_FOUND, - "GType %s not supported", firmware_type_src); - return FALSE; + if (g_strcmp0 (firmware_type_src, "builder") == 0) { + firmware_src = fu_util_firmware_builder_new (priv, blob_src, error); + if (firmware_src == NULL) + return FALSE; + } else { + gtype_src = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_src); + if (gtype_src == G_TYPE_INVALID) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "GType %s not supported", firmware_type_src); + return FALSE; + } + firmware_src = g_object_new (gtype_src, NULL); + if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error)) + return FALSE; } gtype_dst = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_dst); if (gtype_dst == G_TYPE_INVALID) { @@ -1779,9 +1839,6 @@ fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error) "GType %s not supported", firmware_type_dst); return FALSE; } - firmware_src = g_object_new (gtype_src, NULL); - if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error)) - return FALSE; str_src = fu_firmware_to_string (firmware_src); g_print ("%s", str_src); diff --git a/src/meson.build b/src/meson.build index d63a52092..8a07de033 100644 --- a/src/meson.build +++ b/src/meson.build @@ -345,6 +345,7 @@ if get_option('tests') fwupdplugin_incdir, ], dependencies : [ + libxmlb, gio, ], link_with : [