mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-14 21:50:25 +00:00
Export the generic Intel Thunderbolt firmware format
This is being used for other products, e.g. USB4 docks. If non-Intel firmware is being used (e.g. ASMedia) then the explicit calls to `fu_intel_thunderbolt_nvm_new()` can be changed to something like `fu_firmware_new_from_gtypes()` with all the formats listed.
This commit is contained in:
parent
7292d35b07
commit
16b463334e
@ -352,6 +352,7 @@ def _build(bld: Builder) -> None:
|
|||||||
Fuzzer("fmap"),
|
Fuzzer("fmap"),
|
||||||
Fuzzer("ihex"),
|
Fuzzer("ihex"),
|
||||||
Fuzzer("srec"),
|
Fuzzer("srec"),
|
||||||
|
Fuzzer("intel-thunderbolt"),
|
||||||
Fuzzer("ifwi-cpd"),
|
Fuzzer("ifwi-cpd"),
|
||||||
Fuzzer("ifwi-fpt"),
|
Fuzzer("ifwi-fpt"),
|
||||||
Fuzzer("oprom"),
|
Fuzzer("oprom"),
|
||||||
|
129
libfwupdplugin/fu-intel-thunderbolt-firmware.c
Normal file
129
libfwupdplugin/fu-intel-thunderbolt-firmware.c
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Dell Inc.
|
||||||
|
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
|
||||||
|
* Copyright (C) 2020 Mario Limonciello <mario.limonciello@dell.com>
|
||||||
|
* Copyright (C) 2017 Intel Corporation.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define G_LOG_DOMAIN "FuFirmware"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "fu-byte-array.h"
|
||||||
|
#include "fu-intel-thunderbolt-firmware.h"
|
||||||
|
#include "fu-mem.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FuIntelThunderboltFirmware:
|
||||||
|
*
|
||||||
|
* The Non-Volatile-Memory file-format specification. This is what you would find as the update
|
||||||
|
* payload.
|
||||||
|
*
|
||||||
|
* See also: [class@FuFirmware]
|
||||||
|
*/
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(FuIntelThunderboltFirmware,
|
||||||
|
fu_intel_thunderbolt_firmware,
|
||||||
|
FU_TYPE_INTEL_THUNDERBOLT_NVM)
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_valid_farb_pointer(guint32 pointer)
|
||||||
|
{
|
||||||
|
return pointer != 0 && pointer != 0xFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_intel_thunderbolt_firmware_parse(FuFirmware *firmware,
|
||||||
|
GBytes *fw,
|
||||||
|
gsize offset,
|
||||||
|
FwupdInstallFlags flags,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
const guint32 farb_offsets[] = {0x0, 0x1000};
|
||||||
|
gboolean valid = FALSE;
|
||||||
|
guint32 farb_pointer = 0x0;
|
||||||
|
|
||||||
|
/* get header offset */
|
||||||
|
for (guint i = 0; i < G_N_ELEMENTS(farb_offsets); i++) {
|
||||||
|
if (!fu_memread_uint24_safe(g_bytes_get_data(fw, NULL),
|
||||||
|
g_bytes_get_size(fw),
|
||||||
|
offset + farb_offsets[i],
|
||||||
|
&farb_pointer,
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
if (fu_intel_thunderbolt_nvm_valid_farb_pointer(farb_pointer)) {
|
||||||
|
valid = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!valid) {
|
||||||
|
g_set_error_literal(error,
|
||||||
|
FWUPD_ERROR,
|
||||||
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
|
"no valid farb pointer found");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
g_debug("detected digital section begins at 0x%x", farb_pointer);
|
||||||
|
fu_firmware_set_offset(firmware, farb_pointer);
|
||||||
|
|
||||||
|
/* FuIntelThunderboltNvm->parse */
|
||||||
|
return FU_FIRMWARE_CLASS(fu_intel_thunderbolt_firmware_parent_class)
|
||||||
|
->parse(firmware, fw, offset + farb_pointer, flags, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GBytes *
|
||||||
|
fu_intel_thunderbolt_firmware_write(FuFirmware *firmware, GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(GByteArray) buf = g_byte_array_new();
|
||||||
|
g_autoptr(GBytes) blob = NULL;
|
||||||
|
|
||||||
|
/* sanity check */
|
||||||
|
if (fu_firmware_get_offset(firmware) < 0x08) {
|
||||||
|
g_set_error_literal(error,
|
||||||
|
FWUPD_ERROR,
|
||||||
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
|
"not valid offset");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* offset */
|
||||||
|
fu_byte_array_append_uint32(buf, fu_firmware_get_offset(firmware), G_LITTLE_ENDIAN);
|
||||||
|
fu_byte_array_set_size(buf, fu_firmware_get_offset(firmware), 0x00);
|
||||||
|
|
||||||
|
/* FuIntelThunderboltNvm->write */
|
||||||
|
blob =
|
||||||
|
FU_FIRMWARE_CLASS(fu_intel_thunderbolt_firmware_parent_class)->write(firmware, error);
|
||||||
|
fu_byte_array_append_bytes(buf, blob);
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return g_byte_array_free_to_bytes(g_steal_pointer(&buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_intel_thunderbolt_firmware_init(FuIntelThunderboltFirmware *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_intel_thunderbolt_firmware_class_init(FuIntelThunderboltFirmwareClass *klass)
|
||||||
|
{
|
||||||
|
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass);
|
||||||
|
klass_firmware->parse = fu_intel_thunderbolt_firmware_parse;
|
||||||
|
klass_firmware->write = fu_intel_thunderbolt_firmware_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fu_intel_thunderbolt_firmware_new:
|
||||||
|
*
|
||||||
|
* Creates a new #FuFirmware of Intel NVM format
|
||||||
|
*
|
||||||
|
* Since: 1.8.5
|
||||||
|
**/
|
||||||
|
FuFirmware *
|
||||||
|
fu_intel_thunderbolt_firmware_new(void)
|
||||||
|
{
|
||||||
|
return FU_FIRMWARE(g_object_new(FU_TYPE_INTEL_THUNDERBOLT_FIRMWARE, NULL));
|
||||||
|
}
|
26
libfwupdplugin/fu-intel-thunderbolt-firmware.h
Normal file
26
libfwupdplugin/fu-intel-thunderbolt-firmware.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Dell Inc.
|
||||||
|
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
|
||||||
|
* Copyright (C) 2020 Mario Limonciello <mario.limonciello@dell.com>
|
||||||
|
* Copyright (C) 2017 Intel Corporation.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "fu-intel-thunderbolt-nvm.h"
|
||||||
|
|
||||||
|
#define FU_TYPE_INTEL_THUNDERBOLT_FIRMWARE (fu_intel_thunderbolt_firmware_get_type())
|
||||||
|
G_DECLARE_DERIVABLE_TYPE(FuIntelThunderboltFirmware,
|
||||||
|
fu_intel_thunderbolt_firmware,
|
||||||
|
FU,
|
||||||
|
INTEL_THUNDERBOLT_FIRMWARE,
|
||||||
|
FuIntelThunderboltNvm)
|
||||||
|
|
||||||
|
struct _FuIntelThunderboltFirmwareClass {
|
||||||
|
FuIntelThunderboltNvmClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
FuFirmware *
|
||||||
|
fu_intel_thunderbolt_firmware_new(void);
|
967
libfwupdplugin/fu-intel-thunderbolt-nvm.c
Normal file
967
libfwupdplugin/fu-intel-thunderbolt-nvm.c
Normal file
@ -0,0 +1,967 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Dell Inc.
|
||||||
|
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
|
||||||
|
* Copyright (C) 2020 Mario Limonciello <mario.limonciello@dell.com>
|
||||||
|
* Copyright (C) 2017 Intel Corporation.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define G_LOG_DOMAIN "FuFirmware"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "fu-byte-array.h"
|
||||||
|
#include "fu-bytes.h"
|
||||||
|
#include "fu-intel-thunderbolt-nvm.h"
|
||||||
|
#include "fu-mem.h"
|
||||||
|
#include "fu-string.h"
|
||||||
|
#include "fu-version-common.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FuIntelThunderboltNvm:
|
||||||
|
*
|
||||||
|
* The Non-Volatile-Memory device specification. This is what you would find on the device SPI chip.
|
||||||
|
*
|
||||||
|
* See also: [class@FuFirmware]
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DROM,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_ARC_PARAMS,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DRAM_UCODE,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_LAST
|
||||||
|
} FuIntelThunderboltNvmSection;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_FAMILY_UNKNOWN,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_FAMILY_FR,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_FAMILY_WR,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR_C,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_FAMILY_BB,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_FAMILY_MR,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_FAMILY_LAST,
|
||||||
|
} FuIntelThunderboltNvmFamily;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
guint32 sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_LAST];
|
||||||
|
FuIntelThunderboltNvmFamily family;
|
||||||
|
gboolean is_host;
|
||||||
|
gboolean is_native;
|
||||||
|
gboolean has_pd;
|
||||||
|
guint16 vendor_id;
|
||||||
|
guint16 device_id;
|
||||||
|
guint16 model_id;
|
||||||
|
guint gen;
|
||||||
|
guint ports;
|
||||||
|
guint8 flash_size;
|
||||||
|
} FuIntelThunderboltNvmPrivate;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_PRIVATE(FuIntelThunderboltNvm, fu_intel_thunderbolt_nvm, FU_TYPE_FIRMWARE)
|
||||||
|
#define GET_PRIVATE(o) (fu_intel_thunderbolt_nvm_get_instance_private(o))
|
||||||
|
|
||||||
|
#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_AVAILABLE_SECTIONS 0x0002
|
||||||
|
#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_UCODE 0x0003
|
||||||
|
#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_DEVICE_ID 0x0005
|
||||||
|
#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_VERSION 0x0009
|
||||||
|
#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLAGS_HOST 0x0010
|
||||||
|
#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLASH_SIZE 0x0045
|
||||||
|
#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_ARC_PARAMS 0x0075
|
||||||
|
#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLAGS_IS_NATIVE 0x007B
|
||||||
|
#define FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_DROM 0x010E
|
||||||
|
|
||||||
|
#define FU_INTEL_THUNDERBOLT_NVM_DROM_OFFSET_VENDOR_ID 0x0010
|
||||||
|
#define FU_INTEL_THUNDERBOLT_NVM_DROM_OFFSET_MODEL_ID 0x0012
|
||||||
|
|
||||||
|
#define FU_INTEL_THUNDERBOLT_NVM_ARC_PARAMS_OFFSET_PD_POINTER 0x010C
|
||||||
|
|
||||||
|
static const gchar *
|
||||||
|
fu_intel_thunderbolt_nvm_family_to_string(FuIntelThunderboltNvmFamily family)
|
||||||
|
{
|
||||||
|
if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_FR)
|
||||||
|
return "falcon-ridge";
|
||||||
|
if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_WR)
|
||||||
|
return "win-ridge";
|
||||||
|
if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR)
|
||||||
|
return "alpine-ridge";
|
||||||
|
if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR_C)
|
||||||
|
return "alpine-ridge-c";
|
||||||
|
if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR)
|
||||||
|
return "titan-ridge";
|
||||||
|
if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_BB)
|
||||||
|
return "bb";
|
||||||
|
if (family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_MR)
|
||||||
|
return "maple-ridge";
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
static FuIntelThunderboltNvmFamily
|
||||||
|
fu_intel_thunderbolt_nvm_family_from_string(const gchar *family)
|
||||||
|
{
|
||||||
|
if (g_strcmp0(family, "falcon-ridge") == 0)
|
||||||
|
return FU_INTEL_THUNDERBOLT_NVM_FAMILY_FR;
|
||||||
|
if (g_strcmp0(family, "win-ridge") == 0)
|
||||||
|
return FU_INTEL_THUNDERBOLT_NVM_FAMILY_WR;
|
||||||
|
if (g_strcmp0(family, "alpine-ridge") == 0)
|
||||||
|
return FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR;
|
||||||
|
if (g_strcmp0(family, "alpine-ridge-c") == 0)
|
||||||
|
return FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR_C;
|
||||||
|
if (g_strcmp0(family, "titan-ridge") == 0)
|
||||||
|
return FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR;
|
||||||
|
if (g_strcmp0(family, "bb") == 0)
|
||||||
|
return FU_INTEL_THUNDERBOLT_NVM_FAMILY_BB;
|
||||||
|
if (g_strcmp0(family, "maple-ridge") == 0)
|
||||||
|
return FU_INTEL_THUNDERBOLT_NVM_FAMILY_MR;
|
||||||
|
return FU_INTEL_THUNDERBOLT_NVM_FAMILY_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const gchar *
|
||||||
|
fu_intel_thunderbolt_nvm_section_to_string(FuIntelThunderboltNvmSection section)
|
||||||
|
{
|
||||||
|
if (section == FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL)
|
||||||
|
return "digital";
|
||||||
|
if (section == FU_INTEL_THUNDERBOLT_NVM_SECTION_DROM)
|
||||||
|
return "drom";
|
||||||
|
if (section == FU_INTEL_THUNDERBOLT_NVM_SECTION_ARC_PARAMS)
|
||||||
|
return "arc-params";
|
||||||
|
if (section == FU_INTEL_THUNDERBOLT_NVM_SECTION_DRAM_UCODE)
|
||||||
|
return "dram-ucode";
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fu_intel_thunderbolt_nvm_get_vendor_id:
|
||||||
|
* @self: a #FuFirmware
|
||||||
|
*
|
||||||
|
* Gets the vendor ID.
|
||||||
|
*
|
||||||
|
* Returns: an integer, or 0x0 for unset
|
||||||
|
*
|
||||||
|
* Since: 1.8.5
|
||||||
|
**/
|
||||||
|
guint16
|
||||||
|
fu_intel_thunderbolt_nvm_get_vendor_id(FuIntelThunderboltNvm *self)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
g_return_val_if_fail(FU_IS_INTEL_THUNDERBOLT_NVM(self), G_MAXUINT16);
|
||||||
|
return priv->vendor_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fu_intel_thunderbolt_nvm_get_device_id:
|
||||||
|
* @self: a #FuFirmware
|
||||||
|
*
|
||||||
|
* Gets the device ID.
|
||||||
|
*
|
||||||
|
* Returns: an integer, or 0x0 for unset
|
||||||
|
*
|
||||||
|
* Since: 1.8.5
|
||||||
|
**/
|
||||||
|
guint16
|
||||||
|
fu_intel_thunderbolt_nvm_get_device_id(FuIntelThunderboltNvm *self)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
return priv->device_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fu_intel_thunderbolt_nvm_is_host:
|
||||||
|
* @self: a #FuFirmware
|
||||||
|
*
|
||||||
|
* Gets if the firmware is designed for a host controller rather than a device.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE for controller, %FALSE for device
|
||||||
|
*
|
||||||
|
* Since: 1.8.5
|
||||||
|
**/
|
||||||
|
gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_is_host(FuIntelThunderboltNvm *self)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
g_return_val_if_fail(FU_IS_INTEL_THUNDERBOLT_NVM(self), FALSE);
|
||||||
|
return priv->is_host;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fu_intel_thunderbolt_nvm_is_native:
|
||||||
|
* @self: a #FuFirmware
|
||||||
|
*
|
||||||
|
* Gets if the device is native, i.e. not in recovery mode.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if set
|
||||||
|
*
|
||||||
|
* Since: 1.8.5
|
||||||
|
**/
|
||||||
|
gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_is_native(FuIntelThunderboltNvm *self)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
g_return_val_if_fail(FU_IS_INTEL_THUNDERBOLT_NVM(self), FALSE);
|
||||||
|
return priv->is_native;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fu_intel_thunderbolt_nvm_has_pd:
|
||||||
|
* @self: a #FuFirmware
|
||||||
|
*
|
||||||
|
* Gets if the device has power delivery capability.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if set
|
||||||
|
*
|
||||||
|
* Since: 1.8.5
|
||||||
|
**/
|
||||||
|
gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_has_pd(FuIntelThunderboltNvm *self)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
g_return_val_if_fail(FU_IS_INTEL_THUNDERBOLT_NVM(self), FALSE);
|
||||||
|
return priv->has_pd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fu_intel_thunderbolt_nvm_get_model_id:
|
||||||
|
* @self: a #FuFirmware
|
||||||
|
*
|
||||||
|
* Gets the model ID.
|
||||||
|
*
|
||||||
|
* Returns: an integer, or 0x0 for unset
|
||||||
|
*
|
||||||
|
* Since: 1.8.5
|
||||||
|
**/
|
||||||
|
guint16
|
||||||
|
fu_intel_thunderbolt_nvm_get_model_id(FuIntelThunderboltNvm *self)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
g_return_val_if_fail(FU_IS_INTEL_THUNDERBOLT_NVM(self), 0x0);
|
||||||
|
return priv->model_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fu_intel_thunderbolt_nvm_get_flash_size:
|
||||||
|
* @self: a #FuFirmware
|
||||||
|
*
|
||||||
|
* Gets the flash size.
|
||||||
|
*
|
||||||
|
* NOTE: This does not correspond to a size in bytes, or a power of 2 and is only useful for
|
||||||
|
* comparison between firmware and device.
|
||||||
|
*
|
||||||
|
* Returns: an integer, or 0x0 for unset
|
||||||
|
*
|
||||||
|
* Since: 1.8.5
|
||||||
|
**/
|
||||||
|
guint8
|
||||||
|
fu_intel_thunderbolt_nvm_get_flash_size(FuIntelThunderboltNvm *self)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
g_return_val_if_fail(FU_IS_INTEL_THUNDERBOLT_NVM(self), 0x0);
|
||||||
|
return priv->flash_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_intel_thunderbolt_nvm_export(FuFirmware *firmware,
|
||||||
|
FuFirmwareExportFlags flags,
|
||||||
|
XbBuilderNode *bn)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvm *self = FU_INTEL_THUNDERBOLT_NVM(firmware);
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
fu_xmlb_builder_insert_kx(bn, "vendor_id", priv->vendor_id);
|
||||||
|
fu_xmlb_builder_insert_kx(bn, "device_id", priv->device_id);
|
||||||
|
fu_xmlb_builder_insert_kx(bn, "model_id", priv->model_id);
|
||||||
|
fu_xmlb_builder_insert_kv(bn,
|
||||||
|
"family",
|
||||||
|
fu_intel_thunderbolt_nvm_family_to_string(priv->family));
|
||||||
|
fu_xmlb_builder_insert_kb(bn, "is_host", priv->is_host);
|
||||||
|
fu_xmlb_builder_insert_kb(bn, "is_native", priv->is_native);
|
||||||
|
fu_xmlb_builder_insert_kx(bn, "flash_size", priv->flash_size);
|
||||||
|
fu_xmlb_builder_insert_kx(bn, "generation", priv->gen);
|
||||||
|
fu_xmlb_builder_insert_kx(bn, "ports", priv->ports);
|
||||||
|
fu_xmlb_builder_insert_kb(bn, "has_pd", priv->has_pd);
|
||||||
|
for (guint i = 0; i < FU_INTEL_THUNDERBOLT_NVM_SECTION_LAST; i++) {
|
||||||
|
if (priv->sections[i] != 0x0) {
|
||||||
|
g_autofree gchar *tmp = g_strdup_printf("0x%x", priv->sections[i]);
|
||||||
|
g_autoptr(XbBuilderNode) bc =
|
||||||
|
xb_builder_node_insert(bn,
|
||||||
|
"section",
|
||||||
|
"type",
|
||||||
|
fu_intel_thunderbolt_nvm_section_to_string(i),
|
||||||
|
"offset",
|
||||||
|
tmp,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_valid_pd_pointer(guint32 pointer)
|
||||||
|
{
|
||||||
|
return pointer != 0 && pointer != 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_read_uint8(FuIntelThunderboltNvm *self,
|
||||||
|
FuIntelThunderboltNvmSection section,
|
||||||
|
guint32 offset,
|
||||||
|
guint8 *value,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
g_autoptr(GBytes) fw = NULL;
|
||||||
|
|
||||||
|
/* get blob and read */
|
||||||
|
fw = fu_firmware_get_bytes(FU_FIRMWARE(self), error);
|
||||||
|
if (fw == NULL)
|
||||||
|
return FALSE;
|
||||||
|
return fu_memread_uint8_safe(g_bytes_get_data(fw, NULL),
|
||||||
|
g_bytes_get_size(fw),
|
||||||
|
priv->sections[section] + offset,
|
||||||
|
value,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_read_uint16(FuIntelThunderboltNvm *self,
|
||||||
|
FuIntelThunderboltNvmSection section,
|
||||||
|
guint32 offset,
|
||||||
|
guint16 *value,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
g_autoptr(GBytes) fw = NULL;
|
||||||
|
|
||||||
|
/* get blob and read */
|
||||||
|
fw = fu_firmware_get_bytes(FU_FIRMWARE(self), error);
|
||||||
|
if (fw == NULL)
|
||||||
|
return FALSE;
|
||||||
|
return fu_memread_uint16_safe(g_bytes_get_data(fw, NULL),
|
||||||
|
g_bytes_get_size(fw),
|
||||||
|
priv->sections[section] + offset,
|
||||||
|
value,
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_read_uint32(FuIntelThunderboltNvm *self,
|
||||||
|
FuIntelThunderboltNvmSection section,
|
||||||
|
guint32 offset,
|
||||||
|
guint32 *value,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
g_autoptr(GBytes) fw = NULL;
|
||||||
|
|
||||||
|
/* get blob and read */
|
||||||
|
fw = fu_firmware_get_bytes(FU_FIRMWARE(self), error);
|
||||||
|
if (fw == NULL)
|
||||||
|
return FALSE;
|
||||||
|
return fu_memread_uint32_safe(g_bytes_get_data(fw, NULL),
|
||||||
|
g_bytes_get_size(fw),
|
||||||
|
priv->sections[section] + offset,
|
||||||
|
value,
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Size of ucode sections is uint16 value saved at the start of the section,
|
||||||
|
* it's in DWORDS (4-bytes) units and it doesn't include itself. We need the
|
||||||
|
* offset to the next section, so we translate it to bytes and add 2 for the
|
||||||
|
* size field itself.
|
||||||
|
*
|
||||||
|
* offset parameter must be relative to digital section
|
||||||
|
*/
|
||||||
|
static gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_read_ucode_section_len(FuIntelThunderboltNvm *self,
|
||||||
|
guint32 offset,
|
||||||
|
guint16 *value,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_uint16(self,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL,
|
||||||
|
offset,
|
||||||
|
value,
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to read ucode section len: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
*value *= sizeof(guint32);
|
||||||
|
*value += sizeof(guint16);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* assumes sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL].offset is already set */
|
||||||
|
static gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_read_sections(FuIntelThunderboltNvm *self, GError **error)
|
||||||
|
{
|
||||||
|
guint32 offset;
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
|
||||||
|
if (priv->gen >= 3 || priv->gen == 0) {
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_uint32(
|
||||||
|
self,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_DROM,
|
||||||
|
&offset,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DROM] =
|
||||||
|
offset + priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL];
|
||||||
|
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_uint32(
|
||||||
|
self,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_ARC_PARAMS,
|
||||||
|
&offset,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_ARC_PARAMS] =
|
||||||
|
offset + priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priv->is_host && priv->gen > 2) {
|
||||||
|
/*
|
||||||
|
* To find the DRAM section, we have to jump from section to
|
||||||
|
* section in a chain of sections.
|
||||||
|
* available_sections location tells what sections exist at all
|
||||||
|
* (with a flag per section).
|
||||||
|
* ee_ucode_start_addr location tells the offset of the first
|
||||||
|
* section in the list relatively to the digital section start.
|
||||||
|
* After having the offset of the first section, we have a loop
|
||||||
|
* over the section list. If the section exists, we read its
|
||||||
|
* length (2 bytes at section start) and add it to current
|
||||||
|
* offset to find the start of the next section. Otherwise, we
|
||||||
|
* already have the next section offset...
|
||||||
|
*/
|
||||||
|
const guint8 DRAM_FLAG = 1 << 6;
|
||||||
|
guint16 ucode_offset;
|
||||||
|
guint8 available_sections = 0;
|
||||||
|
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_uint8(
|
||||||
|
self,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_AVAILABLE_SECTIONS,
|
||||||
|
&available_sections,
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to read available sections: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_uint16(
|
||||||
|
self,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_UCODE,
|
||||||
|
&ucode_offset,
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to read ucode offset: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
offset = ucode_offset;
|
||||||
|
if ((available_sections & DRAM_FLAG) == 0) {
|
||||||
|
g_set_error_literal(error,
|
||||||
|
FWUPD_ERROR,
|
||||||
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
|
"cannot find needed FW sections in the FW image file");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (guint8 i = 1; i < DRAM_FLAG; i <<= 1) {
|
||||||
|
if (available_sections & i) {
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_ucode_section_len(self,
|
||||||
|
offset,
|
||||||
|
&ucode_offset,
|
||||||
|
error))
|
||||||
|
return FALSE;
|
||||||
|
offset += ucode_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DRAM_UCODE] =
|
||||||
|
offset + priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL];
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_missing_needed_drom(FuIntelThunderboltNvm *self)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
if (priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DROM] != 0)
|
||||||
|
return FALSE;
|
||||||
|
if (priv->is_host && priv->gen < 3)
|
||||||
|
return FALSE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_parse(FuFirmware *firmware,
|
||||||
|
GBytes *fw,
|
||||||
|
gsize offset,
|
||||||
|
FwupdInstallFlags flags,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvm *self = FU_INTEL_THUNDERBOLT_NVM(firmware);
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
guint8 tmp = 0;
|
||||||
|
guint16 version_raw = 0;
|
||||||
|
struct {
|
||||||
|
guint16 device_id;
|
||||||
|
guint gen;
|
||||||
|
FuIntelThunderboltNvmFamily family;
|
||||||
|
guint ports;
|
||||||
|
} hw_info_arr[] = {{0x156D, 2, FU_INTEL_THUNDERBOLT_NVM_FAMILY_FR, 2}, /* FR 4C */
|
||||||
|
{0x156B, 2, FU_INTEL_THUNDERBOLT_NVM_FAMILY_FR, 1}, /* FR 2C */
|
||||||
|
{0x157E, 2, FU_INTEL_THUNDERBOLT_NVM_FAMILY_WR, 1}, /* WR */
|
||||||
|
{0x1578, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR, 2}, /* AR 4C */
|
||||||
|
{0x1576, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR, 1}, /* AR 2C */
|
||||||
|
{0x15C0, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR, 1}, /* AR LP */
|
||||||
|
{0x15D3, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR_C, 2}, /* AR-C 4C */
|
||||||
|
{0x15DA, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR_C, 1}, /* AR-C 2C */
|
||||||
|
{0x15E7, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR, 1}, /* TR 2C */
|
||||||
|
{0x15EA, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR, 2}, /* TR 4C */
|
||||||
|
{0x15EF, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR, 2}, /* TR 4C device */
|
||||||
|
{0x15EE, 3, FU_INTEL_THUNDERBOLT_NVM_FAMILY_BB, 0}, /* BB device */
|
||||||
|
/* Maple ridge devices
|
||||||
|
* NOTE: These are expected to be flashed via UEFI capsules *not*
|
||||||
|
* Thunderbolt plugin Flashing via fwupd will require matching kernel
|
||||||
|
* work. They're left here only for parsing the binaries
|
||||||
|
*/
|
||||||
|
{0x1136, 4, FU_INTEL_THUNDERBOLT_NVM_FAMILY_MR, 2},
|
||||||
|
{0x1137, 4, FU_INTEL_THUNDERBOLT_NVM_FAMILY_MR, 2},
|
||||||
|
{0}};
|
||||||
|
g_autofree gchar *version = NULL;
|
||||||
|
g_autoptr(FuFirmware) img_payload = NULL;
|
||||||
|
g_autoptr(GBytes) fw_payload = NULL;
|
||||||
|
|
||||||
|
/* add this straight away */
|
||||||
|
priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL] = offset;
|
||||||
|
fu_firmware_set_bytes(firmware, fw);
|
||||||
|
|
||||||
|
/* is native */
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_uint8(
|
||||||
|
self,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLAGS_IS_NATIVE,
|
||||||
|
&tmp,
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to read native: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
priv->is_native = tmp & 0x20;
|
||||||
|
|
||||||
|
/* we're only reading the first chunk */
|
||||||
|
if (g_bytes_get_size(fw) == 0x80)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* host or device */
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_uint8(self,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLAGS_HOST,
|
||||||
|
&tmp,
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to read is-host: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
priv->is_host = tmp & (1 << 1);
|
||||||
|
|
||||||
|
/* device ID */
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_uint16(self,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_DEVICE_ID,
|
||||||
|
&priv->device_id,
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to read device-id: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is best-effort */
|
||||||
|
for (guint i = 0; hw_info_arr[i].device_id != 0; i++) {
|
||||||
|
if (hw_info_arr[i].device_id == priv->device_id) {
|
||||||
|
priv->family = hw_info_arr[i].family;
|
||||||
|
priv->gen = hw_info_arr[i].gen;
|
||||||
|
priv->ports = hw_info_arr[i].ports;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (priv->ports == 0 && priv->is_host) {
|
||||||
|
g_set_error(error,
|
||||||
|
FWUPD_ERROR,
|
||||||
|
FWUPD_ERROR_NOT_SUPPORTED,
|
||||||
|
"unknown controller: %x",
|
||||||
|
priv->device_id);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read sections from file */
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_sections(self, error))
|
||||||
|
return FALSE;
|
||||||
|
if (fu_intel_thunderbolt_nvm_missing_needed_drom(self)) {
|
||||||
|
g_set_error_literal(error,
|
||||||
|
FWUPD_ERROR,
|
||||||
|
FWUPD_ERROR_READ,
|
||||||
|
"cannot find required drom section");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vendor:model */
|
||||||
|
if (priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_DROM] != 0x0) {
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_uint16(
|
||||||
|
self,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DROM,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DROM_OFFSET_VENDOR_ID,
|
||||||
|
&priv->vendor_id,
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to read vendor-id: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_uint16(
|
||||||
|
self,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DROM,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DROM_OFFSET_MODEL_ID,
|
||||||
|
&priv->model_id,
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to read model-id: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* has PD */
|
||||||
|
if (priv->sections[FU_INTEL_THUNDERBOLT_NVM_SECTION_ARC_PARAMS] != 0x0) {
|
||||||
|
guint32 pd_pointer = 0x0;
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_uint32(
|
||||||
|
self,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_ARC_PARAMS,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_ARC_PARAMS_OFFSET_PD_POINTER,
|
||||||
|
&pd_pointer,
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to read pd-pointer: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
priv->has_pd = fu_intel_thunderbolt_nvm_valid_pd_pointer(pd_pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* versions */
|
||||||
|
switch (priv->family) {
|
||||||
|
case FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR: // FIXME and GR
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_uint16(
|
||||||
|
self,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_VERSION,
|
||||||
|
&version_raw,
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to read version: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
fu_firmware_set_version_raw(FU_FIRMWARE(self), version_raw);
|
||||||
|
version = fu_version_from_uint16(version_raw, FWUPD_VERSION_FORMAT_BCD);
|
||||||
|
fu_firmware_set_version(FU_FIRMWARE(self), version);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priv->is_host) {
|
||||||
|
switch (priv->family) {
|
||||||
|
case FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR:
|
||||||
|
case FU_INTEL_THUNDERBOLT_NVM_FAMILY_AR_C:
|
||||||
|
case FU_INTEL_THUNDERBOLT_NVM_FAMILY_TR:
|
||||||
|
/* used for comparison between old and new image, not a raw number */
|
||||||
|
if (!fu_intel_thunderbolt_nvm_read_uint8(
|
||||||
|
self,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_SECTION_DIGITAL,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLASH_SIZE,
|
||||||
|
&tmp,
|
||||||
|
error)) {
|
||||||
|
g_prefix_error(error, "failed to read flash size: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
priv->flash_size = tmp & 0x07;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* as as easy-to-grab payload blob */
|
||||||
|
if (offset > 0) {
|
||||||
|
fw_payload = fu_bytes_new_offset(fw, offset, g_bytes_get_size(fw) - offset, error);
|
||||||
|
if (fw_payload == NULL)
|
||||||
|
return FALSE;
|
||||||
|
} else {
|
||||||
|
fw_payload = g_bytes_ref(fw);
|
||||||
|
}
|
||||||
|
img_payload = fu_firmware_new_from_bytes(fw_payload);
|
||||||
|
fu_firmware_set_id(img_payload, FU_FIRMWARE_ID_PAYLOAD);
|
||||||
|
fu_firmware_add_image(firmware, img_payload);
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* can only write version 3 NVM */
|
||||||
|
static GBytes *
|
||||||
|
fu_intel_thunderbolt_nvm_write(FuFirmware *firmware, GError **error)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvm *self = FU_INTEL_THUNDERBOLT_NVM(firmware);
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
guint32 digital_size = 0x120;
|
||||||
|
guint32 drom_offset = 0 + digital_size;
|
||||||
|
guint32 drom_size = 0x20;
|
||||||
|
guint32 arc_param_offset = drom_offset + drom_size;
|
||||||
|
guint32 arc_param_size = 0x120;
|
||||||
|
g_autoptr(GByteArray) buf = g_byte_array_new();
|
||||||
|
|
||||||
|
/* minimal size */
|
||||||
|
fu_byte_array_set_size(buf, arc_param_offset + arc_param_size, 0x0);
|
||||||
|
|
||||||
|
/* digital section */
|
||||||
|
if (!fu_memwrite_uint8_safe(buf->data,
|
||||||
|
buf->len,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_AVAILABLE_SECTIONS,
|
||||||
|
0x0,
|
||||||
|
error))
|
||||||
|
return NULL;
|
||||||
|
if (!fu_memwrite_uint16_safe(buf->data,
|
||||||
|
buf->len,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_UCODE,
|
||||||
|
0x0,
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error))
|
||||||
|
return NULL;
|
||||||
|
if (!fu_memwrite_uint8_safe(buf->data,
|
||||||
|
buf->len,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLAGS_IS_NATIVE,
|
||||||
|
priv->is_native ? 0x20 : 0x0,
|
||||||
|
error))
|
||||||
|
return NULL;
|
||||||
|
if (!fu_memwrite_uint8_safe(buf->data,
|
||||||
|
buf->len,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLAGS_HOST,
|
||||||
|
priv->is_host ? 0x2 : 0x0,
|
||||||
|
error))
|
||||||
|
return NULL;
|
||||||
|
if (!fu_memwrite_uint32_safe(buf->data,
|
||||||
|
buf->len,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_DEVICE_ID,
|
||||||
|
priv->device_id,
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error))
|
||||||
|
return NULL;
|
||||||
|
if (!fu_memwrite_uint16_safe(buf->data,
|
||||||
|
buf->len,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_VERSION,
|
||||||
|
fu_firmware_get_version_raw(firmware),
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error))
|
||||||
|
return NULL;
|
||||||
|
if (!fu_memwrite_uint8_safe(buf->data,
|
||||||
|
buf->len,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLASH_SIZE,
|
||||||
|
priv->flash_size,
|
||||||
|
error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* drom section */
|
||||||
|
if (!fu_memwrite_uint32_safe(buf->data,
|
||||||
|
buf->len,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_DROM,
|
||||||
|
drom_offset,
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error))
|
||||||
|
return NULL;
|
||||||
|
if (!fu_memwrite_uint16_safe(buf->data,
|
||||||
|
buf->len,
|
||||||
|
drom_offset + FU_INTEL_THUNDERBOLT_NVM_DROM_OFFSET_VENDOR_ID,
|
||||||
|
priv->vendor_id,
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error))
|
||||||
|
return NULL;
|
||||||
|
if (!fu_memwrite_uint16_safe(buf->data,
|
||||||
|
buf->len,
|
||||||
|
drom_offset + FU_INTEL_THUNDERBOLT_NVM_DROM_OFFSET_MODEL_ID,
|
||||||
|
priv->model_id,
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* ARC param section */
|
||||||
|
if (!fu_memwrite_uint32_safe(buf->data,
|
||||||
|
buf->len,
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_ARC_PARAMS,
|
||||||
|
arc_param_offset,
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error))
|
||||||
|
return NULL;
|
||||||
|
if (!fu_memwrite_uint32_safe(buf->data,
|
||||||
|
buf->len,
|
||||||
|
arc_param_offset +
|
||||||
|
FU_INTEL_THUNDERBOLT_NVM_ARC_PARAMS_OFFSET_PD_POINTER,
|
||||||
|
priv->has_pd ? 0x1 : 0x0,
|
||||||
|
G_LITTLE_ENDIAN,
|
||||||
|
error))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return g_byte_array_free_to_bytes(g_steal_pointer(&buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_check_compatible(FuFirmware *firmware,
|
||||||
|
FuFirmware *firmware_other,
|
||||||
|
FwupdInstallFlags flags,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvm *self = FU_INTEL_THUNDERBOLT_NVM(firmware);
|
||||||
|
FuIntelThunderboltNvm *other = FU_INTEL_THUNDERBOLT_NVM(firmware_other);
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
FuIntelThunderboltNvmPrivate *priv_other = GET_PRIVATE(other);
|
||||||
|
|
||||||
|
if (priv->is_host != priv_other->is_host) {
|
||||||
|
g_set_error(error,
|
||||||
|
FWUPD_ERROR,
|
||||||
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
|
"incorrect firmware mode, got %s, expected %s",
|
||||||
|
priv->is_host ? "host" : "device",
|
||||||
|
priv_other->is_host ? "host" : "device");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (priv->vendor_id != priv_other->vendor_id) {
|
||||||
|
g_set_error(error,
|
||||||
|
FWUPD_ERROR,
|
||||||
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
|
"incorrect device vendor, got 0x%04x, expected 0x%04x",
|
||||||
|
priv->vendor_id,
|
||||||
|
priv_other->vendor_id);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (priv->device_id != priv_other->device_id) {
|
||||||
|
g_set_error(error,
|
||||||
|
FWUPD_ERROR,
|
||||||
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
|
"incorrect device type, got 0x%04x, expected 0x%04x",
|
||||||
|
priv->device_id,
|
||||||
|
priv_other->device_id);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) {
|
||||||
|
if (priv->model_id != priv_other->model_id) {
|
||||||
|
g_set_error(error,
|
||||||
|
FWUPD_ERROR,
|
||||||
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
|
"incorrect device model, got 0x%04x, expected 0x%04x",
|
||||||
|
priv->model_id,
|
||||||
|
priv_other->model_id);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/* old firmware has PD but new doesn't (we don't care about other way around) */
|
||||||
|
if (priv->has_pd && !priv_other->has_pd) {
|
||||||
|
g_set_error_literal(error,
|
||||||
|
FWUPD_ERROR,
|
||||||
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
|
"incorrect PD section");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (priv->flash_size != priv_other->flash_size) {
|
||||||
|
g_set_error_literal(error,
|
||||||
|
FWUPD_ERROR,
|
||||||
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
|
"incorrect flash size");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_build(FuFirmware *firmware, XbNode *n, GError **error)
|
||||||
|
{
|
||||||
|
FuIntelThunderboltNvm *self = FU_INTEL_THUNDERBOLT_NVM(firmware);
|
||||||
|
FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self);
|
||||||
|
const gchar *tmp;
|
||||||
|
|
||||||
|
/* simple properties */
|
||||||
|
tmp = xb_node_query_text(n, "vendor_id", NULL);
|
||||||
|
if (tmp != NULL) {
|
||||||
|
guint64 val = 0;
|
||||||
|
if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT16, error))
|
||||||
|
return FALSE;
|
||||||
|
priv->vendor_id = val;
|
||||||
|
}
|
||||||
|
tmp = xb_node_query_text(n, "device_id", NULL);
|
||||||
|
if (tmp != NULL) {
|
||||||
|
guint64 val = 0;
|
||||||
|
if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT16, error))
|
||||||
|
return FALSE;
|
||||||
|
priv->device_id = val;
|
||||||
|
}
|
||||||
|
tmp = xb_node_query_text(n, "model_id", NULL);
|
||||||
|
if (tmp != NULL) {
|
||||||
|
guint64 val = 0;
|
||||||
|
if (!fu_strtoull(tmp, &val, 0x0, G_MAXUINT16, error))
|
||||||
|
return FALSE;
|
||||||
|
priv->model_id = val;
|
||||||
|
}
|
||||||
|
tmp = xb_node_query_text(n, "family", NULL);
|
||||||
|
if (tmp != NULL) {
|
||||||
|
priv->family = fu_intel_thunderbolt_nvm_family_from_string(tmp);
|
||||||
|
if (priv->family == FU_INTEL_THUNDERBOLT_NVM_FAMILY_UNKNOWN) {
|
||||||
|
g_set_error(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_INVALID_DATA,
|
||||||
|
"unknown family: %s",
|
||||||
|
tmp);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp = xb_node_query_text(n, "flash_size", NULL);
|
||||||
|
if (tmp != NULL) {
|
||||||
|
guint64 val = 0;
|
||||||
|
if (!fu_strtoull(tmp, &val, 0x0, 0x07, error))
|
||||||
|
return FALSE;
|
||||||
|
priv->flash_size = val;
|
||||||
|
}
|
||||||
|
tmp = xb_node_query_text(n, "is_host", NULL);
|
||||||
|
if (tmp != NULL) {
|
||||||
|
if (!fu_strtobool(tmp, &priv->is_host, error))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
tmp = xb_node_query_text(n, "is_native", NULL);
|
||||||
|
if (tmp != NULL) {
|
||||||
|
if (!fu_strtobool(tmp, &priv->is_native, error))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_intel_thunderbolt_nvm_init(FuIntelThunderboltNvm *self)
|
||||||
|
{
|
||||||
|
fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_intel_thunderbolt_nvm_class_init(FuIntelThunderboltNvmClass *klass)
|
||||||
|
{
|
||||||
|
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass);
|
||||||
|
klass_firmware->export = fu_intel_thunderbolt_nvm_export;
|
||||||
|
klass_firmware->parse = fu_intel_thunderbolt_nvm_parse;
|
||||||
|
klass_firmware->write = fu_intel_thunderbolt_nvm_write;
|
||||||
|
klass_firmware->build = fu_intel_thunderbolt_nvm_build;
|
||||||
|
klass_firmware->check_compatible = fu_intel_thunderbolt_nvm_check_compatible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fu_intel_thunderbolt_nvm_new:
|
||||||
|
*
|
||||||
|
* Creates a new #FuFirmware of Intel NVM format
|
||||||
|
*
|
||||||
|
* Since: 1.8.5
|
||||||
|
**/
|
||||||
|
FuFirmware *
|
||||||
|
fu_intel_thunderbolt_nvm_new(void)
|
||||||
|
{
|
||||||
|
return FU_FIRMWARE(g_object_new(FU_TYPE_INTEL_THUNDERBOLT_NVM, NULL));
|
||||||
|
}
|
41
libfwupdplugin/fu-intel-thunderbolt-nvm.h
Normal file
41
libfwupdplugin/fu-intel-thunderbolt-nvm.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Dell Inc.
|
||||||
|
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
|
||||||
|
* Copyright (C) 2020 Mario Limonciello <mario.limonciello@dell.com>
|
||||||
|
* Copyright (C) 2017 Intel Corporation.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "fu-firmware.h"
|
||||||
|
|
||||||
|
#define FU_TYPE_INTEL_THUNDERBOLT_NVM (fu_intel_thunderbolt_nvm_get_type())
|
||||||
|
G_DECLARE_DERIVABLE_TYPE(FuIntelThunderboltNvm,
|
||||||
|
fu_intel_thunderbolt_nvm,
|
||||||
|
FU,
|
||||||
|
INTEL_THUNDERBOLT_NVM,
|
||||||
|
FuFirmware)
|
||||||
|
|
||||||
|
struct _FuIntelThunderboltNvmClass {
|
||||||
|
FuFirmwareClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
guint16
|
||||||
|
fu_intel_thunderbolt_nvm_get_vendor_id(FuIntelThunderboltNvm *self);
|
||||||
|
guint16
|
||||||
|
fu_intel_thunderbolt_nvm_get_device_id(FuIntelThunderboltNvm *self);
|
||||||
|
guint16
|
||||||
|
fu_intel_thunderbolt_nvm_get_model_id(FuIntelThunderboltNvm *self);
|
||||||
|
gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_is_host(FuIntelThunderboltNvm *self);
|
||||||
|
gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_is_native(FuIntelThunderboltNvm *self);
|
||||||
|
gboolean
|
||||||
|
fu_intel_thunderbolt_nvm_has_pd(FuIntelThunderboltNvm *self);
|
||||||
|
guint8
|
||||||
|
fu_intel_thunderbolt_nvm_get_flash_size(FuIntelThunderboltNvm *self);
|
||||||
|
|
||||||
|
FuFirmware *
|
||||||
|
fu_intel_thunderbolt_nvm_new(void);
|
@ -3249,6 +3249,9 @@ fu_firmware_builder_round_trip_func(void)
|
|||||||
{FU_TYPE_OPROM_FIRMWARE,
|
{FU_TYPE_OPROM_FIRMWARE,
|
||||||
"oprom.builder.xml",
|
"oprom.builder.xml",
|
||||||
"2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"},
|
"2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"},
|
||||||
|
{FU_TYPE_INTEL_THUNDERBOLT_NVM,
|
||||||
|
"intel-thunderbolt.builder.xml",
|
||||||
|
"e858000646fecb5223b41df57647c005b495749b"},
|
||||||
#ifdef HAVE_CBOR
|
#ifdef HAVE_CBOR
|
||||||
{FU_TYPE_USWID_FIRMWARE,
|
{FU_TYPE_USWID_FIRMWARE,
|
||||||
"uswid.builder.xml",
|
"uswid.builder.xml",
|
||||||
|
@ -52,6 +52,8 @@
|
|||||||
#include <libfwupdplugin/fu-ifwi-cpd-firmware.h>
|
#include <libfwupdplugin/fu-ifwi-cpd-firmware.h>
|
||||||
#include <libfwupdplugin/fu-ifwi-fpt-firmware.h>
|
#include <libfwupdplugin/fu-ifwi-fpt-firmware.h>
|
||||||
#include <libfwupdplugin/fu-ihex-firmware.h>
|
#include <libfwupdplugin/fu-ihex-firmware.h>
|
||||||
|
#include <libfwupdplugin/fu-intel-thunderbolt-firmware.h>
|
||||||
|
#include <libfwupdplugin/fu-intel-thunderbolt-nvm.h>
|
||||||
#include <libfwupdplugin/fu-io-channel.h>
|
#include <libfwupdplugin/fu-io-channel.h>
|
||||||
#include <libfwupdplugin/fu-kernel.h>
|
#include <libfwupdplugin/fu-kernel.h>
|
||||||
#include <libfwupdplugin/fu-linear-firmware.h>
|
#include <libfwupdplugin/fu-linear-firmware.h>
|
||||||
|
@ -1095,3 +1095,19 @@ LIBFWUPDPLUGIN_1.8.4 {
|
|||||||
fu_security_attr_new;
|
fu_security_attr_new;
|
||||||
local: *;
|
local: *;
|
||||||
} LIBFWUPDPLUGIN_1.8.3;
|
} LIBFWUPDPLUGIN_1.8.3;
|
||||||
|
|
||||||
|
LIBFWUPDPLUGIN_1.8.5 {
|
||||||
|
global:
|
||||||
|
fu_intel_thunderbolt_firmware_get_type;
|
||||||
|
fu_intel_thunderbolt_firmware_new;
|
||||||
|
fu_intel_thunderbolt_nvm_get_device_id;
|
||||||
|
fu_intel_thunderbolt_nvm_get_flash_size;
|
||||||
|
fu_intel_thunderbolt_nvm_get_model_id;
|
||||||
|
fu_intel_thunderbolt_nvm_get_type;
|
||||||
|
fu_intel_thunderbolt_nvm_get_vendor_id;
|
||||||
|
fu_intel_thunderbolt_nvm_has_pd;
|
||||||
|
fu_intel_thunderbolt_nvm_is_host;
|
||||||
|
fu_intel_thunderbolt_nvm_is_native;
|
||||||
|
fu_intel_thunderbolt_nvm_new;
|
||||||
|
local: *;
|
||||||
|
} LIBFWUPDPLUGIN_1.8.4;
|
||||||
|
@ -36,6 +36,8 @@ fwupdplugin_src = [
|
|||||||
'fu-ifwi-cpd-firmware.c', # fuzzing
|
'fu-ifwi-cpd-firmware.c', # fuzzing
|
||||||
'fu-ifwi-fpt-firmware.c', # fuzzing
|
'fu-ifwi-fpt-firmware.c', # fuzzing
|
||||||
'fu-oprom-firmware.c', # fuzzing
|
'fu-oprom-firmware.c', # fuzzing
|
||||||
|
'fu-intel-thunderbolt-nvm.c', # fuzzing
|
||||||
|
'fu-intel-thunderbolt-firmware.c', # fuzzing
|
||||||
'fu-cfu-common.c', # fuzzing
|
'fu-cfu-common.c', # fuzzing
|
||||||
'fu-cfu-offer.c', # fuzzing
|
'fu-cfu-offer.c', # fuzzing
|
||||||
'fu-cfu-payload.c', # fuzzing
|
'fu-cfu-payload.c', # fuzzing
|
||||||
@ -128,6 +130,8 @@ fwupdplugin_headers = [
|
|||||||
'fu-ifwi-cpd-firmware.h',
|
'fu-ifwi-cpd-firmware.h',
|
||||||
'fu-ifwi-fpt-firmware.h',
|
'fu-ifwi-fpt-firmware.h',
|
||||||
'fu-oprom-firmware.h',
|
'fu-oprom-firmware.h',
|
||||||
|
'fu-intel-thunderbolt-firmware.h',
|
||||||
|
'fu-intel-thunderbolt-nvm.h',
|
||||||
'fu-cfu-common.h',
|
'fu-cfu-common.h',
|
||||||
'fu-cfu-offer.h',
|
'fu-cfu-offer.h',
|
||||||
'fu-cfu-payload.h',
|
'fu-cfu-payload.h',
|
||||||
|
BIN
libfwupdplugin/tests/intel-thunderbolt.bin
Normal file
BIN
libfwupdplugin/tests/intel-thunderbolt.bin
Normal file
Binary file not shown.
11
libfwupdplugin/tests/intel-thunderbolt.builder.xml
Normal file
11
libfwupdplugin/tests/intel-thunderbolt.builder.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<firmware gtype="FuIntelNvmFirmware">
|
||||||
|
<offset>0x10</offset>
|
||||||
|
<vendor_id>0xd4</vendor_id>
|
||||||
|
<device_id>0x15ef</device_id>
|
||||||
|
<model_id>0xb070</model_id>
|
||||||
|
<family>titan-ridge</family>
|
||||||
|
<flash_size>0x3</flash_size>
|
||||||
|
<is_host>false</is_host>
|
||||||
|
<is_native>false</is_native>
|
||||||
|
<version_raw>0x6000</version_raw>
|
||||||
|
</firmware>
|
@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
#include "fu-thunderbolt-common.h"
|
#include "fu-thunderbolt-common.h"
|
||||||
#include "fu-thunderbolt-controller.h"
|
#include "fu-thunderbolt-controller.h"
|
||||||
#include "fu-thunderbolt-firmware-update.h"
|
|
||||||
#include "fu-thunderbolt-firmware.h"
|
|
||||||
#include "fu-thunderbolt-retimer.h"
|
#include "fu-thunderbolt-retimer.h"
|
||||||
|
|
||||||
/*5 seconds sleep until retimer is available \
|
/*5 seconds sleep until retimer is available \
|
||||||
@ -64,8 +62,6 @@ fu_plugin_thunderbolt_init(FuPlugin *plugin)
|
|||||||
fu_plugin_add_udev_subsystem(plugin, "thunderbolt");
|
fu_plugin_add_udev_subsystem(plugin, "thunderbolt");
|
||||||
fu_plugin_add_device_gtype(plugin, FU_TYPE_THUNDERBOLT_CONTROLLER);
|
fu_plugin_add_device_gtype(plugin, FU_TYPE_THUNDERBOLT_CONTROLLER);
|
||||||
fu_plugin_add_device_gtype(plugin, FU_TYPE_THUNDERBOLT_RETIMER);
|
fu_plugin_add_device_gtype(plugin, FU_TYPE_THUNDERBOLT_RETIMER);
|
||||||
fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_THUNDERBOLT_FIRMWARE);
|
|
||||||
fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_THUNDERBOLT_FIRMWARE_UPDATE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -22,8 +22,6 @@
|
|||||||
|
|
||||||
#include "fu-context-private.h"
|
#include "fu-context-private.h"
|
||||||
#include "fu-plugin-private.h"
|
#include "fu-plugin-private.h"
|
||||||
#include "fu-thunderbolt-firmware-update.h"
|
|
||||||
#include "fu-thunderbolt-firmware.h"
|
|
||||||
#include "fu-udev-device-private.h"
|
#include "fu-udev-device-private.h"
|
||||||
|
|
||||||
static gchar *
|
static gchar *
|
||||||
@ -1051,70 +1049,6 @@ test_tree(ThunderboltTest *tt, gconstpointer user_data)
|
|||||||
g_assert_true(ret);
|
g_assert_true(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
_compare_images(FuThunderboltFirmware *firmware_old,
|
|
||||||
FuThunderboltFirmware *firmware,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
if (fu_thunderbolt_firmware_is_host(firmware) !=
|
|
||||||
fu_thunderbolt_firmware_is_host(firmware_old)) {
|
|
||||||
g_set_error(error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
|
||||||
"incorrect firmware mode, got %s, expected %s",
|
|
||||||
fu_thunderbolt_firmware_is_host(firmware) ? "host" : "device",
|
|
||||||
fu_thunderbolt_firmware_is_host(firmware_old) ? "host" : "device");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (fu_thunderbolt_firmware_get_vendor_id(firmware) !=
|
|
||||||
fu_thunderbolt_firmware_get_vendor_id(firmware_old)) {
|
|
||||||
g_set_error(error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
|
||||||
"incorrect device vendor, got 0x%04x, expected 0x%04x",
|
|
||||||
fu_thunderbolt_firmware_get_vendor_id(firmware),
|
|
||||||
fu_thunderbolt_firmware_get_vendor_id(firmware_old));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (fu_thunderbolt_firmware_get_device_id(firmware) !=
|
|
||||||
fu_thunderbolt_firmware_get_device_id(firmware_old)) {
|
|
||||||
g_set_error(error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
|
||||||
"incorrect device type, got 0x%04x, expected 0x%04x",
|
|
||||||
fu_thunderbolt_firmware_get_device_id(firmware),
|
|
||||||
fu_thunderbolt_firmware_get_device_id(firmware_old));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (fu_thunderbolt_firmware_get_model_id(firmware) !=
|
|
||||||
fu_thunderbolt_firmware_get_model_id(firmware_old)) {
|
|
||||||
g_set_error(error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
|
||||||
"incorrect device model, got 0x%04x, expected 0x%04x",
|
|
||||||
fu_thunderbolt_firmware_get_model_id(firmware),
|
|
||||||
fu_thunderbolt_firmware_get_model_id(firmware_old));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (fu_thunderbolt_firmware_get_has_pd(firmware_old) &&
|
|
||||||
!fu_thunderbolt_firmware_get_has_pd(firmware)) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
|
||||||
"new firmware is missing PD");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (fu_thunderbolt_firmware_get_flash_size(firmware) !=
|
|
||||||
fu_thunderbolt_firmware_get_flash_size(firmware_old)) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
|
||||||
"incorrect flash size");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_image_validation(ThunderboltTest *tt, gconstpointer user_data)
|
test_image_validation(ThunderboltTest *tt, gconstpointer user_data)
|
||||||
{
|
{
|
||||||
@ -1129,9 +1063,9 @@ test_image_validation(ThunderboltTest *tt, gconstpointer user_data)
|
|||||||
g_autoptr(GBytes) ctl_data = NULL;
|
g_autoptr(GBytes) ctl_data = NULL;
|
||||||
g_autoptr(GBytes) bad_data = NULL;
|
g_autoptr(GBytes) bad_data = NULL;
|
||||||
g_autoptr(GError) error = NULL;
|
g_autoptr(GError) error = NULL;
|
||||||
g_autoptr(FuThunderboltFirmwareUpdate) firmware_fwi = fu_thunderbolt_firmware_update_new();
|
g_autoptr(FuFirmware) firmware_fwi = fu_intel_thunderbolt_firmware_new();
|
||||||
g_autoptr(FuThunderboltFirmware) firmware_ctl = fu_thunderbolt_firmware_new();
|
g_autoptr(FuFirmware) firmware_ctl = fu_intel_thunderbolt_nvm_new();
|
||||||
g_autoptr(FuThunderboltFirmware) firmware_bad = fu_thunderbolt_firmware_new();
|
g_autoptr(FuFirmware) firmware_bad = fu_intel_thunderbolt_nvm_new();
|
||||||
|
|
||||||
/* image as if read from the controller (i.e. no headers) */
|
/* image as if read from the controller (i.e. no headers) */
|
||||||
ctl_path = g_test_build_filename(G_TEST_DIST, "tests", "minimal-fw-controller.bin", NULL);
|
ctl_path = g_test_build_filename(G_TEST_DIST, "tests", "minimal-fw-controller.bin", NULL);
|
||||||
@ -1143,10 +1077,7 @@ test_image_validation(ThunderboltTest *tt, gconstpointer user_data)
|
|||||||
g_assert_nonnull(ctl_data);
|
g_assert_nonnull(ctl_data);
|
||||||
|
|
||||||
/* parse controller image */
|
/* parse controller image */
|
||||||
ret = fu_firmware_parse(FU_FIRMWARE(firmware_ctl),
|
ret = fu_firmware_parse(firmware_ctl, ctl_data, FWUPD_INSTALL_FLAG_NO_SEARCH, &error);
|
||||||
ctl_data,
|
|
||||||
FWUPD_INSTALL_FLAG_NO_SEARCH,
|
|
||||||
&error);
|
|
||||||
g_assert_true(ret);
|
g_assert_true(ret);
|
||||||
g_assert_no_error(error);
|
g_assert_no_error(error);
|
||||||
|
|
||||||
@ -1160,10 +1091,7 @@ test_image_validation(ThunderboltTest *tt, gconstpointer user_data)
|
|||||||
g_assert_nonnull(fwi_data);
|
g_assert_nonnull(fwi_data);
|
||||||
|
|
||||||
/* parse */
|
/* parse */
|
||||||
ret = fu_firmware_parse(FU_FIRMWARE(firmware_fwi),
|
ret = fu_firmware_parse(firmware_fwi, fwi_data, FWUPD_INSTALL_FLAG_NO_SEARCH, &error);
|
||||||
fwi_data,
|
|
||||||
FWUPD_INSTALL_FLAG_NO_SEARCH,
|
|
||||||
&error);
|
|
||||||
g_assert_true(ret);
|
g_assert_true(ret);
|
||||||
g_assert_no_error(error);
|
g_assert_no_error(error);
|
||||||
|
|
||||||
@ -1177,17 +1105,17 @@ test_image_validation(ThunderboltTest *tt, gconstpointer user_data)
|
|||||||
g_assert_nonnull(bad_data);
|
g_assert_nonnull(bad_data);
|
||||||
|
|
||||||
/* parse; should fail, bad image */
|
/* parse; should fail, bad image */
|
||||||
ret = fu_firmware_parse(FU_FIRMWARE(firmware_bad),
|
ret = fu_firmware_parse(firmware_bad, bad_data, FWUPD_INSTALL_FLAG_NO_SEARCH, &error);
|
||||||
bad_data,
|
|
||||||
FWUPD_INSTALL_FLAG_NO_SEARCH,
|
|
||||||
&error);
|
|
||||||
g_assert_false(ret);
|
g_assert_false(ret);
|
||||||
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_READ);
|
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_READ);
|
||||||
g_debug("expected image validation error [ctl]: %s", error->message);
|
g_debug("expected image validation error [ctl]: %s", error->message);
|
||||||
g_clear_error(&error);
|
g_clear_error(&error);
|
||||||
|
|
||||||
/* now for some testing ... this should work */
|
/* now for some testing ... this should work */
|
||||||
ret = _compare_images(firmware_ctl, FU_THUNDERBOLT_FIRMWARE(firmware_fwi), &error);
|
ret = fu_firmware_check_compatible(firmware_ctl,
|
||||||
|
firmware_fwi,
|
||||||
|
FWUPD_INSTALL_FLAG_NONE,
|
||||||
|
&error);
|
||||||
g_assert_no_error(error);
|
g_assert_no_error(error);
|
||||||
g_assert_true(ret);
|
g_assert_true(ret);
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
#include "fu-thunderbolt-common.h"
|
#include "fu-thunderbolt-common.h"
|
||||||
#include "fu-thunderbolt-controller.h"
|
#include "fu-thunderbolt-controller.h"
|
||||||
#include "fu-thunderbolt-firmware.h"
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FU_THUNDERBOLT_CONTROLLER_KIND_DEVICE,
|
FU_THUNDERBOLT_CONTROLLER_KIND_DEVICE,
|
||||||
@ -30,6 +29,10 @@ struct _FuThunderboltController {
|
|||||||
|
|
||||||
G_DEFINE_TYPE(FuThunderboltController, fu_thunderbolt_controller, FU_TYPE_THUNDERBOLT_DEVICE)
|
G_DEFINE_TYPE(FuThunderboltController, fu_thunderbolt_controller, FU_TYPE_THUNDERBOLT_DEVICE)
|
||||||
|
|
||||||
|
/* byte offsets in firmware image */
|
||||||
|
#define FU_TBT_OFFSET_NATIVE 0x7B
|
||||||
|
#define FU_TBT_CHUNK_SZ 0x40
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fu_thunderbolt_controller_check_safe_mode(FuThunderboltController *self)
|
fu_thunderbolt_controller_check_safe_mode(FuThunderboltController *self)
|
||||||
{
|
{
|
||||||
@ -108,7 +111,7 @@ fu_thunderbolt_controller_read_status_block(FuThunderboltController *self, GErro
|
|||||||
g_autoptr(GFile) nvmem = NULL;
|
g_autoptr(GFile) nvmem = NULL;
|
||||||
g_autoptr(GBytes) controller_fw = NULL;
|
g_autoptr(GBytes) controller_fw = NULL;
|
||||||
g_autoptr(GInputStream) istr = NULL;
|
g_autoptr(GInputStream) istr = NULL;
|
||||||
g_autoptr(FuThunderboltFirmware) firmware = fu_thunderbolt_firmware_new();
|
g_autoptr(FuFirmware) firmware = NULL;
|
||||||
|
|
||||||
nvmem = fu_thunderbolt_device_find_nvmem(FU_THUNDERBOLT_DEVICE(self), TRUE, error);
|
nvmem = fu_thunderbolt_device_find_nvmem(FU_THUNDERBOLT_DEVICE(self), TRUE, error);
|
||||||
if (nvmem == NULL)
|
if (nvmem == NULL)
|
||||||
@ -122,12 +125,18 @@ fu_thunderbolt_controller_read_status_block(FuThunderboltController *self, GErro
|
|||||||
controller_fw = g_input_stream_read_bytes(istr, nr_chunks * FU_TBT_CHUNK_SZ, NULL, error);
|
controller_fw = g_input_stream_read_bytes(istr, nr_chunks * FU_TBT_CHUNK_SZ, NULL, error);
|
||||||
if (controller_fw == NULL)
|
if (controller_fw == NULL)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
if (!fu_firmware_parse(FU_FIRMWARE(firmware),
|
firmware = fu_firmware_new_from_gtypes(controller_fw,
|
||||||
controller_fw,
|
|
||||||
FWUPD_INSTALL_FLAG_NO_SEARCH,
|
FWUPD_INSTALL_FLAG_NO_SEARCH,
|
||||||
error))
|
error,
|
||||||
|
FU_TYPE_INTEL_THUNDERBOLT_NVM,
|
||||||
|
FU_TYPE_FIRMWARE,
|
||||||
|
G_TYPE_INVALID);
|
||||||
|
if (firmware == NULL)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
self->is_native = fu_thunderbolt_firmware_is_native(firmware);
|
if (FU_IS_INTEL_THUNDERBOLT_FIRMWARE(firmware)) {
|
||||||
|
self->is_native =
|
||||||
|
fu_intel_thunderbolt_nvm_is_native(FU_INTEL_THUNDERBOLT_NVM(firmware));
|
||||||
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "fu-thunderbolt-device.h"
|
#include "fu-thunderbolt-device.h"
|
||||||
#include "fu-thunderbolt-firmware-update.h"
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const gchar *auth_method;
|
const gchar *auth_method;
|
||||||
@ -305,13 +304,19 @@ fu_thunderbolt_device_prepare_firmware(FuDevice *device,
|
|||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE(device);
|
FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE(device);
|
||||||
g_autoptr(FuThunderboltFirmwareUpdate) firmware = fu_thunderbolt_firmware_update_new();
|
g_autoptr(FuFirmware) firmware = NULL;
|
||||||
g_autoptr(FuThunderboltFirmware) firmware_old = fu_thunderbolt_firmware_new();
|
g_autoptr(FuFirmware) firmware_old = NULL;
|
||||||
g_autoptr(GBytes) controller_fw = NULL;
|
g_autoptr(GBytes) controller_fw = NULL;
|
||||||
g_autoptr(GFile) nvmem = NULL;
|
g_autoptr(GFile) nvmem = NULL;
|
||||||
|
|
||||||
/* parse */
|
/* parse */
|
||||||
if (!fu_firmware_parse(FU_FIRMWARE(firmware), fw, flags, error))
|
firmware = fu_firmware_new_from_gtypes(fw,
|
||||||
|
flags,
|
||||||
|
error,
|
||||||
|
FU_TYPE_INTEL_THUNDERBOLT_FIRMWARE,
|
||||||
|
FU_TYPE_FIRMWARE,
|
||||||
|
G_TYPE_INVALID);
|
||||||
|
if (firmware == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* get current NVMEM */
|
/* get current NVMEM */
|
||||||
@ -321,75 +326,19 @@ fu_thunderbolt_device_prepare_firmware(FuDevice *device,
|
|||||||
controller_fw = g_file_load_bytes(nvmem, NULL, NULL, error);
|
controller_fw = g_file_load_bytes(nvmem, NULL, NULL, error);
|
||||||
if (controller_fw == NULL)
|
if (controller_fw == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (!fu_firmware_parse(FU_FIRMWARE(firmware_old), controller_fw, flags, error))
|
firmware_old = fu_firmware_new_from_gtypes(controller_fw,
|
||||||
return NULL;
|
flags,
|
||||||
if (fu_thunderbolt_firmware_is_host(FU_THUNDERBOLT_FIRMWARE(firmware)) !=
|
|
||||||
fu_thunderbolt_firmware_is_host(firmware_old)) {
|
|
||||||
g_set_error(error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
|
||||||
"incorrect firmware mode, got %s, expected %s",
|
|
||||||
fu_thunderbolt_firmware_is_host(FU_THUNDERBOLT_FIRMWARE(firmware))
|
|
||||||
? "host"
|
|
||||||
: "device",
|
|
||||||
fu_thunderbolt_firmware_is_host(firmware_old) ? "host" : "device");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (fu_thunderbolt_firmware_get_vendor_id(FU_THUNDERBOLT_FIRMWARE(firmware)) !=
|
|
||||||
fu_thunderbolt_firmware_get_vendor_id(firmware_old)) {
|
|
||||||
g_set_error(
|
|
||||||
error,
|
error,
|
||||||
FWUPD_ERROR,
|
FU_TYPE_INTEL_THUNDERBOLT_NVM,
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
FU_TYPE_FIRMWARE,
|
||||||
"incorrect device vendor, got 0x%04x, expected 0x%04x",
|
G_TYPE_INVALID);
|
||||||
fu_thunderbolt_firmware_get_vendor_id(FU_THUNDERBOLT_FIRMWARE(firmware)),
|
if (firmware_old == NULL)
|
||||||
fu_thunderbolt_firmware_get_vendor_id(firmware_old));
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
if (!fu_firmware_check_compatible(firmware_old, firmware, flags, error))
|
||||||
if (fu_thunderbolt_firmware_get_device_id(FU_THUNDERBOLT_FIRMWARE(firmware)) !=
|
|
||||||
fu_thunderbolt_firmware_get_device_id(firmware_old)) {
|
|
||||||
g_set_error(
|
|
||||||
error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
|
||||||
"incorrect device type, got 0x%04x, expected 0x%04x",
|
|
||||||
fu_thunderbolt_firmware_get_device_id(FU_THUNDERBOLT_FIRMWARE(firmware)),
|
|
||||||
fu_thunderbolt_firmware_get_device_id(firmware_old));
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) {
|
|
||||||
if (fu_thunderbolt_firmware_get_model_id(FU_THUNDERBOLT_FIRMWARE(firmware)) !=
|
|
||||||
fu_thunderbolt_firmware_get_model_id(firmware_old)) {
|
|
||||||
g_set_error(
|
|
||||||
error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
|
||||||
"incorrect device model, got 0x%04x, expected 0x%04x",
|
|
||||||
fu_thunderbolt_firmware_get_model_id(FU_THUNDERBOLT_FIRMWARE(firmware)),
|
|
||||||
fu_thunderbolt_firmware_get_model_id(firmware_old));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/* old firmware has PD but new doesn't (we don't care about other way around) */
|
|
||||||
if (fu_thunderbolt_firmware_get_has_pd(firmware_old) &&
|
|
||||||
!fu_thunderbolt_firmware_get_has_pd(FU_THUNDERBOLT_FIRMWARE(firmware))) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
|
||||||
"incorrect PD section");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (fu_thunderbolt_firmware_get_flash_size(FU_THUNDERBOLT_FIRMWARE(firmware)) !=
|
|
||||||
fu_thunderbolt_firmware_get_flash_size(firmware_old)) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
|
||||||
"incorrect flash size");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* success */
|
/* success */
|
||||||
return FU_FIRMWARE(g_steal_pointer(&firmware));
|
return g_steal_pointer(&firmware);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2020 Mario Limonciello <mario.limonciello@dell.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1+
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <fwupdplugin.h>
|
|
||||||
|
|
||||||
#include "fu-thunderbolt-firmware-update.h"
|
|
||||||
#include "fu-thunderbolt-firmware.h"
|
|
||||||
|
|
||||||
struct _FuThunderboltFirmwareUpdate {
|
|
||||||
FuThunderboltFirmwareClass parent_instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
G_DEFINE_TYPE(FuThunderboltFirmwareUpdate,
|
|
||||||
fu_thunderbolt_firmware_update,
|
|
||||||
FU_TYPE_THUNDERBOLT_FIRMWARE)
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
fu_thunderbolt_firmware_valid_farb_pointer(guint32 pointer)
|
|
||||||
{
|
|
||||||
return pointer != 0 && pointer != 0xFFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
fu_thunderbolt_firmware_read_farb_pointer_impl(FuThunderboltFirmwareUpdate *self,
|
|
||||||
FuThunderboltSection section,
|
|
||||||
guint32 offset,
|
|
||||||
guint32 *value,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
FuThunderboltFirmware *tbt = FU_THUNDERBOLT_FIRMWARE(self);
|
|
||||||
guint32 tmp = 0;
|
|
||||||
if (!fu_thunderbolt_firmware_read_location(tbt,
|
|
||||||
section,
|
|
||||||
offset,
|
|
||||||
(guint8 *)&tmp,
|
|
||||||
3, /* 24 bits */
|
|
||||||
error)) {
|
|
||||||
g_prefix_error(error, "failed to read farb pointer: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
*value = GUINT32_FROM_LE(tmp);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* returns invalid FARB pointer on error */
|
|
||||||
static guint32
|
|
||||||
fu_thunderbolt_firmware_read_farb_pointer(FuThunderboltFirmwareUpdate *self, GError **error)
|
|
||||||
{
|
|
||||||
guint32 value;
|
|
||||||
if (!fu_thunderbolt_firmware_read_farb_pointer_impl(self,
|
|
||||||
_SECTION_DIGITAL,
|
|
||||||
0x0,
|
|
||||||
&value,
|
|
||||||
error))
|
|
||||||
return 0;
|
|
||||||
if (fu_thunderbolt_firmware_valid_farb_pointer(value))
|
|
||||||
return value;
|
|
||||||
|
|
||||||
if (!fu_thunderbolt_firmware_read_farb_pointer_impl(self,
|
|
||||||
_SECTION_DIGITAL,
|
|
||||||
0x1000,
|
|
||||||
&value,
|
|
||||||
error))
|
|
||||||
return 0;
|
|
||||||
if (!fu_thunderbolt_firmware_valid_farb_pointer(value)) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
|
||||||
"Invalid FW image file format");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
fu_thunderbolt_firmware_update_parse(FuFirmware *firmware,
|
|
||||||
GBytes *fw,
|
|
||||||
gsize offset_ignored,
|
|
||||||
FwupdInstallFlags flags,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
FuThunderboltFirmwareUpdate *self = FU_THUNDERBOLT_FIRMWARE_UPDATE(firmware);
|
|
||||||
guint32 offset = fu_thunderbolt_firmware_read_farb_pointer(self, error);
|
|
||||||
if (offset == 0)
|
|
||||||
return FALSE;
|
|
||||||
g_debug("detected digital section begins at 0x%x", offset);
|
|
||||||
fu_thunderbolt_firmware_set_digital(FU_THUNDERBOLT_FIRMWARE(firmware), offset);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
fu_thunderbolt_firmware_update_init(FuThunderboltFirmwareUpdate *self)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
fu_thunderbolt_firmware_update_class_init(FuThunderboltFirmwareUpdateClass *klass)
|
|
||||||
{
|
|
||||||
FuThunderboltFirmwareClass *klass_firmware = FU_THUNDERBOLT_FIRMWARE_CLASS(klass);
|
|
||||||
klass_firmware->parse = fu_thunderbolt_firmware_update_parse;
|
|
||||||
}
|
|
||||||
|
|
||||||
FuThunderboltFirmwareUpdate *
|
|
||||||
fu_thunderbolt_firmware_update_new(void)
|
|
||||||
{
|
|
||||||
return g_object_new(FU_TYPE_THUNDERBOLT_FIRMWARE_UPDATE, NULL);
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2020 Mario Limonciello <mario.limonciello@dell.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1+
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <fwupdplugin.h>
|
|
||||||
|
|
||||||
#include "fu-thunderbolt-firmware.h"
|
|
||||||
|
|
||||||
#define FU_TYPE_THUNDERBOLT_FIRMWARE_UPDATE (fu_thunderbolt_firmware_update_get_type())
|
|
||||||
G_DECLARE_FINAL_TYPE(FuThunderboltFirmwareUpdate,
|
|
||||||
fu_thunderbolt_firmware_update,
|
|
||||||
FU,
|
|
||||||
THUNDERBOLT_FIRMWARE_UPDATE,
|
|
||||||
FuThunderboltFirmware)
|
|
||||||
|
|
||||||
FuThunderboltFirmwareUpdate *
|
|
||||||
fu_thunderbolt_firmware_update_new(void);
|
|
@ -1,569 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 Intel Corporation.
|
|
||||||
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1+
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <fwupdplugin.h>
|
|
||||||
|
|
||||||
#include "fu-thunderbolt-firmware.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
guint32 sections[_SECTION_LAST];
|
|
||||||
FuThunderboltFamily family;
|
|
||||||
gboolean is_host;
|
|
||||||
gboolean is_native;
|
|
||||||
gboolean has_pd;
|
|
||||||
guint16 device_id;
|
|
||||||
guint16 vendor_id;
|
|
||||||
guint16 model_id;
|
|
||||||
guint gen;
|
|
||||||
guint ports;
|
|
||||||
guint8 flash_size;
|
|
||||||
} FuThunderboltFirmwarePrivate;
|
|
||||||
|
|
||||||
G_DEFINE_TYPE_WITH_PRIVATE(FuThunderboltFirmware, fu_thunderbolt_firmware, FU_TYPE_FIRMWARE)
|
|
||||||
|
|
||||||
#define GET_PRIVATE(o) (fu_thunderbolt_firmware_get_instance_private(o))
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
guint16 id;
|
|
||||||
guint gen;
|
|
||||||
FuThunderboltFamily family;
|
|
||||||
guint ports;
|
|
||||||
} FuThunderboltHwInfo;
|
|
||||||
|
|
||||||
enum {
|
|
||||||
DROM_ENTRY_MC = 0x6,
|
|
||||||
};
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
fu_thunderbolt_firmware_is_host(FuThunderboltFirmware *self)
|
|
||||||
{
|
|
||||||
FuThunderboltFirmwarePrivate *priv;
|
|
||||||
g_return_val_if_fail(FU_IS_THUNDERBOLT_FIRMWARE(self), FALSE);
|
|
||||||
priv = GET_PRIVATE(self);
|
|
||||||
return priv->is_host;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
fu_thunderbolt_firmware_is_native(FuThunderboltFirmware *self)
|
|
||||||
{
|
|
||||||
FuThunderboltFirmwarePrivate *priv;
|
|
||||||
g_return_val_if_fail(FU_IS_THUNDERBOLT_FIRMWARE(self), FALSE);
|
|
||||||
priv = GET_PRIVATE(self);
|
|
||||||
return priv->is_native;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
fu_thunderbolt_firmware_get_has_pd(FuThunderboltFirmware *self)
|
|
||||||
{
|
|
||||||
FuThunderboltFirmwarePrivate *priv;
|
|
||||||
g_return_val_if_fail(FU_IS_THUNDERBOLT_FIRMWARE(self), FALSE);
|
|
||||||
priv = GET_PRIVATE(self);
|
|
||||||
return priv->has_pd;
|
|
||||||
}
|
|
||||||
|
|
||||||
guint16
|
|
||||||
fu_thunderbolt_firmware_get_device_id(FuThunderboltFirmware *self)
|
|
||||||
{
|
|
||||||
FuThunderboltFirmwarePrivate *priv;
|
|
||||||
g_return_val_if_fail(FU_IS_THUNDERBOLT_FIRMWARE(self), 0x0);
|
|
||||||
priv = GET_PRIVATE(self);
|
|
||||||
return priv->device_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
guint16
|
|
||||||
fu_thunderbolt_firmware_get_vendor_id(FuThunderboltFirmware *self)
|
|
||||||
{
|
|
||||||
FuThunderboltFirmwarePrivate *priv;
|
|
||||||
g_return_val_if_fail(FU_IS_THUNDERBOLT_FIRMWARE(self), 0x0);
|
|
||||||
priv = GET_PRIVATE(self);
|
|
||||||
return priv->vendor_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
guint16
|
|
||||||
fu_thunderbolt_firmware_get_model_id(FuThunderboltFirmware *self)
|
|
||||||
{
|
|
||||||
FuThunderboltFirmwarePrivate *priv;
|
|
||||||
g_return_val_if_fail(FU_IS_THUNDERBOLT_FIRMWARE(self), 0x0);
|
|
||||||
priv = GET_PRIVATE(self);
|
|
||||||
return priv->model_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
guint8
|
|
||||||
fu_thunderbolt_firmware_get_flash_size(FuThunderboltFirmware *self)
|
|
||||||
{
|
|
||||||
FuThunderboltFirmwarePrivate *priv;
|
|
||||||
g_return_val_if_fail(FU_IS_THUNDERBOLT_FIRMWARE(self), 0x0);
|
|
||||||
priv = GET_PRIVATE(self);
|
|
||||||
return priv->flash_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const gchar *
|
|
||||||
fu_thunderbolt_firmware_family_to_string(FuThunderboltFamily family)
|
|
||||||
{
|
|
||||||
if (family == _FAMILY_FR)
|
|
||||||
return "Falcon Ridge";
|
|
||||||
if (family == _FAMILY_WR)
|
|
||||||
return "Win Ridge";
|
|
||||||
if (family == _FAMILY_AR)
|
|
||||||
return "Alpine Ridge";
|
|
||||||
if (family == _FAMILY_AR_C)
|
|
||||||
return "Alpine Ridge C";
|
|
||||||
if (family == _FAMILY_TR)
|
|
||||||
return "Titan Ridge";
|
|
||||||
if (family == _FAMILY_BB)
|
|
||||||
return "BB";
|
|
||||||
if (family == _FAMILY_MR)
|
|
||||||
return "Maple Ridge";
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
fu_thunderbolt_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
|
|
||||||
{
|
|
||||||
FuThunderboltFirmware *self = FU_THUNDERBOLT_FIRMWARE(firmware);
|
|
||||||
FuThunderboltFirmwarePrivate *priv = GET_PRIVATE(self);
|
|
||||||
|
|
||||||
fu_xmlb_builder_insert_kv(bn,
|
|
||||||
"family",
|
|
||||||
fu_thunderbolt_firmware_family_to_string(priv->family));
|
|
||||||
fu_xmlb_builder_insert_kb(bn, "is_host", priv->is_host);
|
|
||||||
fu_xmlb_builder_insert_kb(bn, "is_native", priv->is_native);
|
|
||||||
fu_xmlb_builder_insert_kx(bn, "device_id", priv->device_id);
|
|
||||||
fu_xmlb_builder_insert_kx(bn, "vendor_id", priv->vendor_id);
|
|
||||||
fu_xmlb_builder_insert_kx(bn, "model_id", priv->model_id);
|
|
||||||
fu_xmlb_builder_insert_kx(bn, "flash_size", priv->flash_size);
|
|
||||||
fu_xmlb_builder_insert_kx(bn, "generation", priv->gen);
|
|
||||||
fu_xmlb_builder_insert_kx(bn, "ports", priv->ports);
|
|
||||||
fu_xmlb_builder_insert_kb(bn, "has_pd", priv->has_pd);
|
|
||||||
for (guint i = 0; i < _SECTION_LAST; i++) {
|
|
||||||
g_autofree gchar *tmp = g_strdup_printf("%x", priv->sections[i]);
|
|
||||||
xb_builder_node_insert_text(bn, "section", tmp, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
fu_thunderbolt_firmware_valid_pd_pointer(guint32 pointer)
|
|
||||||
{
|
|
||||||
return pointer != 0 && pointer != 0xFFFFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
fu_thunderbolt_firmware_read_location(FuThunderboltFirmware *self,
|
|
||||||
FuThunderboltSection section,
|
|
||||||
guint32 offset,
|
|
||||||
guint8 *buf,
|
|
||||||
guint32 len,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
const guint8 *srcbuf;
|
|
||||||
gsize srcbufsz = 0;
|
|
||||||
FuThunderboltFirmwarePrivate *priv = GET_PRIVATE(self);
|
|
||||||
guint32 location_start = priv->sections[section] + offset;
|
|
||||||
g_autoptr(GBytes) fw = NULL;
|
|
||||||
|
|
||||||
/* get blob */
|
|
||||||
fw = fu_firmware_get_bytes(FU_FIRMWARE(self), error);
|
|
||||||
if (fw == NULL)
|
|
||||||
return FALSE;
|
|
||||||
srcbuf = g_bytes_get_data(fw, &srcbufsz);
|
|
||||||
|
|
||||||
if (!fu_memcpy_safe(buf,
|
|
||||||
len,
|
|
||||||
0x0, /* dst */
|
|
||||||
srcbuf,
|
|
||||||
srcbufsz,
|
|
||||||
location_start, /* src */
|
|
||||||
len,
|
|
||||||
error)) {
|
|
||||||
g_prefix_error(error, "location is outside of the given image: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
fu_thunderbolt_firmware_read_uint8(FuThunderboltFirmware *self,
|
|
||||||
FuThunderboltSection section,
|
|
||||||
guint32 offset,
|
|
||||||
guint8 *value,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
return fu_thunderbolt_firmware_read_location(self, section, offset, value, 1, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
fu_thunderbolt_firmware_read_uint16(FuThunderboltFirmware *self,
|
|
||||||
FuThunderboltSection section,
|
|
||||||
guint32 offset,
|
|
||||||
guint16 *value,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
guint16 tmp = 0;
|
|
||||||
if (!fu_thunderbolt_firmware_read_location(self,
|
|
||||||
section,
|
|
||||||
offset,
|
|
||||||
(guint8 *)&tmp,
|
|
||||||
sizeof(tmp),
|
|
||||||
error)) {
|
|
||||||
g_prefix_error(error, "failed to read uint16: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
*value = GUINT16_FROM_LE(tmp);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
fu_thunderbolt_firmware_read_uint32(FuThunderboltFirmware *self,
|
|
||||||
FuThunderboltSection section,
|
|
||||||
guint32 offset,
|
|
||||||
guint32 *value,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
guint32 tmp = 0;
|
|
||||||
if (!fu_thunderbolt_firmware_read_location(self,
|
|
||||||
section,
|
|
||||||
offset,
|
|
||||||
(guint8 *)&tmp,
|
|
||||||
sizeof(tmp),
|
|
||||||
error)) {
|
|
||||||
g_prefix_error(error, "failed to read uint32: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
*value = GUINT32_FROM_LE(tmp);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Size of ucode sections is uint16 value saved at the start of the section,
|
|
||||||
* it's in DWORDS (4-bytes) units and it doesn't include itself. We need the
|
|
||||||
* offset to the next section, so we translate it to bytes and add 2 for the
|
|
||||||
* size field itself.
|
|
||||||
*
|
|
||||||
* offset parameter must be relative to digital section
|
|
||||||
*/
|
|
||||||
static gboolean
|
|
||||||
fu_thunderbolt_firmware_read_ucode_section_len(FuThunderboltFirmware *self,
|
|
||||||
guint32 offset,
|
|
||||||
guint16 *value,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
if (!fu_thunderbolt_firmware_read_uint16(self, _SECTION_DIGITAL, offset, value, error)) {
|
|
||||||
g_prefix_error(error, "failed to read ucode section len: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
*value *= sizeof(guint32);
|
|
||||||
*value += sizeof(guint16);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* assumes sections[_SECTION_DIGITAL].offset is already set */
|
|
||||||
static gboolean
|
|
||||||
fu_thunderbolt_firmware_read_sections(FuThunderboltFirmware *self, GError **error)
|
|
||||||
{
|
|
||||||
guint32 offset;
|
|
||||||
FuThunderboltFirmwarePrivate *priv = GET_PRIVATE(self);
|
|
||||||
|
|
||||||
if (priv->gen >= 3 || priv->gen == 0) {
|
|
||||||
if (!fu_thunderbolt_firmware_read_uint32(self,
|
|
||||||
_SECTION_DIGITAL,
|
|
||||||
0x10e,
|
|
||||||
&offset,
|
|
||||||
error))
|
|
||||||
return FALSE;
|
|
||||||
priv->sections[_SECTION_DROM] = offset + priv->sections[_SECTION_DIGITAL];
|
|
||||||
|
|
||||||
if (!fu_thunderbolt_firmware_read_uint32(self,
|
|
||||||
_SECTION_DIGITAL,
|
|
||||||
0x75,
|
|
||||||
&offset,
|
|
||||||
error))
|
|
||||||
return FALSE;
|
|
||||||
priv->sections[_SECTION_ARC_PARAMS] = offset + priv->sections[_SECTION_DIGITAL];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->is_host && priv->gen > 2) {
|
|
||||||
/*
|
|
||||||
* To find the DRAM section, we have to jump from section to
|
|
||||||
* section in a chain of sections.
|
|
||||||
* available_sections location tells what sections exist at all
|
|
||||||
* (with a flag per section).
|
|
||||||
* ee_ucode_start_addr location tells the offset of the first
|
|
||||||
* section in the list relatively to the digital section start.
|
|
||||||
* After having the offset of the first section, we have a loop
|
|
||||||
* over the section list. If the section exists, we read its
|
|
||||||
* length (2 bytes at section start) and add it to current
|
|
||||||
* offset to find the start of the next section. Otherwise, we
|
|
||||||
* already have the next section offset...
|
|
||||||
*/
|
|
||||||
const guint8 DRAM_FLAG = 1 << 6;
|
|
||||||
guint16 ucode_offset;
|
|
||||||
guint8 available_sections = 0;
|
|
||||||
|
|
||||||
if (!fu_thunderbolt_firmware_read_uint8(self,
|
|
||||||
_SECTION_DIGITAL,
|
|
||||||
0x2,
|
|
||||||
&available_sections,
|
|
||||||
error)) {
|
|
||||||
g_prefix_error(error, "failed to read available sections: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (!fu_thunderbolt_firmware_read_uint16(self,
|
|
||||||
_SECTION_DIGITAL,
|
|
||||||
0x3,
|
|
||||||
&ucode_offset,
|
|
||||||
error)) {
|
|
||||||
g_prefix_error(error, "failed to read ucode offset: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
offset = ucode_offset;
|
|
||||||
if ((available_sections & DRAM_FLAG) == 0) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
|
||||||
"Can't find needed FW sections in the FW image file");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (guint8 i = 1; i < DRAM_FLAG; i <<= 1) {
|
|
||||||
if (available_sections & i) {
|
|
||||||
if (!fu_thunderbolt_firmware_read_ucode_section_len(self,
|
|
||||||
offset,
|
|
||||||
&ucode_offset,
|
|
||||||
error))
|
|
||||||
return FALSE;
|
|
||||||
offset += ucode_offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
priv->sections[_SECTION_DRAM_UCODE] = offset + priv->sections[_SECTION_DIGITAL];
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
fu_thunderbolt_firmware_missing_needed_drom(FuThunderboltFirmware *self)
|
|
||||||
{
|
|
||||||
FuThunderboltFirmwarePrivate *priv = GET_PRIVATE(self);
|
|
||||||
if (priv->sections[_SECTION_DROM] != 0)
|
|
||||||
return FALSE;
|
|
||||||
if (priv->is_host && priv->gen < 3)
|
|
||||||
return FALSE;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
fu_thunderbolt_firmware_set_digital(FuThunderboltFirmware *self, guint32 offset)
|
|
||||||
{
|
|
||||||
FuThunderboltFirmwarePrivate *priv = GET_PRIVATE(self);
|
|
||||||
priv->sections[_SECTION_DIGITAL] = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
fu_thunderbolt_firmware_parse(FuFirmware *firmware,
|
|
||||||
GBytes *fw,
|
|
||||||
gsize offset,
|
|
||||||
FwupdInstallFlags flags,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
FuThunderboltFirmware *self = FU_THUNDERBOLT_FIRMWARE(firmware);
|
|
||||||
FuThunderboltFirmwarePrivate *priv = GET_PRIVATE(self);
|
|
||||||
FuThunderboltFirmwareClass *klass_firmware = FU_THUNDERBOLT_FIRMWARE_GET_CLASS(firmware);
|
|
||||||
|
|
||||||
guint8 tmp = 0;
|
|
||||||
guint16 version = 0;
|
|
||||||
static const FuThunderboltHwInfo hw_info_arr[] = {
|
|
||||||
{0x156D, 2, _FAMILY_FR, 2}, /* FR 4C */
|
|
||||||
{0x156B, 2, _FAMILY_FR, 1}, /* FR 2C */
|
|
||||||
{0x157E, 2, _FAMILY_WR, 1}, /* WR */
|
|
||||||
{0x1578, 3, _FAMILY_AR, 2}, /* AR 4C */
|
|
||||||
{0x1576, 3, _FAMILY_AR, 1}, /* AR 2C */
|
|
||||||
{0x15C0, 3, _FAMILY_AR, 1}, /* AR LP */
|
|
||||||
{0x15D3, 3, _FAMILY_AR_C, 2}, /* AR-C 4C */
|
|
||||||
{0x15DA, 3, _FAMILY_AR_C, 1}, /* AR-C 2C */
|
|
||||||
{0x15E7, 3, _FAMILY_TR, 1}, /* TR 2C */
|
|
||||||
{0x15EA, 3, _FAMILY_TR, 2}, /* TR 4C */
|
|
||||||
{0x15EF, 3, _FAMILY_TR, 2}, /* TR 4C device */
|
|
||||||
{0x15EE, 3, _FAMILY_BB, 0}, /* BB device */
|
|
||||||
/* Maple ridge devices
|
|
||||||
* NOTE: These are expected to be flashed via UEFI capsules *not* Thunderbolt plugin
|
|
||||||
* Flashing via fwupd will require matching kernel work.
|
|
||||||
* They're left here only for parsing the binaries
|
|
||||||
*/
|
|
||||||
{0x1136, 4, _FAMILY_MR, 2},
|
|
||||||
{0x1137, 4, _FAMILY_MR, 2},
|
|
||||||
{0}};
|
|
||||||
g_autofree gchar *version_str = NULL;
|
|
||||||
|
|
||||||
/* add this straight away so we can read it without a self */
|
|
||||||
fu_firmware_set_bytes(firmware, fw);
|
|
||||||
|
|
||||||
/* subclassed */
|
|
||||||
if (klass_firmware->parse != NULL) {
|
|
||||||
if (!klass_firmware->parse(firmware, fw, offset, flags, error))
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* is native */
|
|
||||||
if (!fu_thunderbolt_firmware_read_uint8(self,
|
|
||||||
_SECTION_DIGITAL,
|
|
||||||
FU_TBT_OFFSET_NATIVE,
|
|
||||||
&tmp,
|
|
||||||
error)) {
|
|
||||||
g_prefix_error(error, "failed to read native: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
priv->is_native = tmp & 0x20;
|
|
||||||
|
|
||||||
/* we're only reading the first chunk */
|
|
||||||
if (g_bytes_get_size(fw) == 0x80)
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
/* host or device */
|
|
||||||
if (!fu_thunderbolt_firmware_read_uint8(self, _SECTION_DIGITAL, 0x10, &tmp, error)) {
|
|
||||||
g_prefix_error(error, "failed to read is-host: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
priv->is_host = tmp & (1 << 1);
|
|
||||||
|
|
||||||
/* device ID */
|
|
||||||
if (!fu_thunderbolt_firmware_read_uint16(self,
|
|
||||||
_SECTION_DIGITAL,
|
|
||||||
0x5,
|
|
||||||
&priv->device_id,
|
|
||||||
error)) {
|
|
||||||
g_prefix_error(error, "failed to read device-id: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this is best-effort */
|
|
||||||
for (guint i = 0; hw_info_arr[i].id != 0; i++) {
|
|
||||||
if (hw_info_arr[i].id == priv->device_id) {
|
|
||||||
priv->family = hw_info_arr[i].family;
|
|
||||||
priv->gen = hw_info_arr[i].gen;
|
|
||||||
priv->ports = hw_info_arr[i].ports;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (priv->ports == 0 && priv->is_host) {
|
|
||||||
g_set_error(error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_NOT_SUPPORTED,
|
|
||||||
"Unknown controller: %x",
|
|
||||||
priv->device_id);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read sections from file */
|
|
||||||
if (!fu_thunderbolt_firmware_read_sections(self, error))
|
|
||||||
return FALSE;
|
|
||||||
if (fu_thunderbolt_firmware_missing_needed_drom(self)) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_READ,
|
|
||||||
"Can't find required FW sections");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* vendor:model */
|
|
||||||
if (priv->sections[_SECTION_DROM] != 0) {
|
|
||||||
if (!fu_thunderbolt_firmware_read_uint16(self,
|
|
||||||
_SECTION_DROM,
|
|
||||||
0x10,
|
|
||||||
&priv->vendor_id,
|
|
||||||
error)) {
|
|
||||||
g_prefix_error(error, "failed to read vendor-id: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (!fu_thunderbolt_firmware_read_uint16(self,
|
|
||||||
_SECTION_DROM,
|
|
||||||
0x12,
|
|
||||||
&priv->model_id,
|
|
||||||
error)) {
|
|
||||||
g_prefix_error(error, "failed to read model-id: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* has PD */
|
|
||||||
if (priv->sections[_SECTION_ARC_PARAMS] != 0) {
|
|
||||||
guint32 pd_pointer = 0x0;
|
|
||||||
if (!fu_thunderbolt_firmware_read_uint32(self,
|
|
||||||
_SECTION_ARC_PARAMS,
|
|
||||||
0x10C,
|
|
||||||
&pd_pointer,
|
|
||||||
error)) {
|
|
||||||
g_prefix_error(error, "failed to read pd-pointer: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
priv->has_pd = fu_thunderbolt_firmware_valid_pd_pointer(pd_pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* versions */
|
|
||||||
switch (priv->family) {
|
|
||||||
case _FAMILY_TR:
|
|
||||||
if (!fu_thunderbolt_firmware_read_uint16(self,
|
|
||||||
_SECTION_DIGITAL,
|
|
||||||
0x09,
|
|
||||||
&version,
|
|
||||||
error)) {
|
|
||||||
g_prefix_error(error, "failed to read version: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
version_str = fu_version_from_uint16(version, FWUPD_VERSION_FORMAT_BCD);
|
|
||||||
fu_firmware_set_version(FU_FIRMWARE(self), version_str);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->is_host) {
|
|
||||||
switch (priv->family) {
|
|
||||||
case _FAMILY_AR:
|
|
||||||
case _FAMILY_AR_C:
|
|
||||||
case _FAMILY_TR:
|
|
||||||
/* This is used for comparison between old and new image, not a raw number
|
|
||||||
*/
|
|
||||||
if (!fu_thunderbolt_firmware_read_uint8(self,
|
|
||||||
_SECTION_DIGITAL,
|
|
||||||
0x45,
|
|
||||||
&tmp,
|
|
||||||
error)) {
|
|
||||||
g_prefix_error(error, "failed to read flash size: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
priv->flash_size = tmp & 0x07;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* success */
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
fu_thunderbolt_firmware_init(FuThunderboltFirmware *self)
|
|
||||||
{
|
|
||||||
fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
fu_thunderbolt_firmware_class_init(FuThunderboltFirmwareClass *klass)
|
|
||||||
{
|
|
||||||
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass);
|
|
||||||
klass_firmware->parse = fu_thunderbolt_firmware_parse;
|
|
||||||
klass_firmware->export = fu_thunderbolt_firmware_export;
|
|
||||||
}
|
|
||||||
|
|
||||||
FuThunderboltFirmware *
|
|
||||||
fu_thunderbolt_firmware_new(void)
|
|
||||||
{
|
|
||||||
return g_object_new(FU_TYPE_THUNDERBOLT_FIRMWARE, NULL);
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 Intel Corporation.
|
|
||||||
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1+
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <fwupdplugin.h>
|
|
||||||
|
|
||||||
#define FU_TYPE_THUNDERBOLT_FIRMWARE (fu_thunderbolt_firmware_get_type())
|
|
||||||
G_DECLARE_DERIVABLE_TYPE(FuThunderboltFirmware,
|
|
||||||
fu_thunderbolt_firmware,
|
|
||||||
FU,
|
|
||||||
THUNDERBOLT_FIRMWARE,
|
|
||||||
FuFirmware)
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
_SECTION_DIGITAL,
|
|
||||||
_SECTION_DROM,
|
|
||||||
_SECTION_ARC_PARAMS,
|
|
||||||
_SECTION_DRAM_UCODE,
|
|
||||||
_SECTION_LAST
|
|
||||||
} FuThunderboltSection;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
_FAMILY_UNKNOWN,
|
|
||||||
_FAMILY_FR,
|
|
||||||
_FAMILY_WR,
|
|
||||||
_FAMILY_AR,
|
|
||||||
_FAMILY_AR_C,
|
|
||||||
_FAMILY_TR,
|
|
||||||
_FAMILY_BB,
|
|
||||||
_FAMILY_MR,
|
|
||||||
} FuThunderboltFamily;
|
|
||||||
|
|
||||||
struct _FuThunderboltFirmwareClass {
|
|
||||||
FuFirmwareClass parent_class;
|
|
||||||
gboolean (*parse)(FuFirmware *self,
|
|
||||||
GBytes *fw,
|
|
||||||
gsize offset,
|
|
||||||
FwupdInstallFlags flags,
|
|
||||||
GError **error);
|
|
||||||
/*< private >*/
|
|
||||||
gpointer padding[28];
|
|
||||||
};
|
|
||||||
|
|
||||||
/* byte offsets in firmware image */
|
|
||||||
#define FU_TBT_OFFSET_NATIVE 0x7B
|
|
||||||
#define FU_TBT_CHUNK_SZ 0x40
|
|
||||||
|
|
||||||
FuThunderboltFirmware *
|
|
||||||
fu_thunderbolt_firmware_new(void);
|
|
||||||
gboolean
|
|
||||||
fu_thunderbolt_firmware_is_host(FuThunderboltFirmware *self);
|
|
||||||
gboolean
|
|
||||||
fu_thunderbolt_firmware_is_native(FuThunderboltFirmware *self);
|
|
||||||
gboolean
|
|
||||||
fu_thunderbolt_firmware_get_has_pd(FuThunderboltFirmware *self);
|
|
||||||
guint16
|
|
||||||
fu_thunderbolt_firmware_get_device_id(FuThunderboltFirmware *self);
|
|
||||||
guint16
|
|
||||||
fu_thunderbolt_firmware_get_vendor_id(FuThunderboltFirmware *self);
|
|
||||||
guint16
|
|
||||||
fu_thunderbolt_firmware_get_model_id(FuThunderboltFirmware *self);
|
|
||||||
guint8
|
|
||||||
fu_thunderbolt_firmware_get_flash_size(FuThunderboltFirmware *self);
|
|
||||||
void
|
|
||||||
fu_thunderbolt_firmware_set_digital(FuThunderboltFirmware *self, guint32 offset);
|
|
||||||
gboolean
|
|
||||||
fu_thunderbolt_firmware_read_location(FuThunderboltFirmware *self,
|
|
||||||
FuThunderboltSection section,
|
|
||||||
guint32 offset,
|
|
||||||
guint8 *buf,
|
|
||||||
guint32 len,
|
|
||||||
GError **error);
|
|
@ -11,8 +11,6 @@ fu_plugin_thunderbolt = shared_module('fu_plugin_thunderbolt',
|
|||||||
'fu-thunderbolt-device.c',
|
'fu-thunderbolt-device.c',
|
||||||
'fu-thunderbolt-retimer.c',
|
'fu-thunderbolt-retimer.c',
|
||||||
'fu-thunderbolt-controller.c',
|
'fu-thunderbolt-controller.c',
|
||||||
'fu-thunderbolt-firmware.c',
|
|
||||||
'fu-thunderbolt-firmware-update.c',
|
|
||||||
],
|
],
|
||||||
include_directories: [
|
include_directories: [
|
||||||
root_incdir,
|
root_incdir,
|
||||||
@ -51,8 +49,6 @@ if get_option('tests') and run_sanitize_unsafe_tests and umockdev.found() and gi
|
|||||||
'fu-thunderbolt-device.c',
|
'fu-thunderbolt-device.c',
|
||||||
'fu-thunderbolt-retimer.c',
|
'fu-thunderbolt-retimer.c',
|
||||||
'fu-thunderbolt-controller.c',
|
'fu-thunderbolt-controller.c',
|
||||||
'fu-thunderbolt-firmware.c',
|
|
||||||
'fu-thunderbolt-firmware-update.c',
|
|
||||||
],
|
],
|
||||||
include_directories: [
|
include_directories: [
|
||||||
root_incdir,
|
root_incdir,
|
||||||
|
@ -7746,6 +7746,9 @@ fu_engine_load(FuEngine *self, FuEngineLoadFlags flags, FuProgress *progress, GE
|
|||||||
fu_context_add_firmware_gtype(self->ctx, "cfu-payload", FU_TYPE_CFU_PAYLOAD);
|
fu_context_add_firmware_gtype(self->ctx, "cfu-payload", FU_TYPE_CFU_PAYLOAD);
|
||||||
fu_context_add_firmware_gtype(self->ctx, "uswid", FU_TYPE_USWID_FIRMWARE);
|
fu_context_add_firmware_gtype(self->ctx, "uswid", FU_TYPE_USWID_FIRMWARE);
|
||||||
fu_context_add_firmware_gtype(self->ctx, "coswid", FU_TYPE_COSWID_FIRMWARE);
|
fu_context_add_firmware_gtype(self->ctx, "coswid", FU_TYPE_COSWID_FIRMWARE);
|
||||||
|
fu_context_add_firmware_gtype(self->ctx,
|
||||||
|
"intel-thunderbolt",
|
||||||
|
FU_TYPE_INTEL_THUNDERBOLT_FIRMWARE);
|
||||||
|
|
||||||
/* we are emulating a different host */
|
/* we are emulating a different host */
|
||||||
if (host_emulate != NULL) {
|
if (host_emulate != NULL) {
|
||||||
|
Loading…
Reference in New Issue
Block a user