• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * VFIO PCI Intel Graphics support
4  *
5  * Copyright (C) 2016 Red Hat, Inc.  All rights reserved.
6  *	Author: Alex Williamson <alex.williamson@redhat.com>
7  *
8  * Register a device specific region through which to provide read-only
9  * access to the Intel IGD opregion.  The register defining the opregion
10  * address is also virtualized to prevent user modification.
11  */
12 
13 #include <linux/io.h>
14 #include <linux/pci.h>
15 #include <linux/uaccess.h>
16 #include <linux/vfio.h>
17 
18 #include "vfio_pci_private.h"
19 
20 #define OPREGION_SIGNATURE	"IntelGraphicsMem"
21 #define OPREGION_SIZE		(8 * 1024)
22 #define OPREGION_PCI_ADDR	0xfc
23 
vfio_pci_igd_rw(struct vfio_pci_device * vdev,char __user * buf,size_t count,loff_t * ppos,bool iswrite)24 static size_t vfio_pci_igd_rw(struct vfio_pci_device *vdev, char __user *buf,
25 			      size_t count, loff_t *ppos, bool iswrite)
26 {
27 	unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) - VFIO_PCI_NUM_REGIONS;
28 	void *base = vdev->region[i].data;
29 	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
30 
31 	if (pos >= vdev->region[i].size || iswrite)
32 		return -EINVAL;
33 
34 	count = min(count, (size_t)(vdev->region[i].size - pos));
35 
36 	if (copy_to_user(buf, base + pos, count))
37 		return -EFAULT;
38 
39 	*ppos += count;
40 
41 	return count;
42 }
43 
vfio_pci_igd_release(struct vfio_pci_device * vdev,struct vfio_pci_region * region)44 static void vfio_pci_igd_release(struct vfio_pci_device *vdev,
45 				 struct vfio_pci_region *region)
46 {
47 	memunmap(region->data);
48 }
49 
50 static const struct vfio_pci_regops vfio_pci_igd_regops = {
51 	.rw		= vfio_pci_igd_rw,
52 	.release	= vfio_pci_igd_release,
53 };
54 
vfio_pci_igd_opregion_init(struct vfio_pci_device * vdev)55 static int vfio_pci_igd_opregion_init(struct vfio_pci_device *vdev)
56 {
57 	__le32 *dwordp = (__le32 *)(vdev->vconfig + OPREGION_PCI_ADDR);
58 	u32 addr, size;
59 	void *base;
60 	int ret;
61 
62 	ret = pci_read_config_dword(vdev->pdev, OPREGION_PCI_ADDR, &addr);
63 	if (ret)
64 		return ret;
65 
66 	if (!addr || !(~addr))
67 		return -ENODEV;
68 
69 	base = memremap(addr, OPREGION_SIZE, MEMREMAP_WB);
70 	if (!base)
71 		return -ENOMEM;
72 
73 	if (memcmp(base, OPREGION_SIGNATURE, 16)) {
74 		memunmap(base);
75 		return -EINVAL;
76 	}
77 
78 	size = le32_to_cpu(*(__le32 *)(base + 16));
79 	if (!size) {
80 		memunmap(base);
81 		return -EINVAL;
82 	}
83 
84 	size *= 1024; /* In KB */
85 
86 	if (size != OPREGION_SIZE) {
87 		memunmap(base);
88 		base = memremap(addr, size, MEMREMAP_WB);
89 		if (!base)
90 			return -ENOMEM;
91 	}
92 
93 	ret = vfio_pci_register_dev_region(vdev,
94 		PCI_VENDOR_ID_INTEL | VFIO_REGION_TYPE_PCI_VENDOR_TYPE,
95 		VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION,
96 		&vfio_pci_igd_regops, size, VFIO_REGION_INFO_FLAG_READ, base);
97 	if (ret) {
98 		memunmap(base);
99 		return ret;
100 	}
101 
102 	/* Fill vconfig with the hw value and virtualize register */
103 	*dwordp = cpu_to_le32(addr);
104 	memset(vdev->pci_config_map + OPREGION_PCI_ADDR,
105 	       PCI_CAP_ID_INVALID_VIRT, 4);
106 
107 	return ret;
108 }
109 
vfio_pci_igd_cfg_rw(struct vfio_pci_device * vdev,char __user * buf,size_t count,loff_t * ppos,bool iswrite)110 static size_t vfio_pci_igd_cfg_rw(struct vfio_pci_device *vdev,
111 				  char __user *buf, size_t count, loff_t *ppos,
112 				  bool iswrite)
113 {
114 	unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) - VFIO_PCI_NUM_REGIONS;
115 	struct pci_dev *pdev = vdev->region[i].data;
116 	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
117 	size_t size;
118 	int ret;
119 
120 	if (pos >= vdev->region[i].size || iswrite)
121 		return -EINVAL;
122 
123 	size = count = min(count, (size_t)(vdev->region[i].size - pos));
124 
125 	if ((pos & 1) && size) {
126 		u8 val;
127 
128 		ret = pci_user_read_config_byte(pdev, pos, &val);
129 		if (ret)
130 			return pcibios_err_to_errno(ret);
131 
132 		if (copy_to_user(buf + count - size, &val, 1))
133 			return -EFAULT;
134 
135 		pos++;
136 		size--;
137 	}
138 
139 	if ((pos & 3) && size > 2) {
140 		u16 val;
141 
142 		ret = pci_user_read_config_word(pdev, pos, &val);
143 		if (ret)
144 			return pcibios_err_to_errno(ret);
145 
146 		val = cpu_to_le16(val);
147 		if (copy_to_user(buf + count - size, &val, 2))
148 			return -EFAULT;
149 
150 		pos += 2;
151 		size -= 2;
152 	}
153 
154 	while (size > 3) {
155 		u32 val;
156 
157 		ret = pci_user_read_config_dword(pdev, pos, &val);
158 		if (ret)
159 			return pcibios_err_to_errno(ret);
160 
161 		val = cpu_to_le32(val);
162 		if (copy_to_user(buf + count - size, &val, 4))
163 			return -EFAULT;
164 
165 		pos += 4;
166 		size -= 4;
167 	}
168 
169 	while (size >= 2) {
170 		u16 val;
171 
172 		ret = pci_user_read_config_word(pdev, pos, &val);
173 		if (ret)
174 			return pcibios_err_to_errno(ret);
175 
176 		val = cpu_to_le16(val);
177 		if (copy_to_user(buf + count - size, &val, 2))
178 			return -EFAULT;
179 
180 		pos += 2;
181 		size -= 2;
182 	}
183 
184 	while (size) {
185 		u8 val;
186 
187 		ret = pci_user_read_config_byte(pdev, pos, &val);
188 		if (ret)
189 			return pcibios_err_to_errno(ret);
190 
191 		if (copy_to_user(buf + count - size, &val, 1))
192 			return -EFAULT;
193 
194 		pos++;
195 		size--;
196 	}
197 
198 	*ppos += count;
199 
200 	return count;
201 }
202 
vfio_pci_igd_cfg_release(struct vfio_pci_device * vdev,struct vfio_pci_region * region)203 static void vfio_pci_igd_cfg_release(struct vfio_pci_device *vdev,
204 				     struct vfio_pci_region *region)
205 {
206 	struct pci_dev *pdev = region->data;
207 
208 	pci_dev_put(pdev);
209 }
210 
211 static const struct vfio_pci_regops vfio_pci_igd_cfg_regops = {
212 	.rw		= vfio_pci_igd_cfg_rw,
213 	.release	= vfio_pci_igd_cfg_release,
214 };
215 
vfio_pci_igd_cfg_init(struct vfio_pci_device * vdev)216 static int vfio_pci_igd_cfg_init(struct vfio_pci_device *vdev)
217 {
218 	struct pci_dev *host_bridge, *lpc_bridge;
219 	int ret;
220 
221 	host_bridge = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
222 	if (!host_bridge)
223 		return -ENODEV;
224 
225 	if (host_bridge->vendor != PCI_VENDOR_ID_INTEL ||
226 	    host_bridge->class != (PCI_CLASS_BRIDGE_HOST << 8)) {
227 		pci_dev_put(host_bridge);
228 		return -EINVAL;
229 	}
230 
231 	ret = vfio_pci_register_dev_region(vdev,
232 		PCI_VENDOR_ID_INTEL | VFIO_REGION_TYPE_PCI_VENDOR_TYPE,
233 		VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG,
234 		&vfio_pci_igd_cfg_regops, host_bridge->cfg_size,
235 		VFIO_REGION_INFO_FLAG_READ, host_bridge);
236 	if (ret) {
237 		pci_dev_put(host_bridge);
238 		return ret;
239 	}
240 
241 	lpc_bridge = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0x1f, 0));
242 	if (!lpc_bridge)
243 		return -ENODEV;
244 
245 	if (lpc_bridge->vendor != PCI_VENDOR_ID_INTEL ||
246 	    lpc_bridge->class != (PCI_CLASS_BRIDGE_ISA << 8)) {
247 		pci_dev_put(lpc_bridge);
248 		return -EINVAL;
249 	}
250 
251 	ret = vfio_pci_register_dev_region(vdev,
252 		PCI_VENDOR_ID_INTEL | VFIO_REGION_TYPE_PCI_VENDOR_TYPE,
253 		VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG,
254 		&vfio_pci_igd_cfg_regops, lpc_bridge->cfg_size,
255 		VFIO_REGION_INFO_FLAG_READ, lpc_bridge);
256 	if (ret) {
257 		pci_dev_put(lpc_bridge);
258 		return ret;
259 	}
260 
261 	return 0;
262 }
263 
vfio_pci_igd_init(struct vfio_pci_device * vdev)264 int vfio_pci_igd_init(struct vfio_pci_device *vdev)
265 {
266 	int ret;
267 
268 	ret = vfio_pci_igd_opregion_init(vdev);
269 	if (ret)
270 		return ret;
271 
272 	ret = vfio_pci_igd_cfg_init(vdev);
273 	if (ret)
274 		return ret;
275 
276 	return 0;
277 }
278