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