@@ -24,6 +24,8 @@
#include "hw/sysbus.h"
#include "hw/xen/xen.h"
+#include "hw/i386/x86.h"
+#include "hw/irq.h"
#include "xen_evtchn.h"
#include "xen_overlay.h"
@@ -102,6 +104,7 @@ struct XenEvtchnState {
QemuMutex port_lock;
uint32_t nr_ports;
XenEvtchnPort port_table[EVTCHN_2L_NR_CHANNELS];
+ qemu_irq gsis[GSI_NUM_PINS];
};
struct XenEvtchnState *xen_evtchn_singleton;
@@ -170,9 +173,29 @@ void xen_evtchn_create(void)
{
XenEvtchnState *s = XEN_EVTCHN(sysbus_create_simple(TYPE_XEN_EVTCHN,
-1, NULL));
+ int i;
+
xen_evtchn_singleton = s;
qemu_mutex_init(&s->port_lock);
+
+ for (i = 0; i < GSI_NUM_PINS; i++) {
+ sysbus_init_irq(SYS_BUS_DEVICE(s), &s->gsis[i]);
+ }
+}
+
+void xen_evtchn_connect_gsis(qemu_irq *system_gsis)
+{
+ XenEvtchnState *s = xen_evtchn_singleton;
+ int i;
+
+ if (!s) {
+ return;
+ }
+
+ for (i = 0; i < GSI_NUM_PINS; i++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(s), i, system_gsis[i]);
+ }
}
static void xen_evtchn_register_types(void)
@@ -182,6 +205,28 @@ static void xen_evtchn_register_types(void)
type_init(xen_evtchn_register_types)
+static void xen_evtchn_set_callback_level(XenEvtchnState *s, int level)
+{
+ uint32_t param = (uint32_t)s->callback_param;
+
+ switch (s->callback_param >> CALLBACK_VIA_TYPE_SHIFT) {
+ case HVM_PARAM_CALLBACK_TYPE_GSI:
+ if (param < GSI_NUM_PINS) {
+ qemu_set_irq(s->gsis[param], level);
+ }
+ break;
+ }
+}
+
+void xen_evtchn_deassert_callback(void)
+{
+ XenEvtchnState *s = xen_evtchn_singleton;
+
+ if (s) {
+ xen_evtchn_set_callback_level(s, 0);
+ }
+}
+
int xen_evtchn_set_callback_param(uint64_t param)
{
XenEvtchnState *s = xen_evtchn_singleton;
@@ -207,6 +252,11 @@ int xen_evtchn_set_callback_param(uint64_t param)
}
break;
}
+
+ case HVM_PARAM_CALLBACK_TYPE_GSI:
+ ret = 0;
+ break;
+
default:
ret = -ENOSYS;
break;
@@ -235,7 +285,7 @@ static void inject_callback(XenEvtchnState *s, uint32_t vcpu)
return;
}
- /* ... and isn't implemented yet. */
+ xen_evtchn_set_callback_level(s, 1);
}
static void deassign_kernel_port(evtchn_port_t port)
@@ -12,9 +12,13 @@
#ifndef QEMU_XEN_EVTCHN_H
#define QEMU_XEN_EVTCHN_H
+#include "hw/sysbus.h"
+
void xen_evtchn_create(void);
int xen_evtchn_soft_reset(void);
int xen_evtchn_set_callback_param(uint64_t param);
+void xen_evtchn_connect_gsis(qemu_irq *system_gsis);
+void xen_evtchn_deassert_callback(void);
void hmp_xen_event_list(Monitor *mon, const QDict *qdict);
void hmp_xen_event_inject(Monitor *mon, const QDict *qdict);
@@ -1303,6 +1303,12 @@ void pc_basic_device_init(struct PCMachineState *pcms,
}
*rtc_state = mc146818_rtc_init(isa_bus, 2000, rtc_irq);
+#ifdef CONFIG_XEN_EMU
+ if (xen_mode == XEN_EMULATE) {
+ xen_evtchn_connect_gsis(gsi);
+ }
+#endif
+
qemu_register_boot_set(pc_boot_set, *rtc_state);
if (!xen_enabled() &&
@@ -1795,6 +1795,7 @@ typedef struct CPUArchState {
uint64_t xen_vcpu_time_info_gpa;
uint64_t xen_vcpu_runstate_gpa;
uint8_t xen_vcpu_callback_vector;
+ bool xen_callback_asserted;
uint16_t xen_virq[XEN_NR_VIRQS];
uint64_t xen_singleshot_timer_ns;
#endif
@@ -5415,6 +5415,19 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
char str[256];
KVMState *state;
+#ifdef CONFIG_XEN_EMU
+ /*
+ * If the callback is asserted as a GSI (or PCI INTx) then check if
+ * vcpu_info->evtchn_upcall_pending has been cleared, and deassert
+ * the callback IRQ if so. Ideally we could hook into the PIC/IOAPIC
+ * EOI and only resample then, exactly how the VFIO eventfd pairs
+ * are designed to work for level triggered interrupts.
+ */
+ if (cpu->env.xen_callback_asserted) {
+ kvm_xen_maybe_deassert_callback(cs);
+ }
+#endif
+
switch (run->exit_reason) {
case KVM_EXIT_HLT:
DPRINTF("handle_hlt\n");
@@ -285,18 +285,11 @@ static void *gpa_to_hva(uint64_t gpa)
mrs.offset_within_region);
}
-void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id)
+static void *vcpu_info_hva_from_cs(CPUState *cs)
{
- CPUState *cs = qemu_get_cpu(vcpu_id);
- CPUX86State *env;
- uint64_t gpa;
-
- if (!cs) {
- return NULL;
- }
- env = &X86_CPU(cs)->env;
+ CPUX86State *env = &X86_CPU(cs)->env;
+ uint64_t gpa = env->xen_vcpu_info_gpa;
- gpa = env->xen_vcpu_info_gpa;
if (gpa == INVALID_GPA) {
gpa = env->xen_vcpu_info_default_gpa;
}
@@ -307,6 +300,31 @@ void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id)
return gpa_to_hva(gpa);
}
+void *kvm_xen_get_vcpu_info_hva(uint32_t vcpu_id)
+{
+ CPUState *cs = qemu_get_cpu(vcpu_id);
+
+ if (!cs) {
+ return NULL;
+ }
+
+ return vcpu_info_hva_from_cs(cs);
+}
+
+void kvm_xen_maybe_deassert_callback(CPUState *cs)
+{
+ struct vcpu_info *vi = vcpu_info_hva_from_cs(cs);
+ if (!vi) {
+ return;
+ }
+
+ /* If the evtchn_upcall_pending flag is cleared, turn the GSI off. */
+ if (!vi->evtchn_upcall_pending) {
+ X86_CPU(cs)->env.xen_callback_asserted = false;
+ xen_evtchn_deassert_callback();
+ }
+}
+
bool kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type)
{
CPUState *cs = qemu_get_cpu(vcpu_id);
@@ -339,6 +357,13 @@ bool kvm_xen_inject_vcpu_callback_vector(uint32_t vcpu_id, int type)
*/
qemu_cpu_kick(cs);
return true;
+
+ case HVM_PARAM_CALLBACK_TYPE_GSI:
+ case HVM_PARAM_CALLBACK_TYPE_PCI_INTX:
+ if (vcpu_id == 0) {
+ X86_CPU(cs)->env.xen_callback_asserted = true;
+ }
+ return false;
}
/* Not handled here. */
@@ -28,5 +28,6 @@ int kvm_xen_init_vcpu(CPUState *cs);
int kvm_xen_handle_exit(X86CPU *cpu, struct kvm_xen_exit *exit);
int kvm_put_xen_state(CPUState *cs);
int kvm_get_xen_state(CPUState *cs);
+void kvm_xen_maybe_deassert_callback(CPUState *cs);
#endif /* QEMU_I386_KVM_XEN_EMU_H */