Negotiate KVM_CAP_LAPIC2 during vCPU pre-creation. Enable
KVM_LAPIC2_DEFAULT for the 4KB APIC page. If the CPU has
ExtApicSpace (arch_has_extapic()), also enable KVM_LAPIC2_AMD_DEFAULT
and use the intersection of what host and guest support.
Use a VM-wide has_lapic2 flag so the capability is enabled once on the
first vCPU and reused for the rest. When extended APIC is supported on
both host and guest, set has_extapic for use when syncing extended APIC
registers with KVM. Allocate extended LVT state in
kvm_initialize_extlvt() during pre-creation and free it in
kvm_uninitialize_extlvt() on vCPU destroy.
Suggested-by: Naveen N Rao (AMD) <naveen@kernel.org>
Signed-off-by: Manali Shukla <manali.shukla@amd.com>
---
target/i386/kvm/kvm.c | 61 +++++++++++++++++++++++++++++++++++++-
target/i386/kvm/kvm_i386.h | 2 ++
2 files changed, 62 insertions(+), 1 deletion(-)
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index ea22aa7180..c9f4cb6430 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -177,6 +177,8 @@ static int has_exception_payload;
static int has_triple_fault_event;
static bool has_msr_mcg_ext_ctl;
+static bool has_lapic2;
+static bool has_extapic;
static struct kvm_cpuid2 *cpuid_cache;
static struct kvm_cpuid2 *hv_cpuid_cache;
@@ -2064,13 +2066,69 @@ full:
abort();
}
+bool kvm_has_lapic2(void)
+{
+ return has_lapic2;
+}
+
+bool kvm_has_extapic(void)
+{
+ return has_extapic;
+}
+
+static int kvm_enable_extapic(X86CPU *cpu)
+{
+ KVMState *s = KVM_STATE(current_accel());
+ uint64_t kvm_cap, vm_cap, final_cap;
+ uint8_t nr_extlvt = 0;
+ int ret;
+
+ if (!s) {
+ error_report("KVM accelerator is not available");
+ return -ENODEV;
+ }
+
+ if (!has_lapic2) {
+ kvm_cap = kvm_check_extension(s, KVM_CAP_LAPIC2);
+ if (!kvm_cap) {
+ return 0;
+ }
+
+ vm_cap = KVM_LAPIC2_DEFAULT;
+ if (arch_has_extapic(cpu)) {
+ vm_cap |= KVM_LAPIC2_AMD_DEFAULT;
+ }
+
+ final_cap = kvm_cap & vm_cap;
+ ret = kvm_vm_enable_cap(s, KVM_CAP_LAPIC2, 0, final_cap);
+
+ if (ret < 0) {
+ error_report("kvm: Failed to enable EXTAPIC");
+ return -ENOTSUP;
+ }
+
+ has_lapic2 = true;
+ if (final_cap & KVM_LAPIC2_AMD_DEFAULT) {
+ nr_extlvt = KVM_X86_NR_EXTLVT_DEFAULT;
+ has_extapic = true;
+ }
+ }
+
+ if (nr_extlvt > 0) {
+ kvm_initialize_extlvt(cpu, nr_extlvt);
+ }
+ return 0;
+}
+
int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp)
{
if (is_tdx_vm()) {
return tdx_pre_create_vcpu(cpu, errp);
}
- return 0;
+ X86CPU *cs = X86_CPU(cpu);
+
+ return kvm_enable_extapic(cs);
}
int kvm_arch_init_vcpu(CPUState *cs)
@@ -2399,6 +2457,7 @@ int kvm_arch_destroy_vcpu(CPUState *cs)
g_free(env->nested_state);
env->nested_state = NULL;
+ kvm_uninitialize_extlvt(cpu);
qemu_del_vm_change_state_handler(cpu->vmsentry);
return 0;
diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h
index 338433eb52..b28fed69d8 100644
--- a/target/i386/kvm/kvm_i386.h
+++ b/target/i386/kvm/kvm_i386.h
@@ -25,6 +25,8 @@
(kvm_irqchip_in_kernel() && !kvm_irqchip_is_split())
bool kvm_has_smm(void);
+bool kvm_has_extapic(void);
+bool kvm_has_lapic2(void);
bool kvm_enable_x2apic(void);
bool kvm_hv_vpindex_settable(void);
bool kvm_enable_hypercall(uint64_t enable_mask);
--
2.43.0