FILE
vfio_pci_rdwr.c
File preview with syntax highlighting.
linux-6.19.14/drivers/vfio/pci/vfio_pci_rdwr.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * VFIO PCI I/O Port & MMIO access 4 * 5 * Copyright (C) 2012 Red Hat, Inc. All rights reserved. 6 * Author: Alex Williamson <alex.williamson@redhat.com> 7 * 8 * Derived from original vfio: 9 * Copyright 2010 Cisco Systems, Inc. All rights reserved. 10 * Author: Tom Lyon, pugs@cisco.com 11 */ 12 13#include <linux/fs.h> 14#include <linux/pci.h> 15#include <linux/uaccess.h> 16#include <linux/io.h> 17#include <linux/vfio.h> 18#include <linux/vgaarb.h> 19#include <linux/io-64-nonatomic-lo-hi.h> 20 21#include "vfio_pci_priv.h" 22 23#ifdef __LITTLE_ENDIAN 24#define vfio_ioread64 ioread64 25#define vfio_iowrite64 iowrite64 26#define vfio_ioread32 ioread32 27#define vfio_iowrite32 iowrite32 28#define vfio_ioread16 ioread16 29#define vfio_iowrite16 iowrite16 30#else 31#define vfio_ioread64 ioread64be 32#define vfio_iowrite64 iowrite64be 33#define vfio_ioread32 ioread32be 34#define vfio_iowrite32 iowrite32be 35#define vfio_ioread16 ioread16be 36#define vfio_iowrite16 iowrite16be 37#endif 38#define vfio_ioread8 ioread8 39#define vfio_iowrite8 iowrite8 40 41#define VFIO_IOWRITE(size) \ 42int vfio_pci_core_iowrite##size(struct vfio_pci_core_device *vdev, \ 43 bool test_mem, u##size val, void __iomem *io) \ 44{ \ 45 if (test_mem) { \ 46 down_read(&vdev->memory_lock); \ 47 if (!__vfio_pci_memory_enabled(vdev)) { \ 48 up_read(&vdev->memory_lock); \ 49 return -EIO; \ 50 } \ 51 } \ 52 \ 53 vfio_iowrite##size(val, io); \ 54 \ 55 if (test_mem) \ 56 up_read(&vdev->memory_lock); \ 57 \ 58 return 0; \ 59} \ 60EXPORT_SYMBOL_GPL(vfio_pci_core_iowrite##size); 61 62VFIO_IOWRITE(8) 63VFIO_IOWRITE(16) 64VFIO_IOWRITE(32) 65VFIO_IOWRITE(64) 66 67#define VFIO_IOREAD(size) \ 68int vfio_pci_core_ioread##size(struct vfio_pci_core_device *vdev, \ 69 bool test_mem, u##size *val, void __iomem *io) \ 70{ \ 71 if (test_mem) { \ 72 down_read(&vdev->memory_lock); \ 73 if (!__vfio_pci_memory_enabled(vdev)) { \ 74 up_read(&vdev->memory_lock); \ 75 return -EIO; \ 76 } \ 77 } \ 78 \ 79 *val = vfio_ioread##size(io); \ 80 \ 81 if (test_mem) \ 82 up_read(&vdev->memory_lock); \ 83 \ 84 return 0; \ 85} \ 86EXPORT_SYMBOL_GPL(vfio_pci_core_ioread##size); 87 88VFIO_IOREAD(8) 89VFIO_IOREAD(16) 90VFIO_IOREAD(32) 91VFIO_IOREAD(64) 92 93#define VFIO_IORDWR(size) \ 94static int vfio_pci_iordwr##size(struct vfio_pci_core_device *vdev,\ 95 bool iswrite, bool test_mem, \ 96 void __iomem *io, char __user *buf, \ 97 loff_t off, size_t *filled) \ 98{ \ 99 u##size val; \ 100 int ret; \ 101 \ 102 if (iswrite) { \ 103 if (copy_from_user(&val, buf, sizeof(val))) \ 104 return -EFAULT; \ 105 \ 106 ret = vfio_pci_core_iowrite##size(vdev, test_mem, \ 107 val, io + off); \ 108 if (ret) \ 109 return ret; \ 110 } else { \ 111 ret = vfio_pci_core_ioread##size(vdev, test_mem, \ 112 &val, io + off); \ 113 if (ret) \ 114 return ret; \ 115 \ 116 if (copy_to_user(buf, &val, sizeof(val))) \ 117 return -EFAULT; \ 118 } \ 119 \ 120 *filled = sizeof(val); \ 121 return 0; \ 122} \ 123 124VFIO_IORDWR(8) 125VFIO_IORDWR(16) 126VFIO_IORDWR(32) 127VFIO_IORDWR(64) 128 129/* 130 * Read or write from an __iomem region (MMIO or I/O port) with an excluded 131 * range which is inaccessible. The excluded range drops writes and fills 132 * reads with -1. This is intended for handling MSI-X vector tables and 133 * leftover space for ROM BARs. 134 */ 135ssize_t vfio_pci_core_do_io_rw(struct vfio_pci_core_device *vdev, bool test_mem, 136 void __iomem *io, char __user *buf, 137 loff_t off, size_t count, size_t x_start, 138 size_t x_end, bool iswrite, 139 enum vfio_pci_io_width max_width) 140{ 141 ssize_t done = 0; 142 int ret; 143 144 while (count) { 145 size_t fillable, filled; 146 147 if (off < x_start) 148 fillable = min(count, (size_t)(x_start - off)); 149 else if (off >= x_end) 150 fillable = count; 151 else 152 fillable = 0; 153 154 if (fillable >= 8 && !(off % 8) && max_width >= 8) { 155 ret = vfio_pci_iordwr64(vdev, iswrite, test_mem, 156 io, buf, off, &filled); 157 if (ret) 158 return ret; 159 160 } else if (fillable >= 4 && !(off % 4) && max_width >= 4) { 161 ret = vfio_pci_iordwr32(vdev, iswrite, test_mem, 162 io, buf, off, &filled); 163 if (ret) 164 return ret; 165 166 } else if (fillable >= 2 && !(off % 2) && max_width >= 2) { 167 ret = vfio_pci_iordwr16(vdev, iswrite, test_mem, 168 io, buf, off, &filled); 169 if (ret) 170 return ret; 171 172 } else if (fillable) { 173 ret = vfio_pci_iordwr8(vdev, iswrite, test_mem, 174 io, buf, off, &filled); 175 if (ret) 176 return ret; 177 178 } else { 179 /* Fill reads with -1, drop writes */ 180 filled = min(count, (size_t)(x_end - off)); 181 if (!iswrite) { 182 u8 val = 0xFF; 183 size_t i; 184 185 for (i = 0; i < filled; i++) 186 if (copy_to_user(buf + i, &val, 1)) 187 return -EFAULT; 188 } 189 } 190 191 count -= filled; 192 done += filled; 193 off += filled; 194 buf += filled; 195 } 196 197 return done; 198} 199EXPORT_SYMBOL_GPL(vfio_pci_core_do_io_rw); 200 201int vfio_pci_core_setup_barmap(struct vfio_pci_core_device *vdev, int bar) 202{ 203 struct pci_dev *pdev = vdev->pdev; 204 int ret; 205 void __iomem *io; 206 207 if (vdev->barmap[bar]) 208 return 0; 209 210 ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); 211 if (ret) 212 return ret; 213 214 io = pci_iomap(pdev, bar, 0); 215 if (!io) { 216 pci_release_selected_regions(pdev, 1 << bar); 217 return -ENOMEM; 218 } 219 220 vdev->barmap[bar] = io; 221 222 return 0; 223} 224EXPORT_SYMBOL_GPL(vfio_pci_core_setup_barmap); 225 226ssize_t vfio_pci_bar_rw(struct vfio_pci_core_device *vdev, char __user *buf, 227 size_t count, loff_t *ppos, bool iswrite) 228{ 229 struct pci_dev *pdev = vdev->pdev; 230 loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; 231 int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); 232 size_t x_start = 0, x_end = 0; 233 resource_size_t end; 234 void __iomem *io; 235 struct resource *res = &vdev->pdev->resource[bar]; 236 ssize_t done; 237 enum vfio_pci_io_width max_width = VFIO_PCI_IO_WIDTH_8; 238 239 if (pci_resource_start(pdev, bar)) 240 end = pci_resource_len(pdev, bar); 241 else if (bar == PCI_ROM_RESOURCE && pdev->rom && pdev->romlen) 242 end = roundup_pow_of_two(pdev->romlen); 243 else 244 return -EINVAL; 245 246 if (pos >= end) 247 return -EINVAL; 248 249 count = min(count, (size_t)(end - pos)); 250 251 if (bar == PCI_ROM_RESOURCE) { 252 /* 253 * The ROM can fill less space than the BAR, so we start the 254 * excluded range at the end of the actual ROM. This makes 255 * filling large ROM BARs much faster. 256 */ 257 if (pci_resource_start(pdev, bar)) { 258 io = pci_map_rom(pdev, &x_start); 259 } else { 260 io = ioremap(pdev->rom, pdev->romlen); 261 x_start = pdev->romlen; 262 } 263 if (!io) 264 return -ENOMEM; 265 x_end = end; 266 267 /* 268 * Certain devices (e.g. Intel X710) don't support qword 269 * access to the ROM bar. Otherwise PCI AER errors might be 270 * triggered. 271 * 272 * Disable qword access to the ROM bar universally, which 273 * worked reliably for years before qword access is enabled. 274 */ 275 max_width = VFIO_PCI_IO_WIDTH_4; 276 } else { 277 int ret = vfio_pci_core_setup_barmap(vdev, bar); 278 if (ret) { 279 done = ret; 280 goto out; 281 } 282 283 io = vdev->barmap[bar]; 284 } 285 286 if (bar == vdev->msix_bar) { 287 x_start = vdev->msix_offset; 288 x_end = vdev->msix_offset + vdev->msix_size; 289 } 290 291 done = vfio_pci_core_do_io_rw(vdev, res->flags & IORESOURCE_MEM, io, buf, pos, 292 count, x_start, x_end, iswrite, max_width); 293 294 if (done >= 0) 295 *ppos += done; 296 297 if (bar == PCI_ROM_RESOURCE) { 298 if (pci_resource_start(pdev, bar)) 299 pci_unmap_rom(pdev, io); 300 else 301 iounmap(io); 302 } 303 304out: 305 return done; 306} 307 308#ifdef CONFIG_VFIO_PCI_VGA 309ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf, 310 size_t count, loff_t *ppos, bool iswrite) 311{ 312 int ret; 313 loff_t off, pos = *ppos & VFIO_PCI_OFFSET_MASK; 314 void __iomem *iomem = NULL; 315 unsigned int rsrc; 316 bool is_ioport; 317 ssize_t done; 318 319 if (!vdev->has_vga) 320 return -EINVAL; 321 322 if (pos > 0xbfffful) 323 return -EINVAL; 324 325 switch ((u32)pos) { 326 case 0xa0000 ... 0xbffff: 327 count = min(count, (size_t)(0xc0000 - pos)); 328 iomem = ioremap(0xa0000, 0xbffff - 0xa0000 + 1); 329 off = pos - 0xa0000; 330 rsrc = VGA_RSRC_LEGACY_MEM; 331 is_ioport = false; 332 break; 333 case 0x3b0 ... 0x3bb: 334 count = min(count, (size_t)(0x3bc - pos)); 335 iomem = ioport_map(0x3b0, 0x3bb - 0x3b0 + 1); 336 off = pos - 0x3b0; 337 rsrc = VGA_RSRC_LEGACY_IO; 338 is_ioport = true; 339 break; 340 case 0x3c0 ... 0x3df: 341 count = min(count, (size_t)(0x3e0 - pos)); 342 iomem = ioport_map(0x3c0, 0x3df - 0x3c0 + 1); 343 off = pos - 0x3c0; 344 rsrc = VGA_RSRC_LEGACY_IO; 345 is_ioport = true; 346 break; 347 default: 348 return -EINVAL; 349 } 350 351 if (!iomem) 352 return -ENOMEM; 353 354 ret = vga_get_interruptible(vdev->pdev, rsrc); 355 if (ret) { 356 is_ioport ? ioport_unmap(iomem) : iounmap(iomem); 357 return ret; 358 } 359 360 /* 361 * VGA MMIO is a legacy, non-BAR resource that hopefully allows 362 * probing, so we don't currently worry about access in relation 363 * to the memory enable bit in the command register. 364 */ 365 done = vfio_pci_core_do_io_rw(vdev, false, iomem, buf, off, count, 366 0, 0, iswrite, VFIO_PCI_IO_WIDTH_4); 367 368 vga_put(vdev->pdev, rsrc); 369 370 is_ioport ? ioport_unmap(iomem) : iounmap(iomem); 371 372 if (done >= 0) 373 *ppos += done; 374 375 return done; 376} 377#endif 378 379static void vfio_pci_ioeventfd_do_write(struct vfio_pci_ioeventfd *ioeventfd, 380 bool test_mem) 381{ 382 switch (ioeventfd->count) { 383 case 1: 384 vfio_pci_core_iowrite8(ioeventfd->vdev, test_mem, 385 ioeventfd->data, ioeventfd->addr); 386 break; 387 case 2: 388 vfio_pci_core_iowrite16(ioeventfd->vdev, test_mem, 389 ioeventfd->data, ioeventfd->addr); 390 break; 391 case 4: 392 vfio_pci_core_iowrite32(ioeventfd->vdev, test_mem, 393 ioeventfd->data, ioeventfd->addr); 394 break; 395 case 8: 396 vfio_pci_core_iowrite64(ioeventfd->vdev, test_mem, 397 ioeventfd->data, ioeventfd->addr); 398 break; 399 } 400} 401 402static int vfio_pci_ioeventfd_handler(void *opaque, void *unused) 403{ 404 struct vfio_pci_ioeventfd *ioeventfd = opaque; 405 struct vfio_pci_core_device *vdev = ioeventfd->vdev; 406 407 if (ioeventfd->test_mem) { 408 if (!down_read_trylock(&vdev->memory_lock)) 409 return 1; /* Lock contended, use thread */ 410 if (!__vfio_pci_memory_enabled(vdev)) { 411 up_read(&vdev->memory_lock); 412 return 0; 413 } 414 } 415 416 vfio_pci_ioeventfd_do_write(ioeventfd, false); 417 418 if (ioeventfd->test_mem) 419 up_read(&vdev->memory_lock); 420 421 return 0; 422} 423 424static void vfio_pci_ioeventfd_thread(void *opaque, void *unused) 425{ 426 struct vfio_pci_ioeventfd *ioeventfd = opaque; 427 428 vfio_pci_ioeventfd_do_write(ioeventfd, ioeventfd->test_mem); 429} 430 431int vfio_pci_ioeventfd(struct vfio_pci_core_device *vdev, loff_t offset, 432 uint64_t data, int count, int fd) 433{ 434 struct pci_dev *pdev = vdev->pdev; 435 loff_t pos = offset & VFIO_PCI_OFFSET_MASK; 436 int ret, bar = VFIO_PCI_OFFSET_TO_INDEX(offset); 437 struct vfio_pci_ioeventfd *ioeventfd; 438 439 /* Only support ioeventfds into BARs */ 440 if (bar > VFIO_PCI_BAR5_REGION_INDEX) 441 return -EINVAL; 442 443 if (pos + count > pci_resource_len(pdev, bar)) 444 return -EINVAL; 445 446 /* Disallow ioeventfds working around MSI-X table writes */ 447 if (bar == vdev->msix_bar && 448 !(pos + count <= vdev->msix_offset || 449 pos >= vdev->msix_offset + vdev->msix_size)) 450 return -EINVAL; 451 452 if (count == 8) 453 return -EINVAL; 454 455 ret = vfio_pci_core_setup_barmap(vdev, bar); 456 if (ret) 457 return ret; 458 459 mutex_lock(&vdev->ioeventfds_lock); 460 461 list_for_each_entry(ioeventfd, &vdev->ioeventfds_list, next) { 462 if (ioeventfd->pos == pos && ioeventfd->bar == bar && 463 ioeventfd->data == data && ioeventfd->count == count) { 464 if (fd == -1) { 465 vfio_virqfd_disable(&ioeventfd->virqfd); 466 list_del(&ioeventfd->next); 467 vdev->ioeventfds_nr--; 468 kfree(ioeventfd); 469 ret = 0; 470 } else 471 ret = -EEXIST; 472 473 goto out_unlock; 474 } 475 } 476 477 if (fd < 0) { 478 ret = -ENODEV; 479 goto out_unlock; 480 } 481 482 if (vdev->ioeventfds_nr >= VFIO_PCI_IOEVENTFD_MAX) { 483 ret = -ENOSPC; 484 goto out_unlock; 485 } 486 487 ioeventfd = kzalloc(sizeof(*ioeventfd), GFP_KERNEL_ACCOUNT); 488 if (!ioeventfd) { 489 ret = -ENOMEM; 490 goto out_unlock; 491 } 492 493 ioeventfd->vdev = vdev; 494 ioeventfd->addr = vdev->barmap[bar] + pos; 495 ioeventfd->data = data; 496 ioeventfd->pos = pos; 497 ioeventfd->bar = bar; 498 ioeventfd->count = count; 499 ioeventfd->test_mem = vdev->pdev->resource[bar].flags & IORESOURCE_MEM; 500 501 ret = vfio_virqfd_enable(ioeventfd, vfio_pci_ioeventfd_handler, 502 vfio_pci_ioeventfd_thread, NULL, 503 &ioeventfd->virqfd, fd); 504 if (ret) { 505 kfree(ioeventfd); 506 goto out_unlock; 507 } 508 509 list_add(&ioeventfd->next, &vdev->ioeventfds_list); 510 vdev->ioeventfds_nr++; 511 512out_unlock: 513 mutex_unlock(&vdev->ioeventfds_lock); 514 515 return ret; 516}
~/
Read-only file preview.