2 * VFIO PCI I/O Port & MMIO access
4 * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
5 * Author: Alex Williamson <alex.williamson@redhat.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * Derived from original vfio:
12 * Copyright 2010 Cisco Systems, Inc. All rights reserved.
13 * Author: Tom Lyon, pugs@cisco.com
17 #include <linux/pci.h>
18 #include <linux/uaccess.h>
20 #include <linux/vgaarb.h>
22 #include "vfio_pci_private.h"
25 * Read or write from an __iomem region (MMIO or I/O port) with an excluded
26 * range which is inaccessible. The excluded range drops writes and fills
27 * reads with -1. This is intended for handling MSI-X vector tables and
28 * leftover space for ROM BARs.
30 static ssize_t
do_io_rw(void __iomem
*io
, char __user
*buf
,
31 loff_t off
, size_t count
, size_t x_start
,
32 size_t x_end
, bool iswrite
)
37 size_t fillable
, filled
;
40 fillable
= min(count
, (size_t)(x_start
- off
));
41 else if (off
>= x_end
)
46 if (fillable
>= 4 && !(off
% 4)) {
50 if (copy_from_user(&val
, buf
, 4))
53 iowrite32(le32_to_cpu(val
), io
+ off
);
55 val
= cpu_to_le32(ioread32(io
+ off
));
57 if (copy_to_user(buf
, &val
, 4))
62 } else if (fillable
>= 2 && !(off
% 2)) {
66 if (copy_from_user(&val
, buf
, 2))
69 iowrite16(le16_to_cpu(val
), io
+ off
);
71 val
= cpu_to_le16(ioread16(io
+ off
));
73 if (copy_to_user(buf
, &val
, 2))
78 } else if (fillable
) {
82 if (copy_from_user(&val
, buf
, 1))
85 iowrite8(val
, io
+ off
);
87 val
= ioread8(io
+ off
);
89 if (copy_to_user(buf
, &val
, 1))
95 /* Fill reads with -1, drop writes */
96 filled
= min(count
, (size_t)(x_end
- off
));
101 for (i
= 0; i
< filled
; i
++)
102 if (copy_to_user(buf
+ i
, &val
, 1))
116 ssize_t
vfio_pci_bar_rw(struct vfio_pci_device
*vdev
, char __user
*buf
,
117 size_t count
, loff_t
*ppos
, bool iswrite
)
119 struct pci_dev
*pdev
= vdev
->pdev
;
120 loff_t pos
= *ppos
& VFIO_PCI_OFFSET_MASK
;
121 int bar
= VFIO_PCI_OFFSET_TO_INDEX(*ppos
);
122 size_t x_start
= 0, x_end
= 0;
127 if (pci_resource_start(pdev
, bar
))
128 end
= pci_resource_len(pdev
, bar
);
129 else if (bar
== PCI_ROM_RESOURCE
&&
130 pdev
->resource
[bar
].flags
& IORESOURCE_ROM_SHADOW
)
138 count
= min(count
, (size_t)(end
- pos
));
140 if (bar
== PCI_ROM_RESOURCE
) {
142 * The ROM can fill less space than the BAR, so we start the
143 * excluded range at the end of the actual ROM. This makes
144 * filling large ROM BARs much faster.
146 io
= pci_map_rom(pdev
, &x_start
);
150 } else if (!vdev
->barmap
[bar
]) {
153 ret
= pci_request_selected_regions(pdev
, 1 << bar
, "vfio");
157 io
= pci_iomap(pdev
, bar
, 0);
159 pci_release_selected_regions(pdev
, 1 << bar
);
163 vdev
->barmap
[bar
] = io
;
165 io
= vdev
->barmap
[bar
];
167 if (bar
== vdev
->msix_bar
) {
168 x_start
= vdev
->msix_offset
;
169 x_end
= vdev
->msix_offset
+ vdev
->msix_size
;
172 done
= do_io_rw(io
, buf
, pos
, count
, x_start
, x_end
, iswrite
);
177 if (bar
== PCI_ROM_RESOURCE
)
178 pci_unmap_rom(pdev
, io
);
183 ssize_t
vfio_pci_vga_rw(struct vfio_pci_device
*vdev
, char __user
*buf
,
184 size_t count
, loff_t
*ppos
, bool iswrite
)
187 loff_t off
, pos
= *ppos
& VFIO_PCI_OFFSET_MASK
;
188 void __iomem
*iomem
= NULL
;
200 case 0xa0000 ... 0xbffff:
201 count
= min(count
, (size_t)(0xc0000 - pos
));
202 iomem
= ioremap_nocache(0xa0000, 0xbffff - 0xa0000 + 1);
204 rsrc
= VGA_RSRC_LEGACY_MEM
;
207 case 0x3b0 ... 0x3bb:
208 count
= min(count
, (size_t)(0x3bc - pos
));
209 iomem
= ioport_map(0x3b0, 0x3bb - 0x3b0 + 1);
211 rsrc
= VGA_RSRC_LEGACY_IO
;
214 case 0x3c0 ... 0x3df:
215 count
= min(count
, (size_t)(0x3e0 - pos
));
216 iomem
= ioport_map(0x3c0, 0x3df - 0x3c0 + 1);
218 rsrc
= VGA_RSRC_LEGACY_IO
;
228 ret
= vga_get_interruptible(vdev
->pdev
, rsrc
);
230 is_ioport
? ioport_unmap(iomem
) : iounmap(iomem
);
234 done
= do_io_rw(iomem
, buf
, off
, count
, 0, 0, iswrite
);
236 vga_put(vdev
->pdev
, rsrc
);
238 is_ioport
? ioport_unmap(iomem
) : iounmap(iomem
);