mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-09 18:17:54 +00:00
845 lines
21 KiB
C
845 lines
21 KiB
C
/*
|
|
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#define G_LOG_DOMAIN "FuFirmware"
|
|
|
|
#include "config.h"
|
|
|
|
#include "fu-common.h"
|
|
#include "fu-firmware.h"
|
|
#include "fu-firmware-image-private.h"
|
|
|
|
/**
|
|
* SECTION:fu-firmware
|
|
* @short_description: a firmware file
|
|
*
|
|
* An object that represents a firmware file.
|
|
* See also: #FuDfuFirmware, #FuIhexFirmware, #FuSrecFirmware
|
|
*/
|
|
|
|
typedef struct {
|
|
FuFirmwareFlags flags;
|
|
GPtrArray *images; /* FuFirmwareImage */
|
|
gchar *version;
|
|
} FuFirmwarePrivate;
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (FuFirmware, fu_firmware, G_TYPE_OBJECT)
|
|
#define GET_PRIVATE(o) (fu_firmware_get_instance_private (o))
|
|
|
|
/**
|
|
* fu_firmware_flag_to_string:
|
|
* @flag: A #FuFirmwareFlags, e.g. %FU_FIRMWARE_FLAG_DEDUPE_ID
|
|
*
|
|
* Converts a #FuFirmwareFlags to a string.
|
|
*
|
|
* Return value: identifier string
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
const gchar *
|
|
fu_firmware_flag_to_string (FuFirmwareFlags flag)
|
|
{
|
|
if (flag == FU_FIRMWARE_FLAG_NONE)
|
|
return "none";
|
|
if (flag == FU_FIRMWARE_FLAG_DEDUPE_ID)
|
|
return "dedupe-id";
|
|
if (flag == FU_FIRMWARE_FLAG_DEDUPE_IDX)
|
|
return "dedupe-idx";
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_flag_from_string:
|
|
* @flag: A string, e.g. `dedupe-id`
|
|
*
|
|
* Converts a string to a #FuFirmwareFlags.
|
|
*
|
|
* Return value: enumerated value
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
FuFirmwareFlags
|
|
fu_firmware_flag_from_string (const gchar *flag)
|
|
{
|
|
if (g_strcmp0 (flag, "dedupe-id") == 0)
|
|
return FU_FIRMWARE_FLAG_DEDUPE_ID;
|
|
if (g_strcmp0 (flag, "dedupe-idx") == 0)
|
|
return FU_FIRMWARE_FLAG_DEDUPE_IDX;
|
|
return FU_FIRMWARE_FLAG_NONE;
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_add_flag:
|
|
* @firmware: A #FuFirmware
|
|
* @flag: the #FuFirmwareFlags
|
|
*
|
|
* Adds a specific firmware flag to the firmware.
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
void
|
|
fu_firmware_add_flag (FuFirmware *firmware, FuFirmwareFlags flag)
|
|
{
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (firmware);
|
|
g_return_if_fail (FU_IS_FIRMWARE (firmware));
|
|
priv->flags |= flag;
|
|
}
|
|
|
|
|
|
/**
|
|
* fu_firmware_has_flag:
|
|
* @firmware: A #FuFirmware
|
|
* @flag: the #FuFirmwareFlags
|
|
*
|
|
* Finds if the firmware has a specific firmware flag.
|
|
*
|
|
* Returns: %TRUE if the flag is set
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fu_firmware_has_flag (FuFirmware *firmware, FuFirmwareFlags flag)
|
|
{
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (firmware);
|
|
g_return_val_if_fail (FU_IS_FIRMWARE (firmware), FALSE);
|
|
return (priv->flags & flag) > 0;
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_get_version:
|
|
* @self: A #FuFirmware
|
|
*
|
|
* Gets an optional version that represents the firmware.
|
|
*
|
|
* Returns: a string, or %NULL
|
|
*
|
|
* Since: 1.3.3
|
|
**/
|
|
const gchar *
|
|
fu_firmware_get_version (FuFirmware *self)
|
|
{
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (self);
|
|
g_return_val_if_fail (FU_IS_FIRMWARE (self), NULL);
|
|
return priv->version;
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_set_version:
|
|
* @self: A #FuFirmware
|
|
* @version: A string version, or %NULL
|
|
*
|
|
* Sets an optional version that represents the firmware.
|
|
*
|
|
* Since: 1.3.3
|
|
**/
|
|
void
|
|
fu_firmware_set_version (FuFirmware *self, const gchar *version)
|
|
{
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (self);
|
|
g_return_if_fail (FU_IS_FIRMWARE (self));
|
|
g_free (priv->version);
|
|
priv->version = g_strdup (version);
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_tokenize:
|
|
* @self: A #FuFirmware
|
|
* @fw: A #GBytes
|
|
* @flags: some #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Tokenizes a firmware, typically breaking the firmware into records.
|
|
*
|
|
* Records can be enumerated using subclass-specific functionality, for example
|
|
* using fu_srec_firmware_get_records().
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.3.2
|
|
**/
|
|
gboolean
|
|
fu_firmware_tokenize (FuFirmware *self, GBytes *fw,
|
|
FwupdInstallFlags flags, GError **error)
|
|
{
|
|
FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS (self);
|
|
|
|
g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE);
|
|
g_return_val_if_fail (fw != NULL, FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
/* optionally subclassed */
|
|
if (klass->tokenize != NULL)
|
|
return klass->tokenize (self, fw, flags, error);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_parse_full:
|
|
* @self: A #FuFirmware
|
|
* @fw: A #GBytes
|
|
* @addr_start: Start address, useful for ignoring a bootloader
|
|
* @addr_end: End address, useful for ignoring config bytes
|
|
* @flags: some #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Parses a firmware, typically breaking the firmware into images.
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
gboolean
|
|
fu_firmware_parse_full (FuFirmware *self,
|
|
GBytes *fw,
|
|
guint64 addr_start,
|
|
guint64 addr_end,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS (self);
|
|
g_autoptr(FuFirmwareImage) img = NULL;
|
|
|
|
g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE);
|
|
g_return_val_if_fail (fw != NULL, FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
/* subclassed */
|
|
if (klass->tokenize != NULL) {
|
|
if (!klass->tokenize (self, fw, flags, error))
|
|
return FALSE;
|
|
}
|
|
if (klass->parse != NULL)
|
|
return klass->parse (self, fw, addr_start, addr_end, flags, error);
|
|
|
|
/* just add entire blob */
|
|
img = fu_firmware_image_new (fw);
|
|
fu_firmware_add_image (self, img);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_parse:
|
|
* @self: A #FuFirmware
|
|
* @fw: A #GBytes
|
|
* @flags: some #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Parses a firmware, typically breaking the firmware into images.
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
gboolean
|
|
fu_firmware_parse (FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError **error)
|
|
{
|
|
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. The manifest would typically have the
|
|
* following form:
|
|
*
|
|
* |[<!-- language="XML" -->
|
|
* <?xml version="1.0" encoding="UTF-8"?>
|
|
* <firmware gtype="FuBcm57xxFirmware">
|
|
* <version>1.2.3</version>
|
|
* <image gtype="FuBcm57xxStage1Image">
|
|
* <version>7.8.9</version>
|
|
* <id>stage1</id>
|
|
* <idx>0x01</idx>
|
|
* <filename>stage1.bin</filename>
|
|
* </image>
|
|
* <image gtype="FuBcm57xxStage2Image">
|
|
* <id>stage2</id>
|
|
* <data/> <!-- empty! -->
|
|
* </image>
|
|
* <image gtype="FuBcm57xxDictImage">
|
|
* <id>ape</id>
|
|
* <addr>0x7</addr>
|
|
* <data>aGVsbG8gd29ybGQ=</data> <!-- base64 -->
|
|
* </image>
|
|
* </firmware>
|
|
* ]|
|
|
*
|
|
* This would be used in a build-system to merge images from generated files:
|
|
* `fwupdtool firmware-build fw.builder.xml test.fw`
|
|
*
|
|
* Static binary content can be specified in the `<image>/<data>` sections and
|
|
* is encoded as base64 text if not empty.
|
|
*
|
|
* Additionally, extra nodes can be included under `<image>` and `<firmware>`
|
|
* which can be parsed by the subclassed objects. You should verify the
|
|
* subclassed object `FuFirmwareImage->build` vfunc for the specific additional
|
|
* options supported.
|
|
*
|
|
* Plugins should manually g_type_ensure() subclassed image objects if not
|
|
* constructed as part of the plugin fu_plugin_init() or fu_plugin_setup()
|
|
* functions.
|
|
*
|
|
* 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 = NULL;
|
|
tmp = xb_node_get_attr (xb_image, "gtype");
|
|
if (tmp != NULL) {
|
|
GType gtype = g_type_from_name (tmp);
|
|
if (gtype == G_TYPE_INVALID) {
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_NOT_FOUND,
|
|
"GType %s not registered", tmp);
|
|
return FALSE;
|
|
}
|
|
img = g_object_new (gtype, NULL);
|
|
} else {
|
|
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
|
|
* @file: A #GFile
|
|
* @flags: some #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Parses a firmware file, typically breaking the firmware into images.
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.3.3
|
|
**/
|
|
gboolean
|
|
fu_firmware_parse_file (FuFirmware *self, GFile *file, FwupdInstallFlags flags, GError **error)
|
|
{
|
|
gchar *buf = NULL;
|
|
gsize bufsz = 0;
|
|
g_autoptr(GBytes) fw = NULL;
|
|
if (!g_file_load_contents (file, NULL, &buf, &bufsz, NULL, error))
|
|
return FALSE;
|
|
fw = g_bytes_new_take (buf, bufsz);
|
|
return fu_firmware_parse (self, fw, flags, error);
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_write:
|
|
* @self: A #FuFirmware
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Writes a firmware, typically packing the images into a binary blob.
|
|
*
|
|
* Returns: (transfer full): a #GBytes
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
GBytes *
|
|
fu_firmware_write (FuFirmware *self, GError **error)
|
|
{
|
|
FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS (self);
|
|
|
|
g_return_val_if_fail (FU_IS_FIRMWARE (self), NULL);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
|
|
/* subclassed */
|
|
if (klass->write != NULL)
|
|
return klass->write (self, error);
|
|
|
|
/* just add default blob */
|
|
return fu_firmware_get_image_default_bytes (self, error);
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_write_file:
|
|
* @self: A #FuFirmware
|
|
* @file: A #GFile
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Writes a firmware, typically packing the images into a binary blob.
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.3.3
|
|
**/
|
|
gboolean
|
|
fu_firmware_write_file (FuFirmware *self, GFile *file, GError **error)
|
|
{
|
|
g_autoptr(GBytes) blob = NULL;
|
|
blob = fu_firmware_write (self, error);
|
|
if (blob == NULL)
|
|
return FALSE;
|
|
return g_file_replace_contents (file,
|
|
g_bytes_get_data (blob, NULL),
|
|
g_bytes_get_size (blob),
|
|
NULL, FALSE,
|
|
G_FILE_CREATE_NONE,
|
|
NULL, NULL, error);
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_add_image:
|
|
* @self: a #FuPlugin
|
|
* @img: A #FuFirmwareImage
|
|
*
|
|
* Adds an image to the firmware.
|
|
*
|
|
* If %FU_FIRMWARE_FLAG_DEDUPE_ID is set, an image with the same ID is already
|
|
* present it is replaced.
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
void
|
|
fu_firmware_add_image (FuFirmware *self, FuFirmwareImage *img)
|
|
{
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (self);
|
|
g_return_if_fail (FU_IS_FIRMWARE (self));
|
|
g_return_if_fail (FU_IS_FIRMWARE_IMAGE (img));
|
|
|
|
/* dedupe */
|
|
for (guint i = 0; i < priv->images->len; i++) {
|
|
FuFirmwareImage *img_tmp = g_ptr_array_index (priv->images, i);
|
|
if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_ID) {
|
|
if (g_strcmp0 (fu_firmware_image_get_id (img_tmp),
|
|
fu_firmware_image_get_id (img)) == 0) {
|
|
g_ptr_array_remove_index (priv->images, i);
|
|
break;
|
|
}
|
|
}
|
|
if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_IDX) {
|
|
if (fu_firmware_image_get_idx (img_tmp) ==
|
|
fu_firmware_image_get_idx (img)) {
|
|
g_ptr_array_remove_index (priv->images, i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_ptr_array_add (priv->images, g_object_ref (img));
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_remove_image:
|
|
* @self: a #FuPlugin
|
|
* @img: A #FuFirmwareImage
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Remove an image from the firmware.
|
|
*
|
|
* Returns: %TRUE if the image was removed
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fu_firmware_remove_image (FuFirmware *self, FuFirmwareImage *img, GError **error)
|
|
{
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (self);
|
|
|
|
g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE);
|
|
g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (img), FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
if (g_ptr_array_remove (priv->images, img))
|
|
return TRUE;
|
|
|
|
/* did not exist */
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_FOUND,
|
|
"image %s not found in firmware",
|
|
fu_firmware_image_get_id (img));
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_remove_image_by_idx:
|
|
* @self: a #FuPlugin
|
|
* @idx: index
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Removes the first image from the firmware matching the index.
|
|
*
|
|
* Returns: %TRUE if an image was removed
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fu_firmware_remove_image_by_idx (FuFirmware *self, guint64 idx, GError **error)
|
|
{
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (self);
|
|
g_autoptr(FuFirmwareImage) img = NULL;
|
|
|
|
g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
img = fu_firmware_get_image_by_idx (self, idx, error);
|
|
if (img == NULL)
|
|
return FALSE;
|
|
g_ptr_array_remove (priv->images, img);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_remove_image_by_id:
|
|
* @self: a #FuPlugin
|
|
* @id: (nullable): image ID, e.g. "config"
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Removes the first image from the firmware matching the ID.
|
|
*
|
|
* Returns: %TRUE if an image was removed
|
|
*
|
|
* Since: 1.5.0
|
|
**/
|
|
gboolean
|
|
fu_firmware_remove_image_by_id (FuFirmware *self, const gchar *id, GError **error)
|
|
{
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (self);
|
|
g_autoptr(FuFirmwareImage) img = NULL;
|
|
|
|
g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
img = fu_firmware_get_image_by_id (self, id, error);
|
|
if (img == NULL)
|
|
return FALSE;
|
|
g_ptr_array_remove (priv->images, img);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_get_images:
|
|
* @self: a #FuFirmware
|
|
*
|
|
* Returns all the images in the firmware.
|
|
*
|
|
* Returns: (transfer container) (element-type FuFirmwareImage): images
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
GPtrArray *
|
|
fu_firmware_get_images (FuFirmware *self)
|
|
{
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (self);
|
|
g_autoptr(GPtrArray) imgs = NULL;
|
|
|
|
g_return_val_if_fail (FU_IS_FIRMWARE (self), NULL);
|
|
|
|
imgs = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
|
|
for (guint i = 0; i < priv->images->len; i++) {
|
|
FuFirmwareImage *img = g_ptr_array_index (priv->images, i);
|
|
g_ptr_array_add (imgs, g_object_ref (img));
|
|
}
|
|
return g_steal_pointer (&imgs);
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_get_image_by_id:
|
|
* @self: a #FuPlugin
|
|
* @id: (nullable): image ID, e.g. "config"
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Gets the firmware image using the image ID.
|
|
*
|
|
* Returns: (transfer full): a #FuFirmwareImage, or %NULL if the image is not found
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
FuFirmwareImage *
|
|
fu_firmware_get_image_by_id (FuFirmware *self, const gchar *id, GError **error)
|
|
{
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (self);
|
|
|
|
g_return_val_if_fail (FU_IS_FIRMWARE (self), NULL);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
|
|
for (guint i = 0; i < priv->images->len; i++) {
|
|
FuFirmwareImage *img = g_ptr_array_index (priv->images, i);
|
|
if (g_strcmp0 (fu_firmware_image_get_id (img), id) == 0)
|
|
return g_object_ref (img);
|
|
}
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_FOUND,
|
|
"no image id %s found in firmware", id);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_get_image_by_id_bytes:
|
|
* @self: a #FuPlugin
|
|
* @id: (nullable): image ID, e.g. "config"
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Gets the firmware image bytes using the image ID.
|
|
*
|
|
* Returns: (transfer full): a #GBytes of a #FuFirmwareImage, or %NULL if the image is not found
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
GBytes *
|
|
fu_firmware_get_image_by_id_bytes (FuFirmware *self, const gchar *id, GError **error)
|
|
{
|
|
g_autoptr(FuFirmwareImage) img = fu_firmware_get_image_by_id (self, id, error);
|
|
if (img == NULL)
|
|
return NULL;
|
|
return fu_firmware_image_write (img, error);
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_get_image_by_idx:
|
|
* @self: a #FuPlugin
|
|
* @idx: image index
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Gets the firmware image using the image index.
|
|
*
|
|
* Returns: (transfer full): a #FuFirmwareImage, or %NULL if the image is not found
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
FuFirmwareImage *
|
|
fu_firmware_get_image_by_idx (FuFirmware *self, guint64 idx, GError **error)
|
|
{
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (self);
|
|
|
|
g_return_val_if_fail (FU_IS_FIRMWARE (self), NULL);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
|
|
for (guint i = 0; i < priv->images->len; i++) {
|
|
FuFirmwareImage *img = g_ptr_array_index (priv->images, i);
|
|
if (fu_firmware_image_get_idx (img) == idx)
|
|
return g_object_ref (img);
|
|
}
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_FOUND,
|
|
"no image idx %" G_GUINT64_FORMAT " found in firmware", idx);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_get_image_by_idx_bytes:
|
|
* @self: a #FuPlugin
|
|
* @idx: image index
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Gets the firmware image bytes using the image index.
|
|
*
|
|
* Returns: (transfer full): a #GBytes of a #FuFirmwareImage, or %NULL if the image is not found
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
GBytes *
|
|
fu_firmware_get_image_by_idx_bytes (FuFirmware *self, guint64 idx, GError **error)
|
|
{
|
|
g_autoptr(FuFirmwareImage) img = fu_firmware_get_image_by_idx (self, idx, error);
|
|
if (img == NULL)
|
|
return NULL;
|
|
return fu_firmware_image_write (img, error);
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_get_image_default:
|
|
* @self: a #FuPlugin
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Gets the default firmware image.
|
|
*
|
|
* NOTE: If the firmware has multiple images included then fu_firmware_get_image_by_id()
|
|
* or fu_firmware_get_image_by_idx() must be used rather than this function.
|
|
*
|
|
* Returns: (transfer full): a #FuFirmwareImage, or %NULL if the image is not found
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
FuFirmwareImage *
|
|
fu_firmware_get_image_default (FuFirmware *self, GError **error)
|
|
{
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (self);
|
|
if (priv->images->len == 0) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_FOUND,
|
|
"no images in firmware");
|
|
return NULL;
|
|
}
|
|
if (priv->images->len > 1) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_FOUND,
|
|
"multiple images present in firmware");
|
|
return NULL;
|
|
}
|
|
return g_object_ref (FU_FIRMWARE_IMAGE (g_ptr_array_index (priv->images, 0)));
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_get_image_default_bytes:
|
|
* @self: a #FuPlugin
|
|
* @error: A #GError, or %NULL
|
|
*
|
|
* Gets the default firmware image.
|
|
*
|
|
* Returns: (transfer full): a #GBytes of the image, or %NULL if the image is not found
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
GBytes *
|
|
fu_firmware_get_image_default_bytes (FuFirmware *self, GError **error)
|
|
{
|
|
g_autoptr(FuFirmwareImage) img = fu_firmware_get_image_default (self, error);
|
|
if (img == NULL)
|
|
return NULL;
|
|
return fu_firmware_image_write (img, error);
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_to_string:
|
|
* @self: A #FuFirmware
|
|
*
|
|
* This allows us to easily print the object.
|
|
*
|
|
* Returns: a string value, or %NULL for invalid.
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
gchar *
|
|
fu_firmware_to_string (FuFirmware *self)
|
|
{
|
|
FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS (self);
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (self);
|
|
GString *str = g_string_new (NULL);
|
|
|
|
/* subclassed type */
|
|
fu_common_string_append_kv (str, 0, G_OBJECT_TYPE_NAME (self), NULL);
|
|
if (priv->flags != FU_FIRMWARE_FLAG_NONE) {
|
|
g_autoptr(GString) tmp = g_string_new ("");
|
|
for (guint i = 0; i < 64; i++) {
|
|
if ((priv->flags & ((guint64) 1 << i)) == 0)
|
|
continue;
|
|
g_string_append_printf (tmp, "%s|",
|
|
fu_firmware_flag_to_string ((guint64) 1 << i));
|
|
}
|
|
if (tmp->len > 0)
|
|
g_string_truncate (tmp, tmp->len - 1);
|
|
fu_common_string_append_kv (str, 0, "Flags", tmp->str);
|
|
}
|
|
if (priv->version != NULL)
|
|
fu_common_string_append_kv (str, 0, "Version", priv->version);
|
|
|
|
/* vfunc */
|
|
if (klass->to_string != NULL)
|
|
klass->to_string (self, 0, str);
|
|
|
|
for (guint i = 0; i < priv->images->len; i++) {
|
|
FuFirmwareImage *img = g_ptr_array_index (priv->images, i);
|
|
fu_firmware_image_add_string (img, 1, str);
|
|
}
|
|
|
|
return g_string_free (str, FALSE);
|
|
}
|
|
|
|
static void
|
|
fu_firmware_init (FuFirmware *self)
|
|
{
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (self);
|
|
priv->images = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
|
|
}
|
|
|
|
static void
|
|
fu_firmware_finalize (GObject *object)
|
|
{
|
|
FuFirmware *self = FU_FIRMWARE (object);
|
|
FuFirmwarePrivate *priv = GET_PRIVATE (self);
|
|
g_free (priv->version);
|
|
g_ptr_array_unref (priv->images);
|
|
G_OBJECT_CLASS (fu_firmware_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
fu_firmware_class_init (FuFirmwareClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
object_class->finalize = fu_firmware_finalize;
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_new:
|
|
*
|
|
* Creates an empty firmware object.
|
|
*
|
|
* Returns: a #FuFirmware
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
FuFirmware *
|
|
fu_firmware_new (void)
|
|
{
|
|
FuFirmware *self = g_object_new (FU_TYPE_FIRMWARE, NULL);
|
|
return FU_FIRMWARE (self);
|
|
}
|
|
|
|
/**
|
|
* fu_firmware_new_from_bytes:
|
|
* @fw: A #GBytes image
|
|
*
|
|
* Creates a firmware object with the provided image set as default.
|
|
*
|
|
* Returns: a #FuFirmware
|
|
*
|
|
* Since: 1.3.1
|
|
**/
|
|
FuFirmware *
|
|
fu_firmware_new_from_bytes (GBytes *fw)
|
|
{
|
|
FuFirmware *self = fu_firmware_new ();
|
|
g_autoptr(FuFirmwareImage) img = NULL;
|
|
img = fu_firmware_image_new (fw);
|
|
fu_firmware_add_image (self, img);
|
|
return self;
|
|
}
|