189e1f7d4SAlex Williamson /* 289e1f7d4SAlex Williamson * VFIO PCI I/O Port & MMIO access 389e1f7d4SAlex Williamson * 489e1f7d4SAlex Williamson * Copyright (C) 2012 Red Hat, Inc. All rights reserved. 589e1f7d4SAlex Williamson * Author: Alex Williamson <alex.williamson@redhat.com> 689e1f7d4SAlex Williamson * 789e1f7d4SAlex Williamson * This program is free software; you can redistribute it and/or modify 889e1f7d4SAlex Williamson * it under the terms of the GNU General Public License version 2 as 989e1f7d4SAlex Williamson * published by the Free Software Foundation. 1089e1f7d4SAlex Williamson * 1189e1f7d4SAlex Williamson * Derived from original vfio: 1289e1f7d4SAlex Williamson * Copyright 2010 Cisco Systems, Inc. All rights reserved. 1389e1f7d4SAlex Williamson * Author: Tom Lyon, pugs@cisco.com 1489e1f7d4SAlex Williamson */ 1589e1f7d4SAlex Williamson 1689e1f7d4SAlex Williamson #include <linux/fs.h> 1789e1f7d4SAlex Williamson #include <linux/pci.h> 1889e1f7d4SAlex Williamson #include <linux/uaccess.h> 1989e1f7d4SAlex Williamson #include <linux/io.h> 2084237a82SAlex Williamson #include <linux/vgaarb.h> 2189e1f7d4SAlex Williamson 2289e1f7d4SAlex Williamson #include "vfio_pci_private.h" 2389e1f7d4SAlex Williamson 24*07fd7ef3SAlex Williamson #ifdef __LITTLE_ENDIAN 25*07fd7ef3SAlex Williamson #define vfio_ioread64 ioread64 26*07fd7ef3SAlex Williamson #define vfio_iowrite64 iowrite64 27*07fd7ef3SAlex Williamson #define vfio_ioread32 ioread32 28*07fd7ef3SAlex Williamson #define vfio_iowrite32 iowrite32 29*07fd7ef3SAlex Williamson #define vfio_ioread16 ioread16 30*07fd7ef3SAlex Williamson #define vfio_iowrite16 iowrite16 31*07fd7ef3SAlex Williamson #else 32*07fd7ef3SAlex Williamson #define vfio_ioread64 ioread64be 33*07fd7ef3SAlex Williamson #define vfio_iowrite64 iowrite64be 34*07fd7ef3SAlex Williamson #define vfio_ioread32 ioread32be 35*07fd7ef3SAlex Williamson #define vfio_iowrite32 iowrite32be 36*07fd7ef3SAlex Williamson #define vfio_ioread16 ioread16be 37*07fd7ef3SAlex Williamson #define vfio_iowrite16 iowrite16be 38*07fd7ef3SAlex Williamson #endif 39*07fd7ef3SAlex Williamson #define vfio_ioread8 ioread8 40*07fd7ef3SAlex Williamson #define vfio_iowrite8 iowrite8 41*07fd7ef3SAlex Williamson 4289e1f7d4SAlex Williamson /* 43906ee99dSAlex Williamson * Read or write from an __iomem region (MMIO or I/O port) with an excluded 44906ee99dSAlex Williamson * range which is inaccessible. The excluded range drops writes and fills 45906ee99dSAlex Williamson * reads with -1. This is intended for handling MSI-X vector tables and 46906ee99dSAlex Williamson * leftover space for ROM BARs. 4789e1f7d4SAlex Williamson */ 48906ee99dSAlex Williamson static ssize_t do_io_rw(void __iomem *io, char __user *buf, 49906ee99dSAlex Williamson loff_t off, size_t count, size_t x_start, 50906ee99dSAlex Williamson size_t x_end, bool iswrite) 5189e1f7d4SAlex Williamson { 52906ee99dSAlex Williamson ssize_t done = 0; 5389e1f7d4SAlex Williamson 5489e1f7d4SAlex Williamson while (count) { 5589e1f7d4SAlex Williamson size_t fillable, filled; 5689e1f7d4SAlex Williamson 57906ee99dSAlex Williamson if (off < x_start) 58906ee99dSAlex Williamson fillable = min(count, (size_t)(x_start - off)); 59906ee99dSAlex Williamson else if (off >= x_end) 60906ee99dSAlex Williamson fillable = count; 6189e1f7d4SAlex Williamson else 6289e1f7d4SAlex Williamson fillable = 0; 6389e1f7d4SAlex Williamson 64906ee99dSAlex Williamson if (fillable >= 4 && !(off % 4)) { 65*07fd7ef3SAlex Williamson u32 val; 6689e1f7d4SAlex Williamson 6789e1f7d4SAlex Williamson if (iswrite) { 6889e1f7d4SAlex Williamson if (copy_from_user(&val, buf, 4)) 69906ee99dSAlex Williamson return -EFAULT; 7089e1f7d4SAlex Williamson 71*07fd7ef3SAlex Williamson vfio_iowrite32(val, io + off); 7289e1f7d4SAlex Williamson } else { 73*07fd7ef3SAlex Williamson val = vfio_ioread32(io + off); 7489e1f7d4SAlex Williamson 7589e1f7d4SAlex Williamson if (copy_to_user(buf, &val, 4)) 76906ee99dSAlex Williamson return -EFAULT; 7789e1f7d4SAlex Williamson } 7889e1f7d4SAlex Williamson 7989e1f7d4SAlex Williamson filled = 4; 80906ee99dSAlex Williamson } else if (fillable >= 2 && !(off % 2)) { 81*07fd7ef3SAlex Williamson u16 val; 8289e1f7d4SAlex Williamson 8389e1f7d4SAlex Williamson if (iswrite) { 8489e1f7d4SAlex Williamson if (copy_from_user(&val, buf, 2)) 85906ee99dSAlex Williamson return -EFAULT; 8689e1f7d4SAlex Williamson 87*07fd7ef3SAlex Williamson vfio_iowrite16(val, io + off); 8889e1f7d4SAlex Williamson } else { 89*07fd7ef3SAlex Williamson val = vfio_ioread16(io + off); 9089e1f7d4SAlex Williamson 9189e1f7d4SAlex Williamson if (copy_to_user(buf, &val, 2)) 92906ee99dSAlex Williamson return -EFAULT; 9389e1f7d4SAlex Williamson } 9489e1f7d4SAlex Williamson 9589e1f7d4SAlex Williamson filled = 2; 9689e1f7d4SAlex Williamson } else if (fillable) { 9789e1f7d4SAlex Williamson u8 val; 9889e1f7d4SAlex Williamson 9989e1f7d4SAlex Williamson if (iswrite) { 10089e1f7d4SAlex Williamson if (copy_from_user(&val, buf, 1)) 101906ee99dSAlex Williamson return -EFAULT; 10289e1f7d4SAlex Williamson 103*07fd7ef3SAlex Williamson vfio_iowrite8(val, io + off); 10489e1f7d4SAlex Williamson } else { 105*07fd7ef3SAlex Williamson val = vfio_ioread8(io + off); 10689e1f7d4SAlex Williamson 10789e1f7d4SAlex Williamson if (copy_to_user(buf, &val, 1)) 108906ee99dSAlex Williamson return -EFAULT; 10989e1f7d4SAlex Williamson } 11089e1f7d4SAlex Williamson 11189e1f7d4SAlex Williamson filled = 1; 11289e1f7d4SAlex Williamson } else { 113906ee99dSAlex Williamson /* Fill reads with -1, drop writes */ 114906ee99dSAlex Williamson filled = min(count, (size_t)(x_end - off)); 11589e1f7d4SAlex Williamson if (!iswrite) { 116906ee99dSAlex Williamson u8 val = 0xFF; 11789e1f7d4SAlex Williamson size_t i; 11889e1f7d4SAlex Williamson 119906ee99dSAlex Williamson for (i = 0; i < filled; i++) 120906ee99dSAlex Williamson if (copy_to_user(buf + i, &val, 1)) 121906ee99dSAlex Williamson return -EFAULT; 12289e1f7d4SAlex Williamson } 12389e1f7d4SAlex Williamson } 12489e1f7d4SAlex Williamson 12589e1f7d4SAlex Williamson count -= filled; 12689e1f7d4SAlex Williamson done += filled; 127906ee99dSAlex Williamson off += filled; 12889e1f7d4SAlex Williamson buf += filled; 12989e1f7d4SAlex Williamson } 13089e1f7d4SAlex Williamson 131906ee99dSAlex Williamson return done; 132906ee99dSAlex Williamson } 133906ee99dSAlex Williamson 1340d77ed35SAlex Williamson static int vfio_pci_setup_barmap(struct vfio_pci_device *vdev, int bar) 1350d77ed35SAlex Williamson { 1360d77ed35SAlex Williamson struct pci_dev *pdev = vdev->pdev; 1370d77ed35SAlex Williamson int ret; 1380d77ed35SAlex Williamson void __iomem *io; 1390d77ed35SAlex Williamson 1400d77ed35SAlex Williamson if (vdev->barmap[bar]) 1410d77ed35SAlex Williamson return 0; 1420d77ed35SAlex Williamson 1430d77ed35SAlex Williamson ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); 1440d77ed35SAlex Williamson if (ret) 1450d77ed35SAlex Williamson return ret; 1460d77ed35SAlex Williamson 1470d77ed35SAlex Williamson io = pci_iomap(pdev, bar, 0); 1480d77ed35SAlex Williamson if (!io) { 1490d77ed35SAlex Williamson pci_release_selected_regions(pdev, 1 << bar); 1500d77ed35SAlex Williamson return -ENOMEM; 1510d77ed35SAlex Williamson } 1520d77ed35SAlex Williamson 1530d77ed35SAlex Williamson vdev->barmap[bar] = io; 1540d77ed35SAlex Williamson 1550d77ed35SAlex Williamson return 0; 1560d77ed35SAlex Williamson } 1570d77ed35SAlex Williamson 158906ee99dSAlex Williamson ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, 159906ee99dSAlex Williamson size_t count, loff_t *ppos, bool iswrite) 160906ee99dSAlex Williamson { 161906ee99dSAlex Williamson struct pci_dev *pdev = vdev->pdev; 162906ee99dSAlex Williamson loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; 163906ee99dSAlex Williamson int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); 164906ee99dSAlex Williamson size_t x_start = 0, x_end = 0; 165906ee99dSAlex Williamson resource_size_t end; 166906ee99dSAlex Williamson void __iomem *io; 167906ee99dSAlex Williamson ssize_t done; 168906ee99dSAlex Williamson 169a13b6459SAlex Williamson if (pci_resource_start(pdev, bar)) 170906ee99dSAlex Williamson end = pci_resource_len(pdev, bar); 171a13b6459SAlex Williamson else if (bar == PCI_ROM_RESOURCE && 172a13b6459SAlex Williamson pdev->resource[bar].flags & IORESOURCE_ROM_SHADOW) 173a13b6459SAlex Williamson end = 0x20000; 174a13b6459SAlex Williamson else 175a13b6459SAlex Williamson return -EINVAL; 176906ee99dSAlex Williamson 177906ee99dSAlex Williamson if (pos >= end) 178906ee99dSAlex Williamson return -EINVAL; 179906ee99dSAlex Williamson 180906ee99dSAlex Williamson count = min(count, (size_t)(end - pos)); 181906ee99dSAlex Williamson 182906ee99dSAlex Williamson if (bar == PCI_ROM_RESOURCE) { 183906ee99dSAlex Williamson /* 184906ee99dSAlex Williamson * The ROM can fill less space than the BAR, so we start the 185906ee99dSAlex Williamson * excluded range at the end of the actual ROM. This makes 186906ee99dSAlex Williamson * filling large ROM BARs much faster. 187906ee99dSAlex Williamson */ 188906ee99dSAlex Williamson io = pci_map_rom(pdev, &x_start); 189906ee99dSAlex Williamson if (!io) 190906ee99dSAlex Williamson return -ENOMEM; 191906ee99dSAlex Williamson x_end = end; 1920d77ed35SAlex Williamson } else { 1930d77ed35SAlex Williamson int ret = vfio_pci_setup_barmap(vdev, bar); 194906ee99dSAlex Williamson if (ret) 195906ee99dSAlex Williamson return ret; 196906ee99dSAlex Williamson 197906ee99dSAlex Williamson io = vdev->barmap[bar]; 1980d77ed35SAlex Williamson } 199906ee99dSAlex Williamson 200906ee99dSAlex Williamson if (bar == vdev->msix_bar) { 201906ee99dSAlex Williamson x_start = vdev->msix_offset; 202906ee99dSAlex Williamson x_end = vdev->msix_offset + vdev->msix_size; 203906ee99dSAlex Williamson } 204906ee99dSAlex Williamson 205906ee99dSAlex Williamson done = do_io_rw(io, buf, pos, count, x_start, x_end, iswrite); 206906ee99dSAlex Williamson 207906ee99dSAlex Williamson if (done >= 0) 20889e1f7d4SAlex Williamson *ppos += done; 20989e1f7d4SAlex Williamson 21089e1f7d4SAlex Williamson if (bar == PCI_ROM_RESOURCE) 21189e1f7d4SAlex Williamson pci_unmap_rom(pdev, io); 21289e1f7d4SAlex Williamson 213906ee99dSAlex Williamson return done; 21489e1f7d4SAlex Williamson } 21584237a82SAlex Williamson 21684237a82SAlex Williamson ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf, 21784237a82SAlex Williamson size_t count, loff_t *ppos, bool iswrite) 21884237a82SAlex Williamson { 21984237a82SAlex Williamson int ret; 22084237a82SAlex Williamson loff_t off, pos = *ppos & VFIO_PCI_OFFSET_MASK; 22184237a82SAlex Williamson void __iomem *iomem = NULL; 22284237a82SAlex Williamson unsigned int rsrc; 22384237a82SAlex Williamson bool is_ioport; 22484237a82SAlex Williamson ssize_t done; 22584237a82SAlex Williamson 22684237a82SAlex Williamson if (!vdev->has_vga) 22784237a82SAlex Williamson return -EINVAL; 22884237a82SAlex Williamson 22945e86971SArnd Bergmann if (pos > 0xbfffful) 23045e86971SArnd Bergmann return -EINVAL; 23145e86971SArnd Bergmann 23245e86971SArnd Bergmann switch ((u32)pos) { 23384237a82SAlex Williamson case 0xa0000 ... 0xbffff: 23484237a82SAlex Williamson count = min(count, (size_t)(0xc0000 - pos)); 23584237a82SAlex Williamson iomem = ioremap_nocache(0xa0000, 0xbffff - 0xa0000 + 1); 23684237a82SAlex Williamson off = pos - 0xa0000; 23784237a82SAlex Williamson rsrc = VGA_RSRC_LEGACY_MEM; 23884237a82SAlex Williamson is_ioport = false; 23984237a82SAlex Williamson break; 24084237a82SAlex Williamson case 0x3b0 ... 0x3bb: 24184237a82SAlex Williamson count = min(count, (size_t)(0x3bc - pos)); 24284237a82SAlex Williamson iomem = ioport_map(0x3b0, 0x3bb - 0x3b0 + 1); 24384237a82SAlex Williamson off = pos - 0x3b0; 24484237a82SAlex Williamson rsrc = VGA_RSRC_LEGACY_IO; 24584237a82SAlex Williamson is_ioport = true; 24684237a82SAlex Williamson break; 24784237a82SAlex Williamson case 0x3c0 ... 0x3df: 24884237a82SAlex Williamson count = min(count, (size_t)(0x3e0 - pos)); 24984237a82SAlex Williamson iomem = ioport_map(0x3c0, 0x3df - 0x3c0 + 1); 25084237a82SAlex Williamson off = pos - 0x3c0; 25184237a82SAlex Williamson rsrc = VGA_RSRC_LEGACY_IO; 25284237a82SAlex Williamson is_ioport = true; 25384237a82SAlex Williamson break; 25484237a82SAlex Williamson default: 25584237a82SAlex Williamson return -EINVAL; 25684237a82SAlex Williamson } 25784237a82SAlex Williamson 25884237a82SAlex Williamson if (!iomem) 25984237a82SAlex Williamson return -ENOMEM; 26084237a82SAlex Williamson 26184237a82SAlex Williamson ret = vga_get_interruptible(vdev->pdev, rsrc); 26284237a82SAlex Williamson if (ret) { 26384237a82SAlex Williamson is_ioport ? ioport_unmap(iomem) : iounmap(iomem); 26484237a82SAlex Williamson return ret; 26584237a82SAlex Williamson } 26684237a82SAlex Williamson 26784237a82SAlex Williamson done = do_io_rw(iomem, buf, off, count, 0, 0, iswrite); 26884237a82SAlex Williamson 26984237a82SAlex Williamson vga_put(vdev->pdev, rsrc); 27084237a82SAlex Williamson 27184237a82SAlex Williamson is_ioport ? ioport_unmap(iomem) : iounmap(iomem); 27284237a82SAlex Williamson 27384237a82SAlex Williamson if (done >= 0) 27484237a82SAlex Williamson *ppos += done; 27584237a82SAlex Williamson 27684237a82SAlex Williamson return done; 27784237a82SAlex Williamson } 278