Coverage Report

Created: 2026-08-13 07:13

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