6096cf7877
The way that Xen handles MSI PIRQs is kind of awful. There is a special MSI message which targets a PIRQ. The vector in the low bits of data must be zero. The low 8 bits of the PIRQ# are in the destination ID field, the extended destination ID field is unused, and instead the high bits of the PIRQ# are in the high 32 bits of the address. Using the high bits of the address means that we can't intercept and translate these messages in kvm_send_msi(), because they won't be caught by the APIC — addresses like 0x1000fee46000 aren't in the APIC's range. So we catch them in pci_msi_trigger() instead, and deliver the event channel directly. That isn't even the worst part. The worst part is that Xen snoops on writes to devices' MSI vectors while they are *masked*. When a MSI message is written which looks like it targets a PIRQ, it remembers the device and vector for later. When the guest makes a hypercall to bind that PIRQ# (snooped from a marked MSI vector) to an event channel port, Xen *unmasks* that MSI vector on the device. Xen guests using PIRQ delivery of MSI don't ever actually unmask the MSI for themselves. Now that this is working we can finally enable XENFEAT_hvm_pirqs and let the guest use it all. Tested with passthrough igb and emulated e1000e + AHCI. CPU0 CPU1 0: 65 0 IO-APIC 2-edge timer 1: 0 14 xen-pirq 1-ioapic-edge i8042 4: 0 846 xen-pirq 4-ioapic-edge ttyS0 8: 1 0 xen-pirq 8-ioapic-edge rtc0 9: 0 0 xen-pirq 9-ioapic-level acpi 12: 257 0 xen-pirq 12-ioapic-edge i8042 24: 9600 0 xen-percpu -virq timer0 25: 2758 0 xen-percpu -ipi resched0 26: 0 0 xen-percpu -ipi callfunc0 27: 0 0 xen-percpu -virq debug0 28: 1526 0 xen-percpu -ipi callfuncsingle0 29: 0 0 xen-percpu -ipi spinlock0 30: 0 8608 xen-percpu -virq timer1 31: 0 874 xen-percpu -ipi resched1 32: 0 0 xen-percpu -ipi callfunc1 33: 0 0 xen-percpu -virq debug1 34: 0 1617 xen-percpu -ipi callfuncsingle1 35: 0 0 xen-percpu -ipi spinlock1 36: 8 0 xen-dyn -event xenbus 37: 0 6046 xen-pirq -msi ahci[0000:00:03.0] 38: 1 0 xen-pirq -msi-x ens4 39: 0 73 xen-pirq -msi-x ens4-rx-0 40: 14 0 xen-pirq -msi-x ens4-rx-1 41: 0 32 xen-pirq -msi-x ens4-tx-0 42: 47 0 xen-pirq -msi-x ens4-tx-1 Signed-off-by: David Woodhouse <dwmw@amazon.co.uk> Reviewed-by: Paul Durrant <paul@xen.org>
89 lines
3.3 KiB
C
89 lines
3.3 KiB
C
/*
|
|
* QEMU Xen emulation: Event channel support
|
|
*
|
|
* Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
*
|
|
* Authors: David Woodhouse <dwmw2@infradead.org>
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
* See the COPYING file in the top-level directory.
|
|
*/
|
|
|
|
#ifndef QEMU_XEN_EVTCHN_H
|
|
#define QEMU_XEN_EVTCHN_H
|
|
|
|
#include "hw/sysbus.h"
|
|
|
|
typedef uint32_t evtchn_port_t;
|
|
|
|
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_set_callback_level(int level);
|
|
|
|
int xen_evtchn_set_port(uint16_t port);
|
|
|
|
bool xen_evtchn_set_gsi(int gsi, int level);
|
|
void xen_evtchn_snoop_msi(PCIDevice *dev, bool is_msix, unsigned int vector,
|
|
uint64_t addr, uint32_t data, bool is_masked);
|
|
void xen_evtchn_remove_pci_device(PCIDevice *dev);
|
|
struct kvm_irq_routing_entry;
|
|
int xen_evtchn_translate_pirq_msi(struct kvm_irq_routing_entry *route,
|
|
uint64_t address, uint32_t data);
|
|
bool xen_evtchn_deliver_pirq_msi(uint64_t address, uint32_t data);
|
|
|
|
|
|
/*
|
|
* These functions mirror the libxenevtchn library API, providing the QEMU
|
|
* backend side of "interdomain" event channels.
|
|
*/
|
|
struct xenevtchn_handle;
|
|
struct xenevtchn_handle *xen_be_evtchn_open(void);
|
|
int xen_be_evtchn_bind_interdomain(struct xenevtchn_handle *xc, uint32_t domid,
|
|
evtchn_port_t guest_port);
|
|
int xen_be_evtchn_unbind(struct xenevtchn_handle *xc, evtchn_port_t port);
|
|
int xen_be_evtchn_close(struct xenevtchn_handle *xc);
|
|
int xen_be_evtchn_fd(struct xenevtchn_handle *xc);
|
|
int xen_be_evtchn_notify(struct xenevtchn_handle *xc, evtchn_port_t port);
|
|
int xen_be_evtchn_unmask(struct xenevtchn_handle *xc, evtchn_port_t port);
|
|
int xen_be_evtchn_pending(struct xenevtchn_handle *xc);
|
|
/* Apart from this which is a local addition */
|
|
int xen_be_evtchn_get_guest_port(struct xenevtchn_handle *xc);
|
|
|
|
struct evtchn_status;
|
|
struct evtchn_close;
|
|
struct evtchn_unmask;
|
|
struct evtchn_bind_virq;
|
|
struct evtchn_bind_pirq;
|
|
struct evtchn_bind_ipi;
|
|
struct evtchn_send;
|
|
struct evtchn_alloc_unbound;
|
|
struct evtchn_bind_interdomain;
|
|
struct evtchn_bind_vcpu;
|
|
struct evtchn_reset;
|
|
int xen_evtchn_status_op(struct evtchn_status *status);
|
|
int xen_evtchn_close_op(struct evtchn_close *close);
|
|
int xen_evtchn_unmask_op(struct evtchn_unmask *unmask);
|
|
int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq);
|
|
int xen_evtchn_bind_pirq_op(struct evtchn_bind_pirq *pirq);
|
|
int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi);
|
|
int xen_evtchn_send_op(struct evtchn_send *send);
|
|
int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc);
|
|
int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain);
|
|
int xen_evtchn_bind_vcpu_op(struct evtchn_bind_vcpu *vcpu);
|
|
int xen_evtchn_reset_op(struct evtchn_reset *reset);
|
|
|
|
struct physdev_map_pirq;
|
|
struct physdev_unmap_pirq;
|
|
struct physdev_eoi;
|
|
struct physdev_irq_status_query;
|
|
struct physdev_get_free_pirq;
|
|
int xen_physdev_map_pirq(struct physdev_map_pirq *map);
|
|
int xen_physdev_unmap_pirq(struct physdev_unmap_pirq *unmap);
|
|
int xen_physdev_eoi_pirq(struct physdev_eoi *eoi);
|
|
int xen_physdev_query_pirq(struct physdev_irq_status_query *query);
|
|
int xen_physdev_get_free_pirq(struct physdev_get_free_pirq *get);
|
|
|
|
#endif /* QEMU_XEN_EVTCHN_H */
|