fwupd/plugins/intel-spi/fu-ifd-device.c
Richard Hughes f3c64adb6c intel-spi: Allow downloading the firmware image from the eSPI controller
Add the IFD regions as child devices and set the region access on the child
devices. Also add read-only SPI descriptor as an HSI attribute and require
FLOCKDN on Intel hardware.

Use the hidden PCI 00:1f.5 device to set the SPIBAR automatically and generate
the quirk file automatically to support more hardware.
2021-04-01 21:56:35 +01:00

156 lines
4.4 KiB
C

/*
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
*
* 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);
}