u-boot/drivers/misc/qfw_smbios.c
Simon Glass 53d5a22163 emulation: Use bloblist to hold tables
QEMU can have its own internal ACPI and SMBIOS tables. At present U-Boot
copies out the SMBIOS tables but points directly to the ACPI ones.

The ACPI tables are not aligned on a 4KB boundary, which means that UPL
cannot use them directly, since it uses a reserved-memory node for the
tables and that it assumed (by EDK2) to be 4KB-aligned.

On x86, QEMU provides the tables in a mapped memory region and U-Boot
makes use of these directly, thus making it difficult to use any common
code.

Adjust the logic to fit within the existing table-generation code. Use a
bloblist always and ensure that the ACPI tables is placed in an aligned
region. Set a size of 8K for QEMU. This does not actually put all the
tables in one place, for QEMU, since it currently adds a pointer to the
tables in QFW.

On ARM, enable bloblist so that SMBIOS tables can be added to the
bloblist.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-01-22 17:08:23 -06:00

194 lines
4.4 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2023 Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
*/
#define LOG_CATEGORY UCLASS_QFW
#include <bloblist.h>
#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>
#include <linux/err.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->table_maximum_size = entry2->struct_table_length;
} else {
ret = -ENOENT;
goto out;
}
ret = 0;
out:
free(table);
return ret;
}
/**
* qfw_write_smbios_tables() - copy SMBIOS tables from QEMU
*
* @addr: address of target buffer
* Return: 0 for success, -ve on error
*/
ulong write_smbios_table(ulong addr)
{
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");
memcpy((void *)addr, table, table_size);
free(table);
return addr + table_size;
}
#ifndef CONFIG_X86
/**
* 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)
{
ulong addr, end;
void *ptr;
/*
* 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;
log_debug("qfw_evt_write_smbios_tables bloblist\n");
/* Reserve 4K for SMBIOS tables, aligned to a 4K boundary */
ptr = bloblist_add(BLOBLISTT_SMBIOS_TABLES, size, 12);
if (!ptr)
return log_msg_ret("bloblist", -ENOBUFS);
addr = map_to_sysmem(ptr);
/* Generate SMBIOS tables */
end = write_smbios_table(addr);
if (IS_ERR_VALUE(end)) {
log_warning("SMBIOS: Failed to write (err=%dE)\n", (int)end);
} else {
if (end - addr > size)
return -ENOMEM;
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);
#endif /* !X86 */