]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
smbios: copy QEMU tables
authorHeinrich Schuchardt <heinrich.schuchardt@canonical.com>
Sat, 23 Dec 2023 01:03:34 +0000 (02:03 +0100)
committerTom Rini <trini@konsulko.com>
Tue, 16 Jan 2024 22:05:29 +0000 (17:05 -0500)
QEMU provides SMBIOS tables with detailed information. We should not try to
replicate them in U-Boot.

If we want to inform about U-Boot, we can add a Firmware Inventory
Information (type 45) table in future.

Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
arch/x86/lib/tables.c
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/qfw_smbios.c [new file with mode: 0644]
lib/efi_loader/efi_smbios.c

index d43e77d3730f7774f5c1cf24dd5f630de5742e7d..12eae17c3968dcc8946937c9d926d8b4962ca12e 100644 (file)
@@ -62,7 +62,7 @@ static struct table_info table_list[] = {
 #ifdef CONFIG_GENERATE_ACPI_TABLE
        { "acpi", write_acpi_tables, BLOBLISTT_ACPI_TABLES, 0x10000, 0x1000},
 #endif
-#ifdef CONFIG_GENERATE_SMBIOS_TABLE
+#if defined(CONFIG_GENERATE_SMBIOS_TABLE) && !defined(CONFIG_QFW_SMBIOS)
        { "smbios", write_smbios_table, BLOBLISTT_SMBIOS_TABLES, 0x1000, 0x100},
 #endif
 };
index e85a0dd51ca5f1c4ab1f16e06c38057a40414347..f11ce72525f55b400ea12db1e776e25a540912d1 100644 (file)
@@ -554,6 +554,13 @@ config QFW_MMIO
          Hidden option to enable MMIO QEMU fw_cfg interface. This will be
          selected by the appropriate QEMU board.
 
+config QFW_SMBIOS
+       bool
+       default y
+       depends on QFW && SMBIOS && !SANDBOX
+       help
+         Hidden option to read SMBIOS tables from QEMU.
+
 config I2C_EEPROM
        bool "Enable driver for generic I2C-attached EEPROMs"
        depends on MISC
index 6bf1e79f7e291ba4544d79a12ad23c4f7af106c2..0432db6ed12393db3417455be2e5fcc8999ebfc2 100644 (file)
@@ -65,6 +65,7 @@ obj-y += qfw.o
 obj-$(CONFIG_QFW_ACPI) += qfw_acpi.o
 obj-$(CONFIG_QFW_PIO) += qfw_pio.o
 obj-$(CONFIG_QFW_MMIO) += qfw_mmio.o
+obj-$(CONFIG_QFW_SMBIOS) += qfw_smbios.o
 obj-$(CONFIG_SANDBOX) += qfw_sandbox.o
 endif
 obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o
diff --git a/drivers/misc/qfw_smbios.c b/drivers/misc/qfw_smbios.c
new file mode 100644 (file)
index 0000000..9019345
--- /dev/null
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2023 Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
+ */
+
+#define LOG_CATEGORY UCLASS_QFW
+
+#include <efi_loader.h>
+#include <errno.h>
+#include <log.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <qfw.h>
+#include <smbios.h>
+#include <tables_csum.h>
+#include <linux/sizes.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * qfw_load_smbios_table() - load a QEMU firmware file
+ *
+ * @dev:       QEMU firmware device
+ * @size:      parameter to return the size of the loaded table
+ * @name:      name of the table to load
+ * Return:     address of the loaded table, NULL on error
+ */
+static void *qfw_load_smbios_table(struct udevice *dev, uint32_t *size,
+                                  char *name)
+{
+       struct fw_file *file;
+       struct bios_linker_entry *table;
+
+       file = qfw_find_file(dev, name);
+       if (!file) {
+               log_debug("Can't find %s\n", name);
+               return NULL;
+       }
+
+       *size = be32_to_cpu(file->cfg.size);
+
+       table = malloc(*size);
+       if (!table) {
+               log_err("Out of memory\n");
+               return NULL;
+       }
+
+       qfw_read_entry(dev, be16_to_cpu(file->cfg.select), *size, table);
+
+       return table;
+}
+
+/**
+ * qfw_parse_smbios_anchor() - parse QEMU's SMBIOS anchor
+ *
+ * @dev:       QEMU firmware device
+ * @entry:     SMBIOS 3 structure to be filled from QEMU's anchor
+ * Return:     0 for success, -ve on error
+ */
+static int qfw_parse_smbios_anchor(struct udevice *dev,
+                                  struct smbios3_entry *entry)
+{
+       void *table;
+       uint32_t size;
+       struct smbios_entry *entry2;
+       struct smbios3_entry *entry3;
+       const char smbios_sig[] = "_SM_";
+       const char smbios3_sig[] = "_SM3_";
+       int ret = 0;
+
+       table = qfw_load_smbios_table(dev, &size, "etc/smbios/smbios-anchor");
+       if (!table)
+               return -ENOMEM;
+       if (!memcmp(table, smbios3_sig, sizeof(smbios3_sig) - 1)) {
+               entry3 = table;
+               if (entry3->length != sizeof(struct smbios3_entry)) {
+                       ret = -ENOENT;
+                       goto out;
+               }
+               memcpy(entry, entry3, sizeof(struct smbios3_entry));
+       } else if (!memcmp(table, smbios_sig, sizeof(smbios_sig) - 1)) {
+               entry2 = table;
+               if (entry2->length != sizeof(struct smbios_entry)) {
+                       ret = -ENOENT;
+                       goto out;
+               }
+               memset(entry, 0, sizeof(struct smbios3_entry));
+               memcpy(entry, smbios3_sig, sizeof(smbios3_sig));
+               entry->length = sizeof(struct smbios3_entry);
+               entry->major_ver = entry2->major_ver;
+               entry->minor_ver = entry2->minor_ver;
+               entry->max_struct_size = entry2->max_struct_size;
+       } else {
+               ret = -ENOENT;
+               goto out;
+       }
+       ret = 0;
+out:
+       free(table);
+
+       return ret;
+}
+
+/**
+ * qfw_write_smbios_tables() - copy SMBIOS tables from QEMU
+ *
+ * @addr:      target buffer
+ * @size:      size of target buffer
+ * Return:     0 for success, -ve on error
+ */
+static int qfw_write_smbios_tables(u8 *addr, uint32_t size)
+{
+       int ret;
+       struct udevice *dev;
+       struct smbios3_entry *entry = (void *)addr;
+       void *table;
+       uint32_t table_size;
+
+       ret = qfw_get_dev(&dev);
+       if (ret) {
+               log_err("No QEMU firmware device\n");
+               return ret;
+       }
+
+       ret = qfw_read_firmware_list(dev);
+       if (ret) {
+               log_err("Can't read firmware file list\n");
+               return ret;
+       }
+
+       ret = qfw_parse_smbios_anchor(dev, entry);
+       if (ret) {
+               log_debug("Can't parse anchor\n");
+               return ret;
+       }
+
+       addr += entry->length;
+       entry->struct_table_address = (uintptr_t)addr;
+       entry->checksum = 0;
+       entry->checksum = table_compute_checksum(entry,
+                                                sizeof(struct smbios3_entry));
+
+       table = qfw_load_smbios_table(dev, &table_size,
+                                     "etc/smbios/smbios-tables");
+       if (table_size + sizeof(struct smbios3_entry) > size) {
+               free(table);
+               return -ENOMEM;
+       }
+       memcpy(addr, table, table_size);
+       free(table);
+
+       return 0;
+}
+
+/**
+ * qfw_evt_write_smbios_tables() - event handler for copying QEMU SMBIOS tables
+ *
+ * Return:     0 on success, -ve on error (only out of memory)
+ */
+static int qfw_evt_write_smbios_tables(void)
+{
+       phys_addr_t addr;
+       void *ptr;
+       int ret;
+       /*
+        * TODO:
+        * This size is currently hard coded in lib/efi_loader/efi_smbios.c.
+        * We need a field in global data for the size.
+        */
+       uint32_t size = SZ_4K;
+
+       /* Reserve 64K for SMBIOS tables, aligned to a 4K boundary */
+       ptr = memalign(SZ_4K, size);
+       if (!ptr) {
+               log_err("Out of memory\n");
+               return -ENOMEM;
+       }
+       addr = map_to_sysmem(ptr);
+
+       /* Generate SMBIOS tables */
+       ret = qfw_write_smbios_tables(ptr, size);
+       if (ret) {
+               if (CONFIG_IS_ENABLED(GENERATE_SMBIOS_TABLE)) {
+                       log_info("Falling back to U-Boot generated SMBIOS tables\n");
+                       write_smbios_table(addr);
+               }
+       } else {
+               log_debug("SMBIOS tables copied from QEMU\n");
+       }
+
+       gd_set_smbios_start(addr);
+
+       return 0;
+}
+
+EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, qfw_evt_write_smbios_tables);
index b2ec1f791940485f0e228efb1f78471e91fb6c49..8d2ef6deb51477a484835dc40ff149e61bef4fde 100644 (file)
@@ -60,7 +60,9 @@ static int install_smbios_table(void)
        ulong addr;
        void *buf;
 
-       if (!IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE) || IS_ENABLED(CONFIG_X86))
+       if (!IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE) ||
+           IS_ENABLED(CONFIG_X86) ||
+           IS_ENABLED(CONFIG_QFW_SMBIOS))
                return 0;
 
        /* Align the table to a 4KB boundary to keep EFI happy */