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) <[email protected]>
Signed-off-by: Manali Shukla <[email protected]>
---
 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


Reply via email to