b17a26bc4b
Add module_id member in X86CPUTopoIDs. module_id can be parsed from APIC ID, so also update APIC ID parsing rule to support module level. With this support, the conversions with module level between X86CPUTopoIDs, X86CPUTopoInfo and APIC ID are completed. module_id can be also generated from cpu topology, and before i386 supports "modules" in smp, the default "modules per die" (modules * clusters) is only 1, thus the module_id generated in this way is 0, so that it will not conflict with the module_id generated by APIC ID. Tested-by: Yongwei Ma <yongwei.ma@intel.com> Signed-off-by: Zhuocheng Ding <zhuocheng.ding@intel.com> Co-developed-by: Zhuocheng Ding <zhuocheng.ding@intel.com> Signed-off-by: Zhao Liu <zhao1.liu@intel.com> Tested-by: Babu Moger <babu.moger@amd.com> Message-ID: <20240424154929.1487382-16-zhao1.liu@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
465 lines
15 KiB
C
465 lines
15 KiB
C
/*
|
|
* Copyright (c) 2003-2004 Fabrice Bellard
|
|
* Copyright (c) 2019 Red Hat, Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/error-report.h"
|
|
#include "qemu/units.h"
|
|
#include "qapi/error.h"
|
|
#include "qapi/qapi-visit-common.h"
|
|
#include "qapi/qapi-visit-machine.h"
|
|
#include "qapi/visitor.h"
|
|
#include "sysemu/qtest.h"
|
|
#include "sysemu/numa.h"
|
|
#include "trace.h"
|
|
|
|
#include "hw/acpi/aml-build.h"
|
|
#include "hw/i386/x86.h"
|
|
#include "hw/i386/topology.h"
|
|
|
|
#include "hw/nmi.h"
|
|
#include "kvm/kvm_i386.h"
|
|
|
|
|
|
void init_topo_info(X86CPUTopoInfo *topo_info,
|
|
const X86MachineState *x86ms)
|
|
{
|
|
MachineState *ms = MACHINE(x86ms);
|
|
|
|
topo_info->dies_per_pkg = ms->smp.dies;
|
|
/*
|
|
* Though smp.modules means the number of modules in one cluster,
|
|
* i386 doesn't support cluster level so that the smp.clusters
|
|
* always defaults to 1, therefore using smp.modules directly is
|
|
* fine here.
|
|
*/
|
|
topo_info->modules_per_die = ms->smp.modules;
|
|
topo_info->cores_per_module = ms->smp.cores;
|
|
topo_info->threads_per_core = ms->smp.threads;
|
|
}
|
|
|
|
/*
|
|
* Calculates initial APIC ID for a specific CPU index
|
|
*
|
|
* Currently we need to be able to calculate the APIC ID from the CPU index
|
|
* alone (without requiring a CPU object), as the QEMU<->Seabios interfaces have
|
|
* no concept of "CPU index", and the NUMA tables on fw_cfg need the APIC ID of
|
|
* all CPUs up to max_cpus.
|
|
*/
|
|
uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms,
|
|
unsigned int cpu_index)
|
|
{
|
|
X86CPUTopoInfo topo_info;
|
|
|
|
init_topo_info(&topo_info, x86ms);
|
|
|
|
return x86_apicid_from_cpu_idx(&topo_info, cpu_index);
|
|
}
|
|
|
|
static CpuInstanceProperties
|
|
x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
|
|
{
|
|
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
|
const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
|
|
|
|
assert(cpu_index < possible_cpus->len);
|
|
return possible_cpus->cpus[cpu_index].props;
|
|
}
|
|
|
|
static int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx)
|
|
{
|
|
X86CPUTopoIDs topo_ids;
|
|
X86MachineState *x86ms = X86_MACHINE(ms);
|
|
X86CPUTopoInfo topo_info;
|
|
|
|
init_topo_info(&topo_info, x86ms);
|
|
|
|
assert(idx < ms->possible_cpus->len);
|
|
x86_topo_ids_from_apicid(ms->possible_cpus->cpus[idx].arch_id,
|
|
&topo_info, &topo_ids);
|
|
return topo_ids.pkg_id % ms->numa_state->num_nodes;
|
|
}
|
|
|
|
static const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(ms);
|
|
unsigned int max_cpus = ms->smp.max_cpus;
|
|
X86CPUTopoInfo topo_info;
|
|
int i;
|
|
|
|
if (ms->possible_cpus) {
|
|
/*
|
|
* make sure that max_cpus hasn't changed since the first use, i.e.
|
|
* -smp hasn't been parsed after it
|
|
*/
|
|
assert(ms->possible_cpus->len == max_cpus);
|
|
return ms->possible_cpus;
|
|
}
|
|
|
|
ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
|
|
sizeof(CPUArchId) * max_cpus);
|
|
ms->possible_cpus->len = max_cpus;
|
|
|
|
init_topo_info(&topo_info, x86ms);
|
|
|
|
for (i = 0; i < ms->possible_cpus->len; i++) {
|
|
X86CPUTopoIDs topo_ids;
|
|
|
|
ms->possible_cpus->cpus[i].type = ms->cpu_type;
|
|
ms->possible_cpus->cpus[i].vcpus_count = 1;
|
|
ms->possible_cpus->cpus[i].arch_id =
|
|
x86_cpu_apic_id_from_index(x86ms, i);
|
|
x86_topo_ids_from_apicid(ms->possible_cpus->cpus[i].arch_id,
|
|
&topo_info, &topo_ids);
|
|
ms->possible_cpus->cpus[i].props.has_socket_id = true;
|
|
ms->possible_cpus->cpus[i].props.socket_id = topo_ids.pkg_id;
|
|
if (ms->smp.dies > 1) {
|
|
ms->possible_cpus->cpus[i].props.has_die_id = true;
|
|
ms->possible_cpus->cpus[i].props.die_id = topo_ids.die_id;
|
|
}
|
|
if (ms->smp.modules > 1) {
|
|
ms->possible_cpus->cpus[i].props.has_module_id = true;
|
|
ms->possible_cpus->cpus[i].props.module_id = topo_ids.module_id;
|
|
}
|
|
ms->possible_cpus->cpus[i].props.has_core_id = true;
|
|
ms->possible_cpus->cpus[i].props.core_id = topo_ids.core_id;
|
|
ms->possible_cpus->cpus[i].props.has_thread_id = true;
|
|
ms->possible_cpus->cpus[i].props.thread_id = topo_ids.smt_id;
|
|
}
|
|
return ms->possible_cpus;
|
|
}
|
|
|
|
static void x86_nmi(NMIState *n, int cpu_index, Error **errp)
|
|
{
|
|
/* cpu index isn't used */
|
|
CPUState *cs;
|
|
|
|
CPU_FOREACH(cs) {
|
|
X86CPU *cpu = X86_CPU(cs);
|
|
|
|
if (cpu_is_apic_enabled(cpu->apic_state)) {
|
|
apic_deliver_nmi(cpu->apic_state);
|
|
} else {
|
|
cpu_interrupt(cs, CPU_INTERRUPT_NMI);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool x86_machine_is_smm_enabled(const X86MachineState *x86ms)
|
|
{
|
|
bool smm_available = false;
|
|
|
|
if (x86ms->smm == ON_OFF_AUTO_OFF) {
|
|
return false;
|
|
}
|
|
|
|
if (tcg_enabled() || qtest_enabled()) {
|
|
smm_available = true;
|
|
} else if (kvm_enabled()) {
|
|
smm_available = kvm_has_smm();
|
|
}
|
|
|
|
if (smm_available) {
|
|
return true;
|
|
}
|
|
|
|
if (x86ms->smm == ON_OFF_AUTO_ON) {
|
|
error_report("System Management Mode not supported by this hypervisor.");
|
|
exit(1);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void x86_machine_get_smm(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
OnOffAuto smm = x86ms->smm;
|
|
|
|
visit_type_OnOffAuto(v, name, &smm, errp);
|
|
}
|
|
|
|
static void x86_machine_set_smm(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
|
|
visit_type_OnOffAuto(v, name, &x86ms->smm, errp);
|
|
}
|
|
|
|
bool x86_machine_is_acpi_enabled(const X86MachineState *x86ms)
|
|
{
|
|
if (x86ms->acpi == ON_OFF_AUTO_OFF) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void x86_machine_get_acpi(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
OnOffAuto acpi = x86ms->acpi;
|
|
|
|
visit_type_OnOffAuto(v, name, &acpi, errp);
|
|
}
|
|
|
|
static void x86_machine_set_acpi(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
|
|
visit_type_OnOffAuto(v, name, &x86ms->acpi, errp);
|
|
}
|
|
|
|
static void x86_machine_get_pit(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
OnOffAuto pit = x86ms->pit;
|
|
|
|
visit_type_OnOffAuto(v, name, &pit, errp);
|
|
}
|
|
|
|
static void x86_machine_set_pit(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);;
|
|
|
|
visit_type_OnOffAuto(v, name, &x86ms->pit, errp);
|
|
}
|
|
|
|
static void x86_machine_get_pic(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
OnOffAuto pic = x86ms->pic;
|
|
|
|
visit_type_OnOffAuto(v, name, &pic, errp);
|
|
}
|
|
|
|
static void x86_machine_set_pic(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
|
|
visit_type_OnOffAuto(v, name, &x86ms->pic, errp);
|
|
}
|
|
|
|
static char *x86_machine_get_oem_id(Object *obj, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
|
|
return g_strdup(x86ms->oem_id);
|
|
}
|
|
|
|
static void x86_machine_set_oem_id(Object *obj, const char *value, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
size_t len = strlen(value);
|
|
|
|
if (len > 6) {
|
|
error_setg(errp,
|
|
"User specified "X86_MACHINE_OEM_ID" value is bigger than "
|
|
"6 bytes in size");
|
|
return;
|
|
}
|
|
|
|
strncpy(x86ms->oem_id, value, 6);
|
|
}
|
|
|
|
static char *x86_machine_get_oem_table_id(Object *obj, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
|
|
return g_strdup(x86ms->oem_table_id);
|
|
}
|
|
|
|
static void x86_machine_set_oem_table_id(Object *obj, const char *value,
|
|
Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
size_t len = strlen(value);
|
|
|
|
if (len > 8) {
|
|
error_setg(errp,
|
|
"User specified "X86_MACHINE_OEM_TABLE_ID
|
|
" value is bigger than "
|
|
"8 bytes in size");
|
|
return;
|
|
}
|
|
strncpy(x86ms->oem_table_id, value, 8);
|
|
}
|
|
|
|
static void x86_machine_get_bus_lock_ratelimit(Object *obj, Visitor *v,
|
|
const char *name, void *opaque, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
uint64_t bus_lock_ratelimit = x86ms->bus_lock_ratelimit;
|
|
|
|
visit_type_uint64(v, name, &bus_lock_ratelimit, errp);
|
|
}
|
|
|
|
static void x86_machine_set_bus_lock_ratelimit(Object *obj, Visitor *v,
|
|
const char *name, void *opaque, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
|
|
visit_type_uint64(v, name, &x86ms->bus_lock_ratelimit, errp);
|
|
}
|
|
|
|
static void machine_get_sgx_epc(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
SgxEPCList *list = x86ms->sgx_epc_list;
|
|
|
|
visit_type_SgxEPCList(v, name, &list, errp);
|
|
}
|
|
|
|
static void machine_set_sgx_epc(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
SgxEPCList *list;
|
|
|
|
list = x86ms->sgx_epc_list;
|
|
visit_type_SgxEPCList(v, name, &x86ms->sgx_epc_list, errp);
|
|
|
|
qapi_free_SgxEPCList(list);
|
|
}
|
|
|
|
static int x86_kvm_type(MachineState *ms, const char *vm_type)
|
|
{
|
|
/*
|
|
* No x86 machine has a kvm-type property. If one is added that has
|
|
* it, it should call kvm_get_vm_type() directly or not use it at all.
|
|
*/
|
|
assert(vm_type == NULL);
|
|
return kvm_enabled() ? kvm_get_vm_type(ms) : 0;
|
|
}
|
|
|
|
static void x86_machine_initfn(Object *obj)
|
|
{
|
|
X86MachineState *x86ms = X86_MACHINE(obj);
|
|
|
|
x86ms->smm = ON_OFF_AUTO_AUTO;
|
|
x86ms->acpi = ON_OFF_AUTO_AUTO;
|
|
x86ms->pit = ON_OFF_AUTO_AUTO;
|
|
x86ms->pic = ON_OFF_AUTO_AUTO;
|
|
x86ms->pci_irq_mask = ACPI_BUILD_PCI_IRQS;
|
|
x86ms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6);
|
|
x86ms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
|
|
x86ms->bus_lock_ratelimit = 0;
|
|
x86ms->above_4g_mem_start = 4 * GiB;
|
|
}
|
|
|
|
static void x86_machine_class_init(ObjectClass *oc, void *data)
|
|
{
|
|
MachineClass *mc = MACHINE_CLASS(oc);
|
|
X86MachineClass *x86mc = X86_MACHINE_CLASS(oc);
|
|
NMIClass *nc = NMI_CLASS(oc);
|
|
|
|
mc->cpu_index_to_instance_props = x86_cpu_index_to_props;
|
|
mc->get_default_cpu_node_id = x86_get_default_cpu_node_id;
|
|
mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids;
|
|
mc->kvm_type = x86_kvm_type;
|
|
x86mc->save_tsc_khz = true;
|
|
x86mc->fwcfg_dma_enabled = true;
|
|
nc->nmi_monitor_handler = x86_nmi;
|
|
|
|
object_class_property_add(oc, X86_MACHINE_SMM, "OnOffAuto",
|
|
x86_machine_get_smm, x86_machine_set_smm,
|
|
NULL, NULL);
|
|
object_class_property_set_description(oc, X86_MACHINE_SMM,
|
|
"Enable SMM");
|
|
|
|
object_class_property_add(oc, X86_MACHINE_ACPI, "OnOffAuto",
|
|
x86_machine_get_acpi, x86_machine_set_acpi,
|
|
NULL, NULL);
|
|
object_class_property_set_description(oc, X86_MACHINE_ACPI,
|
|
"Enable ACPI");
|
|
|
|
object_class_property_add(oc, X86_MACHINE_PIT, "OnOffAuto",
|
|
x86_machine_get_pit,
|
|
x86_machine_set_pit,
|
|
NULL, NULL);
|
|
object_class_property_set_description(oc, X86_MACHINE_PIT,
|
|
"Enable i8254 PIT");
|
|
|
|
object_class_property_add(oc, X86_MACHINE_PIC, "OnOffAuto",
|
|
x86_machine_get_pic,
|
|
x86_machine_set_pic,
|
|
NULL, NULL);
|
|
object_class_property_set_description(oc, X86_MACHINE_PIC,
|
|
"Enable i8259 PIC");
|
|
|
|
object_class_property_add_str(oc, X86_MACHINE_OEM_ID,
|
|
x86_machine_get_oem_id,
|
|
x86_machine_set_oem_id);
|
|
object_class_property_set_description(oc, X86_MACHINE_OEM_ID,
|
|
"Override the default value of field OEMID "
|
|
"in ACPI table header."
|
|
"The string may be up to 6 bytes in size");
|
|
|
|
|
|
object_class_property_add_str(oc, X86_MACHINE_OEM_TABLE_ID,
|
|
x86_machine_get_oem_table_id,
|
|
x86_machine_set_oem_table_id);
|
|
object_class_property_set_description(oc, X86_MACHINE_OEM_TABLE_ID,
|
|
"Override the default value of field OEM Table ID "
|
|
"in ACPI table header."
|
|
"The string may be up to 8 bytes in size");
|
|
|
|
object_class_property_add(oc, X86_MACHINE_BUS_LOCK_RATELIMIT, "uint64_t",
|
|
x86_machine_get_bus_lock_ratelimit,
|
|
x86_machine_set_bus_lock_ratelimit, NULL, NULL);
|
|
object_class_property_set_description(oc, X86_MACHINE_BUS_LOCK_RATELIMIT,
|
|
"Set the ratelimit for the bus locks acquired in VMs");
|
|
|
|
object_class_property_add(oc, "sgx-epc", "SgxEPC",
|
|
machine_get_sgx_epc, machine_set_sgx_epc,
|
|
NULL, NULL);
|
|
object_class_property_set_description(oc, "sgx-epc",
|
|
"SGX EPC device");
|
|
}
|
|
|
|
static const TypeInfo x86_machine_info = {
|
|
.name = TYPE_X86_MACHINE,
|
|
.parent = TYPE_MACHINE,
|
|
.abstract = true,
|
|
.instance_size = sizeof(X86MachineState),
|
|
.instance_init = x86_machine_initfn,
|
|
.class_size = sizeof(X86MachineClass),
|
|
.class_init = x86_machine_class_init,
|
|
.interfaces = (InterfaceInfo[]) {
|
|
{ TYPE_NMI },
|
|
{ }
|
|
},
|
|
};
|
|
|
|
static void x86_machine_register_types(void)
|
|
{
|
|
type_register_static(&x86_machine_info);
|
|
}
|
|
|
|
type_init(x86_machine_register_types)
|