Coverage Report

Created: 2026-02-26 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-smbios.c
Line
Count
Source
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 "FuSmbios"
8
9
#include "config.h"
10
11
#include <gio/gio.h>
12
#include <string.h>
13
14
#ifdef _WIN32
15
#include <errhandlingapi.h>
16
#include <sysinfoapi.h>
17
#endif
18
19
#include "fwupd-error.h"
20
21
#include "fu-byte-array.h"
22
#include "fu-common.h"
23
#include "fu-input-stream.h"
24
#include "fu-path.h"
25
#include "fu-smbios-private.h"
26
#include "fu-smbios-struct.h"
27
#include "fu-string.h"
28
29
/**
30
 * FuSmbios:
31
 *
32
 * Enumerate the SMBIOS data on the system.
33
 *
34
 * See also: [class@FuHwids]
35
 */
36
37
struct _FuSmbios {
38
  FuFirmware parent_instance;
39
  FuPathStore *pstore;
40
  guint32 structure_table_len;
41
  GPtrArray *items;
42
};
43
44
typedef struct {
45
  guint8 type;
46
  guint16 handle;
47
  GByteArray *buf;
48
  GPtrArray *strings;
49
} FuSmbiosItem;
50
51
0
G_DEFINE_TYPE(FuSmbios, fu_smbios, FU_TYPE_FIRMWARE)
52
0
53
0
static FuSmbiosItem *
54
0
fu_smbios_item_new(void)
55
0
{
56
0
  FuSmbiosItem *item = g_new0(FuSmbiosItem, 1);
57
0
  item->strings = g_ptr_array_new_with_free_func(g_free);
58
0
  return item;
59
0
}
60
61
static void
62
fu_smbios_item_free(FuSmbiosItem *item)
63
0
{
64
0
  if (item->buf != NULL)
65
0
    g_byte_array_unref(item->buf);
66
0
  g_ptr_array_unref(item->strings);
67
0
  g_free(item);
68
0
}
69
70
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuSmbiosItem, fu_smbios_item_free)
71
72
static FuSmbiosItem *
73
fu_smbios_get_item_for_type_length(FuSmbios *self, guint8 type, guint8 length)
74
0
{
75
0
  for (guint i = 0; i < self->items->len; i++) {
76
0
    FuSmbiosItem *item = g_ptr_array_index(self->items, i);
77
0
    if (item->type != type)
78
0
      continue;
79
0
    if (length != FU_SMBIOS_STRUCTURE_LENGTH_ANY && length != item->buf->len) {
80
0
      g_debug("filtering SMBIOS structure by length: 0x%x != 0x%x",
81
0
        length,
82
0
        item->buf->len);
83
0
      continue;
84
0
    }
85
0
    return item;
86
0
  }
87
0
  return NULL;
88
0
}
89
90
static gboolean
91
fu_smbios_setup_from_data(FuSmbios *self, const guint8 *buf, gsize bufsz, GError **error)
92
0
{
93
  /* go through each structure */
94
0
  for (gsize i = 0; i < bufsz; i++) {
95
0
    FuSmbiosItem *item;
96
0
    guint8 length;
97
0
    g_autoptr(FuStructSmbiosStructure) st_str = NULL;
98
99
    /* sanity check */
100
0
    st_str = fu_struct_smbios_structure_parse(buf, bufsz, i, error);
101
0
    if (st_str == NULL)
102
0
      return FALSE;
103
0
    length = fu_struct_smbios_structure_get_length(st_str);
104
0
    if (length < st_str->buf->len) {
105
0
      g_set_error(error,
106
0
            FWUPD_ERROR,
107
0
            FWUPD_ERROR_INVALID_FILE,
108
0
            "structure smaller than allowed @0x%x",
109
0
            (guint)i);
110
0
      return FALSE;
111
0
    }
112
0
    if (i + length >= bufsz) {
113
0
      g_set_error(error,
114
0
            FWUPD_ERROR,
115
0
            FWUPD_ERROR_INVALID_FILE,
116
0
            "structure larger than available data @0x%x",
117
0
            (guint)i);
118
0
      return FALSE;
119
0
    }
120
121
    /* create a new result */
122
0
    item = fu_smbios_item_new();
123
0
    item->type = fu_struct_smbios_structure_get_type(st_str);
124
0
    item->handle = fu_struct_smbios_structure_get_handle(st_str);
125
0
    item->buf = g_byte_array_sized_new(length);
126
0
    g_byte_array_append(item->buf, buf + i, length);
127
0
    g_ptr_array_add(self->items, item);
128
129
    /* jump to the end of the formatted area of the struct */
130
0
    i += length;
131
132
    /* add strings from table */
133
0
    while (i < bufsz) {
134
0
      GString *str;
135
136
      /* end of string section */
137
0
      if (item->strings->len > 0 && buf[i] == 0x0)
138
0
        break;
139
140
      /* copy into string table */
141
0
      str = fu_strdup((const gchar *)buf, bufsz, i);
142
0
      i += str->len + 1;
143
0
      g_ptr_array_add(item->strings, g_string_free(str, FALSE));
144
0
    }
145
0
  }
146
147
  /* this has to exist */
148
0
  if (fu_smbios_get_item_for_type_length(self,
149
0
                 FU_SMBIOS_STRUCTURE_TYPE_SYSTEM,
150
0
                 FU_SMBIOS_STRUCTURE_LENGTH_ANY) == NULL) {
151
0
    g_set_error_literal(error,
152
0
            FWUPD_ERROR,
153
0
            FWUPD_ERROR_INVALID_FILE,
154
0
            "no structure with required type SYSTEM");
155
0
    return FALSE;
156
0
  }
157
158
  /* success */
159
0
  return TRUE;
160
0
}
161
162
/**
163
 * fu_smbios_setup_from_file:
164
 * @self: a #FuSmbios
165
 * @filename: a filename
166
 * @error: (nullable): optional return location for an error
167
 *
168
 * Reads all the SMBIOS values from a DMI blob.
169
 *
170
 * Returns: %TRUE for success
171
 *
172
 * Since: 1.0.0
173
 **/
174
gboolean
175
fu_smbios_setup_from_file(FuSmbios *self, const gchar *filename, GError **error)
176
0
{
177
0
  gsize sz = 0;
178
0
  g_autofree gchar *buf = NULL;
179
180
0
  g_return_val_if_fail(FU_IS_SMBIOS(self), FALSE);
181
0
  g_return_val_if_fail(filename != NULL, FALSE);
182
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
183
184
  /* DMI blob */
185
0
  if (!g_file_get_contents(filename, &buf, &sz, error))
186
0
    return FALSE;
187
0
  return fu_smbios_setup_from_data(self, (guint8 *)buf, sz, error);
188
0
}
189
190
static gboolean
191
fu_smbios_parse_ep32(FuSmbios *self, const guint8 *buf, gsize bufsz, GError **error)
192
0
{
193
0
  guint8 csum = 0;
194
0
  g_autofree gchar *version_str = NULL;
195
0
  g_autofree gchar *intermediate_anchor_str = NULL;
196
0
  g_autoptr(FuStructSmbiosEp32) st_ep32 = NULL;
197
198
  /* verify checksum */
199
0
  st_ep32 = fu_struct_smbios_ep32_parse(buf, bufsz, 0x0, error);
200
0
  if (st_ep32 == NULL)
201
0
    return FALSE;
202
0
  for (guint i = 0; i < bufsz; i++)
203
0
    csum += buf[i];
204
0
  if (csum != 0x00) {
205
0
    g_set_error_literal(error,
206
0
            FWUPD_ERROR,
207
0
            FWUPD_ERROR_INVALID_FILE,
208
0
            "entry point checksum invalid");
209
0
    return FALSE;
210
0
  }
211
212
  /* verify intermediate section */
213
0
  intermediate_anchor_str = fu_struct_smbios_ep32_get_intermediate_anchor_str(st_ep32);
214
0
  if (g_strcmp0(intermediate_anchor_str, "_DMI_") != 0) {
215
0
    g_set_error(error,
216
0
          FWUPD_ERROR,
217
0
          FWUPD_ERROR_INVALID_FILE,
218
0
          "intermediate anchor signature invalid, got %s",
219
0
          intermediate_anchor_str);
220
0
    return FALSE;
221
0
  }
222
0
  for (guint i = 10; i < bufsz; i++)
223
0
    csum += buf[i];
224
0
  if (csum != 0x00) {
225
0
    g_set_error_literal(error,
226
0
            FWUPD_ERROR,
227
0
            FWUPD_ERROR_INVALID_FILE,
228
0
            "intermediate checksum invalid");
229
0
    return FALSE;
230
0
  }
231
0
  self->structure_table_len = fu_struct_smbios_ep32_get_structure_table_len(st_ep32);
232
0
  version_str = g_strdup_printf("%u.%u",
233
0
              fu_struct_smbios_ep32_get_smbios_major_ver(st_ep32),
234
0
              fu_struct_smbios_ep32_get_smbios_minor_ver(st_ep32));
235
0
  fu_firmware_set_version(FU_FIRMWARE(self), version_str); /* nocheck:set-version */
236
0
  fu_firmware_set_version_raw(
237
0
      FU_FIRMWARE(self),
238
0
      (((guint16)fu_struct_smbios_ep32_get_smbios_major_ver(st_ep32)) << 8) +
239
0
    fu_struct_smbios_ep32_get_smbios_minor_ver(st_ep32));
240
0
  return TRUE;
241
0
}
242
243
static gboolean
244
fu_smbios_parse_ep64(FuSmbios *self, const guint8 *buf, gsize bufsz, GError **error)
245
0
{
246
0
  guint8 csum = 0;
247
0
  g_autofree gchar *version_str = NULL;
248
0
  g_autoptr(FuStructSmbiosEp64) st_ep64 = NULL;
249
250
  /* verify checksum */
251
0
  st_ep64 = fu_struct_smbios_ep64_parse(buf, bufsz, 0x0, error);
252
0
  if (st_ep64 == NULL)
253
0
    return FALSE;
254
0
  for (guint i = 0; i < bufsz; i++)
255
0
    csum += buf[i];
256
0
  if (csum != 0x00) {
257
0
    g_set_error_literal(error,
258
0
            FWUPD_ERROR,
259
0
            FWUPD_ERROR_INVALID_FILE,
260
0
            "entry point checksum invalid");
261
0
    return FALSE;
262
0
  }
263
0
  self->structure_table_len = fu_struct_smbios_ep64_get_structure_table_len(st_ep64);
264
0
  version_str = g_strdup_printf("%u.%u",
265
0
              fu_struct_smbios_ep64_get_smbios_major_ver(st_ep64),
266
0
              fu_struct_smbios_ep64_get_smbios_minor_ver(st_ep64));
267
0
  fu_firmware_set_version(FU_FIRMWARE(self), version_str); /* nocheck:set-version */
268
0
  return TRUE;
269
0
}
270
271
/**
272
 * fu_smbios_setup_from_path:
273
 * @self: a #FuSmbios
274
 * @path: a path, e.g. `/sys/firmware/dmi/tables`
275
 * @error: (nullable): optional return location for an error
276
 *
277
 * Reads all the SMBIOS values from a specific path.
278
 *
279
 * Returns: %TRUE for success
280
 *
281
 * Since: 1.0.0
282
 **/
283
gboolean
284
fu_smbios_setup_from_path(FuSmbios *self, const gchar *path, GError **error)
285
0
{
286
0
  gsize sz = 0;
287
0
  g_autofree gchar *dmi_fn = NULL;
288
0
  g_autofree gchar *dmi_raw = NULL;
289
0
  g_autofree gchar *ep_fn = NULL;
290
0
  g_autofree gchar *ep_raw = NULL;
291
292
0
  g_return_val_if_fail(FU_IS_SMBIOS(self), FALSE);
293
0
  g_return_val_if_fail(path != NULL, FALSE);
294
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
295
296
  /* get the smbios entry point */
297
0
  ep_fn = g_build_filename(path, "smbios_entry_point", NULL);
298
0
  if (!g_file_get_contents(ep_fn, &ep_raw, &sz, error)) {
299
0
    fwupd_error_convert(error);
300
0
    return FALSE;
301
0
  }
302
303
  /* check we got enough data to read the signature */
304
0
  if (sz < 5) {
305
0
    g_set_error(error,
306
0
          FWUPD_ERROR,
307
0
          FWUPD_ERROR_INVALID_FILE,
308
0
          "invalid smbios entry point got 0x%x bytes, expected 0x%x or 0x%x",
309
0
          (guint)sz,
310
0
          (guint)FU_STRUCT_SMBIOS_EP32_SIZE,
311
0
          (guint)FU_STRUCT_SMBIOS_EP64_SIZE);
312
0
    return FALSE;
313
0
  }
314
315
  /* parse 32 bit structure */
316
0
  if (memcmp(ep_raw, "_SM_", 4) == 0) {
317
0
    if (!fu_smbios_parse_ep32(self, (const guint8 *)ep_raw, sz, error))
318
0
      return FALSE;
319
0
  } else if (memcmp(ep_raw, "_SM3_", 5) == 0) {
320
0
    if (!fu_smbios_parse_ep64(self, (const guint8 *)ep_raw, sz, error))
321
0
      return FALSE;
322
0
  } else {
323
0
    g_autofree gchar *tmp = g_strndup(ep_raw, 4);
324
0
    g_set_error(error,
325
0
          FWUPD_ERROR,
326
0
          FWUPD_ERROR_INVALID_FILE,
327
0
          "SMBIOS signature invalid, got %s",
328
0
          tmp);
329
0
    return FALSE;
330
0
  }
331
332
  /* get the DMI data */
333
0
  dmi_fn = g_build_filename(path, "DMI", NULL);
334
0
  if (!g_file_get_contents(dmi_fn, &dmi_raw, &sz, error)) {
335
0
    fwupd_error_convert(error);
336
0
    return FALSE;
337
0
  }
338
0
  if (sz > self->structure_table_len) {
339
0
    g_set_error(error,
340
0
          FWUPD_ERROR,
341
0
          FWUPD_ERROR_INVALID_FILE,
342
0
          "invalid DMI data size, got 0x%x bytes, expected 0x%x",
343
0
          (guint)sz,
344
0
          self->structure_table_len);
345
0
    return FALSE;
346
0
  }
347
348
  /* parse blob */
349
0
  return fu_smbios_setup_from_data(self, (guint8 *)dmi_raw, sz, error);
350
0
}
351
352
static gboolean
353
fu_smbios_parse(FuFirmware *firmware,
354
    GInputStream *stream,
355
    FuFirmwareParseFlags flags,
356
    GError **error)
357
0
{
358
0
  FuSmbios *self = FU_SMBIOS(firmware);
359
0
  g_autoptr(GBytes) fw = NULL;
360
0
  fw = fu_input_stream_read_bytes(stream, 0x0, G_MAXSIZE, NULL, error);
361
0
  if (fw == NULL)
362
0
    return FALSE;
363
0
  return fu_smbios_setup_from_data(self,
364
0
           g_bytes_get_data(fw, NULL),
365
0
           g_bytes_get_size(fw),
366
0
           error);
367
0
}
368
369
#ifdef _WIN32
370
#define FU_SMBIOS_FT_SIG_ACPI 0x41435049
371
#define FU_SMBIOS_FT_SIG_FIRM 0x4649524D
372
#define FU_SMBIOS_FT_SIG_RSMB 0x52534D42
373
#define FU_SMBIOS_FT_RAW_OFFSET 0x08
374
#endif
375
376
/**
377
 * fu_smbios_setup:
378
 * @self: a #FuSmbios
379
 * @error: (nullable): optional return location for an error
380
 *
381
 * Reads all the SMBIOS values from the hardware.
382
 *
383
 * Returns: %TRUE for success
384
 *
385
 * Since: 1.0.0
386
 **/
387
gboolean
388
fu_smbios_setup(FuSmbios *self, GError **error)
389
0
{
390
#ifdef _WIN32
391
  gsize bufsz;
392
  guint rc;
393
  g_autofree guint8 *buf = NULL;
394
395
  rc = GetSystemFirmwareTable(FU_SMBIOS_FT_SIG_RSMB, 0, 0, 0);
396
  if (rc <= 0) {
397
    g_set_error(error,
398
          FWUPD_ERROR,
399
          FWUPD_ERROR_INVALID_FILE,
400
          "failed to access RSMB [%u]",
401
          (guint)GetLastError());
402
    return FALSE;
403
  }
404
  if (rc < FU_SMBIOS_FT_RAW_OFFSET || rc > 0x1000000) {
405
    g_set_error_literal(error,
406
            FWUPD_ERROR,
407
            FWUPD_ERROR_INVALID_FILE,
408
            "RSMB impossible size");
409
    return FALSE;
410
  }
411
  bufsz = rc;
412
  buf = g_malloc0(bufsz);
413
  rc = GetSystemFirmwareTable(FU_SMBIOS_FT_SIG_RSMB, 0, buf, (DWORD)bufsz);
414
  if (rc <= 0) {
415
    g_set_error(error,
416
          FWUPD_ERROR,
417
          FWUPD_ERROR_INVALID_FILE,
418
          "failed to read RSMB [%u]",
419
          (guint)GetLastError());
420
    return FALSE;
421
  }
422
  return fu_smbios_setup_from_data(self,
423
           buf + FU_SMBIOS_FT_RAW_OFFSET,
424
           bufsz - FU_SMBIOS_FT_RAW_OFFSET,
425
           error);
426
#else
427
0
  g_autofree gchar *path = NULL;
428
0
  g_autoptr(GError) error_local = NULL;
429
430
0
  g_return_val_if_fail(FU_IS_SMBIOS(self), FALSE);
431
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
432
433
  /* DMI */
434
0
  path = fu_path_store_build_filename(self->pstore,
435
0
              error,
436
0
              FU_PATH_KIND_SYSFSDIR_FW,
437
0
              "dmi",
438
0
              "tables",
439
0
              NULL);
440
0
  if (path == NULL)
441
0
    return FALSE;
442
0
  if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
443
0
    g_set_error(error,
444
0
          FWUPD_ERROR,
445
0
          FWUPD_ERROR_NOT_SUPPORTED,
446
0
          "SMBIOS tables not found at %s",
447
0
          path);
448
0
    return FALSE;
449
0
  }
450
0
  if (!fu_smbios_setup_from_path(self, path, &error_local)) {
451
0
    if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE)) {
452
0
      g_propagate_error(error, g_steal_pointer(&error_local));
453
0
      return FALSE;
454
0
    }
455
0
    g_debug("ignoring %s", error_local->message);
456
0
  }
457
458
  /* success */
459
0
  return TRUE;
460
0
#endif
461
0
}
462
463
static void
464
fu_smbios_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
465
0
{
466
0
  FuSmbios *self = FU_SMBIOS(firmware);
467
468
0
  for (guint i = 0; i < self->items->len; i++) {
469
0
    FuSmbiosItem *item = g_ptr_array_index(self->items, i);
470
0
    g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "item", NULL);
471
0
    g_autofree gchar *buf = fu_byte_array_to_string(item->buf);
472
0
    fu_xmlb_builder_insert_kx(bc, "type", item->type);
473
0
    fu_xmlb_builder_insert_kx(bc, "length", item->buf->len);
474
0
    fu_xmlb_builder_insert_kx(bc, "handle", item->handle);
475
0
    fu_xmlb_builder_insert_kv(bc, "buf", buf);
476
0
    for (guint j = 0; j < item->strings->len; j++) {
477
0
      const gchar *tmp = g_ptr_array_index(item->strings, j);
478
0
      g_autofree gchar *title = g_strdup_printf("%02u", j);
479
0
      g_autofree gchar *value = fu_strsafe(tmp, 40);
480
0
      xb_builder_node_insert_text(bc, "string", value, "idx", title, NULL);
481
0
    }
482
0
  }
483
0
}
484
485
static gboolean
486
fu_smbios_build_item(FuSmbios *self, XbNode *n, GError **error)
487
0
{
488
0
  const gchar *str;
489
0
  guint64 tmp;
490
0
  g_autoptr(FuSmbiosItem) item = fu_smbios_item_new();
491
0
  g_autoptr(GPtrArray) xb_strings = NULL;
492
493
  /* optional type */
494
0
  tmp = xb_node_query_text_as_uint(n, "type", NULL);
495
0
  if (tmp != G_MAXUINT64) {
496
0
    if (tmp > G_MAXUINT8) {
497
0
      g_set_error_literal(error,
498
0
              FWUPD_ERROR,
499
0
              FWUPD_ERROR_INVALID_DATA,
500
0
              "invalid item type");
501
0
      return FALSE;
502
0
    }
503
0
    item->type = tmp;
504
0
  }
505
506
  /* optional handle */
507
0
  tmp = xb_node_query_text_as_uint(n, "handle", NULL);
508
0
  if (tmp != G_MAXUINT64) {
509
0
    if (tmp > G_MAXUINT16) {
510
0
      g_set_error_literal(error,
511
0
              FWUPD_ERROR,
512
0
              FWUPD_ERROR_INVALID_DATA,
513
0
              "invalid item handle");
514
0
      return FALSE;
515
0
    }
516
0
    item->handle = tmp;
517
0
  }
518
519
  /* optional data buffer */
520
0
  str = xb_node_query_text(n, "buf", NULL);
521
0
  if (str != NULL) {
522
0
    item->buf = fu_byte_array_from_string(str, error);
523
0
    if (item->buf == NULL)
524
0
      return FALSE;
525
0
  }
526
527
  /* optional string table */
528
0
  xb_strings = xb_node_query(n, "string", 0, NULL);
529
0
  if (xb_strings != NULL) {
530
0
    for (guint i = 0; i < xb_strings->len; i++) {
531
0
      XbNode *c = g_ptr_array_index(xb_strings, i);
532
0
      g_ptr_array_add(item->strings, g_strdup(xb_node_get_text(c)));
533
0
    }
534
0
  }
535
536
  /* success */
537
0
  g_ptr_array_add(self->items, g_steal_pointer(&item));
538
0
  return TRUE;
539
0
}
540
541
static gboolean
542
fu_smbios_build(FuFirmware *firmware, XbNode *n, GError **error)
543
0
{
544
0
  FuSmbios *self = FU_SMBIOS(firmware);
545
0
  g_autoptr(GPtrArray) xb_items = NULL;
546
547
  /* optional items */
548
0
  xb_items = xb_node_query(n, "item", 0, NULL);
549
0
  if (xb_items != NULL) {
550
0
    for (guint i = 0; i < xb_items->len; i++) {
551
0
      XbNode *c = g_ptr_array_index(xb_items, i);
552
0
      if (!fu_smbios_build_item(self, c, error))
553
0
        return FALSE;
554
0
    }
555
0
  }
556
557
  /* success */
558
0
  return TRUE;
559
0
}
560
561
/**
562
 * fu_smbios_get_data:
563
 * @self: a #FuSmbios
564
 * @type: a structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS
565
 * @length: expected length of the structure, or %FU_SMBIOS_STRUCTURE_LENGTH_ANY
566
 * @error: (nullable): optional return location for an error
567
 *
568
 * Reads all the SMBIOS data blobs of a specified type.
569
 *
570
 * Returns: (transfer container) (element-type GBytes): a #GBytes, or %NULL if invalid or not found
571
 *
572
 * Since: 2.0.7
573
 **/
574
GPtrArray *
575
fu_smbios_get_data(FuSmbios *self, guint8 type, guint8 length, GError **error)
576
0
{
577
0
  g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref);
578
579
0
  g_return_val_if_fail(FU_IS_SMBIOS(self), NULL);
580
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
581
582
0
  for (guint i = 0; i < self->items->len; i++) {
583
0
    FuSmbiosItem *item = g_ptr_array_index(self->items, i);
584
0
    if (item->type != type)
585
0
      continue;
586
0
    if (length != FU_SMBIOS_STRUCTURE_LENGTH_ANY && length != item->buf->len)
587
0
      continue;
588
0
    if (item->buf->len == 0)
589
0
      continue;
590
0
    g_ptr_array_add(array, g_bytes_new(item->buf->data, item->buf->len));
591
0
  }
592
0
  if (array->len == 0) {
593
0
    g_set_error(error,
594
0
          FWUPD_ERROR,
595
0
          FWUPD_ERROR_INVALID_FILE,
596
0
          "no structures with type %02x",
597
0
          type);
598
0
    return NULL;
599
0
  }
600
0
  return g_steal_pointer(&array);
601
0
}
602
603
/**
604
 * fu_smbios_get_integer:
605
 * @self: a #FuSmbios
606
 * @type: a structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS
607
 * @length: expected length of the structure, or %FU_SMBIOS_STRUCTURE_LENGTH_ANY
608
 * @offset: a structure offset
609
 * @error: (nullable): optional return location for an error
610
 *
611
 * Reads an integer value from the SMBIOS string table of a specific structure.
612
 *
613
 * The @type and @offset can be referenced from the DMTF SMBIOS specification:
614
 * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf
615
 *
616
 * Returns: an integer, or %G_MAXUINT if invalid or not found
617
 *
618
 * Since: 2.0.7
619
 **/
620
guint
621
fu_smbios_get_integer(FuSmbios *self, guint8 type, guint8 length, guint8 offset, GError **error)
622
0
{
623
0
  FuSmbiosItem *item;
624
625
0
  g_return_val_if_fail(FU_IS_SMBIOS(self), 0);
626
0
  g_return_val_if_fail(error == NULL || *error == NULL, 0);
627
628
  /* get item */
629
0
  item = fu_smbios_get_item_for_type_length(self, type, length);
630
0
  if (item == NULL) {
631
0
    g_set_error(error,
632
0
          FWUPD_ERROR,
633
0
          FWUPD_ERROR_INVALID_FILE,
634
0
          "no structure with type %02x",
635
0
          type);
636
0
    return G_MAXUINT;
637
0
  }
638
639
  /* check offset valid */
640
0
  if (offset >= item->buf->len) {
641
0
    g_set_error(error,
642
0
          FWUPD_ERROR,
643
0
          FWUPD_ERROR_INVALID_FILE,
644
0
          "offset bigger than size %u",
645
0
          item->buf->len);
646
0
    return G_MAXUINT;
647
0
  }
648
649
  /* success */
650
0
  return item->buf->data[offset];
651
0
}
652
653
/**
654
 * fu_smbios_get_string:
655
 * @self: a #FuSmbios
656
 * @type: a structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS
657
 * @length: expected length of the structure, or %FU_SMBIOS_STRUCTURE_LENGTH_ANY
658
 * @offset: a structure offset
659
 * @error: (nullable): optional return location for an error
660
 *
661
 * Reads a string from the SMBIOS string table of a specific structure.
662
 *
663
 * The @type and @offset can be referenced from the DMTF SMBIOS specification:
664
 * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf
665
 *
666
 * Returns: a string, or %NULL if invalid or not found
667
 *
668
 * Since: 2.0.7
669
 **/
670
const gchar *
671
fu_smbios_get_string(FuSmbios *self, guint8 type, guint8 length, guint8 offset, GError **error)
672
0
{
673
0
  FuSmbiosItem *item;
674
675
0
  g_return_val_if_fail(FU_IS_SMBIOS(self), NULL);
676
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
677
678
  /* get item */
679
0
  item = fu_smbios_get_item_for_type_length(self, type, length);
680
0
  if (item == NULL) {
681
0
    g_set_error(error,
682
0
          FWUPD_ERROR,
683
0
          FWUPD_ERROR_INVALID_FILE,
684
0
          "no structure with type %02x",
685
0
          type);
686
0
    return NULL;
687
0
  }
688
689
  /* check offset valid */
690
0
  if (offset >= item->buf->len) {
691
0
    g_set_error(error,
692
0
          FWUPD_ERROR,
693
0
          FWUPD_ERROR_INVALID_FILE,
694
0
          "offset bigger than size %u",
695
0
          item->buf->len);
696
0
    return NULL;
697
0
  }
698
0
  if (item->buf->data[offset] == 0x00) {
699
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no data available");
700
0
    return NULL;
701
0
  }
702
703
  /* check string index valid */
704
0
  if (item->buf->data[offset] > item->strings->len) {
705
0
    g_set_error(error,
706
0
          FWUPD_ERROR,
707
0
          FWUPD_ERROR_INVALID_FILE,
708
0
          "index larger than string table %u",
709
0
          item->strings->len);
710
0
    return NULL;
711
0
  }
712
0
  return g_ptr_array_index(item->strings, item->buf->data[offset] - 1);
713
0
}
714
715
static void
716
fu_smbios_finalize(GObject *object)
717
0
{
718
0
  FuSmbios *self = FU_SMBIOS(object);
719
0
  if (self->pstore != NULL)
720
0
    g_object_unref(self->pstore);
721
0
  g_ptr_array_unref(self->items);
722
0
  G_OBJECT_CLASS(fu_smbios_parent_class)->finalize(object);
723
0
}
724
725
static void
726
fu_smbios_class_init(FuSmbiosClass *klass)
727
0
{
728
0
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
729
0
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
730
0
  object_class->finalize = fu_smbios_finalize;
731
0
  firmware_class->parse = fu_smbios_parse;
732
0
  firmware_class->build = fu_smbios_build;
733
0
  firmware_class->export = fu_smbios_export;
734
0
}
735
736
static void
737
fu_smbios_init(FuSmbios *self)
738
0
{
739
0
  self->items = g_ptr_array_new_with_free_func((GDestroyNotify)fu_smbios_item_free);
740
0
}
741
742
/**
743
 * fu_smbios_new:
744
 *
745
 * Creates a new object to parse SMBIOS data.
746
 *
747
 * Returns: a #FuSmbios
748
 *
749
 * Since: 1.0.0
750
 **/
751
FuSmbios *
752
fu_smbios_new(FuPathStore *pstore)
753
0
{
754
0
  FuSmbios *self;
755
756
0
  g_return_val_if_fail(FU_IS_PATH_STORE(pstore), NULL);
757
758
0
  self = g_object_new(FU_TYPE_SMBIOS, NULL);
759
0
  self->pstore = g_object_ref(pstore);
760
0
  return FU_SMBIOS(self);
761
0
}