/* * Copyright (C) 2019 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #define G_LOG_DOMAIN "FuFirmware" #include "config.h" #include "fu-common.h" #include "fu-firmware-image-private.h" /** * SECTION:fu-firmware_image * @short_description: a firmware_image file * * An object that represents a firmware_image file. */ typedef struct { gchar *id; GBytes *bytes; guint64 addr; guint64 idx; gchar *version; } FuFirmwareImagePrivate; G_DEFINE_TYPE_WITH_PRIVATE (FuFirmwareImage, fu_firmware_image, G_TYPE_OBJECT) #define GET_PRIVATE(o) (fu_firmware_image_get_instance_private (o)) /** * fu_firmware_image_get_version: * @self: A #FuFirmwareImage * * Gets an optional version that represents the firmware image. * * Returns: a string, or %NULL * * Since: 1.3.4 **/ const gchar * fu_firmware_image_get_version (FuFirmwareImage *self) { FuFirmwareImagePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), NULL); return priv->version; } /** * fu_firmware_image_set_version: * @self: A #FuFirmwareImage * @version: (nullable): A string version, or %NULL * * Sets an optional version that represents the firmware image. * * Since: 1.3.4 **/ void fu_firmware_image_set_version (FuFirmwareImage *self, const gchar *version) { FuFirmwareImagePrivate *priv = GET_PRIVATE (self); g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self)); g_free (priv->version); priv->version = g_strdup (version); } /** * fu_firmware_image_set_id: * @self: a #FuPlugin * @id: (nullable): image ID, e.g. "config" * * Since: 1.3.1 **/ void fu_firmware_image_set_id (FuFirmwareImage *self, const gchar *id) { FuFirmwareImagePrivate *priv = GET_PRIVATE (self); g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self)); g_free (priv->id); priv->id = g_strdup (id); } /** * fu_firmware_image_get_id: * @self: a #FuPlugin * * Gets the image ID, typically set at construction. * * Returns: image ID, e.g. "config" * * Since: 1.3.1 **/ const gchar * fu_firmware_image_get_id (FuFirmwareImage *self) { FuFirmwareImagePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), NULL); return priv->id; } /** * fu_firmware_image_set_addr: * @self: a #FuPlugin * @addr: integer * * Sets the base address of the image. * * Since: 1.3.1 **/ void fu_firmware_image_set_addr (FuFirmwareImage *self, guint64 addr) { FuFirmwareImagePrivate *priv = GET_PRIVATE (self); g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self)); priv->addr = addr; } /** * fu_firmware_image_get_addr: * @self: a #FuPlugin * * Gets the base address of the image. * * Returns: integer * * Since: 1.3.1 **/ guint64 fu_firmware_image_get_addr (FuFirmwareImage *self) { FuFirmwareImagePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), G_MAXUINT64); return priv->addr; } /** * fu_firmware_image_set_idx: * @self: a #FuPlugin * @idx: integer * * Sets the index of the image which is used for ordering. * * Since: 1.3.1 **/ void fu_firmware_image_set_idx (FuFirmwareImage *self, guint64 idx) { FuFirmwareImagePrivate *priv = GET_PRIVATE (self); g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self)); priv->idx = idx; } /** * fu_firmware_image_get_idx: * @self: a #FuPlugin * * Gets the index of the image which is used for ordering. * * Returns: integer * * Since: 1.3.1 **/ guint64 fu_firmware_image_get_idx (FuFirmwareImage *self) { FuFirmwareImagePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), G_MAXUINT64); return priv->idx; } /** * fu_firmware_image_set_bytes: * @self: a #FuPlugin * @bytes: A #GBytes * * Sets the contents of the image if not created with fu_firmware_image_new(). * * Since: 1.3.1 **/ void fu_firmware_image_set_bytes (FuFirmwareImage *self, GBytes *bytes) { FuFirmwareImagePrivate *priv = GET_PRIVATE (self); g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self)); g_return_if_fail (bytes != NULL); g_return_if_fail (priv->bytes == NULL); priv->bytes = g_bytes_ref (bytes); } /** * fu_firmware_image_write: * @self: a #FuPlugin * @error: A #GError, or %NULL * * Writes the image, which will try to call a superclassed ->write() function. * * By default (and in most cases) this just provides the value set by the * fu_firmware_image_set_bytes() function. * * Returns: (transfer full): a #GBytes of the bytes, or %NULL if the bytes is not set * * Since: 1.3.3 **/ GBytes * fu_firmware_image_write (FuFirmwareImage *self, GError **error) { FuFirmwareImageClass *klass = FU_FIRMWARE_IMAGE_GET_CLASS (self); FuFirmwareImagePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); /* optional vfunc */ if (klass->write != NULL) return klass->write (self, error); /* fall back to what was set manually */ if (priv->bytes == NULL) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no bytes found in firmware bytes %s", priv->id); return NULL; } return g_bytes_ref (priv->bytes); } /** * fu_firmware_image_write_chunk: * @self: a #FuFirmwareImage * @address: an address greater than dfu_element_get_address() * @chunk_sz_max: the size of the new chunk * @error: a #GError, or %NULL * * Gets a block of data from the image. If the contents of the image is * smaller than the requested chunk size then the #GBytes will be smaller * than @chunk_sz_max. Use fu_common_bytes_pad() if padding is required. * * If the @address is larger than the size of the image then an error is returned. * * Return value: (transfer full): a #GBytes, or %NULL * * Since: 1.3.1 **/ GBytes * fu_firmware_image_write_chunk (FuFirmwareImage *self, guint64 address, guint64 chunk_sz_max, GError **error) { FuFirmwareImagePrivate *priv = GET_PRIVATE (self); gsize chunk_left; guint64 offset; /* check address requested is larger than base address */ if (address < priv->addr) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "requested address 0x%x less than base address 0x%x", (guint) address, (guint) priv->addr); return NULL; } /* offset into data */ offset = address - priv->addr; if (offset > g_bytes_get_size (priv->bytes)) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "offset 0x%x larger than data size 0x%x", (guint) offset, (guint) g_bytes_get_size (priv->bytes)); return NULL; } /* if we have less data than requested */ chunk_left = g_bytes_get_size (priv->bytes) - offset; if (chunk_sz_max > chunk_left) return g_bytes_new_from_bytes (priv->bytes, offset, chunk_left); /* check chunk */ return g_bytes_new_from_bytes (priv->bytes, offset, chunk_sz_max); } void fu_firmware_image_add_string (FuFirmwareImage *self, guint idt, GString *str) { FuFirmwareImagePrivate *priv = GET_PRIVATE (self); FuFirmwareImageClass *klass = FU_FIRMWARE_IMAGE_GET_CLASS (self); fu_common_string_append_kv (str, idt, G_OBJECT_TYPE_NAME (self), NULL); if (priv->id != NULL) fu_common_string_append_kv (str, idt, "ID", priv->id); if (priv->idx != 0x0) fu_common_string_append_kx (str, idt, "Index", priv->idx); if (priv->addr != 0x0) fu_common_string_append_kx (str, idt, "Address", priv->addr); if (priv->version != NULL) fu_common_string_append_kv (str, idt, "Version", priv->version); if (priv->bytes != NULL) { fu_common_string_append_kx (str, idt, "Data", g_bytes_get_size (priv->bytes)); } /* vfunc */ if (klass->to_string != NULL) klass->to_string (self, idt, str); } /** * fu_firmware_image_to_string: * @self: A #FuFirmwareImage * * This allows us to easily print the object. * * Returns: a string value, or %NULL for invalid. * * Since: 1.3.1 **/ gchar * fu_firmware_image_to_string (FuFirmwareImage *self) { GString *str = g_string_new (NULL); fu_firmware_image_add_string (self, 0, str); return g_string_free (str, FALSE); } static void fu_firmware_image_init (FuFirmwareImage *self) { } static void fu_firmware_image_finalize (GObject *object) { FuFirmwareImage *self = FU_FIRMWARE_IMAGE (object); FuFirmwareImagePrivate *priv = GET_PRIVATE (self); g_free (priv->id); g_free (priv->version); if (priv->bytes != NULL) g_bytes_unref (priv->bytes); G_OBJECT_CLASS (fu_firmware_image_parent_class)->finalize (object); } static void fu_firmware_image_class_init (FuFirmwareImageClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = fu_firmware_image_finalize; } /** * fu_firmware_image_new: * @bytes: Optional #GBytes * * Creates an empty firmware_image object. * * Returns: a #FuFirmwareImage * * Since: 1.3.1 **/ FuFirmwareImage * fu_firmware_image_new (GBytes *bytes) { FuFirmwareImage *self = g_object_new (FU_TYPE_FIRMWARE_IMAGE, NULL); if (bytes != NULL) fu_firmware_image_set_bytes (self, bytes); return self; }