diff mbox series

[v7,27/51] i386/xen: Add support for Xen event channel delivery to vCPU

Message ID 20230116215805.1123514-28-dwmw2@infradead.org
State New
Headers show
Series Xen support under KVM | expand

Commit Message

David Woodhouse Jan. 16, 2023, 9:57 p.m. UTC
From: David Woodhouse <dwmw@amazon.co.uk>

The kvm_xen_inject_vcpu_callback_vector() function will either deliver
the per-vCPU local APIC vector (as an MSI), or just kick the vCPU out
of the kernel to trigger KVM's automatic delivery of the global vector.
Support for asserting the GSI/PCI_INTX callbacks will come later.

Also add kvm_xen_get_vcpu_info_hva() which returns the vcpu_info of
a given vCPU.

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
 include/sysemu/kvm_xen.h  |  2 +
 target/i386/cpu.h         |  2 +
 target/i386/kvm/xen-emu.c | 86 ++++++++++++++++++++++++++++++++++++---
 3 files changed, 84 insertions(+), 6 deletions(-)

Comments

Durrant, Paul Jan. 17, 2023, 11:11 a.m. UTC | #1
On 16/01/2023 21:57, David Woodhouse wrote:
> From: David Woodhouse <dwmw@amazon.co.uk>
> 
> The kvm_xen_inject_vcpu_callback_vector() function will either deliver
> the per-vCPU local APIC vector (as an MSI), or just kick the vCPU out
> of the kernel to trigger KVM's automatic delivery of the global vector.
> Support for asserting the GSI/PCI_INTX callbacks will come later.
> 
> Also add kvm_xen_get_vcpu_info_hva() which returns the vcpu_info of
> a given vCPU.
> 
> Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
> ---
>   include/sysemu/kvm_xen.h  |  2 +
>   target/i386/cpu.h         |  2 +
>   target/i386/kvm/xen-emu.c | 86 ++++++++++++++++++++++++++++++++++++---
>   3 files changed, 84 insertions(+), 6 deletions(-)
> 
> diff --git a/include/sysemu/kvm_xen.h b/include/sysemu/kvm_xen.h
> index 3e43cd7843..ee53294deb 100644
> --- a/include/sysemu/kvm_xen.h
> +++ b/include/sysemu/kvm_xen.h
> @@ -17,6 +17,8 @@
>   #define INVALID_GFN UINT64_MAX
>   
>   uint32_t kvm_xen_get_caps(void);
> +void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id);
> +void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type);
>   
>   #define kvm_xen_has_cap(cap) (!!(kvm_xen_get_caps() &           \
>                                    KVM_XEN_HVM_CONFIG_ ## cap))
> diff --git a/target/i386/cpu.h b/target/i386/cpu.h
> index 938a1b9c8b..c9b12e7476 100644
> --- a/target/i386/cpu.h
> +++ b/target/i386/cpu.h
> @@ -1788,6 +1788,8 @@ typedef struct CPUArchState {
>   #endif
>   #if defined(CONFIG_KVM)
>       struct kvm_nested_state *nested_state;
> +    MemoryRegion *xen_vcpu_info_mr;
> +    void *xen_vcpu_info_hva;
>       uint64_t xen_vcpu_info_gpa;
>       uint64_t xen_vcpu_info_default_gpa;
>       uint64_t xen_vcpu_time_info_gpa;
> diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c
> index b0e7620b16..d04d858912 100644
> --- a/target/i386/kvm/xen-emu.c
> +++ b/target/i386/kvm/xen-emu.c
> @@ -20,6 +20,8 @@
>   #include "trace.h"
>   #include "sysemu/runstate.h"
>   
> +#include "hw/pci/msi.h"
> +#include "hw/i386/apic-msidef.h"
>   #include "hw/i386/kvm/xen_overlay.h"
>   #include "hw/i386/kvm/xen_evtchn.h"
>   
> @@ -247,6 +249,35 @@ static void do_set_vcpu_callback_vector(CPUState *cs, run_on_cpu_data data)
>       }
>   }
>   
> +static int set_vcpu_info(CPUState *cs, uint64_t gpa)
> +{
> +    X86CPU *cpu = X86_CPU(cs);
> +    CPUX86State *env = &cpu->env;
> +    MemoryRegionSection mrs;
> +    int ret;
> +
> +    ret = kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, gpa);
> +    if (ret || gpa == INVALID_GPA) {
> +    fail:

Ick. Do we really want cross-block gotos? For me it would look a lot 
nicer if you did a forward jump here and later and put the label+code 
after the `return 0`.

> +        if (env->xen_vcpu_info_mr) {
> +            memory_region_unref(env->xen_vcpu_info_mr);
> +            env->xen_vcpu_info_mr = NULL;
> +        }
> +        env->xen_vcpu_info_hva = NULL;
> +        return ret;
> +    }
> +
> +    mrs = memory_region_find(get_system_memory(), gpa, sizeof(struct vcpu_info));
> +    if (!mrs.mr || !mrs.mr->ram_block || mrs.size < sizeof(struct vcpu_info) ||
> +        !(env->xen_vcpu_info_hva = qemu_map_ram_ptr(mrs.mr->ram_block,
> +                                                    mrs.offset_within_region))) {
> +        ret = -EINVAL;
> +        goto fail;
> +    }
> +    env->xen_vcpu_info_mr = mrs.mr;
> +    return 0;
> +}
Durrant, Paul Jan. 17, 2023, 12:01 p.m. UTC | #2
On 16/01/2023 21:57, David Woodhouse wrote:
> From: David Woodhouse <dwmw@amazon.co.uk>
> 
> The kvm_xen_inject_vcpu_callback_vector() function will either deliver
> the per-vCPU local APIC vector (as an MSI), or just kick the vCPU out
> of the kernel to trigger KVM's automatic delivery of the global vector.
> Support for asserting the GSI/PCI_INTX callbacks will come later.
> 
> Also add kvm_xen_get_vcpu_info_hva() which returns the vcpu_info of
> a given vCPU.
> 
> Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
> ---
>   include/sysemu/kvm_xen.h  |  2 +
>   target/i386/cpu.h         |  2 +
>   target/i386/kvm/xen-emu.c | 86 ++++++++++++++++++++++++++++++++++++---
>   3 files changed, 84 insertions(+), 6 deletions(-)
> 

Reviewed-by: Paul Durrant <paul@xen.org>
David Woodhouse Jan. 17, 2023, 12:31 p.m. UTC | #3
On Tue, 2023-01-17 at 11:11 +0000, Paul Durrant wrote:
> 
> Ick. Do we really want cross-block gotos? For me it would look a lot 
> nicer if you did a forward jump here and later and put the label+code
> after the `return 0`.

How's this?

static int set_vcpu_info(CPUState *cs, uint64_t gpa)
{
    X86CPU *cpu = X86_CPU(cs);
    CPUX86State *env = &cpu->env;
    MemoryRegionSection mrs = { .mr = NULL };
    void *vcpu_info_hva = NULL;
    int ret;

    ret = kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, gpa);
    if (ret || gpa == INVALID_GPA) {
        goto out;
    }

    mrs = memory_region_find(get_system_memory(), gpa, sizeof(struct vcpu_info));
    if (!mrs.mr) {
        ret = -EINVAL;
    } else if (!mrs.mr->ram_block || mrs.size < sizeof(struct vcpu_info) ||
               !(vcpu_info_hva = qemu_map_ram_ptr(mrs.mr->ram_block,
                                                  mrs.offset_within_region))) {
        ret = -EINVAL;
        memory_region_unref(mrs.mr);
        mrs.mr = NULL;
    }

 out:
    if (env->xen_vcpu_info_mr) {
        memory_region_unref(env->xen_vcpu_info_mr);
    }
    env->xen_vcpu_info_hva = vcpu_info_hva;
    env->xen_vcpu_info_mr = mrs.mr;
    return ret;
}
Durrant, Paul Jan. 17, 2023, 1:11 p.m. UTC | #4
On 17/01/2023 12:31, David Woodhouse wrote:
> On Tue, 2023-01-17 at 11:11 +0000, Paul Durrant wrote:
>>
>> Ick. Do we really want cross-block gotos? For me it would look a lot
>> nicer if you did a forward jump here and later and put the label+code
>> after the `return 0`.
> 
> How's this?

 From my PoV, much better. Thanks.

> 
> static int set_vcpu_info(CPUState *cs, uint64_t gpa)
> {
>      X86CPU *cpu = X86_CPU(cs);
>      CPUX86State *env = &cpu->env;
>      MemoryRegionSection mrs = { .mr = NULL };
>      void *vcpu_info_hva = NULL;
>      int ret;
> 
>      ret = kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, gpa);
>      if (ret || gpa == INVALID_GPA) {
>          goto out;
>      }
> 
>      mrs = memory_region_find(get_system_memory(), gpa, sizeof(struct vcpu_info));
>      if (!mrs.mr) {
>          ret = -EINVAL;
>      } else if (!mrs.mr->ram_block || mrs.size < sizeof(struct vcpu_info) ||
>                 !(vcpu_info_hva = qemu_map_ram_ptr(mrs.mr->ram_block,
>                                                    mrs.offset_within_region))) {
>          ret = -EINVAL;
>          memory_region_unref(mrs.mr);
>          mrs.mr = NULL;
>      }
> 
>   out:
>      if (env->xen_vcpu_info_mr) {
>          memory_region_unref(env->xen_vcpu_info_mr);
>      }
>      env->xen_vcpu_info_hva = vcpu_info_hva;
>      env->xen_vcpu_info_mr = mrs.mr;
>      return ret;
> }
> 
>
diff mbox series

Patch

diff --git a/include/sysemu/kvm_xen.h b/include/sysemu/kvm_xen.h
index 3e43cd7843..ee53294deb 100644
--- a/include/sysemu/kvm_xen.h
+++ b/include/sysemu/kvm_xen.h
@@ -17,6 +17,8 @@ 
 #define INVALID_GFN UINT64_MAX
 
 uint32_t kvm_xen_get_caps(void);
+void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id);
+void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type);
 
 #define kvm_xen_has_cap(cap) (!!(kvm_xen_get_caps() &           \
                                  KVM_XEN_HVM_CONFIG_ ## cap))
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 938a1b9c8b..c9b12e7476 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1788,6 +1788,8 @@  typedef struct CPUArchState {
 #endif
 #if defined(CONFIG_KVM)
     struct kvm_nested_state *nested_state;
+    MemoryRegion *xen_vcpu_info_mr;
+    void *xen_vcpu_info_hva;
     uint64_t xen_vcpu_info_gpa;
     uint64_t xen_vcpu_info_default_gpa;
     uint64_t xen_vcpu_time_info_gpa;
diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c
index b0e7620b16..d04d858912 100644
--- a/target/i386/kvm/xen-emu.c
+++ b/target/i386/kvm/xen-emu.c
@@ -20,6 +20,8 @@ 
 #include "trace.h"
 #include "sysemu/runstate.h"
 
+#include "hw/pci/msi.h"
+#include "hw/i386/apic-msidef.h"
 #include "hw/i386/kvm/xen_overlay.h"
 #include "hw/i386/kvm/xen_evtchn.h"
 
@@ -247,6 +249,35 @@  static void do_set_vcpu_callback_vector(CPUState *cs, run_on_cpu_data data)
     }
 }
 
+static int set_vcpu_info(CPUState *cs, uint64_t gpa)
+{
+    X86CPU *cpu = X86_CPU(cs);
+    CPUX86State *env = &cpu->env;
+    MemoryRegionSection mrs;
+    int ret;
+
+    ret = kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, gpa);
+    if (ret || gpa == INVALID_GPA) {
+    fail:
+        if (env->xen_vcpu_info_mr) {
+            memory_region_unref(env->xen_vcpu_info_mr);
+            env->xen_vcpu_info_mr = NULL;
+        }
+        env->xen_vcpu_info_hva = NULL;
+        return ret;
+    }
+
+    mrs = memory_region_find(get_system_memory(), gpa, sizeof(struct vcpu_info));
+    if (!mrs.mr || !mrs.mr->ram_block || mrs.size < sizeof(struct vcpu_info) ||
+        !(env->xen_vcpu_info_hva = qemu_map_ram_ptr(mrs.mr->ram_block,
+                                                    mrs.offset_within_region))) {
+        ret = -EINVAL;
+        goto fail;
+    }
+    env->xen_vcpu_info_mr = mrs.mr;
+    return 0;
+}
+
 static void do_set_vcpu_info_default_gpa(CPUState *cs, run_on_cpu_data data)
 {
     X86CPU *cpu = X86_CPU(cs);
@@ -256,8 +287,7 @@  static void do_set_vcpu_info_default_gpa(CPUState *cs, run_on_cpu_data data)
 
     /* Changing the default does nothing if a vcpu_info was explicitly set. */
     if (env->xen_vcpu_info_gpa == INVALID_GPA) {
-        kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO,
-                              env->xen_vcpu_info_default_gpa);
+        set_vcpu_info(cs, env->xen_vcpu_info_default_gpa);
     }
 }
 
@@ -268,8 +298,52 @@  static void do_set_vcpu_info_gpa(CPUState *cs, run_on_cpu_data data)
 
     env->xen_vcpu_info_gpa = data.host_ulong;
 
-    kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO,
-                          env->xen_vcpu_info_gpa);
+    set_vcpu_info(cs, env->xen_vcpu_info_gpa);
+}
+
+void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id)
+{
+    CPUState *cs = qemu_get_cpu(vcpu_id);
+    if (!cs) {
+        return NULL;
+    }
+
+    return X86_CPU(cs)->env.xen_vcpu_info_hva;
+}
+
+void kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type)
+{
+    CPUState *cs = qemu_get_cpu(vcpu_id);
+    uint8_t vector;
+
+    if (!cs) {
+        return;
+    }
+
+    vector = X86_CPU(cs)->env.xen_vcpu_callback_vector;
+    if (vector) {
+        /*
+         * The per-vCPU callback vector injected via lapic. Just
+         * deliver it as an MSI.
+         */
+        MSIMessage msg = {
+            .address = APIC_DEFAULT_ADDRESS | X86_CPU(cs)->apic_id,
+            .data = vector | (1UL << MSI_DATA_LEVEL_SHIFT),
+        };
+        kvm_irqchip_send_msi(kvm_state, msg);
+        return;
+    }
+
+    switch (type) {
+    case HVM_PARAM_CALLBACK_TYPE_VECTOR:
+        /*
+         * If the evtchn_upcall_pending field in the vcpu_info is set, then
+         * KVM will automatically deliver the vector on entering the vCPU
+         * so all we have to do is kick it out.
+         */
+        qemu_cpu_kick(cs);
+        break;
+    }
 }
 
 static void do_set_vcpu_time_info_gpa(CPUState *cs, run_on_cpu_data data)
@@ -305,7 +379,7 @@  static void do_vcpu_soft_reset(CPUState *cs, run_on_cpu_data data)
     env->xen_vcpu_runstate_gpa = INVALID_GPA;
     env->xen_vcpu_callback_vector = 0;
 
-    kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, INVALID_GPA);
+    set_vcpu_info(cs, INVALID_GPA);
     kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO,
                           INVALID_GPA);
     kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR,
@@ -889,7 +963,7 @@  int kvm_put_xen_state(CPUState *cs)
     }
 
     if (gpa != INVALID_GPA) {
-        ret = kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, gpa);
+        ret = set_vcpu_info(cs, gpa);
         if (ret < 0) {
             return ret;
         }