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