diff options
author | Heinrich Schuchardt <heinrich.schuchardt@canonical.com> | 2023-12-23 02:03:34 +0100 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2024-01-16 17:05:29 -0500 |
commit | 1c5aab803c0b0f07be1d3b0029f4031463497acf (patch) | |
tree | f7eadcb44aa89ba17bf9376d91f4f1e676a947b1 /drivers/misc | |
parent | 481ffca4857d08f31e4ee179c5a0e609957869b1 (diff) | |
download | u-boot-1c5aab803c0b0f07be1d3b0029f4031463497acf.tar.gz |
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 <heinrich.schuchardt@canonical.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 7 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/qfw_smbios.c | 197 |
3 files changed, 205 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e85a0dd51ca..f11ce72525f 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 6bf1e79f7e2..0432db6ed12 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 00000000000..9019345783f --- /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 <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); |