diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index 694369dcc..c06bc21d4 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -463,7 +463,8 @@ done %if 0%{?have_uefi} %{_datadir}/installed-tests/fwupd/efi %endif -%{_datadir}/installed-tests/fwupd/dmi +%{_datadir}/installed-tests/fwupd/chassis_type +%{_datadir}/installed-tests/fwupd/sys_vendor %{_datadir}/fwupd/device-tests/*.json %{_libexecdir}/installed-tests/fwupd/* %dir %{_sysconfdir}/fwupd/remotes.d diff --git a/docs/fwupdplugin.toml.in b/docs/fwupdplugin.toml.in index 674b961c9..de9bd99e6 100644 --- a/docs/fwupdplugin.toml.in +++ b/docs/fwupdplugin.toml.in @@ -46,6 +46,7 @@ content_files = [ "tutorial.md", "hsi.md", "ds20.md", + "hwids.md", "bios-settings.md", @plugin_readme_outputs@, ] diff --git a/docs/hwids.md b/docs/hwids.md new file mode 100644 index 000000000..ad7fdc613 --- /dev/null +++ b/docs/hwids.md @@ -0,0 +1,85 @@ +--- +title: Hardware IDs +--- + +## Introduction + +Hardware IDs are used by fwupd to identify specific hardware. +This is useful as the device-specific identifiers may be the same for different firmware streams. +Each hardware ID has varying levels of specificity for the hardware, for instance matching only the +system OEM, or matching up to 8 fields including the system BIOS version. + +For instance, Dell and Lenovo might ship a wireless broadband modem with the same chip vendor and +product IDs of `USB\VID_0BDA&PID_5850` and although the two OEMs share the same internal device, +the firmware may be different. +To cover this case fwupd allows adding `hardware` requirements that mean we can deploy firmware that +targets `USB\VID_0BDA&PID_5850`, but *only for Dell* or *only for Lenovo* systems. + +Microsoft calls these values "CHIDs" and they are generated on Windows from the SBMIOS tables using `ComputerHardwareIds.exe` +binary which can be found [here](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/computerhardwareids). +The list of CHIDs used in Microsoft Windows is: + + HardwareID-0 ← Manufacturer + Family + Product Name + SKU Number + BIOS Vendor + BIOS Version + BIOS Major Release + BIOS Minor Release + HardwareID-1 ← Manufacturer + Family + Product Name + BIOS Vendor + BIOS Version + BIOS Major Release + BIOS Minor Release + HardwareID-2 ← Manufacturer + Product Name + BIOS Vendor + BIOS Version + BIOS Major Release + BIOS Minor Release + HardwareID-3 ← Manufacturer + Family + ProductName + SKU Number + Baseboard_Manufacturer + Baseboard_Product + HardwareID-4 ← Manufacturer + Family + ProductName + SKU Number + HardwareID-5 ← Manufacturer + Family + ProductName + HardwareID-6 ← Manufacturer + SKU Number + Baseboard_Manufacturer + Baseboard_Product + HardwareID-7 ← Manufacturer + SKU Number + HardwareID-8 ← Manufacturer + ProductName + Baseboard_Manufacturer + Baseboard_Product + HardwareID-9 ← Manufacturer + ProductName + HardwareID-10 ← Manufacturer + Family + Baseboard_Manufacturer + Baseboard_Product + HardwareID-11 ← Manufacturer + Family + HardwareID-12 ← Manufacturer + Enclosure Type + HardwareID-13 ← Manufacturer + Baseboard_Manufacturer + Baseboard_Product + HardwareID-14 ← Manufacturer + +On Windows, CHIDs are generated from the ASCII representation of SMBIOS strings, and on Linux the same +mechanism is used. Additionally, on Linux, the Device Tree, DMI and `kenv` data sources +are used to construct emulations of the Microsoft CHIDs. + +When installing firmware and drivers in Windows vendors *already use* the generated HardwareID GUIDs +that match SMBIOS keys like the BIOS vendor and the product SKU. + +Both `fwupdtool hwids` and `ComputerHardwareIds.exe` only compute results that have the necessary +data values available. +If a data field is missing, then any related CHIDs are not generated. +For example, if the SKU field is missing, then `HardwareID` 0, 3, 4 6 and 7 will not be available for +that particular system. + +## Implementation + +Users with versions of fwupd newer than 1.1.1 can run `sudo fwupdtool hwids`. For example: + + Computer Information + -------------------- + BiosVendor: LENOVO + BiosVersion: GJET75WW (2.25 ) + Manufacturer: LENOVO + Family: ThinkPad T440s + ProductName: 20ARS19C0C + ProductSku: LENOVO_MT_20AR_BU_Think_FM_ThinkPad T440s + EnclosureKind: 10 + BaseboardManufacturer: LENOVO + BaseboardProduct: 20ARS19C0C + + Hardware IDs + ------------ + {c4159f74-3d2c-526f-b6d1-fe24a2fbc881} <- Manufacturer + Family + ProductName + ProductSku + BiosVendor + BiosVersion + BiosMajorRelease + BiosMinorRelease + {ff66cb74-5f5d-5669-875a-8a8f97be22c1} <- Manufacturer + Family + ProductName + BiosVendor + BiosVersion + BiosMajorRelease + BiosMinorRelease + {2e4dad4e-27a0-5de0-8e92-f395fc3fa5ba} <- Manufacturer + ProductName + BiosVendor + BiosVersion + BiosMajorRelease + BiosMinorRelease + {3faec92a-3ae3-5744-be88-495e90a7d541} <- Manufacturer + Family + ProductName + ProductSku + BaseboardManufacturer + BaseboardProduct + {660ccba8-1b78-5a33-80e6-9fb8354ee873} <- Manufacturer + Family + ProductName + ProductSku + {8dc9b7c5-f5d5-5850-9ab3-bd6f0549d814} <- Manufacturer + Family + ProductName + {178cd22d-ad9f-562d-ae0a-34009822cdbe} <- Manufacturer + ProductSku + BaseboardManufacturer + BaseboardProduct + {da1da9b6-62f5-5f22-8aaa-14db7eeda2a4} <- Manufacturer + ProductSku + {059eb22d-6dc7-59af-abd3-94bbe017f67c} <- Manufacturer + ProductName + BaseboardManufacturer + BaseboardProduct + {0cf8618d-9eff-537c-9f35-46861406eb9c} <- Manufacturer + ProductName + {f4275c1f-6130-5191-845c-3426247eb6a1} <- Manufacturer + Family + BaseboardManufacturer + BaseboardProduct + {db73af4c-4612-50f7-b8a7-787cf4871847} <- Manufacturer + Family + {5e820764-888e-529d-a6f9-dfd12bacb160} <- Manufacturer + EnclosureKind + {f8e1de5f-b68c-5f52-9d1a-f1ba52f1f773} <- Manufacturer + BaseboardManufacturer + BaseboardProduct + {6de5d951-d755-576b-bd09-c5cf66b27234} <- Manufacturer + +Which matches the output of `ComputerHardwareIds.exe` on the same hardware. diff --git a/docs/index.html b/docs/index.html index 512e14907..13086a6b4 100644 --- a/docs/index.html +++ b/docs/index.html @@ -45,6 +45,9 @@ + diff --git a/libfwupdplugin/fu-context.c b/libfwupdplugin/fu-context.c index 9f9f7ed13..b9deac1b7 100644 --- a/libfwupdplugin/fu-context.c +++ b/libfwupdplugin/fu-context.c @@ -11,7 +11,7 @@ #include "fu-bios-settings-private.h" #include "fu-context-private.h" #include "fu-fdt-firmware.h" -#include "fu-hwids.h" +#include "fu-hwids-private.h" #include "fu-path.h" #include "fu-smbios-private.h" #include "fu-volume-private.h" @@ -455,8 +455,11 @@ gchar * fu_context_get_hwid_replace_value(FuContext *self, const gchar *keys, GError **error) { FuContextPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_CONTEXT(self), NULL); g_return_val_if_fail(keys != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + if (!priv->loaded_hwinfo) { g_critical("cannot use HWIDs before calling ->load_hwinfo()"); g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "no data"); @@ -786,21 +789,64 @@ fu_context_load_hwinfo(FuContext *self, FuContextHwidFlags flags, GError **error g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + if ((flags & FU_CONTEXT_HWID_FLAG_LOAD_CONFIG) > 0) { + g_autoptr(GError) error_local = NULL; + if (!fu_hwids_config_setup(self, priv->hwids, &error_local)) { + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "Failed to load HWIDs config: "); + return FALSE; + } + } + } + if ((flags & FU_CONTEXT_HWID_FLAG_LOAD_DMI) > 0) { + g_autoptr(GError) error_local = NULL; + if (!fu_hwids_dmi_setup(self, priv->hwids, &error_local)) { + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "Failed to load HWIDs DMI: "); + return FALSE; + } + } + } + if ((flags & FU_CONTEXT_HWID_FLAG_LOAD_FDT) > 0) { + g_autoptr(GError) error_local = NULL; + if (!fu_hwids_fdt_setup(self, priv->hwids, &error_local)) { + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "Failed to load HWIDs FDT: "); + return FALSE; + } + } + } + if ((flags & FU_CONTEXT_HWID_FLAG_LOAD_KENV) > 0) { + g_autoptr(GError) error_local = NULL; + if (!fu_hwids_kenv_setup(self, priv->hwids, &error_local)) { + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "Failed to load HWIDs kenv: "); + return FALSE; + } + } + } if ((flags & FU_CONTEXT_HWID_FLAG_LOAD_SMBIOS) > 0) { g_autoptr(GError) error_local = NULL; - if (!fu_smbios_setup(priv->smbios, &error_local)) { - if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) - g_warning("Failed to load SMBIOS: %s", error_local->message); + if (!fu_hwids_smbios_setup(self, priv->hwids, &error_local)) { + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "Failed to load SMBIOS: "); + return FALSE; + } } - fu_context_set_chassis_kind(self, - fu_smbios_get_integer(priv->smbios, - FU_SMBIOS_STRUCTURE_TYPE_CHASSIS, - 0x05, - NULL)); } - if (!fu_hwids_setup(priv->hwids, priv->smbios, &error_hwids)) - g_warning("Failed to load HWIDs: %s", error_hwids->message); priv->loaded_hwinfo = TRUE; + if (!fu_hwids_setup(priv->hwids, &error_hwids)) + g_warning("Failed to load HWIDs: %s", error_hwids->message); /* set the hwid flags */ guids = fu_context_get_hwid_guids(self); diff --git a/libfwupdplugin/fu-hwids-config.c b/libfwupdplugin/fu-hwids-config.c new file mode 100644 index 000000000..808440aa5 --- /dev/null +++ b/libfwupdplugin/fu-hwids-config.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuContext" + +#include "config.h" + +#include "fu-context-private.h" +#include "fu-hwids-private.h" +#include "fu-path.h" + +gboolean +fu_hwids_config_setup(FuContext *ctx, FuHwids *self, GError **error) +{ + g_autofree gchar *localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); + g_autofree gchar *sysconfigdir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR_PKG); + g_autoptr(GKeyFile) kf = g_key_file_new(); + g_autoptr(GPtrArray) fns = g_ptr_array_new_with_free_func(g_free); + g_autoptr(GPtrArray) keys = fu_hwids_get_keys(self); + + /* per-system configuration and optional overrides */ + g_ptr_array_add(fns, g_build_filename(sysconfigdir, "daemon.conf", NULL)); + g_ptr_array_add(fns, g_build_filename(localstatedir, "daemon.conf", NULL)); + for (guint i = 0; i < fns->len; i++) { + const gchar *fn = g_ptr_array_index(fns, i); + if (g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_debug("loading HwId overrides from %s", fn); + if (!g_key_file_load_from_file(kf, fn, G_KEY_FILE_NONE, error)) + return FALSE; + } else { + g_debug("not loading HwId overrides from %s", fn); + } + } + + /* all keys are optional */ + for (guint i = 0; i < keys->len; i++) { + const gchar *key = g_ptr_array_index(keys, i); + g_autofree gchar *value = g_key_file_get_string(kf, "fwupd", key, NULL); + if (value != NULL) + fu_hwids_add_value(self, key, value); + } + + /* success */ + return TRUE; +} diff --git a/libfwupdplugin/fu-hwids-dmi.c b/libfwupdplugin/fu-hwids-dmi.c new file mode 100644 index 000000000..280795f67 --- /dev/null +++ b/libfwupdplugin/fu-hwids-dmi.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuContext" + +#include "config.h" + +#include "fu-context-private.h" +#include "fu-hwids-private.h" +#include "fu-path.h" +#include "fu-string.h" + +gboolean +fu_hwids_dmi_setup(FuContext *ctx, FuHwids *self, GError **error) +{ + g_autofree gchar *path = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_DMI); + struct { + const gchar *hwid; + const gchar *key; + } map[] = {{FU_HWIDS_KEY_BASEBOARD_MANUFACTURER, "board_vendor"}, + {FU_HWIDS_KEY_BASEBOARD_PRODUCT, "board_name"}, + {FU_HWIDS_KEY_BIOS_VENDOR, "bios_vendor"}, + {FU_HWIDS_KEY_BIOS_VERSION, "bios_version"}, + {FU_HWIDS_KEY_FAMILY, "product_family"}, + {FU_HWIDS_KEY_MANUFACTURER, "sys_vendor"}, + {FU_HWIDS_KEY_PRODUCT_NAME, "product_name"}, + {FU_HWIDS_KEY_PRODUCT_SKU, "product_sku"}, + {FU_HWIDS_KEY_ENCLOSURE_KIND, "chassis_type"}, + {NULL, NULL}}; + + /* the values the kernel parsed; these are world-readable */ + if (!g_file_test(path, G_FILE_TEST_IS_DIR)) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no %s", path); + return FALSE; + } + for (guint i = 0; map[i].key != NULL; i++) { + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + g_autofree gchar *fn = g_build_filename(path, map[i].key, NULL); + g_autoptr(GError) error_local = NULL; + + if (!g_file_get_contents(fn, &buf, &bufsz, &error_local)) { + g_debug("unable to read SMBIOS data from %s: %s", fn, error_local->message); + continue; + } + + /* trim trailing newline added by kernel */ + if (buf[bufsz - 1] == '\n') + buf[bufsz - 1] = 0; + fu_hwids_add_value(self, map[i].hwid, buf); + + if (g_strcmp0(map[i].hwid, FU_HWIDS_KEY_ENCLOSURE_KIND) == 0) { + guint64 val = 0; + if (!fu_strtoull(buf, + &val, + FU_SMBIOS_CHASSIS_KIND_OTHER, + FU_SMBIOS_CHASSIS_KIND_LAST, + &error_local)) { + g_warning("ignoring enclosure kind %s", buf); + continue; + } + fu_context_set_chassis_kind(ctx, val); + } + } + + /* success */ + return TRUE; +} diff --git a/libfwupdplugin/fu-hwids-fdt.c b/libfwupdplugin/fu-hwids-fdt.c new file mode 100644 index 000000000..6bdd776a5 --- /dev/null +++ b/libfwupdplugin/fu-hwids-fdt.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuContext" + +#include "config.h" + +#include "fu-context-private.h" +#include "fu-fdt-firmware.h" +#include "fu-hwids-private.h" + +gboolean +fu_hwids_fdt_setup(FuContext *ctx, FuHwids *self, GError **error) +{ + g_auto(GStrv) compatible = NULL; + g_autoptr(FuFirmware) fdt_img = NULL; + g_autoptr(FuFdtImage) fdt_img_fwver = NULL; + g_autoptr(FuFirmware) fdt = NULL; + struct { + const gchar *hwid; + const gchar *key; + } map[] = {{FU_HWIDS_KEY_MANUFACTURER, "vendor"}, + {FU_HWIDS_KEY_FAMILY, "model-name"}, + {FU_HWIDS_KEY_PRODUCT_NAME, "model"}, + {NULL, NULL}}; + + /* adds compatible GUIDs */ + fdt = fu_context_get_fdt(ctx, error); + if (fdt == NULL) + return FALSE; + fdt_img = fu_firmware_get_image_by_id(fdt, NULL, error); + if (fdt_img == NULL) + return FALSE; + if (!fu_fdt_image_get_attr_strlist(FU_FDT_IMAGE(fdt_img), "compatible", &compatible, error)) + return FALSE; + for (guint i = 0; compatible[i] != NULL; i++) { + g_autofree gchar *guid = fwupd_guid_hash_string(compatible[i]); + g_debug("using %s for DT compatible %s", guid, compatible[i]); + fu_hwids_add_guid(self, guid); + } + + /* root node */ + for (guint i = 0; map[i].key != NULL; i++) { + g_autofree gchar *tmp = NULL; + fu_fdt_image_get_attr_str(FU_FDT_IMAGE(fdt_img), map[i].key, &tmp, NULL); + fu_hwids_add_value(self, map[i].hwid, tmp); + } + + /* fallback */ + if (g_strv_length(compatible) > 0) + fu_hwids_add_value(self, FU_HWIDS_KEY_MANUFACTURER, compatible[0]); + if (g_strv_length(compatible) > 1) + fu_hwids_add_value(self, FU_HWIDS_KEY_PRODUCT_NAME, compatible[1]); + if (g_strv_length(compatible) > 2) + fu_hwids_add_value(self, FU_HWIDS_KEY_FAMILY, compatible[2]); + if (fu_context_get_chassis_kind(ctx) == FU_SMBIOS_CHASSIS_KIND_UNKNOWN) { + if (fu_fdt_image_get_attr_str(FU_FDT_IMAGE(fdt_img), "battery", NULL, NULL)) + fu_context_set_chassis_kind(ctx, FU_SMBIOS_CHASSIS_KIND_PORTABLE); + } + fdt_img_fwver = + fu_fdt_firmware_get_image_by_path(FU_FDT_FIRMWARE(fdt), "ibm,firmware-versions", NULL); + if (fdt_img_fwver != NULL) { + g_autofree gchar *version = NULL; + fu_fdt_image_get_attr_str(FU_FDT_IMAGE(fdt_img), "version", &version, NULL); + fu_hwids_add_value(self, FU_HWIDS_KEY_BIOS_VERSION, version); + } + + /* success */ + return TRUE; +} diff --git a/libfwupdplugin/fu-hwids-kenv.c b/libfwupdplugin/fu-hwids-kenv.c new file mode 100644 index 000000000..4d426c87f --- /dev/null +++ b/libfwupdplugin/fu-hwids-kenv.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuContext" + +#include "config.h" + +#include "fu-context-private.h" +#include "fu-hwids-private.h" +#include "fu-kenv.h" + +gboolean +fu_hwids_kenv_setup(FuContext *ctx, FuHwids *self, GError **error) +{ +#ifdef HAVE_KENV_H + struct { + const gchar *hwid; + const gchar *key; + } map[] = {{FU_HWIDS_KEY_BASEBOARD_MANUFACTURER, "smbios.planar.maker"}, + {FU_HWIDS_KEY_BASEBOARD_PRODUCT, "smbios.planar.product"}, + {FU_HWIDS_KEY_BIOS_VENDOR, "smbios.bios.vendor"}, + {FU_HWIDS_KEY_BIOS_VERSION, "smbios.bios.version"}, + {FU_HWIDS_KEY_FAMILY, "smbios.system.family"}, + {FU_HWIDS_KEY_MANUFACTURER, "smbios.system.maker"}, + {FU_HWIDS_KEY_PRODUCT_NAME, "smbios.system.product"}, + {FU_HWIDS_KEY_PRODUCT_SKU, "smbios.system.sku"}, + {{NULL, NULL}}}; + for (guint i = 0; map[i].key != NULL; i++) { + g_autoptr(GError) error_local = NULL; + g_autofree gchar *value = fu_kenv_get_string(map[i].key, error_local); + if (value == NULL) { + g_debug("ignoring: %s", error_local->message); + continue; + } + fu_hwids_add_value(self, map[i].hwid, value); + } +#endif + + /* success */ + return TRUE; +} diff --git a/libfwupdplugin/fu-hwids-private.h b/libfwupdplugin/fu-hwids-private.h new file mode 100644 index 000000000..be2e28d1d --- /dev/null +++ b/libfwupdplugin/fu-hwids-private.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-context.h" +#include "fu-hwids.h" + +FuHwids * +fu_hwids_new(void); +gboolean +fu_hwids_setup(FuHwids *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean +fu_hwids_config_setup(FuContext *ctx, FuHwids *self, GError **error); +gboolean +fu_hwids_dmi_setup(FuContext *ctx, FuHwids *self, GError **error); +gboolean +fu_hwids_fdt_setup(FuContext *ctx, FuHwids *self, GError **error); +gboolean +fu_hwids_kenv_setup(FuContext *ctx, FuHwids *self, GError **error); +gboolean +fu_hwids_smbios_setup(FuContext *ctx, FuHwids *self, GError **error); diff --git a/libfwupdplugin/fu-hwids-smbios.c b/libfwupdplugin/fu-hwids-smbios.c new file mode 100644 index 000000000..130aee7e8 --- /dev/null +++ b/libfwupdplugin/fu-hwids-smbios.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuContext" + +#include "config.h" + +#include "fu-context-private.h" +#include "fu-hwids-private.h" +#include "fu-smbios-private.h" +#include "fu-string.h" + +typedef gchar *(*FuContextHwidConvertFunc)(FuSmbios *smbios, + guint8 type, + guint8 offset, + GError **error); + +static gchar * +fu_hwids_smbios_convert_string_table_cb(FuSmbios *smbios, + guint8 type, + guint8 offset, + GError **error) +{ + const gchar *tmp = fu_smbios_get_string(smbios, type, offset, error); + if (tmp == NULL) + return NULL; + /* ComputerHardwareIds.exe seems to strip spaces */ + return fu_strstrip(tmp); +} + +static gchar * +fu_hwids_smbios_convert_padded_integer_cb(FuSmbios *smbios, + guint8 type, + guint8 offset, + GError **error) +{ + guint tmp = fu_smbios_get_integer(smbios, type, offset, error); + if (tmp == G_MAXUINT) + return NULL; + return g_strdup_printf("%02x", tmp); +} + +static gchar * +fu_hwids_smbios_convert_integer_cb(FuSmbios *smbios, guint8 type, guint8 offset, GError **error) +{ + guint tmp = fu_smbios_get_integer(smbios, type, offset, error); + if (tmp == G_MAXUINT) + return NULL; + return g_strdup_printf("%x", tmp); +} + +gboolean +fu_hwids_smbios_setup(FuContext *ctx, FuHwids *self, GError **error) +{ + FuSmbios *smbios = fu_context_get_smbios(ctx); + struct { + const gchar *key; + guint8 type; + guint8 offset; + FuContextHwidConvertFunc func; + } map[] = {{FU_HWIDS_KEY_MANUFACTURER, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x04, + fu_hwids_smbios_convert_string_table_cb}, + {FU_HWIDS_KEY_ENCLOSURE_KIND, + FU_SMBIOS_STRUCTURE_TYPE_CHASSIS, + 0x05, + fu_hwids_smbios_convert_integer_cb}, + {FU_HWIDS_KEY_FAMILY, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x1a, + fu_hwids_smbios_convert_string_table_cb}, + {FU_HWIDS_KEY_PRODUCT_NAME, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x05, + fu_hwids_smbios_convert_string_table_cb}, + {FU_HWIDS_KEY_PRODUCT_SKU, + FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, + 0x19, + fu_hwids_smbios_convert_string_table_cb}, + {FU_HWIDS_KEY_BIOS_VENDOR, + FU_SMBIOS_STRUCTURE_TYPE_BIOS, + 0x04, + fu_hwids_smbios_convert_string_table_cb}, + {FU_HWIDS_KEY_BIOS_VERSION, + FU_SMBIOS_STRUCTURE_TYPE_BIOS, + 0x05, + fu_hwids_smbios_convert_string_table_cb}, + {FU_HWIDS_KEY_BIOS_MAJOR_RELEASE, + FU_SMBIOS_STRUCTURE_TYPE_BIOS, + 0x14, + fu_hwids_smbios_convert_padded_integer_cb}, + {FU_HWIDS_KEY_BIOS_MINOR_RELEASE, + FU_SMBIOS_STRUCTURE_TYPE_BIOS, + 0x15, + fu_hwids_smbios_convert_padded_integer_cb}, + {FU_HWIDS_KEY_FIRMWARE_MAJOR_RELEASE, + FU_SMBIOS_STRUCTURE_TYPE_BIOS, + 0x16, + fu_hwids_smbios_convert_padded_integer_cb}, + {FU_HWIDS_KEY_FIRMWARE_MINOR_RELEASE, + FU_SMBIOS_STRUCTURE_TYPE_BIOS, + 0x17, + fu_hwids_smbios_convert_padded_integer_cb}, + {FU_HWIDS_KEY_BASEBOARD_MANUFACTURER, + FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, + 0x04, + fu_hwids_smbios_convert_string_table_cb}, + {FU_HWIDS_KEY_BASEBOARD_PRODUCT, + FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, + 0x05, + fu_hwids_smbios_convert_string_table_cb}, + {NULL, 0x00, 0x00, NULL}}; + + if (!fu_smbios_setup(smbios, error)) + return FALSE; + + /* get all DMI data from SMBIOS */ + fu_context_set_chassis_kind( + ctx, + fu_smbios_get_integer(smbios, FU_SMBIOS_STRUCTURE_TYPE_CHASSIS, 0x05, NULL)); + for (guint i = 0; map[i].key != NULL; i++) { + const gchar *contents_hdr = NULL; + g_autofree gchar *contents = NULL; + g_autoptr(GError) error_local = NULL; + + contents = map[i].func(smbios, map[i].type, map[i].offset, &error_local); + if (contents == NULL) { + g_debug("ignoring %s: %s", map[i].key, error_local->message); + continue; + } + g_debug("smbios property %s=%s", map[i].key, contents); + + /* weirdly, remove leading zeros */ + contents_hdr = contents; + while (contents_hdr[0] == '0' && + map[i].func != fu_hwids_smbios_convert_padded_integer_cb) + contents_hdr++; + fu_hwids_add_value(self, map[i].key, contents_hdr); + } + + /* success */ + return TRUE; +} diff --git a/libfwupdplugin/fu-hwids.c b/libfwupdplugin/fu-hwids.c index 94dcfd97a..5192488b3 100644 --- a/libfwupdplugin/fu-hwids.c +++ b/libfwupdplugin/fu-hwids.c @@ -15,7 +15,7 @@ #include "fwupd-error.h" #include "fu-common.h" -#include "fu-hwids.h" +#include "fu-hwids-private.h" #include "fu-path.h" #include "fu-string.h" @@ -32,9 +32,8 @@ struct _FuHwids { GObject parent_instance; - GHashTable *hash_dmi_hw; /* BiosVersion->"1.2.3 " */ - GHashTable *hash_dmi_display; /* BiosVersion->"1.2.3" */ - GHashTable *hash_smbios_override; /* BiosVersion->"1.2.3" */ + GHashTable *hash_values; /* BiosVersion->"1.2.3 " */ + GHashTable *hash_values_display; /* BiosVersion->"1.2.3" */ GHashTable *hash_guid; /* a-c-b-d->1 */ GPtrArray *array_guids; /* a-c-b-d */ }; @@ -56,7 +55,7 @@ G_DEFINE_TYPE(FuHwids, fu_hwids, G_TYPE_OBJECT) const gchar * fu_hwids_get_value(FuHwids *self, const gchar *key) { - return g_hash_table_lookup(self->hash_dmi_display, key); + return g_hash_table_lookup(self->hash_values_display, key); } /** @@ -121,6 +120,7 @@ fu_hwids_get_keys(FuHwids *self) FU_HWIDS_KEY_BASEBOARD_MANUFACTURER, FU_HWIDS_KEY_BASEBOARD_PRODUCT, NULL}; + g_return_val_if_fail(FU_IS_HWIDS(self), NULL); for (guint i = 0; keys[i] != NULL; i++) g_ptr_array_add(array, (gpointer)keys[i]); return array; @@ -219,6 +219,9 @@ fu_hwids_get_replace_keys(FuHwids *self, const gchar *key) {"HardwareID-14", FU_HWIDS_KEY_MANUFACTURER}, {NULL, NULL}}; + g_return_val_if_fail(FU_IS_HWIDS(self), NULL); + g_return_val_if_fail(key != NULL, NULL); + /* defined for Windows 10 */ for (guint i = 0; msdefined[i].search != NULL; i++) { if (g_strcmp0(msdefined[i].search, key) == 0) { @@ -231,23 +234,39 @@ fu_hwids_get_replace_keys(FuHwids *self, const gchar *key) } /** - * fu_hwids_add_smbios_override: + * fu_hwids_add_value: * @self: a #FuHwids * @key: a key, e.g. %FU_HWIDS_KEY_PRODUCT_SKU * @value: (nullable): a new value, e.g. `ExampleModel` * - * Sets SMBIOS override values so you can emulate another system. + * Sets override values so you can emulate another system. * * This function has no effect if called after fu_hwids_setup() * - * Since: 1.5.6 + * Since: 1.8.10 **/ void -fu_hwids_add_smbios_override(FuHwids *self, const gchar *key, const gchar *value) +fu_hwids_add_value(FuHwids *self, const gchar *key, const gchar *value) { g_return_if_fail(FU_IS_HWIDS(self)); g_return_if_fail(key != NULL); - g_hash_table_insert(self->hash_smbios_override, g_strdup(key), g_strdup(value)); + + /* does not replace; first value set wins */ + if (g_hash_table_contains(self->hash_values, key)) + return; + g_hash_table_insert(self->hash_values, g_strdup(key), g_strdup(value)); + + /* make suitable for display */ + if (value != NULL) { + g_autofree gchar *value_safe = g_str_to_ascii(value, "C"); + g_strdelimit(value_safe, "\n\r", '\0'); + g_strchomp(value_safe); + g_hash_table_insert(self->hash_values_display, + g_strdup(key), + g_steal_pointer(&value_safe)); + } else { + g_hash_table_insert(self->hash_values_display, g_strdup(key), NULL); + } } /** @@ -278,7 +297,7 @@ fu_hwids_get_replace_values(FuHwids *self, const gchar *keys, GError **error) /* get each part of the HWID */ split = g_strsplit(keys, "&", -1); for (guint j = 0; split[j] != NULL; j++) { - const gchar *tmp = g_hash_table_lookup(self->hash_dmi_hw, split[j]); + const gchar *tmp = g_hash_table_lookup(self->hash_values, split[j]); if (tmp == NULL) { g_set_error(error, G_IO_ERROR, @@ -321,199 +340,41 @@ fu_hwids_get_guid(FuHwids *self, const gchar *keys, GError **error) return fu_hwids_get_guid_for_str(tmp, error); } -typedef gchar *(*FuHwidsConvertFunc)(FuSmbios *smbios, guint8 type, guint8 offset, GError **error); - -static gchar * -fu_hwids_convert_string_table_cb(FuSmbios *smbios, guint8 type, guint8 offset, GError **error) +/** + * fu_hwids_add_guid: + * @self: a #FuHwids + * @guid: a GUID + * + * Adds a HWID GUID value. + * + * Since: 1.8.10 + **/ +void +fu_hwids_add_guid(FuHwids *self, const gchar *guid) { - const gchar *tmp; - tmp = fu_smbios_get_string(smbios, type, offset, error); - if (tmp == NULL) - return NULL; - /* ComputerHardwareIds.exe seems to strip spaces */ - return fu_strstrip(tmp); -} - -static gchar * -fu_hwids_convert_padded_integer_cb(FuSmbios *smbios, guint8 type, guint8 offset, GError **error) -{ - guint tmp = fu_smbios_get_integer(smbios, type, offset, error); - if (tmp == G_MAXUINT) - return NULL; - return g_strdup_printf("%02x", tmp); -} - -static gchar * -fu_hwids_convert_integer_cb(FuSmbios *smbios, guint8 type, guint8 offset, GError **error) -{ - guint tmp = fu_smbios_get_integer(smbios, type, offset, error); - if (tmp == G_MAXUINT) - return NULL; - return g_strdup_printf("%x", tmp); -} - -static gboolean -fu_hwids_setup_overrides(FuHwids *self, GError **error) -{ - g_autofree gchar *localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); - g_autofree gchar *sysconfigdir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR_PKG); - g_autoptr(GKeyFile) kf = g_key_file_new(); - g_autoptr(GPtrArray) fns = g_ptr_array_new_with_free_func(g_free); - g_autoptr(GPtrArray) keys = fu_hwids_get_keys(self); - - /* per-system configuration and optional overrides */ - g_ptr_array_add(fns, g_build_filename(sysconfigdir, "daemon.conf", NULL)); - g_ptr_array_add(fns, g_build_filename(localstatedir, "daemon.conf", NULL)); - for (guint i = 0; i < fns->len; i++) { - const gchar *fn = g_ptr_array_index(fns, i); - if (g_file_test(fn, G_FILE_TEST_EXISTS)) { - g_debug("loading HwId overrides from %s", fn); - if (!g_key_file_load_from_file(kf, fn, G_KEY_FILE_NONE, error)) - return FALSE; - } else { - g_debug("not loading HwId overrides from %s", fn); - } - } - - /* all keys are optional */ - for (guint i = 0; i < keys->len; i++) { - const gchar *key = g_ptr_array_index(keys, i); - g_autofree gchar *value = g_key_file_get_string(kf, "fwupd", key, NULL); - if (value != NULL) - fu_hwids_add_smbios_override(self, key, value); - } - - /* success */ - return TRUE; + g_return_if_fail(FU_IS_HWIDS(self)); + g_return_if_fail(guid != NULL); + g_hash_table_insert(self->hash_guid, g_strdup(guid), GUINT_TO_POINTER(1)); + g_ptr_array_add(self->array_guids, g_strdup(guid)); } /** * fu_hwids_setup: * @self: a #FuHwids - * @smbios: (nullable): a #FuSmbios * @error: (nullable): optional return location for an error * - * Reads all the SMBIOS values from the hardware. + * Adds all the `HardwareID` GUIDs from the previously supplied data. * * Returns: %TRUE for success * * Since: 0.9.3 **/ gboolean -fu_hwids_setup(FuHwids *self, FuSmbios *smbios, GError **error) +fu_hwids_setup(FuHwids *self, GError **error) { - struct { - const gchar *key; - guint8 type; - guint8 offset; - FuHwidsConvertFunc func; - } map[] = {{FU_HWIDS_KEY_MANUFACTURER, - FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, - 0x04, - fu_hwids_convert_string_table_cb}, - {FU_HWIDS_KEY_ENCLOSURE_KIND, - FU_SMBIOS_STRUCTURE_TYPE_CHASSIS, - 0x05, - fu_hwids_convert_integer_cb}, - {FU_HWIDS_KEY_FAMILY, - FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, - 0x1a, - fu_hwids_convert_string_table_cb}, - {FU_HWIDS_KEY_PRODUCT_NAME, - FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, - 0x05, - fu_hwids_convert_string_table_cb}, - {FU_HWIDS_KEY_PRODUCT_SKU, - FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, - 0x19, - fu_hwids_convert_string_table_cb}, - {FU_HWIDS_KEY_BIOS_VENDOR, - FU_SMBIOS_STRUCTURE_TYPE_BIOS, - 0x04, - fu_hwids_convert_string_table_cb}, - {FU_HWIDS_KEY_BIOS_VERSION, - FU_SMBIOS_STRUCTURE_TYPE_BIOS, - 0x05, - fu_hwids_convert_string_table_cb}, - {FU_HWIDS_KEY_BIOS_MAJOR_RELEASE, - FU_SMBIOS_STRUCTURE_TYPE_BIOS, - 0x14, - fu_hwids_convert_padded_integer_cb}, - {FU_HWIDS_KEY_BIOS_MINOR_RELEASE, - FU_SMBIOS_STRUCTURE_TYPE_BIOS, - 0x15, - fu_hwids_convert_padded_integer_cb}, - {FU_HWIDS_KEY_FIRMWARE_MAJOR_RELEASE, - FU_SMBIOS_STRUCTURE_TYPE_BIOS, - 0x16, - fu_hwids_convert_padded_integer_cb}, - {FU_HWIDS_KEY_FIRMWARE_MINOR_RELEASE, - FU_SMBIOS_STRUCTURE_TYPE_BIOS, - 0x17, - fu_hwids_convert_padded_integer_cb}, - {FU_HWIDS_KEY_BASEBOARD_MANUFACTURER, - FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, - 0x04, - fu_hwids_convert_string_table_cb}, - {FU_HWIDS_KEY_BASEBOARD_PRODUCT, - FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, - 0x05, - fu_hwids_convert_string_table_cb}, - {NULL, 0x00, 0x00, NULL}}; - g_return_val_if_fail(FU_IS_HWIDS(self), FALSE); - g_return_val_if_fail(FU_IS_SMBIOS(smbios) || smbios == NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - /* override using a config file */ - if (!fu_hwids_setup_overrides(self, error)) - return FALSE; - - /* get all DMI data */ - for (guint i = 0; map[i].key != NULL; i++) { - const gchar *contents_hdr = NULL; - g_autofree gchar *contents = NULL; - g_autofree gchar *contents_safe = NULL; - g_autoptr(GError) error_local = NULL; - - /* get the data from a SMBIOS table unless an override exists */ - if (g_hash_table_lookup_extended(self->hash_smbios_override, - map[i].key, - NULL, - (gpointer *)&contents_hdr)) { - if (contents_hdr == NULL) { - g_debug("ignoring %s", map[i].key); - continue; - } - } else if (smbios != NULL) { - contents = map[i].func(smbios, map[i].type, map[i].offset, &error_local); - if (contents == NULL) { - g_debug("ignoring %s: %s", map[i].key, error_local->message); - continue; - } - contents_hdr = contents; - } else { - g_debug("ignoring %s", map[i].key); - continue; - } - g_debug("smbios property %s=%s", map[i].key, contents_hdr); - - /* weirdly, remove leading zeros */ - while (contents_hdr[0] == '0' && map[i].func != fu_hwids_convert_padded_integer_cb) - contents_hdr++; - g_hash_table_insert(self->hash_dmi_hw, - g_strdup(map[i].key), - g_strdup(contents_hdr)); - - /* make suitable for display */ - contents_safe = g_str_to_ascii(contents_hdr, "C"); - g_strdelimit(contents_safe, "\n\r", '\0'); - g_strchomp(contents_safe); - g_hash_table_insert(self->hash_dmi_display, - g_strdup(map[i].key), - g_steal_pointer(&contents_safe)); - } - /* add GUIDs */ for (guint i = 0; i < 15; i++) { g_autofree gchar *guid = NULL; @@ -527,8 +388,7 @@ fu_hwids_setup(FuHwids *self, FuSmbios *smbios, GError **error) g_debug("%s is not available, %s", key, error_local->message); continue; } - g_hash_table_insert(self->hash_guid, g_strdup(guid), GUINT_TO_POINTER(1)); - g_ptr_array_add(self->array_guids, g_steal_pointer(&guid)); + fu_hwids_add_guid(self, guid); } return TRUE; @@ -541,9 +401,8 @@ fu_hwids_finalize(GObject *object) g_return_if_fail(FU_IS_HWIDS(object)); self = FU_HWIDS(object); - g_hash_table_unref(self->hash_dmi_hw); - g_hash_table_unref(self->hash_dmi_display); - g_hash_table_unref(self->hash_smbios_override); + g_hash_table_unref(self->hash_values); + g_hash_table_unref(self->hash_values_display); g_hash_table_unref(self->hash_guid); g_ptr_array_unref(self->array_guids); @@ -560,9 +419,8 @@ fu_hwids_class_init(FuHwidsClass *klass) static void fu_hwids_init(FuHwids *self) { - self->hash_dmi_hw = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - self->hash_dmi_display = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - self->hash_smbios_override = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + self->hash_values = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + self->hash_values_display = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); self->hash_guid = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); self->array_guids = g_ptr_array_new_with_free_func(g_free); } diff --git a/libfwupdplugin/fu-hwids.h b/libfwupdplugin/fu-hwids.h index eb82efaf2..990931d2f 100644 --- a/libfwupdplugin/fu-hwids.h +++ b/libfwupdplugin/fu-hwids.h @@ -117,14 +117,12 @@ G_DECLARE_FINAL_TYPE(FuHwids, fu_hwids, FU, HWIDS, GObject) **/ #define FU_HWIDS_KEY_PRODUCT_SKU "ProductSku" -FuHwids * -fu_hwids_new(void); GPtrArray * fu_hwids_get_keys(FuHwids *self); const gchar * fu_hwids_get_value(FuHwids *self, const gchar *key); void -fu_hwids_add_smbios_override(FuHwids *self, const gchar *key, const gchar *value); +fu_hwids_add_value(FuHwids *self, const gchar *key, const gchar *value); const gchar * fu_hwids_get_replace_keys(FuHwids *self, const gchar *key); gchar * @@ -135,7 +133,7 @@ gchar * fu_hwids_get_guid(FuHwids *self, const gchar *keys, GError **error) G_GNUC_WARN_UNUSED_RESULT; GPtrArray * fu_hwids_get_guids(FuHwids *self); +void +fu_hwids_add_guid(FuHwids *self, const gchar *guid); gboolean fu_hwids_has_guid(FuHwids *self, const gchar *guid); -gboolean -fu_hwids_setup(FuHwids *self, FuSmbios *smbios, GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/libfwupdplugin/fu-plugin.h b/libfwupdplugin/fu-plugin.h index 03267391b..e34caeaff 100644 --- a/libfwupdplugin/fu-plugin.h +++ b/libfwupdplugin/fu-plugin.h @@ -17,7 +17,6 @@ #include "fu-context.h" #include "fu-device-locker.h" #include "fu-device.h" -#include "fu-hwids.h" #include "fu-plugin.h" #include "fu-quirks.h" #include "fu-security-attrs.h" diff --git a/libfwupdplugin/fu-self-test.c b/libfwupdplugin/fu-self-test.c index 80a12c719..35baf1522 100644 --- a/libfwupdplugin/fu-self-test.c +++ b/libfwupdplugin/fu-self-test.c @@ -416,84 +416,23 @@ fu_smbios3_func(void) } static void -fu_smbios_dt_func(void) +fu_context_hwids_dmi_func(void) { - const gchar *str; - gboolean ret; - g_autofree gchar *path = NULL; - g_autoptr(FuSmbios) smbios = NULL; + g_autoptr(FuContext) ctx = fu_context_new(); g_autoptr(GError) error = NULL; + gboolean ret; - path = g_test_build_filename(G_TEST_DIST, "tests", "devicetree", "base", NULL); - smbios = fu_smbios_new(); - ret = fu_smbios_setup_from_path(smbios, path, &error); + ret = fu_context_load_hwinfo(ctx, FU_CONTEXT_HWID_FLAG_LOAD_DMI, &error); g_assert_no_error(error); g_assert_true(ret); if (g_getenv("FWUPD_VERBOSE") != NULL) { - g_autofree gchar *dump = fu_firmware_to_string(FU_FIRMWARE(smbios)); + g_autofree gchar *dump = + fu_firmware_to_string(FU_FIRMWARE(fu_context_get_smbios(ctx))); g_debug("%s", dump); } - /* get vendor */ - str = fu_smbios_get_string(smbios, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x04, &error); - g_assert_no_error(error); - g_assert_cmpstr(str, ==, "Hughski Limited"); -} - -static void -fu_smbios_dt_fallback_func(void) -{ - const gchar *str; - gboolean ret; - g_autofree gchar *path = NULL; - g_autoptr(FuSmbios) smbios = fu_smbios_new(); - g_autoptr(GError) error = NULL; - - path = g_test_build_filename(G_TEST_DIST, "tests", "devicetree-fallback", "base", NULL); - ret = fu_smbios_setup_from_path(smbios, path, &error); - g_assert_no_error(error); - g_assert_true(ret); - if (g_getenv("FWUPD_VERBOSE") != NULL) { - g_autofree gchar *dump = fu_firmware_to_string(FU_FIRMWARE(smbios)); - g_debug("%s", dump); - } - - /* get vendor */ - str = fu_smbios_get_string(smbios, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x04, &error); - g_assert_no_error(error); - g_assert_cmpstr(str, ==, "solidrun"); - - /* get model */ - str = fu_smbios_get_string(smbios, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x05, &error); - g_assert_no_error(error); - g_assert_cmpstr(str, ==, "honeycomb"); -} - -static void -fu_smbios_class_func(void) -{ - g_autofree gchar *path = g_test_build_filename(G_TEST_DIST, "tests", "dmi", "class", NULL); - g_autoptr(FuSmbios) smbios = fu_smbios_new(); - g_autoptr(GError) error = NULL; - gboolean ret; - const gchar *str; - guint8 byte; - - ret = fu_smbios_setup_from_kernel(smbios, path, &error); - g_assert_no_error(error); - g_assert_true(ret); - if (g_getenv("FWUPD_VERBOSE") != NULL) { - g_autofree gchar *dump = fu_firmware_to_string(FU_FIRMWARE(smbios)); - g_debug("%s", dump); - } - - str = fu_smbios_get_string(smbios, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 4, &error); - g_assert_no_error(error); - g_assert_cmpstr(str, ==, "FwupdTest"); - - byte = fu_smbios_get_integer(smbios, FU_SMBIOS_STRUCTURE_TYPE_CHASSIS, 5, &error); - g_assert_no_error(error); - g_assert_cmpuint(byte, ==, 16); + g_assert_cmpstr(fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_MANUFACTURER), ==, "FwupdTest"); + g_assert_cmpuint(fu_context_get_chassis_kind(ctx), ==, 16); } static gboolean @@ -603,8 +542,7 @@ static void fu_hwids_func(void) { g_autofree gchar *testdatadir = NULL; - g_autoptr(FuHwids) hwids = NULL; - g_autoptr(FuSmbios) smbios = NULL; + g_autoptr(FuContext) context = NULL; g_autoptr(GError) error = NULL; gboolean ret; @@ -638,36 +576,42 @@ fu_hwids_func(void) testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); (void)g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE); - smbios = fu_smbios_new(); - ret = fu_smbios_setup(smbios, &error); + context = fu_context_new(); + ret = fu_context_load_hwinfo(context, FU_CONTEXT_HWID_FLAG_LOAD_SMBIOS, &error); g_assert_no_error(error); g_assert_true(ret); - hwids = fu_hwids_new(); - ret = fu_hwids_setup(hwids, smbios, &error); - g_assert_no_error(error); - g_assert_true(ret); - - g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_MANUFACTURER), ==, "LENOVO"); - g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_ENCLOSURE_KIND), ==, "a"); - g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_FAMILY), ==, "ThinkPad T440s"); - g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_PRODUCT_NAME), ==, "20ARS19C0C"); - g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_BIOS_VENDOR), ==, "LENOVO"); - g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_BIOS_VERSION), + g_assert_cmpstr(fu_context_get_hwid_value(context, FU_HWIDS_KEY_MANUFACTURER), + ==, + "LENOVO"); + g_assert_cmpstr(fu_context_get_hwid_value(context, FU_HWIDS_KEY_ENCLOSURE_KIND), ==, "a"); + g_assert_cmpstr(fu_context_get_hwid_value(context, FU_HWIDS_KEY_FAMILY), + ==, + "ThinkPad T440s"); + g_assert_cmpstr(fu_context_get_hwid_value(context, FU_HWIDS_KEY_PRODUCT_NAME), + ==, + "20ARS19C0C"); + g_assert_cmpstr(fu_context_get_hwid_value(context, FU_HWIDS_KEY_BIOS_VENDOR), ==, "LENOVO"); + g_assert_cmpstr(fu_context_get_hwid_value(context, FU_HWIDS_KEY_BIOS_VERSION), ==, "GJET75WW (2.25 )"); - g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_BIOS_MAJOR_RELEASE), ==, "02"); - g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_BIOS_MINOR_RELEASE), ==, "19"); - g_assert_cmpstr(fu_hwids_get_value(hwids, FU_HWIDS_KEY_PRODUCT_SKU), + g_assert_cmpstr(fu_context_get_hwid_value(context, FU_HWIDS_KEY_BIOS_MAJOR_RELEASE), + ==, + "02"); + g_assert_cmpstr(fu_context_get_hwid_value(context, FU_HWIDS_KEY_BIOS_MINOR_RELEASE), + ==, + "19"); + g_assert_cmpstr(fu_context_get_hwid_value(context, FU_HWIDS_KEY_PRODUCT_SKU), ==, "LENOVO_MT_20AR_BU_Think_FM_ThinkPad T440s"); for (guint i = 0; guids[i].key != NULL; i++) { + FuHwids *hwids = fu_context_get_hwids(context); g_autofree gchar *guid = fu_hwids_get_guid(hwids, guids[i].key, &error); g_assert_no_error(error); g_assert_cmpstr(guid, ==, guids[i].value); } for (guint i = 0; guids[i].key != NULL; i++) - g_assert_true(fu_hwids_has_guid(hwids, guids[i].value)); + g_assert_true(fu_context_has_hwid_guid(context, guids[i].value)); } static void @@ -3800,6 +3744,7 @@ main(int argc, char **argv) (void)g_setenv("FWUPD_LIBDIR_PKG", testdatadir, TRUE); (void)g_setenv("FWUPD_SYSCONFDIR", testdatadir, TRUE); (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_SYSFSDMIDIR", testdatadir, TRUE); (void)g_setenv("FWUPD_LOCALSTATEDIR", testdatadir, TRUE); (void)g_setenv("FWUPD_OFFLINE_TRIGGER", "/tmp/fwupd-self-test/system-update", TRUE); (void)g_setenv("FWUPD_LOCALSTATEDIR", "/tmp/fwupd-self-test/var", TRUE); @@ -3845,11 +3790,9 @@ main(int argc, char **argv) g_test_add_func("/fwupd/common{strsafe}", fu_strsafe_func); g_test_add_func("/fwupd/efivar", fu_efivar_func); g_test_add_func("/fwupd/hwids", fu_hwids_func); + g_test_add_func("/fwupd/context{hwids-dmi}", fu_context_hwids_dmi_func); g_test_add_func("/fwupd/smbios", fu_smbios_func); g_test_add_func("/fwupd/smbios3", fu_smbios3_func); - g_test_add_func("/fwupd/smbios{dt}", fu_smbios_dt_func); - g_test_add_func("/fwupd/smbios{dt-fallback}", fu_smbios_dt_fallback_func); - g_test_add_func("/fwupd/smbios{class}", fu_smbios_class_func); g_test_add_func("/fwupd/firmware", fu_firmware_func); g_test_add_func("/fwupd/firmware{common}", fu_firmware_common_func); g_test_add_func("/fwupd/firmware{archive}", fu_firmware_archive_func); diff --git a/libfwupdplugin/fu-smbios-private.h b/libfwupdplugin/fu-smbios-private.h index 8e5cb2488..cd42ae052 100644 --- a/libfwupdplugin/fu-smbios-private.h +++ b/libfwupdplugin/fu-smbios-private.h @@ -18,7 +18,3 @@ gboolean fu_smbios_setup_from_file(FuSmbios *self, const gchar *filename, GError **error) G_GNUC_WARN_UNUSED_RESULT; -gboolean -fu_smbios_setup_from_kernel(FuSmbios *self, - const gchar *path, - GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/libfwupdplugin/fu-smbios.c b/libfwupdplugin/fu-smbios.c index f387aaeb3..229b3c7ca 100644 --- a/libfwupdplugin/fu-smbios.c +++ b/libfwupdplugin/fu-smbios.c @@ -20,7 +20,6 @@ #include "fu-byte-array.h" #include "fu-common.h" -#include "fu-kenv.h" #include "fu-mem.h" #include "fu-path.h" #include "fu-smbios-private.h" @@ -29,7 +28,7 @@ /** * FuSmbios: * - * Enumerate the SMBIOS data on the system, either using DMI or Device Tree. + * Enumerate the SMBIOS data on the system. * * See also: [class@FuHwids] */ @@ -81,274 +80,6 @@ typedef struct { G_DEFINE_TYPE(FuSmbios, fu_smbios, FU_TYPE_FIRMWARE) -static void -fu_smbios_set_integer(FuSmbios *self, guint8 type, guint8 offset, guint8 value) -{ - FuSmbiosItem *item = g_ptr_array_index(self->items, type); - for (guint i = item->buf->len; i < (guint)offset + 1; i++) - fu_byte_array_append_uint8(item->buf, 0x0); - item->buf->data[offset] = value; -} - -static void -fu_smbios_set_string(FuSmbios *self, guint8 type, guint8 offset, const gchar *buf, gssize bufsz) -{ - FuSmbiosItem *item = g_ptr_array_index(self->items, type); - - /* NUL terminated UTF-8 */ - if (bufsz < 0) - bufsz = strlen(buf); - - /* add value to string table */ - g_ptr_array_add(item->strings, g_strndup(buf, (gsize)bufsz)); - fu_smbios_set_integer(self, type, offset, item->strings->len); -} - -static gboolean -fu_smbios_convert_dt_string(FuSmbios *self, - guint8 type, - guint8 offset, - const gchar *path, - const gchar *subpath) -{ - gsize bufsz = 0; - g_autofree gchar *fn = g_build_filename(path, subpath, NULL); - g_autofree gchar *buf = NULL; - - /* not found */ - if (!g_file_get_contents(fn, &buf, &bufsz, NULL)) - return FALSE; - if (bufsz == 0) - return FALSE; - fu_smbios_set_string(self, type, offset, buf, (gssize)bufsz); - return TRUE; -} - -static gchar ** -fu_smbios_convert_dt_string_array(FuSmbios *self, const gchar *path, const gchar *subpath) -{ - gsize bufsz = 0; - g_autofree gchar *fn = g_build_filename(path, subpath, NULL); - g_autofree gchar *buf = NULL; - g_auto(GStrv) split = NULL; - - /* not found */ - if (!g_file_get_contents(fn, &buf, &bufsz, NULL)) - return NULL; - if (bufsz == 0) - return NULL; - - /* return only if valid */ - split = g_strsplit(buf, ",", -1); - if (g_strv_length(split) == 0) - return NULL; - - /* success */ - return g_steal_pointer(&split); -} - -#ifdef HAVE_KENV_H - -static gboolean -fu_smbios_convert_kenv_string(FuSmbios *self, - guint8 type, - guint8 offset, - const gchar *sminfo, - GError **error) -{ - g_autofree gchar *value = fu_kenv_get_string(sminfo, error); - if (value == NULL) - return FALSE; - fu_smbios_set_string(self, type, offset, value, -1); - return TRUE; -} - -static gboolean -fu_smbios_setup_from_kenv(FuSmbios *self, GError **error) -{ - gboolean is_valid = FALSE; - g_autoptr(GError) error_local = NULL; - - /* add all four faked structures */ - for (guint i = 0; i < FU_SMBIOS_STRUCTURE_TYPE_LAST; i++) { - FuSmbiosItem *item = g_new0(FuSmbiosItem, 1); - item->type = i; - item->buf = g_byte_array_new(); - item->strings = g_ptr_array_new_with_free_func(g_free); - g_ptr_array_add(self->items, item); - } - - /* DMI:Manufacturer */ - if (!fu_smbios_convert_kenv_string(self, - FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, - 0x04, - "smbios.bios.vendor", - &error_local)) { - g_debug("ignoring: %s", error_local->message); - g_clear_error(&error_local); - } else { - is_valid = TRUE; - } - - /* DMI:BiosVersion */ - if (!fu_smbios_convert_kenv_string(self, - FU_SMBIOS_STRUCTURE_TYPE_BIOS, - 0x05, - "smbios.bios.version", - &error_local)) { - g_debug("ignoring: %s", error_local->message); - g_clear_error(&error_local); - } else { - is_valid = TRUE; - } - - /* DMI:Family */ - if (!fu_smbios_convert_kenv_string(self, - FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, - 0x1a, - "smbios.system.family", - &error_local)) { - g_debug("ignoring: %s", error_local->message); - g_clear_error(&error_local); - } else { - is_valid = TRUE; - } - - /* DMI:ProductName */ - if (!fu_smbios_convert_kenv_string(self, - FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, - 0x05, - "smbios.planar.product", - &error_local)) { - g_debug("ignoring: %s", error_local->message); - g_clear_error(&error_local); - } else { - is_valid = TRUE; - } - - /* DMI:BaseboardManufacturer */ - if (!fu_smbios_convert_kenv_string(self, - FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, - 0x04, - "smbios.planar.maker", - &error_local)) { - g_debug("ignoring: %s", error_local->message); - g_clear_error(&error_local); - } else { - is_valid = TRUE; - } - - /* we got no data */ - if (!is_valid) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_READ, "no SMBIOS information provided"); - return FALSE; - } - - /* success */ - return TRUE; -} -#endif - -static gboolean -fu_smbios_setup_from_path_dt(FuSmbios *self, const gchar *path, GError **error) -{ - gboolean has_family; - gboolean has_model; - gboolean has_vendor; - g_autofree gchar *fn_battery = NULL; - - /* add all four faked structures */ - for (guint i = 0; i < FU_SMBIOS_STRUCTURE_TYPE_LAST; i++) { - FuSmbiosItem *item = g_new0(FuSmbiosItem, 1); - item->type = i; - item->buf = g_byte_array_new(); - item->strings = g_ptr_array_new_with_free_func(g_free); - g_ptr_array_add(self->items, item); - } - - /* if it has a battery it is portable (probably a laptop) */ - fn_battery = g_build_filename(path, "battery", NULL); - if (g_file_test(fn_battery, G_FILE_TEST_EXISTS)) { - fu_smbios_set_integer(self, - FU_SMBIOS_STRUCTURE_TYPE_CHASSIS, - 0x05, - FU_SMBIOS_CHASSIS_KIND_PORTABLE); - } - - /* DMI:Manufacturer */ - has_vendor = fu_smbios_convert_dt_string(self, - FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, - 0x04, - path, - "vendor"); - - /* DMI:Family */ - has_family = fu_smbios_convert_dt_string(self, - FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, - 0x1a, - path, - "model-name"); - - /* DMI:ProductName */ - has_model = - fu_smbios_convert_dt_string(self, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x05, path, "model"); - - /* fall back to the first compatible string if required */ - if (!has_vendor || !has_model || !has_family) { - g_auto(GStrv) parts = NULL; - - /* NULL if invalid, otherwise we're sure this has size of exactly 3 */ - parts = fu_smbios_convert_dt_string_array(self, path, "compatible"); - if (parts != NULL) { - if (!has_vendor && g_strv_length(parts) > 0) { - fu_smbios_set_string(self, - FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, - 0x4, - parts[0], - -1); - } - if (!has_model && g_strv_length(parts) > 1) { - fu_smbios_set_string(self, - FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, - 0x05, - parts[1], - -1); - } - if (!has_family && g_strv_length(parts) > 2) { - fu_smbios_set_string(self, - FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, - 0x1a, - parts[2], - -1); - } - } - } - - /* DMI:BiosVersion */ - fu_smbios_convert_dt_string(self, - FU_SMBIOS_STRUCTURE_TYPE_BIOS, - 0x05, - path, - "ibm,firmware-versions/version"); - - /* DMI:BaseboardManufacturer */ - fu_smbios_convert_dt_string(self, - FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, - 0x04, - path, - "vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor"); - - /* DMI:BaseboardProduct */ - fu_smbios_convert_dt_string( - self, - FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, - 0x05, - path, - "vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number"); - - return TRUE; -} - static gboolean fu_smbios_setup_from_data(FuSmbios *self, const guint8 *buf, gsize sz, GError **error) { @@ -425,172 +156,17 @@ fu_smbios_setup_from_file(FuSmbios *self, const gchar *filename, GError **error) { gsize sz = 0; g_autofree gchar *buf = NULL; - g_autofree gchar *basename = NULL; g_return_val_if_fail(FU_IS_SMBIOS(self), FALSE); g_return_val_if_fail(filename != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - /* use a heuristic */ - basename = g_path_get_basename(filename); - if (g_strcmp0(basename, "base") == 0) - return fu_smbios_setup_from_path_dt(self, filename, error); - /* DMI blob */ if (!g_file_get_contents(filename, &buf, &sz, error)) return FALSE; return fu_smbios_setup_from_data(self, (guint8 *)buf, sz, error); } -static gboolean -fu_smbios_encode_string_from_kernel(FuSmbios *self, - const gchar *file_contents, - guint8 type, - guint8 offset, - GError **error) -{ - fu_smbios_set_string(self, type, offset, file_contents, -1); - return TRUE; -} - -static gboolean -fu_smbios_encode_byte_from_kernel(FuSmbios *self, - const gchar *file_contents, - guint8 type, - guint8 offset, - GError **error) -{ - gchar *endp; - gint64 value = g_ascii_strtoll(file_contents, &endp, 10); - - if (*endp != 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "non-numeric values in numeric string: %s", - endp); - return FALSE; - } - if (value < 0 || value > G_MAXUINT8) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "value \"%s\" is not representable in a byte", - file_contents); - return FALSE; - } - - fu_smbios_set_integer(self, type, offset, value); - return TRUE; -} - -/* - * The mapping from SMBIOS field to sysfs name can be found by mapping - * the field to a kernel property name in dmi_decode() - * (drivers/firmware/dmi_scan.c), then the property name to sysfs entry - * in dmi_id_init_attr_table() (drivers/firmware/dmi-id.c). This table - * lists each attribute exposed in /sys/class/dmi when CONFIG_DMIID is - * enabled, mapping to the SMBIOS field and a function that can convert - * the textual version of the field back into the raw SMBIOS table - * representation. - */ -#define SYSFS_DMI_FIELD(_name, _type, offset_ignored, kind) \ - { \ - .name = _name, .type = _type, .offset = offset_ignored, \ - .encode = fu_smbios_encode_##kind##_from_kernel \ - } -const struct kernel_dmi_field { - const gchar *name; - gboolean (*encode)(FuSmbios *, const gchar *, guint8, guint8, GError **); - guint8 type; - guint8 offset; -} KERNEL_DMI_FIELDS[] = { - SYSFS_DMI_FIELD("bios_vendor", 0, 4, string), - SYSFS_DMI_FIELD("bios_version", 0, 5, string), - SYSFS_DMI_FIELD("bios_date", 0, 8, string), - SYSFS_DMI_FIELD("sys_vendor", 1, 4, string), - SYSFS_DMI_FIELD("product_name", 1, 5, string), - SYSFS_DMI_FIELD("product_version", 1, 6, string), - SYSFS_DMI_FIELD("product_serial", 1, 7, string), - /* SYSFS_DMI_FIELD("product_uuid", 1, 8, uuid) */ - SYSFS_DMI_FIELD("product_family", 1, 26, string), - SYSFS_DMI_FIELD("product_sku", 1, 25, string), - SYSFS_DMI_FIELD("board_vendor", 2, 4, string), - SYSFS_DMI_FIELD("board_name", 2, 5, string), - SYSFS_DMI_FIELD("board_version", 2, 6, string), - SYSFS_DMI_FIELD("board_serial", 2, 7, string), - SYSFS_DMI_FIELD("board_asset_tag", 2, 8, string), - SYSFS_DMI_FIELD("chassis_vendor", 3, 4, string), - SYSFS_DMI_FIELD("chassis_type", 3, 5, byte), - SYSFS_DMI_FIELD("chassis_version", 3, 6, string), - SYSFS_DMI_FIELD("chassis_serial", 3, 7, string), - SYSFS_DMI_FIELD("chassis_asset_tag", 3, 8, string), -}; - -/** - * fu_smbios_setup_from_kernel: - * @self: a #FuSmbios - * @path: a directory path - * @error: (nullable): optional return location for an error - * - * Reads SMBIOS value from DMI values provided by the kernel, such as in - * /sys/class/dmi on Linux. - * - * Returns: %TRUE for success - * - * Since: 1.6.2 - **/ -gboolean -fu_smbios_setup_from_kernel(FuSmbios *self, const gchar *path, GError **error) -{ - gboolean any_success = FALSE; - - /* add fake structures */ - for (guint i = 0; i < FU_SMBIOS_STRUCTURE_TYPE_LAST; i++) { - FuSmbiosItem *item = g_new0(FuSmbiosItem, 1); - item->type = i; - item->buf = g_byte_array_new(); - item->strings = g_ptr_array_new_with_free_func(g_free); - g_ptr_array_add(self->items, item); - } - - /* parse every known field from the corresponding file */ - for (gsize i = 0; i < G_N_ELEMENTS(KERNEL_DMI_FIELDS); i++) { - const struct kernel_dmi_field *field = &KERNEL_DMI_FIELDS[i]; - gsize bufsz = 0; - g_autofree gchar *buf = NULL; - g_autofree gchar *fn = g_build_filename(path, field->name, NULL); - g_autoptr(GError) local_error = NULL; - - if (!g_file_get_contents(fn, &buf, &bufsz, &local_error)) { - g_debug("unable to read SMBIOS data from %s: %s", fn, local_error->message); - continue; - } - - /* trim trailing newline added by kernel */ - if (buf[bufsz - 1] == '\n') - buf[bufsz - 1] = 0; - - if (!field->encode(self, buf, field->type, field->offset, &local_error)) { - g_warning("failed to parse SMBIOS data from %s: %s", - fn, - local_error->message); - continue; - } - - any_success = TRUE; - } - if (!any_success) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to read any SMBIOS values from %s", - path); - return FALSE; - } - return TRUE; -} - static gboolean fu_smbios_parse_ep32(FuSmbios *self, const gchar *buf, gsize sz, GError **error) { @@ -685,8 +261,20 @@ fu_smbios_parse_ep64(FuSmbios *self, const gchar *buf, gsize sz, GError **error) return TRUE; } -static gboolean -fu_smbios_setup_from_path_dmi(FuSmbios *self, const gchar *path, GError **error) +/** + * fu_smbios_setup_from_path: + * @self: a #FuSmbios + * @path: a path, e.g. `/sys/firmware/dmi/tables` + * @error: (nullable): optional return location for an error + * + * Reads all the SMBIOS values from a specific path. + * + * Returns: %TRUE for success + * + * Since: 1.0.0 + **/ +gboolean +fu_smbios_setup_from_path(FuSmbios *self, const gchar *path, GError **error) { gsize sz = 0; g_autofree gchar *dmi_fn = NULL; @@ -695,6 +283,8 @@ fu_smbios_setup_from_path_dmi(FuSmbios *self, const gchar *path, GError **error) g_autofree gchar *ep_raw = NULL; g_return_val_if_fail(FU_IS_SMBIOS(self), FALSE); + g_return_val_if_fail(path != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); /* get the smbios entry point */ ep_fn = g_build_filename(path, "smbios_entry_point", NULL); @@ -763,34 +353,6 @@ fu_smbios_parse(FuFirmware *firmware, return fu_smbios_setup_from_data(self, buf, bufsz, error); } -/** - * fu_smbios_setup_from_path: - * @self: a #FuSmbios - * @path: a path, e.g. `/sys/firmware/dmi/tables` - * @error: (nullable): optional return location for an error - * - * Reads all the SMBIOS values from a specific path. - * - * Returns: %TRUE for success - * - * Since: 1.0.0 - **/ -gboolean -fu_smbios_setup_from_path(FuSmbios *self, const gchar *path, GError **error) -{ - g_autofree gchar *basename = NULL; - - g_return_val_if_fail(FU_IS_SMBIOS(self), FALSE); - g_return_val_if_fail(path != NULL, FALSE); - g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - - /* use a heuristic */ - basename = g_path_get_basename(path); - if (g_strcmp0(basename, "base") == 0) - return fu_smbios_setup_from_path_dt(self, path, error); - return fu_smbios_setup_from_path_dmi(self, path, error); -} - #ifdef _WIN32 #define FU_SMBIOS_FT_SIG_ACPI 0x41435049 #define FU_SMBIOS_FT_SIG_FIRM 0x4649524D @@ -850,51 +412,33 @@ fu_smbios_setup(FuSmbios *self, GError **error) error); #else g_autofree gchar *path = NULL; - g_autofree gchar *path_dt = NULL; g_autofree gchar *sysfsfwdir = NULL; - const gchar *path_dmi_class = "/sys/class/dmi/id"; + g_autoptr(GError) error_local = NULL; g_return_val_if_fail(FU_IS_SMBIOS(self), FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); - /* DMI */ + sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); path = g_build_filename(sysfsfwdir, "dmi", "tables", NULL); - if (g_file_test(path, G_FILE_TEST_EXISTS)) { - g_autoptr(GError) error_local = NULL; - if (!fu_smbios_setup_from_path(self, path, &error_local)) { - if (!g_error_matches(error_local, G_FILE_ERROR, G_FILE_ERROR_ACCES)) { - g_propagate_error(error, g_steal_pointer(&error_local)); - return FALSE; - } - g_debug("ignoring %s", error_local->message); - } else - return TRUE; - } - - /* the values the kernel parsed; these are world-readable */ - if (g_file_test(path_dmi_class, G_FILE_TEST_IS_DIR)) { - g_debug("trying to read %s", path_dmi_class); - return fu_smbios_setup_from_kernel(self, path_dmi_class, error); - } - - /* DT */ - path_dt = g_build_filename(sysfsfwdir, "devicetree", "base", NULL); - if (g_file_test(path_dt, G_FILE_TEST_EXISTS)) - return fu_smbios_setup_from_path(self, path_dt, error); - -#ifdef HAVE_KENV_H - /* kenv */ - return fu_smbios_setup_from_kenv(self, error); -#endif - - /* neither found */ - g_set_error_literal(error, + if (!g_file_test(path, G_FILE_TEST_EXISTS)) { + g_set_error(error, FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "neither SMBIOS or DT found"); - return FALSE; + FWUPD_ERROR_NOT_SUPPORTED, + "SMBIOS tables not found at %s", + path); + return FALSE; + } + if (!fu_smbios_setup_from_path(self, path, &error_local)) { + if (!g_error_matches(error_local, G_FILE_ERROR, G_FILE_ERROR_ACCES)) { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + g_debug("ignoring %s", error_local->message); + } + + /* success */ + return TRUE; #endif } diff --git a/libfwupdplugin/fwupdplugin.h b/libfwupdplugin/fwupdplugin.h index 6932122ae..721810d3e 100644 --- a/libfwupdplugin/fwupdplugin.h +++ b/libfwupdplugin/fwupdplugin.h @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include diff --git a/libfwupdplugin/fwupdplugin.map b/libfwupdplugin/fwupdplugin.map index d3f26d893..d3682ff3e 100644 --- a/libfwupdplugin/fwupdplugin.map +++ b/libfwupdplugin/fwupdplugin.map @@ -63,8 +63,6 @@ LIBFWUPDPLUGIN_0.9.3 { fu_hwids_get_type; fu_hwids_get_value; fu_hwids_has_guid; - fu_hwids_new; - fu_hwids_setup; local: *; } LIBFWUPDPLUGIN_0.8.0; @@ -540,7 +538,6 @@ LIBFWUPDPLUGIN_1.5.6 { fu_firmware_strparse_uint32_safe; fu_firmware_strparse_uint4_safe; fu_firmware_strparse_uint8_safe; - fu_hwids_add_smbios_override; fu_hwids_get_keys; fu_plugin_get_devices; fu_plugin_runner_backend_device_added; @@ -719,7 +716,6 @@ LIBFWUPDPLUGIN_1.6.2 { fu_ifd_region_to_name; fu_ifd_region_to_string; fu_plugin_add_udev_subsystem; - fu_smbios_setup_from_kernel; fu_udev_device_get_children_with_subsystem; fu_udev_device_set_dev; local: *; @@ -1159,6 +1155,8 @@ LIBFWUPDPLUGIN_1.8.10 { fu_context_get_smbios; fu_context_load_hwinfo; fu_context_set_chassis_kind; + fu_hwids_add_guid; + fu_hwids_add_value; fu_pefile_firmware_get_type; fu_pefile_firmware_new; local: *; diff --git a/libfwupdplugin/meson.build b/libfwupdplugin/meson.build index 4c8152388..ac8c15ea5 100644 --- a/libfwupdplugin/meson.build +++ b/libfwupdplugin/meson.build @@ -48,6 +48,11 @@ fwupdplugin_src = [ 'fu-dfuse-firmware.c', # fuzzing 'fu-fmap-firmware.c', # fuzzing 'fu-hwids.c', # fuzzing + 'fu-hwids-config.c', # fuzzing + 'fu-hwids-dmi.c', # fuzzing + 'fu-hwids-fdt.c', # fuzzing + 'fu-hwids-kenv.c', # fuzzing + 'fu-hwids-smbios.c', # fuzzing 'fu-ihex-firmware.c', # fuzzing 'fu-io-channel.c', # fuzzing 'fu-plugin.c', @@ -363,22 +368,22 @@ if get_option('tests') test('fwupdplugin-self-test', e, is_parallel: false, timeout: 180, env: env) install_data([ - 'tests/dmi/class/chassis_type', - 'tests/dmi/class/sys_vendor', + 'tests/chassis_type', + 'tests/sys_vendor', ], - install_dir: join_paths(installed_test_datadir, 'dmi/class'), + install_dir: installed_test_datadir, ) install_data([ 'tests/dmi/tables/DMI', 'tests/dmi/tables/smbios_entry_point', ], - install_dir: join_paths(installed_test_datadir, 'dmi/tables'), + install_dir: join_paths(installed_test_datadir, 'tests/dmi/tables'), ) install_data([ 'tests/dmi/tables64/DMI', 'tests/dmi/tables64/smbios_entry_point', ], - install_dir: join_paths(installed_test_datadir, 'dmi/tables64'), + install_dir: join_paths(installed_test_datadir, 'tests/dmi/tables64'), ) endif diff --git a/libfwupdplugin/tests/dmi/class/chassis_type b/libfwupdplugin/tests/chassis_type similarity index 100% rename from libfwupdplugin/tests/dmi/class/chassis_type rename to libfwupdplugin/tests/chassis_type diff --git a/libfwupdplugin/tests/devicetree-fallback/base/compatible b/libfwupdplugin/tests/devicetree-fallback/base/compatible deleted file mode 120000 index abcb73dbb..000000000 --- a/libfwupdplugin/tests/devicetree-fallback/base/compatible +++ /dev/null @@ -1 +0,0 @@ -../../devicetree/base/compatible \ No newline at end of file diff --git a/libfwupdplugin/tests/devicetree/base/compatible b/libfwupdplugin/tests/devicetree/base/compatible deleted file mode 100644 index 33529460d..000000000 Binary files a/libfwupdplugin/tests/devicetree/base/compatible and /dev/null differ diff --git a/libfwupdplugin/tests/devicetree/base/ibm,firmware-versions/version b/libfwupdplugin/tests/devicetree/base/ibm,firmware-versions/version deleted file mode 100644 index 94b2bd66d..000000000 --- a/libfwupdplugin/tests/devicetree/base/ibm,firmware-versions/version +++ /dev/null @@ -1 +0,0 @@ -1.2.3-4 \ No newline at end of file diff --git a/libfwupdplugin/tests/devicetree/base/model b/libfwupdplugin/tests/devicetree/base/model deleted file mode 100644 index 86c8a128b..000000000 --- a/libfwupdplugin/tests/devicetree/base/model +++ /dev/null @@ -1 +0,0 @@ -ColorHug \ No newline at end of file diff --git a/libfwupdplugin/tests/devicetree/base/model-name b/libfwupdplugin/tests/devicetree/base/model-name deleted file mode 100644 index 9d37fc1ac..000000000 --- a/libfwupdplugin/tests/devicetree/base/model-name +++ /dev/null @@ -1 +0,0 @@ -To Be Filled By O.E.M. \ No newline at end of file diff --git a/libfwupdplugin/tests/devicetree/base/name b/libfwupdplugin/tests/devicetree/base/name deleted file mode 100644 index f76dd238a..000000000 Binary files a/libfwupdplugin/tests/devicetree/base/name and /dev/null differ diff --git a/libfwupdplugin/tests/devicetree/base/vendor b/libfwupdplugin/tests/devicetree/base/vendor deleted file mode 100644 index 1555d1a5c..000000000 --- a/libfwupdplugin/tests/devicetree/base/vendor +++ /dev/null @@ -1 +0,0 @@ -Hughski Limited \ No newline at end of file diff --git a/libfwupdplugin/tests/devicetree/base/vpd/name b/libfwupdplugin/tests/devicetree/base/vpd/name deleted file mode 100644 index b8acbc9ee..000000000 Binary files a/libfwupdplugin/tests/devicetree/base/vpd/name and /dev/null differ diff --git a/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/name b/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/name deleted file mode 100644 index 1f22cb8d6..000000000 Binary files a/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/name and /dev/null differ diff --git a/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number b/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number deleted file mode 100644 index e00c9e144..000000000 --- a/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number +++ /dev/null @@ -1 +0,0 @@ -PCB-CH001 \ No newline at end of file diff --git a/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor b/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor deleted file mode 100644 index ffbb9e015..000000000 --- a/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor +++ /dev/null @@ -1 +0,0 @@ -Richard Hughes \ No newline at end of file diff --git a/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/name b/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/name deleted file mode 100644 index 88600030b..000000000 Binary files a/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/name and /dev/null differ diff --git a/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/name b/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/name deleted file mode 100644 index c6f7bd989..000000000 Binary files a/libfwupdplugin/tests/devicetree/base/vpd/root-node-vpd@a000/name and /dev/null differ diff --git a/libfwupdplugin/tests/dmi/class/sys_vendor b/libfwupdplugin/tests/sys_vendor similarity index 100% rename from libfwupdplugin/tests/dmi/class/sys_vendor rename to libfwupdplugin/tests/sys_vendor diff --git a/src/fu-tool.c b/src/fu-tool.c index 8271984f1..9437571ca 100644 --- a/src/fu-tool.c +++ b/src/fu-tool.c @@ -34,7 +34,6 @@ #include "fu-device-private.h" #include "fu-engine.h" #include "fu-history.h" -#include "fu-hwids.h" #include "fu-plugin-private.h" #include "fu-progressbar.h" #include "fu-security-attr-common.h" @@ -1974,8 +1973,8 @@ fu_util_activate(FuUtilPrivate *priv, gchar **values, GError **error) static gboolean fu_util_export_hwids(FuUtilPrivate *priv, gchar **values, GError **error) { - g_autoptr(FuHwids) hwids = fu_hwids_new(); - g_autoptr(FuSmbios) smbios = fu_smbios_new(); + FuContext *ctx = fu_engine_get_context(priv->engine); + FuHwids *hwids = fu_context_get_hwids(ctx); g_autoptr(GKeyFile) kf = g_key_file_new(); g_autoptr(GPtrArray) hwid_keys = NULL; @@ -1989,9 +1988,7 @@ fu_util_export_hwids(FuUtilPrivate *priv, gchar **values, GError **error) } /* setup default hwids */ - if (!fu_smbios_setup(smbios, error)) - return FALSE; - if (!fu_hwids_setup(hwids, smbios, error)) + if (!fu_context_load_hwinfo(ctx, FU_CONTEXT_HWID_FLAG_LOAD_ALL, error)) return FALSE; /* save all keys */ @@ -2009,39 +2006,23 @@ fu_util_export_hwids(FuUtilPrivate *priv, gchar **values, GError **error) static gboolean fu_util_hwids(FuUtilPrivate *priv, gchar **values, GError **error) { - g_autoptr(FuSmbios) smbios = NULL; - g_autoptr(FuHwids) hwids = fu_hwids_new(); + FuContext *ctx = fu_engine_get_context(priv->engine); + FuHwids *hwids = fu_context_get_hwids(ctx); g_autoptr(GPtrArray) hwid_keys = fu_hwids_get_keys(hwids); - /* read DMI data */ - if (g_strv_length(values) == 0) { - smbios = fu_smbios_new(); - if (!fu_smbios_setup(smbios, error)) - return FALSE; - } else if (g_strv_length(values) == 1) { - /* a keyfile with overrides */ + /* a keyfile with overrides */ + if (g_strv_length(values) == 1) { g_autoptr(GKeyFile) kf = g_key_file_new(); - if (g_key_file_load_from_file(kf, values[0], G_KEY_FILE_NONE, NULL)) { - for (guint i = 0; i < hwid_keys->len; i++) { - const gchar *hwid_key = g_ptr_array_index(hwid_keys, i); - g_autofree gchar *tmp = NULL; - tmp = g_key_file_get_string(kf, "HwIds", hwid_key, NULL); - fu_hwids_add_smbios_override(hwids, hwid_key, tmp); - } - /* a DMI blob */ - } else { - smbios = fu_smbios_new(); - if (!fu_smbios_setup_from_file(smbios, values[0], error)) - return FALSE; + if (!g_key_file_load_from_file(kf, values[0], G_KEY_FILE_NONE, error)) + return FALSE; + for (guint i = 0; i < hwid_keys->len; i++) { + const gchar *hwid_key = g_ptr_array_index(hwid_keys, i); + g_autofree gchar *tmp = NULL; + tmp = g_key_file_get_string(kf, "HwIds", hwid_key, NULL); + fu_hwids_add_value(hwids, hwid_key, tmp); } - } else { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_ARGS, - "Invalid arguments"); - return FALSE; } - if (!fu_hwids_setup(hwids, smbios, error)) + if (!fu_context_load_hwinfo(ctx, FU_CONTEXT_HWID_FLAG_LOAD_ALL, error)) return FALSE; /* show debug output */