Page MenuHomeFreeBSD

D27456.id86487.diff
No OneTemporary

D27456.id86487.diff

diff --git a/lib/libvmmapi/vmmapi.h b/lib/libvmmapi/vmmapi.h
--- a/lib/libvmmapi/vmmapi.h
+++ b/lib/libvmmapi/vmmapi.h
@@ -73,6 +73,7 @@
VM_SYSMEM,
VM_BOOTROM,
VM_FRAMEBUFFER,
+ VM_VIDEOBIOS
};
/*
@@ -180,6 +181,8 @@
vm_paddr_t gpa, size_t len, vm_paddr_t hpa);
int vm_unmap_pptdev_mmio(struct vmctx *ctx, int bus, int slot, int func,
vm_paddr_t gpa, size_t len);
+int vm_get_vbios(struct vmctx *ctx, int bus, int slot, int func,
+ uint16_t vendor, uint16_t dev_id, void *bios, uint64_t *size);
int vm_setup_pptdev_msi(struct vmctx *ctx, int vcpu, int bus, int slot,
int func, uint64_t addr, uint64_t msg, int numvec);
int vm_setup_pptdev_msix(struct vmctx *ctx, int vcpu, int bus, int slot,
diff --git a/lib/libvmmapi/vmmapi.c b/lib/libvmmapi/vmmapi.c
--- a/lib/libvmmapi/vmmapi.c
+++ b/lib/libvmmapi/vmmapi.c
@@ -1009,6 +1009,30 @@
return (ioctl(ctx->fd, VM_UNMAP_PPTDEV_MMIO, &pptmmio));
}
+int
+vm_get_vbios(struct vmctx *ctx, int bus, int slot, int func, uint16_t vendor,
+ uint16_t dev_id, void *bios, uint64_t *size)
+{
+ struct vm_vbios vbios;
+
+ bzero(&vbios, sizeof(vbios));
+ vbios.bus = bus;
+ vbios.slot = slot;
+ vbios.func = func;
+ vbios.vendor = vendor;
+ vbios.dev_id = dev_id;
+ vbios.bios = bios;
+ if (size != NULL)
+ vbios.size = *size;
+
+ const int error = ioctl(ctx->fd, VM_GET_VBIOS, &vbios);
+
+ if (size)
+ *size = vbios.size;
+
+ return (error);
+}
+
int
vm_setup_pptdev_msi(struct vmctx *ctx, int vcpu, int bus, int slot, int func,
uint64_t addr, uint64_t msg, int numvec)
@@ -1684,7 +1708,7 @@
VM_SET_CAPABILITY, VM_GET_CAPABILITY, VM_BIND_PPTDEV,
VM_UNBIND_PPTDEV, VM_MAP_PPTDEV_MMIO, VM_PPTDEV_MSI,
VM_PPTDEV_MSIX, VM_UNMAP_PPTDEV_MMIO, VM_PPTDEV_DISABLE_MSIX,
- VM_INJECT_NMI, VM_STATS, VM_STAT_DESC,
+ VM_GET_VBIOS, VM_INJECT_NMI, VM_STATS, VM_STAT_DESC,
VM_SET_X2APIC_STATE, VM_GET_X2APIC_STATE,
VM_GET_HPET_CAPABILITIES, VM_GET_GPA_PMAP, VM_GLA2GPA,
VM_GLA2GPA_NOFAULT,
diff --git a/sys/amd64/include/vmm_dev.h b/sys/amd64/include/vmm_dev.h
--- a/sys/amd64/include/vmm_dev.h
+++ b/sys/amd64/include/vmm_dev.h
@@ -146,6 +146,16 @@
size_t len;
};
+struct vm_vbios {
+ int bus;
+ int slot;
+ int func;
+ uint16_t vendor;
+ uint16_t dev_id;
+ void *bios;
+ uint64_t size;
+};
+
struct vm_pptdev_msi {
int vcpu;
int bus;
@@ -309,6 +319,7 @@
IOCNUM_PPTDEV_MSIX = 44,
IOCNUM_PPTDEV_DISABLE_MSIX = 45,
IOCNUM_UNMAP_PPTDEV_MMIO = 46,
+ IOCNUM_GET_VBIOS = 47,
/* statistics */
IOCNUM_VM_STATS = 50,
@@ -427,6 +438,8 @@
_IOW('v', IOCNUM_PPTDEV_DISABLE_MSIX, struct vm_pptdev)
#define VM_UNMAP_PPTDEV_MMIO \
_IOW('v', IOCNUM_UNMAP_PPTDEV_MMIO, struct vm_pptdev_mmio)
+#define VM_GET_VBIOS \
+ _IOWR('v', IOCNUM_GET_VBIOS, struct vm_vbios)
#define VM_INJECT_NMI \
_IOW('v', IOCNUM_INJECT_NMI, struct vm_nmi)
#define VM_STATS \
diff --git a/sys/amd64/vmm/amd/amdgpu_bios.h b/sys/amd64/vmm/amd/amdgpu_bios.h
new file mode 100644
--- /dev/null
+++ b/sys/amd64/vmm/amd/amdgpu_bios.h
@@ -0,0 +1,37 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR OR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#pragma once
+
+#include <machine/vmm.h>
+#include <machine/vmm_dev.h>
+
+int vm_amdgpu_get_vbios(struct vm *vm, int bus, int slot, int func,
+ uint16_t vendor, uint16_t dev_id, void *bios, uint64_t *size);
diff --git a/sys/amd64/vmm/amd/amdgpu_bios.c b/sys/amd64/vmm/amd/amdgpu_bios.c
new file mode 100644
--- /dev/null
+++ b/sys/amd64/vmm/amd/amdgpu_bios.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2008 Advanced Micro Devices, Inc.
+ * Copyright 2008 Red Hat Inc.
+ * Copyright 2009 Jerome Glisse.
+ * Copyright 2021 Beckhoff Automation GmbH & Co. KG
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Dave Airlie
+ * Alex Deucher
+ * Jerome Glisse
+ */
+
+/*
+ * This file is a modified copy of <https://github.com/torvalds/linux/blob/bddbacc9e0373fc1f1f7963fa2a7838dd06e4b1b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c>
+ */
+
+/* includes */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/malloc.h>
+
+#include <dev/pci/pcivar.h>
+
+#include "amdgpu_bios.h"
+#include "atombios.h"
+#include "contrib/dev/acpica/include/acpi.h"
+#include "contrib/dev/acpica/include/acpixf.h"
+
+/* type definitons */
+#define __iomem
+#define true 1
+#define false 0
+typedef uint32_t acpi_size;
+
+/* log definitions */
+#define DRM_ERROR uprintf
+
+#define GFP_NATIVE_MASK (M_NOWAIT | M_WAITOK | M_USE_RESERVE | M_ZERO)
+#define GFP_KERNEL M_WAITOK
+#define __GFP_ZERO M_ZERO
+#define kzalloc(size, flags) kmalloc(size, (flags) | __GFP_ZERO)
+
+#define memcpy_fromio(a, b, c) memcpy((a), (b), (c))
+
+#define acpi_get_table AcpiGetTable
+
+#define PCI_DEVFN(bus, slot, func) ((((bus) & 0xff) << 8) | (((slot) & 0x1f) << 3) | ((func) & 0x07))
+#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
+#define PCI_FUNC(devfn) ((devfn) & 0x07)
+#define PCI_BUS_NUM(devfn) (((devfn) >> 8) & 0xff)
+
+MALLOC_DECLARE(M_VMMDEV);
+
+typedef unsigned gfp_t;
+
+static inline gfp_t
+linux_check_m_flags(gfp_t flags)
+{
+ const gfp_t m = M_NOWAIT | M_WAITOK;
+
+ /* make sure either M_NOWAIT or M_WAITOK is set */
+ if ((flags & m) == 0)
+ flags |= M_NOWAIT;
+ else if ((flags & m) == m)
+ flags &= ~M_WAITOK;
+
+ /* mask away LinuxKPI specific flags */
+ return (flags & GFP_NATIVE_MASK);
+}
+
+static inline void *
+kmalloc(size_t size, gfp_t flags)
+{
+ return (malloc(size, M_VMMDEV, linux_check_m_flags(flags)));
+}
+
+static inline void
+kfree(const void *ptr)
+{
+ free(__DECONST(void *, ptr), M_VMMDEV);
+}
+
+static inline void *
+kmemdup(const void *src, size_t len, gfp_t gfp)
+{
+ void *dst;
+
+ dst = kmalloc(len, gfp);
+ if (dst != NULL) {
+ memcpy(dst, src, len);
+ }
+ return (dst);
+}
+
+struct device {
+ device_t bsddev;
+};
+
+struct pci_dev {
+ struct device dev;
+ uint16_t device;
+ uint16_t vendor;
+ unsigned int devfn;
+};
+struct amdgpu_device {
+ struct pci_dev *pdev;
+ uint8_t *bios;
+ uint32_t bios_size;
+};
+
+/*
+ * BIOS.
+ */
+
+#define AMD_VBIOS_SIGNATURE " 761295520"
+#define AMD_VBIOS_SIGNATURE_OFFSET 0x30
+#define AMD_VBIOS_SIGNATURE_SIZE sizeof(AMD_VBIOS_SIGNATURE)
+#define AMD_VBIOS_SIGNATURE_END (AMD_VBIOS_SIGNATURE_OFFSET + AMD_VBIOS_SIGNATURE_SIZE)
+#define AMD_IS_VALID_VBIOS(p) ((p)[0] == 0x55 && (p)[1] == 0xAA)
+#define AMD_VBIOS_LENGTH(p) ((p)[2] << 9)
+
+/* Check if current bios is an ATOM BIOS.
+ * Return true if it is ATOM BIOS. Otherwise, return false.
+ */
+static bool check_atom_bios(uint8_t *bios, size_t size)
+{
+ uint16_t tmp, bios_header_start;
+
+ if (!bios || size < 0x49) {
+ return false;
+ }
+
+ if (!AMD_IS_VALID_VBIOS(bios)) {
+ return false;
+ }
+
+ bios_header_start = bios[0x48] | (bios[0x49] << 8);
+ if (!bios_header_start) {
+ return false;
+ }
+
+ tmp = bios_header_start + 4;
+ if (size < tmp) {
+ return false;
+ }
+
+ if (!memcmp(bios + tmp, "ATOM", 4) ||
+ !memcmp(bios + tmp, "MOTA", 4)) {
+ return true;
+ }
+
+ return false;
+}
+
+#define pci_map_rom(pdev, sizep) \
+ vga_pci_map_bios(pdev->dev.bsddev, sizep)
+#define pci_unmap_rom(pdev, bios) \
+ vga_pci_unmap_bios(pdev->dev.bsddev, bios)
+
+static
+bool amdgpu_read_bios(struct amdgpu_device *adev)
+{
+ uint8_t __iomem *bios;
+ size_t size;
+
+ adev->bios = NULL;
+ /* XXX: some cards may return 0 for rom size? ddx has a workaround */
+ bios = pci_map_rom(adev->pdev, &size);
+ if (!bios) {
+ return false;
+ }
+
+ adev->bios = kzalloc(size, GFP_KERNEL);
+ if (adev->bios == NULL) {
+ pci_unmap_rom(adev->pdev, bios);
+ return false;
+ }
+ adev->bios_size = size;
+ memcpy_fromio(adev->bios, bios, size);
+ pci_unmap_rom(adev->pdev, bios);
+
+ if (!check_atom_bios(adev->bios, size)) {
+ kfree(adev->bios);
+ return false;
+ }
+
+ return true;
+}
+
+static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev)
+{
+ struct acpi_table_header *hdr;
+ acpi_size tbl_size;
+ UEFI_ACPI_VFCT *vfct;
+ unsigned offset;
+
+ if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr)))
+ return false;
+ tbl_size = hdr->Length;
+ if (tbl_size < sizeof(UEFI_ACPI_VFCT)) {
+ DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n");
+ return false;
+ }
+
+ vfct = (UEFI_ACPI_VFCT *)hdr;
+ offset = vfct->VBIOSImageOffset;
+
+ while (offset < tbl_size) {
+ GOP_VBIOS_CONTENT *vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + offset);
+ VFCT_IMAGE_HEADER *vhdr = &vbios->VbiosHeader;
+
+ offset += sizeof(VFCT_IMAGE_HEADER);
+ if (offset > tbl_size) {
+ DRM_ERROR("ACPI VFCT image header truncated\n");
+ return false;
+ }
+
+ offset += vhdr->ImageLength;
+ if (offset > tbl_size) {
+ DRM_ERROR("ACPI VFCT image truncated\n");
+ return false;
+ }
+
+ if (vhdr->ImageLength &&
+ vhdr->PCIBus == PCI_BUS_NUM(adev->pdev->devfn) &&
+ vhdr->PCIDevice == PCI_SLOT(adev->pdev->devfn) &&
+ vhdr->PCIFunction == PCI_FUNC(adev->pdev->devfn) &&
+ vhdr->VendorID == adev->pdev->vendor &&
+ vhdr->DeviceID == adev->pdev->device) {
+ adev->bios = kmemdup(&vbios->VbiosContent,
+ vhdr->ImageLength,
+ GFP_KERNEL);
+
+ if (!check_atom_bios(adev->bios, vhdr->ImageLength)) {
+ kfree(adev->bios);
+ return false;
+ }
+ adev->bios_size = vhdr->ImageLength;
+ return true;
+ }
+ }
+
+ DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n");
+ return false;
+}
+
+static
+bool amdgpu_get_bios(struct amdgpu_device *adev)
+{
+ if (amdgpu_acpi_vfct_bios(adev)) {
+ goto success;
+ }
+
+ if (amdgpu_read_bios(adev)) {
+ goto success;
+ }
+
+ DRM_ERROR("Unable to locate a BIOS ROM\n");
+ return false;
+
+success:
+ return true;
+}
+
+int
+vm_amdgpu_get_vbios(struct vm *vm, int bus, int slot, int func,
+ uint16_t vendor, uint16_t dev_id, void *bios, uint64_t *size)
+{
+ int error = 0;
+
+ struct pci_dev pdev;
+ struct amdgpu_device adev;
+
+ adev.pdev = &pdev;
+ pdev.dev.bsddev = pci_find_bsf(bus, slot, func);
+ pdev.devfn = PCI_DEVFN(bus, slot, func);
+ pdev.vendor = vendor;
+ pdev.device = dev_id;
+
+ if (!amdgpu_get_bios(&adev))
+ return ENOENT;
+
+ if (bios) {
+ *size = min(adev.bios_size, *size);
+ error = copyout(adev.bios, bios, *size);
+ } else if (size) {
+ *size = adev.bios_size;
+ }
+
+ kfree(adev.bios);
+
+ return (error);
+}
diff --git a/sys/amd64/vmm/amd/atombios.h b/sys/amd64/vmm/amd/atombios.h
new file mode 100644
--- /dev/null
+++ b/sys/amd64/vmm/amd/atombios.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2006-2007 Advanced Micro Devices, Inc.
+ * Copyright 2021 Beckhoff Automation GmbH & Co. KG
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * This file is a modified copy of <https://github.com/torvalds/linux/blob/229f7b1d6344ea35fff0b113e4d91128921f8937/drivers/gpu/drm/amd/include/atombios.h>
+ */
+
+/****************************************************************************/
+/*Portion I: Definitions shared between VBIOS and Driver */
+/****************************************************************************/
+
+#pragma once
+
+typedef unsigned long ULONG;
+typedef unsigned char UCHAR;
+typedef unsigned short USHORT;
+
+//
+// AMD ACPI Table
+//
+#pragma pack(1)
+
+typedef struct {
+ ULONG Signature;
+ ULONG TableLength; //Length
+ UCHAR Revision;
+ UCHAR Checksum;
+ UCHAR OemId[6];
+ UCHAR OemTableId[8]; //UINT64 OemTableId;
+ ULONG OemRevision;
+ ULONG CreatorId;
+ ULONG CreatorRevision;
+} AMD_ACPI_DESCRIPTION_HEADER;
+
+typedef struct {
+ AMD_ACPI_DESCRIPTION_HEADER SHeader;
+ UCHAR TableUUID[16]; //0x24
+ ULONG VBIOSImageOffset; //0x34. Offset to the first GOP_VBIOS_CONTENT block from the beginning of the stucture.
+ ULONG Lib1ImageOffset; //0x38. Offset to the first GOP_LIB1_CONTENT block from the beginning of the stucture.
+ ULONG Reserved[4]; //0x3C
+}UEFI_ACPI_VFCT;
+
+typedef struct {
+ ULONG PCIBus; //0x4C
+ ULONG PCIDevice; //0x50
+ ULONG PCIFunction; //0x54
+ USHORT VendorID; //0x58
+ USHORT DeviceID; //0x5A
+ USHORT SSVID; //0x5C
+ USHORT SSID; //0x5E
+ ULONG Revision; //0x60
+ ULONG ImageLength; //0x64
+}VFCT_IMAGE_HEADER;
+
+typedef struct {
+ VFCT_IMAGE_HEADER VbiosHeader;
+ UCHAR VbiosContent[1];
+}GOP_VBIOS_CONTENT;
+
+#pragma pack()
diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c
--- a/sys/amd64/vmm/vmm.c
+++ b/sys/amd64/vmm/vmm.c
@@ -134,7 +134,7 @@
bool sysmem;
struct vm_object *object;
};
-#define VM_MAX_MEMSEGS 3
+#define VM_MAX_MEMSEGS 4
struct mem_map {
vm_paddr_t gpa;
diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c
--- a/sys/amd64/vmm/vmm_dev.c
+++ b/sys/amd64/vmm/vmm_dev.c
@@ -60,6 +60,7 @@
#include <machine/vmm_snapshot.h>
#include <x86/apicreg.h>
+#include "amd/amdgpu_bios.h"
#include "vmm_lapic.h"
#include "vmm_stat.h"
#include "vmm_mem.h"
@@ -366,6 +367,7 @@
struct vm_capability *vmcap;
struct vm_pptdev *pptdev;
struct vm_pptdev_mmio *pptmmio;
+ struct vm_vbios *vbios;
struct vm_pptdev_msi *pptmsi;
struct vm_pptdev_msix *pptmsix;
struct vm_nmi *vmnmi;
@@ -533,6 +535,13 @@
error = ppt_unmap_mmio(sc->vm, pptmmio->bus, pptmmio->slot,
pptmmio->func, pptmmio->gpa, pptmmio->len);
break;
+ case VM_GET_VBIOS:
+ vbios = (struct vm_vbios *)data;
+ /* currently only amd cpus are supported */
+ error = vm_amdgpu_get_vbios(sc->vm, vbios->bus, vbios->slot,
+ vbios->func, vbios->vendor, vbios->dev_id, vbios->bios,
+ &vbios->size);
+ break;
case VM_BIND_PPTDEV:
pptdev = (struct vm_pptdev *)data;
error = vm_assign_pptdev(sc->vm, pptdev->bus, pptdev->slot,
diff --git a/sys/dev/pci/pcireg.h b/sys/dev/pci/pcireg.h
--- a/sys/dev/pci/pcireg.h
+++ b/sys/dev/pci/pcireg.h
@@ -1098,3 +1098,9 @@
#define PCIM_OSC_CTL_PCIE_PME 0x04 /* PCIe Native Power Mgt Events */
#define PCIM_OSC_CTL_PCIE_AER 0x08 /* PCIe Advanced Error Reporting */
#define PCIM_OSC_CTL_PCIE_CAP_STRUCT 0x10 /* Various Capability Structures */
+
+/*
+ * PCI Vendors
+ */
+#define PCI_VENDOR_INTEL 0x8086
+#define PCI_VENDOR_AMD 0x1002
diff --git a/sys/modules/vmm/Makefile b/sys/modules/vmm/Makefile
--- a/sys/modules/vmm/Makefile
+++ b/sys/modules/vmm/Makefile
@@ -58,6 +58,7 @@
svm_support.S \
npt.c \
ivrs_drv.c \
+ amdgpu_bios.c \
amdvi_hw.c \
svm_msr.c
diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile
--- a/usr.sbin/bhyve/Makefile
+++ b/usr.sbin/bhyve/Makefile
@@ -42,6 +42,7 @@
pci_emul.c \
pci_hda.c \
pci_fbuf.c \
+ pci_gvt-d_amd.c \
pci_hostbridge.c \
pci_irq.c \
pci_lpc.c \
diff --git a/usr.sbin/bhyve/pci_emul.h b/usr.sbin/bhyve/pci_emul.h
--- a/usr.sbin/bhyve/pci_emul.h
+++ b/usr.sbin/bhyve/pci_emul.h
@@ -42,6 +42,8 @@
#include <assert.h>
#define PCI_BARMAX PCIR_MAX_BAR_0 /* BAR registers in a Type 0 header */
+#define PCI_BARMAX_WITH_ROM (PCI_BARMAX + 1)
+#define PCI_ROM_IDX (PCI_BARMAX + 1)
struct vmctx;
struct pci_devinst;
@@ -76,8 +78,8 @@
struct pci_devinst *pi, int baridx,
uint64_t offset, int size);
- void (*pe_baraddr)(struct vmctx *ctx, struct pci_devinst *pi,
- int baridx, int enabled, uint64_t address);
+ int (*pe_baraddr)(struct vmctx *ctx, struct pci_devinst *pi, int baridx,
+ int enabled, uint64_t address);
/* Save/restore device state */
int (*pe_snapshot)(struct vm_snapshot_meta *meta);
@@ -92,13 +94,15 @@
PCIBAR_IO,
PCIBAR_MEM32,
PCIBAR_MEM64,
- PCIBAR_MEMHI64
+ PCIBAR_MEMHI64,
+ PCIBAR_ROM,
};
struct pcibar {
enum pcibar_type type; /* io or memory */
uint64_t size;
uint64_t addr;
+ uint8_t lobits;
};
#define PI_NAMESZ 40
@@ -164,7 +168,8 @@
void *pi_arg; /* devemu-private data */
u_char pi_cfgdata[PCI_REGMAX + 1];
- struct pcibar pi_bar[PCI_BARMAX + 1];
+ /* ROM is handled like a BAR */
+ struct pcibar pi_bar[PCI_BARMAX_WITH_ROM + 1];
};
struct msicap {
diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c
--- a/usr.sbin/bhyve/pci_emul.c
+++ b/usr.sbin/bhyve/pci_emul.c
@@ -38,6 +38,7 @@
#include <vm/pmap.h>
#include <ctype.h>
+#include <err.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
@@ -73,6 +74,10 @@
#define MAXSLOTS (PCI_SLOTMAX + 1)
#define MAXFUNCS (PCI_FUNCMAX + 1)
+#define GB (1024 * 1024 * 1024UL)
+
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
struct funcinfo {
nvlist_t *fi_config;
struct pci_devemu *fi_pde;
@@ -102,10 +107,21 @@
SET_DECLARE(pci_devemu_set, struct pci_devemu);
static uint64_t pci_emul_iobase;
+static uint64_t pci_emul_iolim;
static uint64_t pci_emul_membase32;
+static uint64_t pci_emul_memlim32;
static uint64_t pci_emul_membase64;
static uint64_t pci_emul_memlim64;
+struct pcibarlist {
+ struct pci_devinst *pdi;
+ int idx;
+ enum pcibar_type type;
+ uint64_t size;
+ struct pcibarlist *next;
+};
+struct pcibarlist *pci_bars;
+
#define PCI_EMUL_IOBASE 0x2000
#define PCI_EMUL_IOLIMIT 0x10000
@@ -114,6 +130,7 @@
SYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE);
#define PCI_EMUL_MEMLIMIT32 PCI_EMUL_ECFG_BASE
+#define PCI_EMUL_MEMSIZE64 (32 * GB)
static struct pci_devemu *pci_emul_finddev(const char *name);
static void pci_lintr_route(struct pci_devinst *pi);
@@ -512,6 +529,11 @@
struct mem_range mr;
pe = pi->pi_d;
+ if (pe->pe_baraddr != NULL &&
+ (*pe->pe_baraddr)(
+ pi->pi_vmctx, pi, idx, registration, pi->pi_bar[idx].addr) == 0)
+ return;
+
switch (pi->pi_bar[idx].type) {
case PCIBAR_IO:
bzero(&iop, sizeof(struct inout_port));
@@ -525,9 +547,6 @@
error = register_inout(&iop);
} else
error = unregister_inout(&iop);
- if (pe->pe_baraddr != NULL)
- (*pe->pe_baraddr)(pi->pi_vmctx, pi, idx, registration,
- pi->pi_bar[idx].addr);
break;
case PCIBAR_MEM32:
case PCIBAR_MEM64:
@@ -543,9 +562,13 @@
error = register_mem(&mr);
} else
error = unregister_mem(&mr);
- if (pe->pe_baraddr != NULL)
- (*pe->pe_baraddr)(pi->pi_vmctx, pi, idx, registration,
- pi->pi_bar[idx].addr);
+ break;
+ case PCIBAR_ROM:
+ /* ROM emulation should be handled by pe_baraddr */
+ if (pi->pi_bar[idx].addr != 0)
+ error = EFAULT;
+ else
+ error = 0;
break;
default:
error = EINVAL;
@@ -597,8 +620,9 @@
* the address range decoded by the BAR register.
*/
static void
-update_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type)
+update_bar_address(struct pci_devinst *pi, int idx, uint32_t val)
{
+ int update_idx = idx;
int decode;
if (pi->pi_bar[idx].type == PCIBAR_IO)
@@ -606,51 +630,211 @@
else
decode = memen(pi);
- if (decode)
- unregister_bar(pi, idx);
-
- switch (type) {
+ switch (pi->pi_bar[idx].type) {
+ case PCIBAR_MEMHI64:
+ --update_idx;
case PCIBAR_IO:
case PCIBAR_MEM32:
- pi->pi_bar[idx].addr = addr;
+ case PCIBAR_MEM64: {
+ struct pcibar *bar = &pi->pi_bar[update_idx];
+
+ if (decode && bar->addr)
+ unregister_bar(pi, update_idx);
+
+ if (val == ~0U) {
+ /* guest wants to read size of BAR */
+ pci_set_cfgdata32(pi, PCIR_BAR(idx), ~0U);
+ bar->addr = 0;
+ break;
+ }
+
+ /* guest sets address of BAR */
+ uint64_t mask;
+ uint32_t bar_val;
+ mask = ~(bar->size - 1UL);
+ if (pi->pi_bar[idx].type == PCIBAR_MEMHI64)
+ mask >>= 32UL;
+ bar_val = val & mask;
+ bar_val |= pi->pi_bar[idx].lobits;
+ pci_set_cfgdata32(pi, PCIR_BAR(idx), bar_val);
+
+ /* Only register BAR if it contains a valid address */
+ uint32_t lo, hi;
+ lo = pci_get_cfgdata32(pi, PCIR_BAR(update_idx));
+ hi = 0;
+ if (bar->type == PCIBAR_MEM64)
+ hi = pci_get_cfgdata32(pi, PCIR_BAR(update_idx + 1));
+ if (lo == ~0U || hi == ~0U) {
+ bar->addr = 0;
+ break;
+ }
+
+ if (bar->type == PCIBAR_IO)
+ lo &= PCIM_BAR_IO_BASE;
+ else
+ lo &= PCIM_BAR_MEM_BASE;
+ bar->addr = (uint64_t)lo | ((uint64_t)hi << 32UL);
+ if (decode)
+ register_bar(pi, update_idx);
+
break;
- case PCIBAR_MEM64:
- pi->pi_bar[idx].addr &= ~0xffffffffUL;
- pi->pi_bar[idx].addr |= addr;
+ }
+ case PCIBAR_ROM: {
+ struct pcibar *bar = &pi->pi_bar[update_idx];
+
+ if (decode && bar->lobits && bar->addr)
+ unregister_bar(pi, idx);
+
+ pci_set_cfgdata32(pi, PCIR_BIOS, val);
+
+ /* Update enable bit */
+ bar->lobits = val & PCIM_BIOS_ENABLE;
+
+ /* Update ROM location */
+ if ((val & PCIM_BIOS_ADDR_MASK) == PCIM_BIOS_ADDR_MASK) {
+ /* guest wants to read size of ROM */
+ bar->addr = 0;
+ } else {
+ bar->addr = val & PCIM_BIOS_ADDR_MASK;
+ }
+
+ if (decode && bar->lobits && bar->addr)
+ register_bar(pi, idx);
+
+ break;
+ }
+ case PCIBAR_NONE:
break;
+ default:
+ assert(0);
+ }
+}
+
+static uint32_t
+read_bar_value(struct pci_devinst *pi, int coff, int bytes)
+{
+ uint8_t idx;
+ if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) {
+ idx = (coff - PCIR_BAR(0)) / 4;
+ } else if (coff >= PCIR_BIOS && coff < PCIR_BIOS + 4) {
+ idx = PCI_BARMAX + 1;
+ } else {
+ warnx("%02x is no BAR", coff);
+ return 0;
+ }
+
+ uint8_t update_idx = idx;
+ uint64_t val;
+
+ switch (pi->pi_bar[idx].type) {
case PCIBAR_MEMHI64:
- pi->pi_bar[idx].addr &= 0xffffffff;
- pi->pi_bar[idx].addr |= addr;
+ --update_idx;
+ /* fallthrough */
+ case PCIBAR_IO:
+ case PCIBAR_MEM32:
+ case PCIBAR_MEM64:
+ val = pci_get_cfgdata32(pi, PCIR_BAR(idx));
+ break;
+ case PCIBAR_ROM:
+ val = pci_get_cfgdata32(pi, PCIR_BIOS);
+ /* check if size should be returned instead of address of ROM */
+ if ((val & PCIM_BIOS_ADDR_MASK) == PCIM_BIOS_ADDR_MASK)
+ val = ~0U;
+ break;
+ case PCIBAR_NONE:
+ return 0;
+ default:
+ warnx("%x is no valid BAR type", pi->pi_bar[idx].type);
+ return 0;
+ }
+
+ /* return size of BAR */
+ if (val == ~0U) {
+ val = ~(pi->pi_bar[update_idx].size - 1);
+ val |= pi->pi_bar[update_idx].lobits;
+ if (pi->pi_bar[idx].type == PCIBAR_MEMHI64)
+ val >>= 32;
+ }
+
+ switch (bytes) {
+ case 1:
+ val = (val >> (8 * (coff & 0x03))) & 0xFF;
+ break;
+ case 2:
+ assert((coff & 0x01) == 0);
+ val = (val >> (8 * (coff & 0x02))) & 0xFFFF;
+ break;
+ case 4:
+ assert((coff & 0x03) == 0);
+ val = (uint32_t)val;
break;
default:
assert(0);
}
- if (decode)
- register_bar(pi, idx);
+ return val;
}
int
pci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type,
uint64_t size)
{
- int error;
- uint64_t *baseptr, limit, addr, mask, lobits, bar;
- uint16_t cmd, enbit;
-
- assert(idx >= 0 && idx <= PCI_BARMAX);
+ if ((type != PCIBAR_ROM) && (idx < 0 || idx > PCI_BARMAX)) {
+ errx(4, "Illegal BAR idx");
+ } else if ((type == PCIBAR_ROM) && (idx != PCI_ROM_IDX)) {
+ errx(4, "Illegal ROM idx");
+ }
if ((size & (size - 1)) != 0)
size = 1UL << flsl(size); /* round up to a power of 2 */
/* Enforce minimum BAR sizes required by the PCI standard */
if (type == PCIBAR_IO) {
- if (size < 4)
- size = 4;
+ size = max(size, 4);
+ } else if (type == PCIBAR_ROM) {
+ size = max(size, ~PCIM_BIOS_ADDR_MASK);
} else {
- if (size < 16)
- size = 16;
+ size = max(size, 16);
+ }
+
+ struct pcibarlist *newBar = malloc(sizeof(struct pcibarlist));
+ memset(newBar, 0, sizeof(struct pcibarlist));
+ newBar->pdi = pdi;
+ newBar->idx = idx;
+ newBar->type = type;
+ newBar->size = size;
+ if (pci_bars == NULL) {
+ /* first BAR */
+ pci_bars = newBar;
+ } else {
+ struct pcibarlist *bar = pci_bars;
+ struct pcibarlist *lastBar = NULL;
+ do {
+ if (bar->size < size)
+ break;
+ lastBar = bar;
+ bar = bar->next;
+ } while (bar != NULL);
+ newBar->next = bar;
+ if (lastBar != NULL)
+ lastBar->next = newBar;
+ else
+ pci_bars = newBar;
}
+ return (0);
+}
+
+static int
+pci_emul_assign_bar(struct pcibarlist *pci_bar)
+{
+ struct pci_devinst *pdi = pci_bar->pdi;
+ int idx = pci_bar->idx;
+ enum pcibar_type type = pci_bar->type;
+ uint64_t size = pci_bar->size;
+
+ int error;
+ uint64_t *baseptr, limit, addr, mask, lobits;
+ uint16_t cmd, enbit;
switch (type) {
case PCIBAR_NONE:
@@ -659,7 +843,7 @@
break;
case PCIBAR_IO:
baseptr = &pci_emul_iobase;
- limit = PCI_EMUL_IOLIMIT;
+ limit = pci_emul_iolim;
mask = PCIM_BAR_IO_BASE;
lobits = PCIM_BAR_IO_SPACE;
enbit = PCIM_CMD_PORTEN;
@@ -670,29 +854,41 @@
* Some drivers do not work well if the 64-bit BAR is allocated
* above 4GB. Allow for this by allocating small requests under
* 4GB unless then allocation size is larger than some arbitrary
- * number (128MB currently).
+ * number (256MB currently).
*/
- if (size > 128 * 1024 * 1024) {
+ if (size > 256 * 1024 * 1024) {
baseptr = &pci_emul_membase64;
limit = pci_emul_memlim64;
mask = PCIM_BAR_MEM_BASE;
lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
PCIM_BAR_MEM_PREFETCH;
- } else {
- baseptr = &pci_emul_membase32;
- limit = PCI_EMUL_MEMLIMIT32;
- mask = PCIM_BAR_MEM_BASE;
- lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64;
+ enbit = PCIM_CMD_MEMEN;
+ break;
}
- enbit = PCIM_CMD_MEMEN;
- break;
+ /*
+ * Use 32 bit BARs for small requests:
+ * Fallthrough into MEM32 case
+ */
+ type = PCIBAR_MEM32;
+ pdi->pi_bar[idx + 1].type = PCIBAR_NONE;
+ /* clear 64-bit flag */
+ pdi->pi_bar[idx].lobits &= ~PCIM_BAR_MEM_64;
+ /* [fallthrough] */
case PCIBAR_MEM32:
baseptr = &pci_emul_membase32;
- limit = PCI_EMUL_MEMLIMIT32;
+ limit = pci_emul_memlim32;
mask = PCIM_BAR_MEM_BASE;
lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
enbit = PCIM_CMD_MEMEN;
break;
+ case PCIBAR_ROM:
+ /* do not claim memory for ROM. OVMF will do it for us. */
+ baseptr = NULL;
+ limit = 0;
+ mask = PCIM_BIOS_ADDR_MASK;
+ lobits = 0;
+ enbit = PCIM_CMD_MEMEN;
+ break;
default:
printf("pci_emul_alloc_base: invalid bar type %d\n", type);
assert(0);
@@ -705,23 +901,29 @@
}
pdi->pi_bar[idx].type = type;
- pdi->pi_bar[idx].addr = addr;
+ pdi->pi_bar[idx].addr = 0;
pdi->pi_bar[idx].size = size;
+ /* passthru devices are using same lobits as physical device
+ * they set this property
+ */
+ if (pdi->pi_bar[idx].lobits != 0)
+ lobits = pdi->pi_bar[idx].lobits;
+ else
+ pdi->pi_bar[idx].lobits = lobits;
- /* Initialize the BAR register in config space */
- bar = (addr & mask) | lobits;
- pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar);
+ /* Initialize CMD register in config space */
+ cmd = pci_get_cfgdata16(pdi, PCIR_COMMAND);
+ if ((cmd & enbit) != enbit)
+ pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit);
+ /* Initialize the BAR register in config space */
if (type == PCIBAR_MEM64) {
assert(idx + 1 <= PCI_BARMAX);
pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64;
- pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32);
+ update_bar_address(pdi, idx + 1, addr);
}
- cmd = pci_get_cfgdata16(pdi, PCIR_COMMAND);
- if ((cmd & enbit) != enbit)
- pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit);
- register_bar(pdi, idx);
+ update_bar_address(pdi, idx, addr);
return (0);
}
@@ -1134,6 +1336,9 @@
#define BUSIO_ROUNDUP 32
#define BUSMEM_ROUNDUP (1024 * 1024)
+#define ALIGN_VALUE(Value, Alignment) \
+ ((Value) + (((Alignment) - (Value)) & ((Alignment)-1)))
+
int
init_pci(struct vmctx *ctx)
{
@@ -1146,25 +1351,18 @@
nvlist_t *nvl;
const char *emul;
size_t lowmem;
- uint64_t cpu_maxphysaddr, pci_emul_memresv64;
- u_int regs[4];
int bus, slot, func, error;
pci_emul_iobase = PCI_EMUL_IOBASE;
+ pci_emul_iolim = PCI_EMUL_IOLIMIT;
+
pci_emul_membase32 = vm_get_lowmem_limit(ctx);
+ pci_emul_memlim32 = PCI_EMUL_MEMLIMIT32;
- do_cpuid(0x80000008, regs);
- cpu_maxphysaddr = 1ULL << (regs[0] & 0xff);
- if (cpu_maxphysaddr > VM_MAXUSER_ADDRESS_LA48)
- cpu_maxphysaddr = VM_MAXUSER_ADDRESS_LA48;
- pci_emul_memresv64 = cpu_maxphysaddr / 4;
- /*
- * Max power of 2 that is less then
- * cpu_maxphysaddr - pci_emul_memresv64.
- */
- pci_emul_membase64 = 1ULL << (flsl(cpu_maxphysaddr -
- pci_emul_memresv64) - 1);
- pci_emul_memlim64 = cpu_maxphysaddr;
+ pci_emul_membase64 = 4 * GB + vm_get_highmem_size(ctx);
+ pci_emul_membase64 = ALIGN_VALUE(
+ pci_emul_membase64, PCI_EMUL_MEMSIZE64);
+ pci_emul_memlim64 = pci_emul_membase64 + PCI_EMUL_MEMSIZE64;
for (bus = 0; bus < MAXBUSES; bus++) {
snprintf(node_name, sizeof(node_name), "pci.%d", bus);
@@ -1182,6 +1380,7 @@
bi->membase32 = pci_emul_membase32;
bi->membase64 = pci_emul_membase64;
+ /* first run: init devices */
for (slot = 0; slot < MAXSLOTS; slot++) {
si = &bi->slotinfo[slot];
for (func = 0; func < MAXFUNCS; func++) {
@@ -1221,6 +1420,15 @@
}
}
+ /* second run: assign BARs and free BAR list */
+ struct pcibarlist *bar = pci_bars;
+ while (bar != NULL) {
+ pci_emul_assign_bar(bar);
+ struct pcibarlist *old = bar;
+ bar = bar->next;
+ free(old);
+ }
+
/*
* Add some slop to the I/O and memory resources decoded by
* this bus to give a guest some flexibility if it wants to
@@ -1794,7 +2002,7 @@
* If the MMIO or I/O address space decoding has changed then
* register/unregister all BARs that decode that address space.
*/
- for (i = 0; i <= PCI_BARMAX; i++) {
+ for (i = 0; i <= PCI_BARMAX_WITH_ROM; i++) {
switch (pi->pi_bar[i].type) {
case PCIBAR_NONE:
case PCIBAR_MEMHI64:
@@ -1808,6 +2016,11 @@
unregister_bar(pi, i);
}
break;
+ case PCIBAR_ROM:
+ /* skip (un-)register of ROM if it not enabled
+ */
+ if (pi->pi_bar[i].lobits == 0)
+ break;
case PCIBAR_MEM32:
case PCIBAR_MEM64:
/* MMIO address space decoding changed? */
@@ -1865,7 +2078,6 @@
struct pci_devinst *pi;
struct pci_devemu *pe;
int idx, needcfg;
- uint64_t addr, bar, mask;
if ((bi = pci_businfo[bus]) != NULL) {
si = &bi->slotinfo[slot];
@@ -1917,8 +2129,15 @@
needcfg = 1;
}
- if (needcfg)
- *eax = CFGREAD(pi, coff, bytes);
+ if (needcfg) {
+ if ((coff >= PCIR_BAR(0) &&
+ coff <= PCIR_BAR(PCI_BARMAX)) ||
+ (coff >= PCIR_BIOS && coff < PCIR_BIOS + 4)) {
+ *eax = read_bar_value(pi, coff, bytes);
+ } else {
+ *eax = CFGREAD(pi, coff, bytes);
+ }
+ }
pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax);
} else {
@@ -1928,64 +2147,25 @@
return;
/*
- * Special handling for write to BAR registers
+ * Special handling for write to BAR and ROM registers
*/
- if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) {
+ if ((coff >= PCIR_BAR(0) && coff <= PCIR_BAR(PCI_BARMAX)) ||
+ (coff >= PCIR_BIOS && coff < PCIR_BIOS + 4)) {
/*
* Ignore writes to BAR registers that are not
* 4-byte aligned.
*/
if (bytes != 4 || (coff & 0x3) != 0)
return;
- idx = (coff - PCIR_BAR(0)) / 4;
- mask = ~(pi->pi_bar[idx].size - 1);
- switch (pi->pi_bar[idx].type) {
- case PCIBAR_NONE:
- pi->pi_bar[idx].addr = bar = 0;
- break;
- case PCIBAR_IO:
- addr = *eax & mask;
- addr &= 0xffff;
- bar = addr | PCIM_BAR_IO_SPACE;
- /*
- * Register the new BAR value for interception
- */
- if (addr != pi->pi_bar[idx].addr) {
- update_bar_address(pi, addr, idx,
- PCIBAR_IO);
- }
- break;
- case PCIBAR_MEM32:
- addr = bar = *eax & mask;
- bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
- if (addr != pi->pi_bar[idx].addr) {
- update_bar_address(pi, addr, idx,
- PCIBAR_MEM32);
- }
- break;
- case PCIBAR_MEM64:
- addr = bar = *eax & mask;
- bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
- PCIM_BAR_MEM_PREFETCH;
- if (addr != (uint32_t)pi->pi_bar[idx].addr) {
- update_bar_address(pi, addr, idx,
- PCIBAR_MEM64);
- }
- break;
- case PCIBAR_MEMHI64:
- mask = ~(pi->pi_bar[idx - 1].size - 1);
- addr = ((uint64_t)*eax << 32) & mask;
- bar = addr >> 32;
- if (bar != pi->pi_bar[idx - 1].addr >> 32) {
- update_bar_address(pi, addr, idx - 1,
- PCIBAR_MEMHI64);
- }
- break;
- default:
- assert(0);
- }
- pci_set_cfgdata32(pi, coff, bar);
-
+ /*
+ * coff is equal to PCIR_BIOS on ROM writes because
+ * it's 4-byte aligned
+ */
+ if (coff == PCIR_BIOS)
+ idx = PCI_ROM_IDX;
+ else
+ idx = (coff - PCIR_BAR(0)) / 4;
+ update_bar_address(pi, idx, *eax);
} else if (pci_emul_iscap(pi, coff)) {
pci_emul_capwrite(pi, coff, bytes, *eax, 0, 0);
} else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) {
diff --git a/usr.sbin/bhyve/pci_fbuf.c b/usr.sbin/bhyve/pci_fbuf.c
--- a/usr.sbin/bhyve/pci_fbuf.c
+++ b/usr.sbin/bhyve/pci_fbuf.c
@@ -216,15 +216,15 @@
return (value);
}
-static void
+static int
pci_fbuf_baraddr(struct vmctx *ctx, struct pci_devinst *pi, int baridx,
- int enabled, uint64_t address)
+ int enabled, uint64_t address)
{
struct pci_fbuf_softc *sc;
int prot;
if (baridx != 1)
- return;
+ return (-1);
sc = pi->pi_arg;
if (!enabled && sc->fbaddr != 0) {
@@ -237,6 +237,8 @@
EPRINTLN("pci_fbuf: mmap_memseg failed");
sc->fbaddr = address;
}
+
+ return (0);
}
@@ -375,7 +377,7 @@
static int
pci_fbuf_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
{
- int error, prot;
+ int error;
struct pci_fbuf_softc *sc;
if (fbuf_sc != NULL) {
@@ -393,6 +395,13 @@
pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_DISPLAY);
pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_DISPLAY_VGA);
+ sc->fb_base = vm_create_devmem(
+ ctx, VM_FRAMEBUFFER, "framebuffer", FB_SIZE);
+ if (sc->fb_base == MAP_FAILED) {
+ error = -1;
+ goto done;
+ }
+
error = pci_emul_alloc_bar(pi, 0, PCIBAR_MEM32, DMEMSZ);
assert(error == 0);
@@ -402,7 +411,6 @@
error = pci_emul_add_msicap(pi, PCI_FBUF_MSI_MSGS);
assert(error == 0);
- sc->fbaddr = pi->pi_bar[1].addr;
sc->memregs.fbsize = FB_SIZE;
sc->memregs.width = COLS_DEFAULT;
sc->memregs.height = ROWS_DEFAULT;
@@ -423,27 +431,9 @@
goto done;
}
- sc->fb_base = vm_create_devmem(ctx, VM_FRAMEBUFFER, "framebuffer", FB_SIZE);
- if (sc->fb_base == MAP_FAILED) {
- error = -1;
- goto done;
- }
DPRINTF(DEBUG_INFO, ("fbuf frame buffer base: %p [sz %lu]",
sc->fb_base, FB_SIZE));
- /*
- * Map the framebuffer into the guest address space.
- * XXX This may fail if the BAR is different than a prior
- * run. In this case flag the error. This will be fixed
- * when a change_memseg api is available.
- */
- prot = PROT_READ | PROT_WRITE;
- if (vm_mmap_memseg(ctx, sc->fbaddr, VM_FRAMEBUFFER, 0, FB_SIZE, prot) != 0) {
- EPRINTLN("pci_fbuf: mapseg failed - try deleting VM and restarting");
- error = -1;
- goto done;
- }
-
console_init(sc->memregs.width, sc->memregs.height, sc->fb_base);
console_fb_register(pci_fbuf_render, sc);
diff --git a/usr.sbin/bhyve/pci_gvt-d_amd.c b/usr.sbin/bhyve/pci_gvt-d_amd.c
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/pci_gvt-d_amd.c
@@ -0,0 +1,190 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Beckhoff Automation GmbH & Co. KG
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR OR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <machine/iodev.h>
+#include <machine/vmm.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "debug.h"
+#include "pci_passthru.h"
+
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+static void
+gvt_d_amd_usage(char *opt) {
+ EPRINTLN("Invalid gvt-d amd passthru option \"%s\"", opt);
+ EPRINTLN("passthru: {rom=rom_file}");
+}
+
+static int
+gvt_d_amd_parse_opts(struct passthru_softc *sc, nvlist_t *nvl)
+{
+ const char *value;
+ /* parse rom file */
+ value = get_config_value_node(nvl, "rom");
+ if (value != NULL) {
+ const int fd = open(value, O_RDONLY);
+ if (fd < 0) {
+ return (-1);
+ }
+ /* determine file size */
+ uint64_t bios_size = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+ /* read bios */
+ void *bios_addr = malloc(bios_size);
+ if (bios_addr == NULL) {
+ close(fd);
+ return (-ENOMEM);
+ }
+ bios_size = read(fd, bios_addr, bios_size);
+ close(fd);
+
+ /* save physical values of ROM */
+ sc->psc_bar[PCI_ROM_IDX].type = PCIBAR_ROM;
+ sc->psc_bar[PCI_ROM_IDX].addr = (uint64_t)bios_addr;
+ sc->psc_bar[PCI_ROM_IDX].size = bios_size;
+ }
+
+ return (0);
+}
+
+int
+gvt_d_amd_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
+{
+ int error = 0;
+
+ struct passthru_softc *sc = pi->pi_arg;
+
+ if ((error = gvt_d_amd_parse_opts(sc, nvl)))
+ goto done;
+
+ const uint16_t vendor = read_config(&sc->psc_sel, PCIR_VENDOR, 0x02);
+ const uint16_t dev_id = read_config(&sc->psc_sel, PCIR_DEVICE, 0x02);
+
+ uint64_t bios_size;
+ if (sc->psc_bar[PCI_ROM_IDX].size == 0) {
+ /* get VBIOS size */
+ if ((error = vm_get_vbios(ctx, sc->psc_sel.pc_bus,
+ sc->psc_sel.pc_dev, sc->psc_sel.pc_func, vendor,
+ dev_id, NULL, &bios_size)) != 0) {
+ warnx("vm_get_vbios: %x", errno);
+ goto done;
+ }
+ } else {
+ bios_size = sc->psc_bar[PCI_ROM_IDX].size;
+ }
+ if (bios_size == 0) {
+ error = ESRCH;
+ warnx("Could not determine VBIOS size");
+ goto done;
+ }
+
+ /*
+ * round up size to a power of two
+ * check in descendig order to avoid endless loop
+ */
+ uint64_t rom_size = 1ULL << 63;
+ while (rom_size >= bios_size) {
+ rom_size >>= 1;
+ }
+ rom_size <<= 1;
+ /* ROM size should be greater than 2 KB */
+ rom_size = max(rom_size, (~PCIM_BIOS_ADDR_MASK) + 1);
+
+ /* Allocate VM Memory for VBIOS */
+ const uint64_t rom_addr = (uint64_t)vm_create_devmem(
+ ctx, VM_VIDEOBIOS, "videobios", rom_size);
+ if ((void *)rom_addr == MAP_FAILED) {
+ error = ENOMEM;
+ warnx("vm_create_devmem: %x", errno);
+ goto done;
+ }
+
+ /* get VBIOS */
+ if (sc->psc_bar[PCI_ROM_IDX].addr == 0) {
+ if ((error = vm_get_vbios(ctx, sc->psc_sel.pc_bus,
+ sc->psc_sel.pc_dev, sc->psc_sel.pc_func, vendor,
+ dev_id, (void *)rom_addr, &bios_size)) != 0) {
+ warnx("vm_get_vbios: %x", errno);
+ goto done;
+ }
+ } else {
+ memcpy((void *)rom_addr, (void *)sc->psc_bar[PCI_ROM_IDX].addr,
+ bios_size);
+ free((void *)sc->psc_bar[PCI_ROM_IDX].addr);
+ }
+
+ /* assign a ROM to this device */
+ if ((error = pci_emul_alloc_bar(
+ pi, PCI_ROM_IDX, PCIBAR_ROM, rom_size)) != 0) {
+ warnx("pci_emul_alloc_rom: %x", error);
+ goto done;
+ }
+
+ /* save physical values of ROM */
+ sc->psc_bar[PCI_ROM_IDX].type = PCIBAR_ROM;
+ sc->psc_bar[PCI_ROM_IDX].addr = rom_addr;
+ sc->psc_bar[PCI_ROM_IDX].size = bios_size;
+
+done:
+ return (error);
+}
+
+int
+gvt_d_amd_addr_rom(struct pci_devinst *pi, int idx, int enabled)
+{
+ int error;
+
+ if (!enabled)
+ error = vm_munmap_memseg(
+ pi->pi_vmctx, pi->pi_bar[idx].addr, pi->pi_bar[idx].size);
+ else
+ error = vm_mmap_memseg(pi->pi_vmctx, pi->pi_bar[idx].addr,
+ VM_VIDEOBIOS, 0, pi->pi_bar[idx].size,
+ PROT_READ | PROT_EXEC);
+
+ return error;
+}
diff --git a/usr.sbin/bhyve/pci_passthru.h b/usr.sbin/bhyve/pci_passthru.h
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/pci_passthru.h
@@ -0,0 +1,81 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Beckhoff Automation GmbH & Co. KG
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR OR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#pragma once
+
+#include <sys/pciio.h>
+
+#include <vmmapi.h>
+
+#include "pci_emul.h"
+
+struct passthru_mmio_mapping {
+ uint64_t gpa;
+ uint64_t len;
+ uint64_t hpa;
+};
+
+struct passthru_softc {
+ struct pci_devinst *psc_pi;
+ /* ROM is handled like a BAR */
+ struct pcibar psc_bar[PCI_BARMAX_WITH_ROM + 1];
+ struct {
+ int capoff;
+ int msgctrl;
+ int emulated;
+ } psc_msi;
+ struct {
+ int capoff;
+ } psc_msix;
+ struct pcisel psc_sel;
+
+ struct passthru_mmio_mapping psc_mmio_map[2];
+ uint8_t psc_pcir_prot_map[(PCI_REGMAX + 1) / 4];
+};
+
+#define PT_MAP_PPTDEV_MMIO 1
+#define PT_UNMAP_PPTDEV_MMIO 0
+
+#define PPT_PCIR_PROT_NA 0 /* No Access to physical values */
+#define PPT_PCIR_PROT_RO 1 /* Read Only access to physical values */
+#define PPT_PCIR_PROT_WO 2 /* Write Only access to physical values */
+#define PPT_PCIR_PROT_RW \
+ (PPT_PCIR_PROT_RO | \
+ PPT_PCIR_PROT_WO) /* Read/Write access to physical values */
+#define PPT_PCIR_PROT_MASK 0x03
+
+int passthru_modify_pptdev_mmio(struct vmctx *ctx, struct passthru_softc *sc,
+ struct passthru_mmio_mapping *map, int registration);
+uint32_t read_config(const struct pcisel *sel, long reg, int width);
+void write_config(const struct pcisel *sel, long reg, int width, uint32_t data);
+int set_pcir_prot(
+ struct passthru_softc *sc, uint32_t reg, uint32_t len, uint8_t prot);
+int gvt_d_amd_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl);
+int gvt_d_amd_addr_rom(struct pci_devinst *pi, int idx, int enabled);
diff --git a/usr.sbin/bhyve/pci_passthru.c b/usr.sbin/bhyve/pci_passthru.c
--- a/usr.sbin/bhyve/pci_passthru.c
+++ b/usr.sbin/bhyve/pci_passthru.c
@@ -48,22 +48,23 @@
#ifndef WITHOUT_CAPSICUM
#include <capsicum_helpers.h>
#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <machine/vmm.h>
+
#include <err.h>
#include <errno.h>
#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <machine/vmm.h>
-#include <vmmapi.h>
#include "config.h"
#include "debug.h"
-#include "pci_emul.h"
#include "mem.h"
+#include "pci_passthru.h"
#ifndef _PATH_DEVPCI
#define _PATH_DEVPCI "/dev/pci"
@@ -86,20 +87,6 @@
static int iofd = -1;
static int memfd = -1;
-struct passthru_softc {
- struct pci_devinst *psc_pi;
- struct pcibar psc_bar[PCI_BARMAX + 1];
- struct {
- int capoff;
- int msgctrl;
- int emulated;
- } psc_msi;
- struct {
- int capoff;
- } psc_msix;
- struct pcisel psc_sel;
-};
-
static int
msi_caplen(int msgctrl)
{
@@ -122,7 +109,7 @@
return (len);
}
-static uint32_t
+uint32_t
read_config(const struct pcisel *sel, long reg, int width)
{
struct pci_io pi;
@@ -138,7 +125,7 @@
return (pi.pi_data);
}
-static void
+void
write_config(const struct pcisel *sel, long reg, int width, uint32_t data)
{
struct pci_io pi;
@@ -152,6 +139,20 @@
(void)ioctl(pcifd, PCIOCWRITE, &pi); /* XXX */
}
+int
+passthru_modify_pptdev_mmio(struct vmctx *ctx, struct passthru_softc *sc,
+ struct passthru_mmio_mapping *map, int registration)
+{
+ if (registration == PT_MAP_PPTDEV_MMIO)
+ return vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus,
+ sc->psc_sel.pc_dev, sc->psc_sel.pc_func, map->gpa, map->len,
+ map->hpa);
+ else
+ return vm_unmap_pptdev_mmio(ctx, sc->psc_sel.pc_bus,
+ sc->psc_sel.pc_dev, sc->psc_sel.pc_func, map->gpa,
+ map->len);
+}
+
#ifdef LEGACY_SUPPORT
static int
passthru_add_msicap(struct pci_devinst *pi, int msgnum, int nextptr)
@@ -559,12 +560,23 @@
sc->psc_bar[i].type = bartype;
sc->psc_bar[i].size = size;
sc->psc_bar[i].addr = base;
+ sc->psc_bar[i].lobits = 0;
/* Allocate the BAR in the guest I/O or MMIO space */
error = pci_emul_alloc_bar(pi, i, bartype, size);
if (error)
return (-1);
+ /* Use same lobits as physical bar */
+ uint8_t lobits = read_config(&sc->psc_sel, PCIR_BAR(i), 0x01);
+ if (bartype == PCIBAR_MEM32 || bartype == PCIBAR_MEM64) {
+ lobits &= ~PCIM_BAR_MEM_BASE;
+ } else {
+ lobits &= ~PCIM_BAR_IO_BASE;
+ }
+ sc->psc_bar[i].lobits = lobits;
+ pi->pi_bar[i].lobits = lobits;
+
/* The MSI-X table needs special handling */
if (i == pci_msix_table_bar(pi)) {
error = init_msix_table(ctx, sc, base);
@@ -610,24 +622,109 @@
goto done;
}
- pci_set_cfgdata16(pi, PCIR_COMMAND, read_config(&sc->psc_sel,
- PCIR_COMMAND, 2));
+ write_config(
+ &sc->psc_sel, PCIR_COMMAND, 2, pci_get_cfgdata16(pi, PCIR_COMMAND));
error = 0; /* success */
done:
return (error);
}
+#define PPT_PCIR_PROT(reg) \
+ ((sc->psc_pcir_prot_map[reg / 4] >> (reg & 0x03)) & PPT_PCIR_PROT_MASK)
+
+int
+set_pcir_prot(
+ struct passthru_softc *sc, uint32_t reg, uint32_t len, uint8_t prot)
+{
+ if (reg > PCI_REGMAX || reg + len > PCI_REGMAX + 1)
+ return (-1);
+
+ prot &= PPT_PCIR_PROT_MASK;
+
+ for (int i = reg; i < reg + len; ++i) {
+ /* delete old prot value */
+ sc->psc_pcir_prot_map[i / 4] &= ~(
+ PPT_PCIR_PROT_MASK << (i & 0x03));
+ /* set new prot value */
+ sc->psc_pcir_prot_map[i / 4] |= prot << (i & 0x03);
+ }
+
+ return (0);
+}
+
+static int
+is_pcir_writable(struct passthru_softc *sc, uint32_t reg)
+{
+ if (reg > PCI_REGMAX)
+ return (0);
+
+ return ((PPT_PCIR_PROT(reg) & PPT_PCIR_PROT_WO) != 0);
+}
+
+static int
+is_pcir_readable(struct passthru_softc *sc, uint32_t reg)
+{
+ if (reg > PCI_REGMAX)
+ return (0);
+
+ return ((PPT_PCIR_PROT(reg) & PPT_PCIR_PROT_RO) != 0);
+}
+
+static int
+passthru_init_quirks(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl)
+{
+ struct passthru_softc *sc = pi->pi_arg;
+
+ uint16_t vendor = read_config(&sc->psc_sel, PCIR_VENDOR, 0x02);
+ uint8_t class = read_config(&sc->psc_sel, PCIR_CLASS, 0x01);
+
+ /* currently only display devices have quirks */
+ if (class != PCIC_DISPLAY)
+ return (0);
+
+ if (vendor == PCI_VENDOR_AMD)
+ return gvt_d_amd_init(ctx, pi, nvl);
+
+ return (0);
+}
+
+static void
+passthru_deinit_quirks(struct vmctx *ctx, struct pci_devinst *pi)
+{
+ struct passthru_softc *sc = pi->pi_arg;
+
+ if (sc == NULL)
+ return;
+
+ //uint16_t vendor = read_config(&sc->psc_sel, PCIR_VENDOR, 0x02);
+ uint8_t class = read_config(&sc->psc_sel, PCIR_CLASS, 0x01);
+
+ /* currently only display devices have quirks */
+ if (class != PCIC_DISPLAY)
+ return;
+
+ return;
+}
+
static int
passthru_legacy_config(nvlist_t *nvl, const char *opts)
{
- char value[16];
+ char value[PATH_MAX];
int bus, slot, func;
if (opts == NULL)
return (0);
- if (sscanf(opts, "%d/%d/%d", &bus, &slot, &func) != 3) {
+ const char *bdf = opts;
+
+ char *xopts = strchr(opts, ',');
+ if (xopts != NULL) {
+ *xopts = '\0';
+ ++xopts;
+ }
+
+ if (sscanf(bdf, "%d/%d/%d", &bus, &slot, &func) != 3) {
EPRINTLN("passthru: invalid options \"%s\"", opts);
return (-1);
}
@@ -638,6 +735,32 @@
set_config_value_node(nvl, "slot", value);
snprintf(value, sizeof(value), "%d", func);
set_config_value_node(nvl, "func", value);
+
+ if (xopts == NULL) {
+ return (0);
+ }
+
+ char *xopt = xopts;
+ do {
+ char *xopt_val = strchr(xopt, '=');
+ char *xopt_end = strchr(xopt, ',');
+ if (xopt_val != NULL) {
+ *xopt_val = '\0';
+ ++xopt_val;
+ }
+ if (xopt_end != NULL) {
+ *xopt_end = '\0';
+ ++xopt_end;
+ }
+ if (strcmp(xopt, "rom") == 0) {
+ snprintf(value, sizeof(value), "%s", xopt_val);
+ set_config_value_node(nvl, "rom", value);
+ } else {
+ return (-1);
+ }
+ xopt = xopt_end;
+ } while (xopt != NULL);
+
return (0);
}
@@ -736,9 +859,21 @@
sc->psc_pi = pi;
/* initialize config space */
- error = cfginit(ctx, pi, bus, slot, func);
+ if ((error = cfginit(ctx, pi, bus, slot, func)) != 0)
+ goto done;
+
+ /* allow access to all PCI registers */
+ if ((error = set_pcir_prot(sc, 0, PCI_REGMAX + 1, PPT_PCIR_PROT_RW)) !=
+ 0)
+ goto done;
+
+ if ((error = passthru_init_quirks(ctx, pi, nvl)) != 0)
+ goto done;
+
+ error = 0; /* success */
done:
if (error) {
+ passthru_deinit_quirks(ctx, pi);
free(sc);
vm_unassign_pptdev(ctx, bus, slot, func);
}
@@ -788,12 +923,22 @@
sc = pi->pi_arg;
+ /* skip for protected PCI registers */
+ if (!is_pcir_readable(sc, coff))
+ return (-1);
+
/*
* PCI BARs and MSI capability is emulated.
*/
if (bar_access(coff) || msicap_access(sc, coff))
return (-1);
+ /*
+ * PCI ROM is emulated
+ */
+ if (coff >= PCIR_BIOS && coff < PCIR_BIOS + 4)
+ return (-1);
+
#ifdef LEGACY_SUPPORT
/*
* Emulate PCIR_CAP_PTR if this device does not support MSI capability
@@ -834,12 +979,22 @@
sc = pi->pi_arg;
+ /* skip for protected PCI registers */
+ if (!is_pcir_writable(sc, coff))
+ return (-1);
+
/*
* PCI BARs are emulated
*/
if (bar_access(coff))
return (-1);
+ /*
+ * PCI ROM is emulated
+ */
+ if (coff >= PCIR_BIOS && coff < PCIR_BIOS + 4)
+ return (-1);
+
/*
* MSI capability is emulated
*/
@@ -958,7 +1113,7 @@
static void
passthru_msix_addr(struct vmctx *ctx, struct pci_devinst *pi, int baridx,
- int enabled, uint64_t address)
+ int enabled, uint64_t address)
{
struct passthru_softc *sc;
size_t remaining;
@@ -966,21 +1121,15 @@
sc = pi->pi_arg;
table_offset = rounddown2(pi->pi_msix.table_offset, 4096);
+
+ struct passthru_mmio_mapping map;
+
if (table_offset > 0) {
- if (!enabled) {
- if (vm_unmap_pptdev_mmio(ctx, sc->psc_sel.pc_bus,
- sc->psc_sel.pc_dev,
- sc->psc_sel.pc_func, address,
- table_offset) != 0)
- warnx("pci_passthru: unmap_pptdev_mmio failed");
- } else {
- if (vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus,
- sc->psc_sel.pc_dev,
- sc->psc_sel.pc_func, address,
- table_offset,
- sc->psc_bar[baridx].addr) != 0)
- warnx("pci_passthru: map_pptdev_mmio failed");
- }
+ map.gpa = address;
+ map.len = table_offset;
+ map.hpa = sc->psc_bar[baridx].addr;
+ if (passthru_modify_pptdev_mmio(ctx, sc, &map, enabled) != 0)
+ warnx("pci_passthru: modify_pptdev_mmio failed");
}
table_size = pi->pi_msix.table_offset - table_offset;
table_size += pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE;
@@ -988,58 +1137,84 @@
remaining = pi->pi_bar[baridx].size - table_offset - table_size;
if (remaining > 0) {
address += table_offset + table_size;
- if (!enabled) {
- if (vm_unmap_pptdev_mmio(ctx, sc->psc_sel.pc_bus,
- sc->psc_sel.pc_dev,
- sc->psc_sel.pc_func, address,
- remaining) != 0)
- warnx("pci_passthru: unmap_pptdev_mmio failed");
- } else {
- if (vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus,
- sc->psc_sel.pc_dev,
- sc->psc_sel.pc_func, address,
- remaining,
- sc->psc_bar[baridx].addr +
- table_offset + table_size) != 0)
- warnx("pci_passthru: map_pptdev_mmio failed");
- }
+ map.gpa = address;
+ map.len = remaining;
+ map.hpa = sc->psc_bar[baridx].addr + table_offset + table_size;
+ if (passthru_modify_pptdev_mmio(ctx, sc, &map, enabled) != 0)
+ warnx("pci_passthru: modify_pptdev_mmio failed");
}
}
static void
passthru_mmio_addr(struct vmctx *ctx, struct pci_devinst *pi, int baridx,
- int enabled, uint64_t address)
+ int enabled, uint64_t address)
{
struct passthru_softc *sc;
sc = pi->pi_arg;
- if (!enabled) {
- if (vm_unmap_pptdev_mmio(ctx, sc->psc_sel.pc_bus,
- sc->psc_sel.pc_dev,
- sc->psc_sel.pc_func, address,
- sc->psc_bar[baridx].size) != 0)
- warnx("pci_passthru: unmap_pptdev_mmio failed");
- } else {
- if (vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus,
- sc->psc_sel.pc_dev,
- sc->psc_sel.pc_func, address,
- sc->psc_bar[baridx].size,
- sc->psc_bar[baridx].addr) != 0)
- warnx("pci_passthru: map_pptdev_mmio failed");
+
+ struct passthru_mmio_mapping map;
+ map.gpa = address;
+ map.len = sc->psc_bar[baridx].size;
+ map.hpa = sc->psc_bar[baridx].addr;
+
+ if (passthru_modify_pptdev_mmio(ctx, sc, &map, enabled) != 0)
+ warnx("pci_passthru: modify_pptdev_mmio failed");
+}
+
+static int
+passthru_addr_rom(struct pci_devinst *pi, int idx, int enabled)
+{
+ struct passthru_softc *sc = pi->pi_arg;
+
+ const uint8_t class = read_config(&sc->psc_sel, PCIR_CLASS, 0x01);
+ if (class != PCIC_DISPLAY) {
+ warnx(
+ "%d/%d/%d is no display device; only display devices have a ROM",
+ pi->pi_bus, pi->pi_slot, pi->pi_func);
+ return (-1);
+ }
+
+ const uint16_t vendor = read_config(&sc->psc_sel, PCIR_VENDOR, 0x02);
+ switch (vendor) {
+ case PCI_VENDOR_AMD:
+ return gvt_d_amd_addr_rom(pi, idx, enabled);
+ default:
+ warnx("%d/%d/%d has no ROM", pi->pi_bus, pi->pi_slot,
+ pi->pi_func);
+ return (-1);
}
}
-static void
+static int
passthru_addr(struct vmctx *ctx, struct pci_devinst *pi, int baridx,
- int enabled, uint64_t address)
+ int enabled, uint64_t address)
{
+ int error = 0;
- if (pi->pi_bar[baridx].type == PCIBAR_IO)
- return;
- if (baridx == pci_msix_table_bar(pi))
- passthru_msix_addr(ctx, pi, baridx, enabled, address);
- else
- passthru_mmio_addr(ctx, pi, baridx, enabled, address);
+ switch (pi->pi_bar[baridx].type) {
+ case PCIBAR_IO:
+ /* IO BARs are emulated */
+ return (-1);
+ case PCIBAR_ROM:
+ /* Only quirk devices have a ROM */
+ error = passthru_addr_rom(pi, baridx, enabled);
+ break;
+ case PCIBAR_MEM32:
+ case PCIBAR_MEM64:
+ if (baridx == pci_msix_table_bar(pi))
+ passthru_msix_addr(ctx, pi, baridx, enabled, address);
+ else
+ passthru_mmio_addr(ctx, pi, baridx, enabled, address);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ if (error) {
+ errx(4, "Failed to modify BAR addr: %d", error);
+ }
+ return 0;
}
struct pci_devemu passthru = {

File Metadata

Mime Type
text/plain
Expires
Fri, Feb 14, 12:49 AM (3 h, 9 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16619732
Default Alt Text
D27456.id86487.diff (57 KB)

Event Timeline