mirror of
https://git.proxmox.com/git/fwupd
synced 2025-06-05 08:36:40 +00:00

Fixes: * activate * active * additional * and * approaches * attestation * bootloader * calculate * capability * children * close * compatible * continuous * convenience * critical * data * delivery * disabled * disambiguate * documented * elapse * emergency * erasable * expectations * filesystem * from * haptic * ignorable * images * infinity * information * information * inhibited * insufficient * interrupt * into * limited * management * manifest * maximum * memory * metadata * mismatch * model * nonexistent * not * objects * offset * omissions * ota * past * perform * peripherals * predictable * product * quarterly * quirk * quirks * recognize * release * requests * revocation * sanitized * sector * status * the * the update * timeout * transfer * transfers * typically * unspecified * upgrade * which * will * wireless
312 lines
8.7 KiB
C
312 lines
8.7 KiB
C
/*
|
|
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
|
* Copyright (C) 2022 Intel
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#define G_LOG_DOMAIN "FuFirmware"
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include "fu-byte-array.h"
|
|
#include "fu-bytes.h"
|
|
#include "fu-ifwi-fpt-firmware.h"
|
|
#include "fu-mem.h"
|
|
#include "fu-string.h"
|
|
|
|
/**
|
|
* FuIfwiFptFirmware:
|
|
*
|
|
* An Intel Flash Program Tool (aka FPT) header can be found in IFWI (Integrated Firmware Image)
|
|
* firmware blobs which are used in various Intel products using an IPU (Infrastructure Processing
|
|
* Unit).
|
|
*
|
|
* This could include hardware like SmartNICs, GPUs, camera and audio devices.
|
|
*
|
|
* See also: [class@FuFirmware]
|
|
*/
|
|
|
|
G_DEFINE_TYPE(FuIfwiFptFirmware, fu_ifwi_fpt_firmware, FU_TYPE_FIRMWARE)
|
|
|
|
#define FU_IFWI_FPT_HEADER_MARKER 0x54504624 /* "$FPT" */
|
|
#define FU_IFWI_FPT_HEADER_VERSION 0x20
|
|
#define FU_IFWI_FPT_ENTRY_VERSION 0x10
|
|
#define FU_IFWI_FPT_MAX_ENTRIES 56
|
|
|
|
typedef struct __attribute__((packed)) {
|
|
guint32 header_marker;
|
|
guint32 num_of_entries;
|
|
guint8 header_version;
|
|
guint8 entry_version;
|
|
guint8 header_length; /* bytes */
|
|
guint8 flags;
|
|
guint16 ticks_to_add;
|
|
guint16 tokens_to_add;
|
|
guint32 uma_size;
|
|
guint32 crc32;
|
|
guint16 fitc_major;
|
|
guint16 fitc_minor;
|
|
guint16 fitc_hotfix;
|
|
guint16 fitc_build;
|
|
} FuIfwiFptHeader;
|
|
|
|
typedef struct __attribute__((packed)) {
|
|
guint32 partition_name;
|
|
guint8 reserved1[4];
|
|
guint32 offset;
|
|
guint32 length; /* bytes */
|
|
guint8 reserved2[12];
|
|
guint32 partition_type; /* 0 for code, 1 for data, 2 for GLUT */
|
|
} FuIfwiFptEntry;
|
|
|
|
static gboolean
|
|
fu_ifwi_fpt_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error)
|
|
{
|
|
guint32 magic = 0;
|
|
|
|
if (!fu_memread_uint32_safe(g_bytes_get_data(fw, NULL),
|
|
g_bytes_get_size(fw),
|
|
offset + G_STRUCT_OFFSET(FuIfwiFptHeader, header_marker),
|
|
&magic,
|
|
G_LITTLE_ENDIAN,
|
|
error)) {
|
|
g_prefix_error(error, "failed to read magic: ");
|
|
return FALSE;
|
|
}
|
|
if (magic != FU_IFWI_FPT_HEADER_MARKER) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INVALID_FILE,
|
|
"invalid FPT header marker 0x%x, expected 0x%x",
|
|
magic,
|
|
(guint)FU_IFWI_FPT_HEADER_MARKER);
|
|
return FALSE;
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_ifwi_fpt_firmware_parse(FuFirmware *firmware,
|
|
GBytes *fw,
|
|
gsize offset,
|
|
FwupdInstallFlags flags,
|
|
GError **error)
|
|
{
|
|
gsize bufsz = 0;
|
|
guint32 num_of_entries = 0;
|
|
guint8 header_length = 0;
|
|
guint8 header_version = 0;
|
|
guint8 entry_version = 0;
|
|
const guint8 *buf = g_bytes_get_data(fw, &bufsz);
|
|
|
|
/* sanity check */
|
|
if (!fu_memread_uint32_safe(buf,
|
|
bufsz,
|
|
offset + G_STRUCT_OFFSET(FuIfwiFptHeader, num_of_entries),
|
|
&num_of_entries,
|
|
G_LITTLE_ENDIAN,
|
|
error))
|
|
return FALSE;
|
|
if (num_of_entries > FU_IFWI_FPT_MAX_ENTRIES) {
|
|
g_set_error(error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_DATA,
|
|
"invalid FPT number of entries %u",
|
|
num_of_entries);
|
|
return FALSE;
|
|
}
|
|
if (!fu_memread_uint8_safe(buf,
|
|
bufsz,
|
|
offset + G_STRUCT_OFFSET(FuIfwiFptHeader, header_version),
|
|
&header_version,
|
|
error))
|
|
return FALSE;
|
|
if (header_version < FU_IFWI_FPT_HEADER_VERSION) {
|
|
g_set_error(error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_DATA,
|
|
"invalid FPT header version: 0x%x",
|
|
header_version);
|
|
return FALSE;
|
|
}
|
|
if (!fu_memread_uint8_safe(buf,
|
|
bufsz,
|
|
offset + G_STRUCT_OFFSET(FuIfwiFptHeader, entry_version),
|
|
&entry_version,
|
|
error))
|
|
return FALSE;
|
|
if (entry_version != FU_IFWI_FPT_ENTRY_VERSION) {
|
|
g_set_error(error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_DATA,
|
|
"invalid FPT entry version: 0x%x, expected 0x%x",
|
|
entry_version,
|
|
(guint)FU_IFWI_FPT_ENTRY_VERSION);
|
|
return FALSE;
|
|
}
|
|
|
|
/* offset by header length */
|
|
if (!fu_memread_uint8_safe(buf,
|
|
bufsz,
|
|
offset + G_STRUCT_OFFSET(FuIfwiFptHeader, header_length),
|
|
&header_length,
|
|
error))
|
|
return FALSE;
|
|
offset += header_length;
|
|
|
|
/* read out entries */
|
|
for (guint i = 0; i < num_of_entries; i++) {
|
|
guint32 data_length = 0;
|
|
guint32 data_offset = 0;
|
|
guint32 partition_name = 0;
|
|
g_autofree gchar *id = NULL;
|
|
g_autoptr(FuFirmware) img = fu_firmware_new();
|
|
|
|
/* read IDX */
|
|
if (!fu_memread_uint32_safe(buf,
|
|
bufsz,
|
|
offset +
|
|
G_STRUCT_OFFSET(FuIfwiFptEntry, partition_name),
|
|
&partition_name,
|
|
G_LITTLE_ENDIAN,
|
|
error))
|
|
return FALSE;
|
|
fu_firmware_set_idx(img, partition_name);
|
|
|
|
/* convert to text form for convenience */
|
|
id = fu_strsafe((const gchar *)&partition_name, sizeof(partition_name));
|
|
if (id != NULL)
|
|
fu_firmware_set_id(img, id);
|
|
|
|
/* read offset and length */
|
|
if (!fu_memread_uint32_safe(buf,
|
|
bufsz,
|
|
offset + G_STRUCT_OFFSET(FuIfwiFptEntry, offset),
|
|
&data_offset,
|
|
G_LITTLE_ENDIAN,
|
|
error))
|
|
return FALSE;
|
|
if (!fu_memread_uint32_safe(buf,
|
|
bufsz,
|
|
offset + G_STRUCT_OFFSET(FuIfwiFptEntry, length),
|
|
&data_length,
|
|
G_LITTLE_ENDIAN,
|
|
error))
|
|
return FALSE;
|
|
|
|
/* get data at offset using zero-copy */
|
|
if (data_length != 0x0) {
|
|
g_autoptr(GBytes) blob = NULL;
|
|
blob = fu_bytes_new_offset(fw, data_offset, data_length, error);
|
|
if (blob == NULL)
|
|
return FALSE;
|
|
fu_firmware_set_bytes(img, blob);
|
|
fu_firmware_set_offset(img, data_offset);
|
|
}
|
|
fu_firmware_add_image(firmware, img);
|
|
|
|
/* next */
|
|
offset += sizeof(FuIfwiFptEntry);
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static GBytes *
|
|
fu_ifwi_fpt_firmware_write(FuFirmware *firmware, GError **error)
|
|
{
|
|
gsize offset = 0;
|
|
g_autoptr(GByteArray) buf = g_byte_array_new();
|
|
g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware);
|
|
|
|
/* fixup the image offsets */
|
|
offset += sizeof(FuIfwiFptHeader);
|
|
offset += sizeof(FuIfwiFptEntry) * imgs->len;
|
|
for (guint i = 0; i < imgs->len; i++) {
|
|
FuFirmware *img = g_ptr_array_index(imgs, i);
|
|
g_autoptr(GBytes) blob = NULL;
|
|
blob = fu_firmware_get_bytes(img, error);
|
|
if (blob == NULL) {
|
|
g_prefix_error(error, "image 0x%x: ", i);
|
|
return NULL;
|
|
}
|
|
fu_firmware_set_offset(img, offset);
|
|
offset += g_bytes_get_size(blob);
|
|
}
|
|
|
|
/* write the header */
|
|
fu_byte_array_append_uint32(buf, FU_IFWI_FPT_HEADER_MARKER, G_LITTLE_ENDIAN);
|
|
fu_byte_array_append_uint32(buf, imgs->len, G_LITTLE_ENDIAN);
|
|
fu_byte_array_append_uint8(buf, FU_IFWI_FPT_HEADER_VERSION);
|
|
fu_byte_array_append_uint8(buf, FU_IFWI_FPT_ENTRY_VERSION);
|
|
fu_byte_array_append_uint8(buf, sizeof(FuIfwiFptHeader));
|
|
fu_byte_array_append_uint8(buf, 0x0); /* flags */
|
|
fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* ticks_to_add */
|
|
fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* tokens_to_add */
|
|
fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* uma_size */
|
|
fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* crc32 */
|
|
fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* fitc_major */
|
|
fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* fitc_minor */
|
|
fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* fitc_hotfix */
|
|
fu_byte_array_append_uint16(buf, 0x0, G_LITTLE_ENDIAN); /* fitc_build */
|
|
|
|
/* add entries */
|
|
for (guint i = 0; i < imgs->len; i++) {
|
|
FuFirmware *img = g_ptr_array_index(imgs, i);
|
|
fu_byte_array_append_uint32(buf, fu_firmware_get_idx(img), G_LITTLE_ENDIAN);
|
|
fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved1 */
|
|
fu_byte_array_append_uint32(buf, fu_firmware_get_offset(img), G_LITTLE_ENDIAN);
|
|
fu_byte_array_append_uint32(buf, fu_firmware_get_size(img), G_LITTLE_ENDIAN);
|
|
fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved2 */
|
|
fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved2 */
|
|
fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved2 */
|
|
fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* partition_type */
|
|
}
|
|
|
|
/* add entry data */
|
|
for (guint i = 0; i < imgs->len; i++) {
|
|
FuFirmware *img = g_ptr_array_index(imgs, i);
|
|
g_autoptr(GBytes) blob = NULL;
|
|
blob = fu_firmware_get_bytes(img, error);
|
|
if (blob == NULL)
|
|
return NULL;
|
|
fu_byte_array_append_bytes(buf, blob);
|
|
}
|
|
|
|
/* success */
|
|
return g_byte_array_free_to_bytes(g_steal_pointer(&buf));
|
|
}
|
|
|
|
static void
|
|
fu_ifwi_fpt_firmware_init(FuIfwiFptFirmware *self)
|
|
{
|
|
}
|
|
|
|
static void
|
|
fu_ifwi_fpt_firmware_class_init(FuIfwiFptFirmwareClass *klass)
|
|
{
|
|
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass);
|
|
klass_firmware->check_magic = fu_ifwi_fpt_firmware_check_magic;
|
|
klass_firmware->parse = fu_ifwi_fpt_firmware_parse;
|
|
klass_firmware->write = fu_ifwi_fpt_firmware_write;
|
|
}
|
|
|
|
/**
|
|
* fu_ifwi_fpt_firmware_new:
|
|
*
|
|
* Creates a new #FuFirmware of Intel Flash Program Tool format
|
|
*
|
|
* Since: 1.8.2
|
|
**/
|
|
FuFirmware *
|
|
fu_ifwi_fpt_firmware_new(void)
|
|
{
|
|
return FU_FIRMWARE(g_object_new(FU_TYPE_IFWI_FPT_FIRMWARE, NULL));
|
|
}
|