168f5d3f3SJohannes Berg // SPDX-License-Identifier: GPL-2.0
268f5d3f3SJohannes Berg /*
368f5d3f3SJohannes Berg * Copyright (C) 2020 Intel Corporation
468f5d3f3SJohannes Berg * Author: Johannes Berg <johannes@sipsolutions.net>
568f5d3f3SJohannes Berg */
668f5d3f3SJohannes Berg #include <linux/module.h>
768f5d3f3SJohannes Berg #include <linux/pci.h>
868f5d3f3SJohannes Berg #include <linux/virtio.h>
968f5d3f3SJohannes Berg #include <linux/virtio_config.h>
1068f5d3f3SJohannes Berg #include <linux/logic_iomem.h>
11522c532cSVincent Whitchurch #include <linux/of_platform.h>
1268f5d3f3SJohannes Berg #include <linux/irqdomain.h>
1368f5d3f3SJohannes Berg #include <linux/virtio_pcidev.h>
1443c590cbSJohannes Berg #include <linux/virtio-uml.h>
1568f5d3f3SJohannes Berg #include <linux/delay.h>
1668f5d3f3SJohannes Berg #include <linux/msi.h>
1768f5d3f3SJohannes Berg #include <asm/unaligned.h>
1868f5d3f3SJohannes Berg #include <irq_kern.h>
1968f5d3f3SJohannes Berg
2068f5d3f3SJohannes Berg #define MAX_DEVICES 8
2168f5d3f3SJohannes Berg #define MAX_MSI_VECTORS 32
2268f5d3f3SJohannes Berg #define CFG_SPACE_SIZE 4096
2368f5d3f3SJohannes Berg
2468f5d3f3SJohannes Berg /* for MSI-X we have a 32-bit payload */
2568f5d3f3SJohannes Berg #define MAX_IRQ_MSG_SIZE (sizeof(struct virtio_pcidev_msg) + sizeof(u32))
2668f5d3f3SJohannes Berg #define NUM_IRQ_MSGS 10
2768f5d3f3SJohannes Berg
2868f5d3f3SJohannes Berg #define HANDLE_NO_FREE(ptr) ((void *)((unsigned long)(ptr) | 1))
2968f5d3f3SJohannes Berg #define HANDLE_IS_NO_FREE(ptr) ((unsigned long)(ptr) & 1)
3068f5d3f3SJohannes Berg
3168f5d3f3SJohannes Berg struct um_pci_device {
3268f5d3f3SJohannes Berg struct virtio_device *vdev;
3368f5d3f3SJohannes Berg
3468f5d3f3SJohannes Berg /* for now just standard BARs */
3568f5d3f3SJohannes Berg u8 resptr[PCI_STD_NUM_BARS];
3668f5d3f3SJohannes Berg
3768f5d3f3SJohannes Berg struct virtqueue *cmd_vq, *irq_vq;
3868f5d3f3SJohannes Berg
3968f5d3f3SJohannes Berg #define UM_PCI_STAT_WAITING 0
4068f5d3f3SJohannes Berg unsigned long status;
4168f5d3f3SJohannes Berg
4268f5d3f3SJohannes Berg int irq;
43522c532cSVincent Whitchurch
44522c532cSVincent Whitchurch bool platform;
4568f5d3f3SJohannes Berg };
4668f5d3f3SJohannes Berg
4768f5d3f3SJohannes Berg struct um_pci_device_reg {
4868f5d3f3SJohannes Berg struct um_pci_device *dev;
4968f5d3f3SJohannes Berg void __iomem *iomem;
5068f5d3f3SJohannes Berg };
5168f5d3f3SJohannes Berg
5268f5d3f3SJohannes Berg static struct pci_host_bridge *bridge;
5368f5d3f3SJohannes Berg static DEFINE_MUTEX(um_pci_mtx);
54522c532cSVincent Whitchurch static struct um_pci_device *um_pci_platform_device;
5568f5d3f3SJohannes Berg static struct um_pci_device_reg um_pci_devices[MAX_DEVICES];
5668f5d3f3SJohannes Berg static struct fwnode_handle *um_pci_fwnode;
5768f5d3f3SJohannes Berg static struct irq_domain *um_pci_inner_domain;
5868f5d3f3SJohannes Berg static struct irq_domain *um_pci_msi_domain;
5968f5d3f3SJohannes Berg static unsigned long um_pci_msi_used[BITS_TO_LONGS(MAX_MSI_VECTORS)];
6068f5d3f3SJohannes Berg
61935f8f7aSVincent Whitchurch static unsigned int um_pci_max_delay_us = 40000;
62935f8f7aSVincent Whitchurch module_param_named(max_delay_us, um_pci_max_delay_us, uint, 0644);
6368f5d3f3SJohannes Berg
644a22c4ceSJohannes Berg struct um_pci_message_buffer {
654a22c4ceSJohannes Berg struct virtio_pcidev_msg hdr;
664a22c4ceSJohannes Berg u8 data[8];
674a22c4ceSJohannes Berg };
684a22c4ceSJohannes Berg
694a22c4ceSJohannes Berg static struct um_pci_message_buffer __percpu *um_pci_msg_bufs;
704a22c4ceSJohannes Berg
um_pci_send_cmd(struct um_pci_device * dev,struct virtio_pcidev_msg * cmd,unsigned int cmd_size,const void * extra,unsigned int extra_size,void * out,unsigned int out_size)7168f5d3f3SJohannes Berg static int um_pci_send_cmd(struct um_pci_device *dev,
7268f5d3f3SJohannes Berg struct virtio_pcidev_msg *cmd,
7368f5d3f3SJohannes Berg unsigned int cmd_size,
7468f5d3f3SJohannes Berg const void *extra, unsigned int extra_size,
7568f5d3f3SJohannes Berg void *out, unsigned int out_size)
7668f5d3f3SJohannes Berg {
7768f5d3f3SJohannes Berg struct scatterlist out_sg, extra_sg, in_sg;
7868f5d3f3SJohannes Berg struct scatterlist *sgs_list[] = {
7968f5d3f3SJohannes Berg [0] = &out_sg,
8068f5d3f3SJohannes Berg [1] = extra ? &extra_sg : &in_sg,
8168f5d3f3SJohannes Berg [2] = extra ? &in_sg : NULL,
8268f5d3f3SJohannes Berg };
834a22c4ceSJohannes Berg struct um_pci_message_buffer *buf;
8468f5d3f3SJohannes Berg int delay_count = 0;
8568f5d3f3SJohannes Berg int ret, len;
8668f5d3f3SJohannes Berg bool posted;
8768f5d3f3SJohannes Berg
884a22c4ceSJohannes Berg if (WARN_ON(cmd_size < sizeof(*cmd) || cmd_size > sizeof(*buf)))
8968f5d3f3SJohannes Berg return -EINVAL;
9068f5d3f3SJohannes Berg
9168f5d3f3SJohannes Berg switch (cmd->op) {
9268f5d3f3SJohannes Berg case VIRTIO_PCIDEV_OP_CFG_WRITE:
9368f5d3f3SJohannes Berg case VIRTIO_PCIDEV_OP_MMIO_WRITE:
9468f5d3f3SJohannes Berg case VIRTIO_PCIDEV_OP_MMIO_MEMSET:
9568f5d3f3SJohannes Berg /* in PCI, writes are posted, so don't wait */
9668f5d3f3SJohannes Berg posted = !out;
9768f5d3f3SJohannes Berg WARN_ON(!posted);
9868f5d3f3SJohannes Berg break;
9968f5d3f3SJohannes Berg default:
10068f5d3f3SJohannes Berg posted = false;
10168f5d3f3SJohannes Berg break;
10268f5d3f3SJohannes Berg }
10368f5d3f3SJohannes Berg
1044a22c4ceSJohannes Berg buf = get_cpu_var(um_pci_msg_bufs);
105bdc77507SKees Cook if (buf)
1064a22c4ceSJohannes Berg memcpy(buf, cmd, cmd_size);
1074a22c4ceSJohannes Berg
10868f5d3f3SJohannes Berg if (posted) {
10968f5d3f3SJohannes Berg u8 *ncmd = kmalloc(cmd_size + extra_size, GFP_ATOMIC);
11068f5d3f3SJohannes Berg
11168f5d3f3SJohannes Berg if (ncmd) {
11268f5d3f3SJohannes Berg memcpy(ncmd, cmd, cmd_size);
11368f5d3f3SJohannes Berg if (extra)
11468f5d3f3SJohannes Berg memcpy(ncmd + cmd_size, extra, extra_size);
11568f5d3f3SJohannes Berg cmd = (void *)ncmd;
11668f5d3f3SJohannes Berg cmd_size += extra_size;
11768f5d3f3SJohannes Berg extra = NULL;
11868f5d3f3SJohannes Berg extra_size = 0;
11968f5d3f3SJohannes Berg } else {
12068f5d3f3SJohannes Berg /* try without allocating memory */
12168f5d3f3SJohannes Berg posted = false;
1224a22c4ceSJohannes Berg cmd = (void *)buf;
12368f5d3f3SJohannes Berg }
1244a22c4ceSJohannes Berg } else {
1254a22c4ceSJohannes Berg cmd = (void *)buf;
12668f5d3f3SJohannes Berg }
12768f5d3f3SJohannes Berg
12868f5d3f3SJohannes Berg sg_init_one(&out_sg, cmd, cmd_size);
12968f5d3f3SJohannes Berg if (extra)
13068f5d3f3SJohannes Berg sg_init_one(&extra_sg, extra, extra_size);
13168f5d3f3SJohannes Berg if (out)
13268f5d3f3SJohannes Berg sg_init_one(&in_sg, out, out_size);
13368f5d3f3SJohannes Berg
13468f5d3f3SJohannes Berg /* add to internal virtio queue */
13568f5d3f3SJohannes Berg ret = virtqueue_add_sgs(dev->cmd_vq, sgs_list,
13668f5d3f3SJohannes Berg extra ? 2 : 1,
13768f5d3f3SJohannes Berg out ? 1 : 0,
13868f5d3f3SJohannes Berg posted ? cmd : HANDLE_NO_FREE(cmd),
13968f5d3f3SJohannes Berg GFP_ATOMIC);
1408a6ca543SBenjamin Berg if (ret) {
1418a6ca543SBenjamin Berg if (posted)
1428a6ca543SBenjamin Berg kfree(cmd);
1434a22c4ceSJohannes Berg goto out;
1448a6ca543SBenjamin Berg }
14568f5d3f3SJohannes Berg
14668f5d3f3SJohannes Berg if (posted) {
14768f5d3f3SJohannes Berg virtqueue_kick(dev->cmd_vq);
1484a22c4ceSJohannes Berg ret = 0;
1494a22c4ceSJohannes Berg goto out;
15068f5d3f3SJohannes Berg }
15168f5d3f3SJohannes Berg
15268f5d3f3SJohannes Berg /* kick and poll for getting a response on the queue */
15368f5d3f3SJohannes Berg set_bit(UM_PCI_STAT_WAITING, &dev->status);
15468f5d3f3SJohannes Berg virtqueue_kick(dev->cmd_vq);
15568f5d3f3SJohannes Berg
15668f5d3f3SJohannes Berg while (1) {
15768f5d3f3SJohannes Berg void *completed = virtqueue_get_buf(dev->cmd_vq, &len);
15868f5d3f3SJohannes Berg
15968f5d3f3SJohannes Berg if (completed == HANDLE_NO_FREE(cmd))
16068f5d3f3SJohannes Berg break;
16168f5d3f3SJohannes Berg
16243c590cbSJohannes Berg if (completed && !HANDLE_IS_NO_FREE(completed))
16343c590cbSJohannes Berg kfree(completed);
16443c590cbSJohannes Berg
16568f5d3f3SJohannes Berg if (WARN_ONCE(virtqueue_is_broken(dev->cmd_vq) ||
166935f8f7aSVincent Whitchurch ++delay_count > um_pci_max_delay_us,
16768f5d3f3SJohannes Berg "um virt-pci delay: %d", delay_count)) {
16868f5d3f3SJohannes Berg ret = -EIO;
16968f5d3f3SJohannes Berg break;
17068f5d3f3SJohannes Berg }
17168f5d3f3SJohannes Berg udelay(1);
17268f5d3f3SJohannes Berg }
17368f5d3f3SJohannes Berg clear_bit(UM_PCI_STAT_WAITING, &dev->status);
17468f5d3f3SJohannes Berg
1754a22c4ceSJohannes Berg out:
1764a22c4ceSJohannes Berg put_cpu_var(um_pci_msg_bufs);
17768f5d3f3SJohannes Berg return ret;
17868f5d3f3SJohannes Berg }
17968f5d3f3SJohannes Berg
um_pci_cfgspace_read(void * priv,unsigned int offset,int size)18068f5d3f3SJohannes Berg static unsigned long um_pci_cfgspace_read(void *priv, unsigned int offset,
18168f5d3f3SJohannes Berg int size)
18268f5d3f3SJohannes Berg {
18368f5d3f3SJohannes Berg struct um_pci_device_reg *reg = priv;
18468f5d3f3SJohannes Berg struct um_pci_device *dev = reg->dev;
18568f5d3f3SJohannes Berg struct virtio_pcidev_msg hdr = {
18668f5d3f3SJohannes Berg .op = VIRTIO_PCIDEV_OP_CFG_READ,
18768f5d3f3SJohannes Berg .size = size,
18868f5d3f3SJohannes Berg .addr = offset,
18968f5d3f3SJohannes Berg };
1904a22c4ceSJohannes Berg /* buf->data is maximum size - we may only use parts of it */
1914a22c4ceSJohannes Berg struct um_pci_message_buffer *buf;
1924a22c4ceSJohannes Berg u8 *data;
193d73820dfSJohannes Berg unsigned long ret = ULONG_MAX;
194bdc77507SKees Cook size_t bytes = sizeof(buf->data);
19568f5d3f3SJohannes Berg
19668f5d3f3SJohannes Berg if (!dev)
197d73820dfSJohannes Berg return ULONG_MAX;
19868f5d3f3SJohannes Berg
1994a22c4ceSJohannes Berg buf = get_cpu_var(um_pci_msg_bufs);
2004a22c4ceSJohannes Berg data = buf->data;
2014a22c4ceSJohannes Berg
202bdc77507SKees Cook if (buf)
203bdc77507SKees Cook memset(data, 0xff, bytes);
20468f5d3f3SJohannes Berg
20568f5d3f3SJohannes Berg switch (size) {
20668f5d3f3SJohannes Berg case 1:
20768f5d3f3SJohannes Berg case 2:
20868f5d3f3SJohannes Berg case 4:
20968f5d3f3SJohannes Berg #ifdef CONFIG_64BIT
21068f5d3f3SJohannes Berg case 8:
21168f5d3f3SJohannes Berg #endif
21268f5d3f3SJohannes Berg break;
21368f5d3f3SJohannes Berg default:
21468f5d3f3SJohannes Berg WARN(1, "invalid config space read size %d\n", size);
2154a22c4ceSJohannes Berg goto out;
21668f5d3f3SJohannes Berg }
21768f5d3f3SJohannes Berg
218bdc77507SKees Cook if (um_pci_send_cmd(dev, &hdr, sizeof(hdr), NULL, 0, data, bytes))
2194a22c4ceSJohannes Berg goto out;
22068f5d3f3SJohannes Berg
22168f5d3f3SJohannes Berg switch (size) {
22268f5d3f3SJohannes Berg case 1:
2234a22c4ceSJohannes Berg ret = data[0];
2244a22c4ceSJohannes Berg break;
22568f5d3f3SJohannes Berg case 2:
2264a22c4ceSJohannes Berg ret = le16_to_cpup((void *)data);
2274a22c4ceSJohannes Berg break;
22868f5d3f3SJohannes Berg case 4:
2294a22c4ceSJohannes Berg ret = le32_to_cpup((void *)data);
2304a22c4ceSJohannes Berg break;
23168f5d3f3SJohannes Berg #ifdef CONFIG_64BIT
23268f5d3f3SJohannes Berg case 8:
2334a22c4ceSJohannes Berg ret = le64_to_cpup((void *)data);
2344a22c4ceSJohannes Berg break;
23568f5d3f3SJohannes Berg #endif
23668f5d3f3SJohannes Berg default:
2374a22c4ceSJohannes Berg break;
23868f5d3f3SJohannes Berg }
2394a22c4ceSJohannes Berg
2404a22c4ceSJohannes Berg out:
2414a22c4ceSJohannes Berg put_cpu_var(um_pci_msg_bufs);
2424a22c4ceSJohannes Berg return ret;
24368f5d3f3SJohannes Berg }
24468f5d3f3SJohannes Berg
um_pci_cfgspace_write(void * priv,unsigned int offset,int size,unsigned long val)24568f5d3f3SJohannes Berg static void um_pci_cfgspace_write(void *priv, unsigned int offset, int size,
24668f5d3f3SJohannes Berg unsigned long val)
24768f5d3f3SJohannes Berg {
24868f5d3f3SJohannes Berg struct um_pci_device_reg *reg = priv;
24968f5d3f3SJohannes Berg struct um_pci_device *dev = reg->dev;
25068f5d3f3SJohannes Berg struct {
25168f5d3f3SJohannes Berg struct virtio_pcidev_msg hdr;
25268f5d3f3SJohannes Berg /* maximum size - we may only use parts of it */
25368f5d3f3SJohannes Berg u8 data[8];
25468f5d3f3SJohannes Berg } msg = {
25568f5d3f3SJohannes Berg .hdr = {
25668f5d3f3SJohannes Berg .op = VIRTIO_PCIDEV_OP_CFG_WRITE,
25768f5d3f3SJohannes Berg .size = size,
25868f5d3f3SJohannes Berg .addr = offset,
25968f5d3f3SJohannes Berg },
26068f5d3f3SJohannes Berg };
26168f5d3f3SJohannes Berg
26268f5d3f3SJohannes Berg if (!dev)
26368f5d3f3SJohannes Berg return;
26468f5d3f3SJohannes Berg
26568f5d3f3SJohannes Berg switch (size) {
26668f5d3f3SJohannes Berg case 1:
26768f5d3f3SJohannes Berg msg.data[0] = (u8)val;
26868f5d3f3SJohannes Berg break;
26968f5d3f3SJohannes Berg case 2:
27068f5d3f3SJohannes Berg put_unaligned_le16(val, (void *)msg.data);
27168f5d3f3SJohannes Berg break;
27268f5d3f3SJohannes Berg case 4:
27368f5d3f3SJohannes Berg put_unaligned_le32(val, (void *)msg.data);
27468f5d3f3SJohannes Berg break;
27568f5d3f3SJohannes Berg #ifdef CONFIG_64BIT
27668f5d3f3SJohannes Berg case 8:
27768f5d3f3SJohannes Berg put_unaligned_le64(val, (void *)msg.data);
27868f5d3f3SJohannes Berg break;
27968f5d3f3SJohannes Berg #endif
28068f5d3f3SJohannes Berg default:
28168f5d3f3SJohannes Berg WARN(1, "invalid config space write size %d\n", size);
28268f5d3f3SJohannes Berg return;
28368f5d3f3SJohannes Berg }
28468f5d3f3SJohannes Berg
28568f5d3f3SJohannes Berg WARN_ON(um_pci_send_cmd(dev, &msg.hdr, sizeof(msg), NULL, 0, NULL, 0));
28668f5d3f3SJohannes Berg }
28768f5d3f3SJohannes Berg
28868f5d3f3SJohannes Berg static const struct logic_iomem_ops um_pci_device_cfgspace_ops = {
28968f5d3f3SJohannes Berg .read = um_pci_cfgspace_read,
29068f5d3f3SJohannes Berg .write = um_pci_cfgspace_write,
29168f5d3f3SJohannes Berg };
29268f5d3f3SJohannes Berg
um_pci_bar_copy_from(void * priv,void * buffer,unsigned int offset,int size)29368f5d3f3SJohannes Berg static void um_pci_bar_copy_from(void *priv, void *buffer,
29468f5d3f3SJohannes Berg unsigned int offset, int size)
29568f5d3f3SJohannes Berg {
29668f5d3f3SJohannes Berg u8 *resptr = priv;
29768f5d3f3SJohannes Berg struct um_pci_device *dev = container_of(resptr - *resptr,
29868f5d3f3SJohannes Berg struct um_pci_device,
29968f5d3f3SJohannes Berg resptr[0]);
30068f5d3f3SJohannes Berg struct virtio_pcidev_msg hdr = {
30168f5d3f3SJohannes Berg .op = VIRTIO_PCIDEV_OP_MMIO_READ,
30268f5d3f3SJohannes Berg .bar = *resptr,
30368f5d3f3SJohannes Berg .size = size,
30468f5d3f3SJohannes Berg .addr = offset,
30568f5d3f3SJohannes Berg };
30668f5d3f3SJohannes Berg
30768f5d3f3SJohannes Berg memset(buffer, 0xff, size);
30868f5d3f3SJohannes Berg
30968f5d3f3SJohannes Berg um_pci_send_cmd(dev, &hdr, sizeof(hdr), NULL, 0, buffer, size);
31068f5d3f3SJohannes Berg }
31168f5d3f3SJohannes Berg
um_pci_bar_read(void * priv,unsigned int offset,int size)31268f5d3f3SJohannes Berg static unsigned long um_pci_bar_read(void *priv, unsigned int offset,
31368f5d3f3SJohannes Berg int size)
31468f5d3f3SJohannes Berg {
3154a22c4ceSJohannes Berg /* buf->data is maximum size - we may only use parts of it */
3164a22c4ceSJohannes Berg struct um_pci_message_buffer *buf;
3174a22c4ceSJohannes Berg u8 *data;
318d73820dfSJohannes Berg unsigned long ret = ULONG_MAX;
3194a22c4ceSJohannes Berg
3204a22c4ceSJohannes Berg buf = get_cpu_var(um_pci_msg_bufs);
3214a22c4ceSJohannes Berg data = buf->data;
32268f5d3f3SJohannes Berg
32368f5d3f3SJohannes Berg switch (size) {
32468f5d3f3SJohannes Berg case 1:
32568f5d3f3SJohannes Berg case 2:
32668f5d3f3SJohannes Berg case 4:
32768f5d3f3SJohannes Berg #ifdef CONFIG_64BIT
32868f5d3f3SJohannes Berg case 8:
32968f5d3f3SJohannes Berg #endif
33068f5d3f3SJohannes Berg break;
33168f5d3f3SJohannes Berg default:
33268f5d3f3SJohannes Berg WARN(1, "invalid config space read size %d\n", size);
3334a22c4ceSJohannes Berg goto out;
33468f5d3f3SJohannes Berg }
33568f5d3f3SJohannes Berg
33668f5d3f3SJohannes Berg um_pci_bar_copy_from(priv, data, offset, size);
33768f5d3f3SJohannes Berg
33868f5d3f3SJohannes Berg switch (size) {
33968f5d3f3SJohannes Berg case 1:
3404a22c4ceSJohannes Berg ret = data[0];
3414a22c4ceSJohannes Berg break;
34268f5d3f3SJohannes Berg case 2:
3434a22c4ceSJohannes Berg ret = le16_to_cpup((void *)data);
3444a22c4ceSJohannes Berg break;
34568f5d3f3SJohannes Berg case 4:
3464a22c4ceSJohannes Berg ret = le32_to_cpup((void *)data);
3474a22c4ceSJohannes Berg break;
34868f5d3f3SJohannes Berg #ifdef CONFIG_64BIT
34968f5d3f3SJohannes Berg case 8:
3504a22c4ceSJohannes Berg ret = le64_to_cpup((void *)data);
3514a22c4ceSJohannes Berg break;
35268f5d3f3SJohannes Berg #endif
35368f5d3f3SJohannes Berg default:
3544a22c4ceSJohannes Berg break;
35568f5d3f3SJohannes Berg }
3564a22c4ceSJohannes Berg
3574a22c4ceSJohannes Berg out:
3584a22c4ceSJohannes Berg put_cpu_var(um_pci_msg_bufs);
3594a22c4ceSJohannes Berg return ret;
36068f5d3f3SJohannes Berg }
36168f5d3f3SJohannes Berg
um_pci_bar_copy_to(void * priv,unsigned int offset,const void * buffer,int size)36268f5d3f3SJohannes Berg static void um_pci_bar_copy_to(void *priv, unsigned int offset,
36368f5d3f3SJohannes Berg const void *buffer, int size)
36468f5d3f3SJohannes Berg {
36568f5d3f3SJohannes Berg u8 *resptr = priv;
36668f5d3f3SJohannes Berg struct um_pci_device *dev = container_of(resptr - *resptr,
36768f5d3f3SJohannes Berg struct um_pci_device,
36868f5d3f3SJohannes Berg resptr[0]);
36968f5d3f3SJohannes Berg struct virtio_pcidev_msg hdr = {
37068f5d3f3SJohannes Berg .op = VIRTIO_PCIDEV_OP_MMIO_WRITE,
37168f5d3f3SJohannes Berg .bar = *resptr,
37268f5d3f3SJohannes Berg .size = size,
37368f5d3f3SJohannes Berg .addr = offset,
37468f5d3f3SJohannes Berg };
37568f5d3f3SJohannes Berg
37668f5d3f3SJohannes Berg um_pci_send_cmd(dev, &hdr, sizeof(hdr), buffer, size, NULL, 0);
37768f5d3f3SJohannes Berg }
37868f5d3f3SJohannes Berg
um_pci_bar_write(void * priv,unsigned int offset,int size,unsigned long val)37968f5d3f3SJohannes Berg static void um_pci_bar_write(void *priv, unsigned int offset, int size,
38068f5d3f3SJohannes Berg unsigned long val)
38168f5d3f3SJohannes Berg {
38268f5d3f3SJohannes Berg /* maximum size - we may only use parts of it */
38368f5d3f3SJohannes Berg u8 data[8];
38468f5d3f3SJohannes Berg
38568f5d3f3SJohannes Berg switch (size) {
38668f5d3f3SJohannes Berg case 1:
38768f5d3f3SJohannes Berg data[0] = (u8)val;
38868f5d3f3SJohannes Berg break;
38968f5d3f3SJohannes Berg case 2:
39068f5d3f3SJohannes Berg put_unaligned_le16(val, (void *)data);
39168f5d3f3SJohannes Berg break;
39268f5d3f3SJohannes Berg case 4:
39368f5d3f3SJohannes Berg put_unaligned_le32(val, (void *)data);
39468f5d3f3SJohannes Berg break;
39568f5d3f3SJohannes Berg #ifdef CONFIG_64BIT
39668f5d3f3SJohannes Berg case 8:
39768f5d3f3SJohannes Berg put_unaligned_le64(val, (void *)data);
39868f5d3f3SJohannes Berg break;
39968f5d3f3SJohannes Berg #endif
40068f5d3f3SJohannes Berg default:
40168f5d3f3SJohannes Berg WARN(1, "invalid config space write size %d\n", size);
40268f5d3f3SJohannes Berg return;
40368f5d3f3SJohannes Berg }
40468f5d3f3SJohannes Berg
40568f5d3f3SJohannes Berg um_pci_bar_copy_to(priv, offset, data, size);
40668f5d3f3SJohannes Berg }
40768f5d3f3SJohannes Berg
um_pci_bar_set(void * priv,unsigned int offset,u8 value,int size)40868f5d3f3SJohannes Berg static void um_pci_bar_set(void *priv, unsigned int offset, u8 value, int size)
40968f5d3f3SJohannes Berg {
41068f5d3f3SJohannes Berg u8 *resptr = priv;
41168f5d3f3SJohannes Berg struct um_pci_device *dev = container_of(resptr - *resptr,
41268f5d3f3SJohannes Berg struct um_pci_device,
41368f5d3f3SJohannes Berg resptr[0]);
41468f5d3f3SJohannes Berg struct {
41568f5d3f3SJohannes Berg struct virtio_pcidev_msg hdr;
41668f5d3f3SJohannes Berg u8 data;
41768f5d3f3SJohannes Berg } msg = {
41868f5d3f3SJohannes Berg .hdr = {
41968f5d3f3SJohannes Berg .op = VIRTIO_PCIDEV_OP_CFG_WRITE,
42068f5d3f3SJohannes Berg .bar = *resptr,
42168f5d3f3SJohannes Berg .size = size,
42268f5d3f3SJohannes Berg .addr = offset,
42368f5d3f3SJohannes Berg },
42468f5d3f3SJohannes Berg .data = value,
42568f5d3f3SJohannes Berg };
42668f5d3f3SJohannes Berg
42768f5d3f3SJohannes Berg um_pci_send_cmd(dev, &msg.hdr, sizeof(msg), NULL, 0, NULL, 0);
42868f5d3f3SJohannes Berg }
42968f5d3f3SJohannes Berg
43068f5d3f3SJohannes Berg static const struct logic_iomem_ops um_pci_device_bar_ops = {
43168f5d3f3SJohannes Berg .read = um_pci_bar_read,
43268f5d3f3SJohannes Berg .write = um_pci_bar_write,
43368f5d3f3SJohannes Berg .set = um_pci_bar_set,
43468f5d3f3SJohannes Berg .copy_from = um_pci_bar_copy_from,
43568f5d3f3SJohannes Berg .copy_to = um_pci_bar_copy_to,
43668f5d3f3SJohannes Berg };
43768f5d3f3SJohannes Berg
um_pci_map_bus(struct pci_bus * bus,unsigned int devfn,int where)43868f5d3f3SJohannes Berg static void __iomem *um_pci_map_bus(struct pci_bus *bus, unsigned int devfn,
43968f5d3f3SJohannes Berg int where)
44068f5d3f3SJohannes Berg {
44168f5d3f3SJohannes Berg struct um_pci_device_reg *dev;
44268f5d3f3SJohannes Berg unsigned int busn = bus->number;
44368f5d3f3SJohannes Berg
44468f5d3f3SJohannes Berg if (busn > 0)
44568f5d3f3SJohannes Berg return NULL;
44668f5d3f3SJohannes Berg
44768f5d3f3SJohannes Berg /* not allowing functions for now ... */
44868f5d3f3SJohannes Berg if (devfn % 8)
44968f5d3f3SJohannes Berg return NULL;
45068f5d3f3SJohannes Berg
45168f5d3f3SJohannes Berg if (devfn / 8 >= ARRAY_SIZE(um_pci_devices))
45268f5d3f3SJohannes Berg return NULL;
45368f5d3f3SJohannes Berg
45468f5d3f3SJohannes Berg dev = &um_pci_devices[devfn / 8];
45568f5d3f3SJohannes Berg if (!dev)
45668f5d3f3SJohannes Berg return NULL;
45768f5d3f3SJohannes Berg
45868f5d3f3SJohannes Berg return (void __iomem *)((unsigned long)dev->iomem + where);
45968f5d3f3SJohannes Berg }
46068f5d3f3SJohannes Berg
46168f5d3f3SJohannes Berg static struct pci_ops um_pci_ops = {
46268f5d3f3SJohannes Berg .map_bus = um_pci_map_bus,
46368f5d3f3SJohannes Berg .read = pci_generic_config_read,
46468f5d3f3SJohannes Berg .write = pci_generic_config_write,
46568f5d3f3SJohannes Berg };
46668f5d3f3SJohannes Berg
um_pci_rescan(void)46768f5d3f3SJohannes Berg static void um_pci_rescan(void)
46868f5d3f3SJohannes Berg {
46968f5d3f3SJohannes Berg pci_lock_rescan_remove();
47068f5d3f3SJohannes Berg pci_rescan_bus(bridge->bus);
47168f5d3f3SJohannes Berg pci_unlock_rescan_remove();
47268f5d3f3SJohannes Berg }
47368f5d3f3SJohannes Berg
um_pci_irq_vq_addbuf(struct virtqueue * vq,void * buf,bool kick)47468f5d3f3SJohannes Berg static void um_pci_irq_vq_addbuf(struct virtqueue *vq, void *buf, bool kick)
47568f5d3f3SJohannes Berg {
47668f5d3f3SJohannes Berg struct scatterlist sg[1];
47768f5d3f3SJohannes Berg
47868f5d3f3SJohannes Berg sg_init_one(sg, buf, MAX_IRQ_MSG_SIZE);
47968f5d3f3SJohannes Berg if (virtqueue_add_inbuf(vq, sg, 1, buf, GFP_ATOMIC))
48068f5d3f3SJohannes Berg kfree(buf);
48168f5d3f3SJohannes Berg else if (kick)
48268f5d3f3SJohannes Berg virtqueue_kick(vq);
48368f5d3f3SJohannes Berg }
48468f5d3f3SJohannes Berg
um_pci_handle_irq_message(struct virtqueue * vq,struct virtio_pcidev_msg * msg)48568f5d3f3SJohannes Berg static void um_pci_handle_irq_message(struct virtqueue *vq,
48668f5d3f3SJohannes Berg struct virtio_pcidev_msg *msg)
48768f5d3f3SJohannes Berg {
48868f5d3f3SJohannes Berg struct virtio_device *vdev = vq->vdev;
48968f5d3f3SJohannes Berg struct um_pci_device *dev = vdev->priv;
49068f5d3f3SJohannes Berg
491522c532cSVincent Whitchurch if (!dev->irq)
492522c532cSVincent Whitchurch return;
493522c532cSVincent Whitchurch
49468f5d3f3SJohannes Berg /* we should properly chain interrupts, but on ARCH=um we don't care */
49568f5d3f3SJohannes Berg
49668f5d3f3SJohannes Berg switch (msg->op) {
49768f5d3f3SJohannes Berg case VIRTIO_PCIDEV_OP_INT:
49868f5d3f3SJohannes Berg generic_handle_irq(dev->irq);
49968f5d3f3SJohannes Berg break;
50068f5d3f3SJohannes Berg case VIRTIO_PCIDEV_OP_MSI:
50168f5d3f3SJohannes Berg /* our MSI message is just the interrupt number */
50268f5d3f3SJohannes Berg if (msg->size == sizeof(u32))
50368f5d3f3SJohannes Berg generic_handle_irq(le32_to_cpup((void *)msg->data));
50468f5d3f3SJohannes Berg else
50568f5d3f3SJohannes Berg generic_handle_irq(le16_to_cpup((void *)msg->data));
50668f5d3f3SJohannes Berg break;
50768f5d3f3SJohannes Berg case VIRTIO_PCIDEV_OP_PME:
50868f5d3f3SJohannes Berg /* nothing to do - we already woke up due to the message */
50968f5d3f3SJohannes Berg break;
51068f5d3f3SJohannes Berg default:
51168f5d3f3SJohannes Berg dev_err(&vdev->dev, "unexpected virt-pci message %d\n", msg->op);
51268f5d3f3SJohannes Berg break;
51368f5d3f3SJohannes Berg }
51468f5d3f3SJohannes Berg }
51568f5d3f3SJohannes Berg
um_pci_cmd_vq_cb(struct virtqueue * vq)51668f5d3f3SJohannes Berg static void um_pci_cmd_vq_cb(struct virtqueue *vq)
51768f5d3f3SJohannes Berg {
51868f5d3f3SJohannes Berg struct virtio_device *vdev = vq->vdev;
51968f5d3f3SJohannes Berg struct um_pci_device *dev = vdev->priv;
52068f5d3f3SJohannes Berg void *cmd;
52168f5d3f3SJohannes Berg int len;
52268f5d3f3SJohannes Berg
52368f5d3f3SJohannes Berg if (test_bit(UM_PCI_STAT_WAITING, &dev->status))
52468f5d3f3SJohannes Berg return;
52568f5d3f3SJohannes Berg
52668f5d3f3SJohannes Berg while ((cmd = virtqueue_get_buf(vq, &len))) {
52768f5d3f3SJohannes Berg if (WARN_ON(HANDLE_IS_NO_FREE(cmd)))
52868f5d3f3SJohannes Berg continue;
52968f5d3f3SJohannes Berg kfree(cmd);
53068f5d3f3SJohannes Berg }
53168f5d3f3SJohannes Berg }
53268f5d3f3SJohannes Berg
um_pci_irq_vq_cb(struct virtqueue * vq)53368f5d3f3SJohannes Berg static void um_pci_irq_vq_cb(struct virtqueue *vq)
53468f5d3f3SJohannes Berg {
53568f5d3f3SJohannes Berg struct virtio_pcidev_msg *msg;
53668f5d3f3SJohannes Berg int len;
53768f5d3f3SJohannes Berg
53868f5d3f3SJohannes Berg while ((msg = virtqueue_get_buf(vq, &len))) {
53968f5d3f3SJohannes Berg if (len >= sizeof(*msg))
54068f5d3f3SJohannes Berg um_pci_handle_irq_message(vq, msg);
54168f5d3f3SJohannes Berg
54268f5d3f3SJohannes Berg /* recycle the message buffer */
54368f5d3f3SJohannes Berg um_pci_irq_vq_addbuf(vq, msg, true);
54468f5d3f3SJohannes Berg }
54568f5d3f3SJohannes Berg }
54668f5d3f3SJohannes Berg
547974b808dSVincent Whitchurch #ifdef CONFIG_OF
548314a1408SVincent Whitchurch /* Copied from arch/x86/kernel/devicetree.c */
pcibios_get_phb_of_node(struct pci_bus * bus)549314a1408SVincent Whitchurch struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
550314a1408SVincent Whitchurch {
551314a1408SVincent Whitchurch struct device_node *np;
552314a1408SVincent Whitchurch
553314a1408SVincent Whitchurch for_each_node_by_type(np, "pci") {
554314a1408SVincent Whitchurch const void *prop;
555314a1408SVincent Whitchurch unsigned int bus_min;
556314a1408SVincent Whitchurch
557314a1408SVincent Whitchurch prop = of_get_property(np, "bus-range", NULL);
558314a1408SVincent Whitchurch if (!prop)
559314a1408SVincent Whitchurch continue;
560314a1408SVincent Whitchurch bus_min = be32_to_cpup(prop);
561314a1408SVincent Whitchurch if (bus->number == bus_min)
562314a1408SVincent Whitchurch return np;
563314a1408SVincent Whitchurch }
564314a1408SVincent Whitchurch return NULL;
565314a1408SVincent Whitchurch }
566974b808dSVincent Whitchurch #endif
567314a1408SVincent Whitchurch
um_pci_init_vqs(struct um_pci_device * dev)56868f5d3f3SJohannes Berg static int um_pci_init_vqs(struct um_pci_device *dev)
56968f5d3f3SJohannes Berg {
57068f5d3f3SJohannes Berg struct virtqueue *vqs[2];
57168f5d3f3SJohannes Berg static const char *const names[2] = { "cmd", "irq" };
57268f5d3f3SJohannes Berg vq_callback_t *cbs[2] = { um_pci_cmd_vq_cb, um_pci_irq_vq_cb };
57368f5d3f3SJohannes Berg int err, i;
57468f5d3f3SJohannes Berg
57568f5d3f3SJohannes Berg err = virtio_find_vqs(dev->vdev, 2, vqs, cbs, names, NULL);
57668f5d3f3SJohannes Berg if (err)
57768f5d3f3SJohannes Berg return err;
57868f5d3f3SJohannes Berg
57968f5d3f3SJohannes Berg dev->cmd_vq = vqs[0];
58068f5d3f3SJohannes Berg dev->irq_vq = vqs[1];
58168f5d3f3SJohannes Berg
582eacea844SVincent Whitchurch virtio_device_ready(dev->vdev);
583eacea844SVincent Whitchurch
58468f5d3f3SJohannes Berg for (i = 0; i < NUM_IRQ_MSGS; i++) {
58568f5d3f3SJohannes Berg void *msg = kzalloc(MAX_IRQ_MSG_SIZE, GFP_KERNEL);
58668f5d3f3SJohannes Berg
58768f5d3f3SJohannes Berg if (msg)
58868f5d3f3SJohannes Berg um_pci_irq_vq_addbuf(dev->irq_vq, msg, false);
58968f5d3f3SJohannes Berg }
59068f5d3f3SJohannes Berg
59168f5d3f3SJohannes Berg virtqueue_kick(dev->irq_vq);
59268f5d3f3SJohannes Berg
59368f5d3f3SJohannes Berg return 0;
59468f5d3f3SJohannes Berg }
59568f5d3f3SJohannes Berg
__um_pci_virtio_platform_remove(struct virtio_device * vdev,struct um_pci_device * dev)596522c532cSVincent Whitchurch static void __um_pci_virtio_platform_remove(struct virtio_device *vdev,
597522c532cSVincent Whitchurch struct um_pci_device *dev)
598522c532cSVincent Whitchurch {
599522c532cSVincent Whitchurch virtio_reset_device(vdev);
600522c532cSVincent Whitchurch vdev->config->del_vqs(vdev);
601522c532cSVincent Whitchurch
602522c532cSVincent Whitchurch mutex_lock(&um_pci_mtx);
603522c532cSVincent Whitchurch um_pci_platform_device = NULL;
604522c532cSVincent Whitchurch mutex_unlock(&um_pci_mtx);
605522c532cSVincent Whitchurch
606522c532cSVincent Whitchurch kfree(dev);
607522c532cSVincent Whitchurch }
608522c532cSVincent Whitchurch
um_pci_virtio_platform_probe(struct virtio_device * vdev,struct um_pci_device * dev)609522c532cSVincent Whitchurch static int um_pci_virtio_platform_probe(struct virtio_device *vdev,
610522c532cSVincent Whitchurch struct um_pci_device *dev)
611522c532cSVincent Whitchurch {
612522c532cSVincent Whitchurch int ret;
613522c532cSVincent Whitchurch
614522c532cSVincent Whitchurch dev->platform = true;
615522c532cSVincent Whitchurch
616522c532cSVincent Whitchurch mutex_lock(&um_pci_mtx);
617522c532cSVincent Whitchurch
618522c532cSVincent Whitchurch if (um_pci_platform_device) {
619522c532cSVincent Whitchurch mutex_unlock(&um_pci_mtx);
620522c532cSVincent Whitchurch ret = -EBUSY;
621522c532cSVincent Whitchurch goto out_free;
622522c532cSVincent Whitchurch }
623522c532cSVincent Whitchurch
624522c532cSVincent Whitchurch ret = um_pci_init_vqs(dev);
625522c532cSVincent Whitchurch if (ret) {
626522c532cSVincent Whitchurch mutex_unlock(&um_pci_mtx);
627522c532cSVincent Whitchurch goto out_free;
628522c532cSVincent Whitchurch }
629522c532cSVincent Whitchurch
630522c532cSVincent Whitchurch um_pci_platform_device = dev;
631522c532cSVincent Whitchurch
632522c532cSVincent Whitchurch mutex_unlock(&um_pci_mtx);
633522c532cSVincent Whitchurch
634522c532cSVincent Whitchurch ret = of_platform_default_populate(vdev->dev.of_node, NULL, &vdev->dev);
635522c532cSVincent Whitchurch if (ret)
636522c532cSVincent Whitchurch __um_pci_virtio_platform_remove(vdev, dev);
637522c532cSVincent Whitchurch
638522c532cSVincent Whitchurch return ret;
639522c532cSVincent Whitchurch
640522c532cSVincent Whitchurch out_free:
641522c532cSVincent Whitchurch kfree(dev);
642522c532cSVincent Whitchurch return ret;
643522c532cSVincent Whitchurch }
644522c532cSVincent Whitchurch
um_pci_virtio_probe(struct virtio_device * vdev)64568f5d3f3SJohannes Berg static int um_pci_virtio_probe(struct virtio_device *vdev)
64668f5d3f3SJohannes Berg {
64768f5d3f3SJohannes Berg struct um_pci_device *dev;
64868f5d3f3SJohannes Berg int i, free = -1;
64968f5d3f3SJohannes Berg int err = -ENOSPC;
65068f5d3f3SJohannes Berg
65168f5d3f3SJohannes Berg dev = kzalloc(sizeof(*dev), GFP_KERNEL);
65268f5d3f3SJohannes Berg if (!dev)
65368f5d3f3SJohannes Berg return -ENOMEM;
65468f5d3f3SJohannes Berg
65568f5d3f3SJohannes Berg dev->vdev = vdev;
65668f5d3f3SJohannes Berg vdev->priv = dev;
65768f5d3f3SJohannes Berg
658522c532cSVincent Whitchurch if (of_device_is_compatible(vdev->dev.of_node, "simple-bus"))
659522c532cSVincent Whitchurch return um_pci_virtio_platform_probe(vdev, dev);
660522c532cSVincent Whitchurch
66168f5d3f3SJohannes Berg mutex_lock(&um_pci_mtx);
66268f5d3f3SJohannes Berg for (i = 0; i < MAX_DEVICES; i++) {
66368f5d3f3SJohannes Berg if (um_pci_devices[i].dev)
66468f5d3f3SJohannes Berg continue;
66568f5d3f3SJohannes Berg free = i;
66668f5d3f3SJohannes Berg break;
66768f5d3f3SJohannes Berg }
66868f5d3f3SJohannes Berg
66968f5d3f3SJohannes Berg if (free < 0)
67068f5d3f3SJohannes Berg goto error;
67168f5d3f3SJohannes Berg
67268f5d3f3SJohannes Berg err = um_pci_init_vqs(dev);
67368f5d3f3SJohannes Berg if (err)
67468f5d3f3SJohannes Berg goto error;
67568f5d3f3SJohannes Berg
67668f5d3f3SJohannes Berg dev->irq = irq_alloc_desc(numa_node_id());
67768f5d3f3SJohannes Berg if (dev->irq < 0) {
67868f5d3f3SJohannes Berg err = dev->irq;
679eacea844SVincent Whitchurch goto err_reset;
68068f5d3f3SJohannes Berg }
68168f5d3f3SJohannes Berg um_pci_devices[free].dev = dev;
68268f5d3f3SJohannes Berg vdev->priv = dev;
68368f5d3f3SJohannes Berg
68468f5d3f3SJohannes Berg mutex_unlock(&um_pci_mtx);
68568f5d3f3SJohannes Berg
68668f5d3f3SJohannes Berg device_set_wakeup_enable(&vdev->dev, true);
68768f5d3f3SJohannes Berg
68843c590cbSJohannes Berg /*
68943c590cbSJohannes Berg * In order to do suspend-resume properly, don't allow VQs
69043c590cbSJohannes Berg * to be suspended.
69143c590cbSJohannes Berg */
69243c590cbSJohannes Berg virtio_uml_set_no_vq_suspend(vdev, true);
69343c590cbSJohannes Berg
69468f5d3f3SJohannes Berg um_pci_rescan();
69568f5d3f3SJohannes Berg return 0;
696eacea844SVincent Whitchurch err_reset:
697eacea844SVincent Whitchurch virtio_reset_device(vdev);
698eacea844SVincent Whitchurch vdev->config->del_vqs(vdev);
69968f5d3f3SJohannes Berg error:
70068f5d3f3SJohannes Berg mutex_unlock(&um_pci_mtx);
70168f5d3f3SJohannes Berg kfree(dev);
70268f5d3f3SJohannes Berg return err;
70368f5d3f3SJohannes Berg }
70468f5d3f3SJohannes Berg
um_pci_virtio_remove(struct virtio_device * vdev)70568f5d3f3SJohannes Berg static void um_pci_virtio_remove(struct virtio_device *vdev)
70668f5d3f3SJohannes Berg {
70768f5d3f3SJohannes Berg struct um_pci_device *dev = vdev->priv;
70868f5d3f3SJohannes Berg int i;
70968f5d3f3SJohannes Berg
710522c532cSVincent Whitchurch if (dev->platform) {
711522c532cSVincent Whitchurch of_platform_depopulate(&vdev->dev);
712522c532cSVincent Whitchurch __um_pci_virtio_platform_remove(vdev, dev);
713522c532cSVincent Whitchurch return;
714522c532cSVincent Whitchurch }
715522c532cSVincent Whitchurch
71668f5d3f3SJohannes Berg device_set_wakeup_enable(&vdev->dev, false);
71768f5d3f3SJohannes Berg
71868f5d3f3SJohannes Berg mutex_lock(&um_pci_mtx);
71968f5d3f3SJohannes Berg for (i = 0; i < MAX_DEVICES; i++) {
72068f5d3f3SJohannes Berg if (um_pci_devices[i].dev != dev)
72168f5d3f3SJohannes Berg continue;
722339b84dcSBenjamin Berg
72368f5d3f3SJohannes Berg um_pci_devices[i].dev = NULL;
72468f5d3f3SJohannes Berg irq_free_desc(dev->irq);
725339b84dcSBenjamin Berg
726339b84dcSBenjamin Berg break;
72768f5d3f3SJohannes Berg }
72868f5d3f3SJohannes Berg mutex_unlock(&um_pci_mtx);
72968f5d3f3SJohannes Berg
730339b84dcSBenjamin Berg if (i < MAX_DEVICES) {
731339b84dcSBenjamin Berg struct pci_dev *pci_dev;
732339b84dcSBenjamin Berg
733339b84dcSBenjamin Berg pci_dev = pci_get_slot(bridge->bus, i);
734339b84dcSBenjamin Berg if (pci_dev)
735339b84dcSBenjamin Berg pci_stop_and_remove_bus_device_locked(pci_dev);
736339b84dcSBenjamin Berg }
737339b84dcSBenjamin Berg
738339b84dcSBenjamin Berg /* Stop all virtqueues */
739339b84dcSBenjamin Berg virtio_reset_device(vdev);
740339b84dcSBenjamin Berg dev->cmd_vq = NULL;
741339b84dcSBenjamin Berg dev->irq_vq = NULL;
742339b84dcSBenjamin Berg vdev->config->del_vqs(vdev);
74368f5d3f3SJohannes Berg
74468f5d3f3SJohannes Berg kfree(dev);
74568f5d3f3SJohannes Berg }
74668f5d3f3SJohannes Berg
74768f5d3f3SJohannes Berg static struct virtio_device_id id_table[] = {
74868f5d3f3SJohannes Berg { CONFIG_UML_PCI_OVER_VIRTIO_DEVICE_ID, VIRTIO_DEV_ANY_ID },
74968f5d3f3SJohannes Berg { 0 },
75068f5d3f3SJohannes Berg };
75168f5d3f3SJohannes Berg MODULE_DEVICE_TABLE(virtio, id_table);
75268f5d3f3SJohannes Berg
75368f5d3f3SJohannes Berg static struct virtio_driver um_pci_virtio_driver = {
75468f5d3f3SJohannes Berg .driver.name = "virtio-pci",
75568f5d3f3SJohannes Berg .driver.owner = THIS_MODULE,
75668f5d3f3SJohannes Berg .id_table = id_table,
75768f5d3f3SJohannes Berg .probe = um_pci_virtio_probe,
75868f5d3f3SJohannes Berg .remove = um_pci_virtio_remove,
75968f5d3f3SJohannes Berg };
76068f5d3f3SJohannes Berg
76168f5d3f3SJohannes Berg static struct resource virt_cfgspace_resource = {
76268f5d3f3SJohannes Berg .name = "PCI config space",
76368f5d3f3SJohannes Berg .start = 0xf0000000 - MAX_DEVICES * CFG_SPACE_SIZE,
76468f5d3f3SJohannes Berg .end = 0xf0000000 - 1,
76568f5d3f3SJohannes Berg .flags = IORESOURCE_MEM,
76668f5d3f3SJohannes Berg };
76768f5d3f3SJohannes Berg
um_pci_map_cfgspace(unsigned long offset,size_t size,const struct logic_iomem_ops ** ops,void ** priv)76868f5d3f3SJohannes Berg static long um_pci_map_cfgspace(unsigned long offset, size_t size,
76968f5d3f3SJohannes Berg const struct logic_iomem_ops **ops,
77068f5d3f3SJohannes Berg void **priv)
77168f5d3f3SJohannes Berg {
77268f5d3f3SJohannes Berg if (WARN_ON(size > CFG_SPACE_SIZE || offset % CFG_SPACE_SIZE))
77368f5d3f3SJohannes Berg return -EINVAL;
77468f5d3f3SJohannes Berg
77568f5d3f3SJohannes Berg if (offset / CFG_SPACE_SIZE < MAX_DEVICES) {
77668f5d3f3SJohannes Berg *ops = &um_pci_device_cfgspace_ops;
77768f5d3f3SJohannes Berg *priv = &um_pci_devices[offset / CFG_SPACE_SIZE];
77868f5d3f3SJohannes Berg return 0;
77968f5d3f3SJohannes Berg }
78068f5d3f3SJohannes Berg
78168f5d3f3SJohannes Berg WARN(1, "cannot map offset 0x%lx/0x%zx\n", offset, size);
78268f5d3f3SJohannes Berg return -ENOENT;
78368f5d3f3SJohannes Berg }
78468f5d3f3SJohannes Berg
78568f5d3f3SJohannes Berg static const struct logic_iomem_region_ops um_pci_cfgspace_ops = {
78668f5d3f3SJohannes Berg .map = um_pci_map_cfgspace,
78768f5d3f3SJohannes Berg };
78868f5d3f3SJohannes Berg
78968f5d3f3SJohannes Berg static struct resource virt_iomem_resource = {
79068f5d3f3SJohannes Berg .name = "PCI iomem",
79168f5d3f3SJohannes Berg .start = 0xf0000000,
79268f5d3f3SJohannes Berg .end = 0xffffffff,
79368f5d3f3SJohannes Berg .flags = IORESOURCE_MEM,
79468f5d3f3SJohannes Berg };
79568f5d3f3SJohannes Berg
79668f5d3f3SJohannes Berg struct um_pci_map_iomem_data {
79768f5d3f3SJohannes Berg unsigned long offset;
79868f5d3f3SJohannes Berg size_t size;
79968f5d3f3SJohannes Berg const struct logic_iomem_ops **ops;
80068f5d3f3SJohannes Berg void **priv;
80168f5d3f3SJohannes Berg long ret;
80268f5d3f3SJohannes Berg };
80368f5d3f3SJohannes Berg
um_pci_map_iomem_walk(struct pci_dev * pdev,void * _data)80468f5d3f3SJohannes Berg static int um_pci_map_iomem_walk(struct pci_dev *pdev, void *_data)
80568f5d3f3SJohannes Berg {
80668f5d3f3SJohannes Berg struct um_pci_map_iomem_data *data = _data;
80768f5d3f3SJohannes Berg struct um_pci_device_reg *reg = &um_pci_devices[pdev->devfn / 8];
80868f5d3f3SJohannes Berg struct um_pci_device *dev;
80968f5d3f3SJohannes Berg int i;
81068f5d3f3SJohannes Berg
81168f5d3f3SJohannes Berg if (!reg->dev)
81268f5d3f3SJohannes Berg return 0;
81368f5d3f3SJohannes Berg
81468f5d3f3SJohannes Berg for (i = 0; i < ARRAY_SIZE(dev->resptr); i++) {
81568f5d3f3SJohannes Berg struct resource *r = &pdev->resource[i];
81668f5d3f3SJohannes Berg
81768f5d3f3SJohannes Berg if ((r->flags & IORESOURCE_TYPE_BITS) != IORESOURCE_MEM)
81868f5d3f3SJohannes Berg continue;
81968f5d3f3SJohannes Berg
82068f5d3f3SJohannes Berg /*
82168f5d3f3SJohannes Berg * must be the whole or part of the resource,
82268f5d3f3SJohannes Berg * not allowed to only overlap
82368f5d3f3SJohannes Berg */
82468f5d3f3SJohannes Berg if (data->offset < r->start || data->offset > r->end)
82568f5d3f3SJohannes Berg continue;
82668f5d3f3SJohannes Berg if (data->offset + data->size - 1 > r->end)
82768f5d3f3SJohannes Berg continue;
82868f5d3f3SJohannes Berg
82968f5d3f3SJohannes Berg dev = reg->dev;
83068f5d3f3SJohannes Berg *data->ops = &um_pci_device_bar_ops;
83168f5d3f3SJohannes Berg dev->resptr[i] = i;
83268f5d3f3SJohannes Berg *data->priv = &dev->resptr[i];
83368f5d3f3SJohannes Berg data->ret = data->offset - r->start;
83468f5d3f3SJohannes Berg
83568f5d3f3SJohannes Berg /* no need to continue */
83668f5d3f3SJohannes Berg return 1;
83768f5d3f3SJohannes Berg }
83868f5d3f3SJohannes Berg
83968f5d3f3SJohannes Berg return 0;
84068f5d3f3SJohannes Berg }
84168f5d3f3SJohannes Berg
um_pci_map_iomem(unsigned long offset,size_t size,const struct logic_iomem_ops ** ops,void ** priv)84268f5d3f3SJohannes Berg static long um_pci_map_iomem(unsigned long offset, size_t size,
84368f5d3f3SJohannes Berg const struct logic_iomem_ops **ops,
84468f5d3f3SJohannes Berg void **priv)
84568f5d3f3SJohannes Berg {
84668f5d3f3SJohannes Berg struct um_pci_map_iomem_data data = {
84768f5d3f3SJohannes Berg /* we want the full address here */
84868f5d3f3SJohannes Berg .offset = offset + virt_iomem_resource.start,
84968f5d3f3SJohannes Berg .size = size,
85068f5d3f3SJohannes Berg .ops = ops,
85168f5d3f3SJohannes Berg .priv = priv,
85268f5d3f3SJohannes Berg .ret = -ENOENT,
85368f5d3f3SJohannes Berg };
85468f5d3f3SJohannes Berg
85568f5d3f3SJohannes Berg pci_walk_bus(bridge->bus, um_pci_map_iomem_walk, &data);
85668f5d3f3SJohannes Berg return data.ret;
85768f5d3f3SJohannes Berg }
85868f5d3f3SJohannes Berg
85968f5d3f3SJohannes Berg static const struct logic_iomem_region_ops um_pci_iomem_ops = {
86068f5d3f3SJohannes Berg .map = um_pci_map_iomem,
86168f5d3f3SJohannes Berg };
86268f5d3f3SJohannes Berg
um_pci_compose_msi_msg(struct irq_data * data,struct msi_msg * msg)86368f5d3f3SJohannes Berg static void um_pci_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
86468f5d3f3SJohannes Berg {
86568f5d3f3SJohannes Berg /*
86668f5d3f3SJohannes Berg * This is a very low address and not actually valid 'physical' memory
86768f5d3f3SJohannes Berg * in UML, so we can simply map MSI(-X) vectors to there, it cannot be
86868f5d3f3SJohannes Berg * legitimately written to by the device in any other way.
86968f5d3f3SJohannes Berg * We use the (virtual) IRQ number here as the message to simplify the
87068f5d3f3SJohannes Berg * code that receives the message, where for now we simply trust the
87168f5d3f3SJohannes Berg * device to send the correct message.
87268f5d3f3SJohannes Berg */
87368f5d3f3SJohannes Berg msg->address_hi = 0;
87468f5d3f3SJohannes Berg msg->address_lo = 0xa0000;
87568f5d3f3SJohannes Berg msg->data = data->irq;
87668f5d3f3SJohannes Berg }
87768f5d3f3SJohannes Berg
87868f5d3f3SJohannes Berg static struct irq_chip um_pci_msi_bottom_irq_chip = {
87968f5d3f3SJohannes Berg .name = "UM virtio MSI",
88068f5d3f3SJohannes Berg .irq_compose_msi_msg = um_pci_compose_msi_msg,
88168f5d3f3SJohannes Berg };
88268f5d3f3SJohannes Berg
um_pci_inner_domain_alloc(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs,void * args)88368f5d3f3SJohannes Berg static int um_pci_inner_domain_alloc(struct irq_domain *domain,
88468f5d3f3SJohannes Berg unsigned int virq, unsigned int nr_irqs,
88568f5d3f3SJohannes Berg void *args)
88668f5d3f3SJohannes Berg {
88768f5d3f3SJohannes Berg unsigned long bit;
88868f5d3f3SJohannes Berg
88968f5d3f3SJohannes Berg WARN_ON(nr_irqs != 1);
89068f5d3f3SJohannes Berg
89168f5d3f3SJohannes Berg mutex_lock(&um_pci_mtx);
89268f5d3f3SJohannes Berg bit = find_first_zero_bit(um_pci_msi_used, MAX_MSI_VECTORS);
89368f5d3f3SJohannes Berg if (bit >= MAX_MSI_VECTORS) {
89468f5d3f3SJohannes Berg mutex_unlock(&um_pci_mtx);
89568f5d3f3SJohannes Berg return -ENOSPC;
89668f5d3f3SJohannes Berg }
89768f5d3f3SJohannes Berg
89868f5d3f3SJohannes Berg set_bit(bit, um_pci_msi_used);
89968f5d3f3SJohannes Berg mutex_unlock(&um_pci_mtx);
90068f5d3f3SJohannes Berg
90168f5d3f3SJohannes Berg irq_domain_set_info(domain, virq, bit, &um_pci_msi_bottom_irq_chip,
90268f5d3f3SJohannes Berg domain->host_data, handle_simple_irq,
90368f5d3f3SJohannes Berg NULL, NULL);
90468f5d3f3SJohannes Berg
90568f5d3f3SJohannes Berg return 0;
90668f5d3f3SJohannes Berg }
90768f5d3f3SJohannes Berg
um_pci_inner_domain_free(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs)90868f5d3f3SJohannes Berg static void um_pci_inner_domain_free(struct irq_domain *domain,
90968f5d3f3SJohannes Berg unsigned int virq, unsigned int nr_irqs)
91068f5d3f3SJohannes Berg {
91168f5d3f3SJohannes Berg struct irq_data *d = irq_domain_get_irq_data(domain, virq);
91268f5d3f3SJohannes Berg
91368f5d3f3SJohannes Berg mutex_lock(&um_pci_mtx);
91468f5d3f3SJohannes Berg
91568f5d3f3SJohannes Berg if (!test_bit(d->hwirq, um_pci_msi_used))
91668f5d3f3SJohannes Berg pr_err("trying to free unused MSI#%lu\n", d->hwirq);
91768f5d3f3SJohannes Berg else
91868f5d3f3SJohannes Berg __clear_bit(d->hwirq, um_pci_msi_used);
91968f5d3f3SJohannes Berg
92068f5d3f3SJohannes Berg mutex_unlock(&um_pci_mtx);
92168f5d3f3SJohannes Berg }
92268f5d3f3SJohannes Berg
92368f5d3f3SJohannes Berg static const struct irq_domain_ops um_pci_inner_domain_ops = {
92468f5d3f3SJohannes Berg .alloc = um_pci_inner_domain_alloc,
92568f5d3f3SJohannes Berg .free = um_pci_inner_domain_free,
92668f5d3f3SJohannes Berg };
92768f5d3f3SJohannes Berg
92868f5d3f3SJohannes Berg static struct irq_chip um_pci_msi_irq_chip = {
92968f5d3f3SJohannes Berg .name = "UM virtio PCIe MSI",
93068f5d3f3SJohannes Berg .irq_mask = pci_msi_mask_irq,
93168f5d3f3SJohannes Berg .irq_unmask = pci_msi_unmask_irq,
93268f5d3f3SJohannes Berg };
93368f5d3f3SJohannes Berg
93468f5d3f3SJohannes Berg static struct msi_domain_info um_pci_msi_domain_info = {
93568f5d3f3SJohannes Berg .flags = MSI_FLAG_USE_DEF_DOM_OPS |
93668f5d3f3SJohannes Berg MSI_FLAG_USE_DEF_CHIP_OPS |
93768f5d3f3SJohannes Berg MSI_FLAG_PCI_MSIX,
93868f5d3f3SJohannes Berg .chip = &um_pci_msi_irq_chip,
93968f5d3f3SJohannes Berg };
94068f5d3f3SJohannes Berg
94168f5d3f3SJohannes Berg static struct resource busn_resource = {
94268f5d3f3SJohannes Berg .name = "PCI busn",
94368f5d3f3SJohannes Berg .start = 0,
94468f5d3f3SJohannes Berg .end = 0,
94568f5d3f3SJohannes Berg .flags = IORESOURCE_BUS,
94668f5d3f3SJohannes Berg };
94768f5d3f3SJohannes Berg
um_pci_map_irq(const struct pci_dev * pdev,u8 slot,u8 pin)94868f5d3f3SJohannes Berg static int um_pci_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
94968f5d3f3SJohannes Berg {
95068f5d3f3SJohannes Berg struct um_pci_device_reg *reg = &um_pci_devices[pdev->devfn / 8];
95168f5d3f3SJohannes Berg
95268f5d3f3SJohannes Berg if (WARN_ON(!reg->dev))
95368f5d3f3SJohannes Berg return -EINVAL;
95468f5d3f3SJohannes Berg
95568f5d3f3SJohannes Berg /* Yes, we map all pins to the same IRQ ... doesn't matter for now. */
95668f5d3f3SJohannes Berg return reg->dev->irq;
95768f5d3f3SJohannes Berg }
95868f5d3f3SJohannes Berg
pci_root_bus_fwnode(struct pci_bus * bus)95968f5d3f3SJohannes Berg void *pci_root_bus_fwnode(struct pci_bus *bus)
96068f5d3f3SJohannes Berg {
96168f5d3f3SJohannes Berg return um_pci_fwnode;
96268f5d3f3SJohannes Berg }
96368f5d3f3SJohannes Berg
um_pci_map_platform(unsigned long offset,size_t size,const struct logic_iomem_ops ** ops,void ** priv)964522c532cSVincent Whitchurch static long um_pci_map_platform(unsigned long offset, size_t size,
965522c532cSVincent Whitchurch const struct logic_iomem_ops **ops,
966522c532cSVincent Whitchurch void **priv)
967522c532cSVincent Whitchurch {
968522c532cSVincent Whitchurch if (!um_pci_platform_device)
969522c532cSVincent Whitchurch return -ENOENT;
970522c532cSVincent Whitchurch
971522c532cSVincent Whitchurch *ops = &um_pci_device_bar_ops;
972522c532cSVincent Whitchurch *priv = &um_pci_platform_device->resptr[0];
973522c532cSVincent Whitchurch
974*e111d231SVincent Whitchurch return offset;
975522c532cSVincent Whitchurch }
976522c532cSVincent Whitchurch
977522c532cSVincent Whitchurch static const struct logic_iomem_region_ops um_pci_platform_ops = {
978522c532cSVincent Whitchurch .map = um_pci_map_platform,
979522c532cSVincent Whitchurch };
980522c532cSVincent Whitchurch
981522c532cSVincent Whitchurch static struct resource virt_platform_resource = {
982522c532cSVincent Whitchurch .name = "platform",
983522c532cSVincent Whitchurch .start = 0x10000000,
984522c532cSVincent Whitchurch .end = 0x1fffffff,
985522c532cSVincent Whitchurch .flags = IORESOURCE_MEM,
986522c532cSVincent Whitchurch };
987522c532cSVincent Whitchurch
um_pci_init(void)98898639412SXiu Jianfeng static int __init um_pci_init(void)
98968f5d3f3SJohannes Berg {
99068f5d3f3SJohannes Berg int err, i;
99168f5d3f3SJohannes Berg
99268f5d3f3SJohannes Berg WARN_ON(logic_iomem_add_region(&virt_cfgspace_resource,
99368f5d3f3SJohannes Berg &um_pci_cfgspace_ops));
99468f5d3f3SJohannes Berg WARN_ON(logic_iomem_add_region(&virt_iomem_resource,
99568f5d3f3SJohannes Berg &um_pci_iomem_ops));
996522c532cSVincent Whitchurch WARN_ON(logic_iomem_add_region(&virt_platform_resource,
997522c532cSVincent Whitchurch &um_pci_platform_ops));
99868f5d3f3SJohannes Berg
99968f5d3f3SJohannes Berg if (WARN(CONFIG_UML_PCI_OVER_VIRTIO_DEVICE_ID < 0,
100068f5d3f3SJohannes Berg "No virtio device ID configured for PCI - no PCI support\n"))
100168f5d3f3SJohannes Berg return 0;
100268f5d3f3SJohannes Berg
10034a22c4ceSJohannes Berg um_pci_msg_bufs = alloc_percpu(struct um_pci_message_buffer);
10044a22c4ceSJohannes Berg if (!um_pci_msg_bufs)
100568f5d3f3SJohannes Berg return -ENOMEM;
100668f5d3f3SJohannes Berg
10074a22c4ceSJohannes Berg bridge = pci_alloc_host_bridge(0);
10084a22c4ceSJohannes Berg if (!bridge) {
10094a22c4ceSJohannes Berg err = -ENOMEM;
10104a22c4ceSJohannes Berg goto free;
10114a22c4ceSJohannes Berg }
10124a22c4ceSJohannes Berg
101368f5d3f3SJohannes Berg um_pci_fwnode = irq_domain_alloc_named_fwnode("um-pci");
101468f5d3f3SJohannes Berg if (!um_pci_fwnode) {
101568f5d3f3SJohannes Berg err = -ENOMEM;
101668f5d3f3SJohannes Berg goto free;
101768f5d3f3SJohannes Berg }
101868f5d3f3SJohannes Berg
101968f5d3f3SJohannes Berg um_pci_inner_domain = __irq_domain_add(um_pci_fwnode, MAX_MSI_VECTORS,
102068f5d3f3SJohannes Berg MAX_MSI_VECTORS, 0,
102168f5d3f3SJohannes Berg &um_pci_inner_domain_ops, NULL);
102268f5d3f3SJohannes Berg if (!um_pci_inner_domain) {
102368f5d3f3SJohannes Berg err = -ENOMEM;
102468f5d3f3SJohannes Berg goto free;
102568f5d3f3SJohannes Berg }
102668f5d3f3SJohannes Berg
102768f5d3f3SJohannes Berg um_pci_msi_domain = pci_msi_create_irq_domain(um_pci_fwnode,
102868f5d3f3SJohannes Berg &um_pci_msi_domain_info,
102968f5d3f3SJohannes Berg um_pci_inner_domain);
103068f5d3f3SJohannes Berg if (!um_pci_msi_domain) {
103168f5d3f3SJohannes Berg err = -ENOMEM;
103268f5d3f3SJohannes Berg goto free;
103368f5d3f3SJohannes Berg }
103468f5d3f3SJohannes Berg
103568f5d3f3SJohannes Berg pci_add_resource(&bridge->windows, &virt_iomem_resource);
103668f5d3f3SJohannes Berg pci_add_resource(&bridge->windows, &busn_resource);
103768f5d3f3SJohannes Berg bridge->ops = &um_pci_ops;
103868f5d3f3SJohannes Berg bridge->map_irq = um_pci_map_irq;
103968f5d3f3SJohannes Berg
104068f5d3f3SJohannes Berg for (i = 0; i < MAX_DEVICES; i++) {
104168f5d3f3SJohannes Berg resource_size_t start;
104268f5d3f3SJohannes Berg
104368f5d3f3SJohannes Berg start = virt_cfgspace_resource.start + i * CFG_SPACE_SIZE;
104468f5d3f3SJohannes Berg um_pci_devices[i].iomem = ioremap(start, CFG_SPACE_SIZE);
104568f5d3f3SJohannes Berg if (WARN(!um_pci_devices[i].iomem, "failed to map %d\n", i)) {
104668f5d3f3SJohannes Berg err = -ENOMEM;
104768f5d3f3SJohannes Berg goto free;
104868f5d3f3SJohannes Berg }
104968f5d3f3SJohannes Berg }
105068f5d3f3SJohannes Berg
105168f5d3f3SJohannes Berg err = pci_host_probe(bridge);
105268f5d3f3SJohannes Berg if (err)
105368f5d3f3SJohannes Berg goto free;
105468f5d3f3SJohannes Berg
105568f5d3f3SJohannes Berg err = register_virtio_driver(&um_pci_virtio_driver);
105668f5d3f3SJohannes Berg if (err)
105768f5d3f3SJohannes Berg goto free;
105868f5d3f3SJohannes Berg return 0;
105968f5d3f3SJohannes Berg free:
106068f5d3f3SJohannes Berg if (um_pci_inner_domain)
106168f5d3f3SJohannes Berg irq_domain_remove(um_pci_inner_domain);
106268f5d3f3SJohannes Berg if (um_pci_fwnode)
106368f5d3f3SJohannes Berg irq_domain_free_fwnode(um_pci_fwnode);
10644a22c4ceSJohannes Berg if (bridge) {
106568f5d3f3SJohannes Berg pci_free_resource_list(&bridge->windows);
106668f5d3f3SJohannes Berg pci_free_host_bridge(bridge);
10674a22c4ceSJohannes Berg }
10684a22c4ceSJohannes Berg free_percpu(um_pci_msg_bufs);
106968f5d3f3SJohannes Berg return err;
107068f5d3f3SJohannes Berg }
107168f5d3f3SJohannes Berg module_init(um_pci_init);
107268f5d3f3SJohannes Berg
um_pci_exit(void)107398639412SXiu Jianfeng static void __exit um_pci_exit(void)
107468f5d3f3SJohannes Berg {
107568f5d3f3SJohannes Berg unregister_virtio_driver(&um_pci_virtio_driver);
107668f5d3f3SJohannes Berg irq_domain_remove(um_pci_msi_domain);
107768f5d3f3SJohannes Berg irq_domain_remove(um_pci_inner_domain);
107868f5d3f3SJohannes Berg pci_free_resource_list(&bridge->windows);
107968f5d3f3SJohannes Berg pci_free_host_bridge(bridge);
10804a22c4ceSJohannes Berg free_percpu(um_pci_msg_bufs);
108168f5d3f3SJohannes Berg }
108268f5d3f3SJohannes Berg module_exit(um_pci_exit);
1083