Index: lib/libvmmapi/vmmapi.h =================================================================== --- lib/libvmmapi/vmmapi.h +++ lib/libvmmapi/vmmapi.h @@ -178,6 +178,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, uint64_t *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 @@ -1000,6 +1000,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, uint64_t *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; + + error = ioctl(ctx->fd, VM_GET_VBIOS, &vbios); + + if (bios) + *bios = vbios.bios; + 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) @@ -1660,7 +1684,7 @@ VM_IOAPIC_PULSE_IRQ, VM_IOAPIC_PINCOUNT, VM_ISA_ASSERT_IRQ, 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_UNMAP_PPTDEV_MMIO, VM_PPTDEV_MSI, + VM_UNBIND_PPTDEV, VM_MAP_PPTDEV_MMIO, VM_UNMAP_PPTDEV_MMIO, VM_GET_VBIOS, VM_PPTDEV_MSI, VM_PPTDEV_MSIX, 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, 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; + uint64_t bios; + uint64_t size; +}; + struct vm_pptdev_msi { int vcpu; int bus; @@ -302,6 +312,7 @@ IOCNUM_UNMAP_PPTDEV_MMIO = 45, IOCNUM_PPTDEV_MSI = 43, IOCNUM_PPTDEV_MSIX = 44, + IOCNUM_GET_VBIOS = 46, /* statistics */ IOCNUM_VM_STATS = 50, @@ -412,6 +423,8 @@ _IOW('v', IOCNUM_MAP_PPTDEV_MMIO, struct vm_pptdev_mmio) #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_PPTDEV_MSI \ _IOW('v', IOCNUM_PPTDEV_MSI, struct vm_pptdev_msi) #define VM_PPTDEV_MSIX \ 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, uint64_t *bios, uint64_t *size); + +#endif /* !_AMD_AMDGPU_H_ */ \ No newline at end of file Index: sys/amd64/vmm/amd/amdgpu.c =================================================================== --- /dev/null +++ sys/amd64/vmm/amd/amdgpu.c @@ -0,0 +1,257 @@ +#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(roundup2(len, PAGE_SIZE), gfp); + if (dst != NULL) { + memcpy(dst, src, len); + memset((char *)dst + len, 0, roundup2(len, PAGE_SIZE) - 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, uint64_t *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 = (uint64_t)kmemdup(&vbios->VbiosContent, + vhdr->ImageLength, + GFP_KERNEL); + + if (!check_atom_bios((uint8_t *)*bios, vhdr->ImageLength)) { + kfree((void *)*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, uint64_t *bios, uint64_t *size) +{ + if (amdgpu_get_vbios_vfct(vm, bus, slot, func, vendor, dev_id, bios, size) == 0) + goto done; + + return ENOENT; + +done: + *bios = vtophys(*bios); + + return (0); +} 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; @@ -436,6 +438,7 @@ case VM_MAP_PPTDEV_MMIO: case VM_UNMAP_PPTDEV_MMIO: + case VM_GET_VBIOS: case VM_BIND_PPTDEV: case VM_UNBIND_PPTDEV: #ifdef COMPAT_FREEBSD12 @@ -526,6 +529,12 @@ 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, Index: sys/dev/pci/vga_pci.c =================================================================== --- sys/dev/pci/vga_pci.c +++ sys/dev/pci/vga_pci.c @@ -172,21 +172,21 @@ volatile unsigned char *bios; int i, rid, found; -#if defined(__amd64__) || defined(__i386__) - if (vga_pci_is_boot_display(dev)) { - /* - * On x86, the System BIOS copy the default display - * device's Video BIOS at a fixed location in system - * memory (0xC0000, 128 kBytes long) at boot time. - * - * We use this copy for the default boot device, because - * the original ROM may not be valid after boot. - */ - - *size = VGA_PCI_BIOS_SHADOW_SIZE; - return (pmap_mapbios(VGA_PCI_BIOS_SHADOW_ADDR, *size)); - } -#endif +// #if defined(__amd64__) || defined(__i386__) +// if (vga_pci_is_boot_display(dev)) { +// /* +// * On x86, the System BIOS copy the default display +// * device's Video BIOS at a fixed location in system +// * memory (0xC0000, 128 kBytes long) at boot time. +// * +// * We use this copy for the default boot device, because +// * the original ROM may not be valid after boot. +// */ + +// *size = VGA_PCI_BIOS_SHADOW_SIZE; +// return (pmap_mapbios(VGA_PCI_BIOS_SHADOW_ADDR, *size)); +// } +// #endif pcib = device_get_parent(device_get_parent(dev)); if (device_get_devclass(device_get_parent(pcib)) == 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/pci_emul.h =================================================================== --- usr.sbin/bhyve/pci_emul.h +++ usr.sbin/bhyve/pci_emul.h @@ -117,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; @@ -159,6 +165,8 @@ u_char pi_cfgdata[PCI_REGMAX + 1]; struct pcibar pi_bar[PCI_BARMAX + 1]; + + struct pci_vbiosemu vbios; }; struct msicap { Index: usr.sbin/bhyve/pci_passthru.c =================================================================== --- usr.sbin/bhyve/pci_passthru.c +++ usr.sbin/bhyve/pci_passthru.c @@ -854,6 +854,15 @@ passthru_cfgwrite_igd_gen5_75(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, int bytes, uint32_t val); +static int +passthru_init_apu(struct vmctx *ctx, struct passthru_softc *sc); +static int +passthru_cfgread_apu(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int coff, int bytes, uint32_t *rv); +static int +passthru_cfgwrite_apu(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int coff, int bytes, uint32_t val); + static int passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) { @@ -959,6 +968,14 @@ } } + // init amd apu + if (opt != NULL && strcmp(opt, "apu") == 0) { + if ((error = passthru_init_apu(ctx, sc)) != 0) { + warnx("Failed to init apu"); + goto done; + } + } + error = 0; /* success */ done: if (error) { @@ -1325,6 +1342,37 @@ return (error); } +static int +passthru_init_apu(struct vmctx *ctx, struct passthru_softc *sc) +{ + int error; + + uint16_t vendor = read_config(&sc->psc_sel, PCIR_VENDOR, 2); + uint16_t dev_id = read_config(&sc->psc_sel, PCIR_DEVICE, 2); + + if ((error = vm_get_vbios(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, vendor, dev_id, &sc->psc_pi->vbios.hpa, &sc->psc_pi->vbios.len)) != 0) { + warnx("Failed to load vBIOS (%d)", error); + goto done; + } + if ((sc->psc_pi->vbios.gpa = pci_emul_alloc_mmio(PCIBAR_MEM32, roundup2(sc->psc_pi->vbios.len, PAGE_SIZE), 0x7FF)) == 0) { + warnx("Failed to alloc guest memory for vBIOS"); + error = -ENOMEM; + goto done; + } + if ((error = vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, sc->psc_pi->vbios.gpa, roundup2(sc->psc_pi->vbios.len, PAGE_SIZE), sc->psc_pi->vbios.hpa)) != 0) { + warnx("Failed to map vBIOS to guest"); + goto done; + } + + pci_set_cfgdata32(sc->psc_pi, PCIR_BIOS, sc->psc_pi->vbios.gpa | 0x01); + + sc->psc_pi->pi_d->pe_cfgread = passthru_cfgread_apu; + sc->psc_pi->pi_d->pe_cfgwrite = passthru_cfgwrite_apu; + +done: + return (error); +} + static int bar_access(int coff) { @@ -1443,6 +1491,16 @@ return passthru_cfgread(ctx, vcpu, pi, coff, bytes, rv); } +static int +passthru_cfgread_apu(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int coff, int bytes, uint32_t *rv) +{ + if (coff >= PCIR_BIOS && coff < PCIR_BIOS + 4) + return (-1); + else + return passthru_cfgread(ctx, vcpu, pi, coff, bytes, rv); +} + static int passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, int bytes, uint32_t val) @@ -1592,6 +1650,37 @@ return passthru_cfgwrite(ctx, vcpu, pi, coff, bytes, val); } +static int +passthru_cfgwrite_apu(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int coff, int bytes, uint32_t val) +{ + if (coff == PCIR_BIOS) { + struct passthru_softc *sc; + + sc = pi->pi_arg; + + if ((val & ~0x7FF) == 0xFFFFF800) { + uint32_t lobits = pci_get_cfgdata32(pi, PCIR_BIOS) & 0x7FF; + uint32_t hibits = ~(roundup2(pi->vbios.len, PAGE_SIZE) - 1); + pci_set_cfgdata32(pi, PCIR_BIOS, hibits | lobits); + vm_unmap_pptdev_mmio(pi->pi_vmctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, pi->vbios.gpa, roundup2(pi->vbios.len, PAGE_SIZE)); + pi->vbios.gpa = 0; + } + else { + pci_set_cfgdata32(pi, PCIR_BIOS, val); + val &= ~0x7FF; + if (vm_map_pptdev_mmio(pi->pi_vmctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, val, roundup2(pi->vbios.len, PAGE_SIZE), pi->vbios.hpa)) + goto done; + pi->vbios.gpa = val; + } + } + else + return passthru_cfgwrite(ctx, vcpu, pi, coff, bytes, val); + +done: + return (0); +} + static void passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size, uint64_t value)