/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 | } |