From 4c0181a5dbc2e18126eec6011d093e82d40fadf4 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Thu, 29 Mar 2018 23:05:04 +0800 Subject: [PATCH] DM/GVT: implement emulated graphics pci device Enable graphics virtualization GVT-g Signed-off-by: Fei Jiang Reviewed-by: He, Min --- devicemodel/Makefile | 1 + devicemodel/core/main.c | 7 + devicemodel/hw/pci/gvt.c | 310 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 318 insertions(+) create mode 100644 devicemodel/hw/pci/gvt.c diff --git a/devicemodel/Makefile b/devicemodel/Makefile index ae970b37e..d4f635358 100644 --- a/devicemodel/Makefile +++ b/devicemodel/Makefile @@ -82,6 +82,7 @@ SRCS += hw/pci/virtio/virtio_hyper_dmabuf.c SRCS += hw/pci/virtio/virtio_heci.c SRCS += hw/pci/irq.c SRCS += hw/pci/uart.c +SRCS += hw/pci/gvt.c # core #SRCS += core/bootrom.c diff --git a/devicemodel/core/main.c b/devicemodel/core/main.c index d1d693bdb..20341238b 100644 --- a/devicemodel/core/main.c +++ b/devicemodel/core/main.c @@ -157,6 +157,7 @@ usage(int code) " -k: kernel image path\n" " -r: ramdisk image path\n" " -B: bootargs for kernel\n" + " -G: GVT args: low_gm_size, high_gm_size, fence_sz\n" " -v: version\n" " -i: ioc boot parameters\n" " --vsbl: vsbl file path\n" @@ -765,6 +766,12 @@ main(int argc, char *argv[]) else break; break; + case 'G': + if (acrn_parse_gvtargs(optarg) != 0) { + errx(EX_USAGE, "invalid GVT param %s", optarg); + exit(1); + } + break; case 'M': ptdev_prefer_msi(false); break; diff --git a/devicemodel/hw/pci/gvt.c b/devicemodel/hw/pci/gvt.c new file mode 100644 index 000000000..e87ea883c --- /dev/null +++ b/devicemodel/hw/pci/gvt.c @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dm.h" +#include "pci_core.h" +#include "vmmapi.h" + +#define __STDC_FORMAT_MACROS +#include + +static int pci_gvt_debug; + +#define DPRINTF(params) do { if (pci_gvt_debug) printf params; } while (0) + +#define WPRINTF(params) (printf params) + +struct PCIHostDeviceAddress { + uint32_t domain; + uint32_t bus; + uint32_t slot; + uint32_t function; +}; + +struct pci_gvt { + struct pci_vdev *gvt_pi; + struct PCIHostDeviceAddress addr; + int host_config_fd; + FILE *gvt_file; + /* PCI config space */ + uint8_t *host_config; + int instance_created; +}; + +/* These are the default values */ +int gvt_low_gm_sz = 64; /* in MB */ +int gvt_high_gm_sz = 448; /* in MB */ +int gvt_fence_sz = 8; +int guest_domid = 1; + +int +acrn_parse_gvtargs(char *arg) +{ + if (sscanf(arg, " %d %d %d", &gvt_low_gm_sz, + &gvt_high_gm_sz, &gvt_fence_sz) != 3) { + return -1; + } + printf("passed gvt-g optargs low_gm %d, high_gm %d, fence %d\n", + gvt_low_gm_sz, gvt_high_gm_sz, gvt_fence_sz); + + return 0; +} + +static void +pci_gvt_write(struct vmctx *ctx, int vcpu, struct pci_vdev *pi, + int baridx, uint64_t offset, int size, uint64_t value) +{ + /* null function, pci config space write should be trapped */ + DPRINTF(("%s: write vcpu %d, baridx %d, offset %"PRIu64", size %d, " + "value %"PRIu64"\n", + __func__, vcpu, baridx, offset, size, value)); +} + +static uint64_t +pci_gvt_read(struct vmctx *ctx, int vcpu, struct pci_vdev *pi, + int baridx, uint64_t offset, int size) +{ + /* null function, pci config space read should be trapped */ + DPRINTF(("%s: read vcpu %d, baridx %d, offset %"PRIu64", size %d\n", + __func__, vcpu, baridx, offset, size)); + + return 0; +} + +static int +gvt_init_config(struct pci_gvt *gvt) +{ + int ret, len; + char name[PATH_MAX]; + uint8_t cap_ptr = 0; + + gvt->host_config = calloc(1, 256); + if (!gvt->host_config) { + perror("gvt:calloc host config failed\n"); + return -1; + } + + snprintf(name, sizeof(name), + "/sys/bus/pci/devices/%04x:%02x:%02x.%x/config", + gvt->addr.domain, gvt->addr.bus, gvt->addr.slot, + gvt->addr.function); + gvt->host_config_fd = open(name, O_RDONLY); + if (gvt->host_config_fd == -1) { + perror("gvt:open host pci config failed\n"); + return -1; + } + + len = 256; + ret = pread(gvt->host_config_fd, gvt->host_config, len, 0); + if (ret < len) { + ret = ret < 0 ? -errno : -EFAULT; + perror("failed to read host device config space"); + return ret; + } + + /* initialize config space */ + pci_set_cfgdata16(gvt->gvt_pi, PCIR_VENDOR, gvt->host_config[0]); + pci_set_cfgdata16(gvt->gvt_pi, PCIR_DEVICE, gvt->host_config[0x02]); + /* status */ + pci_set_cfgdata16(gvt->gvt_pi, PCIR_STATUS, gvt->host_config[0x06]); + /* revision id */ + pci_set_cfgdata16(gvt->gvt_pi, PCIR_REVID, gvt->host_config[0x08]); + /* class and sub class */ + pci_set_cfgdata8(gvt->gvt_pi, PCIR_CLASS, PCIC_DISPLAY); + pci_set_cfgdata8(gvt->gvt_pi, PCIR_SUBCLASS, PCIS_DISPLAY_VGA); + /* capability */ + pci_set_cfgdata8(gvt->gvt_pi, PCIR_CAP_PTR, gvt->host_config[0x34]); + cap_ptr = gvt->host_config[0x34]; + while (cap_ptr != 0) { + pci_set_cfgdata32(gvt->gvt_pi, cap_ptr, + gvt->host_config[cap_ptr]); + pci_set_cfgdata32(gvt->gvt_pi, cap_ptr + 4, + gvt->host_config[cap_ptr + 4]); + pci_set_cfgdata32(gvt->gvt_pi, cap_ptr + 8, + gvt->host_config[cap_ptr + 8]); + pci_set_cfgdata32(gvt->gvt_pi, cap_ptr + 12, + gvt->host_config[cap_ptr + 12]); + cap_ptr = gvt->host_config[cap_ptr + 1]; + } + + /* SNB: processor graphics control register */ + pci_set_cfgdata16(gvt->gvt_pi, 0x50, gvt->host_config[0x50]); + /* processor graphics control register */ + pci_set_cfgdata16(gvt->gvt_pi, 0x52, gvt->host_config[0x52]); + + ret = pci_emul_alloc_bar(gvt->gvt_pi, 0, PCIBAR_MEM32, + 16 * 1024 * 1024); + assert(ret == 0); + /* same as host, but guest only use partition of it by ballon */ + ret = pci_emul_alloc_bar(gvt->gvt_pi, 2, PCIBAR_MEM32, + 256 * 1024 * 1024); + assert(ret == 0); + /* same as host, lagecy vga usage */ + ret = pci_emul_alloc_bar(gvt->gvt_pi, 4, PCIBAR_IO, 64); + assert(ret == 0); + + close(gvt->host_config_fd); + + return 0; +} + +static int +gvt_create_instance(struct pci_gvt *gvt) +{ + const char *path = "/sys/kernel/gvt/control/create_gvt_instance"; + int ret = 0; + + gvt->gvt_file = fopen(path, "w"); + if (gvt->gvt_file == NULL) { + WPRINTF(("GVT: open %s failed\n", path)); + return errno; + } + + DPRINTF(("GVT: %s: domid=%d, low_gm_sz=%dMB, high_gm_sz=%dMB, " + "fence_sz=%d\n", __func__, guest_domid, + gvt_low_gm_sz, gvt_high_gm_sz, gvt_fence_sz)); + + if (gvt_low_gm_sz <= 0 || gvt_high_gm_sz <= 0 || gvt_fence_sz <= 0) { + WPRINTF(("GVT: %s failed: invalid parameters!\n", __func__)); + abort(); + } + + /* The format of the string is: + * domid,aperture_size,gm_size,fence_size. This means we want the gvt + * driver to create a gvt instanc for Domain domid with the required + * parameters. NOTE: aperture_size and gm_size are in MB. + */ + if (fprintf(gvt->gvt_file, "%d,%u,%u,%u\n", guest_domid, + gvt_low_gm_sz, gvt_high_gm_sz, gvt_fence_sz) < 0) + ret = errno; + + if (fclose(gvt->gvt_file) != 0) + return errno; + + if (!ret) + gvt->instance_created = 1; + + return ret; +} + +/* did not find deinit function caller, leave it here only */ +static int +gvt_destroy_instance(struct pci_gvt *gvt) +{ + const char *path = "/sys/kernel/gvt/control/create_gvt_instance"; + int ret = 0; + + gvt->gvt_file = fopen(path, "w"); + if (gvt->gvt_file == NULL) { + WPRINTF(("gvt: error: open %s failed", path)); + return errno; + } + + /* -domid means we want the gvt driver to free the gvt instance + * of Domain domid. + **/ + if (fprintf(gvt->gvt_file, "%d\n", -guest_domid) < 0) + ret = errno; + + if (fclose(gvt->gvt_file) != 0) + return errno; + + if (!ret) + gvt->instance_created = 0; + + return ret; +} + +static int +pci_gvt_init(struct vmctx *ctx, struct pci_vdev *pi, char *opts) +{ + int ret; + struct pci_gvt *gvt = NULL; + + gvt = calloc(1, sizeof(struct pci_gvt)); + if (!gvt) { + perror("gvt:calloc gvt failed\n"); + return -1; + } + + gvt->addr.domain = 0; + gvt->addr.bus = pi->bus; + gvt->addr.slot = pi->slot; + gvt->addr.function = pi->func; + + pi->arg = gvt; + gvt->gvt_pi = pi; + guest_domid = ctx->vmid; + + ret = gvt_init_config(gvt); + + ret = gvt_create_instance(gvt); + + return ret; +} + +void +pci_gvt_deinit(struct vmctx *ctx, struct pci_vdev *pi, char *opts) +{ + int ret; + struct pci_gvt *gvt = pi->arg; + + ret = gvt_destroy_instance(gvt); + if (ret) + WPRINTF(("GVT: %s: failed: errno=%d\n", __func__, ret)); +} + +struct pci_vdev_ops pci_ops_gvt = { + .class_name = "pci-gvt", + .vdev_init = pci_gvt_init, + .vdev_deinit = pci_gvt_deinit, + .vdev_barwrite = pci_gvt_write, + .vdev_barread = pci_gvt_read, +}; + +DEFINE_PCI_DEVTYPE(pci_ops_gvt);