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
24static 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
44static void vfio_pci_igd_release(struct vfio_pci_device *vdev,
45				 struct vfio_pci_region *region)
46{
47	memunmap(region->data);
48}
49
50static const struct vfio_pci_regops vfio_pci_igd_regops = {
51	.rw		= vfio_pci_igd_rw,
52	.release	= vfio_pci_igd_release,
53};
54
55static 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
110static 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
203static 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
211static 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
216static 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
264int 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