Index: lib/libvmmapi/vmmapi.h =================================================================== --- lib/libvmmapi/vmmapi.h +++ lib/libvmmapi/vmmapi.h @@ -41,7 +41,7 @@ * API version for out-of-tree consumers like grub-bhyve for making compile * time decisions. */ -#define VMMAPI_VERSION 0103 /* 2 digit major followed by 2 digit minor */ +#define VMMAPI_VERSION 0104 /* 2 digit major followed by 2 digit minor */ struct iovec; struct vmctx; @@ -73,6 +73,8 @@ VM_SYSMEM, VM_BOOTROM, VM_FRAMEBUFFER, + VM_VIDEOBIOS, + VM_NOTHING /* Last entry */ }; /* @@ -111,6 +113,12 @@ int vm_mmap_memseg(struct vmctx *ctx, vm_paddr_t gpa, int segid, vm_ooffset_t segoff, size_t len, int prot); +/* + * Unmap a memory segment previously mapped with the given parameters. + */ +int vm_munmap_memseg(struct vmctx *ctx, vm_paddr_t gpa, + int segid, vm_ooffset_t segoff); + int vm_create(const char *name); int vm_get_device_fd(struct vmctx *ctx); struct vmctx *vm_open(const char *name); @@ -176,6 +184,8 @@ int vm_unassign_pptdev(struct vmctx *ctx, int bus, int slot, int func); int vm_map_pptdev_mmio(struct vmctx *ctx, int bus, int slot, int func, vm_paddr_t gpa, size_t len, vm_paddr_t hpa); +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, Index: lib/libvmmapi/vmmapi.c =================================================================== --- lib/libvmmapi/vmmapi.c +++ lib/libvmmapi/vmmapi.c @@ -61,9 +61,13 @@ #include "vmmapi.h" +#define KB (1024UL) #define MB (1024 * 1024UL) #define GB (1024 * 1024 * 1024UL) +#define VGAMEM_START (640*KB) +#define VGAMEM_END (VGAMEM_START + 128*KB) + /* * Size of the guard region before and after the virtual address space * mapping the guest physical memory. This must be a multiple of the @@ -87,6 +91,10 @@ #define CREATE(x) sysctlbyname("hw.vmm.create", NULL, NULL, (x), strlen((x))) #define DESTROY(x) sysctlbyname("hw.vmm.destroy", NULL, NULL, (x), strlen((x))) +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + static int vm_device_open(const char *name) { @@ -227,8 +235,10 @@ */ error = vm_mmap_getnext(ctx, &gpa, &segid, &off, &len, &prot, &flags); if (error == 0 && gpa == memmap.gpa) { - if (segid != memmap.segid || off != memmap.segoff || - prot != memmap.prot || flags != memmap.flags) { + /* Allow other segments to align with SYSMEM borders */ + if ((segid != VM_SYSMEM || memmap.segid == VM_SYSMEM) && + (segid != memmap.segid || off != memmap.segoff || + prot != memmap.prot || flags != memmap.flags)) { errno = EEXIST; return (-1); } else { @@ -251,6 +261,24 @@ return (0); } +int +vm_munmap_memseg(struct vmctx *ctx, vm_paddr_t gpa, + int segid, vm_ooffset_t segoff) +{ + struct vm_memmap memmap; + int error; + + memmap.gpa = gpa; + memmap.segid = segid; + memmap.segoff = segoff; + memmap.len = 0; + memmap.prot = 0; + memmap.flags = 0; + + error = ioctl(ctx->fd, VM_MMAP_MEMSEG, &memmap); + return (error); +} + int vm_mmap_getnext(struct vmctx *ctx, vm_paddr_t *gpa, int *segid, vm_ooffset_t *segoff, size_t *len, int *prot, int *flags) @@ -420,9 +448,23 @@ return (error); } + /* + * Leave a memory hole for VGA memory. + */ + if (ctx->lowmem > VGAMEM_END) { + gpa = VGAMEM_END; + len = ctx->lowmem - VGAMEM_END; + error = setup_memory_segment(ctx, gpa, len, baseaddr); + if (error) + return (error); + } + + /* + * Add up to 640 KB of base memory. + */ if (ctx->lowmem > 0) { gpa = 0; - len = ctx->lowmem; + len = min(ctx->lowmem, VGAMEM_START); error = setup_memory_segment(ctx, gpa, len, baseaddr); if (error) return (error); @@ -446,7 +488,13 @@ if (ctx->lowmem > 0) { if (gaddr < ctx->lowmem && len <= ctx->lowmem && - gaddr + len <= ctx->lowmem) + gaddr + len <= VGAMEM_START) + return (ctx->baseaddr + gaddr); + } + + if (ctx->lowmem > VGAMEM_END) { + if (gaddr >= VGAMEM_END && gaddr < ctx->lowmem && + len <= ctx->lowmem && gaddr + len <= ctx->lowmem) return (ctx->baseaddr + gaddr); } @@ -980,6 +1028,33 @@ return (ioctl(ctx->fd, VM_MAP_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) +{ + int error; + 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; + else + vbios.size = 0; + + 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) @@ -1311,10 +1386,6 @@ return (error); } -#ifndef min -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif - int vm_copy_setup(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging, uint64_t gla, size_t len, int prot, struct iovec *iov, int iovcnt, @@ -1641,8 +1712,8 @@ VM_ISA_DEASSERT_IRQ, VM_ISA_PULSE_IRQ, VM_ISA_SET_IRQ_TRIGGER, VM_SET_CAPABILITY, VM_GET_CAPABILITY, VM_BIND_PPTDEV, VM_UNBIND_PPTDEV, VM_MAP_PPTDEV_MMIO, VM_PPTDEV_MSI, - VM_PPTDEV_MSIX, VM_INJECT_NMI, VM_STATS, VM_STAT_DESC, - VM_SET_X2APIC_STATE, VM_GET_X2APIC_STATE, + VM_PPTDEV_MSIX, 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, VM_ACTIVATE_CPU, VM_GET_CPUS, VM_SUSPEND_CPU, VM_RESUME_CPU, Index: sys/amd64/include/vmm_dev.h =================================================================== --- sys/amd64/include/vmm_dev.h +++ sys/amd64/include/vmm_dev.h @@ -141,6 +141,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; @@ -301,6 +311,7 @@ IOCNUM_MAP_PPTDEV_MMIO = 42, IOCNUM_PPTDEV_MSI = 43, IOCNUM_PPTDEV_MSIX = 44, + IOCNUM_GET_VBIOS = 46, /* statistics */ IOCNUM_VM_STATS = 50, @@ -413,6 +424,8 @@ _IOW('v', IOCNUM_PPTDEV_MSI, struct vm_pptdev_msi) #define VM_PPTDEV_MSIX \ _IOW('v', IOCNUM_PPTDEV_MSIX, struct vm_pptdev_msix) +#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 \ Index: sys/amd64/vmm/amd/amdgpu.h =================================================================== --- /dev/null +++ sys/amd64/vmm/amd/amdgpu.h @@ -0,0 +1,10 @@ + +#ifndef _AMD_AMDGPU_H_ +#define _AMD_AMDGPU_H_ + +#include + +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); + +#endif /* !_AMD_AMDGPU_H_ */ Index: sys/amd64/vmm/amd/amdgpu.c =================================================================== --- /dev/null +++ sys/amd64/vmm/amd/amdgpu.c @@ -0,0 +1,270 @@ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "amd/amdgpu.h" +#include "contrib/dev/acpica/include/acpi.h" +#include "contrib/dev/acpica/include/acpixf.h" + +#define GFP_NATIVE_MASK (M_NOWAIT | M_WAITOK | M_USE_RESERVE | M_ZERO) +#define GFP_KERNEL M_WAITOK + +//MALLOC_DECLARE(M_KMALLOC); +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); +} + +#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) + +#define acpi_get_table AcpiGetTable + +typedef struct { + uint32_t Signature; + uint32_t TableLength; //Length + uint8_t Revision; + uint8_t Checksum; + uint8_t OemId[6]; + uint8_t OemTableId[8]; //UINT64 OemTableId; + uint32_t OemRevision; + uint32_t CreatorId; + uint32_t CreatorRevision; +} AMD_ACPI_DESCRIPTION_HEADER; + +typedef struct { + AMD_ACPI_DESCRIPTION_HEADER SHeader; + uint8_t TableUUID[16]; //0x24 + uint32_t VBIOSImageOffset; //0x34. Offset to the first GOP_VBIOS_CONTENT block from the beginning of the stucture. + uint32_t Lib1ImageOffset; //0x38. Offset to the first GOP_LIB1_CONTENT block from the beginning of the stucture. + uint32_t Reserved[4]; //0x3C +}UEFI_ACPI_VFCT; + +typedef struct { + uint32_t PCIBus; //0x4C + uint32_t PCIDevice; //0x50 + uint32_t PCIFunction; //0x54 + uint16_t VendorID; //0x58 + uint16_t DeviceID; //0x5A + uint16_t SSVID; //0x5C + uint16_t SSID; //0x5E + uint32_t Revision; //0x60 + uint32_t ImageLength; //0x64 +}VFCT_IMAGE_HEADER; + + +typedef struct { + VFCT_IMAGE_HEADER VbiosHeader; + uint8_t VbiosContent[1]; +}GOP_VBIOS_CONTENT; + +/* 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) { + // vbios mem is null or mem size is wrong + return false; + } + + if (!AMD_IS_VALID_VBIOS(bios)) { + // BIOS signature incorrect + return false; + } + + bios_header_start = bios[0x48] | (bios[0x49] << 8); + if (!bios_header_start) { + // Can't locate bios header + return false; + } + + tmp = bios_header_start + 4; + if (size < tmp) { + // BIOS header is broken + return false; + } + + if (!memcmp(bios + tmp, "ATOM", 4) || + !memcmp(bios + tmp, "MOTA", 4)) { + // ATOMBIOS detected + return true; + } + + return false; +} + +static int +amdgpu_get_vbios_vfct(struct vm *vm, int bus, int slot, int func, + uint16_t vendor, uint16_t dev_id, void **bios, uint64_t *size) +{ + if (vm == NULL || bios == NULL || size == NULL) + return EINVAL; + + struct acpi_table_header *hdr; + uint32_t tbl_size; + UEFI_ACPI_VFCT *vfct; + unsigned offset; + + if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr))) + return ENOENT; + + tbl_size = hdr->Length; + + if (tbl_size < sizeof(UEFI_ACPI_VFCT)) + return ENODEV; + + 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) + return ENODEV; + + offset += vhdr->ImageLength; + if (offset > tbl_size) + return ENODEV; + + if (vhdr->ImageLength && + vhdr->PCIBus == bus && + vhdr->PCIDevice == slot && + vhdr->PCIFunction == func && + vhdr->VendorID == vendor && + vhdr->DeviceID == dev_id) { + *bios = kmemdup(&vbios->VbiosContent, + vhdr->ImageLength, + GFP_KERNEL); + + if (!check_atom_bios((uint8_t *)*bios, vhdr->ImageLength)) { + kfree(*bios); + return ENODEV; + } + *size = vhdr->ImageLength; + return 0; + } + } + + return ENOENT; +} + +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; + void *bios_base; + uint64_t bios_size; + + error = 0; + + if (amdgpu_get_vbios_vfct(vm, bus, slot, func, vendor, dev_id, &bios_base, &bios_size) == 0) + goto done; + + return ENOENT; + +done: + + if (bios) { + *size = min(bios_size, *size); + error = copyout(bios_base, bios, *size); + } else { + *size = bios_size; + } + + kfree(bios_base); + + return (error); +} Index: sys/amd64/vmm/vmm.c =================================================================== --- sys/amd64/vmm/vmm.c +++ sys/amd64/vmm/vmm.c @@ -133,7 +133,7 @@ bool sysmem; struct vm_object *object; }; -#define VM_MAX_MEMSEGS 3 +#define VM_MAX_MEMSEGS 4 struct mem_map { vm_paddr_t gpa; @@ -256,6 +256,8 @@ static void vm_free_memmap(struct vm *vm, int ident); static bool sysmem_mapping(struct vm *vm, struct mem_map *mm); static void vcpu_notify_event_locked(struct vcpu *vcpu, bool lapic_intr); +static int vm_munmap_memseg(struct vm *vm, vm_paddr_t gpa, + int segid, vm_ooffset_t segoff); #ifdef KTR static const char * @@ -729,7 +731,7 @@ vm_ooffset_t last; int i, error; - if (prot == 0 || (prot & ~(VM_PROT_ALL)) != 0) + if ((len != 0 && prot == 0) || (prot & ~(VM_PROT_ALL)) != 0) return (EINVAL); if (flags & ~VM_MEMMAP_F_WIRED) @@ -743,12 +745,16 @@ return (EINVAL); last = first + len; - if (first < 0 || first >= last || last > seg->len) + if (first < 0 || first > last || last > seg->len) return (EINVAL); if ((gpa | first | last) & PAGE_MASK) return (EINVAL); + /* The same thing at the same place but with zero length means unmap */ + if (len == 0) + return vm_munmap_memseg(vm, gpa, segid, first); + map = NULL; for (i = 0; i < VM_MAX_MEMMAPS; i++) { m = &vm->mem_maps[i]; @@ -787,6 +793,26 @@ return (0); } +static int +vm_munmap_memseg(struct vm *vm, vm_paddr_t gpa, int segid, vm_ooffset_t segoff) +{ + struct mem_map *m; + int i; + + for (i = 0; i < VM_MAX_MEMMAPS; i++) { + m = &vm->mem_maps[i]; + if (m->gpa == gpa && m->segid == segid && m->segoff == segoff) + break; + } + + if (i >= VM_MAX_MEMMAPS) + return (ENOENT); + + vm_free_memmap(vm, i); + + return (0); +} + int vm_mmap_getnext(struct vm *vm, vm_paddr_t *gpa, int *segid, vm_ooffset_t *segoff, size_t *len, int *prot, int *flags) Index: sys/amd64/vmm/vmm_dev.c =================================================================== --- sys/amd64/vmm/vmm_dev.c +++ sys/amd64/vmm/vmm_dev.c @@ -60,6 +60,7 @@ #include #include +#include "amd/amdgpu.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; @@ -435,6 +437,7 @@ break; case VM_MAP_PPTDEV_MMIO: + case VM_GET_VBIOS: case VM_BIND_PPTDEV: case VM_UNBIND_PPTDEV: #ifdef COMPAT_FREEBSD12 @@ -520,6 +523,12 @@ pptmmio->func, pptmmio->gpa, pptmmio->len, pptmmio->hpa); 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, Index: sys/modules/vmm/Makefile =================================================================== --- sys/modules/vmm/Makefile +++ sys/modules/vmm/Makefile @@ -56,7 +56,8 @@ npt.c \ ivrs_drv.c \ amdvi_hw.c \ - svm_msr.c + svm_msr.c \ + amdgpu.c .if ${KERN_OPTS:MBHYVE_SNAPSHOT} != "" SRCS+= vmm_snapshot.c Index: usr.sbin/bhyve/Makefile =================================================================== --- usr.sbin/bhyve/Makefile +++ usr.sbin/bhyve/Makefile @@ -9,6 +9,8 @@ PROG= bhyve PACKAGE= bhyve +# DEBUG_FLAGS= -g -O0 --coverage -DWITHOUT_CAPSICUM + MAN= bhyve.8 BHYVE_SYSDIR?=${SRCTOP} @@ -38,6 +40,7 @@ net_backends.c \ net_utils.c \ pci_ahci.c \ + pci_apu-d.c \ pci_e82545.c \ pci_emul.c \ pci_hda.c \ @@ -103,6 +106,7 @@ LIBADD+= crypto .endif +CFLAGS+= -no-integrated-as CFLAGS+= -I${BHYVE_SYSDIR}/sys/dev/e1000 CFLAGS+= -I${BHYVE_SYSDIR}/sys/dev/mii CFLAGS+= -I${BHYVE_SYSDIR}/sys/dev/usb/controller Index: usr.sbin/bhyve/pci_apu-d.c =================================================================== --- /dev/null +++ usr.sbin/bhyve/pci_apu-d.c @@ -0,0 +1,139 @@ +/*- + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "inout.h" +#include "pci_passthru.h" + +#define KB (1024UL) +#define MB (1024 * 1024UL) +#define GB (1024 * 1024 * 1024UL) + +#define VBIOS_ADDR 0xC0000 + +static int +apu_d_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) +{ + int error; + struct passthru_softc *sc; + + error = 0; + + if ((error = passthru_init(ctx, pi, opts)) != 0) + goto done; + + sc = pi->pi_arg; + + uint16_t vendor, dev_id; + uint64_t bios_size; + vendor = read_config(&sc->psc_sel, PCIR_VENDOR, 0x02); + dev_id = read_config(&sc->psc_sel, PCIR_DEVICE, 0x02); + + 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); + + // round up size to a power of two + // check in descendig order to avoid endless loop + pi->pi_vbios.len = 1ULL << 63; + while (pi->pi_vbios.len > bios_size) { + pi->pi_vbios.len >>= 1; + } + pi->pi_vbios.len <<= 1; + pi->pi_vbios.gpa = VBIOS_ADDR; + pi->pi_vbios.hpa = (uint64_t)vm_create_devmem(ctx, VM_VIDEOBIOS, "videobios", pi->pi_vbios.len); + if ((void *)pi->pi_vbios.hpa == MAP_FAILED) { + warnx("vm_create_devmem: %x", errno); + error = -1; + goto done; + } + + vm_get_vbios(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, vendor, dev_id, (void *)pi->pi_vbios.hpa, &bios_size); + + if ((error = pci_emul_alloc_rom(pi, VM_VIDEOBIOS, pi->pi_vbios.gpa, pi->pi_vbios.len)) != 0) { + warnx("pci_emul_alloc_rom: %x", error); + goto done; + } + +done: + return (error); +} + +static int +apu_d_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, int bytes, uint32_t val) +{ + return passthru_cfgwrite(ctx, vcpu, pi, coff, bytes, val); +} + +static int +apu_d_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, int bytes, uint32_t *rv) +{ + return passthru_cfgread(ctx, vcpu, pi, coff, bytes, rv); +} + +static void +apu_d_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size, uint64_t value) +{ + passthru_write(ctx, vcpu, pi, baridx, offset, size, value); +} + +static uint64_t +apu_d_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size) +{ + return passthru_read(ctx, vcpu, pi, baridx, offset, size); +} + +struct pci_devemu apu_d = { + .pe_emu = "apu-d", + .pe_init = apu_d_init, + .pe_cfgwrite = apu_d_cfgwrite, + .pe_cfgread = apu_d_cfgread, + .pe_barwrite = apu_d_write, + .pe_barread = apu_d_read, +}; +PCI_EMUL_SET(apu_d); + Index: usr.sbin/bhyve/pci_emul.h =================================================================== --- usr.sbin/bhyve/pci_emul.h +++ usr.sbin/bhyve/pci_emul.h @@ -85,7 +85,8 @@ PCIBAR_IO, PCIBAR_MEM32, PCIBAR_MEM64, - PCIBAR_MEMHI64 + PCIBAR_MEMHI64, + PCIBAR_ROM, }; struct pcibar { @@ -116,6 +117,12 @@ PENDING }; +struct pci_vbiosemu { + uint64_t hpa; + uint64_t len; + uint64_t gpa; +}; + struct pci_devinst { struct pci_devemu *pi_d; struct vmctx *pi_vmctx; @@ -158,6 +165,11 @@ u_char pi_cfgdata[PCI_REGMAX + 1]; struct pcibar pi_bar[PCI_BARMAX + 1]; + struct pcibar pi_rom_bar; + int pi_rom_segment; + int pi_rom_enabled; + + struct pci_vbiosemu pi_vbios; }; struct msicap { @@ -223,6 +235,8 @@ enum pcibar_type type, uint64_t size); int pci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase, enum pcibar_type type, uint64_t size); +int pci_emul_alloc_rom(struct pci_devinst *pdi, int segid, + uint32_t addr, uint32_t size); int pci_emul_add_msicap(struct pci_devinst *pi, int msgnum); int pci_emul_add_pciecap(struct pci_devinst *pi, int pcie_device_type); void pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, Index: usr.sbin/bhyve/pci_emul.c =================================================================== --- usr.sbin/bhyve/pci_emul.c +++ usr.sbin/bhyve/pci_emul.c @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -582,6 +583,76 @@ register_bar(pi, idx); } +int +pci_emul_alloc_rom(struct pci_devinst *pdi, int segid, + uint32_t addr, uint32_t size) +{ + /* The segment ID must be valid */ + if (segid <= VM_SYSMEM || segid >= VM_NOTHING) + return (-1); + + /* The size must be a power of two >= 4 KiB */ + if ((size & (size - 1)) != 0 || size < 4096) + return (-2); + + /* The address must be at a multiple of size */ + if ((addr & (size - 1)) != 0) + return (-3); + + pdi->pi_rom_segment = segid; + pdi->pi_rom_bar.type = PCIBAR_ROM; + pdi->pi_rom_bar.addr = addr; + pdi->pi_rom_bar.size = size; + pdi->pi_rom_enabled = 0; + + pci_set_cfgdata32(pdi, PCIR_BIOS, addr); + + return (0); +} + +static int +update_rom_address(struct pci_devinst *pdi) +{ + uint32_t bar, addr; + int enable; + + /* Grab info directly from the config data */ + bar = pci_get_cfgdata32(pdi, PCIR_BIOS); + enable = (bar & PCIM_BIOS_ENABLE)? 1 : 0; + addr = bar & PCIM_BIOS_ADDR_MASK; + + /* Ignore the mapping if we don't have a ROM segment to map */ + if (!pdi->pi_rom_segment) { + pdi->pi_rom_bar.addr = addr; + return (0); + } + + /* Something has changed; unmap the segment if it's mapped */ + if (pdi->pi_rom_enabled) { + vm_munmap_memseg(pdi->pi_vmctx, pdi->pi_rom_bar.addr, + pdi->pi_rom_segment, 0); + } + + pdi->pi_rom_enabled = enable && memen(pdi); + pdi->pi_rom_bar.addr = addr; + + /* Map the segment only if both ROM and global Memory Space are on */ + if (pdi->pi_rom_enabled) { + int error; + + error = vm_mmap_memseg(pdi->pi_vmctx, pdi->pi_rom_bar.addr, + pdi->pi_rom_segment, 0, pdi->pi_rom_bar.size, + PROT_READ | PROT_EXEC); + + if (error) { + perror("ROM mapping failed"); + return (-1); + } + } + + return (0); +} + int pci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase, enum pcibar_type type, uint64_t size) @@ -1736,6 +1807,9 @@ } } + if ((changed & PCIM_CMD_MEMEN)) + update_rom_address(pi); + /* * If INTx has been unmasked and is pending, assert the * interrupt. @@ -1899,6 +1973,25 @@ } pci_set_cfgdata32(pi, coff, bar); + /* + * The BAR register for an Expansion ROM is slightly different. + */ + } else if (coff >= PCIR_BIOS && coff < PCIR_BIOS + 4) { + + /* Well, it's ignored for ordinary BAR registers... */ + if (bytes != 4 || (coff & 0x3) != 0) + return; + + /* Refuse to enable unless we have a BIOS */ + mask = ~(pi->pi_rom_bar.size - 1); + if (pi->pi_rom_bar.size > 0) + mask |= PCIM_BIOS_ENABLE; + + /* Make the change and update ROM location */ + bar = *eax & mask; + pci_set_cfgdata32(pi, coff, bar); + update_rom_address(pi); + } else if (pci_emul_iscap(pi, coff)) { pci_emul_capwrite(pi, coff, bytes, *eax, 0, 0); } else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) { Index: usr.sbin/bhyve/pci_passthru.h =================================================================== --- /dev/null +++ usr.sbin/bhyve/pci_passthru.h @@ -0,0 +1,63 @@ +/*- + * 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$ + */ + +#ifndef __PCI_PASSTHRU_H__ +#define __PCI_PASSTHRU_H__ + +#include + +#include + +#include "pci_emul.h" + +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; +}; + +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 bar_access(int coff); +int passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts); +int passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, int bytes, uint32_t val); +int passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, int bytes, uint32_t *rv); +void passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size, uint64_t value); +uint64_t passthru_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size); + +#endif Index: usr.sbin/bhyve/pci_passthru.c =================================================================== --- usr.sbin/bhyve/pci_passthru.c +++ usr.sbin/bhyve/pci_passthru.c @@ -61,6 +61,7 @@ #include #include "pci_emul.h" #include "mem.h" +#include "pci_passthru.h" #ifndef _PATH_DEVPCI #define _PATH_DEVPCI "/dev/pci" @@ -83,20 +84,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) { @@ -119,7 +106,7 @@ return (len); } -static uint32_t +uint32_t read_config(const struct pcisel *sel, long reg, int width) { struct pci_io pi; @@ -135,7 +122,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; @@ -647,7 +634,7 @@ return (error); } -static int +int passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) { int bus, slot, func, error, memflags; @@ -743,7 +730,7 @@ return (error); } -static int +int bar_access(int coff) { if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) @@ -778,7 +765,7 @@ coff < sc->psc_msix.capoff + MSIX_CAPLEN); } -static int +int passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, int bytes, uint32_t *rv) { @@ -822,7 +809,7 @@ return (0); } -static int +int passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, int bytes, uint32_t val) { @@ -898,7 +885,7 @@ return (0); } -static void +void passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size, uint64_t value) { @@ -921,7 +908,7 @@ } } -static uint64_t +uint64_t passthru_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size) {