diff --git a/docs/fwupd-docs.xml b/docs/fwupd-docs.xml index 36c6994d3..42a1656e2 100644 --- a/docs/fwupd-docs.xml +++ b/docs/fwupd-docs.xml @@ -49,6 +49,8 @@ + + diff --git a/libfwupdplugin/fu-cfu-offer.c b/libfwupdplugin/fu-cfu-offer.c new file mode 100644 index 000000000..54333fd20 --- /dev/null +++ b/libfwupdplugin/fu-cfu-offer.c @@ -0,0 +1,561 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-cfu-offer.h" +#include "fu-common.h" + +/** + * FuCfuOffer: + * + * A CFU offer. This is a 16 byte blob which contains enough data for the device to either accept + * or refuse a firmware payload. The offer may be loaded from disk, network, or even constructed + * manually. There is much left to how the specific firmware implements CFU, and it's expected + * that multiple different plugins will use this offer in different ways. + * + * Documented: https://docs.microsoft.com/en-us/windows-hardware/drivers/cfu/cfu-specification + * + * See also: [class@FuFirmware] + */ + +typedef struct { + guint8 segment_number; + gboolean force_immediate_reset; + gboolean force_ignore_version; + guint8 component_id; + guint8 token; + guint32 hw_variant; + guint8 protocol_revision; + guint8 bank; + guint8 milestone; + guint16 product_id; +} FuCfuOfferPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuCfuOffer, fu_cfu_offer, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_cfu_offer_get_instance_private(o)) + +static void +fu_cfu_offer_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuCfuOffer *self = FU_CFU_OFFER(firmware); + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kx(bn, "segment_number", priv->segment_number); + fu_xmlb_builder_insert_kb(bn, "force_immediate_reset", priv->force_immediate_reset); + fu_xmlb_builder_insert_kb(bn, "force_ignore_version", priv->force_ignore_version); + fu_xmlb_builder_insert_kx(bn, "component_id", priv->component_id); + fu_xmlb_builder_insert_kx(bn, "token", priv->token); + fu_xmlb_builder_insert_kx(bn, "hw_variant", priv->hw_variant); + fu_xmlb_builder_insert_kx(bn, "protocol_revision", priv->protocol_revision); + fu_xmlb_builder_insert_kx(bn, "bank", priv->bank); + fu_xmlb_builder_insert_kx(bn, "milestone", priv->milestone); + fu_xmlb_builder_insert_kx(bn, "product_id", priv->product_id); +} + +/** + * fu_cfu_offer_get_segment_number: + * @self: a #FuCfuOffer + * + * Gets the part of the firmware that is being transferred. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint8 +fu_cfu_offer_get_segment_number(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->segment_number; +} + +/** + * fu_cfu_offer_get_force_immediate_reset: + * @self: a #FuCfuOffer + * + * Gets if the in-situ firmware should reset into the new firmware immediately, rather than waiting + * for the next time the device is replugged. + * + * Returns: boolean + * + * Since: 1.7.0 + **/ +gboolean +fu_cfu_offer_get_force_immediate_reset(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), FALSE); + return priv->force_immediate_reset; +} + +/** + * fu_cfu_offer_get_force_ignore_version: + * @self: a #FuCfuOffer + * + * Gets if the in-situ firmware should ignore version mismatch (e.g. downgrade). + * + * Returns: boolean + * + * Since: 1.7.0 + **/ +gboolean +fu_cfu_offer_get_force_ignore_version(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), FALSE); + return priv->force_ignore_version; +} + +/** + * fu_cfu_offer_get_component_id: + * @self: a #FuCfuOffer + * + * Gets the component in the device to apply the firmware update. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint8 +fu_cfu_offer_get_component_id(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->component_id; +} + +/** + * fu_cfu_offer_get_token: + * @self: a #FuCfuOffer + * + * Gets the token to identify the user specific software making the offer. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint8 +fu_cfu_offer_get_token(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->token; +} + +/** + * fu_cfu_offer_get_hw_variant: + * @self: a #FuCfuOffer + * + * Gets the hardware variant bitmask corresponding with compatible firmware. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint32 +fu_cfu_offer_get_hw_variant(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->hw_variant; +} + +/** + * fu_cfu_offer_get_protocol_revision: + * @self: a #FuCfuOffer + * + * Gets the CFU protocol version. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint8 +fu_cfu_offer_get_protocol_revision(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->protocol_revision; +} + +/** + * fu_cfu_offer_get_bank: + * @self: a #FuCfuOffer + * + * Gets the bank register, used if multiple banks are supported. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint8 +fu_cfu_offer_get_bank(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->bank; +} + +/** + * fu_cfu_offer_get_milestone: + * @self: a #FuCfuOffer + * + * Gets the milestone, which can be used as a version for example EV1, EVT etc. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint8 +fu_cfu_offer_get_milestone(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->milestone; +} + +/** + * fu_cfu_offer_get_product_id: + * @self: a #FuCfuOffer + * + * Gets the product ID for this CFU image. + * + * Returns: integer + * + * Since: 1.7.0 + **/ +guint16 +fu_cfu_offer_get_product_id(FuCfuOffer *self) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CFU_OFFER(self), 0x0); + return priv->product_id; +} + +/** + * fu_cfu_offer_set_segment_number: + * @self: a #FuCfuOffer + * @segment_number: integer + * + * Sets the part of the firmware that is being transferred. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_segment_number(FuCfuOffer *self, guint8 segment_number) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + priv->segment_number = segment_number; +} + +/** + * fu_cfu_offer_set_force_immediate_reset: + * @self: a #FuCfuOffer + * @force_immediate_reset: boolean + * + * Sets if the in-situ firmware should reset into the new firmware immediately, rather than waiting + * for the next time the device is replugged. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_force_immediate_reset(FuCfuOffer *self, gboolean force_immediate_reset) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + priv->force_immediate_reset = force_immediate_reset; +} + +/** + * fu_cfu_offer_set_force_ignore_version: + * @self: a #FuCfuOffer + * @force_ignore_version: boolean + * + * Sets if the in-situ firmware should ignore version mismatch (e.g. downgrade). + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_force_ignore_version(FuCfuOffer *self, gboolean force_ignore_version) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + priv->force_ignore_version = force_ignore_version; +} + +/** + * fu_cfu_offer_set_component_id: + * @self: a #FuCfuOffer + * @component_id: integer + * + * Sets the component in the device to apply the firmware update. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_component_id(FuCfuOffer *self, guint8 component_id) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + priv->component_id = component_id; +} + +/** + * fu_cfu_offer_set_token: + * @self: a #FuCfuOffer + * @token: integer + * + * Sets the token to identify the user specific software making the offer. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_token(FuCfuOffer *self, guint8 token) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + priv->token = token; +} + +/** + * fu_cfu_offer_set_hw_variant: + * @self: a #FuCfuOffer + * @hw_variant: integer + * + * Sets the hardware variant bitmask corresponding with compatible firmware. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_hw_variant(FuCfuOffer *self, guint32 hw_variant) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + priv->hw_variant = hw_variant; +} + +/** + * fu_cfu_offer_set_protocol_revision: + * @self: a #FuCfuOffer + * @protocol_revision: integer + * + * Sets the CFU protocol version. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_protocol_revision(FuCfuOffer *self, guint8 protocol_revision) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + g_return_if_fail(protocol_revision <= 0b1111); + priv->protocol_revision = protocol_revision; +} + +/** + * fu_cfu_offer_set_bank: + * @self: a #FuCfuOffer + * @bank: integer + * + * Sets bank register, used if multiple banks are supported. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_bank(FuCfuOffer *self, guint8 bank) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + g_return_if_fail(bank <= 0b11); + priv->bank = bank; +} + +/** + * fu_cfu_offer_set_milestone: + * @self: a #FuCfuOffer + * @milestone: integer + * + * Sets the milestone, which can be used as a version for example EV1, EVT etc. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_milestone(FuCfuOffer *self, guint8 milestone) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + g_return_if_fail(milestone <= 0b111); + priv->milestone = milestone; +} + +/** + * fu_cfu_offer_set_product_id: + * @self: a #FuCfuOffer + * @product_id: integer + * + * Sets the product ID for this CFU image. + * + * Since: 1.7.0 + **/ +void +fu_cfu_offer_set_product_id(FuCfuOffer *self, guint16 product_id) +{ + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CFU_OFFER(self)); + priv->product_id = product_id; +} + +static gboolean +fu_cfu_offer_parse(FuFirmware *firmware, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) +{ + FuCfuOffer *self = FU_CFU_OFFER(firmware); + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + gsize bufsz = 0; + guint8 tmp = 0; + guint32 tmp32 = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* component info */ + if (!fu_common_read_uint8_safe(buf, bufsz, 0x0, &priv->segment_number, error)) + return FALSE; + if (!fu_common_read_uint8_safe(buf, bufsz, 0x1, &tmp, error)) + return FALSE; + priv->force_ignore_version = (tmp & 0b1) > 0; + priv->force_immediate_reset = (tmp & 0b10) > 0; + if (!fu_common_read_uint8_safe(buf, bufsz, 0x2, &priv->component_id, error)) + return FALSE; + if (!fu_common_read_uint8_safe(buf, bufsz, 0x3, &priv->token, error)) + return FALSE; + + /* version */ + if (!fu_common_read_uint32_safe(buf, bufsz, 0x4, &tmp32, G_LITTLE_ENDIAN, error)) + return FALSE; + fu_firmware_set_version_raw(firmware, tmp32); + if (!fu_common_read_uint32_safe(buf, bufsz, 0x8, &priv->hw_variant, G_LITTLE_ENDIAN, error)) + return FALSE; + + /* product info */ + if (!fu_common_read_uint8_safe(buf, bufsz, 0xC, &tmp, error)) + return FALSE; + priv->protocol_revision = (tmp >> 4) & 0b1111; + priv->bank = (tmp >> 2) & 0b11; + if (!fu_common_read_uint8_safe(buf, bufsz, 0xD, &tmp, error)) + return FALSE; + priv->milestone = (tmp >> 5) & 0b111; + if (!fu_common_read_uint16_safe(buf, bufsz, 0xE, &priv->product_id, G_LITTLE_ENDIAN, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static GBytes * +fu_cfu_offer_write(FuFirmware *firmware, GError **error) +{ + FuCfuOffer *self = FU_CFU_OFFER(firmware); + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* component info */ + fu_byte_array_append_uint8(buf, priv->segment_number); + fu_byte_array_append_uint8(buf, + priv->force_ignore_version | (priv->force_immediate_reset << 1)); + fu_byte_array_append_uint8(buf, priv->component_id); + fu_byte_array_append_uint8(buf, priv->token); + + /* version */ + fu_byte_array_append_uint32(buf, fu_firmware_get_version_raw(firmware), G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(buf, priv->hw_variant, G_LITTLE_ENDIAN); + + /* product info */ + fu_byte_array_append_uint8(buf, (priv->protocol_revision << 4) | (priv->bank << 2)); + fu_byte_array_append_uint8(buf, priv->milestone << 5); + fu_byte_array_append_uint16(buf, priv->product_id, G_LITTLE_ENDIAN); + + /* success */ + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static gboolean +fu_cfu_offer_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuCfuOffer *self = FU_CFU_OFFER(firmware); + FuCfuOfferPrivate *priv = GET_PRIVATE(self); + guint64 tmp; + + /* optional properties */ + tmp = xb_node_query_text_as_uint(n, "segment_number", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->segment_number = tmp; + tmp = xb_node_query_text_as_uint(n, "force_immediate_reset", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->force_immediate_reset = tmp; + tmp = xb_node_query_text_as_uint(n, "force_ignore_version", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->force_ignore_version = tmp; + tmp = xb_node_query_text_as_uint(n, "component_id", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->component_id = tmp; + tmp = xb_node_query_text_as_uint(n, "token", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->token = tmp; + tmp = xb_node_query_text_as_uint(n, "hw_variant", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32) + priv->hw_variant = tmp; + tmp = xb_node_query_text_as_uint(n, "protocol_revision", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->protocol_revision = tmp; + tmp = xb_node_query_text_as_uint(n, "bank", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->bank = tmp; + tmp = xb_node_query_text_as_uint(n, "milestone", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT8) + priv->milestone = tmp; + tmp = xb_node_query_text_as_uint(n, "product_id", NULL); + if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16) + priv->product_id = tmp; + + /* success */ + return TRUE; +} + +static void +fu_cfu_offer_init(FuCfuOffer *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID); +} + +static void +fu_cfu_offer_class_init(FuCfuOfferClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->export = fu_cfu_offer_export; + klass_firmware->parse = fu_cfu_offer_parse; + klass_firmware->write = fu_cfu_offer_write; + klass_firmware->build = fu_cfu_offer_build; +} + +/** + * fu_cfu_offer_new: + * + * Creates a new #FuFirmware for a CFU offer + * + * Since: 1.7.0 + **/ +FuFirmware * +fu_cfu_offer_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_CFU_OFFER, NULL)); +} diff --git a/libfwupdplugin/fu-cfu-offer.h b/libfwupdplugin/fu-cfu-offer.h new file mode 100644 index 000000000..601d2635a --- /dev/null +++ b/libfwupdplugin/fu-cfu-offer.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_CFU_OFFER (fu_cfu_offer_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuCfuOffer, fu_cfu_offer, FU, CFU_OFFER, FuFirmware) + +struct _FuCfuOfferClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_cfu_offer_new(void); +guint8 +fu_cfu_offer_get_segment_number(FuCfuOffer *self); +gboolean +fu_cfu_offer_get_force_immediate_reset(FuCfuOffer *self); +gboolean +fu_cfu_offer_get_force_ignore_version(FuCfuOffer *self); +guint8 +fu_cfu_offer_get_component_id(FuCfuOffer *self); +guint8 +fu_cfu_offer_get_token(FuCfuOffer *self); +guint32 +fu_cfu_offer_get_hw_variant(FuCfuOffer *self); +guint8 +fu_cfu_offer_get_protocol_revision(FuCfuOffer *self); +guint8 +fu_cfu_offer_get_bank(FuCfuOffer *self); +guint8 +fu_cfu_offer_get_milestone(FuCfuOffer *self); +guint16 +fu_cfu_offer_get_product_id(FuCfuOffer *self); + +void +fu_cfu_offer_set_segment_number(FuCfuOffer *self, guint8 segment_number); +void +fu_cfu_offer_set_force_immediate_reset(FuCfuOffer *self, gboolean force_immediate_reset); +void +fu_cfu_offer_set_force_ignore_version(FuCfuOffer *self, gboolean force_ignore_version); +void +fu_cfu_offer_set_component_id(FuCfuOffer *self, guint8 component_id); +void +fu_cfu_offer_set_token(FuCfuOffer *self, guint8 token); +void +fu_cfu_offer_set_hw_variant(FuCfuOffer *self, guint32 hw_variant); +void +fu_cfu_offer_set_protocol_revision(FuCfuOffer *self, guint8 protocol_revision); +void +fu_cfu_offer_set_bank(FuCfuOffer *self, guint8 bank); +void +fu_cfu_offer_set_milestone(FuCfuOffer *self, guint8 milestone); +void +fu_cfu_offer_set_product_id(FuCfuOffer *self, guint16 product_id); diff --git a/libfwupdplugin/fu-cfu-payload.c b/libfwupdplugin/fu-cfu-payload.c new file mode 100644 index 000000000..d6aaad9cc --- /dev/null +++ b/libfwupdplugin/fu-cfu-payload.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-cfu-payload.h" +#include "fu-common.h" + +/** + * FuCfuPayload: + * + * A CFU payload. This contains of a variable number of blocks, each containing the address, size + * and the chunk data. The chunks do not have to be the same size, and the address ranges do not + * have to be continuous. + * + * Documented: https://docs.microsoft.com/en-us/windows-hardware/drivers/cfu/cfu-specification + * + * See also: [class@FuFirmware] + */ + +G_DEFINE_TYPE(FuCfuPayload, fu_cfu_payload, FU_TYPE_FIRMWARE) + +static gboolean +fu_cfu_payload_parse(FuFirmware *firmware, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) +{ + guint32 offset = 0; + gsize bufsz = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + + /* process into chunks */ + while (offset < bufsz) { + guint32 chunk_addr = 0; + guint8 chunk_size = 0; + g_autoptr(FuChunk) chk = NULL; + g_autoptr(GBytes) blob = NULL; + + /* read chunk header */ + if (!fu_common_read_uint32_safe(buf, + bufsz, + offset, + &chunk_addr, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (!fu_common_read_uint8_safe(buf, bufsz, offset + 0x4, &chunk_size, error)) + return FALSE; + offset += 0x5; + blob = fu_common_bytes_new_offset(fw, offset, chunk_size, error); + if (blob == NULL) + return FALSE; + chk = fu_chunk_bytes_new(blob); + fu_chunk_set_address(chk, chunk_addr); + fu_firmware_add_chunk(firmware, chk); + + /* next! */ + offset += chunk_size; + } + + /* success */ + return TRUE; +} + +static GBytes * +fu_cfu_payload_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GPtrArray) chunks = NULL; + + chunks = fu_firmware_get_chunks(firmware, error); + if (chunks == NULL) + return NULL; + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + fu_byte_array_append_uint32(buf, fu_chunk_get_address(chk), G_LITTLE_ENDIAN); + fu_byte_array_append_uint8(buf, fu_chunk_get_data_sz(chk)); + g_byte_array_append(buf, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + } + return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); +} + +static void +fu_cfu_payload_init(FuCfuPayload *self) +{ +} + +static void +fu_cfu_payload_class_init(FuCfuPayloadClass *klass) +{ + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + klass_firmware->parse = fu_cfu_payload_parse; + klass_firmware->write = fu_cfu_payload_write; +} + +/** + * fu_cfu_payload_new: + * + * Creates a new #FuFirmware for a CFU payload + * + * Since: 1.7.0 + **/ +FuFirmware * +fu_cfu_payload_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_CFU_PAYLOAD, NULL)); +} diff --git a/libfwupdplugin/fu-cfu-payload.h b/libfwupdplugin/fu-cfu-payload.h new file mode 100644 index 000000000..5dd6e2e6b --- /dev/null +++ b/libfwupdplugin/fu-cfu-payload.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_CFU_PAYLOAD (fu_cfu_payload_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuCfuPayload, fu_cfu_payload, FU, CFU_PAYLOAD, FuFirmware) + +struct _FuCfuPayloadClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_cfu_payload_new(void); diff --git a/libfwupdplugin/fwupdplugin.map b/libfwupdplugin/fwupdplugin.map index 3790a9bf0..d4c756d9f 100644 --- a/libfwupdplugin/fwupdplugin.map +++ b/libfwupdplugin/fwupdplugin.map @@ -861,6 +861,30 @@ LIBFWUPDPLUGIN_1.6.2 { LIBFWUPDPLUGIN_1.7.0 { global: + fu_cfu_offer_get_bank; + fu_cfu_offer_get_component_id; + fu_cfu_offer_get_force_ignore_version; + fu_cfu_offer_get_force_immediate_reset; + fu_cfu_offer_get_hw_variant; + fu_cfu_offer_get_milestone; + fu_cfu_offer_get_product_id; + fu_cfu_offer_get_protocol_revision; + fu_cfu_offer_get_segment_number; + fu_cfu_offer_get_token; + fu_cfu_offer_get_type; + fu_cfu_offer_new; + fu_cfu_offer_set_bank; + fu_cfu_offer_set_component_id; + fu_cfu_offer_set_force_ignore_version; + fu_cfu_offer_set_force_immediate_reset; + fu_cfu_offer_set_hw_variant; + fu_cfu_offer_set_milestone; + fu_cfu_offer_set_product_id; + fu_cfu_offer_set_protocol_revision; + fu_cfu_offer_set_segment_number; + fu_cfu_offer_set_token; + fu_cfu_payload_get_type; + fu_cfu_payload_new; fu_common_strnsplit_full; fu_device_attach_full; fu_device_detach_full; diff --git a/libfwupdplugin/meson.build b/libfwupdplugin/meson.build index 0716e7d2a..ec26e9790 100644 --- a/libfwupdplugin/meson.build +++ b/libfwupdplugin/meson.build @@ -18,6 +18,8 @@ fwupdplugin_src = [ 'fu-device-locker.c', # fuzzing 'fu-device.c', # fuzzing 'fu-dfu-firmware.c', # fuzzing + 'fu-cfu-offer.c', # fuzzing + 'fu-cfu-payload.c', # fuzzing 'fu-volume.c', # fuzzing 'fu-firmware.c', # fuzzing 'fu-firmware-common.c', # fuzzing @@ -82,6 +84,8 @@ fwupdplugin_headers = [ 'fu-device-metadata.h', 'fu-device-locker.h', 'fu-dfu-firmware.h', + 'fu-cfu-offer.h', + 'fu-cfu-payload.h', 'fu-efi-common.h', 'fu-efi-firmware-file.h', 'fu-efi-firmware-filesystem.h', diff --git a/src/fu-engine.c b/src/fu-engine.c index a81c6349b..9c6960eed 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -29,6 +29,8 @@ #include "fu-backend.h" #include "fu-cabinet.h" +#include "fu-cfu-offer.h" +#include "fu-cfu-payload.h" #include "fu-common-cab.h" #include "fu-common.h" #include "fu-config.h" @@ -6716,6 +6718,8 @@ fu_engine_load(FuEngine *self, FuEngineLoadFlags flags, GError **error) FU_TYPE_EFI_FIRMWARE_VOLUME); fu_context_add_firmware_gtype(self->ctx, "ifd-bios", FU_TYPE_IFD_BIOS); fu_context_add_firmware_gtype(self->ctx, "ifd-firmware", FU_TYPE_IFD_FIRMWARE); + fu_context_add_firmware_gtype(self->ctx, "cfu-offer", FU_TYPE_CFU_OFFER); + fu_context_add_firmware_gtype(self->ctx, "cfu-payload", FU_TYPE_CFU_PAYLOAD); /* set up backends */ if (flags & FU_ENGINE_LOAD_FLAG_COLDPLUG) { diff --git a/src/fuzzing/cfu-offer.builder.xml b/src/fuzzing/cfu-offer.builder.xml new file mode 100644 index 000000000..3a7a7ecc9 --- /dev/null +++ b/src/fuzzing/cfu-offer.builder.xml @@ -0,0 +1,13 @@ + + 0x42 + 1 + 1 + 0xAB + 0xCD + 0x4567 + 0xFACE + 0xF + 0x1 + 0x2 + 0xDEAD + diff --git a/src/fuzzing/cfu-payload.builder.xml b/src/fuzzing/cfu-payload.builder.xml new file mode 100644 index 000000000..a2da25cd3 --- /dev/null +++ b/src/fuzzing/cfu-payload.builder.xml @@ -0,0 +1,12 @@ + + + + aGVsbG8gd29ybGQ= + 0x8001234 + + + aGVsbG8gd29ybGQ= + 0x8005678 + + + diff --git a/src/fuzzing/firmware/cfu.offer b/src/fuzzing/firmware/cfu.offer new file mode 100644 index 000000000..919dfedf9 Binary files /dev/null and b/src/fuzzing/firmware/cfu.offer differ diff --git a/src/fuzzing/firmware/cfu.payload b/src/fuzzing/firmware/cfu.payload new file mode 100644 index 000000000..6d9b10362 Binary files /dev/null and b/src/fuzzing/firmware/cfu.payload differ diff --git a/src/fuzzing/generate.py b/src/fuzzing/generate.py index d6e598862..01d5b1ad7 100755 --- a/src/fuzzing/generate.py +++ b/src/fuzzing/generate.py @@ -17,6 +17,8 @@ if __name__ == "__main__": ("bcm57xx.builder.xml", "bcm57xx.bin"), ("ccgx.builder.xml", "ccgx.cyacd"), ("ccgx-dmc.builder.xml", "ccgx-dmc.bin"), + ("cfu-offer.builder.xml", "cfu.offer"), + ("cfu-payload.builder.xml", "cfu.payload"), ("cros-ec.builder.xml", "cros-ec.bin"), ("dfuse.builder.xml", "dfuse.dfu"), ("ebitdo.builder.xml", "ebitdo.dat"),