/* * 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)