diff --git a/data/tests/devicetree/base/ibm,firmware-versions/version b/data/tests/devicetree/base/ibm,firmware-versions/version new file mode 100644 index 000000000..94b2bd66d --- /dev/null +++ b/data/tests/devicetree/base/ibm,firmware-versions/version @@ -0,0 +1 @@ +1.2.3-4 \ No newline at end of file diff --git a/data/tests/devicetree/base/model b/data/tests/devicetree/base/model new file mode 100644 index 000000000..86c8a128b --- /dev/null +++ b/data/tests/devicetree/base/model @@ -0,0 +1 @@ +ColorHug \ No newline at end of file diff --git a/data/tests/devicetree/base/model-name b/data/tests/devicetree/base/model-name new file mode 100644 index 000000000..9d37fc1ac --- /dev/null +++ b/data/tests/devicetree/base/model-name @@ -0,0 +1 @@ +To Be Filled By O.E.M. \ No newline at end of file diff --git a/data/tests/devicetree/base/name b/data/tests/devicetree/base/name new file mode 100644 index 000000000..f76dd238a Binary files /dev/null and b/data/tests/devicetree/base/name differ diff --git a/data/tests/devicetree/base/vendor b/data/tests/devicetree/base/vendor new file mode 100644 index 000000000..1555d1a5c --- /dev/null +++ b/data/tests/devicetree/base/vendor @@ -0,0 +1 @@ +Hughski Limited \ No newline at end of file diff --git a/data/tests/devicetree/base/vpd/name b/data/tests/devicetree/base/vpd/name new file mode 100644 index 000000000..b8acbc9ee Binary files /dev/null and b/data/tests/devicetree/base/vpd/name differ diff --git a/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/name b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/name new file mode 100644 index 000000000..1f22cb8d6 Binary files /dev/null and b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/name differ diff --git a/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number new file mode 100644 index 000000000..e00c9e144 --- /dev/null +++ b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number @@ -0,0 +1 @@ +PCB-CH001 \ No newline at end of file diff --git a/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor new file mode 100644 index 000000000..ffbb9e015 --- /dev/null +++ b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor @@ -0,0 +1 @@ +Richard Hughes \ No newline at end of file diff --git a/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/name b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/name new file mode 100644 index 000000000..88600030b Binary files /dev/null and b/data/tests/devicetree/base/vpd/root-node-vpd@a000/enclosure@1e00/name differ diff --git a/data/tests/devicetree/base/vpd/root-node-vpd@a000/name b/data/tests/devicetree/base/vpd/root-node-vpd@a000/name new file mode 100644 index 000000000..c6f7bd989 Binary files /dev/null and b/data/tests/devicetree/base/vpd/root-node-vpd@a000/name differ diff --git a/libfwupdplugin/fu-self-test.c b/libfwupdplugin/fu-self-test.c index 383788f9d..feab161b1 100644 --- a/libfwupdplugin/fu-self-test.c +++ b/libfwupdplugin/fu-self-test.c @@ -275,6 +275,31 @@ fu_smbios3_func (void) g_assert_cmpstr (str, ==, "Dell Inc."); } +static void +fu_smbios_dt_func (void) +{ + const gchar *str; + gboolean ret; + g_autofree gchar *path = NULL; + g_autoptr(FuSmbios) smbios = NULL; + g_autoptr(GError) error = NULL; + + path = g_build_filename (TESTDATADIR_SRC, "devicetree", "base", NULL); + smbios = fu_smbios_new (); + ret = fu_smbios_setup_from_path (smbios, path, &error); + g_assert_no_error (error); + g_assert (ret); + if (g_getenv ("VERBOSE") != NULL) { + g_autofree gchar *dump = fu_smbios_to_string (smbios); + g_debug ("%s", dump); + } + + /* get vendor */ + str = fu_smbios_get_string (smbios, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x04, &error); + g_assert_no_error (error); + g_assert_cmpstr (str, ==, "Hughski Limited"); +} + static void fu_hwids_func (void) { @@ -2068,6 +2093,7 @@ main (int argc, char **argv) g_test_add_func ("/fwupd/hwids", fu_hwids_func); g_test_add_func ("/fwupd/smbios", fu_smbios_func); g_test_add_func ("/fwupd/smbios3", fu_smbios3_func); + g_test_add_func ("/fwupd/smbios{dt}", fu_smbios_dt_func); g_test_add_func ("/fwupd/firmware", fu_firmware_func); g_test_add_func ("/fwupd/firmware{dedupe}", fu_firmware_dedupe_func); g_test_add_func ("/fwupd/firmware{build}", fu_firmware_build_func); diff --git a/libfwupdplugin/fu-smbios.c b/libfwupdplugin/fu-smbios.c index c1e9cdee4..90ec1e4f4 100644 --- a/libfwupdplugin/fu-smbios.c +++ b/libfwupdplugin/fu-smbios.c @@ -64,12 +64,71 @@ typedef struct __attribute__((packed)) { typedef struct { guint8 type; guint16 handle; - GBytes *data; + GByteArray *buf; GPtrArray *strings; } FuSmbiosItem; G_DEFINE_TYPE (FuSmbios, fu_smbios, G_TYPE_OBJECT) +static void +fu_smbios_convert_dt_string (FuSmbios *self, guint8 type, guint8 offset, + const gchar *path, const gchar *subpath) +{ + FuSmbiosItem *item = g_ptr_array_index (self->items, type); + gsize bufsz = 0; + g_autofree gchar *fn = g_build_filename (path, subpath, NULL); + g_autofree gchar *buf = NULL; + + /* not found */ + if (!g_file_get_contents (fn, &buf, &bufsz, NULL)) + return; + + /* add to strtab */ + g_ptr_array_add (item->strings, g_strndup (buf, bufsz)); + for (guint i = item->buf->len; i < (guint) offset + 1; i++) + fu_byte_array_append_uint8 (item->buf, 0x0); + item->buf->data[offset] = item->strings->len; +} + +static gboolean +fu_smbios_setup_from_path_dt (FuSmbios *self, const gchar *path, GError **error) +{ + /* add all four faked structures */ + for (guint i = 0; i < FU_SMBIOS_STRUCTURE_TYPE_LAST; i++) { + FuSmbiosItem *item = g_new0 (FuSmbiosItem, 1); + item->type = i; + item->buf = g_byte_array_new (); + item->strings = g_ptr_array_new_with_free_func (g_free); + g_ptr_array_add (self->items, item); + } + + /* DMI:Manufacturer */ + fu_smbios_convert_dt_string (self, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x04, + path, "vendor"); + + /* DMI:Family */ + fu_smbios_convert_dt_string (self, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x1a, + path, "model-name"); + + /* DMI:ProductName */ + fu_smbios_convert_dt_string (self, FU_SMBIOS_STRUCTURE_TYPE_SYSTEM, 0x05, + path, "model"); + + /* DMI:BiosVersion */ + fu_smbios_convert_dt_string (self, FU_SMBIOS_STRUCTURE_TYPE_BIOS, 0x05, + path, "ibm,firmware-versions/version"); + + /* DMI:BaseboardManufacturer */ + fu_smbios_convert_dt_string (self, FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, 0x04, + path, "vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/vendor"); + + /* DMI:BaseboardProduct */ + fu_smbios_convert_dt_string (self, FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD, 0x05, + path, "vpd/root-node-vpd@a000/enclosure@1e00/backplane@800/part-number"); + + return TRUE; +} + static gboolean fu_smbios_setup_from_data (FuSmbios *self, const guint8 *buf, gsize sz, GError **error) { @@ -93,8 +152,9 @@ fu_smbios_setup_from_data (FuSmbios *self, const guint8 *buf, gsize sz, GError * item = g_new0 (FuSmbiosItem, 1); item->type = str->type; item->handle = GUINT16_FROM_LE (str->handle); - item->data = g_bytes_new (buf + i, str->len); + item->buf = g_byte_array_sized_new (str->len); item->strings = g_ptr_array_new_with_free_func (g_free); + g_byte_array_append (item->buf, buf + i, str->len); g_ptr_array_add (self->items, item); /* jump to the end of the struct */ @@ -135,6 +195,17 @@ fu_smbios_setup_from_file (FuSmbios *self, const gchar *filename, GError **error { gsize sz = 0; g_autofree gchar *buf = NULL; + g_autofree gchar *basename = NULL; + + g_return_val_if_fail (FU_IS_SMBIOS (self), FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + + /* use a heuristic */ + basename = g_path_get_basename (filename); + if (g_strcmp0 (basename, "base") == 0) + return fu_smbios_setup_from_path_dt (self, filename, error); + + /* DMI blob */ if (!g_file_get_contents (filename, &buf, &sz, error)) return FALSE; return fu_smbios_setup_from_data (self, (guint8 *) buf, sz, error); @@ -229,20 +300,8 @@ fu_smbios_parse_ep64 (FuSmbios *self, const gchar *buf, gsize sz, GError **error return TRUE; } -/** - * fu_smbios_setup_from_path: - * @self: A #FuSmbios - * @path: A path, e.g. `/sys/firmware/dmi/tables` - * @error: A #GError or %NULL - * - * Reads all the SMBIOS values from a specific path. - * - * Returns: %TRUE for success - * - * Since: 1.0.0 - **/ -gboolean -fu_smbios_setup_from_path (FuSmbios *self, const gchar *path, GError **error) +static gboolean +fu_smbios_setup_from_path_dmi (FuSmbios *self, const gchar *path, GError **error) { gsize sz = 0; g_autofree gchar *dmi_fn = NULL; @@ -304,6 +363,33 @@ fu_smbios_setup_from_path (FuSmbios *self, const gchar *path, GError **error) return fu_smbios_setup_from_data (self, (guint8 *) dmi_raw, sz, error); } +/** + * fu_smbios_setup_from_path: + * @self: A #FuSmbios + * @path: A path, e.g. `/sys/firmware/dmi/tables` + * @error: A #GError or %NULL + * + * Reads all the SMBIOS values from a specific path. + * + * Returns: %TRUE for success + * + * Since: 1.0.0 + **/ +gboolean +fu_smbios_setup_from_path (FuSmbios *self, const gchar *path, GError **error) +{ + g_autofree gchar *basename = NULL; + + g_return_val_if_fail (FU_IS_SMBIOS (self), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + /* use a heuristic */ + basename = g_path_get_basename (path); + if (g_strcmp0 (basename, "base") == 0) + return fu_smbios_setup_from_path_dt (self, path, error); + return fu_smbios_setup_from_path_dmi (self, path, error); +} + /** * fu_smbios_setup: * @self: A #FuSmbios @@ -319,11 +405,30 @@ gboolean fu_smbios_setup (FuSmbios *self, GError **error) { g_autofree gchar *path = NULL; + g_autofree gchar *path_dt = NULL; g_autofree gchar *sysfsfwdir = NULL; + g_return_val_if_fail (FU_IS_SMBIOS (self), FALSE); + sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); + + /* DMI */ path = g_build_filename (sysfsfwdir, "dmi", "tables", NULL); - return fu_smbios_setup_from_path (self, path, error); + if (g_file_test (path, G_FILE_TEST_EXISTS)) + return fu_smbios_setup_from_path (self, path, error); + + /* DT */ + path_dt = g_build_filename (sysfsfwdir, "devicetree", "base", NULL); + if (g_file_test (path_dt, G_FILE_TEST_EXISTS)) + return fu_smbios_setup_from_path (self, path_dt, error); + + /* neither found */ + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "neither SMBIOS or DT found"); + return FALSE; + } /** @@ -348,8 +453,7 @@ fu_smbios_to_string (FuSmbios *self) for (guint i = 0; i < self->items->len; i++) { FuSmbiosItem *item = g_ptr_array_index (self->items, i); g_string_append_printf (str, "Type: %02x\n", item->type); - g_string_append_printf (str, " Length: %" G_GSIZE_FORMAT "\n", - g_bytes_get_size (item->data)); + g_string_append_printf (str, " Length: %u\n", item->buf->len); g_string_append_printf (str, " Handle: 0x%04x\n", item->handle); for (guint j = 0; j < item->strings->len; j++) { const gchar *tmp = g_ptr_array_index (item->strings, j); @@ -395,7 +499,7 @@ fu_smbios_get_data (FuSmbios *self, guint8 type, GError **error) "no structure with type %02x", type); return NULL; } - return g_bytes_ref (item->data); + return g_bytes_new (item->buf->data, item->buf->len); } /** @@ -418,8 +522,6 @@ const gchar * fu_smbios_get_string (FuSmbios *self, guint8 type, guint8 offset, GError **error) { FuSmbiosItem *item; - const guint8 *data; - gsize sz; g_return_val_if_fail (FU_IS_SMBIOS (self), NULL); @@ -434,15 +536,15 @@ fu_smbios_get_string (FuSmbios *self, guint8 type, guint8 offset, GError **error } /* check offset valid */ - data = g_bytes_get_data (item->data, &sz); - if (offset >= sz) { + if (offset >= item->buf->len) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "offset bigger than size %" G_GSIZE_FORMAT, sz); + "offset bigger than size %u", + item->buf->len); return NULL; } - if (data[offset] == 0x00) { + if (item->buf->data[offset] == 0x00) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, @@ -451,21 +553,21 @@ fu_smbios_get_string (FuSmbios *self, guint8 type, guint8 offset, GError **error } /* check string index valid */ - if (data[offset] > item->strings->len) { + if (item->buf->data[offset] > item->strings->len) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "index larger than string table %u", - data[offset]); + item->strings->len); return NULL; } - return g_ptr_array_index (item->strings, data[offset] - 1); + return g_ptr_array_index (item->strings, item->buf->data[offset] - 1); } static void fu_smbios_item_free (FuSmbiosItem *item) { - g_bytes_unref (item->data); + g_byte_array_unref (item->buf); g_ptr_array_unref (item->strings); g_free (item); } diff --git a/libfwupdplugin/fu-smbios.h b/libfwupdplugin/fu-smbios.h index e9ef02c74..a8084c351 100644 --- a/libfwupdplugin/fu-smbios.h +++ b/libfwupdplugin/fu-smbios.h @@ -18,6 +18,7 @@ FuSmbios *fu_smbios_new (void); #define FU_SMBIOS_STRUCTURE_TYPE_SYSTEM 0x01 #define FU_SMBIOS_STRUCTURE_TYPE_BASEBOARD 0x02 #define FU_SMBIOS_STRUCTURE_TYPE_CHASSIS 0x03 +#define FU_SMBIOS_STRUCTURE_TYPE_LAST 0x04 gchar *fu_smbios_to_string (FuSmbios *self);