mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-25 18:44:54 +00:00

We can use these in the future elantp plugin, and I'm sure we'll need them again in the future for an *actual* CFU plugin.
116 lines
2.7 KiB
C
116 lines
2.7 KiB
C
/*
|
|
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* 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));
|
|
}
|