Coverage Report

Created: 2026-09-14 07:00

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