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>
This commit is contained in:
David Woodhouse 2022-12-16 14:32:25 +00:00
parent 91cce75617
commit 27d4075dd8
3 changed files with 92 additions and 6 deletions

View File

@ -21,6 +21,8 @@
int kvm_xen_soft_reset(void);
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))

View File

@ -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;

View File

@ -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;
}