Refactor the hwids functionality

This refactors the code as it was getting very confusing; before FuSmbios was
reading both SMBIOS and the kernel-provided DT -- and various things were
injecting overrides in three different place. To properly support FDT remove
one layer of indirection.

This also lets us use the compatible strings to enable plugins specifying the
flag _REQUIRE_HWID -- which means we only load the plugin if it's got a chance
of working. e.g.

    [aspeed,ast2500]
This commit is contained in:
Richard Hughes 2023-01-16 15:42:55 +00:00
parent 72f5c1e01e
commit 2b0f92506b
36 changed files with 709 additions and 849 deletions

View File

@ -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

View File

@ -46,6 +46,7 @@ content_files = [
"tutorial.md",
"hsi.md",
"ds20.md",
"hwids.md",
"bios-settings.md",
@plugin_readme_outputs@,
]

85
docs/hwids.md Normal file
View File

@ -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.

View File

@ -45,6 +45,9 @@
<ul>
<li><a href="libfwupdplugin/tutorial.html">Plugin tutorial</a></li>
</ul>
<ul>
<li><a href="libfwupdplugin/hwids.html">Hardware IDs</a></li>
</ul>
<ul>
<li><a href="libfwupdplugin/env.html">Environment variables</a></li>
</ul>

View File

@ -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);

View File

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

View File

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

View File

@ -0,0 +1,73 @@
/*
* Copyright (C) 2023 Richard Hughes <richard@hughsie.com>
*
* 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;
}

View File

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

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2023 Richard Hughes <richard@hughsie.com>
*
* 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);

View File

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

View File

@ -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);
}

View File

@ -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;

View File

@ -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"

View File

@ -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);

View File

@ -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;

View File

@ -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
}

View File

@ -45,6 +45,7 @@
#include <libfwupdplugin/fu-fit-firmware.h>
#include <libfwupdplugin/fu-fmap-firmware.h>
#include <libfwupdplugin/fu-hid-device.h>
#include <libfwupdplugin/fu-hwids.h>
#include <libfwupdplugin/fu-i2c-device.h>
#include <libfwupdplugin/fu-ifd-bios.h>
#include <libfwupdplugin/fu-ifd-firmware.h>

View File

@ -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: *;

View File

@ -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

View File

@ -1 +0,0 @@
../../devicetree/base/compatible

View File

@ -1 +0,0 @@
ColorHug

View File

@ -1 +0,0 @@
To Be Filled By O.E.M.

View File

@ -1 +0,0 @@
Hughski Limited

View File

@ -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 */