Claudio Fontana | f5cc5a5 | 2021-03-22 14:27:40 +0100 | [diff] [blame] | 1 | /* |
| 2 | * x86 KVM CPU type initialization |
| 3 | * |
| 4 | * Copyright 2021 SUSE LLC |
| 5 | * |
| 6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| 7 | * See the COPYING file in the top-level directory. |
| 8 | */ |
| 9 | |
| 10 | #include "qemu/osdep.h" |
| 11 | #include "cpu.h" |
| 12 | #include "host-cpu.h" |
| 13 | #include "kvm-cpu.h" |
| 14 | #include "qapi/error.h" |
| 15 | #include "sysemu/sysemu.h" |
| 16 | #include "hw/boards.h" |
| 17 | |
| 18 | #include "kvm_i386.h" |
| 19 | #include "hw/core/accel-cpu.h" |
| 20 | |
Claudio Fontana | 9ea057d | 2021-03-22 14:27:44 +0100 | [diff] [blame] | 21 | static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) |
Claudio Fontana | f5cc5a5 | 2021-03-22 14:27:40 +0100 | [diff] [blame] | 22 | { |
| 23 | X86CPU *cpu = X86_CPU(cs); |
| 24 | CPUX86State *env = &cpu->env; |
| 25 | |
| 26 | /* |
| 27 | * The realize order is important, since x86_cpu_realize() checks if |
| 28 | * nothing else has been set by the user (or by accelerators) in |
Claudio Fontana | 662175b | 2021-06-03 14:30:00 +0200 | [diff] [blame] | 29 | * cpu->ucode_rev and cpu->phys_bits, and updates the CPUID results in |
| 30 | * mwait.ecx. |
| 31 | * This accel realization code also assumes cpu features are already expanded. |
Claudio Fontana | f5cc5a5 | 2021-03-22 14:27:40 +0100 | [diff] [blame] | 32 | * |
| 33 | * realize order: |
Claudio Fontana | 662175b | 2021-06-03 14:30:00 +0200 | [diff] [blame] | 34 | * |
| 35 | * x86_cpu_realize(): |
| 36 | * -> x86_cpu_expand_features() |
| 37 | * -> cpu_exec_realizefn(): |
| 38 | * -> accel_cpu_realizefn() |
| 39 | * kvm_cpu_realizefn() -> host_cpu_realizefn() |
| 40 | * -> check/update ucode_rev, phys_bits, mwait |
Claudio Fontana | f5cc5a5 | 2021-03-22 14:27:40 +0100 | [diff] [blame] | 41 | */ |
| 42 | if (cpu->max_features) { |
| 43 | if (enable_cpu_pm && kvm_has_waitpkg()) { |
| 44 | env->features[FEAT_7_0_ECX] |= CPUID_7_0_ECX_WAITPKG; |
| 45 | } |
| 46 | if (cpu->ucode_rev == 0) { |
| 47 | cpu->ucode_rev = |
| 48 | kvm_arch_get_supported_msr_feature(kvm_state, |
| 49 | MSR_IA32_UCODE_REV); |
| 50 | } |
| 51 | } |
Claudio Fontana | 9ea057d | 2021-03-22 14:27:44 +0100 | [diff] [blame] | 52 | return host_cpu_realizefn(cs, errp); |
Claudio Fontana | f5cc5a5 | 2021-03-22 14:27:40 +0100 | [diff] [blame] | 53 | } |
| 54 | |
Claudio Fontana | f5cc5a5 | 2021-03-22 14:27:40 +0100 | [diff] [blame] | 55 | static bool lmce_supported(void) |
| 56 | { |
| 57 | uint64_t mce_cap = 0; |
| 58 | |
| 59 | if (kvm_ioctl(kvm_state, KVM_X86_GET_MCE_CAP_SUPPORTED, &mce_cap) < 0) { |
| 60 | return false; |
| 61 | } |
| 62 | return !!(mce_cap & MCG_LMCE_P); |
| 63 | } |
| 64 | |
| 65 | static void kvm_cpu_max_instance_init(X86CPU *cpu) |
| 66 | { |
| 67 | CPUX86State *env = &cpu->env; |
| 68 | KVMState *s = kvm_state; |
| 69 | |
| 70 | host_cpu_max_instance_init(cpu); |
| 71 | |
| 72 | if (lmce_supported()) { |
| 73 | object_property_set_bool(OBJECT(cpu), "lmce", true, &error_abort); |
| 74 | } |
| 75 | |
| 76 | env->cpuid_min_level = |
| 77 | kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX); |
| 78 | env->cpuid_min_xlevel = |
| 79 | kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX); |
| 80 | env->cpuid_min_xlevel2 = |
| 81 | kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX); |
| 82 | } |
| 83 | |
David Edmondson | fea4500 | 2021-07-05 11:46:31 +0100 | [diff] [blame] | 84 | static void kvm_cpu_xsave_init(void) |
| 85 | { |
| 86 | static bool first = true; |
Yang Zhong | 19db68c | 2022-02-16 22:04:29 -0800 | [diff] [blame] | 87 | uint32_t eax, ebx, ecx, edx; |
David Edmondson | fea4500 | 2021-07-05 11:46:31 +0100 | [diff] [blame] | 88 | int i; |
| 89 | |
| 90 | if (!first) { |
| 91 | return; |
| 92 | } |
| 93 | first = false; |
| 94 | |
| 95 | /* x87 and SSE states are in the legacy region of the XSAVE area. */ |
| 96 | x86_ext_save_areas[XSTATE_FP_BIT].offset = 0; |
| 97 | x86_ext_save_areas[XSTATE_SSE_BIT].offset = 0; |
| 98 | |
| 99 | for (i = XSTATE_SSE_BIT + 1; i < XSAVE_STATE_AREA_COUNT; i++) { |
| 100 | ExtSaveArea *esa = &x86_ext_save_areas[i]; |
| 101 | |
Paolo Bonzini | 58f7db2 | 2022-03-23 12:33:25 +0100 | [diff] [blame] | 102 | if (!esa->size) { |
| 103 | continue; |
| 104 | } |
| 105 | if ((x86_cpu_get_supported_feature_word(esa->feature, false) & esa->bits) |
| 106 | != esa->bits) { |
| 107 | continue; |
| 108 | } |
| 109 | host_cpuid(0xd, i, &eax, &ebx, &ecx, &edx); |
| 110 | if (eax != 0) { |
| 111 | assert(esa->size == eax); |
| 112 | esa->offset = ebx; |
| 113 | esa->ecx = ecx; |
David Edmondson | fea4500 | 2021-07-05 11:46:31 +0100 | [diff] [blame] | 114 | } |
| 115 | } |
| 116 | } |
| 117 | |
Claudio Fontana | 5b8978d | 2021-07-23 13:29:21 +0200 | [diff] [blame] | 118 | /* |
| 119 | * KVM-specific features that are automatically added/removed |
| 120 | * from cpudef models when KVM is enabled. |
| 121 | * Only for builtin_x86_defs models initialized with x86_register_cpudef_types. |
| 122 | * |
| 123 | * NOTE: features can be enabled by default only if they were |
| 124 | * already available in the oldest kernel version supported |
| 125 | * by the KVM accelerator (see "OS requirements" section at |
| 126 | * docs/system/target-i386.rst) |
| 127 | */ |
| 128 | static PropValue kvm_default_props[] = { |
| 129 | { "kvmclock", "on" }, |
| 130 | { "kvm-nopiodelay", "on" }, |
| 131 | { "kvm-asyncpf", "on" }, |
| 132 | { "kvm-steal-time", "on" }, |
| 133 | { "kvm-pv-eoi", "on" }, |
| 134 | { "kvmclock-stable-bit", "on" }, |
| 135 | { "x2apic", "on" }, |
| 136 | { "kvm-msi-ext-dest-id", "off" }, |
| 137 | { "acpi", "off" }, |
| 138 | { "monitor", "off" }, |
| 139 | { "svm", "off" }, |
| 140 | { NULL, NULL }, |
| 141 | }; |
| 142 | |
| 143 | /* |
| 144 | * Only for builtin_x86_defs models initialized with x86_register_cpudef_types. |
| 145 | */ |
| 146 | void x86_cpu_change_kvm_default(const char *prop, const char *value) |
| 147 | { |
| 148 | PropValue *pv; |
| 149 | for (pv = kvm_default_props; pv->prop; pv++) { |
| 150 | if (!strcmp(pv->prop, prop)) { |
| 151 | pv->value = value; |
| 152 | break; |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | /* |
| 157 | * It is valid to call this function only for properties that |
| 158 | * are already present in the kvm_default_props table. |
| 159 | */ |
| 160 | assert(pv->prop); |
| 161 | } |
| 162 | |
Claudio Fontana | f5cc5a5 | 2021-03-22 14:27:40 +0100 | [diff] [blame] | 163 | static void kvm_cpu_instance_init(CPUState *cs) |
| 164 | { |
| 165 | X86CPU *cpu = X86_CPU(cs); |
Claudio Fontana | 5b8978d | 2021-07-23 13:29:21 +0200 | [diff] [blame] | 166 | X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); |
Claudio Fontana | f5cc5a5 | 2021-03-22 14:27:40 +0100 | [diff] [blame] | 167 | |
| 168 | host_cpu_instance_init(cpu); |
| 169 | |
Claudio Fontana | 5b8978d | 2021-07-23 13:29:21 +0200 | [diff] [blame] | 170 | if (xcc->model) { |
| 171 | /* only applies to builtin_x86_defs cpus */ |
| 172 | if (!kvm_irqchip_in_kernel()) { |
| 173 | x86_cpu_change_kvm_default("x2apic", "off"); |
David Woodhouse | dc89f32 | 2022-03-14 14:25:41 +0000 | [diff] [blame] | 174 | } else if (kvm_irqchip_is_split()) { |
Claudio Fontana | 5b8978d | 2021-07-23 13:29:21 +0200 | [diff] [blame] | 175 | x86_cpu_change_kvm_default("kvm-msi-ext-dest-id", "on"); |
| 176 | } |
| 177 | |
| 178 | /* Special cases not set in the X86CPUDefinition structs: */ |
| 179 | x86_cpu_apply_props(cpu, kvm_default_props); |
Claudio Fontana | f5cc5a5 | 2021-03-22 14:27:40 +0100 | [diff] [blame] | 180 | } |
| 181 | |
Claudio Fontana | f5cc5a5 | 2021-03-22 14:27:40 +0100 | [diff] [blame] | 182 | if (cpu->max_features) { |
| 183 | kvm_cpu_max_instance_init(cpu); |
| 184 | } |
David Edmondson | fea4500 | 2021-07-05 11:46:31 +0100 | [diff] [blame] | 185 | |
| 186 | kvm_cpu_xsave_init(); |
Claudio Fontana | f5cc5a5 | 2021-03-22 14:27:40 +0100 | [diff] [blame] | 187 | } |
| 188 | |
| 189 | static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data) |
| 190 | { |
| 191 | AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); |
| 192 | |
| 193 | acc->cpu_realizefn = kvm_cpu_realizefn; |
| 194 | acc->cpu_instance_init = kvm_cpu_instance_init; |
| 195 | } |
| 196 | static const TypeInfo kvm_cpu_accel_type_info = { |
| 197 | .name = ACCEL_CPU_NAME("kvm"), |
| 198 | |
| 199 | .parent = TYPE_ACCEL_CPU, |
| 200 | .class_init = kvm_cpu_accel_class_init, |
| 201 | .abstract = true, |
| 202 | }; |
| 203 | static void kvm_cpu_accel_register_types(void) |
| 204 | { |
| 205 | type_register_static(&kvm_cpu_accel_type_info); |
| 206 | } |
| 207 | type_init(kvm_cpu_accel_register_types); |