Add support for KVM_GET_LAPIC2 and KVM_SET_LAPIC2 ioctls to synchronize extended APIC register state between QEMU and KVM. The extended ioctls operate on a 4KB APIC page (struct kvm_lapic_state2) instead of the legacy 1KB. (struct kvm_lapic_state).
Use the extended ioctls when KVM_CAP_LAPIC2 is enabled (has_lapic2), otherwise fall back to the legacy KVM_GET/SET_LAPIC ioctls to maintain compatibility with older KVM versions or when extended APIC is not negotiated. When extended APIC is enabled (has_extapic) for AMD processors, synchronize the extended registers: - APIC_EFEAT (offset 0x400): Extended Features register - APIC_ECTRL (offset 0x410): Extended Control register - APIC_EILVTn (offset 0x500+): Extended interrupt LVT entries Currently on 4 extended interrupt LVT entries are supported but future processors may support more. Add kvm_apic_put2() and kvm_get_apic2() is added to mirror kvm_apic_put() and kvm_get_apic() for the extended ioctl path. Route kvm_apic_post_load() , kvm_apic_reset(), kvm_arch_get_registers() through the appropriate put/get function based on has_lapic2. Signed-off-by: Manali Shukla <[email protected]> --- hw/i386/kvm/apic.c | 55 ++++++++++++++++++++++++++++++++++++++++--- target/i386/kvm/kvm.c | 24 ++++++++++++++++++- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c index 7bec7909e9..f91af66116 100644 --- a/hw/i386/kvm/apic.c +++ b/hw/i386/kvm/apic.c @@ -33,7 +33,11 @@ static void kvm_put_apic_state(APICCommonState *s, void *regs) { int i; - memset(regs, 0, KVM_APIC_REG_SIZE); + if (kvm_has_lapic2()) { + memset(regs, 0, KVM_APIC_EXT_REG_SIZE); + } else { + memset(regs, 0, KVM_APIC_REG_SIZE); + } if (kvm_has_x2apic_api() && s->apicbase & MSR_IA32_APICBASE_EXTD) { kvm_apic_set_reg(regs, 0x2, s->initial_apic_id); @@ -58,6 +62,13 @@ static void kvm_put_apic_state(APICCommonState *s, void *regs) kvm_apic_set_reg(regs, 0x38, s->initial_count); kvm_apic_set_reg(regs, 0x3e, s->divide_conf); + if (kvm_has_extapic()) { + kvm_apic_set_reg(regs, 0x40, s->efeat); + kvm_apic_set_reg(regs, 0x41, s->ectrl); + for (i = 0; i < s->nr_extlvt; i++) { + kvm_apic_set_reg(regs, 0x50 + i, s->extlvt[i]); + } + } } void kvm_get_apic_state(APICCommonState *s, void *kapic) @@ -91,6 +102,15 @@ void kvm_get_apic_state(APICCommonState *s, void *kapic) v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4); s->count_shift = (v + 1) & 7; + if (kvm_has_extapic()) { + s->efeat = kvm_apic_get_reg(kapic, 0x40); + s->ectrl = kvm_apic_get_reg(kapic, 0x41); + + for (i = 0; i < s->nr_extlvt; i++) { + s->extlvt[i] = kvm_apic_get_reg(kapic, 0x50 + i); + } + } + s->initial_count_load_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); apic_next_timer(s, s->initial_count_load_time); } @@ -156,6 +176,27 @@ void kvm_uninitialize_extlvt(X86CPU *cpu) } } +static void kvm_apic_put2(CPUState *cs, run_on_cpu_data data) +{ + APICCommonState *s = data.host_ptr; + struct kvm_lapic_state2 kapic2; + int ret; + + if (is_tdx_vm()) { + return; + } + + kvm_put_apicbase(s->cpu, s->apicbase); + kvm_put_apic_state(s, &kapic2); + + ret = kvm_vcpu_ioctl(CPU(s->cpu), KVM_SET_LAPIC2, &kapic2); + if (ret < 0) { + fprintf(stderr, "KVM_SET_LAPIC2 failed EXT: %s\n", + strerror(-ret)); + abort(); + } +} + static void kvm_apic_put(CPUState *cs, run_on_cpu_data data) { APICCommonState *s = data.host_ptr; @@ -178,7 +219,11 @@ static void kvm_apic_put(CPUState *cs, run_on_cpu_data data) static void kvm_apic_post_load(APICCommonState *s) { - run_on_cpu(CPU(s->cpu), kvm_apic_put, RUN_ON_CPU_HOST_PTR(s)); + if (kvm_has_lapic2()) { + run_on_cpu(CPU(s->cpu), kvm_apic_put2, RUN_ON_CPU_HOST_PTR(s)); + } else { + run_on_cpu(CPU(s->cpu), kvm_apic_put, RUN_ON_CPU_HOST_PTR(s)); + } } static void do_inject_external_nmi(CPUState *cpu, run_on_cpu_data data) @@ -247,7 +292,11 @@ static void kvm_apic_reset(APICCommonState *s) /* Not used by KVM, which uses the CPU mp_state instead. */ s->wait_for_sipi = 0; - run_on_cpu(CPU(s->cpu), kvm_apic_put, RUN_ON_CPU_HOST_PTR(s)); + if (kvm_has_lapic2()) { + run_on_cpu(CPU(s->cpu), kvm_apic_put2, RUN_ON_CPU_HOST_PTR(s)); + } else { + run_on_cpu(CPU(s->cpu), kvm_apic_put, RUN_ON_CPU_HOST_PTR(s)); + } } static void kvm_apic_realize(DeviceState *dev, Error **errp) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index c9f4cb6430..ad7a4c3c5c 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5069,6 +5069,28 @@ static int kvm_get_mp_state(X86CPU *cpu) return 0; } +static int kvm_get_apic2(X86CPU *cpu) +{ + APICCommonState *apic; + struct kvm_lapic_state2 kapic2; + int ret; + + apic = APIC_COMMON(cpu->apic_state); + + if (!apic || !kvm_irqchip_in_kernel()) { + return 0; + } + + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_LAPIC2, &kapic2); + + if (ret < 0) { + return ret; + } + + kvm_get_apic_state(apic, &kapic2); + return 0; +} + static int kvm_get_apic(X86CPU *cpu) { APICCommonState *apic; @@ -5476,7 +5498,7 @@ int kvm_arch_get_registers(CPUState *cs, Error **errp) error_setg_errno(errp, -ret, "Failed to get MSRs"); goto out; } - ret = kvm_get_apic(cpu); + ret = has_lapic2 ? kvm_get_apic2(cpu) : kvm_get_apic(cpu); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to get APIC"); goto out; -- 2.43.0
