Coverage Report

Created: 2025-08-26 06:55

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