Add layer to support Jailhouse hypervisor
[AGL/meta-agl-devel.git] / meta-agl-jailhouse / recipes-kernel / linux / linux / 0012-uio-Add-driver-for-inter-VM-shared-memory-device.patch
1 From 205cdad2dc9fc8a6a7204b2a71408b43085dd45f Mon Sep 17 00:00:00 2001
2 From: Jan Kiszka <jan.kiszka@siemens.com>
3 Date: Tue, 4 Jun 2019 18:40:25 +0200
4 Subject: [PATCH 12/32] uio: Add driver for inter-VM shared memory device
5
6 This adds a UIO driver the ivshmem device, found in QEMU and the
7 Jailhouse hypervisor. It exposes the MMIO register region and all shared
8 memory section to userspace. Interrupts are configured in one-shot mode
9 so that userspace needs to re-enable them after each event via the
10 Interrupt Control register. The driver registers all possible MSI-X
11 vectors, coalescing them into the single notifier UIO provides.
12
13 Note: Specification work for the interface is ongoing, so details may
14 still change.
15
16 Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
17 ---
18  drivers/uio/Kconfig       |   7 ++
19  drivers/uio/Makefile      |   1 +
20  drivers/uio/uio_ivshmem.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++
21  include/linux/pci_ids.h   |   1 +
22  4 files changed, 250 insertions(+)
23  create mode 100644 drivers/uio/uio_ivshmem.c
24
25 diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
26 index 202ee81cfc2b..a130500f46b8 100644
27 --- a/drivers/uio/Kconfig
28 +++ b/drivers/uio/Kconfig
29 @@ -165,4 +165,11 @@ config UIO_HV_GENERIC
30           to network and storage devices from userspace.
31  
32           If you compile this as a module, it will be called uio_hv_generic.
33 +
34 +config UIO_IVSHMEM
35 +       tristate "Inter-VM Shared Memory driver"
36 +       depends on PCI
37 +       help
38 +         Userspace I/O driver for the inter-VM shared memory PCI device
39 +         as provided by QEMU and the Jailhouse hypervisor.
40  endif
41 diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile
42 index c285dd2a4539..3911fefb2a7e 100644
43 --- a/drivers/uio/Makefile
44 +++ b/drivers/uio/Makefile
45 @@ -11,3 +11,4 @@ obj-$(CONFIG_UIO_PRUSS)         += uio_pruss.o
46  obj-$(CONFIG_UIO_MF624)         += uio_mf624.o
47  obj-$(CONFIG_UIO_FSL_ELBC_GPCM)        += uio_fsl_elbc_gpcm.o
48  obj-$(CONFIG_UIO_HV_GENERIC)   += uio_hv_generic.o
49 +obj-$(CONFIG_UIO_IVSHMEM)      += uio_ivshmem.o
50 diff --git a/drivers/uio/uio_ivshmem.c b/drivers/uio/uio_ivshmem.c
51 new file mode 100644
52 index 000000000000..0c16d428c6ed
53 --- /dev/null
54 +++ b/drivers/uio/uio_ivshmem.c
55 @@ -0,0 +1,241 @@
56 +// SPDX-License-Identifier: GPL-2.0
57 +/*
58 + * UIO driver for Inter-VM shared memory PCI device
59 + *
60 + * Copyright (c) Siemens AG, 2019
61 + *
62 + * Authors:
63 + *  Jan Kiszka <jan.kiszka@siemens.com>
64 + */
65 +
66 +#include <linux/ivshmem.h>
67 +#include <linux/module.h>
68 +#include <linux/pci.h>
69 +#include <linux/uio_driver.h>
70 +
71 +#define DRV_NAME "uio_ivshmem"
72 +
73 +struct ivshm_dev {
74 +       struct uio_info info;
75 +       struct pci_dev *pdev;
76 +       struct ivshm_regs __iomem *regs;
77 +       int vectors;
78 +};
79 +
80 +static irqreturn_t ivshm_irq_handler(int irq, void *dev_id)
81 +{
82 +       struct ivshm_dev *ivshm_dev = (struct ivshm_dev *)dev_id;
83 +
84 +       /* nothing else to do, we configured one-shot interrupt mode */
85 +       uio_event_notify(&ivshm_dev->info);
86 +
87 +       return IRQ_HANDLED;
88 +}
89 +
90 +static u64 get_config_qword(struct pci_dev *pdev, unsigned int pos)
91 +{
92 +       u32 lo, hi;
93 +
94 +       pci_read_config_dword(pdev, pos, &lo);
95 +       pci_read_config_dword(pdev, pos + 4, &hi);
96 +       return lo | ((u64)hi << 32);
97 +}
98 +
99 +static int ivshm_release(struct uio_info *info, struct inode *inode)
100 +{
101 +       struct ivshm_dev *ivshm_dev =
102 +               container_of(info, struct ivshm_dev, info);
103 +
104 +       writel(0, &ivshm_dev->regs->state);
105 +       return 0;
106 +}
107 +
108 +static int ivshm_probe(struct pci_dev *pdev, const struct pci_device_id *id)
109 +{
110 +       resource_size_t rw_section_sz, output_section_sz;
111 +       struct ivshm_dev *ivshm_dev;
112 +       phys_addr_t section_addr;
113 +       int err, vendor_cap, i;
114 +       unsigned int cap_pos;
115 +       struct uio_mem *mem;
116 +       char *device_name;
117 +       u32 dword;
118 +
119 +       ivshm_dev = devm_kzalloc(&pdev->dev, sizeof(struct ivshm_dev),
120 +                                GFP_KERNEL);
121 +       if (!ivshm_dev)
122 +               return -ENOMEM;
123 +
124 +       err = pcim_enable_device(pdev);
125 +       if (err)
126 +               return err;
127 +
128 +       device_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s[%s]", DRV_NAME,
129 +                                    dev_name(&pdev->dev));
130 +       if (!device_name)
131 +               return -ENOMEM;
132 +
133 +       ivshm_dev->info.name = device_name;
134 +       ivshm_dev->info.version = "1";
135 +       ivshm_dev->info.release = ivshm_release;
136 +
137 +       err = pcim_iomap_regions(pdev, BIT(0), device_name);
138 +       if (err)
139 +               return err;
140 +       ivshm_dev->regs = pcim_iomap_table(pdev)[0];
141 +
142 +       mem = &ivshm_dev->info.mem[0];
143 +
144 +       mem->name = "registers";
145 +       mem->addr = pci_resource_start(pdev, 0);
146 +       if (!mem->addr)
147 +               return -ENODEV;
148 +       mem->size = pci_resource_len(pdev, 0);
149 +       mem->memtype = UIO_MEM_PHYS;
150 +
151 +       vendor_cap = pci_find_capability(pdev, PCI_CAP_ID_VNDR);
152 +       if (vendor_cap < 0)
153 +               return -ENODEV;
154 +
155 +       if (pci_resource_len(pdev, 2) > 0) {
156 +               section_addr = pci_resource_start(pdev, 2);
157 +       } else {
158 +               cap_pos = vendor_cap + IVSHM_CFG_ADDRESS;
159 +               section_addr = get_config_qword(pdev, cap_pos);
160 +       }
161 +
162 +       mem++;
163 +       mem->name = "state_table";
164 +       mem->addr = section_addr;
165 +       cap_pos = vendor_cap + IVSHM_CFG_STATE_TAB_SZ;
166 +       pci_read_config_dword(pdev, cap_pos, &dword);
167 +       mem->size = dword;
168 +       mem->memtype = UIO_MEM_IOVA;
169 +       mem->readonly = true;
170 +       if (!devm_request_mem_region(&pdev->dev, mem->addr, mem->size,
171 +                                    device_name))
172 +               return -EBUSY;
173 +       dev_info(&pdev->dev, "%s at %pa, size %pa\n", mem->name, &mem->addr,
174 +                &mem->size);
175 +
176 +       cap_pos = vendor_cap + IVSHM_CFG_RW_SECTION_SZ;
177 +       rw_section_sz = get_config_qword(pdev, cap_pos);
178 +       if (rw_section_sz > 0) {
179 +               section_addr += mem->size;
180 +
181 +               mem++;
182 +               mem->name = "rw_section";
183 +               mem->addr = section_addr;
184 +               mem->size = rw_section_sz;
185 +               mem->memtype = UIO_MEM_IOVA;
186 +               if (!devm_request_mem_region(&pdev->dev, mem->addr, mem->size,
187 +                                            device_name))
188 +                       return -EBUSY;
189 +               dev_info(&pdev->dev, "%s at %pa, size %pa\n", mem->name,
190 +                        &mem->addr, &mem->size);
191 +       }
192 +
193 +       cap_pos = vendor_cap + IVSHM_CFG_OUTPUT_SECTION_SZ;
194 +       output_section_sz = get_config_qword(pdev, cap_pos);
195 +       if (output_section_sz > 0) {
196 +               section_addr += mem->size;
197 +
198 +               mem++;
199 +               mem->name = "input_sections";
200 +               mem->addr = section_addr;
201 +               mem->size =
202 +                       readl(&ivshm_dev->regs->max_peers) * output_section_sz;
203 +               mem->memtype = UIO_MEM_IOVA;
204 +               mem->readonly = true;
205 +               if (!devm_request_mem_region(&pdev->dev, mem->addr, mem->size,
206 +                                            device_name))
207 +                       return -EBUSY;
208 +               dev_info(&pdev->dev, "%s at %pa, size %pa\n", mem->name,
209 +                        &mem->addr, &mem->size);
210 +
211 +               mem++;
212 +               mem->name = "output_section";
213 +               mem->addr = section_addr +
214 +                       readl(&ivshm_dev->regs->id) * output_section_sz;
215 +               mem->size = output_section_sz;
216 +               mem->memtype = UIO_MEM_IOVA;
217 +               dev_info(&pdev->dev, "%s at %pa, size %pa\n", mem->name,
218 +                        &mem->addr, &mem->size);
219 +       }
220 +
221 +       pci_write_config_byte(pdev, vendor_cap + IVSHM_CFG_PRIV_CNTL,
222 +                             IVSHM_PRIV_CNTL_ONESHOT_INT);
223 +
224 +       /*
225 +        * Grab all vectors although we can only coalesce them into a single
226 +        * notifier. This avoids missing any event.
227 +        */
228 +       ivshm_dev->vectors = pci_msix_vec_count(pdev);
229 +       if (ivshm_dev->vectors < 0)
230 +               ivshm_dev->vectors = 1;
231 +
232 +       err = pci_alloc_irq_vectors(pdev, ivshm_dev->vectors,
233 +                                   ivshm_dev->vectors,
234 +                                   PCI_IRQ_LEGACY | PCI_IRQ_MSIX);
235 +       if (err < 0)
236 +               return err;
237 +
238 +       for (i = 0; i < ivshm_dev->vectors; i++) {
239 +               err = request_irq(pci_irq_vector(pdev, i), ivshm_irq_handler,
240 +                                 IRQF_SHARED, ivshm_dev->info.name, ivshm_dev);
241 +               if (err)
242 +                       goto error;
243 +       }
244 +
245 +       ivshm_dev->info.irq = UIO_IRQ_CUSTOM;
246 +
247 +       err = uio_register_device(&pdev->dev, &ivshm_dev->info);
248 +       if (err)
249 +               goto error;
250 +
251 +       pci_set_master(pdev);
252 +
253 +       pci_set_drvdata(pdev, ivshm_dev);
254 +
255 +       return 0;
256 +
257 +error:
258 +       while (--i > 0)
259 +               free_irq(pci_irq_vector(pdev, i), ivshm_dev);
260 +       pci_free_irq_vectors(pdev);
261 +       return err;
262 +}
263 +
264 +static void ivshm_remove(struct pci_dev *pdev)
265 +{
266 +       struct ivshm_dev *ivshm_dev = pci_get_drvdata(pdev);
267 +       int i;
268 +
269 +       writel(0, &ivshm_dev->regs->int_control);
270 +       pci_clear_master(pdev);
271 +
272 +       uio_unregister_device(&ivshm_dev->info);
273 +
274 +       for (i = 0; i < ivshm_dev->vectors; i++)
275 +               free_irq(pci_irq_vector(pdev, i), ivshm_dev);
276 +
277 +       pci_free_irq_vectors(pdev);
278 +}
279 +
280 +static const struct pci_device_id ivshm_device_id_table[] = {
281 +       { PCI_DEVICE(PCI_VENDOR_ID_SIEMENS, PCI_DEVICE_ID_IVSHMEM),
282 +         (PCI_CLASS_OTHERS << 16) | IVSHM_PROTO_UNDEFINED, 0xffffff },
283 +       { 0 }
284 +};
285 +MODULE_DEVICE_TABLE(pci, ivshm_device_id_table);
286 +
287 +static struct pci_driver uio_ivshm_driver = {
288 +       .name = DRV_NAME,
289 +       .id_table = ivshm_device_id_table,
290 +       .probe = ivshm_probe,
291 +       .remove = ivshm_remove,
292 +};
293 +module_pci_driver(uio_ivshm_driver);
294 +
295 +MODULE_AUTHOR("Jan Kiszka <jan.kiszka@siemens.com>");
296 +MODULE_LICENSE("GPL v2");
297 diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
298 index 21a572469a4e..e450458c8ba8 100644
299 --- a/include/linux/pci_ids.h
300 +++ b/include/linux/pci_ids.h
301 @@ -1477,6 +1477,7 @@
302  
303  #define PCI_VENDOR_ID_SIEMENS           0x110A
304  #define PCI_DEVICE_ID_SIEMENS_DSCC4     0x2102
305 +#define PCI_DEVICE_ID_IVSHMEM          0x4106
306  
307  #define PCI_VENDOR_ID_VORTEX           0x1119
308  #define PCI_DEVICE_ID_VORTEX_GDT60x0   0x0000
309 -- 
310 2.11.0
311