// 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->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: 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);