linux/drivers/vfio/pci/vfio_pci_rdwr.c
<<
>>
Prefs
   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 */
  24ssize_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 */
 127ssize_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                        filled = min((size_t)(x_end - pos), count);
 244                        if (!iswrite) {
 245                                char val = 0xFF;
 246                                size_t i;
 247
 248                                for (i = 0; i < filled; i++) {
 249                                        if (put_user(val, buf + i))
 250                                                goto out;
 251                                }
 252                        }
 253
 254                }
 255
 256                count -= filled;
 257                done += filled;
 258                buf += filled;
 259                pos += filled;
 260        }
 261
 262        *ppos += done;
 263
 264out:
 265        if (bar == PCI_ROM_RESOURCE)
 266                pci_unmap_rom(pdev, io);
 267
 268        return count ? -EFAULT : done;
 269}
 270