qemu/hw/i386/fw_cfg.c
Ani Sinha 4ad08e8a57 hw/i386/e820: remove legacy reserved entries for e820
e820 reserved entries were used before the dynamic entries with fw config files
were intoduced. Please see the following change:
7d67110f2d9a6("pc: add etc/e820 fw_cfg file")

Identical support was introduced into seabios as well with the following commit:
ce39bd4031820 ("Add support for etc/e820 fw_cfg file")

Both the above commits are now quite old. QEMU machines 1.7 and newer no longer
use the reserved entries. Seabios uses fw config files and
dynamic e820 entries by default and only falls back to using reserved entries
when it has to work with old qemu (versions earlier than 1.7). Please see
functions qemu_cfg_e820() and qemu_early_e820(). It is safe to remove legacy
FW_CFG_E820_TABLE and associated code now as QEMU 7.0 has deprecated i440fx
machines 1.7 and older. It would be incredibly rare to run the latest qemu
version with a very old version of seabios that did not support fw config files
for e820.

As far as I could see, edk2/ovfm never supported reserved entries and uses fw
config files from the beginning. So there should be no incompatibilities with
ovfm as well.

CC: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Ani Sinha <ani@anisinha.ca>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Message-Id: <20220831045311.33083-1-ani@anisinha.ca>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2022-11-02 06:56:31 -04:00

219 lines
7.4 KiB
C

/*
* QEMU fw_cfg helpers (X86 specific)
*
* Copyright (c) 2019 Red Hat, Inc.
*
* Author:
* Philippe Mathieu-Daudé <philmd@redhat.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "sysemu/numa.h"
#include "hw/acpi/acpi.h"
#include "hw/acpi/aml-build.h"
#include "hw/firmware/smbios.h"
#include "hw/i386/fw_cfg.h"
#include "hw/timer/hpet.h"
#include "hw/nvram/fw_cfg.h"
#include "e820_memory_layout.h"
#include "kvm/kvm_i386.h"
#include "qapi/error.h"
#include CONFIG_DEVICES
struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX};
const char *fw_cfg_arch_key_name(uint16_t key)
{
static const struct {
uint16_t key;
const char *name;
} fw_cfg_arch_wellknown_keys[] = {
{FW_CFG_ACPI_TABLES, "acpi_tables"},
{FW_CFG_SMBIOS_ENTRIES, "smbios_entries"},
{FW_CFG_IRQ0_OVERRIDE, "irq0_override"},
{FW_CFG_HPET, "hpet"},
};
for (size_t i = 0; i < ARRAY_SIZE(fw_cfg_arch_wellknown_keys); i++) {
if (fw_cfg_arch_wellknown_keys[i].key == key) {
return fw_cfg_arch_wellknown_keys[i].name;
}
}
return NULL;
}
void fw_cfg_build_smbios(MachineState *ms, FWCfgState *fw_cfg)
{
#ifdef CONFIG_SMBIOS
uint8_t *smbios_tables, *smbios_anchor;
size_t smbios_tables_len, smbios_anchor_len;
struct smbios_phys_mem_area *mem_array;
unsigned i, array_count;
X86CPU *cpu = X86_CPU(ms->possible_cpus->cpus[0].cpu);
/* tell smbios about cpuid version and features */
smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]);
smbios_tables = smbios_get_table_legacy(ms, &smbios_tables_len);
if (smbios_tables) {
fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
smbios_tables, smbios_tables_len);
}
/* build the array of physical mem area from e820 table */
mem_array = g_malloc0(sizeof(*mem_array) * e820_get_num_entries());
for (i = 0, array_count = 0; i < e820_get_num_entries(); i++) {
uint64_t addr, len;
if (e820_get_entry(i, E820_RAM, &addr, &len)) {
mem_array[array_count].address = addr;
mem_array[array_count].length = len;
array_count++;
}
}
smbios_get_tables(ms, mem_array, array_count,
&smbios_tables, &smbios_tables_len,
&smbios_anchor, &smbios_anchor_len,
&error_fatal);
g_free(mem_array);
if (smbios_anchor) {
fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables",
smbios_tables, smbios_tables_len);
fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor",
smbios_anchor, smbios_anchor_len);
}
#endif
}
FWCfgState *fw_cfg_arch_create(MachineState *ms,
uint16_t boot_cpus,
uint16_t apic_id_limit)
{
FWCfgState *fw_cfg;
uint64_t *numa_fw_cfg;
int i;
MachineClass *mc = MACHINE_GET_CLASS(ms);
const CPUArchIdList *cpus = mc->possible_cpu_arch_ids(ms);
int nb_numa_nodes = ms->numa_state->num_nodes;
fw_cfg = fw_cfg_init_io_dma(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4,
&address_space_memory);
fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, boot_cpus);
/* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86:
*
* For machine types prior to 1.8, SeaBIOS needs FW_CFG_MAX_CPUS for
* building MPTable, ACPI MADT, ACPI CPU hotplug and ACPI SRAT table,
* that tables are based on xAPIC ID and QEMU<->SeaBIOS interface
* for CPU hotplug also uses APIC ID and not "CPU index".
* This means that FW_CFG_MAX_CPUS is not the "maximum number of CPUs",
* but the "limit to the APIC ID values SeaBIOS may see".
*
* So for compatibility reasons with old BIOSes we are stuck with
* "etc/max-cpus" actually being apic_id_limit
*/
fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, apic_id_limit);
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, ms->ram_size);
#ifdef CONFIG_ACPI
fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES,
acpi_tables, acpi_tables_len);
#endif
fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, 1);
fw_cfg_add_file(fw_cfg, "etc/e820", e820_table,
sizeof(struct e820_entry) * e820_get_num_entries());
fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_cfg, sizeof(hpet_cfg));
/* allocate memory for the NUMA channel: one (64bit) word for the number
* of nodes, one word for each VCPU->node and one word for each node to
* hold the amount of memory.
*/
numa_fw_cfg = g_new0(uint64_t, 1 + apic_id_limit + nb_numa_nodes);
numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes);
for (i = 0; i < cpus->len; i++) {
unsigned int apic_id = cpus->cpus[i].arch_id;
assert(apic_id < apic_id_limit);
numa_fw_cfg[apic_id + 1] = cpu_to_le64(cpus->cpus[i].props.node_id);
}
for (i = 0; i < nb_numa_nodes; i++) {
numa_fw_cfg[apic_id_limit + 1 + i] =
cpu_to_le64(ms->numa_state->nodes[i].node_mem);
}
fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, numa_fw_cfg,
(1 + apic_id_limit + nb_numa_nodes) *
sizeof(*numa_fw_cfg));
return fw_cfg;
}
void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg)
{
X86CPU *cpu = X86_CPU(ms->possible_cpus->cpus[0].cpu);
CPUX86State *env = &cpu->env;
uint32_t unused, ebx, ecx, edx;
uint64_t feature_control_bits = 0;
uint64_t *val;
cpu_x86_cpuid(env, 1, 0, &unused, &unused, &ecx, &edx);
if (ecx & CPUID_EXT_VMX) {
feature_control_bits |= FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX;
}
if ((edx & (CPUID_EXT2_MCE | CPUID_EXT2_MCA)) ==
(CPUID_EXT2_MCE | CPUID_EXT2_MCA) &&
(env->mcg_cap & MCG_LMCE_P)) {
feature_control_bits |= FEATURE_CONTROL_LMCE;
}
if (env->cpuid_level >= 7) {
cpu_x86_cpuid(env, 0x7, 0, &unused, &ebx, &ecx, &unused);
if (ebx & CPUID_7_0_EBX_SGX) {
feature_control_bits |= FEATURE_CONTROL_SGX;
}
if (ecx & CPUID_7_0_ECX_SGX_LC) {
feature_control_bits |= FEATURE_CONTROL_SGX_LC;
}
}
if (!feature_control_bits) {
return;
}
val = g_malloc(sizeof(*val));
*val = cpu_to_le64(feature_control_bits | FEATURE_CONTROL_LOCKED);
fw_cfg_add_file(fw_cfg, "etc/msr_feature_control", val, sizeof(*val));
}
void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg)
{
/*
* when using port i/o, the 8-bit data register *always* overlaps
* with half of the 16-bit control register. Hence, the total size
* of the i/o region used is FW_CFG_CTL_SIZE; when using DMA, the
* DMA control register is located at FW_CFG_DMA_IO_BASE + 4
*/
Object *obj = OBJECT(fw_cfg);
uint8_t io_size = object_property_get_bool(obj, "dma_enabled", NULL) ?
ROUND_UP(FW_CFG_CTL_SIZE, 4) + sizeof(dma_addr_t) :
FW_CFG_CTL_SIZE;
Aml *dev = aml_device("FWCF");
Aml *crs = aml_resource_template();
aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002")));
/* device present, functioning, decoding, not shown in UI */
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
aml_append(crs,
aml_io(AML_DECODE16, FW_CFG_IO_BASE, FW_CFG_IO_BASE, 0x01, io_size));
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
}