From 1c5aab803c0b0f07be1d3b0029f4031463497acf Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 23 Dec 2023 02:03:34 +0100 Subject: [PATCH] smbios: copy QEMU tables 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 Reviewed-by: Simon Glass --- arch/x86/lib/tables.c | 2 +- drivers/misc/Kconfig | 7 ++ drivers/misc/Makefile | 1 + drivers/misc/qfw_smbios.c | 197 ++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_smbios.c | 4 +- 5 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 drivers/misc/qfw_smbios.c diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c index d43e77d373..12eae17c39 100644 --- a/arch/x86/lib/tables.c +++ b/arch/x86/lib/tables.c @@ -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 }; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e85a0dd51c..f11ce72525 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -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 diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 6bf1e79f7e..0432db6ed1 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -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 index 0000000000..9019345783 --- /dev/null +++ b/drivers/misc/qfw_smbios.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2023 Heinrich Schuchardt + */ + +#define LOG_CATEGORY UCLASS_QFW + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); diff --git a/lib/efi_loader/efi_smbios.c b/lib/efi_loader/efi_smbios.c index b2ec1f7919..8d2ef6deb5 100644 --- a/lib/efi_loader/efi_smbios.c +++ b/lib/efi_loader/efi_smbios.c @@ -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 */