1 /*
2  * Copyright (C) 2018-2022 Intel Corporation.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 /*
9  * The interface of gvt is obsoleted.
10  * We reserve these code for future.
11  */
12 
13 #include <sys/stat.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 
20 #include "dm.h"
21 #include "pci_core.h"
22 #include "vmmapi.h"
23 
24 #define __STDC_FORMAT_MACROS
25 #include <inttypes.h>
26 
27 static int pci_gvt_debug;
28 
29 static struct pci_vdev *gvt_dev;
30 
31 #define DPRINTF(params) do { if (pci_gvt_debug) pr_dbg params; } while (0)
32 
33 #define WPRINTF(params) (pr_err params)
34 
35 struct PCIHostDeviceAddress {
36 	uint32_t domain;
37 	uint32_t bus;
38 	uint32_t slot;
39 	uint32_t function;
40 };
41 
42 struct pci_gvt {
43 	struct pci_vdev *gvt_pi;
44 	struct PCIHostDeviceAddress addr;
45 	int host_config_fd;
46 	FILE *gvt_file;
47 	/* PCI config space */
48 	uint8_t host_config[PCI_REGMAX+1];
49 	int instance_created;
50 };
51 
52 /* These are the default values */
53 int gvt_low_gm_sz = 64; /* in MB */
54 int gvt_high_gm_sz = 448; /* in MB */
55 int gvt_fence_sz = 8;
56 int guest_domid = 1;
57 
58 int
acrn_parse_gvtargs(char * arg)59 acrn_parse_gvtargs(char *arg)
60 {
61 	if (dm_strtoi(arg, &arg, 10, &gvt_low_gm_sz) != 0 ||
62 		dm_strtoi(arg, &arg, 10, &gvt_high_gm_sz) != 0 ||
63 		dm_strtoi(arg, &arg, 10, &gvt_fence_sz) != 0)
64 		return -1;
65 
66 	printf("passed gvt-g optargs low_gm %d, high_gm %d, fence %d\n",
67 		gvt_low_gm_sz, gvt_high_gm_sz, gvt_fence_sz);
68 
69 	return 0;
70 }
71 
72 static void
pci_gvt_write(struct vmctx * ctx,int vcpu,struct pci_vdev * pi,int baridx,uint64_t offset,int size,uint64_t value)73 pci_gvt_write(struct vmctx *ctx, int vcpu, struct pci_vdev *pi,
74 	      int baridx, uint64_t offset, int size, uint64_t value)
75 {
76 	/* null function, pci config space write should be trapped */
77 	DPRINTF(("%s: write vcpu %d, baridx %d, offset %"PRIu64", size %d, "
78 			"value %"PRIu64"\n",
79 			__func__, vcpu, baridx, offset, size, value));
80 }
81 
82 static uint64_t
pci_gvt_read(struct vmctx * ctx,int vcpu,struct pci_vdev * pi,int baridx,uint64_t offset,int size)83 pci_gvt_read(struct vmctx *ctx, int vcpu, struct pci_vdev *pi,
84 	     int baridx, uint64_t offset, int size)
85 {
86 	/* null function, pci config space read should be trapped */
87 	DPRINTF(("%s: read vcpu %d, baridx %d, offset %"PRIu64", size %d\n",
88 				__func__, vcpu, baridx, offset, size));
89 
90 	return 0;
91 }
92 
93 void
update_gvt_bar(struct vmctx * ctx)94 update_gvt_bar(struct vmctx *ctx)
95 {
96 	char bar_path[PATH_MAX];
97 	int bar_fd;
98 	int ret;
99 	char resource[76];
100 	char *next;
101 	uint64_t bar0_start_addr, bar0_end_addr, bar2_start_addr, bar2_end_addr;
102 	int i;
103 
104 	/* "/sys/kernel/gvt/vmx/vgpu_bar_info" exposes vgpu bar regions. */
105 	snprintf(bar_path, sizeof(bar_path),
106 		"/sys/kernel/gvt/vm%d/vgpu_bar_info",
107 		ctx->vmid);
108 
109 	if(access(bar_path, F_OK) == -1)
110 		return;
111 
112 	bar_fd = open(bar_path, O_RDONLY);
113 	if(bar_fd == -1){
114 		pr_err("failed to open sys bar info\n");
115 		return;
116 	}
117 
118 	ret = pread(bar_fd, resource, 76, 0);
119 
120 	close(bar_fd);
121 
122 	if (ret < 76) {
123 		pr_err("failed to read sys bar info\n");
124 		return;
125 	}
126 
127 	next = resource;
128 	bar0_start_addr = strtoull(next, &next, 16);
129 	bar0_end_addr = strtoull(next, &next, 16) + bar0_start_addr -1;
130 	bar2_start_addr = strtoull(next, &next, 16);
131 	bar2_end_addr = strtoull(next, &next, 16) + bar2_start_addr -1;
132 
133 	for(i = 0; i < REGION_NUMS; i++){
134 		if(reserved_bar_regions[i].vdev &&
135 			reserved_bar_regions[i].vdev == gvt_dev){
136 			pci_emul_free_bar(gvt_dev, reserved_bar_regions[i].idx);
137 		}
138 	}
139 
140 	destory_io_rsvd_rgns(gvt_dev);
141 
142 	ret = reserve_io_rgn(bar0_start_addr,
143                         bar0_end_addr, 0, PCIBAR_MEM32, gvt_dev);
144 	if(ret != 0)
145 		return;
146 	ret = reserve_io_rgn(bar2_start_addr,
147                         bar2_end_addr, 2, PCIBAR_MEM32, gvt_dev);
148 	if(ret != 0)
149 		return;
150 
151 	pci_emul_alloc_bar(gvt_dev, 0, PCIBAR_MEM32,
152 			bar0_end_addr - bar0_start_addr + 1);
153 	pci_emul_alloc_bar(gvt_dev, 2, PCIBAR_MEM32,
154 			bar2_end_addr - bar2_start_addr + 1);
155 }
156 
157 static int
gvt_init_config(struct pci_gvt * gvt)158 gvt_init_config(struct pci_gvt *gvt)
159 {
160 	int ret;
161 	char name[PATH_MAX];
162 	uint8_t cap_ptr = 0;
163 	char res_name[PATH_MAX];
164 	char resource[512];
165 	int res_fd;
166 	uint64_t bar0_start_addr;
167 	uint64_t bar0_end_addr;
168 	uint64_t bar2_start_addr;
169 	uint64_t bar2_end_addr;
170 	char *next;
171 	struct vmctx *ctx;
172 
173 	/* get physical gpu bars info from
174 	 * "/sys/bus/PCI/devices/0000\:00\:02.0/resource"
175 	 */
176 	snprintf(res_name, sizeof(res_name),
177 		"/sys/bus/pci/devices/%04x:%02x:%02x.%x/resource",
178 		gvt->addr.domain, gvt->addr.bus, gvt->addr.slot,
179 		gvt->addr.function);
180 	res_fd = open(res_name, O_RDONLY);
181 	if (res_fd == -1) {
182 		pr_err("gvt:open host pci resource failed\n");
183 		return -1;
184 	}
185 
186 	ret = pread(res_fd, resource, 512, 0);
187 
188 	close(res_fd);
189 
190 	if (ret < 512) {
191 		pr_err("failed to read host device resource space\n");
192 		return -1;
193 	}
194 
195 	next = resource;
196 	bar0_start_addr = strtoull(next, &next, 16);
197 	bar0_end_addr = strtoull(next, &next, 16);
198 
199 	/* bar0 and bar2 have some distance, need pass the distance */
200 	next = next + 80;
201 	bar2_start_addr = strtoull(next, &next, 16);
202 	bar2_end_addr = strtoull(next, &next, 16);
203 
204 	ctx = gvt->gvt_pi->vmctx;
205 	if(bar0_start_addr < PCI_EMUL_MEMBASE32
206 		|| bar2_start_addr < PCI_EMUL_MEMBASE32
207 		|| bar0_end_addr > PCI_EMUL_MEMLIMIT32
208 		|| bar2_end_addr > PCI_EMUL_MEMLIMIT32){
209 		pr_err("gvt pci bases are out of range\n");
210 		return -1;
211 	}
212 
213 	ctx->gvt_enabled = true;
214 	ctx->update_gvt_bar = &update_gvt_bar;
215 
216 	/* In GVT-g design, it only use pci bar0 and bar2,
217 	 * So we need reserve bar0 region and bar2 region only
218 	 */
219 	ret = reserve_io_rgn(bar0_start_addr,
220 				bar0_end_addr, 0, PCIBAR_MEM32, gvt->gvt_pi);
221 	if(ret != 0)
222 		return -1;
223 	ret = reserve_io_rgn(bar2_start_addr,
224 				bar2_end_addr, 2, PCIBAR_MEM32, gvt->gvt_pi);
225 	if(ret != 0)
226 		return -1;
227 	snprintf(name, sizeof(name),
228 		"/sys/bus/pci/devices/%04x:%02x:%02x.%x/config",
229 		gvt->addr.domain, gvt->addr.bus, gvt->addr.slot,
230 		gvt->addr.function);
231 	gvt->host_config_fd = open(name, O_RDONLY);
232 	if (gvt->host_config_fd == -1) {
233 		pr_err("gvt:open host pci config failed\n");
234 		return -1;
235 	}
236 
237 	ret = pread(gvt->host_config_fd, gvt->host_config, PCI_REGMAX+1, 0);
238 
239 	close(gvt->host_config_fd);
240 
241 	if (ret <= PCI_REGMAX) {
242 		pr_err("failed to read host device config space\n");
243 		return -1;
244 	}
245 
246 	/* initialize config space */
247 	pci_set_cfgdata16(gvt->gvt_pi, PCIR_VENDOR, gvt->host_config[0]);
248 	pci_set_cfgdata16(gvt->gvt_pi, PCIR_DEVICE, gvt->host_config[0x02]);
249 	/* status */
250 	pci_set_cfgdata16(gvt->gvt_pi, PCIR_STATUS, gvt->host_config[0x06]);
251 	/* revision id */
252 	pci_set_cfgdata16(gvt->gvt_pi, PCIR_REVID, gvt->host_config[0x08]);
253 	/* class and sub class */
254 	pci_set_cfgdata8(gvt->gvt_pi, PCIR_CLASS, PCIC_DISPLAY);
255 	pci_set_cfgdata8(gvt->gvt_pi, PCIR_SUBCLASS, PCIS_DISPLAY_VGA);
256 	/* capability */
257 	pci_set_cfgdata8(gvt->gvt_pi, PCIR_CAP_PTR, gvt->host_config[0x34]);
258 	cap_ptr = gvt->host_config[0x34];
259 	while (cap_ptr != 0 && cap_ptr <= PCI_REGMAX - 15) {
260 		pci_set_cfgdata32(gvt->gvt_pi, cap_ptr,
261 			gvt->host_config[cap_ptr]);
262 		pci_set_cfgdata32(gvt->gvt_pi, cap_ptr + 4,
263 			gvt->host_config[cap_ptr + 4]);
264 		pci_set_cfgdata32(gvt->gvt_pi, cap_ptr + 8,
265 			gvt->host_config[cap_ptr + 8]);
266 		pci_set_cfgdata32(gvt->gvt_pi, cap_ptr + 12,
267 			gvt->host_config[cap_ptr + 12]);
268 		cap_ptr = gvt->host_config[cap_ptr + 1];
269 	}
270 
271 	/* SNB: processor graphics control register */
272 	pci_set_cfgdata16(gvt->gvt_pi, 0x50, gvt->host_config[0x50]);
273 	/* processor graphics control register */
274 	pci_set_cfgdata16(gvt->gvt_pi, 0x52, gvt->host_config[0x52]);
275 
276 	ret = pci_emul_alloc_bar(gvt->gvt_pi, 0, PCIBAR_MEM32,
277 		bar0_end_addr - bar0_start_addr + 1);
278 	if (ret != 0) {
279 		pr_err("allocate gvt pci bar[0] failed\n");
280 		return -1;
281 	}
282 
283 	ret = pci_emul_alloc_bar(gvt->gvt_pi, 2, PCIBAR_MEM32,
284 		bar2_end_addr - bar2_start_addr + 1);
285 	if (ret != 0) {
286 		pr_err("allocate gvt pci bar[2] failed\n");
287 		return -1;
288 	}
289 
290 	/* same as host, lagecy vga usage */
291 	ret = pci_emul_alloc_bar(gvt->gvt_pi, 4, PCIBAR_IO, 64);
292 	if (ret != 0) {
293 		pr_err("allocate gvt pci bar[4] failed\n");
294 		return -1;
295 	}
296 
297 	return 0;
298 }
299 
300 static int
gvt_create_instance(struct pci_gvt * gvt)301 gvt_create_instance(struct pci_gvt *gvt)
302 {
303 	const char *path = "/sys/kernel/gvt/control/create_gvt_instance";
304 	int ret = 0;
305 
306 	gvt->gvt_file = fopen(path, "w");
307 	if (gvt->gvt_file == NULL) {
308 		WPRINTF(("GVT: open %s failed\n", path));
309 		return -errno;
310 	}
311 
312 	DPRINTF(("GVT: %s: domid=%d, low_gm_sz=%dMB, high_gm_sz=%dMB, "
313 			"fence_sz=%d\n", __func__, guest_domid,
314 			gvt_low_gm_sz, gvt_high_gm_sz, gvt_fence_sz));
315 
316 	if (gvt_low_gm_sz <= 0 || gvt_high_gm_sz <= 0 || gvt_fence_sz <= 0) {
317 		WPRINTF(("GVT: %s failed: invalid parameters!\n", __func__));
318 		fclose(gvt->gvt_file);
319 		return -EINVAL;
320 	}
321 
322 	/* The format of the string is:
323 	 * domid,aperture_size,gm_size,fence_size. This means we want the gvt
324 	 * driver to create a gvt instanc for Domain domid with the required
325 	 * parameters. NOTE: aperture_size and gm_size are in MB.
326 	 */
327 	if (fprintf(gvt->gvt_file, "%d,%u,%u,%u\n", guest_domid,
328 			gvt_low_gm_sz, gvt_high_gm_sz, gvt_fence_sz) < 0)
329 		ret = -errno;
330 
331 	if (fclose(gvt->gvt_file) != 0)
332 		return -errno;
333 
334 	if (!ret)
335 		gvt->instance_created = 1;
336 
337 	return ret;
338 }
339 
340 /* did not find deinit function caller, leave it here only */
341 static int
gvt_destroy_instance(struct pci_gvt * gvt)342 gvt_destroy_instance(struct pci_gvt *gvt)
343 {
344 	const char *path = "/sys/kernel/gvt/control/create_gvt_instance";
345 	int ret = 0;
346 
347 	gvt->gvt_file = fopen(path, "w");
348 	if (gvt->gvt_file == NULL) {
349 		WPRINTF(("gvt: error: open %s failed", path));
350 		return -errno;
351 	}
352 
353 	/* -domid means we want the gvt driver to free the gvt instance
354 	 * of Domain domid.
355 	 **/
356 	if (fprintf(gvt->gvt_file, "%d\n", -guest_domid) < 0)
357 		ret = -errno;
358 
359 	if (fclose(gvt->gvt_file) != 0)
360 		return -errno;
361 
362 	if (!ret)
363 		gvt->instance_created = 0;
364 
365 	return ret;
366 }
367 
368 static int
pci_gvt_init(struct vmctx * ctx,struct pci_vdev * pi,char * opts)369 pci_gvt_init(struct vmctx *ctx, struct pci_vdev *pi, char *opts)
370 {
371 	int ret;
372 	struct pci_gvt *gvt = NULL;
373 
374 	gvt = calloc(1, sizeof(struct pci_gvt));
375 	if (!gvt) {
376 		pr_err("gvt:calloc gvt failed\n");
377 		return -1;
378 	}
379 
380 	gvt->instance_created = 0;
381 	gvt->addr.domain = 0;
382 	gvt->addr.bus = pi->bus;
383 	gvt->addr.slot = pi->slot;
384 	gvt->addr.function = pi->func;
385 
386 	pi->arg = gvt;
387 	gvt->gvt_pi = pi;
388 	guest_domid = ctx->vmid;
389 
390 	gvt_dev = pi;
391 
392 	ret = gvt_init_config(gvt);
393 
394 	if (ret)
395 		goto fail;
396 
397 	ret = gvt_create_instance(gvt);
398 
399 	if(!ret)
400 		return ret;
401 fail:
402 	gvt_dev = NULL;
403 	ctx->gvt_enabled = false;
404 	pr_err("GVT: init failed\n");
405 	free(gvt);
406 	return -1;
407 }
408 
409 void
pci_gvt_deinit(struct vmctx * ctx,struct pci_vdev * pi,char * opts)410 pci_gvt_deinit(struct vmctx *ctx, struct pci_vdev *pi, char *opts)
411 {
412 	struct pci_gvt *gvt = pi->arg;
413 	int ret = 0;
414 
415 	if (gvt) {
416 		if (gvt->instance_created)
417 			ret = gvt_destroy_instance(gvt);
418 		if (ret)
419 			WPRINTF(("GVT: %s: failed: errno=%d\n", __func__, ret));
420 
421 		destory_io_rsvd_rgns(gvt_dev);
422 		free(gvt);
423 		pi->arg = NULL;
424 		gvt_dev = NULL;
425 	}
426 }
427 
428 struct pci_vdev_ops pci_ops_gvt = {
429 	.class_name	= "pci-gvt",
430 	.vdev_init	= pci_gvt_init,
431 	.vdev_deinit	= pci_gvt_deinit,
432 	.vdev_barwrite	= pci_gvt_write,
433 	.vdev_barread	= pci_gvt_read,
434 };
435 
436 DEFINE_PCI_DEVTYPE(pci_ops_gvt);
437