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) Signed-off-by: Manali Shukla --- 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