#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"
}
}
+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);
/* 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);
}
}
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)
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,
}
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;
}