Coverage Report

Created: 2025-07-18 06:26

/src/fwupd/libfwupdplugin/fu-hwids.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2017 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
0
#define G_LOG_DOMAIN "FuHwids"
8
9
#include "config.h"
10
11
#include <gio/gio.h>
12
13
#include "fwupd-common.h"
14
#include "fwupd-error.h"
15
16
#include "fu-hwids-private.h"
17
18
/**
19
 * FuHwids:
20
 *
21
 * A the hardware IDs on the system.
22
 *
23
 * Note, these are called "CHIDs" in Microsoft Windows and the results here
24
 * will match that of `ComputerHardwareIds.exe`.
25
 *
26
 * See also: [class@FuSmbios]
27
 */
28
29
struct _FuHwids {
30
  GObject parent_instance;
31
  GHashTable *hash_values;   /* BiosVersion->"1.2.3 " */
32
  GHashTable *hash_values_display; /* BiosVersion->"1.2.3" */
33
  GHashTable *hash_guid;     /* a-c-b-d->1 */
34
  GPtrArray *array_guids;    /* a-c-b-d */
35
  GHashTable *chids;     /* "HardwareID-5"->"Manufacturer&ProductName" */
36
};
37
38
G_DEFINE_TYPE(FuHwids, fu_hwids, G_TYPE_OBJECT)
39
40
/**
41
 * fu_hwids_get_value:
42
 * @self: a #FuHwids
43
 * @key: a DMI ID, e.g. `BiosVersion`
44
 *
45
 * Gets the cached value for one specific key that is valid ASCII and suitable
46
 * for display.
47
 *
48
 * Returns: the string, e.g. `1.2.3`, or %NULL if not found
49
 *
50
 * Since: 0.9.3
51
 **/
52
const gchar *
53
fu_hwids_get_value(FuHwids *self, const gchar *key)
54
0
{
55
0
  return g_hash_table_lookup(self->hash_values_display, key);
56
0
}
57
58
/**
59
 * fu_hwids_has_guid:
60
 * @self: a #FuHwids
61
 * @guid: a GUID, e.g. `059eb22d-6dc7-59af-abd3-94bbe017f67c`
62
 *
63
 * Finds out if a hardware GUID exists.
64
 *
65
 * Returns: %TRUE if the GUID exists
66
 *
67
 * Since: 0.9.3
68
 **/
69
gboolean
70
fu_hwids_has_guid(FuHwids *self, const gchar *guid)
71
0
{
72
0
  return g_hash_table_lookup(self->hash_guid, guid) != NULL;
73
0
}
74
75
/**
76
 * fu_hwids_get_guids:
77
 * @self: a #FuHwids
78
 *
79
 * Returns all the defined HWIDs
80
 *
81
 * Returns: (transfer none) (element-type utf8): an array of GUIDs
82
 *
83
 * Since: 0.9.3
84
 **/
85
GPtrArray *
86
fu_hwids_get_guids(FuHwids *self)
87
0
{
88
0
  return self->array_guids;
89
0
}
90
91
/**
92
 * fu_hwids_get_keys:
93
 * @self: a #FuHwids
94
 *
95
 * Returns all the defined HWID keys.
96
 *
97
 * Returns: (transfer container) (element-type utf8): All the known keys,
98
 * e.g. %FU_HWIDS_KEY_FAMILY
99
 *
100
 * Since: 1.5.6
101
 **/
102
GPtrArray *
103
fu_hwids_get_keys(FuHwids *self)
104
0
{
105
0
  GPtrArray *array = g_ptr_array_new();
106
0
  const gchar *keys[] = {FU_HWIDS_KEY_BIOS_VENDOR,
107
0
             FU_HWIDS_KEY_BIOS_VERSION,
108
0
             FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
109
0
             FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
110
0
             FU_HWIDS_KEY_FIRMWARE_MAJOR_RELEASE,
111
0
             FU_HWIDS_KEY_FIRMWARE_MINOR_RELEASE,
112
0
             FU_HWIDS_KEY_MANUFACTURER,
113
0
             FU_HWIDS_KEY_FAMILY,
114
0
             FU_HWIDS_KEY_PRODUCT_NAME,
115
0
             FU_HWIDS_KEY_PRODUCT_SKU,
116
0
             FU_HWIDS_KEY_ENCLOSURE_KIND,
117
0
             FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
118
0
             FU_HWIDS_KEY_BASEBOARD_PRODUCT,
119
0
             NULL};
120
0
  g_return_val_if_fail(FU_IS_HWIDS(self), NULL);
121
0
  for (guint i = 0; keys[i] != NULL; i++)
122
0
    g_ptr_array_add(array, (gpointer)keys[i]);
123
0
  return array;
124
0
}
125
126
static gchar *
127
fu_hwids_get_guid_for_str(const gchar *str, GError **error)
128
0
{
129
0
  glong items_written = 0;
130
0
  g_autofree gunichar2 *data = NULL;
131
132
  /* convert to UTF-16 and convert to GUID using custom namespace */
133
0
  data = g_utf8_to_utf16(str, -1, NULL, &items_written, error);
134
0
  if (data == NULL)
135
0
    return NULL;
136
137
0
  if (items_written == 0) {
138
0
    g_set_error_literal(error,
139
0
            FWUPD_ERROR,
140
0
            FWUPD_ERROR_INVALID_FILE,
141
0
            "no GUIDs in data");
142
0
    return NULL;
143
0
  }
144
145
  /* ensure the data is in little endian format */
146
0
  for (glong i = 0; i < items_written; i++)
147
0
    data[i] = GUINT16_TO_LE(data[i]); /* nocheck:blocked */
148
149
  /* convert to a GUID */
150
0
  return fwupd_guid_hash_data((guint8 *)data,
151
0
            items_written * 2,
152
0
            FWUPD_GUID_FLAG_NAMESPACE_MICROSOFT);
153
0
}
154
155
/**
156
 * fu_hwids_get_replace_keys:
157
 * @self: a #FuHwids
158
 * @key: a CHID key, e.g. `HardwareID-03`
159
 *
160
 * Gets the defined values for a well known value.
161
 *
162
 * Returns: the replacement value, e.g. `Manufacturer&ProductName`, or %NULL for error.
163
 *
164
 * Since: 0.9.3
165
 **/
166
const gchar *
167
fu_hwids_get_replace_keys(FuHwids *self, const gchar *key)
168
0
{
169
0
  const gchar *value;
170
171
0
  g_return_val_if_fail(FU_IS_HWIDS(self), NULL);
172
0
  g_return_val_if_fail(key != NULL, NULL);
173
174
0
  value = g_hash_table_lookup(self->chids, key);
175
0
  if (value != NULL)
176
0
    return value;
177
0
  return key;
178
0
}
179
180
/**
181
 * fu_hwids_add_chid:
182
 * @self: a #FuHwids
183
 * @key: an textual ID, e.g. `HardwareID-05`
184
 * @value: a composite hardware key, e.g. `Manufacturer&ProductName`
185
 *
186
 * Defines a "Computer Hardware ID" in terms of a set of SMBIOS values.
187
 *
188
 * Since: 1.9.16
189
 **/
190
void
191
fu_hwids_add_chid(FuHwids *self, const gchar *key, const gchar *value)
192
0
{
193
0
  g_return_if_fail(FU_IS_HWIDS(self));
194
0
  g_return_if_fail(key != NULL);
195
0
  g_return_if_fail(value != NULL);
196
0
  g_hash_table_insert(self->chids, g_strdup(key), g_strdup(value));
197
0
}
198
199
static gint
200
fu_hwids_sort_keys_cb(gconstpointer a, gconstpointer b)
201
0
{
202
0
  const gchar *key1 = *((gchar **)a);
203
0
  const gchar *key2 = *((gchar **)b);
204
0
  return g_strcmp0(key1, key2);
205
0
}
206
207
/**
208
 * fu_hwids_get_chid_keys:
209
 * @self: a #FuHwids
210
 *
211
 * Returns all the CHID keys added by fu_hwids_add_chid().
212
 *
213
 * Returns: (transfer container) (element-type utf8): IDs
214
 *
215
 * Since: 1.9.16
216
 **/
217
GPtrArray *
218
fu_hwids_get_chid_keys(FuHwids *self)
219
0
{
220
0
  GHashTableIter iter;
221
0
  gpointer key;
222
0
  g_autoptr(GPtrArray) keys = g_ptr_array_new_with_free_func(g_free);
223
224
0
  g_return_val_if_fail(FU_IS_HWIDS(self), NULL);
225
226
0
  g_hash_table_iter_init(&iter, self->chids);
227
0
  while (g_hash_table_iter_next(&iter, &key, NULL))
228
0
    g_ptr_array_add(keys, g_strdup(key));
229
0
  g_ptr_array_sort(keys, fu_hwids_sort_keys_cb);
230
231
0
  return g_steal_pointer(&keys);
232
0
}
233
234
/**
235
 * fu_hwids_add_value:
236
 * @self: a #FuHwids
237
 * @key: a key, e.g. %FU_HWIDS_KEY_PRODUCT_SKU
238
 * @value: (nullable): a new value, e.g. `ExampleModel`
239
 *
240
 * Sets override values so you can emulate another system.
241
 *
242
 * This function has no effect if called after fu_hwids_setup()
243
 *
244
 * Since: 1.8.10
245
 **/
246
void
247
fu_hwids_add_value(FuHwids *self, const gchar *key, const gchar *value)
248
0
{
249
0
  g_return_if_fail(FU_IS_HWIDS(self));
250
0
  g_return_if_fail(key != NULL);
251
252
  /* does not replace; first value set wins */
253
0
  if (g_hash_table_contains(self->hash_values, key))
254
0
    return;
255
0
  g_hash_table_insert(self->hash_values, g_strdup(key), g_strdup(value));
256
257
  /* make suitable for display */
258
0
  if (value != NULL) {
259
0
    g_autofree gchar *value_safe = g_str_to_ascii(value, "C");
260
0
    g_strdelimit(value_safe, "\n\r", '\0');
261
0
    g_strchomp(value_safe);
262
0
    g_hash_table_insert(self->hash_values_display,
263
0
            g_strdup(key),
264
0
            g_steal_pointer(&value_safe));
265
0
  } else {
266
0
    g_hash_table_insert(self->hash_values_display, g_strdup(key), NULL);
267
0
  }
268
0
}
269
270
/**
271
 * fu_hwids_get_replace_values:
272
 * @self: a #FuHwids
273
 * @keys: a key, e.g. `HardwareID-3` or %FU_HWIDS_KEY_PRODUCT_SKU
274
 * @error: (nullable): optional return location for an error
275
 *
276
 * Gets the replacement values for a HardwareID key or plain key.
277
 *
278
 * Returns: a string, e.g. `LENOVO&ThinkPad T440s`, or %NULL for error.
279
 *
280
 * Since: 0.9.3
281
 **/
282
gchar *
283
fu_hwids_get_replace_values(FuHwids *self, const gchar *keys, GError **error)
284
0
{
285
0
  g_auto(GStrv) split = NULL;
286
0
  g_autoptr(GString) str = g_string_new(NULL);
287
288
0
  g_return_val_if_fail(FU_IS_HWIDS(self), NULL);
289
0
  g_return_val_if_fail(keys != NULL, NULL);
290
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
291
292
  /* do any replacements */
293
0
  keys = fu_hwids_get_replace_keys(self, keys);
294
295
  /* get each part of the HWID */
296
0
  split = g_strsplit(keys, "&", -1);
297
0
  for (guint j = 0; split[j] != NULL; j++) {
298
0
    const gchar *tmp = g_hash_table_lookup(self->hash_values, split[j]);
299
0
    if (tmp == NULL) {
300
0
      g_set_error(error,
301
0
            FWUPD_ERROR,
302
0
            FWUPD_ERROR_NOT_FOUND,
303
0
            "not available as '%s' unknown",
304
0
            split[j]);
305
0
      return NULL;
306
0
    }
307
0
    g_string_append_printf(str, "%s&", tmp);
308
0
  }
309
0
  if (str->len > 0)
310
0
    g_string_truncate(str, str->len - 1);
311
0
  return g_strdup(str->str);
312
0
}
313
314
/**
315
 * fu_hwids_get_guid:
316
 * @self: a #FuHwids
317
 * @keys: a key, e.g. `HardwareID-3` or %FU_HWIDS_KEY_PRODUCT_SKU
318
 * @error: (nullable): optional return location for an error
319
 *
320
 * Gets the GUID for a specific key.
321
 *
322
 * Returns: a string, or %NULL for error.
323
 *
324
 * Since: 0.9.3
325
 **/
326
gchar *
327
fu_hwids_get_guid(FuHwids *self, const gchar *keys, GError **error)
328
0
{
329
0
  g_autofree gchar *tmp = NULL;
330
331
0
  g_return_val_if_fail(FU_IS_HWIDS(self), NULL);
332
0
  g_return_val_if_fail(keys != NULL, NULL);
333
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
334
335
0
  tmp = fu_hwids_get_replace_values(self, keys, error);
336
0
  if (tmp == NULL)
337
0
    return NULL;
338
0
  return fu_hwids_get_guid_for_str(tmp, error);
339
0
}
340
341
/**
342
 * fu_hwids_add_guid:
343
 * @self: a #FuHwids
344
 * @guid: a GUID
345
 *
346
 * Adds a HWID GUID value.
347
 *
348
 * Since: 1.8.10
349
 **/
350
void
351
fu_hwids_add_guid(FuHwids *self, const gchar *guid)
352
0
{
353
0
  g_return_if_fail(FU_IS_HWIDS(self));
354
0
  g_return_if_fail(guid != NULL);
355
0
  g_hash_table_insert(self->hash_guid, g_strdup(guid), GUINT_TO_POINTER(1));
356
0
  g_ptr_array_add(self->array_guids, g_strdup(guid));
357
0
}
358
359
/**
360
 * fu_hwids_setup:
361
 * @self: a #FuHwids
362
 * @error: (nullable): optional return location for an error
363
 *
364
 * Adds all the `HardwareID` GUIDs from the previously supplied data.
365
 *
366
 * Returns: %TRUE for success
367
 *
368
 * Since: 0.9.3
369
 **/
370
gboolean
371
fu_hwids_setup(FuHwids *self, GError **error)
372
0
{
373
0
  g_autoptr(GPtrArray) chids = fu_hwids_get_chid_keys(self);
374
375
0
  g_return_val_if_fail(FU_IS_HWIDS(self), FALSE);
376
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
377
378
  /* add GUIDs */
379
0
  for (guint i = 0; i < chids->len; i++) {
380
0
    const gchar *key = g_ptr_array_index(chids, i);
381
0
    g_autofree gchar *guid = NULL;
382
0
    g_autoptr(GError) error_local = NULL;
383
384
    /* get the GUID and add to hash */
385
0
    guid = fu_hwids_get_guid(self, key, &error_local);
386
0
    if (guid == NULL) {
387
0
      g_debug("%s is not available, %s", key, error_local->message);
388
0
      continue;
389
0
    }
390
0
    fu_hwids_add_guid(self, guid);
391
0
  }
392
393
0
  return TRUE;
394
0
}
395
396
static void
397
fu_hwids_finalize(GObject *object)
398
0
{
399
0
  FuHwids *self;
400
0
  g_return_if_fail(FU_IS_HWIDS(object));
401
0
  self = FU_HWIDS(object);
402
403
0
  g_hash_table_unref(self->hash_values);
404
0
  g_hash_table_unref(self->hash_values_display);
405
0
  g_hash_table_unref(self->hash_guid);
406
0
  g_hash_table_unref(self->chids);
407
0
  g_ptr_array_unref(self->array_guids);
408
409
0
  G_OBJECT_CLASS(fu_hwids_parent_class)->finalize(object);
410
0
}
411
412
static void
413
fu_hwids_class_init(FuHwidsClass *klass)
414
0
{
415
0
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
416
0
  object_class->finalize = fu_hwids_finalize;
417
0
}
418
419
static void
420
fu_hwids_init(FuHwids *self)
421
0
{
422
0
  self->hash_values = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
423
0
  self->hash_values_display = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
424
0
  self->hash_guid = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
425
0
  self->chids = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
426
0
  self->array_guids = g_ptr_array_new_with_free_func(g_free);
427
428
  /* Windows 10 CHIDs */
429
0
  fu_hwids_add_chid(self,
430
0
        "HardwareID-00",
431
0
        "Manufacturer&Family&ProductName&ProductSku&BiosVendor&BiosVersion&"
432
0
        "BiosMajorRelease&BiosMinorRelease");
433
0
  fu_hwids_add_chid(self,
434
0
        "HardwareID-01",
435
0
        "Manufacturer&Family&ProductName&BiosVendor&BiosVersion&"
436
0
        "BiosMajorRelease&BiosMinorRelease");
437
0
  fu_hwids_add_chid(self,
438
0
        "HardwareID-02",
439
0
        "Manufacturer&ProductName&BiosVendor&BiosVersion&"
440
0
        "BiosMajorRelease&BiosMinorRelease");
441
0
  fu_hwids_add_chid(self,
442
0
        "HardwareID-03",
443
0
        "Manufacturer&Family&ProductName&ProductSku&"
444
0
        "BaseboardManufacturer&BaseboardProduct");
445
0
  fu_hwids_add_chid(self, "HardwareID-04", "Manufacturer&Family&ProductName&ProductSku");
446
0
  fu_hwids_add_chid(self, "HardwareID-05", "Manufacturer&Family&ProductName");
447
0
  fu_hwids_add_chid(self,
448
0
        "HardwareID-06",
449
0
        "Manufacturer&ProductSku&BaseboardManufacturer&BaseboardProduct");
450
0
  fu_hwids_add_chid(self, "HardwareID-07", "Manufacturer&ProductSku");
451
0
  fu_hwids_add_chid(self,
452
0
        "HardwareID-08",
453
0
        "Manufacturer&ProductName&BaseboardManufacturer&BaseboardProduct");
454
0
  fu_hwids_add_chid(self, "HardwareID-09", "Manufacturer&ProductName");
455
0
  fu_hwids_add_chid(self,
456
0
        "HardwareID-10",
457
0
        "Manufacturer&Family&BaseboardManufacturer&BaseboardProduct");
458
0
  fu_hwids_add_chid(self, "HardwareID-11", "Manufacturer&Family");
459
0
  fu_hwids_add_chid(self, "HardwareID-12", "Manufacturer&EnclosureKind");
460
0
  fu_hwids_add_chid(self,
461
0
        "HardwareID-13",
462
0
        "Manufacturer&BaseboardManufacturer&BaseboardProduct");
463
0
  fu_hwids_add_chid(self, "HardwareID-14", "Manufacturer");
464
465
  /* used by the flashrom plugin */
466
0
  fu_hwids_add_chid(self,
467
0
        "fwupd-04",
468
0
        "Manufacturer&Family&ProductName&ProductSku&BiosVendor");
469
0
  fu_hwids_add_chid(self, "fwupd-05", "Manufacturer&Family&ProductName&BiosVendor");
470
0
  fu_hwids_add_chid(self, "fwupd-14", "Manufacturer&BiosVendor");
471
0
}
472
473
/**
474
 * fu_hwids_new:
475
 *
476
 * Creates a new #FuHwids
477
 *
478
 * Since: 0.9.3
479
 **/
480
FuHwids *
481
fu_hwids_new(void)
482
0
{
483
0
  FuHwids *self;
484
0
  self = g_object_new(FU_TYPE_HWIDS, NULL);
485
0
  return FU_HWIDS(self);
486
0
}