Hello Eduardo, thank you for your answer,
On 11/9/20 8:04 PM, Eduardo Habkost wrote: > On Mon, Nov 09, 2020 at 06:27:54PM +0100, Claudio Fontana wrote: >> split cpu.c into: >> >> cpu.c cpuid and common x86 cpu functionality >> host-cpu.c host x86 cpu functions and "host" cpu type >> kvm-cpu-type.c KVM x86 cpu type >> hvf-cpu-type.c HVF x86 cpu type >> tcg-cpu-type.c TCG x86 cpu type >> >> Defer the x86 models registration to MODULE_INIT_ACCEL_CPU, >> so that accel-specific types can be used as parent types for all >> cpu models. Use the generic TYPE_X86_CPU only if no >> accel-specific specialization is enabled. > > This is very unfriendly to introspection, I don't think this is > the direction we want to go with the QOM type hierarchy. > > Why do you need inheritance here? accel-specific behavior can be > delegated to a separate object, instead of requiring the CPU > object to inherit from a accel-specific class. Looking in detail at QOM CPU I got the impression that really it's inheritance that makes sense here, it just flows correctly I think from an OOP perspective. I'd note that it's not really the specialized cpu types that are the problem. TYPE_TCG_CPU , TYPE_KVM_CPU are defined statically as usual during MODULE_INIT_QOM time. That part actually works surprisingly well and seems just the right thing to do to me. It's the second step that is the contentious one I think, ie the CPU models registration. > > In case inheritance is really the best mechanism for this, I'd > prefer to register accel-specific subclasses unconditionally, and Indeed, accel-specific sub types are already registered unconditionally. > make cpu_class_by_name() resolve to the right accel-specific > class name. Ah! I'll look at this! This might point into a new direction. Thanks! Claudio > >> >> Signed-off-by: Claudio Fontana <cfont...@suse.de> >> --- >> bsd-user/main.c | 4 + >> hw/i386/pc_piix.c | 1 + >> linux-user/main.c | 10 +- >> softmmu/vl.c | 2 +- >> target/i386/accel/hvf/hvf-cpu-type.c | 78 +++++ >> target/i386/accel/hvf/meson.build | 1 + >> target/i386/accel/kvm/kvm-cpu-type.c | 161 ++++++++++ >> target/i386/accel/kvm/kvm-cpu-type.h | 41 +++ >> target/i386/accel/kvm/kvm.c | 3 +- >> target/i386/accel/kvm/meson.build | 7 +- >> target/i386/accel/tcg/meson.build | 3 +- >> target/i386/accel/tcg/tcg-cpu-type.c | 176 +++++++++++ >> target/i386/accel/tcg/tcg-cpu-type.h | 25 ++ >> target/i386/cpu.c | 454 +++++---------------------- >> target/i386/cpu.h | 26 +- >> target/i386/host-cpu.c | 201 ++++++++++++ >> target/i386/host-cpu.h | 21 ++ >> target/i386/meson.build | 8 +- >> target/i386/tcg-cpu.c | 71 ----- >> target/i386/tcg-cpu.h | 15 - >> 20 files changed, 834 insertions(+), 474 deletions(-) >> create mode 100644 target/i386/accel/hvf/hvf-cpu-type.c >> create mode 100644 target/i386/accel/kvm/kvm-cpu-type.c >> create mode 100644 target/i386/accel/kvm/kvm-cpu-type.h >> create mode 100644 target/i386/accel/tcg/tcg-cpu-type.c >> create mode 100644 target/i386/accel/tcg/tcg-cpu-type.h >> create mode 100644 target/i386/host-cpu.c >> create mode 100644 target/i386/host-cpu.h >> delete mode 100644 target/i386/tcg-cpu.c >> delete mode 100644 target/i386/tcg-cpu.h >> >> diff --git a/bsd-user/main.c b/bsd-user/main.c >> index ac40d79bfa..48dd4b8ba5 100644 >> --- a/bsd-user/main.c >> +++ b/bsd-user/main.c >> @@ -911,6 +911,10 @@ int main(int argc, char **argv) >> >> /* init tcg before creating CPUs and to get qemu_host_page_size */ >> tcg_exec_init(0); >> + /* >> + * TCG has been initialized, now it is time to register the cpu models. >> + */ >> + module_call_init(MODULE_INIT_ACCEL_CPU); >> >> cpu_type = parse_cpu_option(cpu_model); >> cpu = cpu_create(cpu_type); >> diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c >> index 13d1628f13..a59c3e1457 100644 >> --- a/hw/i386/pc_piix.c >> +++ b/hw/i386/pc_piix.c >> @@ -64,6 +64,7 @@ >> #include "hw/hyperv/vmbus-bridge.h" >> #include "hw/mem/nvdimm.h" >> #include "hw/i386/acpi-build.h" >> +#include "accel/kvm/kvm-cpu-type.h" >> >> #define MAX_IDE_BUS 2 >> >> diff --git a/linux-user/main.c b/linux-user/main.c >> index 75c9785157..86ebd5db5e 100644 >> --- a/linux-user/main.c >> +++ b/linux-user/main.c >> @@ -699,14 +699,18 @@ int main(int argc, char **argv, char **envp) >> } >> } >> >> + /* init tcg before creating CPUs and to get qemu_host_page_size */ >> + tcg_exec_init(0); >> + /* >> + * TCG has been initialized, now it is time to register the cpu models. >> + */ >> + module_call_init(MODULE_INIT_ACCEL_CPU); >> + >> if (cpu_model == NULL) { >> cpu_model = cpu_get_model(get_elf_eflags(execfd)); >> } >> cpu_type = parse_cpu_option(cpu_model); >> >> - /* init tcg before creating CPUs and to get qemu_host_page_size */ >> - tcg_exec_init(0); >> - >> cpu = cpu_create(cpu_type); >> env = cpu->env_ptr; >> cpu_reset(cpu); >> diff --git a/softmmu/vl.c b/softmmu/vl.c >> index 6a6363902d..47cc938cef 100644 >> --- a/softmmu/vl.c >> +++ b/softmmu/vl.c >> @@ -4176,7 +4176,7 @@ void qemu_init(int argc, char **argv, char **envp) >> >> /* >> * accelerator has been chosen and initialized, now it is time to >> - * register the cpu accel interface. >> + * register the cpu models, and the cpu accel interface. >> */ >> module_call_init(MODULE_INIT_ACCEL_CPU); >> >> diff --git a/target/i386/accel/hvf/hvf-cpu-type.c >> b/target/i386/accel/hvf/hvf-cpu-type.c >> new file mode 100644 >> index 0000000000..dfe4ec4e9e >> --- /dev/null >> +++ b/target/i386/accel/hvf/hvf-cpu-type.c >> @@ -0,0 +1,78 @@ >> +/* >> + * x86 HVF CPU type initialization >> + * >> + * Copyright 2020 SUSE LLC >> + * >> + * 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 "cpu.h" >> +#include "host-cpu.h" >> +#include "hvf-cpu-type.h" >> +#include "qapi/error.h" >> +#include "sysemu/sysemu.h" >> +#include "hw/boards.h" >> +#include "sysemu/hvf.h" >> + >> + >> +static void hvf_cpu_common_class_init(ObjectClass *oc, void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(oc); >> + >> + dc->realize = host_cpu_realizefn; >> +} >> + >> +static void hvf_cpu_max_initfn(Object *obj) >> +{ >> + X86CPU *cpu = X86_CPU(obj); >> + CPUX86State *env = &cpu->env; >> + >> + host_cpu_max_initfn(cpu); >> + >> + env->cpuid_min_level = >> + hvf_get_supported_cpuid(0x0, 0, R_EAX); >> + env->cpuid_min_xlevel = >> + hvf_get_supported_cpuid(0x80000000, 0, R_EAX); >> + env->cpuid_min_xlevel2 = >> + hvf_get_supported_cpuid(0xC0000000, 0, R_EAX); >> +} >> + >> +static void hvf_cpu_initfn(Object *obj) >> +{ >> + X86CPU *cpu = X86_CPU(obj); >> + >> + host_cpu_initfn(obj); >> + >> + /* Special cases not set in the X86CPUDefinition structs: */ >> + /* TODO: in-kernel irqchip for hvf */ >> + >> + if (cpu->max_features) { >> + hvf_cpu_max_initfn(obj); >> + } >> +} >> + >> +static const TypeInfo hvf_cpu_type_info = { >> + .name = X86_CPU_TYPE_NAME("hvf"), >> + .parent = TYPE_X86_CPU, >> + >> + .instance_init = hvf_cpu_initfn, >> + .class_init = hvf_cpu_common_class_init, >> +}; >> + >> +static void hvf_cpu_register_base_type(void) >> +{ >> + type_register_static(&hvf_cpu_type_info); >> +} >> + >> +type_init(hvf_cpu_register_base_type); >> + >> +void hvf_cpu_type_init(void) >> +{ >> + if (hvf_enabled()) { >> + x86_cpu_register_cpu_models(X86_CPU_TYPE_NAME("hvf")); >> + } >> +} >> + >> +accel_cpu_init(hvf_cpu_type_init); >> diff --git a/target/i386/accel/hvf/meson.build >> b/target/i386/accel/hvf/meson.build >> index 409c9a3f14..785dee72fc 100644 >> --- a/target/i386/accel/hvf/meson.build >> +++ b/target/i386/accel/hvf/meson.build >> @@ -10,4 +10,5 @@ i386_softmmu_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: >> files( >> 'x86_mmu.c', >> 'x86_task.c', >> 'x86hvf.c', >> + 'hvf-cpu-type.c', >> )) >> diff --git a/target/i386/accel/kvm/kvm-cpu-type.c >> b/target/i386/accel/kvm/kvm-cpu-type.c >> new file mode 100644 >> index 0000000000..f696f21e2b >> --- /dev/null >> +++ b/target/i386/accel/kvm/kvm-cpu-type.c >> @@ -0,0 +1,161 @@ >> +/* >> + * x86 KVM CPU type initialization >> + * >> + * Copyright 2020 SUSE LLC >> + * >> + * 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 "cpu.h" >> +#include "host-cpu.h" >> +#include "kvm-cpu-type.h" >> +#include "qapi/error.h" >> +#include "sysemu/sysemu.h" >> +#include "hw/boards.h" >> + >> +#include "kvm_i386.h" >> + >> +/* this information overloads the TYPE_X86_CPU type in x86-cpu.c */ >> + >> +static void kvm_cpu_realizefn(DeviceState *dev, Error **errp) >> +{ >> + X86CPU *cpu = X86_CPU(dev); >> + CPUX86State *env = &cpu->env; >> + >> + /* >> + * also for KVM the realize order is important, since >> + * x86_cpu_realize() checks if nothing else has been set by the user, >> + * or by the specialized x86 cpus (KVM, HVF) in >> + * cpu->ucode_rev and cpu->phys_bits. >> + * >> + * So it's kvm_cpu -> host_cpu -> x86_cpu >> + */ >> + if (cpu->max_features) { >> + if (enable_cpu_pm && kvm_has_waitpkg()) { >> + env->features[FEAT_7_0_ECX] |= CPUID_7_0_ECX_WAITPKG; >> + } >> + if (cpu->ucode_rev == 0) { >> + cpu->ucode_rev = >> + kvm_arch_get_supported_msr_feature(kvm_state, >> + MSR_IA32_UCODE_REV); >> + } >> + } >> + host_cpu_realizefn(dev, errp); >> +} >> + >> +static void kvm_cpu_common_class_init(ObjectClass *oc, void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(oc); >> + dc->realize = kvm_cpu_realizefn; >> +} >> + >> +/* >> + * KVM-specific features that are automatically added/removed >> + * from all CPU models when KVM is enabled. >> + */ >> +static PropValue kvm_default_props[] = { >> + { "kvmclock", "on" }, >> + { "kvm-nopiodelay", "on" }, >> + { "kvm-asyncpf", "on" }, >> + { "kvm-steal-time", "on" }, >> + { "kvm-pv-eoi", "on" }, >> + { "kvmclock-stable-bit", "on" }, >> + { "x2apic", "on" }, >> + { "acpi", "off" }, >> + { "monitor", "off" }, >> + { "svm", "off" }, >> + { NULL, NULL }, >> +}; >> + >> +void x86_cpu_change_kvm_default(const char *prop, const char *value) >> +{ >> + PropValue *pv; >> + for (pv = kvm_default_props; pv->prop; pv++) { >> + if (!strcmp(pv->prop, prop)) { >> + pv->value = value; >> + break; >> + } >> + } >> + >> + /* >> + * It is valid to call this function only for properties that >> + * are already present in the kvm_default_props table. >> + */ >> + assert(pv->prop); >> +} >> + >> +static bool lmce_supported(void) >> +{ >> + uint64_t mce_cap = 0; >> + >> + if (kvm_ioctl(kvm_state, KVM_X86_GET_MCE_CAP_SUPPORTED, &mce_cap) < 0) { >> + return false; >> + } >> + return !!(mce_cap & MCG_LMCE_P); >> +} >> + >> +static void kvm_cpu_max_initfn(Object *obj) >> +{ >> + X86CPU *cpu = X86_CPU(obj); >> + CPUX86State *env = &cpu->env; >> + KVMState *s = kvm_state; >> + >> + host_cpu_max_initfn(cpu); >> + >> + if (lmce_supported()) { >> + object_property_set_bool(OBJECT(cpu), "lmce", true, &error_abort); >> + } >> + >> + env->cpuid_min_level = >> + kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX); >> + env->cpuid_min_xlevel = >> + kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX); >> + env->cpuid_min_xlevel2 = >> + kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX); >> +} >> + >> +static void kvm_cpu_initfn(Object *obj) >> +{ >> + X86CPU *cpu = X86_CPU(obj); >> + >> + host_cpu_initfn(obj); >> + >> + if (!kvm_irqchip_in_kernel()) { >> + x86_cpu_change_kvm_default("x2apic", "off"); >> + } >> + >> + /* Special cases not set in the X86CPUDefinition structs: */ >> + >> + x86_cpu_apply_props(cpu, kvm_default_props); >> + >> + if (cpu->max_features) { >> + kvm_cpu_max_initfn(obj); >> + } >> +} >> + >> +static const TypeInfo kvm_cpu_type_info = { >> + .name = X86_CPU_TYPE_NAME("kvm"), >> + .parent = TYPE_X86_CPU, >> + >> + .instance_init = kvm_cpu_initfn, >> + .class_init = kvm_cpu_common_class_init, >> +}; >> + >> +static void kvm_cpu_register_base_type(void) >> +{ >> + type_register_static(&kvm_cpu_type_info); >> +} >> + >> +type_init(kvm_cpu_register_base_type); >> + >> +void kvm_cpu_type_init(void) >> +{ >> + if (kvm_enabled()) { >> + x86_cpu_register_cpu_models(X86_CPU_TYPE_NAME("kvm")); >> + host_cpu_type_init(); >> + } >> +} >> + >> +accel_cpu_init(kvm_cpu_type_init); >> diff --git a/target/i386/accel/kvm/kvm-cpu-type.h >> b/target/i386/accel/kvm/kvm-cpu-type.h >> new file mode 100644 >> index 0000000000..2448f49222 >> --- /dev/null >> +++ b/target/i386/accel/kvm/kvm-cpu-type.h >> @@ -0,0 +1,41 @@ >> +/* >> + * i386 KVM CPU type initialization >> + * >> + * Copyright (c) 2003 Fabrice Bellard >> + * >> + * This library is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU Lesser General Public >> + * License as published by the Free Software Foundation; either >> + * version 2 of the License, or (at your option) any later version. >> + * >> + * This library is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + * Lesser General Public License for more details. >> + * >> + * You should have received a copy of the GNU Lesser General Public >> + * License along with this library; if not, see >> <http://www.gnu.org/licenses/>. >> + */ >> + >> +#ifndef KVM_CPU_TYPE_H >> +#define KVM_CPU_TYPE_H >> + >> +#ifdef CONFIG_KVM >> +void kvm_cpu_type_init(void); >> + >> +/* >> + * Change the value of a KVM-specific default >> + * >> + * If value is NULL, no default will be set and the original >> + * value from the CPU model table will be kept. >> + * >> + * It is valid to call this function only for properties that >> + * are already present in the kvm_default_props table. >> + */ >> +void x86_cpu_change_kvm_default(const char *prop, const char *value); >> + >> +#else /* CONFIG_KVM */ >> +#define x86_cpu_change_kvm_default(a, b) >> +#endif /* CONFIG_KVM */ >> + >> +#endif /* KVM_CPU_TYPE_H */ >> diff --git a/target/i386/accel/kvm/kvm.c b/target/i386/accel/kvm/kvm.c >> index cf46259534..829730d3c2 100644 >> --- a/target/i386/accel/kvm/kvm.c >> +++ b/target/i386/accel/kvm/kvm.c >> @@ -22,6 +22,7 @@ >> #include "standard-headers/asm-x86/kvm_para.h" >> >> #include "cpu.h" >> +#include "host-cpu.h" >> #include "sysemu/sysemu.h" >> #include "sysemu/hw_accel.h" >> #include "sysemu/kvm_int.h" >> @@ -285,7 +286,7 @@ static bool host_tsx_broken(void) >> int family, model, stepping;\ >> char vendor[CPUID_VENDOR_SZ + 1]; >> >> - host_vendor_fms(vendor, &family, &model, &stepping); >> + host_cpu_vendor_fms(vendor, &family, &model, &stepping); >> >> /* Check if we are running on a Haswell host known to have broken TSX */ >> return !strcmp(vendor, CPUID_VENDOR_INTEL) && >> diff --git a/target/i386/accel/kvm/meson.build >> b/target/i386/accel/kvm/meson.build >> index 1d66559187..b6b32166b4 100644 >> --- a/target/i386/accel/kvm/meson.build >> +++ b/target/i386/accel/kvm/meson.build >> @@ -1,3 +1,8 @@ >> i386_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) >> -i386_softmmu_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c')) >> + >> +i386_softmmu_ss.add(when: 'CONFIG_KVM', if_true: files( >> + 'kvm.c', >> + 'kvm-cpu-type.c', >> +)) >> + >> i386_softmmu_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), >> if_false: files('hyperv-stub.c')) >> diff --git a/target/i386/accel/tcg/meson.build >> b/target/i386/accel/tcg/meson.build >> index 02794226c2..211ecef5f9 100644 >> --- a/target/i386/accel/tcg/meson.build >> +++ b/target/i386/accel/tcg/meson.build >> @@ -10,4 +10,5 @@ i386_ss.add(when: 'CONFIG_TCG', if_true: files( >> 'seg_helper.c', >> 'smm_helper.c', >> 'svm_helper.c', >> - 'translate.c'), if_false: files('tcg-stub.c')) >> + 'translate.c', >> + 'tcg-cpu-type.c'), if_false: files('tcg-stub.c')) >> diff --git a/target/i386/accel/tcg/tcg-cpu-type.c >> b/target/i386/accel/tcg/tcg-cpu-type.c >> new file mode 100644 >> index 0000000000..f0ee4ef5f7 >> --- /dev/null >> +++ b/target/i386/accel/tcg/tcg-cpu-type.c >> @@ -0,0 +1,176 @@ >> +/* >> + * i386 TCG cpu class initialization >> + * >> + * Copyright (c) 2003 Fabrice Bellard >> + * >> + * This library is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU Lesser General Public >> + * License as published by the Free Software Foundation; either >> + * version 2 of the License, or (at your option) any later version. >> + * >> + * This library is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + * Lesser General Public License for more details. >> + * >> + * You should have received a copy of the GNU Lesser General Public >> + * License along with this library; if not, see >> <http://www.gnu.org/licenses/>. >> + */ >> + >> +#include "qemu/osdep.h" >> +#include "qemu/units.h" >> +#include "cpu.h" >> +#include "tcg-cpu-type.h" >> + >> +#include "helper-tcg.h" >> +#include "sysemu/sysemu.h" >> + >> +#ifndef CONFIG_USER_ONLY >> +#include "exec/address-spaces.h" >> +#endif >> + >> +/* Frob eflags into and out of the CPU temporary format. */ >> + >> +static void x86_cpu_exec_enter(CPUState *cs) >> +{ >> + X86CPU *cpu = X86_CPU(cs); >> + CPUX86State *env = &cpu->env; >> + >> + CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); >> + env->df = 1 - (2 * ((env->eflags >> 10) & 1)); >> + CC_OP = CC_OP_EFLAGS; >> + env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); >> +} >> + >> +static void x86_cpu_exec_exit(CPUState *cs) >> +{ >> + X86CPU *cpu = X86_CPU(cs); >> + CPUX86State *env = &cpu->env; >> + >> + env->eflags = cpu_compute_eflags(env); >> +} >> + >> +static void x86_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) >> +{ >> + X86CPU *cpu = X86_CPU(cs); >> + >> + cpu->env.eip = tb->pc - tb->cs_base; >> +} >> + >> +#ifndef CONFIG_USER_ONLY >> + >> +static void x86_cpu_machine_done(Notifier *n, void *unused) >> +{ >> + X86CPU *cpu = container_of(n, X86CPU, machine_done); >> + MemoryRegion *smram = >> + (MemoryRegion *) object_resolve_path("/machine/smram", NULL); >> + >> + if (smram) { >> + cpu->smram = g_new(MemoryRegion, 1); >> + memory_region_init_alias(cpu->smram, OBJECT(cpu), "smram", >> + smram, 0, 4 * GiB); >> + memory_region_set_enabled(cpu->smram, true); >> + memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, >> + cpu->smram, 1); >> + } >> +} >> + >> +static void tcg_cpu_realizefn(DeviceState *dev, Error **errp) >> +{ >> + X86CPU *cpu = X86_CPU(dev); >> + CPUState *cs = CPU(dev); >> + >> + /* >> + * also for TCG the realize order is important, >> + * as the memory regions initialized here are needed >> + * in x86_cpu_realizefn() >> + */ >> + cpu->cpu_as_mem = g_new(MemoryRegion, 1); >> + cpu->cpu_as_root = g_new(MemoryRegion, 1); >> + >> + /* Outer container... */ >> + memory_region_init(cpu->cpu_as_root, OBJECT(cpu), "memory", ~0ull); >> + memory_region_set_enabled(cpu->cpu_as_root, true); >> + >> + /* >> + * ... with two regions inside: normal system memory with low >> + * priority, and... >> + */ >> + memory_region_init_alias(cpu->cpu_as_mem, OBJECT(cpu), "memory", >> + get_system_memory(), 0, ~0ull); >> + memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, >> cpu->cpu_as_mem, 0); >> + memory_region_set_enabled(cpu->cpu_as_mem, true); >> + >> + cs->num_ases = 2; >> + cpu_address_space_init(cs, 0, "cpu-memory", cs->memory); >> + cpu_address_space_init(cs, 1, "cpu-smm", cpu->cpu_as_root); >> + >> + /* ... SMRAM with higher priority, linked from /machine/smram. */ >> + cpu->machine_done.notify = x86_cpu_machine_done; >> + qemu_add_machine_init_done_notifier(&cpu->machine_done); >> + >> + /* call the generic x86_cpu_realizefn() after regions initialized */ >> + x86_cpu_realizefn(dev, errp); >> +} >> +#endif /* !CONFIG_USER_ONLY */ >> + >> +static void tcg_cpu_common_class_init(ObjectClass *oc, void *data) >> +{ >> + CPUClass *cc = CPU_CLASS(oc); >> +#ifndef CONFIG_USER_ONLY >> + DeviceClass *dc = DEVICE_CLASS(oc); >> + >> + dc->realize = tcg_cpu_realizefn; >> +#endif /* !CONFIG_USER_ONLY */ >> + >> + cc->do_interrupt = x86_cpu_do_interrupt; >> + cc->cpu_exec_interrupt = x86_cpu_exec_interrupt; >> + cc->synchronize_from_tb = x86_cpu_synchronize_from_tb; >> + cc->cpu_exec_enter = x86_cpu_exec_enter; >> + cc->cpu_exec_exit = x86_cpu_exec_exit; >> + cc->tcg_initialize = tcg_x86_init; >> + cc->tlb_fill = x86_cpu_tlb_fill; >> +#ifndef CONFIG_USER_ONLY >> + cc->debug_excp_handler = breakpoint_handler; >> +#endif /* !CONFIG_USER_ONLY */ >> +} >> + >> +/* >> + * TCG-specific defaults that override all CPU models when using TCG >> + */ >> +static PropValue tcg_default_props[] = { >> + { "vme", "off" }, >> + { NULL, NULL }, >> +}; >> + >> +static void tcg_cpu_initfn(Object *obj) >> +{ >> + X86CPU *cpu = X86_CPU(obj); >> + >> + /* Special cases not set in the X86CPUDefinition structs: */ >> + x86_cpu_apply_props(cpu, tcg_default_props); >> +} >> + >> +static const TypeInfo tcg_cpu_type_info = { >> + .name = X86_CPU_TYPE_NAME("tcg"), >> + .parent = TYPE_X86_CPU, >> + >> + .instance_init = tcg_cpu_initfn, >> + .class_init = tcg_cpu_common_class_init, >> +}; >> + >> +static void tcg_cpu_register_base_type(void) >> +{ >> + type_register_static(&tcg_cpu_type_info); >> +} >> + >> +type_init(tcg_cpu_register_base_type); >> + >> +void tcg_cpu_type_init(void) >> +{ >> + if (tcg_enabled()) { >> + x86_cpu_register_cpu_models(X86_CPU_TYPE_NAME("tcg")); >> + } >> +} >> + >> +accel_cpu_init(tcg_cpu_type_init); >> diff --git a/target/i386/accel/tcg/tcg-cpu-type.h >> b/target/i386/accel/tcg/tcg-cpu-type.h >> new file mode 100644 >> index 0000000000..d741a8f6be >> --- /dev/null >> +++ b/target/i386/accel/tcg/tcg-cpu-type.h >> @@ -0,0 +1,25 @@ >> +/* >> + * i386 TCG CPU type initialization >> + * >> + * Copyright (c) 2003 Fabrice Bellard >> + * >> + * This library is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU Lesser General Public >> + * License as published by the Free Software Foundation; either >> + * version 2 of the License, or (at your option) any later version. >> + * >> + * This library is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + * Lesser General Public License for more details. >> + * >> + * You should have received a copy of the GNU Lesser General Public >> + * License along with this library; if not, see >> <http://www.gnu.org/licenses/>. >> + */ >> + >> +#ifndef TCG_CPU_TYPE_H >> +#define TCG_CPU_TYPE_H >> + >> +void tcg_cpu_type_init(void); >> + >> +#endif /* TCG_CPU_TYPE_H */ >> diff --git a/target/i386/cpu.c b/target/i386/cpu.c >> index b185789d88..b547c9d39d 100644 >> --- a/target/i386/cpu.c >> +++ b/target/i386/cpu.c >> @@ -22,9 +22,7 @@ >> #include "qemu/cutils.h" >> #include "qemu/bitops.h" >> #include "qemu/qemu-print.h" >> - >> #include "cpu.h" >> -#include "tcg-cpu.h" >> #include "helper-tcg.h" >> #include "exec/exec-all.h" >> #include "sysemu/kvm.h" >> @@ -34,27 +32,17 @@ >> #include "sysemu/xen.h" >> #include "accel/kvm/kvm_i386.h" >> #include "sev_i386.h" >> - >> -#include "qemu/error-report.h" >> #include "qemu/module.h" >> -#include "qemu/option.h" >> -#include "qemu/config-file.h" >> -#include "qapi/error.h" >> #include "qapi/qapi-visit-machine.h" >> #include "qapi/qapi-visit-run-state.h" >> #include "qapi/qmp/qdict.h" >> #include "qapi/qmp/qerror.h" >> -#include "qapi/visitor.h" >> #include "qom/qom-qobject.h" >> -#include "sysemu/arch_init.h" >> #include "qapi/qapi-commands-machine-target.h" >> - >> #include "standard-headers/asm-x86/kvm_para.h" >> - >> -#include "sysemu/sysemu.h" >> -#include "sysemu/tcg.h" >> #include "hw/qdev-properties.h" >> #include "hw/i386/topology.h" >> + >> #ifndef CONFIG_USER_ONLY >> #include "exec/address-spaces.h" >> #include "hw/i386/apic_internal.h" >> @@ -594,8 +582,8 @@ static CPUCacheInfo legacy_l3_cache = { >> #define INTEL_PT_CYCLE_BITMAP 0x1fff /* Support 0,2^(0~11) */ >> #define INTEL_PT_PSB_BITMAP (0x003f << 16) /* Support >> 2K,4K,8K,16K,32K,64K */ >> >> -static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, >> - uint32_t vendor2, uint32_t vendor3) >> +void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, >> + uint32_t vendor2, uint32_t vendor3) >> { >> int i; >> for (i = 0; i < 4; i++) { >> @@ -1563,25 +1551,6 @@ void host_cpuid(uint32_t function, uint32_t count, >> *edx = vec[3]; >> } >> >> -void host_vendor_fms(char *vendor, int *family, int *model, int *stepping) >> -{ >> - uint32_t eax, ebx, ecx, edx; >> - >> - host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx); >> - x86_cpu_vendor_words2str(vendor, ebx, edx, ecx); >> - >> - host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx); >> - if (family) { >> - *family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF); >> - } >> - if (model) { >> - *model = ((eax >> 4) & 0x0F) | ((eax & 0xF0000) >> 12); >> - } >> - if (stepping) { >> - *stepping = eax & 0x0F; >> - } >> -} >> - >> /* CPU class name definitions: */ >> >> /* Return type name for a given CPU model name >> @@ -1606,10 +1575,6 @@ static char *x86_cpu_class_get_model_name(X86CPUClass >> *cc) >> strlen(class_name) - strlen(X86_CPU_TYPE_SUFFIX)); >> } >> >> -typedef struct PropValue { >> - const char *prop, *value; >> -} PropValue; >> - >> typedef struct X86CPUVersionDefinition { >> X86CPUVersion version; >> const char *alias; >> @@ -4106,31 +4071,6 @@ static X86CPUDefinition builtin_x86_defs[] = { >> }, >> }; >> >> -/* KVM-specific features that are automatically added/removed >> - * from all CPU models when KVM is enabled. >> - */ >> -static PropValue kvm_default_props[] = { >> - { "kvmclock", "on" }, >> - { "kvm-nopiodelay", "on" }, >> - { "kvm-asyncpf", "on" }, >> - { "kvm-steal-time", "on" }, >> - { "kvm-pv-eoi", "on" }, >> - { "kvmclock-stable-bit", "on" }, >> - { "x2apic", "on" }, >> - { "acpi", "off" }, >> - { "monitor", "off" }, >> - { "svm", "off" }, >> - { NULL, NULL }, >> -}; >> - >> -/* TCG-specific defaults that override all CPU models when using TCG >> - */ >> -static PropValue tcg_default_props[] = { >> - { "vme", "off" }, >> - { NULL, NULL }, >> -}; >> - >> - >> /* >> * We resolve CPU model aliases using -v1 when using "-machine >> * none", but this is just for compatibility while libvirt isn't >> @@ -4172,61 +4112,6 @@ static X86CPUVersion >> x86_cpu_model_resolve_version(const X86CPUModel *model) >> return v; >> } >> >> -void x86_cpu_change_kvm_default(const char *prop, const char *value) >> -{ >> - PropValue *pv; >> - for (pv = kvm_default_props; pv->prop; pv++) { >> - if (!strcmp(pv->prop, prop)) { >> - pv->value = value; >> - break; >> - } >> - } >> - >> - /* It is valid to call this function only for properties that >> - * are already present in the kvm_default_props table. >> - */ >> - assert(pv->prop); >> -} >> - >> -static bool lmce_supported(void) >> -{ >> - uint64_t mce_cap = 0; >> - >> -#ifdef CONFIG_KVM >> - if (kvm_ioctl(kvm_state, KVM_X86_GET_MCE_CAP_SUPPORTED, &mce_cap) < 0) { >> - return false; >> - } >> -#endif >> - >> - return !!(mce_cap & MCG_LMCE_P); >> -} >> - >> -#define CPUID_MODEL_ID_SZ 48 >> - >> -/** >> - * cpu_x86_fill_model_id: >> - * Get CPUID model ID string from host CPU. >> - * >> - * @str should have at least CPUID_MODEL_ID_SZ bytes >> - * >> - * The function does NOT add a null terminator to the string >> - * automatically. >> - */ >> -static int cpu_x86_fill_model_id(char *str) >> -{ >> - uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; >> - int i; >> - >> - for (i = 0; i < 3; i++) { >> - host_cpuid(0x80000002 + i, 0, &eax, &ebx, &ecx, &edx); >> - memcpy(str + i * 16 + 0, &eax, 4); >> - memcpy(str + i * 16 + 4, &ebx, 4); >> - memcpy(str + i * 16 + 8, &ecx, 4); >> - memcpy(str + i * 16 + 12, &edx, 4); >> - } >> - return 0; >> -} >> - >> static Property max_x86_cpu_properties[] = { >> DEFINE_PROP_BOOL("migratable", X86CPU, migratable, true), >> DEFINE_PROP_BOOL("host-cache-info", X86CPU, cache_info_passthrough, >> false), >> @@ -4246,98 +4131,38 @@ static void max_x86_cpu_class_init(ObjectClass *oc, >> void *data) >> device_class_set_props(dc, max_x86_cpu_properties); >> } >> >> -static void max_x86_cpu_initfn(Object *obj) >> +void max_x86_cpu_initfn(Object *obj) >> { >> X86CPU *cpu = X86_CPU(obj); >> - CPUX86State *env = &cpu->env; >> - KVMState *s = kvm_state; >> >> /* We can't fill the features array here because we don't know yet if >> * "migratable" is true or false. >> */ >> cpu->max_features = true; >> - >> - if (accel_uses_host_cpuid()) { >> - char vendor[CPUID_VENDOR_SZ + 1] = { 0 }; >> - char model_id[CPUID_MODEL_ID_SZ + 1] = { 0 }; >> - int family, model, stepping; >> - >> - host_vendor_fms(vendor, &family, &model, &stepping); >> - cpu_x86_fill_model_id(model_id); >> - >> - object_property_set_str(OBJECT(cpu), "vendor", vendor, >> &error_abort); >> - object_property_set_int(OBJECT(cpu), "family", family, >> &error_abort); >> - object_property_set_int(OBJECT(cpu), "model", model, &error_abort); >> - object_property_set_int(OBJECT(cpu), "stepping", stepping, >> - &error_abort); >> - object_property_set_str(OBJECT(cpu), "model-id", model_id, >> - &error_abort); >> - >> - if (kvm_enabled()) { >> - env->cpuid_min_level = >> - kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX); >> - env->cpuid_min_xlevel = >> - kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX); >> - env->cpuid_min_xlevel2 = >> - kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX); >> - } else { >> - env->cpuid_min_level = >> - hvf_get_supported_cpuid(0x0, 0, R_EAX); >> - env->cpuid_min_xlevel = >> - hvf_get_supported_cpuid(0x80000000, 0, R_EAX); >> - env->cpuid_min_xlevel2 = >> - hvf_get_supported_cpuid(0xC0000000, 0, R_EAX); >> - } >> - >> - if (lmce_supported()) { >> - object_property_set_bool(OBJECT(cpu), "lmce", true, >> &error_abort); >> - } >> - } else { >> - object_property_set_str(OBJECT(cpu), "vendor", CPUID_VENDOR_AMD, >> - &error_abort); >> - object_property_set_int(OBJECT(cpu), "family", 6, &error_abort); >> - object_property_set_int(OBJECT(cpu), "model", 6, &error_abort); >> - object_property_set_int(OBJECT(cpu), "stepping", 3, &error_abort); >> - object_property_set_str(OBJECT(cpu), "model-id", >> - "QEMU TCG CPU version " QEMU_HW_VERSION, >> - &error_abort); >> - } >> - >> object_property_set_bool(OBJECT(cpu), "pmu", true, &error_abort); >> + >> + /* >> + * these defaults are used for TCG and all other accelerators >> + * besides KVM and HVF, which overwrite these values >> + */ >> + object_property_set_str(OBJECT(cpu), "vendor", CPUID_VENDOR_AMD, >> + &error_abort); >> + object_property_set_int(OBJECT(cpu), "family", 6, &error_abort); >> + object_property_set_int(OBJECT(cpu), "model", 6, &error_abort); >> + object_property_set_int(OBJECT(cpu), "stepping", 3, &error_abort); >> + object_property_set_str(OBJECT(cpu), "model-id", >> + "QEMU TCG CPU version " QEMU_HW_VERSION, >> + &error_abort); >> } >> >> -static const TypeInfo max_x86_cpu_type_info = { >> +static TypeInfo max_x86_cpu_type_info = { >> .name = X86_CPU_TYPE_NAME("max"), >> - .parent = TYPE_X86_CPU, >> - .instance_init = max_x86_cpu_initfn, >> + .parent = NULL, /* set by x86_cpu_register_cpu_models */ >> + >> .class_init = max_x86_cpu_class_init, >> + .instance_init = max_x86_cpu_initfn, >> }; >> >> -#if defined(CONFIG_KVM) || defined(CONFIG_HVF) >> -static void host_x86_cpu_class_init(ObjectClass *oc, void *data) >> -{ >> - X86CPUClass *xcc = X86_CPU_CLASS(oc); >> - >> - xcc->host_cpuid_required = true; >> - xcc->ordering = 8; >> - >> -#if defined(CONFIG_KVM) >> - xcc->model_description = >> - "KVM processor with all supported host features "; >> -#elif defined(CONFIG_HVF) >> - xcc->model_description = >> - "HVF processor with all supported host features "; >> -#endif >> -} >> - >> -static const TypeInfo host_x86_cpu_type_info = { >> - .name = X86_CPU_TYPE_NAME("host"), >> - .parent = X86_CPU_TYPE_NAME("max"), >> - .class_init = host_x86_cpu_class_init, >> -}; >> - >> -#endif >> - >> static char *feature_word_description(FeatureWordInfo *f, uint32_t bit) >> { >> assert(f->type == CPUID_FEATURE_WORD || f->type == MSR_FEATURE_WORD); >> @@ -5063,7 +4888,7 @@ static uint64_t >> x86_cpu_get_supported_feature_word(FeatureWord w, >> return r; >> } >> >> -static void x86_cpu_apply_props(X86CPU *cpu, PropValue *props) >> +void x86_cpu_apply_props(X86CPU *cpu, PropValue *props) >> { >> PropValue *pv; >> for (pv = props; pv->prop; pv++) { >> @@ -5110,8 +4935,6 @@ static void x86_cpu_load_model(X86CPU *cpu, >> X86CPUModel *model) >> { >> X86CPUDefinition *def = model->cpudef; >> CPUX86State *env = &cpu->env; >> - const char *vendor; >> - char host_vendor[CPUID_VENDOR_SZ + 1]; >> FeatureWord w; >> >> /*NOTE: any property set by this function should be returned by >> @@ -5138,18 +4961,6 @@ static void x86_cpu_load_model(X86CPU *cpu, >> X86CPUModel *model) >> /* legacy-cache defaults to 'off' if CPU model provides cache info */ >> cpu->legacy_cache = !def->cache_info; >> >> - /* Special cases not set in the X86CPUDefinition structs: */ >> - /* TODO: in-kernel irqchip for hvf */ >> - if (kvm_enabled()) { >> - if (!kvm_irqchip_in_kernel()) { >> - x86_cpu_change_kvm_default("x2apic", "off"); >> - } >> - >> - x86_cpu_apply_props(cpu, kvm_default_props); >> - } else if (tcg_enabled()) { >> - x86_cpu_apply_props(cpu, tcg_default_props); >> - } >> - >> env->features[FEAT_1_ECX] |= CPUID_EXT_HYPERVISOR; >> >> /* sysenter isn't supported in compatibility mode on AMD, >> @@ -5159,15 +4970,12 @@ static void x86_cpu_load_model(X86CPU *cpu, >> X86CPUModel *model) >> * KVM's sysenter/syscall emulation in compatibility mode and >> * when doing cross vendor migration >> */ >> - vendor = def->vendor; >> - if (accel_uses_host_cpuid()) { >> - uint32_t ebx = 0, ecx = 0, edx = 0; >> - host_cpuid(0, 0, NULL, &ebx, &ecx, &edx); >> - x86_cpu_vendor_words2str(host_vendor, ebx, edx, ecx); >> - vendor = host_vendor; >> - } >> >> - object_property_set_str(OBJECT(cpu), "vendor", vendor, &error_abort); >> + /* >> + * vendor property is set here but then overloaded with the >> + * host cpu vendor for KVM and HVF. >> + */ >> + object_property_set_str(OBJECT(cpu), "vendor", def->vendor, >> &error_abort); >> >> x86_cpu_apply_version_props(cpu, model); >> >> @@ -5400,12 +5208,13 @@ static void x86_cpu_cpudef_class_init(ObjectClass >> *oc, void *data) >> cc->deprecation_note = model->cpudef->deprecation_note; >> } >> >> -static void x86_register_cpu_model_type(const char *name, X86CPUModel >> *model) >> +static void x86_register_cpu_model_type(const char *name, X86CPUModel >> *model, >> + const char *parent_type) >> { >> g_autofree char *typename = x86_cpu_type_name(name); >> TypeInfo ti = { >> .name = typename, >> - .parent = TYPE_X86_CPU, >> + .parent = parent_type, >> .class_init = x86_cpu_cpudef_class_init, >> .class_data = model, >> }; >> @@ -5413,7 +5222,8 @@ static void x86_register_cpu_model_type(const char >> *name, X86CPUModel *model) >> type_register(&ti); >> } >> >> -static void x86_register_cpudef_types(X86CPUDefinition *def) >> +static void x86_register_cpudef(X86CPUDefinition *def, >> + const char *parent_type) >> { >> X86CPUModel *m; >> const X86CPUVersionDefinition *vdef; >> @@ -5430,7 +5240,7 @@ static void x86_register_cpudef_types(X86CPUDefinition >> *def) >> m->cpudef = def; >> m->version = CPU_VERSION_AUTO; >> m->is_alias = true; >> - x86_register_cpu_model_type(def->name, m); >> + x86_register_cpu_model_type(def->name, m, parent_type); >> >> /* Versioned models: */ >> >> @@ -5441,14 +5251,14 @@ static void >> x86_register_cpudef_types(X86CPUDefinition *def) >> m->cpudef = def; >> m->version = vdef->version; >> m->note = vdef->note; >> - x86_register_cpu_model_type(name, m); >> + x86_register_cpu_model_type(name, m, parent_type); >> >> if (vdef->alias) { >> X86CPUModel *am = g_new0(X86CPUModel, 1); >> am->cpudef = def; >> am->version = vdef->version; >> am->is_alias = true; >> - x86_register_cpu_model_type(vdef->alias, am); >> + x86_register_cpu_model_type(vdef->alias, am, parent_type); >> } >> } >> >> @@ -6192,53 +6002,12 @@ static void x86_cpu_apic_realize(X86CPU *cpu, Error >> **errp) >> apic_mmio_map_once = true; >> } >> } >> - >> -static void x86_cpu_machine_done(Notifier *n, void *unused) >> -{ >> - X86CPU *cpu = container_of(n, X86CPU, machine_done); >> - MemoryRegion *smram = >> - (MemoryRegion *) object_resolve_path("/machine/smram", NULL); >> - >> - if (smram) { >> - cpu->smram = g_new(MemoryRegion, 1); >> - memory_region_init_alias(cpu->smram, OBJECT(cpu), "smram", >> - smram, 0, 4 * GiB); >> - memory_region_set_enabled(cpu->smram, true); >> - memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, >> cpu->smram, 1); >> - } >> -} >> #else >> static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) >> { >> } >> #endif >> >> -/* Note: Only safe for use on x86(-64) hosts */ >> -static uint32_t x86_host_phys_bits(void) >> -{ >> - uint32_t eax; >> - uint32_t host_phys_bits; >> - >> - host_cpuid(0x80000000, 0, &eax, NULL, NULL, NULL); >> - if (eax >= 0x80000008) { >> - host_cpuid(0x80000008, 0, &eax, NULL, NULL, NULL); >> - /* Note: According to AMD doc 25481 rev 2.34 they have a field >> - * at 23:16 that can specify a maximum physical address bits for >> - * the guest that can override this value; but I've not seen >> - * anything with that set. >> - */ >> - host_phys_bits = eax & 0xff; >> - } else { >> - /* It's an odd 64 bit machine that doesn't have the leaf for >> - * physical address bits; fall back to 36 that's most older >> - * Intel. >> - */ >> - host_phys_bits = 36; >> - } >> - >> - return host_phys_bits; >> -} >> - >> static void x86_cpu_adjust_level(X86CPU *cpu, uint32_t *min, uint32_t value) >> { >> if (*min < value) { >> @@ -6512,7 +6281,7 @@ static void x86_cpu_filter_features(X86CPU *cpu, bool >> verbose) >> } >> } >> >> -static void x86_cpu_realizefn(DeviceState *dev, Error **errp) >> +void x86_cpu_realizefn(DeviceState *dev, Error **errp) >> { >> CPUState *cs = CPU(dev); >> X86CPU *cpu = X86_CPU(dev); >> @@ -6521,27 +6290,15 @@ static void x86_cpu_realizefn(DeviceState *dev, >> Error **errp) >> Error *local_err = NULL; >> static bool ht_warned; >> >> - if (xcc->host_cpuid_required) { >> - if (!accel_uses_host_cpuid()) { >> - g_autofree char *name = x86_cpu_class_get_model_name(xcc); >> - error_setg(&local_err, "CPU model '%s' requires KVM", name); >> - goto out; >> - } >> - } >> + /* >> + * For accelerators that specialize the x86 cpu, >> + * this common code must be called after the accelerator-specific >> realizefn. >> + */ >> >> - if (cpu->max_features && accel_uses_host_cpuid()) { >> - if (enable_cpu_pm) { >> - host_cpuid(5, 0, &cpu->mwait.eax, &cpu->mwait.ebx, >> - &cpu->mwait.ecx, &cpu->mwait.edx); >> - env->features[FEAT_1_ECX] |= CPUID_EXT_MONITOR; >> - if (kvm_enabled() && kvm_has_waitpkg()) { >> - env->features[FEAT_7_0_ECX] |= CPUID_7_0_ECX_WAITPKG; >> - } >> - } >> - if (kvm_enabled() && cpu->ucode_rev == 0) { >> - cpu->ucode_rev = kvm_arch_get_supported_msr_feature(kvm_state, >> - >> MSR_IA32_UCODE_REV); >> - } >> + if (xcc->host_cpuid_required && !accel_uses_host_cpuid()) { >> + g_autofree char *name = x86_cpu_class_get_model_name(xcc); >> + error_setg(&local_err, "CPU model '%s' requires KVM or HVF", name); >> + goto out; >> } >> >> if (cpu->ucode_rev == 0) { >> @@ -6593,39 +6350,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error >> **errp) >> * consumer AMD devices but nothing else. >> */ >> if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { >> - if (accel_uses_host_cpuid()) { >> - uint32_t host_phys_bits = x86_host_phys_bits(); >> - static bool warned; >> - >> - /* Print a warning if the user set it to a value that's not the >> - * host value. >> - */ >> - if (cpu->phys_bits != host_phys_bits && cpu->phys_bits != 0 && >> - !warned) { >> - warn_report("Host physical bits (%u)" >> - " does not match phys-bits property (%u)", >> - host_phys_bits, cpu->phys_bits); >> - warned = true; >> - } >> - >> - if (cpu->host_phys_bits) { >> - /* The user asked for us to use the host physical bits */ >> - cpu->phys_bits = host_phys_bits; >> - if (cpu->host_phys_bits_limit && >> - cpu->phys_bits > cpu->host_phys_bits_limit) { >> - cpu->phys_bits = cpu->host_phys_bits_limit; >> - } >> - } >> - >> - if (cpu->phys_bits && >> - (cpu->phys_bits > TARGET_PHYS_ADDR_SPACE_BITS || >> - cpu->phys_bits < 32)) { >> - error_setg(errp, "phys-bits should be between 32 and %u " >> - " (but is %u)", >> - TARGET_PHYS_ADDR_SPACE_BITS, >> cpu->phys_bits); >> - return; >> - } >> - } else { >> + if (!accel_uses_host_cpuid()) { >> if (cpu->phys_bits && cpu->phys_bits != TCG_PHYS_ADDR_BITS) { >> error_setg(errp, "TCG only supports phys-bits=%u", >> TCG_PHYS_ADDR_BITS); >> @@ -6633,8 +6358,8 @@ static void x86_cpu_realizefn(DeviceState *dev, Error >> **errp) >> } >> } >> /* 0 means it was not explicitly set by the user (or by machine >> - * compat_props or by the host code above). In this case, the >> default >> - * is the value used by TCG (40). >> + * compat_props or by the host code in host-cpu.c). >> + * In this case, the default is the value used by TCG (40). >> */ >> if (cpu->phys_bits == 0) { >> cpu->phys_bits = TCG_PHYS_ADDR_BITS; >> @@ -6704,32 +6429,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error >> **errp) >> >> mce_init(cpu); >> >> -#ifndef CONFIG_USER_ONLY >> - if (tcg_enabled()) { >> - cpu->cpu_as_mem = g_new(MemoryRegion, 1); >> - cpu->cpu_as_root = g_new(MemoryRegion, 1); >> - >> - /* Outer container... */ >> - memory_region_init(cpu->cpu_as_root, OBJECT(cpu), "memory", ~0ull); >> - memory_region_set_enabled(cpu->cpu_as_root, true); >> - >> - /* ... with two regions inside: normal system memory with low >> - * priority, and... >> - */ >> - memory_region_init_alias(cpu->cpu_as_mem, OBJECT(cpu), "memory", >> - get_system_memory(), 0, ~0ull); >> - memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, >> cpu->cpu_as_mem, 0); >> - memory_region_set_enabled(cpu->cpu_as_mem, true); >> - >> - cs->num_ases = 2; >> - cpu_address_space_init(cs, 0, "cpu-memory", cs->memory); >> - cpu_address_space_init(cs, 1, "cpu-smm", cpu->cpu_as_root); >> - >> - /* ... SMRAM with higher priority, linked from /machine/smram. */ >> - cpu->machine_done.notify = x86_cpu_machine_done; >> - qemu_add_machine_init_done_notifier(&cpu->machine_done); >> - } >> -#endif >> + /* XXX this is the place for tcg region initialization XXX */ >> >> qemu_init_vcpu(cs); >> >> @@ -6932,7 +6632,7 @@ static void x86_cpu_get_crash_info_qom(Object *obj, >> Visitor *v, >> } >> #endif /* !CONFIG_USER_ONLY */ >> >> -static void x86_cpu_initfn(Object *obj) >> +void x86_cpu_initfn(Object *obj) >> { >> X86CPU *cpu = X86_CPU(obj); >> X86CPUClass *xcc = X86_CPU_GET_CLASS(obj); >> @@ -7230,7 +6930,7 @@ static Property x86_cpu_properties[] = { >> DEFINE_PROP_END_OF_LIST() >> }; >> >> -static void x86_cpu_common_class_init(ObjectClass *oc, void *data) >> +void x86_cpu_common_class_init(ObjectClass *oc, void *data) >> { >> X86CPUClass *xcc = X86_CPU_CLASS(oc); >> CPUClass *cc = CPU_CLASS(oc); >> @@ -7249,10 +6949,6 @@ static void x86_cpu_common_class_init(ObjectClass >> *oc, void *data) >> cc->parse_features = x86_cpu_parse_featurestr; >> cc->has_work = x86_cpu_has_work; >> >> -#ifdef CONFIG_TCG >> - tcg_cpu_common_class_init(cc); >> -#endif /* CONFIG_TCG */ >> - >> cc->dump_state = x86_cpu_dump_state; >> cc->set_pc = x86_cpu_set_pc; >> cc->gdb_read_register = x86_cpu_gdb_read_register; >> @@ -7341,25 +7037,49 @@ static void x86_cpu_base_class_init(ObjectClass *oc, >> void *data) >> xcc->ordering = 8; >> } >> >> -static const TypeInfo x86_base_cpu_type_info = { >> - .name = X86_CPU_TYPE_NAME("base"), >> - .parent = TYPE_X86_CPU, >> - .class_init = x86_cpu_base_class_init, >> +static TypeInfo x86_base_cpu_type_info = { >> + .name = X86_CPU_TYPE_NAME("base"), >> + .parent = NULL, /* set by x86_cpu_register_cpu_models */ >> + >> + .class_init = x86_cpu_base_class_init, >> }; >> >> -static void x86_cpu_register_types(void) >> +/* >> + * x86 cpu types are only registered once accelerator is determined, >> + * passing the parent cpu type as an argument here. >> + */ >> +void x86_cpu_register_cpu_models(const char *parent_type) >> { >> int i; >> >> - type_register_static(&x86_cpu_type_info); >> for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) { >> - x86_register_cpudef_types(&builtin_x86_defs[i]); >> + x86_register_cpudef(&builtin_x86_defs[i], parent_type); >> + } >> + max_x86_cpu_type_info.parent = parent_type; >> + type_register(&max_x86_cpu_type_info); >> + >> + x86_base_cpu_type_info.parent = parent_type; >> + type_register(&x86_base_cpu_type_info); >> +} >> + >> +static void x86_cpu_register_base_type(void) >> +{ >> + type_register_static(&x86_cpu_type_info); >> +} >> + >> +type_init(x86_cpu_register_base_type); >> + >> +/* >> + * generic initializer if no other specialization. >> + */ >> +static void x86_cpu_type_init(void) >> +{ >> + /* >> + * I would like something better than this check. >> + */ >> + if (!tcg_enabled() && !kvm_enabled() && !hvf_enabled()) { >> + x86_cpu_register_cpu_models(TYPE_X86_CPU); >> } >> - type_register_static(&max_x86_cpu_type_info); >> - type_register_static(&x86_base_cpu_type_info); >> -#if defined(CONFIG_KVM) || defined(CONFIG_HVF) >> - type_register_static(&host_x86_cpu_type_info); >> -#endif >> } >> >> -type_init(x86_cpu_register_types) >> +accel_cpu_init(x86_cpu_type_init); >> diff --git a/target/i386/cpu.h b/target/i386/cpu.h >> index f1bce16b53..9232672eb2 100644 >> --- a/target/i386/cpu.h >> +++ b/target/i386/cpu.h >> @@ -1905,13 +1905,26 @@ int cpu_x86_signal_handler(int host_signum, void >> *pinfo, >> void *puc); >> >> /* cpu.c */ >> +void x86_cpu_register_cpu_models(const char *parent_type); >> + >> +void x86_cpu_initfn(Object *obj); >> +void max_x86_cpu_initfn(Object *obj); >> +void x86_cpu_realizefn(DeviceState *dev, Error **errp); >> +void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, >> + uint32_t vendor2, uint32_t vendor3); >> +void x86_cpu_common_class_init(ObjectClass *oc, void *data); >> +typedef struct PropValue { >> + const char *prop, *value; >> +} PropValue; >> +void x86_cpu_apply_props(X86CPU *cpu, PropValue *props); >> + >> +/* cpu.c other functions (cpuid) */ >> void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, >> uint32_t *eax, uint32_t *ebx, >> uint32_t *ecx, uint32_t *edx); >> void cpu_clear_apic_feature(CPUX86State *env); >> void host_cpuid(uint32_t function, uint32_t count, >> uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); >> -void host_vendor_fms(char *vendor, int *family, int *model, int *stepping); >> >> /* helper.c */ >> void x86_cpu_set_a20(X86CPU *cpu, int a20_state); >> @@ -2111,17 +2124,6 @@ void cpu_report_tpr_access(CPUX86State *env, >> TPRAccess access); >> void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip, >> TPRAccess access); >> >> - >> -/* Change the value of a KVM-specific default >> - * >> - * If value is NULL, no default will be set and the original >> - * value from the CPU model table will be kept. >> - * >> - * It is valid to call this function only for properties that >> - * are already present in the kvm_default_props table. >> - */ >> -void x86_cpu_change_kvm_default(const char *prop, const char *value); >> - >> /* Special values for X86CPUVersion: */ >> >> /* Resolve to latest CPU version */ >> diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c >> new file mode 100644 >> index 0000000000..65a0119f4b >> --- /dev/null >> +++ b/target/i386/host-cpu.c >> @@ -0,0 +1,201 @@ >> +/* >> + * x86 host CPU functions, and "host" cpu type initialization >> + * >> + * Copyright 2020 SUSE LLC >> + * >> + * 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 "cpu.h" >> +#include "host-cpu.h" >> +#include "qapi/error.h" >> +#include "sysemu/sysemu.h" >> +#include "hw/boards.h" >> + >> +/* Note: Only safe for use on x86(-64) hosts */ >> +static uint32_t host_cpu_phys_bits(void) >> +{ >> + uint32_t eax; >> + uint32_t host_phys_bits; >> + >> + host_cpuid(0x80000000, 0, &eax, NULL, NULL, NULL); >> + if (eax >= 0x80000008) { >> + host_cpuid(0x80000008, 0, &eax, NULL, NULL, NULL); >> + /* >> + * Note: According to AMD doc 25481 rev 2.34 they have a field >> + * at 23:16 that can specify a maximum physical address bits for >> + * the guest that can override this value; but I've not seen >> + * anything with that set. >> + */ >> + host_phys_bits = eax & 0xff; >> + } else { >> + /* >> + * It's an odd 64 bit machine that doesn't have the leaf for >> + * physical address bits; fall back to 36 that's most older >> + * Intel. >> + */ >> + host_phys_bits = 36; >> + } >> + >> + return host_phys_bits; >> +} >> + >> +static void host_cpu_enable_cpu_pm(X86CPU *cpu) >> +{ >> + CPUX86State *env = &cpu->env; >> + >> + host_cpuid(5, 0, &cpu->mwait.eax, &cpu->mwait.ebx, >> + &cpu->mwait.ecx, &cpu->mwait.edx); >> + env->features[FEAT_1_ECX] |= CPUID_EXT_MONITOR; >> +} >> + >> +static uint32_t host_cpu_adjust_phys_bits(X86CPU *cpu, Error **errp) >> +{ >> + uint32_t host_phys_bits = host_cpu_phys_bits(); >> + uint32_t phys_bits = cpu->phys_bits; >> + static bool warned; >> + >> + /* >> + * Print a warning if the user set it to a value that's not the >> + * host value. >> + */ >> + if (phys_bits != host_phys_bits && phys_bits != 0 && >> + !warned) { >> + warn_report("Host physical bits (%u)" >> + " does not match phys-bits property (%u)", >> + host_phys_bits, phys_bits); >> + warned = true; >> + } >> + >> + if (cpu->host_phys_bits) { >> + /* The user asked for us to use the host physical bits */ >> + phys_bits = host_phys_bits; >> + if (cpu->host_phys_bits_limit && >> + phys_bits > cpu->host_phys_bits_limit) { >> + phys_bits = cpu->host_phys_bits_limit; >> + } >> + } >> + >> + if (phys_bits && >> + (phys_bits > TARGET_PHYS_ADDR_SPACE_BITS || >> + phys_bits < 32)) { >> + error_setg(errp, "phys-bits should be between 32 and %u " >> + " (but is %u)", >> + TARGET_PHYS_ADDR_SPACE_BITS, phys_bits); >> + } >> + >> + return phys_bits; >> +} >> + >> +void host_cpu_realizefn(DeviceState *dev, Error **errp) >> +{ >> + X86CPU *cpu = X86_CPU(dev); >> + CPUX86State *env = &cpu->env; >> + >> + if (cpu->max_features && enable_cpu_pm) { >> + host_cpu_enable_cpu_pm(cpu); >> + } >> + if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { >> + cpu->phys_bits = host_cpu_adjust_phys_bits(cpu, errp); >> + } >> + x86_cpu_realizefn(dev, errp); >> +} >> + >> +#define CPUID_MODEL_ID_SZ 48 >> +/** >> + * cpu_x86_fill_model_id: >> + * Get CPUID model ID string from host CPU. >> + * >> + * @str should have at least CPUID_MODEL_ID_SZ bytes >> + * >> + * The function does NOT add a null terminator to the string >> + * automatically. >> + */ >> +static int host_cpu_fill_model_id(char *str) >> +{ >> + uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; >> + int i; >> + >> + for (i = 0; i < 3; i++) { >> + host_cpuid(0x80000002 + i, 0, &eax, &ebx, &ecx, &edx); >> + memcpy(str + i * 16 + 0, &eax, 4); >> + memcpy(str + i * 16 + 4, &ebx, 4); >> + memcpy(str + i * 16 + 8, &ecx, 4); >> + memcpy(str + i * 16 + 12, &edx, 4); >> + } >> + return 0; >> +} >> + >> +void host_cpu_vendor_fms(char *vendor, int *family, int *model, int >> *stepping) >> +{ >> + uint32_t eax, ebx, ecx, edx; >> + >> + host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx); >> + x86_cpu_vendor_words2str(vendor, ebx, edx, ecx); >> + >> + host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx); >> + if (family) { >> + *family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF); >> + } >> + if (model) { >> + *model = ((eax >> 4) & 0x0F) | ((eax & 0xF0000) >> 12); >> + } >> + if (stepping) { >> + *stepping = eax & 0x0F; >> + } >> +} >> + >> +void host_cpu_initfn(Object *obj) >> +{ >> + X86CPU *cpu = X86_CPU(obj); >> + uint32_t ebx = 0, ecx = 0, edx = 0; >> + char vendor[CPUID_VENDOR_SZ + 1]; >> + >> + host_cpuid(0, 0, NULL, &ebx, &ecx, &edx); >> + x86_cpu_vendor_words2str(vendor, ebx, edx, ecx); >> + >> + object_property_set_str(OBJECT(cpu), "vendor", vendor, &error_abort); >> +} >> + >> +void host_cpu_max_initfn(X86CPU *cpu) >> +{ >> + char vendor[CPUID_VENDOR_SZ + 1] = { 0 }; >> + char model_id[CPUID_MODEL_ID_SZ + 1] = { 0 }; >> + int family, model, stepping; >> + >> + host_cpu_vendor_fms(vendor, &family, &model, &stepping); >> + host_cpu_fill_model_id(model_id); >> + >> + object_property_set_str(OBJECT(cpu), "vendor", vendor, &error_abort); >> + object_property_set_int(OBJECT(cpu), "family", family, &error_abort); >> + object_property_set_int(OBJECT(cpu), "model", model, &error_abort); >> + object_property_set_int(OBJECT(cpu), "stepping", stepping, >> + &error_abort); >> + object_property_set_str(OBJECT(cpu), "model-id", model_id, >> + &error_abort); >> +} >> + >> +static void host_cpu_class_init(ObjectClass *oc, void *data) >> +{ >> + X86CPUClass *xcc = X86_CPU_CLASS(oc); >> + >> + xcc->host_cpuid_required = true; >> + xcc->ordering = 8; >> + xcc->model_description = >> + g_strdup_printf("%s processor with all supported host features ", >> + ACCEL_GET_CLASS(current_accel())->name); >> +} >> + >> +static const TypeInfo host_cpu_type_info = { >> + .name = X86_CPU_TYPE_NAME("host"), >> + .parent = X86_CPU_TYPE_NAME("max"), >> + >> + .class_init = host_cpu_class_init, >> +}; >> + >> +void host_cpu_type_init(void) >> +{ >> + type_register(&host_cpu_type_info); >> +} >> diff --git a/target/i386/host-cpu.h b/target/i386/host-cpu.h >> new file mode 100644 >> index 0000000000..e9d4726833 >> --- /dev/null >> +++ b/target/i386/host-cpu.h >> @@ -0,0 +1,21 @@ >> +/* >> + * x86 host CPU type initialization >> + * >> + * Copyright 2020 SUSE LLC >> + * >> + * This work is licensed under the terms of the GNU GPL, version 2 or later. >> + * See the COPYING file in the top-level directory. >> + */ >> + >> +#ifndef HOST_CPU_TYPE_H >> +#define HOST_CPU_TYPE_H >> + >> +void host_cpu_type_init(void); >> + >> +void host_cpu_initfn(Object *obj); >> +void host_cpu_realizefn(DeviceState *dev, Error **errp); >> +void host_cpu_max_initfn(X86CPU *cpu); >> + >> +void host_cpu_vendor_fms(char *vendor, int *family, int *model, int >> *stepping); >> + >> +#endif /* HOST_CPU_TYPE_H */ >> diff --git a/target/i386/meson.build b/target/i386/meson.build >> index 50c8fba6cb..c84b0d6965 100644 >> --- a/target/i386/meson.build >> +++ b/target/i386/meson.build >> @@ -6,8 +6,12 @@ i386_ss.add(files( >> 'xsave_helper.c', >> 'cpu-dump.c', >> )) >> -i386_ss.add(when: 'CONFIG_TCG', if_true: files('tcg-cpu.c')) >> -i386_ss.add(when: 'CONFIG_SEV', if_true: files('sev.c'), if_false: >> files('sev-stub.c')) >> + >> +i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c', 'sev.c'), >> if_false: files('sev-stub.c')) >> + >> +# x86 cpu type >> +i386_ss.add(when: 'CONFIG_KVM', if_true: files('host-cpu.c')) >> +i386_ss.add(when: 'CONFIG_HVF', if_true: files('host-cpu.c')) >> >> i386_softmmu_ss = ss.source_set() >> i386_softmmu_ss.add(files( >> diff --git a/target/i386/tcg-cpu.c b/target/i386/tcg-cpu.c >> deleted file mode 100644 >> index 628dd29fe7..0000000000 >> --- a/target/i386/tcg-cpu.c >> +++ /dev/null >> @@ -1,71 +0,0 @@ >> -/* >> - * i386 TCG cpu class initialization >> - * >> - * Copyright (c) 2003 Fabrice Bellard >> - * >> - * This library is free software; you can redistribute it and/or >> - * modify it under the terms of the GNU Lesser General Public >> - * License as published by the Free Software Foundation; either >> - * version 2 of the License, or (at your option) any later version. >> - * >> - * This library is distributed in the hope that it will be useful, >> - * but WITHOUT ANY WARRANTY; without even the implied warranty of >> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> - * Lesser General Public License for more details. >> - * >> - * You should have received a copy of the GNU Lesser General Public >> - * License along with this library; if not, see >> <http://www.gnu.org/licenses/>. >> - */ >> - >> -#include "qemu/osdep.h" >> -#include "cpu.h" >> -#include "tcg-cpu.h" >> -#include "exec/exec-all.h" >> -#include "sysemu/runstate.h" >> -#include "helper-tcg.h" >> - >> -#if !defined(CONFIG_USER_ONLY) >> -#include "hw/i386/apic.h" >> -#endif >> - >> -/* Frob eflags into and out of the CPU temporary format. */ >> - >> -static void x86_cpu_exec_enter(CPUState *cs) >> -{ >> - X86CPU *cpu = X86_CPU(cs); >> - CPUX86State *env = &cpu->env; >> - >> - CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); >> - env->df = 1 - (2 * ((env->eflags >> 10) & 1)); >> - CC_OP = CC_OP_EFLAGS; >> - env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); >> -} >> - >> -static void x86_cpu_exec_exit(CPUState *cs) >> -{ >> - X86CPU *cpu = X86_CPU(cs); >> - CPUX86State *env = &cpu->env; >> - >> - env->eflags = cpu_compute_eflags(env); >> -} >> - >> -static void x86_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) >> -{ >> - X86CPU *cpu = X86_CPU(cs); >> - >> - cpu->env.eip = tb->pc - tb->cs_base; >> -} >> - >> -void tcg_cpu_common_class_init(CPUClass *cc) >> -{ >> - cc->do_interrupt = x86_cpu_do_interrupt; >> - cc->cpu_exec_interrupt = x86_cpu_exec_interrupt; >> - cc->synchronize_from_tb = x86_cpu_synchronize_from_tb; >> - cc->cpu_exec_enter = x86_cpu_exec_enter; >> - cc->cpu_exec_exit = x86_cpu_exec_exit; >> - cc->tcg_initialize = tcg_x86_init; >> - cc->tlb_fill = x86_cpu_tlb_fill; >> -#ifndef CONFIG_USER_ONLY >> - cc->debug_excp_handler = breakpoint_handler; >> -#endif >> -} >> diff --git a/target/i386/tcg-cpu.h b/target/i386/tcg-cpu.h >> deleted file mode 100644 >> index 81f02e562e..0000000000 >> --- a/target/i386/tcg-cpu.h >> +++ /dev/null >> @@ -1,15 +0,0 @@ >> -/* >> - * i386 TCG CPU class initialization >> - * >> - * Copyright 2020 SUSE LLC >> - * >> - * This work is licensed under the terms of the GNU GPL, version 2 or later. >> - * See the COPYING file in the top-level directory. >> - */ >> - >> -#ifndef TCG_CPU_H >> -#define TCG_CPU_H >> - >> -void tcg_cpu_common_class_init(CPUClass *cc); >> - >> -#endif /* TCG_CPU_H */ >> -- >> 2.26.2 >> >