diff --git a/libfwupdplugin/fu-acpi-table.c b/libfwupdplugin/fu-acpi-table.c new file mode 100644 index 000000000..ac2233b82 --- /dev/null +++ b/libfwupdplugin/fu-acpi-table.c @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2023 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-acpi-table.h" +#include "fu-mem.h" +#include "fu-string.h" +#include "fu-sum.h" + +/** + * FuAcpiTable: + * + * An generic ACPI table. + * + * See also: [class@FuFirmware] + */ + +typedef struct { + guint8 revision; + gchar *oem_id; + gchar *oem_table_id; + guint32 oem_revision; +} FuAcpiTablePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuAcpiTable, fu_acpi_table, FU_TYPE_FIRMWARE) + +#define GET_PRIVATE(o) (fu_acpi_table_get_instance_private(o)) + +/* almost all ACPI tables have this structure */ +typedef struct __attribute__((packed)) { + gchar signature[4]; + guint32 length; + guint8 revision; + guint8 checksum; + gchar oem_id[6]; + gchar oem_table_id[8]; + guint32 oem_revision; + gchar asl_compiler_id[4]; + guint32 asl_compiler_revision; +} FuAcpiTableHdr; + +static void +fu_acpi_table_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuAcpiTable *self = FU_ACPI_TABLE(firmware); + FuAcpiTablePrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kx(bn, "revision", priv->revision); + fu_xmlb_builder_insert_kv(bn, "oem_id", priv->oem_id); + fu_xmlb_builder_insert_kv(bn, "oem_table_id", priv->oem_table_id); + fu_xmlb_builder_insert_kx(bn, "oem_revision", priv->oem_revision); +} + +/** + * fu_acpi_table_get_revision: + * @self: a #FuAcpiTable + * + * Gets the revision of the table. + * + * Returns: integer, default 0x0 + * + * Since: 1.8.11 + **/ +guint8 +fu_acpi_table_get_revision(FuAcpiTable *self) +{ + FuAcpiTablePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_ACPI_TABLE(self), G_MAXUINT8); + return priv->revision; +} + +/** + * fu_acpi_table_get_oem_id: + * @self: a #FuAcpiTable + * + * Gets an optional OEM ID. + * + * Returns: a string, or %NULL + * + * Since: 1.8.11 + **/ +const gchar * +fu_acpi_table_get_oem_id(FuAcpiTable *self) +{ + FuAcpiTablePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_ACPI_TABLE(self), NULL); + return priv->oem_id; +} + +/** + * fu_acpi_table_get_oem_table_id: + * @self: a #FuAcpiTable + * + * Gets an optional OEM table ID. + * + * Returns: a string, or %NULL + * + * Since: 1.8.11 + **/ +const gchar * +fu_acpi_table_get_oem_table_id(FuAcpiTable *self) +{ + FuAcpiTablePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_ACPI_TABLE(self), NULL); + return priv->oem_table_id; +} + +/** + * fu_acpi_table_get_oem_revision: + * @self: a #FuAcpiTable + * + * Gets the OEM revision. + * + * Returns: integer, default 0x0 + * + * Since: 1.8.11 + **/ +guint32 +fu_acpi_table_get_oem_revision(FuAcpiTable *self) +{ + FuAcpiTablePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_ACPI_TABLE(self), G_MAXUINT32); + return priv->oem_revision; +} + +static gboolean +fu_acpi_table_parse(FuFirmware *firmware, + GBytes *fw, + gsize offset, + FwupdInstallFlags flags, + GError **error) +{ + FuAcpiTable *self = FU_ACPI_TABLE(firmware); + FuAcpiTablePrivate *priv = GET_PRIVATE(self); + gchar oem_id[6] = {'\0'}; + gchar oem_table_id[8] = {'\0'}; + gchar signature[4] = {'\0'}; + gsize bufsz = 0; + guint32 length = 0; + guint8 checksum = 0; + const guint8 *buf = g_bytes_get_data(fw, &bufsz); + g_autofree gchar *id = NULL; + + /* signature */ + if (!fu_memcpy_safe((guint8 *)signature, + sizeof(signature), + 0x0, /* dst */ + buf, + bufsz, + offset + G_STRUCT_OFFSET(FuAcpiTableHdr, signature), /* src */ + sizeof(signature), + error)) + return FALSE; + id = fu_strsafe(signature, sizeof(signature)); + fu_firmware_set_id(FU_FIRMWARE(self), id); + + /* length */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuAcpiTableHdr, length), + &length, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (length > bufsz || length < sizeof(FuAcpiTableHdr)) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "table length not valid: got 0x%x but expected 0x%x", + (guint)bufsz, + (guint)length); + return FALSE; + } + fu_firmware_set_size(firmware, length); + + /* revision */ + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuAcpiTableHdr, revision), + &priv->revision, + error)) + return FALSE; + + /* checksum */ + if (!fu_memread_uint8_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuAcpiTableHdr, checksum), + &checksum, + error)) + return FALSE; + if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + guint8 checksum_actual = fu_sum8(buf, length); + if (checksum_actual != 0x0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "CRC failed, expected %02x, got %02x", + (guint)checksum - checksum_actual, + checksum); + return FALSE; + } + } + + /* OEM ID */ + if (!fu_memcpy_safe((guint8 *)oem_id, + sizeof(oem_id), + 0x0, /* dst */ + buf, + bufsz, + offset + G_STRUCT_OFFSET(FuAcpiTableHdr, oem_id), /* src */ + sizeof(oem_id), + error)) + return FALSE; + priv->oem_id = fu_strsafe(oem_id, sizeof(oem_id)); + + /* OEM table ID */ + if (!fu_memcpy_safe((guint8 *)oem_table_id, + sizeof(oem_table_id), + 0x0, /* dst */ + buf, + bufsz, + offset + G_STRUCT_OFFSET(FuAcpiTableHdr, oem_table_id), /* src */ + sizeof(oem_table_id), + error)) + return FALSE; + priv->oem_table_id = fu_strsafe(oem_table_id, sizeof(oem_table_id)); + + /* OEM revision */ + if (!fu_memread_uint32_safe(buf, + bufsz, + offset + G_STRUCT_OFFSET(FuAcpiTableHdr, oem_revision), + &priv->oem_revision, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +static void +fu_acpi_table_init(FuAcpiTable *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); +} + +static void +fu_acpi_table_finalize(GObject *object) +{ + FuAcpiTable *self = FU_ACPI_TABLE(object); + FuAcpiTablePrivate *priv = GET_PRIVATE(self); + g_free(priv->oem_table_id); + g_free(priv->oem_id); + G_OBJECT_CLASS(fu_acpi_table_parent_class)->finalize(object); +} + +static void +fu_acpi_table_class_init(FuAcpiTableClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_acpi_table_finalize; + klass_firmware->parse = fu_acpi_table_parse; + klass_firmware->export = fu_acpi_table_export; +} + +/** + * fu_acpi_table_new: + * + * Creates a new #FuFirmware + * + * Since: 1.8.11 + **/ +FuFirmware * +fu_acpi_table_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_ACPI_TABLE, NULL)); +} diff --git a/libfwupdplugin/fu-acpi-table.h b/libfwupdplugin/fu-acpi-table.h new file mode 100644 index 000000000..74bed33f0 --- /dev/null +++ b/libfwupdplugin/fu-acpi-table.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_ACPI_TABLE (fu_acpi_table_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuAcpiTable, fu_acpi_table, FU, ACPI_TABLE, FuFirmware) + +struct _FuAcpiTableClass { + FuFirmwareClass parent_class; +}; + +FuFirmware * +fu_acpi_table_new(void); +guint8 +fu_acpi_table_get_revision(FuAcpiTable *self); +const gchar * +fu_acpi_table_get_oem_id(FuAcpiTable *self); +const gchar * +fu_acpi_table_get_oem_table_id(FuAcpiTable *self); +guint32 +fu_acpi_table_get_oem_revision(FuAcpiTable *self); diff --git a/libfwupdplugin/fwupdplugin.h b/libfwupdplugin/fwupdplugin.h index 71746b20a..95c0c3e1b 100644 --- a/libfwupdplugin/fwupdplugin.h +++ b/libfwupdplugin/fwupdplugin.h @@ -9,6 +9,7 @@ #define __FWUPDPLUGIN_H_INSIDE__ #include +#include #include #include #include diff --git a/libfwupdplugin/fwupdplugin.map b/libfwupdplugin/fwupdplugin.map index c8a0b93e9..15291c391 100644 --- a/libfwupdplugin/fwupdplugin.map +++ b/libfwupdplugin/fwupdplugin.map @@ -1162,6 +1162,12 @@ LIBFWUPDPLUGIN_1.8.10 { LIBFWUPDPLUGIN_1.8.11 { global: + fu_acpi_table_get_oem_id; + fu_acpi_table_get_oem_revision; + fu_acpi_table_get_oem_table_id; + fu_acpi_table_get_revision; + fu_acpi_table_get_type; + fu_acpi_table_new; fu_device_has_problem; fu_device_progress_get_type; fu_device_progress_new; diff --git a/libfwupdplugin/meson.build b/libfwupdplugin/meson.build index 84f87ab0f..ff29e9510 100644 --- a/libfwupdplugin/meson.build +++ b/libfwupdplugin/meson.build @@ -9,6 +9,7 @@ fwupdplugin_version_h = configure_file( ) fwupdplugin_src = [ + 'fu-acpi-table.c', # fuzzing 'fu-archive.c', 'fu-backend.c', 'fu-bluez-device.c', @@ -112,6 +113,7 @@ else endif fwupdplugin_headers = [ + 'fu-acpi-table.h', 'fu-archive.h', 'fu-backend.h', 'fu-bluez-device.h', diff --git a/plugins/acpi-dmar/fu-acpi-dmar.c b/plugins/acpi-dmar/fu-acpi-dmar.c index bf1aecb1d..cfcd8a671 100644 --- a/plugins/acpi-dmar/fu-acpi-dmar.c +++ b/plugins/acpi-dmar/fu-acpi-dmar.c @@ -6,18 +6,14 @@ #include "config.h" -#include - -#include - #include "fu-acpi-dmar.h" struct _FuAcpiDmar { - GObject parent_instance; + FuAcpiTable parent_instance; gboolean opt_in; }; -G_DEFINE_TYPE(FuAcpiDmar, fu_acpi_dmar, G_TYPE_OBJECT) +G_DEFINE_TYPE(FuAcpiDmar, fu_acpi_dmar, FU_TYPE_ACPI_TABLE) #define DMAR_DMA_CTRL_PLATFORM_OPT_IN_FLAG 0x4 @@ -25,59 +21,25 @@ FuAcpiDmar * fu_acpi_dmar_new(GBytes *blob, GError **error) { FuAcpiDmar *self = g_object_new(FU_TYPE_ACPI_DMAR, NULL); - gchar creator_id[5] = {'\0'}; - gchar oem_table_id[9] = {'\0'}; - gchar signature[5] = {'\0'}; gsize bufsz = 0; guint8 flags = 0; const guint8 *buf = g_bytes_get_data(blob, &bufsz); - /* parse table */ - if (!fu_memcpy_safe((guint8 *)signature, - sizeof(signature), - 0x0, /* dst */ - buf, - bufsz, - 0x00, /* src */ - sizeof(signature) - 1, - error)) + /* FuAcpiTable->parse */ + if (!FU_FIRMWARE_CLASS(fu_acpi_dmar_parent_class) + ->parse(FU_FIRMWARE(self), blob, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) return NULL; - if (strcmp(signature, "DMAR") != 0) { + + /* check signature and read flags */ + if (g_strcmp0(fu_firmware_get_id(FU_FIRMWARE(self)), "DMAR") != 0) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Not a DMAR table, got %s", - signature); + fu_firmware_get_id(FU_FIRMWARE(self))); return NULL; } - if (!fu_memcpy_safe((guint8 *)oem_table_id, - sizeof(oem_table_id), - 0x0, /* dst */ - buf, - bufsz, - 0x10, /* src */ - sizeof(oem_table_id) - 1, - error)) - return NULL; - g_debug("OemTableId: %s", oem_table_id); - if (!fu_memcpy_safe((guint8 *)creator_id, - sizeof(creator_id), - 0x0, /* dst */ - buf, - bufsz, - 0x1c, /* src */ - sizeof(creator_id) - 1, - error)) - return NULL; - g_debug("CreatorId: %s", creator_id); - if (!fu_memcpy_safe(&flags, - sizeof(flags), - 0x0, /* dst */ - buf, - bufsz, - 0x25, /* src */ - sizeof(flags), - error)) + if (!fu_memread_uint8_safe(buf, bufsz, 0x25, &flags, error)) return NULL; g_debug("Flags: 0x%02x", flags); self->opt_in = (flags & DMAR_DMA_CTRL_PLATFORM_OPT_IN_FLAG) > 0; diff --git a/plugins/acpi-dmar/fu-acpi-dmar.h b/plugins/acpi-dmar/fu-acpi-dmar.h index 46ff5dff3..0f26532f3 100644 --- a/plugins/acpi-dmar/fu-acpi-dmar.h +++ b/plugins/acpi-dmar/fu-acpi-dmar.h @@ -6,10 +6,10 @@ #pragma once -#include +#include #define FU_TYPE_ACPI_DMAR (fu_acpi_dmar_get_type()) -G_DECLARE_FINAL_TYPE(FuAcpiDmar, fu_acpi_dmar, FU, ACPI_DMAR, GObject) +G_DECLARE_FINAL_TYPE(FuAcpiDmar, fu_acpi_dmar, FU, ACPI_DMAR, FuAcpiTable) FuAcpiDmar * fu_acpi_dmar_new(GBytes *blob, GError **error); diff --git a/plugins/acpi-ivrs/fu-acpi-ivrs.c b/plugins/acpi-ivrs/fu-acpi-ivrs.c index c24741dd0..79a8c2895 100644 --- a/plugins/acpi-ivrs/fu-acpi-ivrs.c +++ b/plugins/acpi-ivrs/fu-acpi-ivrs.c @@ -7,18 +7,14 @@ #include "config.h" -#include - -#include - #include "fu-acpi-ivrs.h" struct _FuAcpiIvrs { - GObject parent_instance; + FuAcpiTable parent_instance; gboolean remap_support; }; -G_DEFINE_TYPE(FuAcpiIvrs, fu_acpi_ivrs, G_TYPE_OBJECT) +G_DEFINE_TYPE(FuAcpiIvrs, fu_acpi_ivrs, FU_TYPE_ACPI_TABLE) /* IVINfo field */ #define IVRS_DMA_REMAP_SUPPORT_FLAG 0x2 @@ -27,59 +23,25 @@ FuAcpiIvrs * fu_acpi_ivrs_new(GBytes *blob, GError **error) { FuAcpiIvrs *self = g_object_new(FU_TYPE_ACPI_IVRS, NULL); - gchar creator_id[5] = {'\0'}; - gchar oem_table_id[9] = {'\0'}; - gchar signature[5] = {'\0'}; gsize bufsz = 0; guint8 ivinfo = 0; const guint8 *buf = g_bytes_get_data(blob, &bufsz); - /* parse table */ - if (!fu_memcpy_safe((guint8 *)signature, - sizeof(signature), - 0x0, /* dst */ - buf, - bufsz, - 0x00, /* src */ - sizeof(signature) - 1, - error)) + /* FuAcpiTable->parse */ + if (!FU_FIRMWARE_CLASS(fu_acpi_ivrs_parent_class) + ->parse(FU_FIRMWARE(self), blob, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) return NULL; - if (strcmp(signature, "IVRS") != 0) { + + /* check signature and read flags */ + if (g_strcmp0(fu_firmware_get_id(FU_FIRMWARE(self)), "IVRS") != 0) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Not a IVRS table, got %s", - signature); + fu_firmware_get_id(FU_FIRMWARE(self))); return NULL; } - if (!fu_memcpy_safe((guint8 *)oem_table_id, - sizeof(oem_table_id), - 0x0, /* dst */ - buf, - bufsz, - 0x10, /* src */ - sizeof(oem_table_id) - 1, - error)) - return NULL; - g_debug("OemTableId: %s", oem_table_id); - if (!fu_memcpy_safe((guint8 *)creator_id, - sizeof(creator_id), - 0x0, /* dst */ - buf, - bufsz, - 0x1c, /* src */ - sizeof(creator_id) - 1, - error)) - return NULL; - g_debug("CreatorId: %s", creator_id); - if (!fu_memcpy_safe(&ivinfo, - sizeof(ivinfo), - 0x0, /* dst */ - buf, - bufsz, - 0x24, /* src */ - sizeof(ivinfo), - error)) + if (!fu_memread_uint8_safe(buf, bufsz, 0x24, &ivinfo, error)) return NULL; g_debug("Flags: 0x%02x", ivinfo); self->remap_support = ivinfo & IVRS_DMA_REMAP_SUPPORT_FLAG; diff --git a/plugins/acpi-ivrs/fu-acpi-ivrs.h b/plugins/acpi-ivrs/fu-acpi-ivrs.h index 6ebfd3397..a95eea08e 100644 --- a/plugins/acpi-ivrs/fu-acpi-ivrs.h +++ b/plugins/acpi-ivrs/fu-acpi-ivrs.h @@ -7,10 +7,10 @@ #pragma once -#include +#include #define FU_TYPE_ACPI_IVRS (fu_acpi_ivrs_get_type()) -G_DECLARE_FINAL_TYPE(FuAcpiIvrs, fu_acpi_ivrs, FU, ACPI_IVRS, GObject) +G_DECLARE_FINAL_TYPE(FuAcpiIvrs, fu_acpi_ivrs, FU, ACPI_IVRS, FuAcpiTable) FuAcpiIvrs * fu_acpi_ivrs_new(GBytes *blob, GError **error); diff --git a/src/fu-engine.c b/src/fu-engine.c index b57b573de..0a956f6d6 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -8558,6 +8558,7 @@ fu_engine_load(FuEngine *self, FuEngineLoadFlags flags, FuProgress *progress, GE fu_context_add_firmware_gtype(self->ctx, "srec", FU_TYPE_SREC_FIRMWARE); fu_context_add_firmware_gtype(self->ctx, "archive", FU_TYPE_ARCHIVE_FIRMWARE); fu_context_add_firmware_gtype(self->ctx, "smbios", FU_TYPE_SMBIOS); + fu_context_add_firmware_gtype(self->ctx, "acpi-table", FU_TYPE_ACPI_TABLE); fu_context_add_firmware_gtype(self->ctx, "efi-firmware-file", FU_TYPE_EFI_FIRMWARE_FILE); fu_context_add_firmware_gtype(self->ctx, "efi-firmware-filesystem",