Release fwupd 1.8.10

-----BEGIN PGP SIGNATURE-----
 
 iQFIBAABCAAyFiEEFj61ARkiXbPfj0nqF6y6jfqXDhcFAmPOXskUHHJpY2hhcmRA
 aHVnaHNpZS5jb20ACgkQF6y6jfqXDhdyBAf/YXQ8w8giq39B2Bn1AnMhjQ1Raox7
 4+dgbabmHY8yP1zEDk7g2SkkXP19Ur76pYfJHg9nlo4zuGj5QlxgHwTfIeDrJE+O
 pZVytRS1sKZpSJOzGXRsKbJHEr7scNap02wlSqoyzku3X1lfv8B2oKQT5aMFLxPH
 E6eFN4v3V1XGeAybpTTu4UNJB3mWAZoq2oeyeavxehkznCLh9oPW+HURjtO6T1nO
 RxFQC2Nu7wOc/IwE5h77OvsycC0g9VAjPFmQgP8MqT2lP3MkeIWpXH0cPBvjw+97
 TvAZADAa2VEIJPVkFsIUCHxCXC2/Lo4WlrYXvW1/zWf97Kp+BtzGi9xg4Q==
 =nLs4
 -----END PGP SIGNATURE-----

Merge tag '1.8.10' into debian

Release fwupd 1.8.10
This commit is contained in:
Mario Limonciello 2023-01-23 08:18:27 -06:00
commit 0a1f339298
207 changed files with 3972 additions and 1504 deletions

View File

@ -10,7 +10,7 @@ To make sure it's done right, you can reference commit 433e809318c68c9ab6d4ae50e
Write release entries:
git log --format="%s" --cherry-pick --right-only 1.8.8... | grep -i -v trivial | grep -v Merge | sort | uniq
git log --format="%s" --cherry-pick --right-only 1.8.9... | grep -i -v trivial | grep -v Merge | sort | uniq
Add any user visible changes into ../data/org.freedesktop.fwupd.metainfo.xml
appstream-util appdata-to-news ../data/org.freedesktop.fwupd.metainfo.xml > NEWS
@ -25,7 +25,7 @@ git add ../po/*.po
2. Commit changes to git:
# MAKE SURE THIS IS CORRECT
export release_ver="1.8.9"
export release_ver="1.8.10"
git commit -a -m "Release fwupd ${release_ver}" --no-verify
git tag -s -f -m "Release fwupd ${release_ver}" "${release_ver}"

View File

@ -87,7 +87,6 @@ def build_install(revision):
"--prefix=/usr",
"--libdir=lib",
"-Dauto_features=disabled",
"-Ddocs=none",
"-Db_coverage=false",
"-Dgusb:docs=false",
"-Dtests=false",

View File

@ -178,7 +178,7 @@ def install_packages(os, variant, yes, debugging, packages):
installer += packages
if debugging:
print(installer)
subprocess.call(installer)
subprocess.check_call(installer)
if __name__ == "__main__":

View File

@ -463,6 +463,8 @@ done
%if 0%{?have_uefi}
%{_datadir}/installed-tests/fwupd/efi
%endif
%{_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

@ -191,6 +191,9 @@ FirmwareSizeMax = 0x20000
[CFI\FLASHID_EF4018]
Name = W25Q128
FirmwareSizeMax = 0x1000000
[CFI\FLASHID_EF4019]
Name = W25Q256
FirmwareSizeMax = 0x2000000
# Fidelix
[CFI\FLASHID_F8]

View File

@ -33,6 +33,36 @@
<binary>fwupdtool</binary>
</provides>
<releases>
<release version="1.8.10" date="2023-01-23">
<description>
<p>
This release adds the following features:
</p>
<ul>
<li>Add a PE/COFF firmware parser to allow reading coSWID SBoM data</li>
<li>Allow dumping CFI SPI chips using devices like CH341a</li>
<li>Refactor the HWIDs functionality to include FDT data</li>
</ul>
<p>This release fixes the following bugs:</p>
<ul>
<li>Add back a legacy eMMC GUID to fix a regression</li>
<li>Always search for uSWID SBoM data in the image</li>
<li>Do not allow LZX compressed cabinet archives</li>
<li>Fallback to the checksum if the metadata artifact is invalid</li>
<li>Improve FDT parsing compatibility with new OpenBMC images</li>
<li>Never call grub2-probe without arguments</li>
<li>Respect user requested paths for the ESP even if they are not volumes</li>
<li>Speed up ChromeOS startup by a huge amount when using directory remotes</li>
<li>Verify the Synaptics RMI signature in more cases</li>
</ul>
<p>This release adds support for the following hardware:</p>
<ul>
<li>Quectel RM520</li>
<li>StarBook Mk VI</li>
<li>System76 launch_heavy_1</li>
</ul>
</description>
</release>
<release version="1.8.9" date="2023-01-03">
<description>
<p>

View File

@ -1,4 +1,6 @@
# fwupd BOS DS20 Specification
---
title: BOS DS20 Specification
---
## Introduction

View File

@ -85,6 +85,7 @@ for details.
* `FWUPD_OFFLINE_TRIGGER`
* `FWUPD_PROCFS`
* `FWUPD_SYSCONFDIR`
* `FWUPD_SYSFSDMIDIR`
* `FWUPD_SYSFSDRIVERDIR`
* `FWUPD_SYSFSFWATTRIBDIR`
* `FWUPD_SYSFSFWDIR`

View File

@ -46,7 +46,9 @@ content_files = [
"tutorial.md",
"hsi.md",
"ds20.md",
"hwids.md",
"bios-settings.md",
@plugin_readme_outputs@,
]
content_images = [
"architecture-plan.svg",

View File

@ -5,6 +5,11 @@
"AMD SOCs include the ability to prevent a rollback attack by a rollback protection feature on the secure processor.",
"This feature prevents an attacker from loading an older firmware onto the part after a security vulnerability has been fixed."
],
"more-information": [
"This particular check is not for the Microsoft Pluton Security processor which is present on some chips.",
"End users are not able to directly modify rollback protection, this is controlled by the manufacturer.",
"On Lenovo systems it has been reported that if this is disabled it may potentially be enabled by loading 'OS Optimized Defaults' in BIOS setup."
],
"failure-impact": [
"SOCs without this feature may be attacked by an attacker installing an older firmware that takes advantage of a well-known vulnerability."
],
@ -16,7 +21,10 @@
},
"hsi-level": 4,
"references": {
"https://www.psacertified.org/blog/anti-rollback-explained/": "Rollback protection"
"https://www.psacertified.org/blog/anti-rollback-explained/": "Rollback protection",
"https://www.amd.com/en/technologies/pro-security": "AMD Secure Processor",
"https://forums.lenovo.com/t5/Fedora/AMD-Rollback-protection-not-detected-by-fwupd-on-T14-G3-AMD/m-p/5182708?page=1#5810366":
"Loading OS Optimized Defaults on Lenovo systems"
},
"fwupd-version": "1.8.0"
}

View File

@ -15,6 +15,12 @@
"success-results" : {
"valid" : "the CPU platform is supported and has HSI tests"
},
"more-information": [
"On AMD APUs or CPUs this information is reported on kernel 5.19 or later via the `ccp` kernel module. ",
"If the kernel module is enabled but is not being auto-loaded, this is a kernel bug and should be reported to kernel bugzilla. ",
"If the kernel module has loaded but you still don't have data this is NOT a fwupd bug. You will have to contact ",
"your motherboard or system manufacturer to enable reporting this information."
],
"hsi-level" : 1,
"fwupd-version" : "1.8.0"
}

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

@ -1,3 +1,16 @@
plugin_readme_targets = []
plugin_readme_outputs = []
foreach plugin: plugins
plugin_readme_output = '@0@-README.md'.format(plugin)
plugin_readme_targets += custom_target('doc-plugin-@0@'.format(plugin),
input: join_paths(meson.project_source_root(), 'plugins', plugin, 'README.md'),
output: plugin_readme_output,
command: ['ln', '-sf', '@INPUT0@', '@OUTPUT@'],
build_by_default: true,
)
plugin_readme_outputs += '"@0@"'.format(plugin_readme_output)
endforeach
if build_docs and introspection.allowed()
toml_conf = configuration_data()
docgen_version = source_version
@ -5,6 +18,7 @@ if build_docs and introspection.allowed()
docgen_version = run_command([git, 'branch', '--show-current'], check: true).stdout().strip()
endif
toml_conf.set('version', docgen_version)
toml_conf.set('plugin_readme_outputs', ','.join(plugin_readme_outputs))
fwupd_toml = configure_file(
input: 'fwupd.toml.in',
@ -89,6 +103,7 @@ if build_docs and introspection.allowed()
depends: [
fwupdplugin_gir[0],
hsi_md,
plugin_readme_targets,
],
build_by_default: true,
install: true,

View File

@ -59,6 +59,10 @@ fu_backend_device_added(FuBackend *self, FuDevice *device)
if (priv->ctx != NULL)
fu_device_set_context(device, priv->ctx);
/* set backend ID if required */
if (fu_device_get_backend_id(device) == NULL)
fu_device_set_backend_id(device, priv->name);
/* add */
g_hash_table_insert(priv->devices,
g_strdup(fu_device_get_backend_id(device)),

View File

@ -13,7 +13,6 @@ G_DECLARE_DERIVABLE_TYPE(FuBluezDevice, fu_bluez_device, FU, BLUEZ_DEVICE, FuDev
struct _FuBluezDeviceClass {
FuDeviceClass parent_class;
gpointer __reserved[31];
};
GByteArray *

View File

@ -69,6 +69,10 @@ fu_cabinet_init(FuCabinet *self)
{
self->size_max = 1024 * 1024 * 100;
self->gcab_cabinet = gcab_cabinet_new();
#ifdef HAVE_GCAB_CABINET_ADD_ALLOWED_COMPRESSION
gcab_cabinet_add_allowed_compression(self->gcab_cabinet, GCAB_COMPRESSION_NONE);
gcab_cabinet_add_allowed_compression(self->gcab_cabinet, GCAB_COMPRESSION_MSZIP);
#endif
self->builder = xb_builder_new();
self->jcat_file = jcat_file_new();
self->jcat_context = jcat_context_new();

View File

@ -14,7 +14,6 @@ G_DECLARE_DERIVABLE_TYPE(FuCfiDevice, fu_cfi_device, FU, CFI_DEVICE, FuDevice)
struct _FuCfiDeviceClass {
FuDeviceClass parent_class;
gboolean (*chip_select)(FuCfiDevice *self, gboolean value, GError **error);
gpointer __reserved[30];
};
/**

View File

@ -11,12 +11,22 @@
#include "fu-quirks.h"
#include "fu-volume.h"
typedef enum {
FU_CONTEXT_HWID_FLAG_NONE = 0,
FU_CONTEXT_HWID_FLAG_LOAD_CONFIG = 1 << 0,
FU_CONTEXT_HWID_FLAG_LOAD_SMBIOS = 1 << 1,
FU_CONTEXT_HWID_FLAG_LOAD_FDT = 1 << 2,
FU_CONTEXT_HWID_FLAG_LOAD_DMI = 1 << 3,
FU_CONTEXT_HWID_FLAG_LOAD_KENV = 1 << 4,
FU_CONTEXT_HWID_FLAG_LOAD_ALL = G_MAXUINT,
} FuContextHwidFlags;
FuContext *
fu_context_new(void);
gboolean
fu_context_reload_bios_settings(FuContext *self, GError **error);
gboolean
fu_context_load_hwinfo(FuContext *self, GError **error);
fu_context_load_hwinfo(FuContext *self, FuContextHwidFlags flags, GError **error);
gboolean
fu_context_load_quirks(FuContext *self, FuQuirksLoadFlags flags, GError **error);
void
@ -35,3 +45,9 @@ GPtrArray *
fu_context_get_udev_subsystems(FuContext *self);
void
fu_context_add_esp_volume(FuContext *self, FuVolume *volume);
FuSmbios *
fu_context_get_smbios(FuContext *self);
FuHwids *
fu_context_get_hwids(FuContext *self);
void
fu_context_set_chassis_kind(FuContext *self, FuSmbiosChassisKind chassis_kind);

View File

@ -10,7 +10,9 @@
#include "fu-bios-settings-private.h"
#include "fu-context-private.h"
#include "fu-hwids.h"
#include "fu-fdt-firmware.h"
#include "fu-hwids-private.h"
#include "fu-path.h"
#include "fu-smbios-private.h"
#include "fu-volume-private.h"
@ -25,6 +27,7 @@ typedef struct {
FuContextFlags flags;
FuHwids *hwids;
FuSmbios *smbios;
FuSmbiosChassisKind chassis_kind;
FuQuirks *quirks;
GHashTable *runtime_versions;
GHashTable *compile_versions;
@ -38,6 +41,7 @@ typedef struct {
guint battery_threshold;
FuBiosSettings *host_bios_settings;
gboolean loaded_hwinfo;
FuFirmware *fdt; /* optional */
} FuContextPrivate;
enum { SIGNAL_SECURITY_CHANGED, SIGNAL_LAST };
@ -48,6 +52,7 @@ enum {
PROP_LID_STATE,
PROP_BATTERY_LEVEL,
PROP_BATTERY_THRESHOLD,
PROP_FLAGS,
PROP_LAST
};
@ -57,11 +62,116 @@ G_DEFINE_TYPE_WITH_PRIVATE(FuContext, fu_context, G_TYPE_OBJECT)
#define GET_PRIVATE(o) (fu_context_get_instance_private(o))
static GFile *
fu_context_get_fdt_file(GError **error)
{
g_autofree gchar *fdtfn_local = NULL;
g_autofree gchar *fdtfn_sys = NULL;
g_autofree gchar *localstatedir_pkg = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG);
g_autofree gchar *sysfsdir = NULL;
/* look for override first, fall back to system value */
fdtfn_local = g_build_filename(localstatedir_pkg, "system.dtb", NULL);
if (g_file_test(fdtfn_local, G_FILE_TEST_EXISTS))
return g_file_new_for_path(fdtfn_local);
/* actual hardware value */
sysfsdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW);
fdtfn_sys = g_build_filename(sysfsdir, "fdt", NULL);
if (g_file_test(fdtfn_sys, G_FILE_TEST_EXISTS))
return g_file_new_for_path(fdtfn_sys);
/* failed */
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"cannot find %s or override %s",
fdtfn_sys,
fdtfn_local);
return NULL;
}
/**
* fu_context_get_fdt:
* @self: a #FuContext
* @error: (nullable): optional return location for an error
*
* Gets and parses the system FDT, aka. the Flat Device Tree.
*
* The results are cached internally to the context, and subsequent calls to this function
* returns the pre-parsed object.
*
* Returns: (transfer full): a #FuFdtFirmware, or %NULL
*
* Since: 1.8.10
**/
FuFirmware *
fu_context_get_fdt(FuContext *self, GError **error)
{
FuContextPrivate *priv = GET_PRIVATE(self);
g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
/* load if not already parsed */
if (priv->fdt == NULL) {
g_autoptr(FuFirmware) fdt_tmp = fu_fdt_firmware_new();
g_autoptr(GFile) file = fu_context_get_fdt_file(error);
if (file == NULL)
return NULL;
if (!fu_firmware_parse_file(fdt_tmp, file, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) {
g_prefix_error(error, "failed to parse FDT: ");
return NULL;
}
priv->fdt = g_steal_pointer(&fdt_tmp);
}
/* success */
return g_object_ref(priv->fdt);
}
/**
* fu_context_get_smbios:
* @self: a #FuContext
*
* Gets the SMBIOS store.
*
* Returns: (transfer none): a #FuSmbios
*
* Since: 1.8.10
**/
FuSmbios *
fu_context_get_smbios(FuContext *self)
{
FuContextPrivate *priv = GET_PRIVATE(self);
g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
return priv->smbios;
}
/**
* fu_context_get_hwids:
* @self: a #FuContext
*
* Gets the HWIDs store.
*
* Returns: (transfer none): a #FuHwids
*
* Since: 1.8.10
**/
FuHwids *
fu_context_get_hwids(FuContext *self)
{
FuContextPrivate *priv = GET_PRIVATE(self);
g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
return priv->hwids;
}
/**
* fu_context_get_smbios_string:
* @self: a #FuContext
* @structure_type: a SMBIOS structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS
* @offset: a SMBIOS offset
* @error: (nullable): optional return location for an error
*
* Gets a hardware SMBIOS string.
*
@ -73,7 +183,7 @@ G_DEFINE_TYPE_WITH_PRIVATE(FuContext, fu_context, G_TYPE_OBJECT)
* Since: 1.6.0
**/
const gchar *
fu_context_get_smbios_string(FuContext *self, guint8 structure_type, guint8 offset)
fu_context_get_smbios_string(FuContext *self, guint8 structure_type, guint8 offset, GError **error)
{
FuContextPrivate *priv = GET_PRIVATE(self);
g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
@ -81,7 +191,7 @@ fu_context_get_smbios_string(FuContext *self, guint8 structure_type, guint8 offs
g_critical("cannot use SMBIOS before calling ->load_hwinfo()");
return NULL;
}
return fu_smbios_get_string(priv->smbios, structure_type, offset, NULL);
return fu_smbios_get_string(priv->smbios, structure_type, offset, error);
}
/**
@ -125,6 +235,7 @@ fu_context_get_smbios_data(FuContext *self, guint8 structure_type, GError **erro
* @self: a #FuContext
* @type: a structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS
* @offset: a structure offset
* @error: (nullable): optional return location for an error
*
* Reads an integer value from the SMBIOS string table of a specific structure.
*
@ -136,7 +247,7 @@ fu_context_get_smbios_data(FuContext *self, guint8 structure_type, GError **erro
* Since: 1.6.0
**/
guint
fu_context_get_smbios_integer(FuContext *self, guint8 type, guint8 offset)
fu_context_get_smbios_integer(FuContext *self, guint8 type, guint8 offset, GError **error)
{
FuContextPrivate *priv = GET_PRIVATE(self);
g_return_val_if_fail(FU_IS_CONTEXT(self), G_MAXUINT);
@ -144,7 +255,7 @@ fu_context_get_smbios_integer(FuContext *self, guint8 type, guint8 offset)
g_critical("cannot use SMBIOS before calling ->load_hwinfo()");
return G_MAXUINT;
}
return fu_smbios_get_integer(priv->smbios, type, offset, NULL);
return fu_smbios_get_integer(priv->smbios, type, offset, error);
}
/**
@ -222,6 +333,41 @@ fu_context_get_bios_setting_pending_reboot(FuContext *self)
return ret;
}
/**
* fu_context_get_chassis_kind:
* @self: a #FuContext
*
* Gets the chassis kind, if known.
*
* Returns: a #FuSmbiosChassisKind, e.g. %FU_SMBIOS_CHASSIS_KIND_LAPTOP
*
* Since: 1.8.10
**/
FuSmbiosChassisKind
fu_context_get_chassis_kind(FuContext *self)
{
FuContextPrivate *priv = GET_PRIVATE(self);
g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
return priv->chassis_kind;
}
/**
* fu_context_set_chassis_kind:
* @self: a #FuContext
* @chassis_kind: a #FuSmbiosChassisKind, e.g. %FU_SMBIOS_CHASSIS_KIND_TABLET
*
* Sets the chassis kind.
*
* Since: 1.8.10
**/
void
fu_context_set_chassis_kind(FuContext *self, FuSmbiosChassisKind chassis_kind)
{
FuContextPrivate *priv = GET_PRIVATE(self);
g_return_if_fail(FU_IS_CONTEXT(self));
priv->chassis_kind = chassis_kind;
}
/**
* fu_context_has_hwid_guid:
* @self: a #FuContext
@ -310,8 +456,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");
@ -621,31 +770,84 @@ fu_context_security_changed(FuContext *self)
/**
* fu_context_load_hwinfo:
* @self: a #FuContext
* @flags: a #FuContextHwidFlags, e.g. %FU_CONTEXT_HWID_FLAG_LOAD_SMBIOS
* @error: (nullable): optional return location for an error
*
* Loads all hardware information parts of the context.
*
* Returns: %TRUE for success
*
* Since: 1.6.0
* Since: 1.8.10
**/
gboolean
fu_context_load_hwinfo(FuContext *self, GError **error)
fu_context_load_hwinfo(FuContext *self, FuContextHwidFlags flags, GError **error)
{
FuContextPrivate *priv = GET_PRIVATE(self);
GPtrArray *guids;
g_autoptr(GError) error_smbios = NULL;
g_autoptr(GError) error_hwids = NULL;
g_autoptr(GError) error_bios_settings = NULL;
g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
if (!fu_smbios_setup(priv->smbios, &error_smbios))
g_warning("Failed to load SMBIOS: %s", error_smbios->message);
if (!fu_hwids_setup(priv->hwids, priv->smbios, &error_hwids))
g_warning("Failed to load HWIDs: %s", error_hwids->message);
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_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;
}
}
}
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);
@ -891,7 +1093,30 @@ fu_context_add_flag(FuContext *context, FuContextFlags flag)
{
FuContextPrivate *priv = GET_PRIVATE(context);
g_return_if_fail(FU_IS_CONTEXT(context));
if (priv->flags & flag)
return;
priv->flags |= flag;
g_object_notify(G_OBJECT(context), "flags");
}
/**
* fu_context_remove_flag:
* @context: a #FuContext
* @flag: the context flag
*
* Removes a specific flag from the context.
*
* Since: 1.8.10
**/
void
fu_context_remove_flag(FuContext *context, FuContextFlags flag)
{
FuContextPrivate *priv = GET_PRIVATE(context);
g_return_if_fail(FU_IS_CONTEXT(context));
if ((priv->flags & flag) == 0)
return;
priv->flags &= ~flag;
g_object_notify(G_OBJECT(context), "flags");
}
/**
@ -1039,6 +1264,9 @@ fu_context_get_property(GObject *object, guint prop_id, GValue *value, GParamSpe
case PROP_BATTERY_THRESHOLD:
g_value_set_uint(value, priv->battery_threshold);
break;
case PROP_FLAGS:
g_value_set_uint64(value, priv->flags);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@ -1049,6 +1277,7 @@ static void
fu_context_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
FuContext *self = FU_CONTEXT(object);
FuContextPrivate *priv = GET_PRIVATE(self);
switch (prop_id) {
case PROP_BATTERY_STATE:
fu_context_set_battery_state(self, g_value_get_uint(value));
@ -1062,6 +1291,9 @@ fu_context_set_property(GObject *object, guint prop_id, const GValue *value, GPa
case PROP_BATTERY_THRESHOLD:
fu_context_set_battery_threshold(self, g_value_get_uint(value));
break;
case PROP_FLAGS:
priv->flags = g_value_get_uint64(value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@ -1078,6 +1310,8 @@ fu_context_finalize(GObject *object)
g_hash_table_unref(priv->runtime_versions);
if (priv->compile_versions != NULL)
g_hash_table_unref(priv->compile_versions);
if (priv->fdt != NULL)
g_object_unref(priv->fdt);
g_object_unref(priv->hwids);
g_hash_table_unref(priv->hwid_flags);
g_object_unref(priv->quirks);
@ -1163,6 +1397,22 @@ fu_context_class_init(FuContextClass *klass)
G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
g_object_class_install_property(object_class, PROP_BATTERY_THRESHOLD, pspec);
/**
* FuContext:flags:
*
* The context flags.
*
* Since: 1.8.10
*/
pspec = g_param_spec_uint64("flags",
NULL,
NULL,
FU_CONTEXT_FLAG_NONE,
G_MAXUINT64,
FU_CONTEXT_FLAG_NONE,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
g_object_class_install_property(object_class, PROP_FLAGS, pspec);
/**
* FuContext::security-changed:
* @self: the #FuContext instance that emitted the signal
@ -1190,6 +1440,7 @@ static void
fu_context_init(FuContext *self)
{
FuContextPrivate *priv = GET_PRIVATE(self);
priv->chassis_kind = FU_SMBIOS_CHASSIS_KIND_UNKNOWN;
priv->battery_level = FWUPD_BATTERY_LEVEL_INVALID;
priv->battery_threshold = FWUPD_BATTERY_LEVEL_INVALID;
priv->smbios = fu_smbios_new();

View File

@ -10,6 +10,8 @@
#include "fu-bios-settings.h"
#include "fu-common.h"
#include "fu-firmware.h"
#include "fu-smbios.h"
#define FU_TYPE_CONTEXT (fu_context_get_type())
G_DECLARE_DERIVABLE_TYPE(FuContext, fu_context, FU, CONTEXT, GObject)
@ -61,13 +63,15 @@ typedef guint64 FuContextFlags;
void
fu_context_add_flag(FuContext *context, FuContextFlags flag);
void
fu_context_remove_flag(FuContext *context, FuContextFlags flag);
gboolean
fu_context_has_flag(FuContext *context, FuContextFlags flag);
const gchar *
fu_context_get_smbios_string(FuContext *self, guint8 structure_type, guint8 offset);
fu_context_get_smbios_string(FuContext *self, guint8 structure_type, guint8 offset, GError **error);
guint
fu_context_get_smbios_integer(FuContext *self, guint8 type, guint8 offset);
fu_context_get_smbios_integer(FuContext *self, guint8 type, guint8 offset, GError **error);
GBytes *
fu_context_get_smbios_data(FuContext *self, guint8 structure_type, GError **error);
gboolean
@ -124,3 +128,7 @@ fu_context_get_bios_setting(FuContext *self, const gchar *name);
GPtrArray *
fu_context_get_esp_volumes(FuContext *self, GError **error) G_GNUC_WARN_UNUSED_RESULT;
FuFirmware *
fu_context_get_fdt(FuContext *self, GError **error) G_GNUC_WARN_UNUSED_RESULT;
FuSmbiosChassisKind
fu_context_get_chassis_kind(FuContext *self);

View File

@ -169,7 +169,7 @@ fu_fdt_firmware_get_image_by_path(FuFdtFirmware *self, const gchar *path, GError
}
static gboolean
fu_fdt_firmware_parse_dt_struct(FuFdtFirmware *self, GBytes *fw, GHashTable *strtab, GError **error)
fu_fdt_firmware_parse_dt_struct(FuFdtFirmware *self, GBytes *fw, GBytes *strtab, GError **error)
{
gsize bufsz = 0;
gsize offset = 0;
@ -258,8 +258,8 @@ fu_fdt_firmware_parse_dt_struct(FuFdtFirmware *self, GBytes *fw, GHashTable *str
if (token == FDT_PROP) {
guint32 prop_len = 0;
guint32 prop_nameoff = 0;
gpointer value = NULL;
g_autoptr(GBytes) blob = NULL;
g_autoptr(GString) str = NULL;
/* sanity check */
if (firmware_current == FU_FIRMWARE(self)) {
@ -288,23 +288,18 @@ fu_fdt_firmware_parse_dt_struct(FuFdtFirmware *self, GBytes *fw, GHashTable *str
offset += sizeof(FuFdtProp);
/* add property */
if (!g_hash_table_lookup_extended(strtab,
GINT_TO_POINTER(prop_nameoff),
NULL,
&value)) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"invalid strtab offset 0x%x",
prop_nameoff);
str = fu_string_new_safe(g_bytes_get_data(strtab, NULL),
g_bytes_get_size(strtab),
prop_nameoff,
error);
if (str == NULL) {
g_prefix_error(error, "invalid strtab offset 0x%x: ", prop_nameoff);
return FALSE;
}
blob = fu_bytes_new_offset(fw, offset, prop_len, error);
if (blob == NULL)
return FALSE;
fu_fdt_image_set_attr(FU_FDT_IMAGE(firmware_current),
(const gchar *)value,
blob);
fu_fdt_image_set_attr(FU_FDT_IMAGE(firmware_current), str->str, blob);
offset += prop_len;
continue;
}
@ -332,43 +327,6 @@ fu_fdt_firmware_parse_dt_struct(FuFdtFirmware *self, GBytes *fw, GHashTable *str
return TRUE;
}
static gboolean
fu_fdt_firmware_parse_dt_strings(FuFdtFirmware *self,
GBytes *fw,
GHashTable *strtab,
GError **error)
{
gsize bufsz = 0;
const guint8 *buf = g_bytes_get_data(fw, &bufsz);
/* debug */
if (g_getenv("FU_FDT_FIRMWARE_VERBOSE") != NULL)
fu_dump_bytes(G_LOG_DOMAIN, "dt_strings", fw);
/* parse */
for (gsize offset = 0; offset < bufsz; offset++) {
g_autoptr(GString) str = NULL;
str = fu_string_new_safe(buf, bufsz, offset, error);
if (str == NULL)
return FALSE;
if (str->len == 0) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"strtab invalid @0x%x",
(guint)offset);
return FALSE;
}
if (g_getenv("FU_FDT_FIRMWARE_VERBOSE") != NULL)
g_debug("strtab: %s", str->str);
g_hash_table_insert(strtab, GINT_TO_POINTER(offset), g_strdup(str->str));
offset += str->len;
}
/* success */
return TRUE;
}
static gboolean
fu_fdt_firmware_parse_mem_rsvmap(FuFdtFirmware *self,
const guint8 *buf,
@ -451,7 +409,6 @@ fu_fdt_firmware_parse(FuFirmware *firmware,
guint32 size_dt_strings = 0;
guint32 size_dt_struct = 0;
const guint8 *buf = g_bytes_get_data(fw, &bufsz);
g_autoptr(GHashTable) strtab = NULL; /* uint:str */
/* sanity check */
if (!fu_memread_uint32_safe(buf,
@ -472,8 +429,7 @@ fu_fdt_firmware_parse(FuFirmware *firmware,
}
fu_firmware_set_size(firmware, totalsize);
/* read string table */
strtab = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
/* read header */
if (!fu_memread_uint32_safe(buf,
bufsz,
offset + G_STRUCT_OFFSET(FuFdtHeader, off_dt_strings),
@ -488,17 +444,6 @@ fu_fdt_firmware_parse(FuFirmware *firmware,
G_BIG_ENDIAN,
error))
return FALSE;
if (size_dt_strings != 0x0) {
g_autoptr(GBytes) dt_strings = NULL;
dt_strings =
fu_bytes_new_offset(fw, offset + off_dt_strings, size_dt_strings, error);
if (dt_strings == NULL)
return FALSE;
if (!fu_fdt_firmware_parse_dt_strings(self, dt_strings, strtab, error))
return FALSE;
}
/* read out DT struct */
if (!fu_memread_uint32_safe(buf,
bufsz,
offset + G_STRUCT_OFFSET(FuFdtHeader, off_dt_struct),
@ -513,16 +458,6 @@ fu_fdt_firmware_parse(FuFirmware *firmware,
G_BIG_ENDIAN,
error))
return FALSE;
if (size_dt_struct != 0x0) {
g_autoptr(GBytes) dt_struct = NULL;
dt_struct = fu_bytes_new_offset(fw, offset + off_dt_struct, size_dt_struct, error);
if (dt_struct == NULL)
return FALSE;
if (!fu_fdt_firmware_parse_dt_struct(self, dt_struct, strtab, error))
return FALSE;
}
/* read out reserved map */
if (!fu_memread_uint32_safe(buf,
bufsz,
offset + G_STRUCT_OFFSET(FuFdtHeader, off_mem_rsvmap),
@ -538,8 +473,6 @@ fu_fdt_firmware_parse(FuFirmware *firmware,
error))
return FALSE;
}
/* read in CPUID */
if (!fu_memread_uint32_safe(buf,
bufsz,
offset + G_STRUCT_OFFSET(FuFdtHeader, boot_cpuid_phys),
@ -547,8 +480,6 @@ fu_fdt_firmware_parse(FuFirmware *firmware,
G_BIG_ENDIAN,
error))
return FALSE;
/* header version */
if (!fu_memread_uint32_safe(buf,
bufsz,
offset + G_STRUCT_OFFSET(FuFdtHeader, last_comp_version),
@ -573,6 +504,23 @@ fu_fdt_firmware_parse(FuFirmware *firmware,
error))
return FALSE;
fu_firmware_set_version_raw(firmware, version);
/* parse device tree struct */
if (size_dt_struct != 0x0 && size_dt_strings != 0x0) {
g_autoptr(GBytes) dt_strings = NULL;
g_autoptr(GBytes) dt_struct = NULL;
dt_strings =
fu_bytes_new_offset(fw, offset + off_dt_strings, size_dt_strings, error);
if (dt_strings == NULL)
return FALSE;
dt_struct = fu_bytes_new_offset(fw, offset + off_dt_struct, size_dt_struct, error);
if (dt_struct == NULL)
return FALSE;
if (!fu_fdt_firmware_parse_dt_struct(self, dt_struct, dt_strings, error))
return FALSE;
}
/* success */
return TRUE;
}

View File

@ -1392,6 +1392,7 @@ gboolean
fu_firmware_write_file(FuFirmware *self, GFile *file, GError **error)
{
g_autoptr(GBytes) blob = NULL;
g_autoptr(GFile) parent = NULL;
g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE);
g_return_val_if_fail(G_IS_FILE(file), FALSE);
@ -1400,6 +1401,11 @@ fu_firmware_write_file(FuFirmware *self, GFile *file, GError **error)
blob = fu_firmware_write(self, error);
if (blob == NULL)
return FALSE;
parent = g_file_get_parent(file);
if (!g_file_query_exists(parent, NULL)) {
if (!g_file_make_directory_with_parents(parent, NULL, error))
return FALSE;
}
return g_file_replace_contents(file,
g_bytes_get_data(blob, NULL),
g_bytes_get_size(blob),

View File

@ -13,7 +13,6 @@ G_DECLARE_DERIVABLE_TYPE(FuHidDevice, fu_hid_device, FU, HID_DEVICE, FuUsbDevice
struct _FuHidDeviceClass {
FuUsbDeviceClass parent_class;
gpointer __reserved[31];
};
/**

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,94 @@
/*
* 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_autofree gchar *chassis_type = NULL;
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);
}
/* chassis kind */
fu_fdt_image_get_attr_str(FU_FDT_IMAGE(fdt_img), "chassis-type", &chassis_type, NULL);
if (chassis_type != NULL) {
struct {
FuSmbiosChassisKind chassis_kind;
const gchar *dt;
} chassis_map[] = {{FU_SMBIOS_CHASSIS_KIND_CONVERTIBLE, "convertible"},
{FU_SMBIOS_CHASSIS_KIND_EMBEDDED_PC, "embedded"},
{FU_SMBIOS_CHASSIS_KIND_HAND_HELD, "handset"},
{FU_SMBIOS_CHASSIS_KIND_LAPTOP, "laptop"},
{FU_SMBIOS_CHASSIS_KIND_TABLET, "tablet"},
{FU_SMBIOS_CHASSIS_KIND_UNKNOWN, NULL}};
for (guint i = 0; chassis_map[i].dt != NULL; i++) {
if (g_strcmp0(chassis_type, chassis_map[i].dt) == 0) {
fu_context_set_chassis_kind(ctx, chassis_map[i].chassis_kind);
break;
}
}
}
/* 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

@ -13,7 +13,6 @@ G_DECLARE_DERIVABLE_TYPE(FuI2cDevice, fu_i2c_device, FU, I2C_DEVICE, FuUdevDevic
struct _FuI2cDeviceClass {
FuUdevDeviceClass parent_class;
gpointer __reserved[31];
};
guint

View File

@ -13,7 +13,6 @@ G_DECLARE_DERIVABLE_TYPE(FuMeiDevice, fu_mei_device, FU, MEI_DEVICE, FuUdevDevic
struct _FuMeiDeviceClass {
FuUdevDeviceClass parent_class;
gpointer __reserved[31];
};
gboolean

View File

@ -292,6 +292,12 @@ fu_path_from_kind(FuPathKind path_kind)
if (tmp != NULL)
return g_strdup(tmp);
return g_strdup("/sys/kernel/security");
/* /sys/class/dmi/id */
case FU_PATH_KIND_SYSFSDIR_DMI:
tmp = g_getenv("FWUPD_SYSFSDMIDIR");
if (tmp != NULL)
return g_strdup(tmp);
return g_strdup("/sys/class/dmi/id");
/* /sys/firmware/acpi/tables */
case FU_PATH_KIND_ACPI_TABLES:
tmp = g_getenv("FWUPD_ACPITABLESDIR");

View File

@ -44,6 +44,7 @@
* /var/lib/fwupd/remotes.d)
* @FU_PATH_KIND_WIN32_BASEDIR: The root of the install directory on Windows
* @FU_PATH_KIND_LOCALCONFDIR_PKG: The package configuration override (IE /var/etc/fwupd)
* @FU_PATH_KIND_SYSFSDIR_DMI: The sysfs DMI location, (IE /sys/class/dmi/id)
*
* Path types to use when dynamically determining a path at runtime
**/
@ -73,6 +74,7 @@ typedef enum {
FU_PATH_KIND_LOCALSTATEDIR_REMOTES,
FU_PATH_KIND_WIN32_BASEDIR,
FU_PATH_KIND_LOCALCONFDIR_PKG,
FU_PATH_KIND_SYSFSDIR_DMI,
/*< private >*/
FU_PATH_KIND_LAST
} FuPathKind;

View File

@ -0,0 +1,212 @@
/*
* Copyright (C) 2023 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#define G_LOG_DOMAIN "FuFirmware"
#include "config.h"
#include "fu-bytes.h"
#include "fu-coswid-firmware.h"
#include "fu-dump.h"
#include "fu-mem.h"
#include "fu-pefile-firmware.h"
#include "fu-string.h"
/**
* FuPefileFirmware:
*
* A PE file consists of a Microsoft MS-DOS stub, the PE signature, the COFF file header, and an
* optional header, followed by section data.
*
* Documented:
* https://learn.microsoft.com/en-gb/windows/win32/debug/pe-format
*/
G_DEFINE_TYPE(FuPefileFirmware, fu_pefile_firmware, FU_TYPE_FIRMWARE)
static gboolean
fu_pefile_firmware_check_magic(FuFirmware *firmware, GBytes *fw, gsize offset, GError **error)
{
guint16 magic = 0x0;
if (!fu_memread_uint16_safe(g_bytes_get_data(fw, NULL),
g_bytes_get_size(fw),
offset,
&magic,
G_LITTLE_ENDIAN,
error)) {
g_prefix_error(error, "failed to read magic: ");
return FALSE;
}
if (magic != 0x5A4D) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid magic for file");
return FALSE;
}
/* success */
return TRUE;
}
static gboolean
fu_pefile_firmware_parse(FuFirmware *firmware,
GBytes *fw,
gsize offset,
FwupdInstallFlags flags,
GError **error)
{
gsize bufsz = 0;
gsize hdr_offset = offset;
guint16 opt_hdrsz = 0x0;
guint32 nr_sections = 0x0;
guint32 offset_sig = 0x0;
guint32 signature = 0x0;
const guint8 *buf = g_bytes_get_data(fw, &bufsz);
/* we already checked the MS-DOS magic, check the signature now */
if (!fu_memread_uint32_safe(buf,
bufsz,
hdr_offset + 0x3C,
&offset_sig,
G_LITTLE_ENDIAN,
error))
return FALSE;
hdr_offset += offset_sig;
if (!fu_memread_uint32_safe(buf, bufsz, hdr_offset, &signature, G_LITTLE_ENDIAN, error))
return FALSE;
if (signature != 0x4550) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid signature for file");
return FALSE;
}
hdr_offset += 4;
/* read number of sections */
if (!fu_memread_uint32_safe(buf,
bufsz,
hdr_offset + 0x02,
&nr_sections,
G_LITTLE_ENDIAN,
error))
return FALSE;
if (nr_sections == 0) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid number of sections");
return FALSE;
}
/* read optional extra header size */
if (!fu_memread_uint16_safe(buf,
bufsz,
hdr_offset + 0x10,
&opt_hdrsz,
G_LITTLE_ENDIAN,
error))
return FALSE;
hdr_offset += 0x14 + opt_hdrsz;
/* read out COFF header */
for (guint idx = 0; idx < nr_sections; idx++) {
guint32 sect_length = 0x0;
guint32 sect_offset = 0x0;
guint8 name[8] = {0x0};
g_autofree gchar *sect_id = NULL;
g_autoptr(FuFirmware) fw_sect = NULL;
g_autoptr(GBytes) sect_blob = NULL;
/* read the section name */
if (!fu_memcpy_safe(name,
sizeof(name),
0x0,
buf,
bufsz,
hdr_offset,
sizeof(name),
error))
return FALSE;
sect_id = fu_strsafe((const gchar *)name, sizeof(name));
if (sect_id == NULL) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no section name");
return FALSE;
}
/* create new firmware */
if (g_strcmp0(sect_id, ".sbom") == 0) {
fw_sect = fu_coswid_firmware_new();
} else {
fw_sect = fu_firmware_new();
}
fu_firmware_set_idx(fw_sect, idx);
fu_firmware_set_id(fw_sect, sect_id);
/* add data */
if (!fu_memread_uint32_safe(buf,
bufsz,
hdr_offset + 0x08,
&sect_length,
G_LITTLE_ENDIAN,
error))
return FALSE;
fu_firmware_set_size(fw_sect, sect_length);
if (!fu_memread_uint32_safe(buf,
bufsz,
hdr_offset + 0x14,
&sect_offset,
G_LITTLE_ENDIAN,
error))
return FALSE;
fu_firmware_set_offset(fw_sect, sect_offset);
sect_blob = fu_bytes_new_offset(fw, sect_offset, sect_length, error);
if (sect_blob == NULL) {
g_prefix_error(error, "failed to get raw data for %s: ", sect_id);
return FALSE;
}
if (!fu_firmware_parse(fw_sect, sect_blob, flags, error))
return FALSE;
fu_firmware_add_image(firmware, fw_sect);
/* next! */
hdr_offset += 0x28;
}
/* success */
return TRUE;
}
static void
fu_pefile_firmware_init(FuPefileFirmware *self)
{
}
static void
fu_pefile_firmware_class_init(FuPefileFirmwareClass *klass)
{
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS(klass);
klass_firmware->check_magic = fu_pefile_firmware_check_magic;
klass_firmware->parse = fu_pefile_firmware_parse;
}
/**
* fu_pefile_firmware_new:
*
* Creates a new #FuPefileFirmware
*
* Since: 1.8.10
**/
FuFirmware *
fu_pefile_firmware_new(void)
{
return FU_FIRMWARE(g_object_new(FU_TYPE_PEFILE_FIRMWARE, NULL));
}

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2023 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include "fu-firmware.h"
#define FU_TYPE_PEFILE_FIRMWARE (fu_pefile_firmware_get_type())
G_DECLARE_DERIVABLE_TYPE(FuPefileFirmware, fu_pefile_firmware, FU, PEFILE_FIRMWARE, FuFirmware)
struct _FuPefileFirmwareClass {
FuFirmwareClass parent_class;
};
FuFirmware *
fu_pefile_firmware_new(void);

View File

@ -891,16 +891,33 @@ fu_plugin_runner_startup(FuPlugin *self, FuProgress *progress, GError **error)
/* ensure the configure file is set to the correct permission */
if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_SECURE_CONFIG)) {
if (g_file_test(config_filename, G_FILE_TEST_EXISTS)) {
gint rc = g_chmod(config_filename, FU_PLUGIN_FILE_MODE_SECURE);
GStatBuf st = {0x0};
gint rc = g_stat(config_filename, &st);
if (rc != 0) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"failed to change permission of %s",
"failed to get permission of %s",
config_filename);
fu_plugin_add_flag(self, FWUPD_PLUGIN_FLAG_FAILED_OPEN);
return FALSE;
}
st.st_mode &= 0777;
if (st.st_mode != FU_PLUGIN_FILE_MODE_SECURE) {
g_debug("mode was 0%o, and needs to be 0%o",
st.st_mode,
(guint)FU_PLUGIN_FILE_MODE_SECURE);
rc = g_chmod(config_filename, FU_PLUGIN_FILE_MODE_SECURE);
if (rc != 0) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"failed to change permission of %s",
config_filename);
fu_plugin_add_flag(self, FWUPD_PLUGIN_FLAG_FAILED_OPEN);
return FALSE;
}
}
}
}

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,39 @@ fu_smbios3_func(void)
}
static void
fu_smbios_dt_func(void)
fu_context_flags_func(void)
{
const gchar *str;
gboolean ret;
g_autofree gchar *path = NULL;
g_autoptr(FuSmbios) smbios = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(FuContext) ctx = fu_context_new();
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);
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, ==, "Hughski Limited");
g_assert_false(fu_context_has_flag(ctx, FU_CONTEXT_FLAG_SAVE_EVENTS));
fu_context_add_flag(ctx, FU_CONTEXT_FLAG_SAVE_EVENTS);
g_assert_true(fu_context_has_flag(ctx, FU_CONTEXT_FLAG_SAVE_EVENTS));
fu_context_remove_flag(ctx, FU_CONTEXT_FLAG_SAVE_EVENTS);
fu_context_remove_flag(ctx, FU_CONTEXT_FLAG_SAVE_EVENTS);
g_assert_false(fu_context_has_flag(ctx, FU_CONTEXT_FLAG_SAVE_EVENTS));
fu_context_add_flag(ctx, FU_CONTEXT_FLAG_SAVE_EVENTS);
fu_context_add_flag(ctx, FU_CONTEXT_FLAG_SAVE_EVENTS);
g_assert_true(fu_context_has_flag(ctx, FU_CONTEXT_FLAG_SAVE_EVENTS));
}
static void
fu_smbios_dt_fallback_func(void)
fu_context_hwids_dmi_func(void)
{
const gchar *str;
gboolean ret;
g_autofree gchar *path = NULL;
g_autoptr(FuSmbios) smbios = fu_smbios_new();
g_autoptr(FuContext) ctx = fu_context_new();
g_autoptr(GError) error = NULL;
gboolean ret;
path = g_test_build_filename(G_TEST_DIST, "tests", "devicetree-fallback", "base", NULL);
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, ==, "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 +558,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 +592,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
@ -838,6 +798,47 @@ fu_plugin_delay_func(void)
g_clear_object(&device_tmp);
}
static void
fu_plugin_fdt_func(void)
{
gboolean ret;
g_autofree gchar *compatible = NULL;
g_autoptr(FuContext) ctx = fu_context_new();
g_autoptr(FuFirmware) fdt = NULL;
g_autoptr(FuFirmware) fdt_root = NULL;
g_autoptr(FuFirmware) fdt_tmp = fu_fdt_firmware_new();
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file =
g_file_new_for_path("/tmp/fwupd-self-test/var/lib/fwupd/system.dtb");
/* write file */
ret = fu_firmware_build_from_xml(
FU_FIRMWARE(fdt_tmp),
"<firmware gtype=\"FuFdtFirmware\">\n"
" <firmware gtype=\"FuFdtImage\">\n"
" <metadata key=\"compatible\" format=\"str\">pine64,rockpro64-v2.1</metadata>\n"
" </firmware>\n"
"</firmware>\n",
&error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_firmware_write_file(FU_FIRMWARE(fdt_tmp), file, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* get compatible from the context */
fdt = fu_context_get_fdt(ctx, &error);
g_assert_no_error(error);
g_assert_nonnull(fdt);
fdt_root = fu_firmware_get_image_by_id(fdt, NULL, &error);
g_assert_no_error(error);
g_assert_nonnull(fdt_root);
ret = fu_fdt_image_get_attr_str(FU_FDT_IMAGE(fdt_root), "compatible", &compatible, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_cmpstr(compatible, ==, "pine64,rockpro64-v2.1");
}
static void
fu_plugin_quirks_func(void)
{
@ -3759,6 +3760,8 @@ 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);
(void)g_setenv("FWUPD_PROFILE", "1", TRUE);
@ -3780,6 +3783,7 @@ main(int argc, char **argv)
fu_plugin_device_inhibit_children_func);
g_test_add_func("/fwupd/plugin{delay}", fu_plugin_delay_func);
g_test_add_func("/fwupd/plugin{quirks}", fu_plugin_quirks_func);
g_test_add_func("/fwupd/plugin{fdt}", fu_plugin_fdt_func);
g_test_add_func("/fwupd/plugin{quirks-performance}", fu_plugin_quirks_performance_func);
g_test_add_func("/fwupd/plugin{quirks-device}", fu_plugin_quirks_device_func);
g_test_add_func("/fwupd/backend", fu_backend_func);
@ -3802,11 +3806,10 @@ 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{flags}", fu_context_flags_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

@ -27,7 +27,6 @@ G_DECLARE_DERIVABLE_TYPE(FuUdevDevice, fu_udev_device, FU, UDEV_DEVICE, FuDevice
struct _FuUdevDeviceClass {
FuDeviceClass parent_class;
gpointer __reserved[31];
};
/**

View File

@ -24,7 +24,6 @@ G_DECLARE_DERIVABLE_TYPE(FuUsbDevice, fu_usb_device, FU, USB_DEVICE, FuDevice)
struct _FuUsbDeviceClass {
FuDeviceClass parent_class;
gpointer __reserved[31];
};
FuUsbDevice *

View File

@ -319,6 +319,7 @@ fu_uswid_firmware_init(FuUswidFirmware *self)
priv->hdrver = USWID_HEADER_VERSION_V1;
priv->compressed = FALSE;
fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE);
fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_ALWAYS_SEARCH);
}
static void

View File

@ -725,6 +725,10 @@ fu_volume_new_esp_for_path(const gchar *esp_path, GError **error)
if (g_strcmp0(basename, vol_basename) == 0)
return g_object_ref(vol);
}
if (g_file_test(esp_path, G_FILE_TEST_IS_DIR)) {
g_debug("Using user requested path %s for ESP", esp_path);
return fu_volume_new_from_mount_path(esp_path);
}
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_FILENAME,

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>
@ -61,11 +62,13 @@
#include <libfwupdplugin/fu-mem.h>
#include <libfwupdplugin/fu-oprom-firmware.h>
#include <libfwupdplugin/fu-path.h>
#include <libfwupdplugin/fu-pefile-firmware.h>
#include <libfwupdplugin/fu-plugin-vfuncs.h>
#include <libfwupdplugin/fu-plugin.h>
#include <libfwupdplugin/fu-progress.h>
#include <libfwupdplugin/fu-security-attr.h>
#include <libfwupdplugin/fu-security-attrs.h>
#include <libfwupdplugin/fu-smbios.h>
#include <libfwupdplugin/fu-srec-firmware.h>
#include <libfwupdplugin/fu-string.h>
#include <libfwupdplugin/fu-sum.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;
@ -603,7 +600,6 @@ LIBFWUPDPLUGIN_1.6.0 {
fu_context_get_type;
fu_context_get_udev_subsystems;
fu_context_has_hwid_guid;
fu_context_load_hwinfo;
fu_context_load_quirks;
fu_context_lookup_quirk_by_id;
fu_context_lookup_quirk_by_id_iter;
@ -720,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: *;
@ -1151,3 +1146,19 @@ LIBFWUPDPLUGIN_1.8.9 {
fu_version_from_uint24;
local: *;
} LIBFWUPDPLUGIN_1.8.7;
LIBFWUPDPLUGIN_1.8.10 {
global:
fu_context_get_chassis_kind;
fu_context_get_fdt;
fu_context_get_hwids;
fu_context_get_smbios;
fu_context_load_hwinfo;
fu_context_remove_flag;
fu_context_set_chassis_kind;
fu_hwids_add_guid;
fu_hwids_add_value;
fu_pefile_firmware_get_type;
fu_pefile_firmware_new;
local: *;
} LIBFWUPDPLUGIN_1.8.9;

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',
@ -74,6 +79,7 @@ fwupdplugin_src = [
'fu-uswid-firmware.c', # fuzzing
'fu-coswid-common.c', # fuzzing
'fu-coswid-firmware.c', # fuzzing
'fu-pefile-firmware.c', # fuzzing
'fu-efivar.c',
'fu-udev-device.c',
'fu-i2c-device.c',
@ -149,6 +155,7 @@ fwupdplugin_headers = [
'fu-ifd-firmware.h',
'fu-uswid-firmware.h',
'fu-coswid-firmware.h',
'fu-pefile-firmware.h',
'fu-ifd-image.h',
'fu-linear-firmware.h',
'fu-volume.h',
@ -359,6 +366,25 @@ if get_option('tests')
],
)
test('fwupdplugin-self-test', e, is_parallel: false, timeout: 180, env: env)
install_data([
'tests/chassis_type',
'tests/sys_vendor',
],
install_dir: installed_test_datadir,
)
install_data([
'tests/dmi/tables/DMI',
'tests/dmi/tables/smbios_entry_point',
],
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, 'tests/dmi/tables64'),
)
endif
fwupdplugin_incdir = include_directories('.')

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

@ -1,5 +1,5 @@
project('fwupd', 'c',
version: '1.8.9',
version: '1.8.10',
license: 'LGPL-2.1+',
meson_version: '>=0.61.0',
default_options: ['warning_level=2', 'c_std=c11'],
@ -219,7 +219,7 @@ hsi = get_option('hsi').disable_auto_if(host_machine.system() != 'linux').disabl
if hsi
conf.set('HAVE_HSI', '1')
endif
libxmlb = dependency('xmlb', version: '>= 0.1.13', fallback: ['libxmlb', 'libxmlb_dep'])
libxmlb = dependency('xmlb', version: '>= 0.1.15', fallback: ['libxmlb', 'libxmlb_dep'])
gusb = dependency('gusb', version: '>= 0.3.0', fallback: ['gusb', 'gusb_dep'], required: get_option('gusb'))
if gusb.found()
conf.set('HAVE_GUSB', '1')
@ -267,8 +267,13 @@ if build_daemon
endif
libm = cc.find_library('m', required: false)
libgcab = dependency('libgcab-1.0', version: '>= 1.0', fallback: ['gcab', 'gcab_dep'])
if libgcab.type_name() == 'pkgconfig' and cc.has_function('gcab_file_set_bytes', dependencies: libgcab)
conf.set('HAVE_GCAB_FILE_SET_BYTES', '1')
if libgcab.type_name() == 'pkgconfig'
if cc.has_function('gcab_file_set_bytes', dependencies: libgcab)
conf.set('HAVE_GCAB_FILE_SET_BYTES', '1')
endif
if cc.has_function('gcab_cabinet_add_allowed_compression', dependencies: libgcab)
conf.set('HAVE_GCAB_CABINET_ADD_ALLOWED_COMPRESSION', '1')
endif
endif
bashcomp = dependency('bash-completion', required: false)
@ -608,7 +613,7 @@ custom_target('builtin-quirk-gz',
install_dir: join_paths(datadir, 'fwupd', 'quirks.d'),
)
if build_daemon and libsystemd.found()
if build_daemon and libsystemd.found() and offline.allowed()
install_symlink('fwupd-offline-update.service',
install_dir: join_paths(systemdunitdir, 'system-update.target.wants'),
pointing_to: join_paths('..', 'fwupd-offline-update.service')

View File

@ -1,4 +1,6 @@
# DMA Protection
---
title: Plugin: ACPI DMAR — DMA Protection
---
## Introduction

View File

@ -1,4 +1,6 @@
# ACPI FACP
---
title: Plugin: ACPI FACP — Fixed ACPI Description Table
---
## Introduction

View File

@ -1,4 +1,6 @@
# DMA Protection
---
title: Plugin: ACPI IVRS — DMA Protection
---
## Introduction

View File

@ -1,4 +1,6 @@
# Platform Health Assessment Table
---
title: Plugin: ACPI PHAT — Platform Health Assessment Table
---
## Introduction

View File

@ -1,4 +1,6 @@
# AMD PMC
---
title: Plugin: AMD PMC
---
## Introduction

View File

@ -1,4 +1,6 @@
# Analogix
---
title: Plugin: Analogix
---
## Introduction
@ -15,7 +17,7 @@ a Intel Hex file format. The resulting binary image is either:
This plugin supports the following protocol ID:
* com.analogix.bb
* `com.analogix.bb`
## GUID Generation

View File

@ -1,4 +1,6 @@
# Android Bootloaders
---
title: Plugin: Android Boot
---
## Introduction
@ -13,7 +15,7 @@ to flash from the device itself rather than external device.
This plugin supports the following protocol ID:
* com.google.android_boot
* `com.google.android_boot`
## GUID Generation

View File

@ -1,4 +1,6 @@
# ATA
---
title: Plugin: ATA
---
## Introduction
@ -18,7 +20,7 @@ an unspecified binary file format.
This plugin supports the following protocol ID:
* org.t13.ata
* `org.t13.ata`
## GUID Generation

View File

@ -1,4 +1,6 @@
# BCM57xx
---
title: Plugin: BCM57xx
---
## Introduction

11
plugins/bios/README.md Normal file
View File

@ -0,0 +1,11 @@
---
title: Plugin: BIOS
---
## Introduction
This plugin checks UEFI capsules are available, and if missing a HSI failure is reported.
## External Interface Access
This plugin requires read only access to attributes located within `/sys/firmware/efi/esrt`.

View File

@ -1,4 +1,6 @@
# Infineon Technologies
---
title: Plugin: CCGX
---
## Introduction
@ -9,10 +11,10 @@ controller family of devices used in docks.
This plugin supports the following protocol IDs:
* com.cypress.ccgx
* com.cypress.ccgx.dmc
* com.infineon.ccgx
* com.infineon.ccgx.dmc
* `com.cypress.ccgx` (deprecated)
* `com.cypress.ccgx.dmc` (deprecated)
* `com.infineon.ccgx`
* `com.infineon.ccgx.dmc`
## Device Flash
@ -101,7 +103,7 @@ The vendor ID is set from the USB vendor, for example set to `USB:0x04B4`
This plugin uses the following plugin-specific quirks:
### Trigger Code
### CcgxDmcTriggerCode
DMC devices need a specified trigger code to request the device to update
the firmware and the trigger code depends on the devices.

View File

@ -1,4 +1,6 @@
# Component Firmware update
---
title: Plugin: CFU - Component Firmware Update
---
## Introduction
@ -13,7 +15,7 @@ details.
This plugin supports the following protocol ID:
* com.microsoft.cfu
* `com.microsoft.cfu`
## GUID Generation

View File

@ -1,4 +1,6 @@
# CH341A
---
title: Plugin: CH341A
---
## Introduction
@ -22,7 +24,7 @@ The daemon will decompress the cabinet archive and extract a firmware blob of un
This plugin supports the following protocol ID:
- com.winchiphead.ch341a
- `com.winchiphead.ch341a`
## GUID Generation

View File

@ -106,14 +106,17 @@ fu_ch341a_cfi_device_read_jedec(FuCh341aCfiDevice *self, GError **error)
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"flash ID non-valid");
"flash ID non-valid, got 0x000000");
return FALSE;
}
if (buf[1] == 0xFF && buf[2] == 0xFF && buf[3] == 0xFF) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"device not detected");
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"device not detected, flash ID 0x%02X%02X%02X",
buf[1],
buf[2],
buf[3]);
return FALSE;
}
g_string_append_printf(flash_id, "%02X", buf[1]);
@ -426,6 +429,7 @@ fu_ch341a_cfi_device_init(FuCh341aCfiDevice *self)
fu_device_add_protocol(FU_DEVICE(self), "org.jedec.cfi");
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE);
}
static void

View File

@ -1,4 +1,6 @@
# ColorHug
---
title: Plugin: ColorHug
---
## Introduction
@ -16,7 +18,7 @@ a packed binary file format.
This plugin supports the following protocol ID:
* com.hughski.colorhug
* `com.hughski.colorhug`
## GUID Generation

View File

@ -1,4 +1,6 @@
# Corsair
---
title: Plugin: Corsair
---
## Introduction

View File

@ -1,4 +1,6 @@
# CPU Microcode
---
title: Plugin: CPU Microcode
---
## Introduction

View File

@ -1,4 +1,6 @@
# Chrome OS EC
---
title: Plugin: Chrome OS EC
---
## Introduction
@ -19,7 +21,7 @@ the Google [fmap file format](https://www.chromium.org/chromium-os/firmware-port
This plugin supports the following protocol ID:
* com.google.usb.crosec
* `com.google.usb.crosec`
## GUID Generation

View File

@ -1,4 +1,6 @@
# Dell USB-C Dock
---
title: Plugin: Dell USB-C Dock
---
## Dell System
@ -35,8 +37,8 @@ blobs with an unspecified binary file format.
This plugin supports the following protocol ID:
* com.dell.dock
* com.synaptics.mst
* `com.dell.dock`
* `com.synaptics.mst`
## GUID Generation

View File

@ -1,4 +1,6 @@
# Dell ESRT Support
---
title: Plugin: Dell ESRT
---
## Introduction

View File

@ -1,4 +1,6 @@
# Dell Support
---
title: Plugin: Dell
---
## Introduction

View File

@ -147,9 +147,9 @@ static gboolean
fu_dell_supported(FuPlugin *plugin, GError **error)
{
FuContext *ctx = fu_plugin_get_context(plugin);
FuSmbiosChassisKind chassis_kind = fu_context_get_chassis_kind(ctx);
g_autoptr(GBytes) de_table = NULL;
g_autoptr(GBytes) da_table = NULL;
g_autoptr(GBytes) enclosure = NULL;
guint8 value = 0;
struct da_structure da_values = {0x0};
@ -194,19 +194,8 @@ fu_dell_supported(FuPlugin *plugin, GError **error)
}
/* only run on intended Dell hw types */
enclosure = fu_context_get_smbios_data(ctx, FU_SMBIOS_STRUCTURE_TYPE_CHASSIS, error);
if (enclosure == NULL)
return FALSE;
if (!fu_memread_uint8_safe(g_bytes_get_data(enclosure, NULL),
g_bytes_get_size(enclosure),
0x0,
&value,
error)) {
g_prefix_error(error, "invalid enclosure data: ");
return FALSE;
}
for (guint i = 0; i < G_N_ELEMENTS(enclosure_allowlist); i++) {
if (enclosure_allowlist[i] == value)
if (enclosure_allowlist[i] == chassis_kind)
return TRUE;
}

View File

@ -1,4 +1,6 @@
# DFU CSR
---
title: Plugin: DFU CSR — Cambridge Silicon Radio
---
## Introduction
@ -21,7 +23,7 @@ DFU file format.
This plugin supports the following protocol ID:
* com.qualcomm.dfu
* `com.qualcomm.dfu`
## GUID Generation

View File

@ -1,4 +1,6 @@
# DFU
---
title: Plugin: DFU
---
## Introduction
@ -12,8 +14,8 @@ DFU or DfuSe file format.
This plugin supports the following protocol IDs:
* org.usb.dfu
* com.st.dfuse
* `org.usb.dfu`
* `com.st.dfuse`
## GUID Generation
@ -32,6 +34,42 @@ the device again re-enumerates back to the runtime mode.
For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that
the bootloader and runtime modes are treated as the same device.
## Implementation Notes
The runtime mode is just as important as the DFU mode from the point of view of
fwupd and should be included if firmware updates are to "just work". Without a
DFU runtime interface we can match the device with `Flags = no-dfu-runtime` but
will need a suitably new fwupd version before the device is recognized.
The USB interface revision (`REV`) is used as the BCD version number, as DFU has
no way of representing a firmware version number. A new firmware version should
always increment the USB REV of the *runtime* interface as fwupd will **not** switch
the device into *DFU mode* during enumeration to read the version number.
The version number of the DFU mode should represent the *bootloader version* and
this should not change as the firmware is updated.
The runtime USB interface should have a unique vendor ID and product ID for the
specific firmware stream. A different version of software should have a unique
VID/PID USB descriptor pair. The microcontroller example VID/PID should **never**
be used in the runtime mode otherwise fwupd would not know what firmware to match.
Ideally, the bootloader should also have a unique USB vendor ID and product ID.
This allows fwupd to more easily recognize the runtime interface *going away* and
the DFU interface *coming back*. If the VID/PID is the same in runtime and DFU
modes then the quirk `Flags = no-pid-change` is required.
If the bootloader VID/PID is not customized (as might be the default for the supplied
MCU) then fwupd can match the runtime VID/PID to the bootloader VID/PID. Where this
fails is when the device is *stuck* in the DFU mode, perhaps because the user removed
the USB cable before the device had completed updating.
With a unique VID/PID fwupd can *recover* the device stuck in DFU mode, reflashing the
device with the latest matching firmware and then attaching it back into runtime mode.
Using a *generic* VID/PID for the bootloader means fwupd does not know how to recover
the device back into runtime mode as the client does not know what firmware to choose
and the user is forced to either RMA the device, or to download the correct file manually
from the vendor vebsite and use low-level commands like `sudo fwupdtool install-blob`.
## Vendor ID Security
The vendor ID is set from the USB vendor, for example `USB:0x0A12`

View File

@ -1,4 +1,6 @@
# 8BitDo
---
title: Plugin: 8BitDo
---
## Introduction
@ -18,7 +20,7 @@ that is used when flashing the image.
This plugin supports the following protocol ID:
* com.8bitdo
* `com.8bitdo`
## GUID Generation

View File

@ -1,23 +1,21 @@
Elan Fingerprint Sensor Support
=================================
---
title: Plugin: Elan Fingerprint Sensor
---
Introduction
------------
## Introduction
The plugin used for update firmware for fingerprint sensors from Elan.
Firmware Format
---------------
## Firmware Format
The daemon will decompress the cabinet archive and extract a firmware blob in
a packed binary file format.
This plugin supports the following protocol ID:
* tw.com.emc.elanfp
* `tw.com.emc.elanfp`
GUID Generation
---------------
## GUID Generation
These devices use the standard USB DeviceInstanceId values, e.g.
@ -25,7 +23,6 @@ These devices use the standard USB DeviceInstanceId values, e.g.
* `USB\VID_04F3&PID_0C7E`
* `USB\VID_04F3`
Vendor ID Security
------------------
## Vendor ID Security
The vendor ID is set from the USB vendor, in this instance set to `USB:0x04F3`

View File

@ -1,4 +1,6 @@
# Elan TouchPad
---
title: Plugin: Elan TouchPad
---
## Introduction
@ -13,7 +15,7 @@ an unspecified binary file format.
This plugin supports the following protocol ID:
* tw.com.emc.elantp
* `tw.com.emc.elantp`
## GUID Generation

View File

@ -1,4 +1,6 @@
# eMMC
---
title: Plugin: eMMC
---
## Introduction
@ -13,12 +15,16 @@ eMMC devices support the `org.jedec.mmc` protocol.
These devices use the following instance values:
* `EMMC\%NAME%`
* `EMMC\%NAME%&%REV%`
* `EMMC\%MANFID%&%OEMID%`
* `EMMC\%MANFID%&%OEMID%&%NAME%`
* `EMMC\%MANFID%&%NAME%&%REV%`
* `EMMC\%MANFID%&%OEMID%&%NAME%&%REV%`
* `EMMC\NAME_%name%`
* `EMMC\NAME_%name%&REV_%rev%`
* `EMMC\MAN_%manfid%&OEM_%oemid%`
* `EMMC\MAN_%manfid%&OEM_%oemid%&NAME_%name%`
* `EMMC\MAN_%manfid%&NAME_%name%&REV_%rev%`
* `EMMC\MAN_%manfid%&OEM_%oemid%&NAME_%name%&REV_%rev%`
One deprecated instance ID is also added; new firmware should not use this.
* `EMMC\%manfid%&%oemid%&%name%`
## Update Behavior

View File

@ -136,6 +136,7 @@ fu_emmc_device_probe(FuDevice *device, GError **error)
guint64 manfid = 0;
const gchar *tmp;
g_autoptr(GUdevDevice) udev_parent = NULL;
g_autofree gchar *man_oem_name = NULL;
g_autofree gchar *vendor_id = NULL;
g_autoptr(GRegex) dev_regex = NULL;
@ -221,6 +222,13 @@ fu_emmc_device_probe(FuDevice *device, GError **error)
fu_device_build_instance_id(device, NULL, "EMMC", "MAN", "NAME", "REV", NULL);
fu_device_build_instance_id(device, NULL, "EMMC", "MAN", "OEM", "NAME", "REV", NULL);
/* this is a (invalid!) instance ID added for legacy compatibility */
man_oem_name = g_strdup_printf("EMMC\\%04" G_GUINT64_FORMAT "&%04" G_GUINT64_FORMAT "&%s",
manfid,
oemid,
fu_device_get_name(device));
fu_device_add_instance_id(device, man_oem_name);
/* set the vendor */
tmp = g_udev_device_get_sysfs_attr(udev_parent, "manfid");
vendor_id = g_strdup_printf("EMMC:%s", tmp);

View File

@ -1,4 +1,6 @@
# Explore EP963x
---
title: Plugin: EP963x
---
## Introduction
@ -11,7 +13,7 @@ a packed binary file format.
This plugin supports the following protocol ID:
* tw.com.exploretech.ep963x
* `tw.com.exploretech.ep963x`
## GUID Generation

View File

@ -1,4 +1,6 @@
# Fastboot
---
title: Plugin: Fastboot
---
## Introduction
@ -17,7 +19,7 @@ be updated.
This plugin supports the following protocol ID:
* com.google.fastboot
* `com.google.fastboot`
## GUID Generation

View File

@ -1,4 +1,6 @@
# Flashrom
---
title: Plugin: Flashrom
---
## Introduction
@ -15,7 +17,7 @@ EEPROM programmer.
This plugin supports the following protocol ID:
* org.flashrom
* `org.flashrom`
## Coreboot Version String

View File

@ -99,11 +99,16 @@ Branch = coreboot
Flags = reset-cmos
PciBcrAddr = 0x0
# StarBook Mk VI - Intel (AMI GUID)
[1292e166-a66f-5e11-b2bb-53265a8f53d9]
FirmwareSizeMax = 0x2000000
# StarBook Mk VI - Intel (coreboot GUID)
[8c994a92-7ef8-5d68-80b5-99ead7cf4686]
Branch = coreboot
Flags = reset-cmos
PciBcrAddr = 0x0
FirmwareSizeMax = 0x2000000
# NovaCustom NV4x (HwId)
[25b6ea34-8f52-598e-a27a-31e03014dbe3]

View File

@ -1,4 +1,6 @@
# Focal TouchPad
---
title: Plugin: Focal TouchPad
---
## Introduction
@ -12,7 +14,7 @@ an unspecified binary file format.
This plugin supports the following protocol ID:
* tw.com.focalfp
* `tw.com.focalfp`
## GUID Generation

Some files were not shown because too many files have changed in this diff Show More