fwupd/plugins/intel-spi/fu-ifd-device.c
2021-08-24 11:18:40 -05:00

140 lines
4.0 KiB
C

/*
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupdplugin.h>
#include "fu-ifd-device.h"
#include "fu-intel-spi-device.h"
typedef struct {
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 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_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_USE_PARENT_FOR_OPEN);
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;
}
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);
}