1 /* 2 * VFIO PCI I/O Port & MMIO access 3 * 4 * Copyright (C) 2012 Red Hat, Inc. All rights reserved. 5 * Author: Alex Williamson <alex.williamson@redhat.com> 6 * 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. 10 * 11 * Derived from original vfio: 12 * Copyright 2010 Cisco Systems, Inc. All rights reserved. 13 * Author: Tom Lyon, pugs@cisco.com 14 */ 15 16 #include <linux/fs.h> 17 #include <linux/pci.h> 18 #include <linux/uaccess.h> 19 #include <linux/io.h> 20 21 #include "vfio_pci_private.h" 22 23 /* I/O Port BAR access */ 24 ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, char __user *buf, 25 size_t count, loff_t *ppos, bool iswrite) 26 { 27 struct pci_dev *pdev = vdev->pdev; 28 loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; 29 int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); 30 void __iomem *io; 31 size_t done = 0; 32 33 if (!pci_resource_start(pdev, bar)) 34 return -EINVAL; 35 36 if (pos + count > pci_resource_len(pdev, bar)) 37 return -EINVAL; 38 39 if (!vdev->barmap[bar]) { 40 int ret; 41 42 ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); 43 if (ret) 44 return ret; 45 46 vdev->barmap[bar] = pci_iomap(pdev, bar, 0); 47 48 if (!vdev->barmap[bar]) { 49 pci_release_selected_regions(pdev, 1 << bar); 50 return -EINVAL; 51 } 52 } 53 54 io = vdev->barmap[bar]; 55 56 while (count) { 57 int filled; 58 59 if (count >= 3 && !(pos % 4)) { 60 __le32 val; 61 62 if (iswrite) { 63 if (copy_from_user(&val, buf, 4)) 64 return -EFAULT; 65 66 iowrite32(le32_to_cpu(val), io + pos); 67 } else { 68 val = cpu_to_le32(ioread32(io + pos)); 69 70 if (copy_to_user(buf, &val, 4)) 71 return -EFAULT; 72 } 73 74 filled = 4; 75 76 } else if ((pos % 2) == 0 && count >= 2) { 77 __le16 val; 78 79 if (iswrite) { 80 if (copy_from_user(&val, buf, 2)) 81 return -EFAULT; 82 83 iowrite16(le16_to_cpu(val), io + pos); 84 } else { 85 val = cpu_to_le16(ioread16(io + pos)); 86 87 if (copy_to_user(buf, &val, 2)) 88 return -EFAULT; 89 } 90 91 filled = 2; 92 } else { 93 u8 val; 94 95 if (iswrite) { 96 if (copy_from_user(&val, buf, 1)) 97 return -EFAULT; 98 99 iowrite8(val, io + pos); 100 } else { 101 val = ioread8(io + pos); 102 103 if (copy_to_user(buf, &val, 1)) 104 return -EFAULT; 105 } 106 107 filled = 1; 108 } 109 110 count -= filled; 111 done += filled; 112 buf += filled; 113 pos += filled; 114 } 115 116 *ppos += done; 117 118 return done; 119 } 120 121 /* 122 * MMIO BAR access 123 * We handle two excluded ranges here as well, if the user tries to read 124 * the ROM beyond what PCI tells us is available or the MSI-X table region, 125 * we return 0xFF and writes are dropped. 126 */ 127 ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf, 128 size_t count, loff_t *ppos, bool iswrite) 129 { 130 struct pci_dev *pdev = vdev->pdev; 131 loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; 132 int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); 133 void __iomem *io; 134 resource_size_t end; 135 size_t done = 0; 136 size_t x_start = 0, x_end = 0; /* excluded range */ 137 138 if (!pci_resource_start(pdev, bar)) 139 return -EINVAL; 140 141 end = pci_resource_len(pdev, bar); 142 143 if (pos > end) 144 return -EINVAL; 145 146 if (pos == end) 147 return 0; 148 149 if (pos + count > end) 150 count = end - pos; 151 152 if (bar == PCI_ROM_RESOURCE) { 153 io = pci_map_rom(pdev, &x_start); 154 x_end = end; 155 } else { 156 if (!vdev->barmap[bar]) { 157 int ret; 158 159 ret = pci_request_selected_regions(pdev, 1 << bar, 160 "vfio"); 161 if (ret) 162 return ret; 163 164 vdev->barmap[bar] = pci_iomap(pdev, bar, 0); 165 166 if (!vdev->barmap[bar]) { 167 pci_release_selected_regions(pdev, 1 << bar); 168 return -EINVAL; 169 } 170 } 171 172 io = vdev->barmap[bar]; 173 174 if (bar == vdev->msix_bar) { 175 x_start = vdev->msix_offset; 176 x_end = vdev->msix_offset + vdev->msix_size; 177 } 178 } 179 180 if (!io) 181 return -EINVAL; 182 183 while (count) { 184 size_t fillable, filled; 185 186 if (pos < x_start) 187 fillable = x_start - pos; 188 else if (pos >= x_end) 189 fillable = end - pos; 190 else 191 fillable = 0; 192 193 if (fillable >= 4 && !(pos % 4) && (count >= 4)) { 194 __le32 val; 195 196 if (iswrite) { 197 if (copy_from_user(&val, buf, 4)) 198 goto out; 199 200 iowrite32(le32_to_cpu(val), io + pos); 201 } else { 202 val = cpu_to_le32(ioread32(io + pos)); 203 204 if (copy_to_user(buf, &val, 4)) 205 goto out; 206 } 207 208 filled = 4; 209 } else if (fillable >= 2 && !(pos % 2) && (count >= 2)) { 210 __le16 val; 211 212 if (iswrite) { 213 if (copy_from_user(&val, buf, 2)) 214 goto out; 215 216 iowrite16(le16_to_cpu(val), io + pos); 217 } else { 218 val = cpu_to_le16(ioread16(io + pos)); 219 220 if (copy_to_user(buf, &val, 2)) 221 goto out; 222 } 223 224 filled = 2; 225 } else if (fillable) { 226 u8 val; 227 228 if (iswrite) { 229 if (copy_from_user(&val, buf, 1)) 230 goto out; 231 232 iowrite8(val, io + pos); 233 } else { 234 val = ioread8(io + pos); 235 236 if (copy_to_user(buf, &val, 1)) 237 goto out; 238 } 239 240 filled = 1; 241 } else { 242 /* Drop writes, fill reads with FF */ 243 if (!iswrite) { 244 char val = 0xFF; 245 size_t i; 246 247 for (i = 0; i < x_end - pos; i++) { 248 if (put_user(val, buf + i)) 249 goto out; 250 } 251 } 252 253 filled = x_end - pos; 254 } 255 256 count -= filled; 257 done += filled; 258 buf += filled; 259 pos += filled; 260 } 261 262 *ppos += done; 263 264 out: 265 if (bar == PCI_ROM_RESOURCE) 266 pci_unmap_rom(pdev, io); 267 268 return count ? -EFAULT : done; 269 } 270