136f5dc91SSasha Levin #include "kvm/virtio-pci.h"
236f5dc91SSasha Levin
336f5dc91SSasha Levin #include "kvm/ioport.h"
436f5dc91SSasha Levin #include "kvm/kvm.h"
54123ca55SMarc Zyngier #include "kvm/kvm-cpu.h"
636f5dc91SSasha Levin #include "kvm/virtio-pci-dev.h"
736f5dc91SSasha Levin #include "kvm/irq.h"
836f5dc91SSasha Levin #include "kvm/virtio.h"
91599d724SSasha Levin #include "kvm/ioeventfd.h"
102e7380dbSMarc Zyngier #include "kvm/util.h"
1136f5dc91SSasha Levin
1243c81c74SSasha Levin #include <sys/ioctl.h>
1336f5dc91SSasha Levin #include <linux/virtio_pci.h>
145a8e4f25SAlexandru Elisei #include <assert.h>
1536f5dc91SSasha Levin #include <string.h>
1636f5dc91SSasha Levin
17*d560235fSKeir Fraser /* The bit of the ISR which indicates a queue change. */
18*d560235fSKeir Fraser #define VIRTIO_PCI_ISR_QUEUE 0x1
19*d560235fSKeir Fraser
virtio_pci__add_msix_route(struct virtio_pci * vpci,u32 vec)20930876d5SJean-Philippe Brucker int virtio_pci__add_msix_route(struct virtio_pci *vpci, u32 vec)
21f44af23eSJean-Philippe Brucker {
22f44af23eSJean-Philippe Brucker int gsi;
23f44af23eSJean-Philippe Brucker struct msi_msg *msg;
24f44af23eSJean-Philippe Brucker
25f44af23eSJean-Philippe Brucker if (vec == VIRTIO_MSI_NO_VECTOR)
26f44af23eSJean-Philippe Brucker return -EINVAL;
27f44af23eSJean-Philippe Brucker
28f44af23eSJean-Philippe Brucker msg = &vpci->msix_table[vec].msg;
29f44af23eSJean-Philippe Brucker gsi = irq__add_msix_route(vpci->kvm, msg, vpci->dev_hdr.dev_num << 3);
30f44af23eSJean-Philippe Brucker /*
31f44af23eSJean-Philippe Brucker * We don't need IRQ routing if we can use
32f44af23eSJean-Philippe Brucker * MSI injection via the KVM_SIGNAL_MSI ioctl.
33f44af23eSJean-Philippe Brucker */
34c86ef0b8SJean-Philippe Brucker if (gsi == -ENXIO && vpci->signal_msi)
35f44af23eSJean-Philippe Brucker return gsi;
36f44af23eSJean-Philippe Brucker
37f44af23eSJean-Philippe Brucker if (gsi < 0)
38f44af23eSJean-Philippe Brucker die("failed to configure MSIs");
39f44af23eSJean-Philippe Brucker
40f44af23eSJean-Philippe Brucker return gsi;
41f44af23eSJean-Philippe Brucker }
42f44af23eSJean-Philippe Brucker
virtio_pci__del_msix_route(struct virtio_pci * vpci,u32 gsi)43c6590f78SJean-Philippe Brucker static void virtio_pci__del_msix_route(struct virtio_pci *vpci, u32 gsi)
44c6590f78SJean-Philippe Brucker {
45c6590f78SJean-Philippe Brucker struct msi_msg msg = { 0 };
46c6590f78SJean-Philippe Brucker
47c6590f78SJean-Philippe Brucker irq__update_msix_route(vpci->kvm, gsi, &msg);
48c6590f78SJean-Philippe Brucker }
49c6590f78SJean-Philippe Brucker
virtio_pci__ioevent_callback(struct kvm * kvm,void * param)501599d724SSasha Levin static void virtio_pci__ioevent_callback(struct kvm *kvm, void *param)
511599d724SSasha Levin {
521599d724SSasha Levin struct virtio_pci_ioevent_param *ioeventfd = param;
5302eca50cSAsias He struct virtio_pci *vpci = ioeventfd->vdev->virtio;
541599d724SSasha Levin
5502eca50cSAsias He ioeventfd->vdev->ops->notify_vq(kvm, vpci->dev, ioeventfd->vq);
561599d724SSasha Levin }
571599d724SSasha Levin
virtio_pci__init_ioeventfd(struct kvm * kvm,struct virtio_device * vdev,u32 vq)58930876d5SJean-Philippe Brucker int virtio_pci__init_ioeventfd(struct kvm *kvm, struct virtio_device *vdev,
59930876d5SJean-Philippe Brucker u32 vq)
601599d724SSasha Levin {
611599d724SSasha Levin struct ioevent ioevent;
6202eca50cSAsias He struct virtio_pci *vpci = vdev->virtio;
63e539f3e4SAlexandru Elisei u32 mmio_addr = virtio_pci__mmio_addr(vpci);
64e539f3e4SAlexandru Elisei u16 port_addr = virtio_pci__port_addr(vpci);
6521c9bc74SJean-Philippe Brucker off_t offset = vpci->doorbell_offset;
660e1882a4SJean-Philippe Brucker int r, flags = 0;
6773fd1368SJean-Philippe Brucker int pio_fd, mmio_fd;
681599d724SSasha Levin
691599d724SSasha Levin vpci->ioeventfds[vq] = (struct virtio_pci_ioevent_param) {
7002eca50cSAsias He .vdev = vdev,
711599d724SSasha Levin .vq = vq,
721599d724SSasha Levin };
731599d724SSasha Levin
741599d724SSasha Levin ioevent = (struct ioevent) {
751599d724SSasha Levin .fn = virtio_pci__ioevent_callback,
761599d724SSasha Levin .fn_ptr = &vpci->ioeventfds[vq],
771599d724SSasha Levin .datamatch = vq,
781599d724SSasha Levin .fn_kvm = kvm,
791599d724SSasha Levin };
801599d724SSasha Levin
81627d6874SAsias He /*
82a463650cSWill Deacon * Vhost will poll the eventfd in host kernel side, otherwise we
83a463650cSWill Deacon * need to poll in userspace.
84627d6874SAsias He */
85a463650cSWill Deacon if (!vdev->use_vhost)
86a463650cSWill Deacon flags |= IOEVENTFD_FLAG_USER_POLL;
87a463650cSWill Deacon
88a463650cSWill Deacon /* ioport */
8921c9bc74SJean-Philippe Brucker ioevent.io_addr = port_addr + offset;
90a463650cSWill Deacon ioevent.io_len = sizeof(u16);
9173fd1368SJean-Philippe Brucker ioevent.fd = pio_fd = eventfd(0, 0);
9271ca0facSAndre Przywara r = ioeventfd__add_event(&ioevent, flags | IOEVENTFD_FLAG_PIO);
93ea6eeb1cSSasha Levin if (r)
94ea6eeb1cSSasha Levin return r;
951599d724SSasha Levin
96a463650cSWill Deacon /* mmio */
9721c9bc74SJean-Philippe Brucker ioevent.io_addr = mmio_addr + offset;
98fe50bacbSAndreas Herrmann ioevent.io_len = sizeof(u16);
9973fd1368SJean-Philippe Brucker ioevent.fd = mmio_fd = eventfd(0, 0);
100a463650cSWill Deacon r = ioeventfd__add_event(&ioevent, flags);
101a463650cSWill Deacon if (r)
102a463650cSWill Deacon goto free_ioport_evt;
103263b80e8SSasha Levin
104a463650cSWill Deacon if (vdev->ops->notify_vq_eventfd)
10573fd1368SJean-Philippe Brucker vdev->ops->notify_vq_eventfd(kvm, vpci->dev, vq,
10673fd1368SJean-Philippe Brucker vdev->legacy ? pio_fd : mmio_fd);
1071599d724SSasha Levin return 0;
108a463650cSWill Deacon
109a463650cSWill Deacon free_ioport_evt:
11021c9bc74SJean-Philippe Brucker ioeventfd__del_event(port_addr + offset, vq);
111a463650cSWill Deacon return r;
1121599d724SSasha Levin }
1131599d724SSasha Levin
virtio_pci_init_vq(struct kvm * kvm,struct virtio_device * vdev,int vq)114930876d5SJean-Philippe Brucker int virtio_pci_init_vq(struct kvm *kvm, struct virtio_device *vdev, int vq)
115d0607293SJean-Philippe Brucker {
116d0607293SJean-Philippe Brucker int ret;
117d0607293SJean-Philippe Brucker struct virtio_pci *vpci = vdev->virtio;
118d0607293SJean-Philippe Brucker
119d0607293SJean-Philippe Brucker ret = virtio_pci__init_ioeventfd(kvm, vdev, vq);
120d0607293SJean-Philippe Brucker if (ret) {
121d0607293SJean-Philippe Brucker pr_err("couldn't add ioeventfd for vq %d: %d", vq, ret);
122d0607293SJean-Philippe Brucker return ret;
123d0607293SJean-Philippe Brucker }
124d0607293SJean-Philippe Brucker return vdev->ops->init_vq(kvm, vpci->dev, vq);
125d0607293SJean-Philippe Brucker }
126d0607293SJean-Philippe Brucker
virtio_pci_exit_vq(struct kvm * kvm,struct virtio_device * vdev,int vq)127930876d5SJean-Philippe Brucker void virtio_pci_exit_vq(struct kvm *kvm, struct virtio_device *vdev, int vq)
128ad346c2eSJean-Philippe Brucker {
129ad346c2eSJean-Philippe Brucker struct virtio_pci *vpci = vdev->virtio;
130e539f3e4SAlexandru Elisei u32 mmio_addr = virtio_pci__mmio_addr(vpci);
131e539f3e4SAlexandru Elisei u16 port_addr = virtio_pci__port_addr(vpci);
13221c9bc74SJean-Philippe Brucker off_t offset = vpci->doorbell_offset;
133ad346c2eSJean-Philippe Brucker
134c6590f78SJean-Philippe Brucker virtio_pci__del_msix_route(vpci, vpci->gsis[vq]);
135c6590f78SJean-Philippe Brucker vpci->gsis[vq] = 0;
136c6590f78SJean-Philippe Brucker vpci->vq_vector[vq] = VIRTIO_MSI_NO_VECTOR;
13721c9bc74SJean-Philippe Brucker ioeventfd__del_event(mmio_addr + offset, vq);
13821c9bc74SJean-Philippe Brucker ioeventfd__del_event(port_addr + offset, vq);
139ad346c2eSJean-Philippe Brucker virtio_exit_vq(kvm, vdev, vpci->dev, vq);
140ad346c2eSJean-Philippe Brucker }
141ad346c2eSJean-Philippe Brucker
update_msix_map(struct virtio_pci * vpci,struct msix_table * msix_entry,u32 vecnum)1426518065aSAndre Przywara static void update_msix_map(struct virtio_pci *vpci,
1436518065aSAndre Przywara struct msix_table *msix_entry, u32 vecnum)
1446518065aSAndre Przywara {
1456518065aSAndre Przywara u32 gsi, i;
1466518065aSAndre Przywara
1476518065aSAndre Przywara /* Find the GSI number used for that vector */
1486518065aSAndre Przywara if (vecnum == vpci->config_vector) {
1496518065aSAndre Przywara gsi = vpci->config_gsi;
1506518065aSAndre Przywara } else {
1516518065aSAndre Przywara for (i = 0; i < VIRTIO_PCI_MAX_VQ; i++)
1526518065aSAndre Przywara if (vpci->vq_vector[i] == vecnum)
1536518065aSAndre Przywara break;
1546518065aSAndre Przywara if (i == VIRTIO_PCI_MAX_VQ)
1556518065aSAndre Przywara return;
1566518065aSAndre Przywara gsi = vpci->gsis[i];
1576518065aSAndre Przywara }
1586518065aSAndre Przywara
1596518065aSAndre Przywara if (gsi == 0)
1606518065aSAndre Przywara return;
1616518065aSAndre Przywara
1626518065aSAndre Przywara msix_entry = &msix_entry[vecnum];
1636518065aSAndre Przywara irq__update_msix_route(vpci->kvm, gsi, &msix_entry->msg);
1646518065aSAndre Przywara }
1656518065aSAndre Przywara
virtio_pci__msix_mmio_callback(struct kvm_cpu * vcpu,u64 addr,u8 * data,u32 len,u8 is_write,void * ptr)1669b735910SMarc Zyngier static void virtio_pci__msix_mmio_callback(struct kvm_cpu *vcpu,
1679b735910SMarc Zyngier u64 addr, u8 *data, u32 len,
168a463650cSWill Deacon u8 is_write, void *ptr)
16936f5dc91SSasha Levin {
170e09b599aSJulien Thierry struct virtio_device *vdev = ptr;
171e09b599aSJulien Thierry struct virtio_pci *vpci = vdev->virtio;
1726518065aSAndre Przywara struct msix_table *table;
173e539f3e4SAlexandru Elisei u32 msix_io_addr = virtio_pci__msix_io_addr(vpci);
1742e7380dbSMarc Zyngier u32 pba_offset;
1756518065aSAndre Przywara int vecnum;
1766518065aSAndre Przywara size_t offset;
17736f5dc91SSasha Levin
1782e7380dbSMarc Zyngier BUILD_BUG_ON(VIRTIO_NR_MSIX > (sizeof(vpci->msix_pba) * 8));
1792e7380dbSMarc Zyngier
1802e7380dbSMarc Zyngier pba_offset = vpci->pci_hdr.msix.pba_offset & ~PCI_MSIX_TABLE_BIR;
1812e7380dbSMarc Zyngier if (addr >= msix_io_addr + pba_offset) {
1822e7380dbSMarc Zyngier /* Read access to PBA */
1836518065aSAndre Przywara if (is_write)
1846518065aSAndre Przywara return;
1852e7380dbSMarc Zyngier offset = addr - (msix_io_addr + pba_offset);
1862e7380dbSMarc Zyngier if ((offset + len) > sizeof (vpci->msix_pba))
1872e7380dbSMarc Zyngier return;
1882e7380dbSMarc Zyngier memcpy(data, (void *)&vpci->msix_pba + offset, len);
1892e7380dbSMarc Zyngier return;
1902e7380dbSMarc Zyngier }
1912e7380dbSMarc Zyngier
1926518065aSAndre Przywara table = vpci->msix_table;
193e539f3e4SAlexandru Elisei offset = addr - msix_io_addr;
1942e7380dbSMarc Zyngier
1956518065aSAndre Przywara vecnum = offset / sizeof(struct msix_table);
1966518065aSAndre Przywara offset = offset % sizeof(struct msix_table);
1976518065aSAndre Przywara
1986518065aSAndre Przywara if (!is_write) {
1996518065aSAndre Przywara memcpy(data, (void *)&table[vecnum] + offset, len);
2006518065aSAndre Przywara return;
20136f5dc91SSasha Levin }
20236f5dc91SSasha Levin
2036518065aSAndre Przywara memcpy((void *)&table[vecnum] + offset, data, len);
2046518065aSAndre Przywara
2056518065aSAndre Przywara /* Did we just update the address or payload? */
2066518065aSAndre Przywara if (offset < offsetof(struct msix_table, ctrl))
2076518065aSAndre Przywara update_msix_map(vpci, table, vecnum);
20806f48103SSasha Levin }
20906f48103SSasha Levin
virtio_pci__signal_msi(struct kvm * kvm,struct virtio_pci * vpci,int vec)210714ab9e6SAndre Przywara static void virtio_pci__signal_msi(struct kvm *kvm, struct virtio_pci *vpci,
211714ab9e6SAndre Przywara int vec)
21243c81c74SSasha Levin {
21343c81c74SSasha Levin struct kvm_msi msi = {
21443c81c74SSasha Levin .address_lo = vpci->msix_table[vec].msg.address_lo,
21543c81c74SSasha Levin .address_hi = vpci->msix_table[vec].msg.address_hi,
21643c81c74SSasha Levin .data = vpci->msix_table[vec].msg.data,
21743c81c74SSasha Levin };
21843c81c74SSasha Levin
219714ab9e6SAndre Przywara if (kvm->msix_needs_devid) {
220714ab9e6SAndre Przywara msi.flags = KVM_MSI_VALID_DEVID;
221714ab9e6SAndre Przywara msi.devid = vpci->dev_hdr.dev_num << 3;
222714ab9e6SAndre Przywara }
223714ab9e6SAndre Przywara
224f6108d72SJean-Philippe Brucker irq__signal_msi(kvm, &msi);
22543c81c74SSasha Levin }
22643c81c74SSasha Levin
virtio_pci__signal_vq(struct kvm * kvm,struct virtio_device * vdev,u32 vq)22702eca50cSAsias He int virtio_pci__signal_vq(struct kvm *kvm, struct virtio_device *vdev, u32 vq)
22836f5dc91SSasha Levin {
22902eca50cSAsias He struct virtio_pci *vpci = vdev->virtio;
23006f48103SSasha Levin int tbl = vpci->vq_vector[vq];
23136f5dc91SSasha Levin
232f8327b05SSasha Levin if (virtio_pci__msix_enabled(vpci) && tbl != VIRTIO_MSI_NO_VECTOR) {
233aa73be70SMatt Evans if (vpci->pci_hdr.msix.ctrl & cpu_to_le16(PCI_MSIX_FLAGS_MASKALL) ||
234aa73be70SMatt Evans vpci->msix_table[tbl].ctrl & cpu_to_le16(PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
23506f48103SSasha Levin
23606f48103SSasha Levin vpci->msix_pba |= 1 << tbl;
23706f48103SSasha Levin return 0;
23806f48103SSasha Levin }
23906f48103SSasha Levin
240c86ef0b8SJean-Philippe Brucker if (vpci->signal_msi)
24143c81c74SSasha Levin virtio_pci__signal_msi(kvm, vpci, vpci->vq_vector[vq]);
24243c81c74SSasha Levin else
24306f48103SSasha Levin kvm__irq_trigger(kvm, vpci->gsis[vq]);
24406f48103SSasha Levin } else {
245*d560235fSKeir Fraser vpci->isr |= VIRTIO_PCI_ISR_QUEUE;
2462108c86dSMarc Zyngier kvm__irq_line(kvm, vpci->legacy_irq_line, VIRTIO_IRQ_HIGH);
24706f48103SSasha Levin }
24836f5dc91SSasha Levin return 0;
24936f5dc91SSasha Levin }
25036f5dc91SSasha Levin
virtio_pci__signal_config(struct kvm * kvm,struct virtio_device * vdev)25102eca50cSAsias He int virtio_pci__signal_config(struct kvm *kvm, struct virtio_device *vdev)
25236f5dc91SSasha Levin {
25302eca50cSAsias He struct virtio_pci *vpci = vdev->virtio;
25406f48103SSasha Levin int tbl = vpci->config_vector;
25506f48103SSasha Levin
256f8327b05SSasha Levin if (virtio_pci__msix_enabled(vpci) && tbl != VIRTIO_MSI_NO_VECTOR) {
257aa73be70SMatt Evans if (vpci->pci_hdr.msix.ctrl & cpu_to_le16(PCI_MSIX_FLAGS_MASKALL) ||
258aa73be70SMatt Evans vpci->msix_table[tbl].ctrl & cpu_to_le16(PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
25906f48103SSasha Levin
26006f48103SSasha Levin vpci->msix_pba |= 1 << tbl;
26106f48103SSasha Levin return 0;
26206f48103SSasha Levin }
26306f48103SSasha Levin
264c86ef0b8SJean-Philippe Brucker if (vpci->signal_msi)
265f8327b05SSasha Levin virtio_pci__signal_msi(kvm, vpci, tbl);
26643c81c74SSasha Levin else
26706f48103SSasha Levin kvm__irq_trigger(kvm, vpci->config_gsi);
26806f48103SSasha Levin } else {
26929214484SKeir Fraser vpci->isr |= VIRTIO_PCI_ISR_CONFIG;
270353fa0d8SKeir Fraser kvm__irq_line(kvm, vpci->legacy_irq_line, VIRTIO_IRQ_HIGH);
27106f48103SSasha Levin }
27236f5dc91SSasha Levin
27336f5dc91SSasha Levin return 0;
27436f5dc91SSasha Levin }
27536f5dc91SSasha Levin
virtio_pci__bar_activate(struct kvm * kvm,struct pci_device_header * pci_hdr,int bar_num,void * data)2765a8e4f25SAlexandru Elisei static int virtio_pci__bar_activate(struct kvm *kvm,
2775a8e4f25SAlexandru Elisei struct pci_device_header *pci_hdr,
2785a8e4f25SAlexandru Elisei int bar_num, void *data)
2795a8e4f25SAlexandru Elisei {
2805a8e4f25SAlexandru Elisei struct virtio_device *vdev = data;
281b0d56e3cSJean-Philippe Brucker mmio_handler_fn mmio_fn;
2825a8e4f25SAlexandru Elisei u32 bar_addr, bar_size;
2835a8e4f25SAlexandru Elisei int r = -EINVAL;
2845a8e4f25SAlexandru Elisei
285b0d56e3cSJean-Philippe Brucker if (vdev->legacy)
286b0d56e3cSJean-Philippe Brucker mmio_fn = &virtio_pci_legacy__io_mmio_callback;
287b0d56e3cSJean-Philippe Brucker else
288b0d56e3cSJean-Philippe Brucker mmio_fn = &virtio_pci_modern__io_mmio_callback;
289b0d56e3cSJean-Philippe Brucker
2905a8e4f25SAlexandru Elisei assert(bar_num <= 2);
2915a8e4f25SAlexandru Elisei
2925a8e4f25SAlexandru Elisei bar_addr = pci__bar_address(pci_hdr, bar_num);
2935a8e4f25SAlexandru Elisei bar_size = pci__bar_size(pci_hdr, bar_num);
2945a8e4f25SAlexandru Elisei
2955a8e4f25SAlexandru Elisei switch (bar_num) {
2965a8e4f25SAlexandru Elisei case 0:
297b0d56e3cSJean-Philippe Brucker r = kvm__register_pio(kvm, bar_addr, bar_size, mmio_fn, vdev);
2985a8e4f25SAlexandru Elisei break;
2995a8e4f25SAlexandru Elisei case 1:
300b0d56e3cSJean-Philippe Brucker r = kvm__register_mmio(kvm, bar_addr, bar_size, false, mmio_fn,
301930876d5SJean-Philippe Brucker vdev);
3025a8e4f25SAlexandru Elisei break;
3035a8e4f25SAlexandru Elisei case 2:
3045a8e4f25SAlexandru Elisei r = kvm__register_mmio(kvm, bar_addr, bar_size, false,
3055a8e4f25SAlexandru Elisei virtio_pci__msix_mmio_callback, vdev);
3065a8e4f25SAlexandru Elisei break;
3075a8e4f25SAlexandru Elisei }
3085a8e4f25SAlexandru Elisei
3095a8e4f25SAlexandru Elisei return r;
3105a8e4f25SAlexandru Elisei }
3115a8e4f25SAlexandru Elisei
virtio_pci__bar_deactivate(struct kvm * kvm,struct pci_device_header * pci_hdr,int bar_num,void * data)3125a8e4f25SAlexandru Elisei static int virtio_pci__bar_deactivate(struct kvm *kvm,
3135a8e4f25SAlexandru Elisei struct pci_device_header *pci_hdr,
3145a8e4f25SAlexandru Elisei int bar_num, void *data)
3155a8e4f25SAlexandru Elisei {
3165a8e4f25SAlexandru Elisei u32 bar_addr;
3175a8e4f25SAlexandru Elisei bool success;
3185a8e4f25SAlexandru Elisei int r = -EINVAL;
3195a8e4f25SAlexandru Elisei
3205a8e4f25SAlexandru Elisei assert(bar_num <= 2);
3215a8e4f25SAlexandru Elisei
3225a8e4f25SAlexandru Elisei bar_addr = pci__bar_address(pci_hdr, bar_num);
3235a8e4f25SAlexandru Elisei
3245a8e4f25SAlexandru Elisei switch (bar_num) {
3255a8e4f25SAlexandru Elisei case 0:
326205eaa79SAndre Przywara r = kvm__deregister_pio(kvm, bar_addr);
3275a8e4f25SAlexandru Elisei break;
3285a8e4f25SAlexandru Elisei case 1:
3295a8e4f25SAlexandru Elisei case 2:
3305a8e4f25SAlexandru Elisei success = kvm__deregister_mmio(kvm, bar_addr);
3315a8e4f25SAlexandru Elisei /* kvm__deregister_mmio fails when the region is not found. */
3325a8e4f25SAlexandru Elisei r = (success ? 0 : -ENOENT);
3335a8e4f25SAlexandru Elisei break;
3345a8e4f25SAlexandru Elisei }
3355a8e4f25SAlexandru Elisei
3365a8e4f25SAlexandru Elisei return r;
3375a8e4f25SAlexandru Elisei }
3385a8e4f25SAlexandru Elisei
virtio_pci__init(struct kvm * kvm,void * dev,struct virtio_device * vdev,int device_id,int subsys_id,int class)33902eca50cSAsias He int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
340507e02d8SAsias He int device_id, int subsys_id, int class)
34136f5dc91SSasha Levin {
34202eca50cSAsias He struct virtio_pci *vpci = vdev->virtio;
343e539f3e4SAlexandru Elisei u32 mmio_addr, msix_io_block;
344e539f3e4SAlexandru Elisei u16 port_addr;
3457af40b91SSasha Levin int r;
34636f5dc91SSasha Levin
347a463650cSWill Deacon vpci->kvm = kvm;
34836f5dc91SSasha Levin vpci->dev = dev;
34936f5dc91SSasha Levin
350ce2fc8f5SAlexandru Elisei BUILD_BUG_ON(!is_power_of_two(PCI_IO_SIZE));
351ce2fc8f5SAlexandru Elisei
352e539f3e4SAlexandru Elisei port_addr = pci_get_io_port_block(PCI_IO_SIZE);
353e539f3e4SAlexandru Elisei mmio_addr = pci_get_mmio_block(PCI_IO_SIZE);
3542e7380dbSMarc Zyngier msix_io_block = pci_get_mmio_block(VIRTIO_MSIX_BAR_SIZE);
355a463650cSWill Deacon
35636f5dc91SSasha Levin vpci->pci_hdr = (struct pci_device_header) {
357aa73be70SMatt Evans .vendor_id = cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET),
358aa73be70SMatt Evans .device_id = cpu_to_le16(device_id),
359ec7dd52fSSasha Levin .command = PCI_COMMAND_IO | PCI_COMMAND_MEMORY,
36036f5dc91SSasha Levin .header_type = PCI_HEADER_TYPE_NORMAL,
361b0d56e3cSJean-Philippe Brucker .revision_id = vdev->legacy ? 0 : 1,
362aa73be70SMatt Evans .class[0] = class & 0xff,
363aa73be70SMatt Evans .class[1] = (class >> 8) & 0xff,
364aa73be70SMatt Evans .class[2] = (class >> 16) & 0xff,
365aa73be70SMatt Evans .subsys_vendor_id = cpu_to_le16(PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET),
366aa73be70SMatt Evans .subsys_id = cpu_to_le16(subsys_id),
367e539f3e4SAlexandru Elisei .bar[0] = cpu_to_le32(port_addr
3689c26dab4SSasha Levin | PCI_BASE_ADDRESS_SPACE_IO),
369e539f3e4SAlexandru Elisei .bar[1] = cpu_to_le32(mmio_addr
370a508ea95SJean-Philippe Brucker | PCI_BASE_ADDRESS_SPACE_MEMORY),
371e539f3e4SAlexandru Elisei .bar[2] = cpu_to_le32(msix_io_block
372b4dab816SSasha Levin | PCI_BASE_ADDRESS_SPACE_MEMORY),
373aa73be70SMatt Evans .status = cpu_to_le16(PCI_STATUS_CAP_LIST),
374b0d56e3cSJean-Philippe Brucker .capabilities = PCI_CAP_OFF(&vpci->pci_hdr, msix),
37548843d10SJulien Thierry .bar_size[0] = cpu_to_le32(PCI_IO_SIZE),
37648843d10SJulien Thierry .bar_size[1] = cpu_to_le32(PCI_IO_SIZE),
3772e7380dbSMarc Zyngier .bar_size[2] = cpu_to_le32(VIRTIO_MSIX_BAR_SIZE),
37836f5dc91SSasha Levin };
37936f5dc91SSasha Levin
3805a8e4f25SAlexandru Elisei r = pci__register_bar_regions(kvm, &vpci->pci_hdr,
3815a8e4f25SAlexandru Elisei virtio_pci__bar_activate,
3825a8e4f25SAlexandru Elisei virtio_pci__bar_deactivate, vdev);
3835a8e4f25SAlexandru Elisei if (r < 0)
3845a8e4f25SAlexandru Elisei return r;
3855a8e4f25SAlexandru Elisei
38621ff329dSWill Deacon vpci->dev_hdr = (struct device_header) {
38721ff329dSWill Deacon .bus_type = DEVICE_BUS_PCI,
38821ff329dSWill Deacon .data = &vpci->pci_hdr,
38921ff329dSWill Deacon };
39021ff329dSWill Deacon
39136f5dc91SSasha Levin vpci->pci_hdr.msix.cap = PCI_CAP_ID_MSIX;
39236f5dc91SSasha Levin vpci->pci_hdr.msix.next = 0;
39314bba8a0SAsias He /*
3942e7380dbSMarc Zyngier * We at most have VIRTIO_NR_MSIX entries (VIRTIO_PCI_MAX_VQ
3952e7380dbSMarc Zyngier * entries for virt queue, VIRTIO_PCI_MAX_CONFIG entries for
3962e7380dbSMarc Zyngier * config).
39714bba8a0SAsias He *
39814bba8a0SAsias He * To quote the PCI spec:
39914bba8a0SAsias He *
40014bba8a0SAsias He * System software reads this field to determine the
40114bba8a0SAsias He * MSI-X Table Size N, which is encoded as N-1.
40214bba8a0SAsias He * For example, a returned value of "00000000011"
40314bba8a0SAsias He * indicates a table size of 4.
40414bba8a0SAsias He */
4052e7380dbSMarc Zyngier vpci->pci_hdr.msix.ctrl = cpu_to_le16(VIRTIO_NR_MSIX - 1);
40606f48103SSasha Levin
407a463650cSWill Deacon /* Both table and PBA are mapped to the same BAR (2) */
408a463650cSWill Deacon vpci->pci_hdr.msix.table_offset = cpu_to_le32(2);
4092e7380dbSMarc Zyngier vpci->pci_hdr.msix.pba_offset = cpu_to_le32(2 | VIRTIO_MSIX_TABLE_SIZE);
4103d5cefc2SJean-Philippe Brucker vpci->config_vector = VIRTIO_MSI_NO_VECTOR;
4113d5cefc2SJean-Philippe Brucker /* Initialize all vq vectors to NO_VECTOR */
4123d5cefc2SJean-Philippe Brucker memset(vpci->vq_vector, 0xff, sizeof(vpci->vq_vector));
41336f5dc91SSasha Levin
414f6108d72SJean-Philippe Brucker if (irq__can_signal_msi(kvm))
415c86ef0b8SJean-Philippe Brucker vpci->signal_msi = true;
41643c81c74SSasha Levin
417c0c45eedSAndre Przywara vpci->legacy_irq_line = pci__assign_irq(&vpci->pci_hdr);
418c0c45eedSAndre Przywara
41921ff329dSWill Deacon r = device__register(&vpci->dev_hdr);
420495fbd4eSSasha Levin if (r < 0)
4215a8e4f25SAlexandru Elisei return r;
422495fbd4eSSasha Levin
423b0d56e3cSJean-Philippe Brucker if (vdev->legacy)
424b0d56e3cSJean-Philippe Brucker vpci->doorbell_offset = VIRTIO_PCI_QUEUE_NOTIFY;
425b0d56e3cSJean-Philippe Brucker else
426b0d56e3cSJean-Philippe Brucker return virtio_pci_modern_init(vdev);
427b0d56e3cSJean-Philippe Brucker
428495fbd4eSSasha Levin return 0;
429495fbd4eSSasha Levin }
430495fbd4eSSasha Levin
virtio_pci__reset(struct kvm * kvm,struct virtio_device * vdev)431eb34a8c2SJean-Philippe Brucker int virtio_pci__reset(struct kvm *kvm, struct virtio_device *vdev)
432eb34a8c2SJean-Philippe Brucker {
43331e0eaccSMartin Radev unsigned int vq;
434eb34a8c2SJean-Philippe Brucker struct virtio_pci *vpci = vdev->virtio;
435eb34a8c2SJean-Philippe Brucker
436c6590f78SJean-Philippe Brucker virtio_pci__del_msix_route(vpci, vpci->config_gsi);
437c6590f78SJean-Philippe Brucker vpci->config_gsi = 0;
438c6590f78SJean-Philippe Brucker vpci->config_vector = VIRTIO_MSI_NO_VECTOR;
439c6590f78SJean-Philippe Brucker
440eb34a8c2SJean-Philippe Brucker for (vq = 0; vq < vdev->ops->get_vq_count(kvm, vpci->dev); vq++)
441eb34a8c2SJean-Philippe Brucker virtio_pci_exit_vq(kvm, vdev, vq);
442eb34a8c2SJean-Philippe Brucker
443eb34a8c2SJean-Philippe Brucker return 0;
444eb34a8c2SJean-Philippe Brucker }
445eb34a8c2SJean-Philippe Brucker
virtio_pci__exit(struct kvm * kvm,struct virtio_device * vdev)44602eca50cSAsias He int virtio_pci__exit(struct kvm *kvm, struct virtio_device *vdev)
447495fbd4eSSasha Levin {
44802eca50cSAsias He struct virtio_pci *vpci = vdev->virtio;
449495fbd4eSSasha Levin
450eb34a8c2SJean-Philippe Brucker virtio_pci__reset(kvm, vdev);
451e539f3e4SAlexandru Elisei kvm__deregister_mmio(kvm, virtio_pci__mmio_addr(vpci));
452e539f3e4SAlexandru Elisei kvm__deregister_mmio(kvm, virtio_pci__msix_io_addr(vpci));
453205eaa79SAndre Przywara kvm__deregister_pio(kvm, virtio_pci__port_addr(vpci));
454495fbd4eSSasha Levin
45536f5dc91SSasha Levin return 0;
45636f5dc91SSasha Levin }
457