[PATCH 0/2] smbios: copy QEMU tables

QEMU provides SMBIOS tables with detailed information. We should not try to replicate them in U-Boot.
With this series we add code to copy the QEMU generated SMBIOS tables to RAM. efi_smbios_register() later installs the SMBIOS table as EFI configuration table for the OS to pick it up.
If we want to inform about U-Boot, we can add a Firmware Inventory Information (type 45) table in future.
With 32bit ARM I did not see a correct SMBIOS table being transferred. We need to check if our code in qfw_read_entry() is 32bit compliant.
For providing SMBIOS support in QEMU for RISC-V I have sent a patch upstream:
[PATCH 1/1] target/riscv: SMBIOS support for RISC-V virt machine https://lore.kernel.org/qemu-riscv/9183a3fb-b9f7-470c-b9a6-8d8cc6dce7c3@cano...
Currently we have a predefined constant TABLE_SIZE = SZ_4K in lib/efi_loader/efi_smbios.c. We should strive to refactor the code in future to provide the length of the SMBIOS tables in global data. But that is beyond the scope of this series.
Further we should find a matainer for drivers/misc/qfw*. It would be plausible to add it to 'ACPI' maintenance.
Heinrich Schuchardt (2): smbios: SMBIOS 3.0 (64-bit) Entry Point structure smbios: copy QEMU tables
drivers/misc/Kconfig | 8 ++ drivers/misc/Makefile | 1 + drivers/misc/qfw_smbios.c | 195 ++++++++++++++++++++++++++++++++++++ include/smbios.h | 26 +++++ lib/efi_loader/efi_smbios.c | 4 +- 5 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 drivers/misc/qfw_smbios.c

Add definition of the SMBIOS 3.0 (64-bit) Entry Point structure.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com --- include/smbios.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/include/smbios.h b/include/smbios.h index c9df2706f5..e601283d29 100644 --- a/include/smbios.h +++ b/include/smbios.h @@ -54,6 +54,32 @@ struct __packed smbios_entry { u8 bcd_rev; };
+/** + * struct smbios3_entry - SMBIOS 3.0 (64-bit) Entry Point structure + */ +struct __packed smbios3_entry { + /** @anchor: anchor string */ + u8 anchor[5]; + /** @checksum: checksum of the entry point structure */ + u8 checksum; + /** @length: length of the entry point structure */ + u8 length; + /** @major_ver: major version of the SMBIOS specification */ + u8 major_ver; + /** @minor_ver: minor version of the SMBIOS specification */ + u8 minor_ver; + /** @docrev: revision of the SMBIOS specification */ + u8 doc_rev; + /** @entry_point_rev: revision of the entry point structure */ + u8 entry_point_rev; + /** @reserved: reserved */ + u8 reserved; + /** maximum size of SMBIOS table */ + u32 max_struct_size; + /** @struct_table_address: 64-bit physical starting address */ + u64 struct_table_address; +}; + /* BIOS characteristics */ #define BIOS_CHARACTERISTICS_PCI_SUPPORTED (1 << 7) #define BIOS_CHARACTERISTICS_UPGRADEABLE (1 << 11)

Hi Heinrich,
On Thu, Dec 21, 2023 at 10:50 AM Heinrich Schuchardt heinrich.schuchardt@canonical.com wrote:
Add definition of the SMBIOS 3.0 (64-bit) Entry Point structure.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com
include/smbios.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)\
What happened to the patch here? [1]
diff --git a/include/smbios.h b/include/smbios.h index c9df2706f5..e601283d29 100644 --- a/include/smbios.h +++ b/include/smbios.h @@ -54,6 +54,32 @@ struct __packed smbios_entry { u8 bcd_rev; };
+/**
- struct smbios3_entry - SMBIOS 3.0 (64-bit) Entry Point structure
- */
+struct __packed smbios3_entry {
/** @anchor: anchor string */
u8 anchor[5];
/** @checksum: checksum of the entry point structure */
u8 checksum;
/** @length: length of the entry point structure */
u8 length;
/** @major_ver: major version of the SMBIOS specification */
u8 major_ver;
/** @minor_ver: minor version of the SMBIOS specification */
u8 minor_ver;
/** @docrev: revision of the SMBIOS specification */
u8 doc_rev;
/** @entry_point_rev: revision of the entry point structure */
u8 entry_point_rev;
/** @reserved: reserved */
u8 reserved;
/** maximum size of SMBIOS table */
u32 max_struct_size;
/** @struct_table_address: 64-bit physical starting address */
u64 struct_table_address;
+};
/* BIOS characteristics */ #define BIOS_CHARACTERISTICS_PCI_SUPPORTED (1 << 7)
#define BIOS_CHARACTERISTICS_UPGRADEABLE (1 << 11)
2.40.1
Regards, Simon
[1] https://patchwork.ozlabs.org/project/uboot/patch/20231015024511.3995580-4-sj...

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 --- drivers/misc/Kconfig | 8 ++ drivers/misc/Makefile | 1 + drivers/misc/qfw_smbios.c | 195 ++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_smbios.c | 4 +- 4 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 drivers/misc/qfw_smbios.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e8e4400516..3d885f12c8 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -561,6 +561,14 @@ 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 if ARM64 + default y if RISCV && 64BIT + depends on QFW && SMBIOS + 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 cda701d38e..64bea92f51 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -66,6 +66,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..38f0225b36 --- /dev/null +++ b/drivers/misc/qfw_smbios.c @@ -0,0 +1,195 @@ +// 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))) { + entry3 = table; + if (entry3->length != sizeof(struct smbios3_entry)) { + ret = -ENOENT; + goto out; + } + memcpy(entry, entry3, sizeof(struct smbios3_entry)); + } else if (memcmp(table, smbios3_sig, sizeof(smbios_sig))) { + entry2 = table; + if (entry->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(smbios3_sig); + 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) { + 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 0fbf51b98d..ba39382d04 100644 --- a/lib/efi_loader/efi_smbios.c +++ b/lib/efi_loader/efi_smbios.c @@ -52,7 +52,9 @@ static int install_smbios_table(void) u64 addr; efi_status_t ret;
- 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;
addr = SZ_4G;

Hi Heinrich,
On Thu, Dec 21, 2023 at 6:50 PM Heinrich Schuchardt heinrich.schuchardt@canonical.com wrote:
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
drivers/misc/Kconfig | 8 ++ drivers/misc/Makefile | 1 + drivers/misc/qfw_smbios.c | 195 ++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_smbios.c | 4 +- 4 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 drivers/misc/qfw_smbios.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e8e4400516..3d885f12c8 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -561,6 +561,14 @@ 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 if ARM64
default y if RISCV && 64BIT
Would you add QEMU x86 support as well?
depends on QFW && SMBIOS
help
Hidden option to read SMBIOS tables from QEMU.
config I2C_EEPROM bool "Enable driver for generic I2C-attached EEPROMs" depends on MISC
[snip]
Regards, Bin

On 21.12.23 14:01, Bin Meng wrote:
Hi Heinrich,
On Thu, Dec 21, 2023 at 6:50 PM Heinrich Schuchardt heinrich.schuchardt@canonical.com wrote:
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
drivers/misc/Kconfig | 8 ++ drivers/misc/Makefile | 1 + drivers/misc/qfw_smbios.c | 195 ++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_smbios.c | 4 +- 4 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 drivers/misc/qfw_smbios.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e8e4400516..3d885f12c8 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -561,6 +561,14 @@ 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 if ARM64
default y if RISCV && 64BIT
Would you add QEMU x86 support as well?
On x86_64 reading the QEMU SMBIOS firmware tables does not work for me. x86 uses the qfw_pio driver instead of qfw_mmio.
x86 overwrites gd->arch.smbios_start in write_tables(). Copying SMBIOS tables from QEMU would only make sense after some refactoring by the x86 maintainers.
Best regards
Heinrich
depends on QFW && SMBIOS
help
Hidden option to read SMBIOS tables from QEMU.
- config I2C_EEPROM bool "Enable driver for generic I2C-attached EEPROMs" depends on MISC
[snip]
Regards, Bin
participants (3)
-
Bin Meng
-
Heinrich Schuchardt
-
Simon Glass