blob: e9d78eb91ed7d19432de394a952fc1ee5f0c01e4 [file] [log] [blame]
Alex Williamson89e1f7d2012-07-31 08:16:24 -06001/*
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
Alex Williamson89e1f7d2012-07-31 08:16:24 -060023/*
Alex Williamson906ee992013-02-14 14:02:12 -070024 * Read or write from an __iomem region (MMIO or I/O port) with an excluded
25 * range which is inaccessible. The excluded range drops writes and fills
26 * reads with -1. This is intended for handling MSI-X vector tables and
27 * leftover space for ROM BARs.
Alex Williamson89e1f7d2012-07-31 08:16:24 -060028 */
Alex Williamson906ee992013-02-14 14:02:12 -070029static ssize_t do_io_rw(void __iomem *io, char __user *buf,
30 loff_t off, size_t count, size_t x_start,
31 size_t x_end, bool iswrite)
Alex Williamson89e1f7d2012-07-31 08:16:24 -060032{
Alex Williamson906ee992013-02-14 14:02:12 -070033 ssize_t done = 0;
Alex Williamson89e1f7d2012-07-31 08:16:24 -060034
35 while (count) {
36 size_t fillable, filled;
37
Alex Williamson906ee992013-02-14 14:02:12 -070038 if (off < x_start)
39 fillable = min(count, (size_t)(x_start - off));
40 else if (off >= x_end)
41 fillable = count;
Alex Williamson89e1f7d2012-07-31 08:16:24 -060042 else
43 fillable = 0;
44
Alex Williamson906ee992013-02-14 14:02:12 -070045 if (fillable >= 4 && !(off % 4)) {
Alex Williamson89e1f7d2012-07-31 08:16:24 -060046 __le32 val;
47
48 if (iswrite) {
49 if (copy_from_user(&val, buf, 4))
Alex Williamson906ee992013-02-14 14:02:12 -070050 return -EFAULT;
Alex Williamson89e1f7d2012-07-31 08:16:24 -060051
Alex Williamson906ee992013-02-14 14:02:12 -070052 iowrite32(le32_to_cpu(val), io + off);
Alex Williamson89e1f7d2012-07-31 08:16:24 -060053 } else {
Alex Williamson906ee992013-02-14 14:02:12 -070054 val = cpu_to_le32(ioread32(io + off));
Alex Williamson89e1f7d2012-07-31 08:16:24 -060055
56 if (copy_to_user(buf, &val, 4))
Alex Williamson906ee992013-02-14 14:02:12 -070057 return -EFAULT;
Alex Williamson89e1f7d2012-07-31 08:16:24 -060058 }
59
60 filled = 4;
Alex Williamson906ee992013-02-14 14:02:12 -070061 } else if (fillable >= 2 && !(off % 2)) {
Alex Williamson89e1f7d2012-07-31 08:16:24 -060062 __le16 val;
63
64 if (iswrite) {
65 if (copy_from_user(&val, buf, 2))
Alex Williamson906ee992013-02-14 14:02:12 -070066 return -EFAULT;
Alex Williamson89e1f7d2012-07-31 08:16:24 -060067
Alex Williamson906ee992013-02-14 14:02:12 -070068 iowrite16(le16_to_cpu(val), io + off);
Alex Williamson89e1f7d2012-07-31 08:16:24 -060069 } else {
Alex Williamson906ee992013-02-14 14:02:12 -070070 val = cpu_to_le16(ioread16(io + off));
Alex Williamson89e1f7d2012-07-31 08:16:24 -060071
72 if (copy_to_user(buf, &val, 2))
Alex Williamson906ee992013-02-14 14:02:12 -070073 return -EFAULT;
Alex Williamson89e1f7d2012-07-31 08:16:24 -060074 }
75
76 filled = 2;
77 } else if (fillable) {
78 u8 val;
79
80 if (iswrite) {
81 if (copy_from_user(&val, buf, 1))
Alex Williamson906ee992013-02-14 14:02:12 -070082 return -EFAULT;
Alex Williamson89e1f7d2012-07-31 08:16:24 -060083
Alex Williamson906ee992013-02-14 14:02:12 -070084 iowrite8(val, io + off);
Alex Williamson89e1f7d2012-07-31 08:16:24 -060085 } else {
Alex Williamson906ee992013-02-14 14:02:12 -070086 val = ioread8(io + off);
Alex Williamson89e1f7d2012-07-31 08:16:24 -060087
88 if (copy_to_user(buf, &val, 1))
Alex Williamson906ee992013-02-14 14:02:12 -070089 return -EFAULT;
Alex Williamson89e1f7d2012-07-31 08:16:24 -060090 }
91
92 filled = 1;
93 } else {
Alex Williamson906ee992013-02-14 14:02:12 -070094 /* Fill reads with -1, drop writes */
95 filled = min(count, (size_t)(x_end - off));
Alex Williamson89e1f7d2012-07-31 08:16:24 -060096 if (!iswrite) {
Alex Williamson906ee992013-02-14 14:02:12 -070097 u8 val = 0xFF;
Alex Williamson89e1f7d2012-07-31 08:16:24 -060098 size_t i;
99
Alex Williamson906ee992013-02-14 14:02:12 -0700100 for (i = 0; i < filled; i++)
101 if (copy_to_user(buf + i, &val, 1))
102 return -EFAULT;
Alex Williamson89e1f7d2012-07-31 08:16:24 -0600103 }
Alex Williamson89e1f7d2012-07-31 08:16:24 -0600104 }
105
106 count -= filled;
107 done += filled;
Alex Williamson906ee992013-02-14 14:02:12 -0700108 off += filled;
Alex Williamson89e1f7d2012-07-31 08:16:24 -0600109 buf += filled;
Alex Williamson89e1f7d2012-07-31 08:16:24 -0600110 }
111
Alex Williamson906ee992013-02-14 14:02:12 -0700112 return done;
113}
Alex Williamson89e1f7d2012-07-31 08:16:24 -0600114
Alex Williamson906ee992013-02-14 14:02:12 -0700115ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
116 size_t count, loff_t *ppos, bool iswrite)
117{
118 struct pci_dev *pdev = vdev->pdev;
119 loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
120 int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
121 size_t x_start = 0, x_end = 0;
122 resource_size_t end;
123 void __iomem *io;
124 ssize_t done;
125
126 if (!pci_resource_start(pdev, bar))
127 return -EINVAL;
128
129 end = pci_resource_len(pdev, bar);
130
131 if (pos >= end)
132 return -EINVAL;
133
134 count = min(count, (size_t)(end - pos));
135
136 if (bar == PCI_ROM_RESOURCE) {
137 /*
138 * The ROM can fill less space than the BAR, so we start the
139 * excluded range at the end of the actual ROM. This makes
140 * filling large ROM BARs much faster.
141 */
142 io = pci_map_rom(pdev, &x_start);
143 if (!io)
144 return -ENOMEM;
145 x_end = end;
146 } else if (!vdev->barmap[bar]) {
147 int ret;
148
149 ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
150 if (ret)
151 return ret;
152
153 io = pci_iomap(pdev, bar, 0);
154 if (!io) {
155 pci_release_selected_regions(pdev, 1 << bar);
156 return -ENOMEM;
157 }
158
159 vdev->barmap[bar] = io;
160 } else
161 io = vdev->barmap[bar];
162
163 if (bar == vdev->msix_bar) {
164 x_start = vdev->msix_offset;
165 x_end = vdev->msix_offset + vdev->msix_size;
166 }
167
168 done = do_io_rw(io, buf, pos, count, x_start, x_end, iswrite);
169
170 if (done >= 0)
171 *ppos += done;
172
Alex Williamson89e1f7d2012-07-31 08:16:24 -0600173 if (bar == PCI_ROM_RESOURCE)
174 pci_unmap_rom(pdev, io);
175
Alex Williamson906ee992013-02-14 14:02:12 -0700176 return done;
Alex Williamson89e1f7d2012-07-31 08:16:24 -0600177}