diff --git a/docs/hsi.xml b/docs/hsi.xml
index 6450c061d..dc7a3ef07 100644
--- a/docs/hsi.xml
+++ b/docs/hsi.xml
@@ -393,6 +393,22 @@
+
+ Read-only SPI Descriptor
+
+ The SPI descriptor must always be read only from all other regions.
+ Additionally on Intel architectures the FLOCKDN register must be set to
+ prevent configuration registers in the SPI BAR from being changed.
+
+
+
+
+ For HSI-1 this should be read only from all regions v1.6.0
+
+
+
+
+
TPM 2.0 Present
diff --git a/libfwupd/fwupd-security-attr-private.h b/libfwupd/fwupd-security-attr-private.h
index f72faddec..b0849ba5c 100644
--- a/libfwupd/fwupd-security-attr-private.h
+++ b/libfwupd/fwupd-security-attr-private.h
@@ -36,6 +36,7 @@ G_BEGIN_DECLS
#define FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE "org.fwupd.hsi.Spi.Bioswe" /* Since: 1.5.0 */
#define FWUPD_SECURITY_ATTR_ID_SPI_BLE "org.fwupd.hsi.Spi.Ble" /* Since: 1.5.0 */
#define FWUPD_SECURITY_ATTR_ID_SPI_SMM_BWP "org.fwupd.hsi.Spi.SmmBwp" /* Since: 1.5.0 */
+#define FWUPD_SECURITY_ATTR_ID_SPI_DESCRIPTOR "org.fwupd.hsi.Spi.Descriptor" /* Since: 1.6.0 */
#define FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_IDLE "org.fwupd.hsi.SuspendToIdle" /* Since: 1.5.0 */
#define FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_RAM "org.fwupd.hsi.SuspendToRam" /* Since: 1.5.0 */
#define FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0 "org.fwupd.hsi.Tpm.ReconstructionPcr0" /* Since: 1.5.0 */
diff --git a/plugins/intel-spi/fu-ifd-common.c b/plugins/intel-spi/fu-ifd-common.c
index 36390c47e..2a4f28cd0 100644
--- a/plugins/intel-spi/fu-ifd-common.c
+++ b/plugins/intel-spi/fu-ifd-common.c
@@ -44,6 +44,42 @@ fu_ifd_region_to_string (FuIfdRegion region)
return NULL;
}
+/**
+ * fu_ifd_region_to_name:
+ * @region: A #FuIfdRegion, e.g. %FU_IFD_REGION_BIOS
+ *
+ * Converts a #FuIfdRegion to a name the user might recognize.
+ *
+ * Return value: identifier string
+ *
+ * Since: 1.6.0
+ **/
+const gchar *
+fu_ifd_region_to_name (FuIfdRegion region)
+{
+ if (region == FU_IFD_REGION_DESC)
+ return "IFD descriptor region";
+ if (region == FU_IFD_REGION_BIOS)
+ return "BIOS";
+ if (region == FU_IFD_REGION_ME)
+ return "Intel Management Engine";
+ if (region == FU_IFD_REGION_GBE)
+ return "Gigabit Ethernet";
+ if (region == FU_IFD_REGION_PLATFORM)
+ return "Platform firmware";
+ if (region == FU_IFD_REGION_DEVEXP)
+ return "Device Firmware";
+ if (region == FU_IFD_REGION_BIOS2)
+ return "BIOS Backup";
+ if (region == FU_IFD_REGION_EC)
+ return "Embedded Controller";
+ if (region == FU_IFD_REGION_IE)
+ return "Innovation Engine";
+ if (region == FU_IFD_REGION_10GBE)
+ return "10 Gigabit Ethernet";
+ return NULL;
+}
+
/**
* fu_ifd_access_to_string:
* @access: A #FuIfdAccess, e.g. %FU_IFD_ACCESS_READ
@@ -57,6 +93,8 @@ fu_ifd_region_to_string (FuIfdRegion region)
const gchar *
fu_ifd_access_to_string (FuIfdAccess access)
{
+ if (access == FU_IFD_ACCESS_NONE)
+ return "--";
if (access == FU_IFD_ACCESS_READ)
return "ro";
if (access == FU_IFD_ACCESS_WRITE)
@@ -65,3 +103,35 @@ fu_ifd_access_to_string (FuIfdAccess access)
return "rw";
return NULL;
}
+
+FuIfdAccess
+fu_ifd_region_to_access (FuIfdRegion region, guint32 flash_master, gboolean is_skylake)
+{
+ guint8 bit_r = 0;
+ guint8 bit_w = 0;
+
+ /* new layout */
+ if (is_skylake) {
+ bit_r = (flash_master >> (region + 8)) & 0b1;
+ bit_w = (flash_master >> (region + 20)) & 0b1;
+ return (bit_r ? FU_IFD_ACCESS_READ : FU_IFD_ACCESS_NONE) |
+ (bit_w ? FU_IFD_ACCESS_WRITE : FU_IFD_ACCESS_NONE);
+ }
+
+ /* old layout */
+ if (region == FU_IFD_REGION_DESC) {
+ bit_r = 16;
+ bit_w = 24;
+ } else if (region == FU_IFD_REGION_BIOS) {
+ bit_r = 17;
+ bit_w = 25;
+ } else if (region == FU_IFD_REGION_ME) {
+ bit_r = 18;
+ bit_w = 26;
+ } else if (region == FU_IFD_REGION_GBE) {
+ bit_r = 19;
+ bit_w = 27;
+ }
+ return ((flash_master >> bit_r) & 0b1 ? FU_IFD_ACCESS_READ : FU_IFD_ACCESS_NONE) |
+ ((flash_master >> bit_w) & 0b1 ? FU_IFD_ACCESS_WRITE : FU_IFD_ACCESS_NONE);
+}
diff --git a/plugins/intel-spi/fu-ifd-common.h b/plugins/intel-spi/fu-ifd-common.h
index 93ee315dd..0cdfef69c 100644
--- a/plugins/intel-spi/fu-ifd-common.h
+++ b/plugins/intel-spi/fu-ifd-common.h
@@ -28,5 +28,13 @@ typedef enum {
FU_IFD_ACCESS_WRITE = 1 << 1,
} FuIfdAccess;
+#define FU_IFD_FREG_BASE(freg) (((freg) << 12) & 0x07FFF000)
+#define FU_IFD_FREG_LIMIT(freg) ((((freg) >> 4) & 0x07FFF000) | 0x00000FFF)
+
const gchar *fu_ifd_region_to_string (FuIfdRegion region);
+const gchar *fu_ifd_region_to_name (FuIfdRegion region);
const gchar *fu_ifd_access_to_string (FuIfdAccess access);
+
+FuIfdAccess fu_ifd_region_to_access (FuIfdRegion region,
+ guint32 flash_master,
+ gboolean is_skylake);
diff --git a/plugins/intel-spi/fu-ifd-device.c b/plugins/intel-spi/fu-ifd-device.c
new file mode 100644
index 000000000..0cba98dea
--- /dev/null
+++ b/plugins/intel-spi/fu-ifd-device.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2021 Richard Hughes
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#include "config.h"
+
+#include "fu-common.h"
+
+#include "fu-ifd-device.h"
+#include "fu-ifd-bios.h"
+
+#include "fu-intel-spi-device.h"
+
+typedef struct {
+ FuDevice parent_instance;
+ FuIfdRegion region;
+ guint32 offset;
+ FuIfdAccess access[FU_IFD_REGION_MAX];
+} FuIfdDevicePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (FuIfdDevice, fu_ifd_device, FU_TYPE_DEVICE)
+
+#define GET_PRIVATE(o) (fu_ifd_device_get_instance_private (o))
+
+static void
+fu_ifd_device_set_region (FuIfdDevice *self, FuIfdRegion region)
+{
+ FuIfdDevicePrivate *priv = GET_PRIVATE (self);
+ const gchar *region_str = fu_ifd_region_to_string (region);
+ g_autofree gchar *instance_id = NULL;
+ g_autofree gchar *region_str_up = NULL;
+
+ priv->region = region;
+ fu_device_set_name (FU_DEVICE (self), fu_ifd_region_to_name (region));
+ fu_device_set_logical_id (FU_DEVICE (self), region_str);
+ region_str_up = g_ascii_strup (region_str, -1);
+ instance_id = g_strdup_printf ("IFD\\%s", region_str_up);
+ fu_device_add_instance_id (FU_DEVICE (self), instance_id);
+}
+
+static void
+fu_ifd_device_set_freg (FuIfdDevice *self, guint32 freg)
+{
+ FuIfdDevicePrivate *priv = GET_PRIVATE (self);
+ guint32 freg_base = FU_IFD_FREG_BASE (freg);
+ guint32 freg_limt = FU_IFD_FREG_LIMIT (freg);
+ guint32 freg_size = (freg_limt - freg_base) + 1;
+
+ priv->offset = freg_base;
+ fu_device_set_firmware_size (FU_DEVICE (self), freg_size);
+}
+
+void
+fu_ifd_device_set_access (FuIfdDevice *self, FuIfdRegion region, FuIfdAccess access)
+{
+ FuIfdDevicePrivate *priv = GET_PRIVATE (self);
+ priv->access[region] = access;
+}
+
+static gboolean
+fu_ifd_device_open (FuDevice *device, GError **error)
+{
+ return fu_device_open (fu_device_get_parent (device), error);
+}
+
+static gboolean
+fu_ifd_device_close (FuDevice *device, GError **error)
+{
+ return fu_device_close (fu_device_get_parent (device), error);
+}
+
+static void
+fu_ifd_device_to_string (FuDevice *device, guint idt, GString *str)
+{
+ FuIfdDevice *self = FU_IFD_DEVICE (device);
+ FuIfdDevicePrivate *priv = GET_PRIVATE (self);
+
+ fu_common_string_append_kv (str, idt, "Region",
+ fu_ifd_region_to_string (priv->region));
+ fu_common_string_append_kx (str, idt, "Offset", priv->offset);
+
+ for (guint i = 0; i < FU_IFD_REGION_MAX; i++) {
+ g_autofree gchar *title = NULL;
+ if (priv->access[i] == FU_IFD_ACCESS_NONE)
+ continue;
+ title = g_strdup_printf ("Access[%s]", fu_ifd_region_to_string (i));
+ fu_common_string_append_kv (str, idt, title,
+ fu_ifd_access_to_string (priv->access[i]));
+ }
+
+}
+
+static GBytes *
+fu_ifd_device_dump_firmware (FuDevice *device, GError **error)
+{
+ FuIfdDevice *self = FU_IFD_DEVICE (device);
+ FuIfdDevicePrivate *priv = GET_PRIVATE (self);
+ FuDevice *parent = fu_device_get_parent (device);
+ guint64 total_size = fu_device_get_firmware_size_max (device);
+ return fu_intel_spi_device_dump (FU_INTEL_SPI_DEVICE (parent),
+ device,
+ priv->offset,
+ total_size,
+ error);
+}
+
+static FuFirmware *
+fu_ifd_device_read_firmware (FuDevice *device, GError **error)
+{
+ FuIfdDevice *self = FU_IFD_DEVICE (device);
+ FuIfdDevicePrivate *priv = GET_PRIVATE (self);
+ g_autoptr(FuFirmware) firmware = fu_ifd_image_new ();
+ g_autoptr(GBytes) blob = NULL;
+
+ blob = fu_ifd_device_dump_firmware (device, error);
+ if (blob == NULL)
+ return NULL;
+ if (priv->region == FU_IFD_REGION_BIOS)
+ firmware = fu_ifd_bios_new ();
+ else
+ firmware = fu_ifd_image_new ();
+ if (!fu_firmware_parse (firmware, blob, FWUPD_INSTALL_FLAG_NONE, error))
+ return NULL;
+ return g_steal_pointer (&firmware);
+}
+
+static void
+fu_ifd_device_init (FuIfdDevice *self)
+{
+ fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL);
+ fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE);
+ fu_device_add_icon (FU_DEVICE (self), "computer");
+}
+
+static void
+fu_ifd_device_class_init (FuIfdDeviceClass *klass)
+{
+ FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
+ klass_device->to_string = fu_ifd_device_to_string;
+ klass_device->dump_firmware = fu_ifd_device_dump_firmware;
+ klass_device->read_firmware = fu_ifd_device_read_firmware;
+ klass_device->open = fu_ifd_device_open;
+ klass_device->close = fu_ifd_device_close;
+}
+
+FuDevice *
+fu_ifd_device_new (FuIfdRegion region, guint32 freg)
+{
+ FuIfdDevice *self = FU_IFD_DEVICE (g_object_new (FU_TYPE_IFD_DEVICE, NULL));
+ fu_ifd_device_set_region (self, region);
+ fu_ifd_device_set_freg (self, freg);
+ return FU_DEVICE (self);
+}
diff --git a/plugins/intel-spi/fu-ifd-device.h b/plugins/intel-spi/fu-ifd-device.h
new file mode 100644
index 000000000..c399a704e
--- /dev/null
+++ b/plugins/intel-spi/fu-ifd-device.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 Richard Hughes
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#pragma once
+
+#include "fu-device.h"
+#include "fu-ifd-common.h"
+
+#define FU_TYPE_IFD_DEVICE (fu_ifd_device_get_type ())
+G_DECLARE_DERIVABLE_TYPE (FuIfdDevice, fu_ifd_device, FU, IFD_DEVICE, FuDevice)
+
+struct _FuIfdDeviceClass
+{
+ FuDeviceClass parent_class;
+};
+
+FuDevice *fu_ifd_device_new (FuIfdRegion region,
+ guint32 freg);
+void fu_ifd_device_set_access (FuIfdDevice *self,
+ FuIfdRegion region,
+ FuIfdAccess access);
diff --git a/plugins/intel-spi/fu-ifd-firmware.c b/plugins/intel-spi/fu-ifd-firmware.c
index 9fab3a006..6649a98c5 100644
--- a/plugins/intel-spi/fu-ifd-firmware.c
+++ b/plugins/intel-spi/fu-ifd-firmware.c
@@ -204,8 +204,6 @@ fu_ifd_firmware_parse (FuFirmware *firmware,
guint32 freg_size = (freg_limt - freg_base) + 1;
g_autoptr(FuFirmware) img = NULL;
g_autoptr(GBytes) contents = NULL;
- guint8 bit_r = 0;
- guint8 bit_w = 0;
/* invalid */
if (freg_base > freg_limt)
@@ -230,35 +228,10 @@ fu_ifd_firmware_parse (FuFirmware *firmware,
fu_firmware_add_image (firmware, img);
/* is writable by anything other than the region itself */
- if (priv->is_skylake) {
- for (FuIfdRegion r = 1; r <= 3; r++) {
- bit_r = (priv->flash_master[r] >> (i + 8)) & 0b1;
- bit_w = (priv->flash_master[r] >> (i + 20)) & 0b1;
- fu_ifd_image_set_access (FU_IFD_IMAGE (img), r,
- (bit_r ? FU_IFD_ACCESS_READ : FU_IFD_ACCESS_NONE) |
- (bit_w ? FU_IFD_ACCESS_WRITE : FU_IFD_ACCESS_NONE));
- }
- } else {
- if (i == FU_IFD_REGION_DESC) {
- bit_r = 16;
- bit_w = 24;
- } else if (i == FU_IFD_REGION_BIOS) {
- bit_r = 17;
- bit_w = 25;
- } else if (i == FU_IFD_REGION_ME) {
- bit_r = 18;
- bit_w = 26;
- } else if (i == FU_IFD_REGION_GBE) {
- bit_r = 19;
- bit_w = 27;
- }
- for (FuIfdRegion r = 1; r <= 3 && bit_r != 0; r++) {
- fu_ifd_image_set_access (FU_IFD_IMAGE (img), r,
- ((priv->flash_master[r] >> bit_r) & 0b1 ?
- FU_IFD_ACCESS_READ : FU_IFD_ACCESS_NONE) |
- ((priv->flash_master[r] >> bit_w) & 0b1 ?
- FU_IFD_ACCESS_WRITE : FU_IFD_ACCESS_NONE));
- }
+ for (FuIfdRegion r = 1; r <= 3; r++) {
+ FuIfdAccess acc;
+ acc = fu_ifd_region_to_access (i, priv->flash_master[r], priv->is_skylake);
+ fu_ifd_image_set_access (FU_IFD_IMAGE (img), r, acc);
}
}
diff --git a/plugins/intel-spi/fu-intel-spi-common.c b/plugins/intel-spi/fu-intel-spi-common.c
new file mode 100644
index 000000000..1c26910dc
--- /dev/null
+++ b/plugins/intel-spi/fu-intel-spi-common.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 Richard Hughes
+ *
+ * SPDX-License-Identifier: GPL-2+
+ */
+
+#include "config.h"
+
+#include "fu-intel-spi-common.h"
+
+FuIntelSpiKind
+fu_intel_spi_kind_from_string (const gchar *kind)
+{
+ if (g_strcmp0 (kind, "ich9") == 0)
+ return FU_INTEL_SPI_KIND_ICH9;
+ if (g_strcmp0 (kind, "pch100") == 0)
+ return FU_INTEL_SPI_KIND_PCH100;
+ if (g_strcmp0 (kind, "apl") == 0)
+ return FU_INTEL_SPI_KIND_APL;
+ if (g_strcmp0 (kind, "c620") == 0)
+ return FU_INTEL_SPI_KIND_C620;
+ if (g_strcmp0 (kind, "ich0") == 0)
+ return FU_INTEL_SPI_KIND_ICH0;
+ if (g_strcmp0 (kind, "ich2345") == 0)
+ return FU_INTEL_SPI_KIND_ICH2345;
+ if (g_strcmp0 (kind, "ich6") == 0)
+ return FU_INTEL_SPI_KIND_ICH6;
+ if (g_strcmp0 (kind, "pch300") == 0)
+ return FU_INTEL_SPI_KIND_PCH300;
+ if (g_strcmp0 (kind, "pch400") == 0)
+ return FU_INTEL_SPI_KIND_PCH400;
+ if (g_strcmp0 (kind, "poulsbo") == 0)
+ return FU_INTEL_SPI_KIND_POULSBO;
+ return FU_INTEL_SPI_KIND_UNKNOWN;
+}
+
+const gchar *
+fu_intel_spi_kind_to_string (FuIntelSpiKind kind)
+{
+ if (kind == FU_INTEL_SPI_KIND_ICH9)
+ return "ich9";
+ if (kind == FU_INTEL_SPI_KIND_PCH100)
+ return "pch100";
+ if (kind == FU_INTEL_SPI_KIND_APL)
+ return "apl";
+ if (kind == FU_INTEL_SPI_KIND_C620)
+ return "c620";
+ if (kind == FU_INTEL_SPI_KIND_ICH0)
+ return "ich0";
+ if (kind == FU_INTEL_SPI_KIND_ICH2345)
+ return "ich2345";
+ if (kind == FU_INTEL_SPI_KIND_ICH6)
+ return "ich6";
+ if (kind == FU_INTEL_SPI_KIND_PCH300)
+ return "pch300";
+ if (kind == FU_INTEL_SPI_KIND_PCH400)
+ return "pch400";
+ if (kind == FU_INTEL_SPI_KIND_POULSBO)
+ return "poulsbo";
+ return NULL;
+}
+
+guint16
+fu_mmio_read16 (gconstpointer addr, goffset offset)
+{
+ addr = (guint8 *) addr + offset;
+ return *(volatile const guint16 *) addr;
+}
+
+guint32
+fu_mmio_read32 (gconstpointer addr, goffset offset)
+{
+ addr = (guint8 *) addr + offset;
+ return *(volatile const guint32 *) addr;
+}
+
+void
+fu_mmio_write16 (gpointer addr, goffset offset, guint16 val)
+{
+ addr = (guint8 *) addr + offset;
+ *(volatile guint16 *) addr = val;
+}
+
+void
+fu_mmio_write32 (gpointer addr, goffset offset, guint32 val)
+{
+ addr = (guint8 *) addr + offset;
+ *(volatile guint32 *) addr = val;
+}
+
+guint32
+fu_mmio_read32_le (gconstpointer addr, goffset offset)
+{
+ return GUINT32_FROM_LE (fu_mmio_read32 (addr, offset));
+}
+
+void
+fu_mmio_write32_le (gpointer addr, goffset offset, guint32 val)
+{
+ fu_mmio_write32 (addr, offset, GUINT32_TO_LE (val));
+}
diff --git a/plugins/intel-spi/fu-intel-spi-common.h b/plugins/intel-spi/fu-intel-spi-common.h
new file mode 100644
index 000000000..2eee1b1ac
--- /dev/null
+++ b/plugins/intel-spi/fu-intel-spi-common.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 Richard Hughes
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#pragma once
+
+#include
+
+#define ICH9_REG_BFPR 0x00
+#define ICH9_REG_HSFS 0x04
+#define ICH9_REG_HSFC 0x06
+#define ICH9_REG_FADDR 0x08
+#define ICH9_REG_RESRVD 0x0C
+#define ICH9_REG_FDATA0 0x10
+#define ICH9_REG_FDATAN 0x14
+
+#define ICH9_REG_FRAP 0x50
+#define ICH9_REG_FREG0 0x54
+#define ICH9_REG_PR0 0x74
+#define ICH9_REG_FDOC 0xB0
+#define ICH9_REG_FDOD 0xB4
+
+#define PCH100_REG_FDOC 0xB4
+#define PCH100_REG_FDOD 0xB8
+#define PCH100_REG_FPR0 0x84
+#define PCH100_REG_GPR0 0x98
+
+#define PCH100_FADDR_FLA 0x07ffffff
+
+#define PCH100_HSFC_FCYCLE (0xf << 1)
+
+#define FDOC_FDSI (0x3F << 2)
+#define FDOC_FDSS (0x03 << 12)
+
+#define HSFS_FDONE (0x01 << 0)
+#define HSFS_FCERR (0x01 << 1)
+#define HSFS_AEL (0x01 << 2)
+#define HSFS_BERASE (0x03 << 3)
+#define HSFS_SCIP (0x01 << 5)
+#define HSFS_FDOPSS (0x01 << 13)
+#define HSFS_FDV (0x01 << 14)
+#define HSFS_FLOCKDN (0x01 << 15)
+
+#define HSFC_FGO (0x01 << 0)
+#define HSFC_FCYCLE (0x03 << 1)
+#define HSFC_FDBC (0x3f << 8)
+#define HSFC_SME (0x01 << 15)
+
+typedef enum {
+ FU_INTEL_SPI_KIND_UNKNOWN,
+ FU_INTEL_SPI_KIND_APL,
+ FU_INTEL_SPI_KIND_C620,
+ FU_INTEL_SPI_KIND_ICH0,
+ FU_INTEL_SPI_KIND_ICH2345,
+ FU_INTEL_SPI_KIND_ICH6,
+ FU_INTEL_SPI_KIND_ICH9,
+ FU_INTEL_SPI_KIND_PCH100,
+ FU_INTEL_SPI_KIND_PCH300,
+ FU_INTEL_SPI_KIND_PCH400,
+ FU_INTEL_SPI_KIND_POULSBO,
+ FU_INTEL_SPI_KIND_LAST
+} FuIntelSpiKind;
+
+FuIntelSpiKind fu_intel_spi_kind_from_string (const gchar *kind);
+const gchar *fu_intel_spi_kind_to_string (FuIntelSpiKind kind);
+
+guint16 fu_mmio_read16 (gconstpointer addr,
+ goffset offset);
+void fu_mmio_write16 (gpointer addr,
+ goffset offset,
+ guint16 val);
+
+guint32 fu_mmio_read32 (gconstpointer addr,
+ goffset offset);
+void fu_mmio_write32 (gpointer addr,
+ goffset offset,
+ guint32 val);
+
+guint32 fu_mmio_read32_le (gconstpointer addr,
+ goffset offset);
+void fu_mmio_write32_le (gpointer addr,
+ goffset offset,
+ guint32 val);
diff --git a/plugins/intel-spi/fu-intel-spi-device.c b/plugins/intel-spi/fu-intel-spi-device.c
new file mode 100644
index 000000000..d277b32e1
--- /dev/null
+++ b/plugins/intel-spi/fu-intel-spi-device.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2021 Richard Hughes
+ *
+ * SPDX-License-Identifier: GPL-2+
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include "fu-device-locker.h"
+#include "fu-plugin-vfuncs.h"
+
+#include "fu-pci-device.h"
+
+#include "fu-intel-spi-common.h"
+#include "fu-intel-spi-device.h"
+
+#include "fu-ifd-common.h"
+#include "fu-ifd-device.h"
+#include "fu-ifd-firmware.h"
+
+struct _FuIntelSpiDevice {
+ FuDevice parent_instance;
+ FuIntelSpiKind kind;
+ gchar *spibar_proxy;
+ guint32 phys_spibar;
+ gpointer spibar;
+ guint16 hsfs;
+ guint16 frap;
+ guint32 freg[4];
+ guint32 flvalsig;
+ guint32 descriptor_map0;
+ guint32 descriptor_map1;
+ guint32 descriptor_map2;
+ guint32 components_rcd;
+ guint32 illegal_jedec;
+ guint32 flpb;
+ guint32 flash_master[4];
+ guint32 protected_range[4];
+};
+
+#define FU_INTEL_SPI_PHYS_SPIBAR_SIZE 0x10000 /* bytes */
+#define FU_INTEL_SPI_READ_TIMEOUT 10 /* ms */
+
+#define PCI_BASE_ADDRESS_0 0x0010
+
+G_DEFINE_TYPE (FuIntelSpiDevice, fu_intel_spi_device, FU_TYPE_DEVICE)
+
+static void
+fu_intel_spi_device_to_string (FuDevice *device, guint idt, GString *str)
+{
+ FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE (device);
+ fu_common_string_append_kv (str, idt, "Kind",
+ fu_intel_spi_kind_to_string (self->kind));
+ fu_common_string_append_kx (str, idt, "SPIBAR", self->phys_spibar);
+ fu_common_string_append_kx (str, idt, "HSFS", self->hsfs);
+ fu_common_string_append_kx (str, idt, "FRAP", self->frap);
+ for (guint i = 0; i < 4; i++) {
+ g_autofree gchar *title = g_strdup_printf ("FREG%u", i);
+ fu_common_string_append_kx (str, idt, title, self->freg[i]);
+ }
+ for (guint i = 0; i < 4; i++) {
+ g_autofree gchar *title = g_strdup_printf ("FLMSTR%u", i);
+ fu_common_string_append_kx (str, idt, title, self->flash_master[i]);
+ }
+ fu_common_string_append_kx (str, idt, "FLVALSIG", self->flvalsig);
+ fu_common_string_append_kx (str, idt, "FLMAP0", self->descriptor_map0);
+ fu_common_string_append_kx (str, idt, "FLMAP1", self->descriptor_map1);
+ fu_common_string_append_kx (str, idt, "FLMAP2", self->descriptor_map2);
+ fu_common_string_append_kx (str, idt, "FLCOMP", self->components_rcd);
+ fu_common_string_append_kx (str, idt, "FLILL", self->illegal_jedec);
+ fu_common_string_append_kx (str, idt, "FLPB", self->flpb);
+
+ /* PRx */
+ for (guint i = 0; i < 4; i++) {
+ guint32 limit = 0;
+ guint32 base = 0;
+ FuIfdAccess access = FU_IFD_ACCESS_NONE;
+ g_autofree gchar *title = NULL;
+ g_autofree gchar *tmp = NULL;
+
+ if (self->protected_range[i] == 0x0)
+ continue;
+ if ((self->protected_range[i] >> 31) & 0b1)
+ access |= FU_IFD_ACCESS_WRITE;
+ if ((self->protected_range[i] >> 15) & 0b1)
+ access |= FU_IFD_ACCESS_READ;
+ if (access != FU_IFD_ACCESS_NONE) {
+ base = ((self->protected_range[i] >> 0) & 0x1FFF) << 12;
+ limit = (((self->protected_range[i] >> 16) & 0x1FFF) << 12) | 0xFFFF;
+ }
+ title = g_strdup_printf ("PR%u", i);
+ tmp = g_strdup_printf ("blocked %s from 0x%x to 0x%x [0x%x]",
+ fu_ifd_access_to_string (access),
+ base, limit,
+ self->protected_range[i]);
+ fu_common_string_append_kv (str, idt, title, tmp);
+ }
+}
+
+static gboolean
+fu_intel_spi_device_open (FuDevice *device, GError **error)
+{
+ FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE (device);
+ int fd;
+ g_autoptr(GInputStream) istr = NULL;
+
+ /* this will fail if the kernel is locked down */
+ fd = open ("/dev/mem", O_SYNC | O_RDWR);
+ if (fd == -1) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "failed to open /dev/mem: %s",
+ strerror (errno));
+ return FALSE;
+ }
+ istr = g_unix_input_stream_new (fd, TRUE);
+ if (istr == NULL) {
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "failed to create input stream");
+ return FALSE;
+ }
+ self->spibar = mmap (NULL, FU_INTEL_SPI_PHYS_SPIBAR_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd,
+ self->phys_spibar);
+ if (self->spibar == MAP_FAILED) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "failed to open mmap SPIBAR: %s",
+ strerror (errno));
+ return FALSE;
+ }
+
+ /* success */
+ return TRUE;
+}
+
+static gboolean
+fu_intel_spi_device_close (FuDevice *device, GError **error)
+{
+ FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE (device);
+
+ /* close */
+ if (self->spibar != NULL) {
+ if (munmap (self->spibar, FU_INTEL_SPI_PHYS_SPIBAR_SIZE) == -1) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "failed to unmap spibar: %s",
+ strerror (errno));
+ return FALSE;
+ }
+ self->spibar = NULL;
+ }
+
+ /* success */
+ return TRUE;
+}
+
+static guint32
+fu_intel_spi_device_read_reg (FuIntelSpiDevice *self, guint8 section, guint16 offset)
+{
+ guint32 control = 0;
+ control |= (((guint32) section) << 12) & FDOC_FDSS;
+ control |= (((guint32) offset) << 2) & FDOC_FDSI;
+ fu_mmio_write32_le (self->spibar, PCH100_REG_FDOC, control);
+ return fu_mmio_read32_le (self->spibar, PCH100_REG_FDOD);
+}
+
+static void
+fu_intel_spi_device_add_security_attrs (FuDevice *device, FuSecurityAttrs *attrs)
+{
+ FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE (device);
+ FuIfdAccess access_global = FU_IFD_ACCESS_NONE;
+ g_autoptr(FwupdSecurityAttr) attr = NULL;
+
+ /* create attr */
+ attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_SPI_DESCRIPTOR);
+ fwupd_security_attr_set_plugin (attr, fu_device_get_plugin (FU_DEVICE (self)));
+ fwupd_security_attr_set_level (attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL);
+ fu_security_attrs_append (attrs, attr);
+
+ /* check for read access from other regions */
+ for (guint j = FU_IFD_REGION_BIOS; j < 4; j++) {
+ FuIfdAccess access;
+ access = fu_ifd_region_to_access (FU_IFD_REGION_DESC,
+ self->flash_master[j-1], TRUE);
+ fwupd_security_attr_add_metadata (attr,
+ fu_ifd_region_to_string (j),
+ fu_ifd_access_to_string (access));
+ access_global |= access;
+ }
+
+ /* any region can write to the flash descriptor */
+ if (access_global & FU_IFD_ACCESS_WRITE) {
+ fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID);
+ return;
+ }
+
+ /* FLOCKDN is unset */
+ if ((self->hsfs >> 15 & 0b1) == 0) {
+ fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED);
+ return;
+ }
+
+ /* success */
+ fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS);
+ fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_LOCKED);
+}
+
+static gboolean
+fu_intel_spi_device_probe (FuDevice *device, GError **error)
+{
+ FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE (device);
+
+ /* verify this was set in the quirk file */
+ if (self->kind == FU_INTEL_SPI_KIND_UNKNOWN) {
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "IntelSpiKind not set");
+ return FALSE;
+ }
+
+ /* use a hidden PCI device to get the RCBA */
+ if (self->spibar_proxy != NULL) {
+ g_autoptr(FuDevice) pcidev = NULL;
+ g_autoptr(FuDeviceLocker) locker = NULL;
+
+ /* get SPIBAR from a hidden (VID set to 0xFFFF) PCI device */
+ pcidev = fu_pci_device_new (self->spibar_proxy, error);
+ if (pcidev == NULL)
+ return FALSE;
+ locker = fu_device_locker_new (pcidev, error);
+ if (locker == NULL)
+ return FALSE;
+ self->phys_spibar = fu_pci_device_read_config (FU_PCI_DEVICE (pcidev),
+ PCI_BASE_ADDRESS_0);
+ if (self->phys_spibar == 0 ||
+ self->phys_spibar == G_MAXUINT32) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "SPIBAR not valid: 0x%x",
+ self->phys_spibar);
+ return FALSE;
+ }
+ }
+
+ /* specified explicitly as a physical address */
+ if (self->phys_spibar == 0) {
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "IntelSpiBar not set");
+ return FALSE;
+ }
+
+ /* success */
+ return TRUE;
+}
+
+static gboolean
+fu_intel_spi_device_setup (FuDevice *device, GError **error)
+{
+ FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE (device);
+ guint64 total_size = 0;
+ guint8 comp1_density;
+ guint8 comp2_density;
+ guint16 reg_pr0 = fu_device_has_custom_flag (device, "ICH") ? ICH9_REG_PR0 : PCH100_REG_FPR0;
+
+ /* dump everything */
+ if (g_getenv ("FWUPD_INTEL_SPI_VERBOSE") != NULL) {
+ for (guint i = 0; i < 0xff; i += 4) {
+ guint32 tmp = fu_mmio_read32 (self->spibar, i);
+ g_print ("SPIBAR[0x%02x] = 0x%x\n", i, tmp);
+ }
+ }
+
+ /* read from descriptor */
+ self->hsfs = fu_mmio_read16 (self->spibar, ICH9_REG_HSFS);
+ self->frap = fu_mmio_read16 (self->spibar, ICH9_REG_FRAP);
+ for (guint i = FU_IFD_REGION_DESC; i < 4; i++)
+ self->freg[i] = fu_mmio_read32 (self->spibar, ICH9_REG_FREG0 + i * 4);
+ self->flvalsig = fu_intel_spi_device_read_reg (self, 0, 0);
+ self->descriptor_map0 = fu_intel_spi_device_read_reg (self, 0, 1);
+ self->descriptor_map1 = fu_intel_spi_device_read_reg (self, 0, 2);
+ self->descriptor_map2 = fu_intel_spi_device_read_reg (self, 0, 3);
+ self->components_rcd = fu_intel_spi_device_read_reg (self, 1, 0);
+ self->illegal_jedec = fu_intel_spi_device_read_reg (self, 1, 1);
+ self->flpb = fu_intel_spi_device_read_reg (self, 1, 2);
+
+ for (guint i = 0; i < 4; i++)
+ self->flash_master[i] = fu_intel_spi_device_read_reg (self, 3, i);
+ for (guint i = 0; i < 4; i++) {
+ self->protected_range[i] = fu_mmio_read32 (self->spibar,
+ reg_pr0 + i * sizeof(guint32));
+ }
+
+ /* set size */
+ comp1_density = (self->components_rcd & 0x0f) >> 0;
+ if (comp1_density != 0xf)
+ total_size += 1 << (19 + comp1_density);
+ comp2_density = (self->components_rcd & 0xf0) >> 4;
+ if (comp2_density != 0xf)
+ total_size += 1 << (19 + comp2_density);
+ fu_device_set_firmware_size (device, total_size);
+
+ /* add children */
+ for (guint i = FU_IFD_REGION_BIOS; i < 4; i++) {
+ g_autoptr(FuDevice) child = NULL;
+ if (self->freg[i] == 0x0)
+ continue;
+ child = fu_ifd_device_new (i, self->freg[i]);
+ for (guint j = 1; j < 4; j++) {
+ FuIfdAccess access;
+ access = fu_ifd_region_to_access (i, self->flash_master[j-1], TRUE);
+ fu_ifd_device_set_access (FU_IFD_DEVICE (child), j, access);
+ }
+ fu_device_add_child (device, child);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+fu_intel_spi_device_wait (FuIntelSpiDevice *self, guint timeout_ms, GError **error)
+{
+ g_usleep (1);
+ for (guint i = 0; i < timeout_ms * 100; i++) {
+ guint16 hsfs = fu_mmio_read16 (self->spibar, ICH9_REG_HSFS);
+ if (hsfs & HSFS_FDONE)
+ return TRUE;
+ if (hsfs & HSFS_FCERR) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "HSFS transaction error");
+ return FALSE;
+ }
+ g_usleep (10);
+ }
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_TIMED_OUT,
+ "HSFS timed out");
+ return FALSE;
+}
+
+static void
+fu_intel_spi_device_set_addr (FuIntelSpiDevice *self, guint32 addr)
+{
+ guint32 addr_old = fu_mmio_read32 (self->spibar, ICH9_REG_FADDR) & ~PCH100_FADDR_FLA;
+ fu_mmio_write32 (self->spibar, ICH9_REG_FADDR, (addr & PCH100_FADDR_FLA) | addr_old);
+}
+
+GBytes *
+fu_intel_spi_device_dump (FuIntelSpiDevice *self,
+ FuDevice *device,
+ guint32 offset,
+ guint32 length,
+ GError **error)
+{
+ guint8 block_len = 0x40;
+ g_autoptr(GByteArray) buf = g_byte_array_sized_new (length);
+
+ /* set FDONE, FCERR, AEL */
+ fu_device_set_status (device, FWUPD_STATUS_DEVICE_READ);
+ fu_mmio_write16 (self->spibar, ICH9_REG_HSFS,
+ fu_mmio_read16 (self->spibar, ICH9_REG_HSFS));
+ for (guint32 addr = offset; addr < offset + length; addr += block_len) {
+ guint16 hsfc;
+ guint32 buftmp32 = 0;
+
+ /* set up read */
+ fu_intel_spi_device_set_addr (self, addr);
+ hsfc = fu_mmio_read16 (self->spibar, ICH9_REG_HSFC);
+ hsfc &= ~PCH100_HSFC_FCYCLE;
+ hsfc &= ~HSFC_FDBC;
+
+ /* set byte count */
+ hsfc |= ((block_len - 1) << 8) & HSFC_FDBC;
+ hsfc |= HSFC_FGO;
+ fu_mmio_write16 (self->spibar, ICH9_REG_HSFC, hsfc);
+ if (!fu_intel_spi_device_wait (self, FU_INTEL_SPI_READ_TIMEOUT, error)) {
+ g_prefix_error (error, "failed @0x%x: ", addr);
+ return NULL;
+ }
+
+ /* copy out data */
+ for (guint i = 0; i < block_len; i++) {
+ if (i % 4 == 0)
+ buftmp32 = fu_mmio_read32 (self->spibar, ICH9_REG_FDATA0 + i);
+ fu_byte_array_append_uint8 (buf, buftmp32 >> ((i % 4) * 8));
+ }
+
+ /* progress */
+ fu_device_set_progress_full (device, addr - offset + block_len, length);
+ }
+
+ /* success */
+ return g_byte_array_free_to_bytes (g_steal_pointer (&buf));
+}
+
+static GBytes *
+fu_intel_spi_device_dump_firmware (FuDevice *device, GError **error)
+{
+ FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE (device);
+ guint64 total_size = fu_device_get_firmware_size_max (device);
+ return fu_intel_spi_device_dump (self, device,
+ 0x0, total_size,
+ error);
+}
+
+static FuFirmware *
+fu_intel_spi_device_read_firmware (FuDevice *device, GError **error)
+{
+ g_autoptr(FuFirmware) firmware = fu_ifd_firmware_new ();
+ g_autoptr(GBytes) blob = NULL;
+
+ blob = fu_intel_spi_device_dump_firmware (device, error);
+ if (blob == NULL)
+ return NULL;
+ if (!fu_firmware_parse (firmware, blob, FWUPD_INSTALL_FLAG_NONE, error))
+ return NULL;
+ return g_steal_pointer (&firmware);
+}
+
+static gboolean
+fu_intel_spi_device_set_quirk_kv (FuDevice *device,
+ const gchar *key,
+ const gchar *value,
+ GError **error)
+{
+ FuIntelSpiDevice *self = FU_INTEL_SPI_DEVICE (device);
+ if (g_strcmp0 (key, "IntelSpiBar") == 0) {
+ guint64 tmp = fu_common_strtoull (value);
+ self->phys_spibar = tmp;
+ return TRUE;
+ }
+ if (g_strcmp0 (key, "IntelSpiKind") == 0) {
+ g_autofree gchar *instance_id = NULL;
+ g_autofree gchar *kind_up = NULL;
+
+ /* validate */
+ self->kind = fu_intel_spi_kind_from_string (value);
+ if (self->kind == FU_INTEL_SPI_KIND_UNKNOWN) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "%s not supported",
+ value);
+ return FALSE;
+ }
+
+ /* get things like SPIBAR */
+ kind_up = g_ascii_strup (value, -1);
+ instance_id = g_strdup_printf ("INTEL_SPI_CHIPSET\\%s", kind_up);
+ fu_device_add_instance_id (device, instance_id);
+ return TRUE;
+ }
+ if (g_strcmp0 (key, "IntelSpiBarProxy") == 0) {
+ self->spibar_proxy = g_strdup (value);
+ return TRUE;
+ }
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "no supported");
+ return FALSE;
+}
+
+static void
+fu_intel_spi_device_init (FuIntelSpiDevice *self)
+{
+ fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL);
+ fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE);
+ fu_device_add_icon (FU_DEVICE (self), "computer");
+ fu_device_set_physical_id (FU_DEVICE (self), "intel_spi");
+}
+
+static void
+fu_intel_spi_device_class_init (FuIntelSpiDeviceClass *klass)
+{
+ FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
+ klass_device->to_string = fu_intel_spi_device_to_string;
+ klass_device->probe = fu_intel_spi_device_probe;
+ klass_device->setup = fu_intel_spi_device_setup;
+ klass_device->dump_firmware = fu_intel_spi_device_dump_firmware;
+ klass_device->read_firmware = fu_intel_spi_device_read_firmware;
+ klass_device->open = fu_intel_spi_device_open;
+ klass_device->close = fu_intel_spi_device_close;
+ klass_device->set_quirk_kv = fu_intel_spi_device_set_quirk_kv;
+ klass_device->add_security_attrs = fu_intel_spi_device_add_security_attrs;
+}
diff --git a/plugins/intel-spi/fu-intel-spi-device.h b/plugins/intel-spi/fu-intel-spi-device.h
new file mode 100644
index 000000000..736b7d27a
--- /dev/null
+++ b/plugins/intel-spi/fu-intel-spi-device.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2021 Richard Hughes
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#pragma once
+
+#include "fu-plugin.h"
+
+#define FU_TYPE_INTEL_SPI_DEVICE (fu_intel_spi_device_get_type ())
+G_DECLARE_FINAL_TYPE (FuIntelSpiDevice, fu_intel_spi_device, FU, INTEL_SPI_DEVICE, FuDevice)
+
+GBytes *fu_intel_spi_device_dump (FuIntelSpiDevice *self,
+ FuDevice *device,
+ guint32 offset,
+ guint32 length,
+ GError **error);
diff --git a/plugins/intel-spi/fu-pci-device.c b/plugins/intel-spi/fu-pci-device.c
new file mode 100644
index 000000000..6d38c0f37
--- /dev/null
+++ b/plugins/intel-spi/fu-pci-device.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2021 Richard Hughes
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#include "fu-common.h"
+
+#include "fu-pci-device.h"
+
+typedef struct {
+ FuDevice parent_instance;
+ guint32 bus;
+ guint32 dev;
+ guint32 fun;
+} FuPciDevicePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (FuPciDevice, fu_pci_device, FU_TYPE_DEVICE)
+
+#define PCI_CONFIG_ADDRESS 0x0CF8
+#define PCI_CONFIG_DATA 0x0CFC
+
+#define GET_PRIVATE(o) (fu_pci_device_get_instance_private (o))
+
+guint32
+fu_pci_device_read_config (FuPciDevice *self, guint32 addr)
+{
+ FuPciDevicePrivate *priv = GET_PRIVATE (self);
+ guint32 val = 0x80000000;
+
+ /* we have to do this horrible port access as the PCI device is not
+ * visible to even the kernel as the vendor ID is set as 0xFFFF */
+ val |= priv->bus << 16;
+ val |= priv->dev << 11;
+ val |= priv->fun << 8;
+ val |= addr;
+
+ /* we do this multiple times until we get the same result for every
+ * request as the port is shared between the kernel and all processes */
+ for (guint cnt = 0; cnt < 0xff; cnt++) {
+ guint32 results[0x20] = { 0x0 };
+ gboolean consistent = TRUE;
+
+ /* fill up array */
+ for (guint i = 0; i < G_N_ELEMENTS(results); i++) {
+ outl (val, PCI_CONFIG_ADDRESS);
+ results[i] = inl (PCI_CONFIG_DATA);
+ }
+
+ /* check they are all the same */
+ for (guint i = 0; i < G_N_ELEMENTS(results); i++) {
+ if (results[0] != results[i]) {
+ consistent = FALSE;
+ break;
+ }
+ }
+
+ /* success */
+ if (consistent)
+ return results[0];
+ }
+
+ /* failed */
+ return G_MAXUINT32;
+}
+
+static gboolean
+fu_pci_device_open (FuDevice *device, GError **error)
+{
+ /* this will fail if userspace is locked down */
+ if (ioperm (PCI_CONFIG_ADDRESS, 64, 1) < 0) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "failed to open port: %s",
+ strerror (errno));
+ return FALSE;
+ }
+
+ /* success */
+ return TRUE;
+}
+
+static gboolean
+fu_pci_device_close (FuDevice *device, GError **error)
+{
+ /* this might fail if userspace is locked down */
+ if (ioperm (PCI_CONFIG_ADDRESS, 64, 0) < 0) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "failed to open port: %s",
+ strerror (errno));
+ return FALSE;
+ }
+
+ /* success */
+ return TRUE;
+}
+
+static void
+fu_pci_device_to_string (FuDevice *device, guint idt, GString *str)
+{
+ FuPciDevice *self = FU_PCI_DEVICE (device);
+ FuPciDevicePrivate *priv = GET_PRIVATE (self);
+ fu_common_string_append_kx (str, idt, "Bus", priv->bus);
+ fu_common_string_append_kx (str, idt, "Dev", priv->dev);
+ fu_common_string_append_kx (str, idt, "Fun", priv->fun);
+}
+
+static gboolean
+fu_pci_device_parse_bdf (FuPciDevice *self, const gchar *bdf, GError **error)
+{
+ FuPciDevicePrivate *priv = GET_PRIVATE (self);
+ guint64 bus_tmp;
+ guint64 dev_tmp;
+ guint64 fun_tmp;
+ g_auto(GStrv) split = g_strsplit_set (bdf, ":.", 0);
+
+ /* parse the BDF */
+ if (g_strv_length (split) != 3) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "%s invalid, expected '00:1f.5'",
+ bdf);
+ return FALSE;
+ }
+ bus_tmp = g_ascii_strtoull (split[0], NULL, 16);
+ dev_tmp = g_ascii_strtoull (split[1], NULL, 16);
+ fun_tmp = g_ascii_strtoull (split[2], NULL, 16);
+ if (bus_tmp > 0xff || dev_tmp > 0x1f || fun_tmp > 0x7) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "%s invalid, expected '00:1f.5'",
+ bdf);
+ return FALSE;
+ }
+
+ /* success */
+ priv->bus = bus_tmp;
+ priv->dev = dev_tmp;
+ priv->fun = fun_tmp;
+ return TRUE;
+}
+
+static void
+fu_pci_device_init (FuPciDevice *self)
+{
+ fu_device_set_physical_id (FU_DEVICE (self), "PCI");
+}
+
+static void
+fu_pci_device_class_init (FuPciDeviceClass *klass)
+{
+ FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
+ klass_device->to_string = fu_pci_device_to_string;
+ klass_device->open = fu_pci_device_open;
+ klass_device->close = fu_pci_device_close;
+}
+
+FuDevice *
+fu_pci_device_new (const gchar *bdf, GError **error)
+{
+ g_autoptr(FuPciDevice) self = FU_PCI_DEVICE (g_object_new (FU_TYPE_PCI_DEVICE, NULL));
+ if (!fu_pci_device_parse_bdf (self, bdf, error))
+ return NULL;
+ return FU_DEVICE (g_steal_pointer (&self));
+}
diff --git a/plugins/intel-spi/fu-pci-device.h b/plugins/intel-spi/fu-pci-device.h
new file mode 100644
index 000000000..5fb2d6cb3
--- /dev/null
+++ b/plugins/intel-spi/fu-pci-device.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 Richard Hughes
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#pragma once
+
+#include "fu-device.h"
+
+#define FU_TYPE_PCI_DEVICE (fu_pci_device_get_type ())
+G_DECLARE_DERIVABLE_TYPE (FuPciDevice, fu_pci_device, FU, PCI_DEVICE, FuDevice)
+
+struct _FuPciDeviceClass
+{
+ FuDeviceClass parent_class;
+};
+
+FuDevice *fu_pci_device_new (const gchar *bdf,
+ GError **error);
+guint32 fu_pci_device_read_config (FuPciDevice *self,
+ guint32 addr);
diff --git a/plugins/intel-spi/fu-plugin-intel-spi.c b/plugins/intel-spi/fu-plugin-intel-spi.c
index 576633bc1..c1d611fef 100644
--- a/plugins/intel-spi/fu-plugin-intel-spi.c
+++ b/plugins/intel-spi/fu-plugin-intel-spi.c
@@ -8,6 +8,8 @@
#include "fu-plugin-vfuncs.h"
+#include "fu-intel-spi-device.h"
+
#include "fu-ifd-firmware.h"
#include "fu-ifd-bios.h"
@@ -19,6 +21,7 @@
void
fu_plugin_init (FuPlugin *plugin)
{
+ FuContext *ctx = fu_plugin_get_context (plugin);
fu_plugin_set_build_hash (plugin, FU_BUILD_HASH);
fu_plugin_add_firmware_gtype (plugin, NULL, FU_TYPE_EFI_FIRMWARE_FILE);
fu_plugin_add_firmware_gtype (plugin, NULL, FU_TYPE_EFI_FIRMWARE_FILESYSTEM);
@@ -26,4 +29,23 @@ fu_plugin_init (FuPlugin *plugin)
fu_plugin_add_firmware_gtype (plugin, NULL, FU_TYPE_EFI_FIRMWARE_VOLUME);
fu_plugin_add_firmware_gtype (plugin, NULL, FU_TYPE_IFD_BIOS);
fu_plugin_add_firmware_gtype (plugin, NULL, FU_TYPE_IFD_FIRMWARE);
+ fu_context_add_udev_subsystem (ctx, "pci");
+ fu_context_add_quirk_key (ctx, "IntelSpiKind");
+ fu_context_add_quirk_key (ctx, "IntelSpiBar");
+ fu_context_add_quirk_key (ctx, "IntelSpiBarProxy");
+ fu_context_add_quirk_key (ctx, "IntelSpiBiosCntl");
+ fu_plugin_set_device_gtype (plugin, FU_TYPE_INTEL_SPI_DEVICE);
+}
+
+gboolean
+fu_plugin_startup (FuPlugin *plugin, GError **error)
+{
+ if (fu_common_kernel_locked_down ()) {
+ g_set_error_literal (error,
+ FWUPD_ERROR,
+ FWUPD_ERROR_NOT_SUPPORTED,
+ "not supported when kernel locked down");
+ return FALSE;
+ }
+ return TRUE;
}
diff --git a/plugins/intel-spi/generate-quirk.py b/plugins/intel-spi/generate-quirk.py
new file mode 100755
index 000000000..78935beaa
--- /dev/null
+++ b/plugins/intel-spi/generate-quirk.py
@@ -0,0 +1,76 @@
+#!/usr/bin/python3
+# pylint: disable=invalid-name,missing-docstring
+#
+# Copyright (C) 2021 Richard Hughes
+#
+# SPDX-License-Identifier: LGPL-2.1+
+
+import sys
+
+
+class Chipset:
+ def __init__(self, spibar=None, bios_cntl=0x0, spibar_proxy=None, flags=None):
+
+ self.bios_cntl = bios_cntl
+ self.spibar_proxy = spibar_proxy
+ self.spibar = spibar
+ self.flags = flags
+
+
+if __name__ == "__main__":
+
+ if len(sys.argv) != 2:
+ print("required: /path/to/chipset_enable.c")
+ sys.exit(1)
+
+ chipsets = {
+ "apl": Chipset(flags="PCH", bios_cntl=0xDC, spibar_proxy="00:0d.2"),
+ "c620": Chipset(flags="PCH", bios_cntl=0xDC, spibar_proxy="00:1f.5"),
+ "ich0": Chipset(flags="ICH", bios_cntl=0x4E),
+ "ich2345": Chipset(flags="ICH", bios_cntl=0x4E),
+ "ich6": Chipset(flags="ICH", bios_cntl=0xDC),
+ "pch100": Chipset(flags="PCH", bios_cntl=0xDC, spibar_proxy="00:1f.5"),
+ "pch300": Chipset(flags="PCH", bios_cntl=0xDC, spibar_proxy="00:1f.5"),
+ "pch400": Chipset(flags="PCH", bios_cntl=0xDC, spibar_proxy="00:1f.5"),
+ "poulsbo": Chipset(flags="ICH", bios_cntl=0xD8),
+ }
+
+ devices = {"PCI\VEN_8086&DEV_A0A4": "pch100"}
+
+ with open("intel-spi.quirk", "w") as out_f:
+ with open(sys.argv[1], "r") as in_f:
+ lines = in_f.read().split("\n")
+ for line in lines:
+ if line.find("0x8086") == -1:
+ continue
+ if line.find("Sample") != -1:
+ continue
+ for char in ["}", "{", '"', " ", "\t"]:
+ line = line.replace(char, "")
+ ven, dev, _, _, _, _, kind, _ = line.split(",")
+
+ if kind.startswith("enable_flash_"):
+ kind = kind[13:]
+ if kind not in chipsets:
+ print("ignoring {}...".format(kind))
+ continue
+
+ devices["PCI\VEN_{}&DEV_{}".format(ven[2:], dev[2:].upper())] = kind
+
+ for device in devices:
+ kind = devices[device]
+ out_f.write("[{}]\n".format(device))
+ out_f.write("Plugin = intel_spi\n")
+ out_f.write("IntelSpiKind = {}\n\n".format(kind))
+
+ for kind in sorted(chipsets):
+ cs = chipsets[kind]
+ out_f.write("\n[INTEL_SPI_CHIPSET\\{}]\n".format(kind.upper()))
+ if cs.spibar:
+ out_f.write("IntelSpiBar = 0x{:x}\n".format(cs.spibar))
+ if cs.spibar_proxy:
+ out_f.write("IntelSpiBarProxy = {}\n".format(cs.spibar_proxy))
+ if cs.bios_cntl:
+ out_f.write("IntelSpiBiosCntl = 0x{:X}\n".format(cs.bios_cntl))
+ if cs.flags:
+ out_f.write("Flags = {}\n".format(cs.flags))
diff --git a/plugins/intel-spi/intel-spi.quirk b/plugins/intel-spi/intel-spi.quirk
new file mode 100644
index 000000000..2abb73551
--- /dev/null
+++ b/plugins/intel-spi/intel-spi.quirk
@@ -0,0 +1,401 @@
+[PCI\VEN_8086&DEV_A0A4]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_2410]
+Plugin = intel_spi
+IntelSpiKind = ich0
+
+[PCI\VEN_8086&DEV_2420]
+Plugin = intel_spi
+IntelSpiKind = ich0
+
+[PCI\VEN_8086&DEV_2440]
+Plugin = intel_spi
+IntelSpiKind = ich2345
+
+[PCI\VEN_8086&DEV_244C]
+Plugin = intel_spi
+IntelSpiKind = ich2345
+
+[PCI\VEN_8086&DEV_2450]
+Plugin = intel_spi
+IntelSpiKind = ich2345
+
+[PCI\VEN_8086&DEV_2480]
+Plugin = intel_spi
+IntelSpiKind = ich2345
+
+[PCI\VEN_8086&DEV_248C]
+Plugin = intel_spi
+IntelSpiKind = ich2345
+
+[PCI\VEN_8086&DEV_24C0]
+Plugin = intel_spi
+IntelSpiKind = ich2345
+
+[PCI\VEN_8086&DEV_24CC]
+Plugin = intel_spi
+IntelSpiKind = ich2345
+
+[PCI\VEN_8086&DEV_24D0]
+Plugin = intel_spi
+IntelSpiKind = ich2345
+
+[PCI\VEN_8086&DEV_25A1]
+Plugin = intel_spi
+IntelSpiKind = ich2345
+
+[PCI\VEN_8086&DEV_2640]
+Plugin = intel_spi
+IntelSpiKind = ich6
+
+[PCI\VEN_8086&DEV_2641]
+Plugin = intel_spi
+IntelSpiKind = ich6
+
+[PCI\VEN_8086&DEV_2642]
+Plugin = intel_spi
+IntelSpiKind = ich6
+
+[PCI\VEN_8086&DEV_2670]
+Plugin = intel_spi
+IntelSpiKind = ich6
+
+[PCI\VEN_8086&DEV_8119]
+Plugin = intel_spi
+IntelSpiKind = poulsbo
+
+[PCI\VEN_8086&DEV_9D43]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_9D46]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_9D48]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_9D4B]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_9D4E]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_9D50]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_9D53]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_9D56]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_9D58]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_9D84]
+Plugin = intel_spi
+IntelSpiKind = pch300
+
+[PCI\VEN_8086&DEV_0284]
+Plugin = intel_spi
+IntelSpiKind = pch400
+
+[PCI\VEN_8086&DEV_0285]
+Plugin = intel_spi
+IntelSpiKind = pch400
+
+[PCI\VEN_8086&DEV_A143]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A144]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A145]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A146]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A147]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A148]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A149]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A14A]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A14D]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A14E]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A150]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A151]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A152]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A153]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A154]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A155]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A1A4]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A1C0]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A1C1]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A1C2]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A1C3]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A1C4]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A1C5]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A1C6]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A1C7]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A1C8]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A1C9]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A1CA]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A1CB]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A1CC]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A1CD]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A240]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A241]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A242]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A243]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A244]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A245]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A246]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A247]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A248]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A249]
+Plugin = intel_spi
+IntelSpiKind = c620
+
+[PCI\VEN_8086&DEV_A2C4]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A2C5]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A2C6]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A2C7]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A2C8]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A2C9]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_A2D2]
+Plugin = intel_spi
+IntelSpiKind = pch100
+
+[PCI\VEN_8086&DEV_5AE8]
+Plugin = intel_spi
+IntelSpiKind = apl
+
+[PCI\VEN_8086&DEV_5AF0]
+Plugin = intel_spi
+IntelSpiKind = apl
+
+[PCI\VEN_8086&DEV_A303]
+Plugin = intel_spi
+IntelSpiKind = pch300
+
+[PCI\VEN_8086&DEV_A304]
+Plugin = intel_spi
+IntelSpiKind = pch300
+
+[PCI\VEN_8086&DEV_A305]
+Plugin = intel_spi
+IntelSpiKind = pch300
+
+[PCI\VEN_8086&DEV_A306]
+Plugin = intel_spi
+IntelSpiKind = pch300
+
+[PCI\VEN_8086&DEV_A308]
+Plugin = intel_spi
+IntelSpiKind = pch300
+
+[PCI\VEN_8086&DEV_A309]
+Plugin = intel_spi
+IntelSpiKind = pch300
+
+[PCI\VEN_8086&DEV_A30A]
+Plugin = intel_spi
+IntelSpiKind = pch300
+
+[PCI\VEN_8086&DEV_A30C]
+Plugin = intel_spi
+IntelSpiKind = pch300
+
+[PCI\VEN_8086&DEV_A30D]
+Plugin = intel_spi
+IntelSpiKind = pch300
+
+[PCI\VEN_8086&DEV_A30E]
+Plugin = intel_spi
+IntelSpiKind = pch300
+
+[PCI\VEN_8086&DEV_3482]
+Plugin = intel_spi
+IntelSpiKind = pch300
+
+
+[INTEL_SPI_CHIPSET\APL]
+IntelSpiBarProxy = 00:0d.2
+IntelSpiBiosCntl = 0xDC
+Flags = PCH
+
+[INTEL_SPI_CHIPSET\C620]
+IntelSpiBarProxy = 00:1f.5
+IntelSpiBiosCntl = 0xDC
+Flags = PCH
+
+[INTEL_SPI_CHIPSET\ICH0]
+IntelSpiBiosCntl = 0x4E
+Flags = ICH
+
+[INTEL_SPI_CHIPSET\ICH2345]
+IntelSpiBiosCntl = 0x4E
+Flags = ICH
+
+[INTEL_SPI_CHIPSET\ICH6]
+IntelSpiBiosCntl = 0xDC
+Flags = ICH
+
+[INTEL_SPI_CHIPSET\PCH100]
+IntelSpiBarProxy = 00:1f.5
+IntelSpiBiosCntl = 0xDC
+Flags = PCH
+
+[INTEL_SPI_CHIPSET\PCH300]
+IntelSpiBarProxy = 00:1f.5
+IntelSpiBiosCntl = 0xDC
+Flags = PCH
+
+[INTEL_SPI_CHIPSET\PCH400]
+IntelSpiBarProxy = 00:1f.5
+IntelSpiBiosCntl = 0xDC
+Flags = PCH
+
+[INTEL_SPI_CHIPSET\POULSBO]
+IntelSpiBiosCntl = 0xD8
+Flags = ICH
diff --git a/plugins/intel-spi/meson.build b/plugins/intel-spi/meson.build
index 118374a04..f36160659 100644
--- a/plugins/intel-spi/meson.build
+++ b/plugins/intel-spi/meson.build
@@ -1,6 +1,10 @@
if get_option('plugin_intel_spi')
cargs = ['-DG_LOG_DOMAIN="FuPluginIntelSpi"']
+install_data(['intel-spi.quirk'],
+ install_dir: join_paths(datadir, 'fwupd', 'quirks.d')
+)
+
shared_module('fu_plugin_intel_spi',
fu_hash,
sources : [
@@ -12,8 +16,12 @@ shared_module('fu_plugin_intel_spi',
'fu-efi-firmware-volume.c', # fuzzing
'fu-ifd-bios.c', # fuzzing
'fu-ifd-common.c', # fuzzing
+ 'fu-ifd-device.c',
'fu-ifd-firmware.c', # fuzzing
'fu-ifd-image.c', # fuzzing
+ 'fu-intel-spi-common.c',
+ 'fu-intel-spi-device.c',
+ 'fu-pci-device.c',
'fu-plugin-intel-spi.c',
],
include_directories : [
diff --git a/src/fu-security-attr.c b/src/fu-security-attr.c
index 6be49519b..bb293e353 100644
--- a/src/fu-security-attr.c
+++ b/src/fu-security-attr.c
@@ -27,6 +27,10 @@ fu_security_attr_get_name (FwupdSecurityAttr *attr)
/* TRANSLATORS: Title: SPI refers to the flash chip in the computer */
return g_strdup (_("SPI BIOS region"));
}
+ if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_SPI_DESCRIPTOR) == 0) {
+ /* TRANSLATORS: Title: SPI refers to the flash chip in the computer */
+ return g_strdup (_("SPI BIOS Descriptor"));
+ }
if (g_strcmp0 (appstream_id, FWUPD_SECURITY_ATTR_ID_ACPI_DMAR) == 0) {
/* TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack */
return g_strdup (_("Pre-boot DMA protection"));