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 <manali.shukla@amd.com>
---
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