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


Reply via email to