aboutsummaryrefslogtreecommitdiff
path: root/target/i386
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw@amazon.co.uk>2022-12-16 14:32:25 +0000
committerDavid Woodhouse <dwmw@amazon.co.uk>2023-03-01 08:22:50 +0000
commit27d4075dd88a3c558fdc2da13b95915c1b6c66c9 (patch)
tree44d0ef0c925b17603bdad52e2c6fc824288158c1 /target/i386
parent91cce756179195f0725f551a5b2922ae2abbef9f (diff)
i386/xen: Add support for Xen event channel delivery to vCPU
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> Reviewed-by: Paul Durrant <paul@xen.org>
Diffstat (limited to 'target/i386')
-rw-r--r--target/i386/cpu.h2
-rw-r--r--target/i386/kvm/xen-emu.c94
2 files changed, 90 insertions, 6 deletions
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index e882c4e251..1c7603221d 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1799,6 +1799,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 435c51f625..1b319e8bad 100644
--- a/target/i386/kvm/xen-emu.c
+++ b/target/i386/kvm/xen-emu.c
@@ -21,6 +21,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"
@@ -248,6 +250,43 @@ 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 = { .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 && mrs.mr->ram_block &&
+ !int128_lt(mrs.size, int128_make64(sizeof(struct vcpu_info)))) {
+ vcpu_info_hva = qemu_map_ram_ptr(mrs.mr->ram_block,
+ mrs.offset_within_region);
+ }
+ if (!vcpu_info_hva) {
+ if (mrs.mr) {
+ memory_region_unref(mrs.mr);
+ mrs.mr = NULL;
+ }
+ ret = -EINVAL;
+ }
+
+ 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;
+}
+
static void do_set_vcpu_info_default_gpa(CPUState *cs, run_on_cpu_data data)
{
X86CPU *cpu = X86_CPU(cs);
@@ -257,8 +296,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);
}
}
@@ -269,8 +307,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)
@@ -306,7 +388,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,
@@ -903,7 +985,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;
}