diff --git a/devicemodel/hw/pci/passthrough.c b/devicemodel/hw/pci/passthrough.c index 4c227aea9..13acf494c 100644 --- a/devicemodel/hw/pci/passthrough.c +++ b/devicemodel/hw/pci/passthrough.c @@ -174,6 +174,113 @@ static inline int ptdev_msix_pba_bar(struct passthru_dev *ptdev) return ptdev->dev->msix.pba_bar; } +static void +load_pci_rombar(struct vmctx *ctx, struct pci_vdev *dev, char *rom_file) +{ + FILE *fp; + int file_size, rom_size; + uint8_t rom_header[4]; + size_t read_size; + char *rom_buffer; + struct passthru_dev *ptdev; + uint64_t bar_addr; + + /* + * It is unnecessary to check dev & dev->arg again as it is already + * checked in caller. + */ + ptdev = (struct passthru_dev *) dev->arg; + fp = fopen(rom_file, "rb"); + if (fp == NULL) { + pr_warn("Fail to open %s rom_file\n", rom_file); + ptdev->need_rombar = false; + } else { + rom_buffer = NULL; + read_size = fread(rom_header, 1, 4, fp); + file_size = 0; + if (read_size != 4) + pr_warn("%s: Fail to read rom_header\n", __func__); + + if ((rom_header[0] == 0x55) && (rom_header[1] == 0xaa)) { + /* check file size */ + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + if (file_size > (2048 * 1024)) { + pr_warn("Rom file is too big.\n"); + ptdev->need_rombar = false; + } + } else { + pr_warn("Incorrect Rom file format\n"); + ptdev->need_rombar = false; + } + if (ptdev->need_rombar) { + rom_size = ALIGN_UP(file_size, 4096); + /* round up to a power of 2 */ + if ((rom_size & (rom_size - 1)) != 0) + rom_size = 1UL << fls(rom_size); + + if (posix_memalign((void **)&rom_buffer, 4096, rom_size) == 0) { + ptdev->rom_buffer = rom_buffer; + read_size = fread(rom_buffer, 1, file_size, fp); + if (read_size != file_size) { + pr_warn("%s: read size is different with rom_size\n", __func__); + } + pci_emul_alloc_bar(dev, PCI_ROMBAR, PCIBAR_ROM, rom_size); + + /* Setup the EPT mapping for ROM bar */ + bar_addr = dev->bar[PCI_ROMBAR].addr; + if (vm_map_memseg_vma(ctx, rom_size, bar_addr, + (uint64_t)rom_buffer, PROT_READ)) { + pr_err("%s: Fail to map ROM bar\n", __func__); + free(rom_buffer); + ptdev->need_rombar = false; + dev->bar[PCI_ROMBAR].addr = 0; + dev->bar[PCI_ROMBAR].size = 0; + } else { + /* Update the enable flag */ + pci_set_cfgdata32(dev, PCIR_BIOS, (bar_addr | PCIM_BIOS_ENABLE)); + } + } else { + ptdev->need_rombar = false; + pr_err("Fail to allocate buffer for rom_file\n"); + } + } + fclose(fp); + } + return; +} + +static void +release_pci_rombar(struct vmctx *ctx, struct pci_vdev *dev) +{ + int error; + struct acrn_vm_memmap rom_unmap; + struct passthru_dev *ptdev; + + /* + * As it is already checked in caller, it is unnecessary to + * check dev and dev->arg again. + */ + ptdev = (struct passthru_dev *) dev->arg; + + rom_unmap.user_vm_pa = dev->bar[PCI_ROMBAR].addr; + rom_unmap.len = dev->bar[PCI_ROMBAR].size; + /* remove the EPT mapping for PCI_rombar. + * WA: Use ACRN_MEMMAP_MMIO to del the mapping + */ + rom_unmap.type = ACRN_MEMMAP_MMIO; + error = ioctl(ctx->fd, ACRN_IOCTL_UNSET_MEMSEG, &rom_unmap); + if (error) { + pr_err("%s: unset_memseg ioctl() returned an error: %s\n", + __func__, errormsg(errno)); + } + free(ptdev->rom_buffer); + ptdev->rom_buffer = NULL; + ptdev->need_rombar = false; + return; +} + static int cfginitbar(struct vmctx *ctx, struct passthru_dev *ptdev) { @@ -600,6 +707,7 @@ passthru_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) struct acrn_pcidev pcidev = {}; uint16_t vendor = 0, device = 0; uint8_t class = 0; + char rom_file[256]; ptdev = NULL; error = -EINVAL; @@ -615,6 +723,7 @@ passthru_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) return -EINVAL; } + memset(rom_file, 0, sizeof(rom_file)); while ((opt = strsep(&opts, ",")) != NULL) { if (!strncmp(opt, "keep_gsi", 8)) keep_gsi = true; @@ -711,6 +820,16 @@ passthru_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) error = ACRN_PTDEV_IRQ_MSI; } + if (class != 3) { + if (ptdev->need_rombar) { + pr_warn("Virtual PCI rom is only supported for GPU device\n"); + ptdev->need_rombar = false; + } + } + + if (ptdev->need_rombar) + load_pci_rombar(ctx, dev, rom_file); + if (is_intel_graphics_dev(dev)) { if (is_rtvm) { pr_err("%s RTVM doesn't support GVT-D.", __func__); @@ -805,6 +924,11 @@ passthru_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts) vm_reset_ptdev_intx_info(ctx, virt_bdf, ptdev->phys_bdf, dev->lintr.ioapic_irq, false); } + if (ptdev->need_rombar) { + release_pci_rombar(ctx, dev); + ptdev->need_rombar = false; + } + if (ptdev) phys_bdf = ptdev->phys_bdf; diff --git a/devicemodel/include/passthru.h b/devicemodel/include/passthru.h index 7f693913e..655a17000 100644 --- a/devicemodel/include/passthru.h +++ b/devicemodel/include/passthru.h @@ -16,7 +16,7 @@ struct passthru_dev { struct pci_vdev *dev; - struct pcibar bar[PCI_BARMAX + 1]; + struct pcibar bar[PCI_BARMAX + 2]; struct { int capoff; } msi; @@ -36,6 +36,8 @@ struct passthru_dev { */ bool need_reset; bool d3hot_reset; + bool need_rombar; + char *rom_buffer; bool (*has_virt_pcicfg_regs)(int offset); };