Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_gvt-d.c
- This file was added.
/*- | |||||
* 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/param.h> | |||||
#include <sys/types.h> | |||||
#include <dev/pci/pcireg.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <err.h> | |||||
#include <errno.h> | |||||
#include <fcntl.h> | |||||
#include <sysexits.h> | |||||
#include <unistd.h> | |||||
#include <machine/vmm.h> | |||||
#include "inout.h" | |||||
#include "pci_passthru.h" | |||||
#define KB (1024UL) | |||||
#define MB (1024 * 1024UL) | |||||
#define GB (1024 * 1024 * 1024UL) | |||||
static int | |||||
gvt_d_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, uint32_t *eax, void *arg) | |||||
{ | |||||
struct pci_devinst *pdi = arg; | |||||
struct pci_devemu *pe = pdi->pi_d; | |||||
uint64_t offset; | |||||
int i; | |||||
for (i = 0; i <= PCI_BARMAX; i++) { | |||||
if (pdi->pi_bar[i].type == PCIBAR_IO && | |||||
port >= pdi->pi_bar[i].addr && | |||||
port + bytes <= pdi->pi_bar[i].addr + pdi->pi_bar[i].size) { | |||||
offset = port - pdi->pi_bar[i].addr; | |||||
if (in) | |||||
*eax = (*pe->pe_barread)(ctx, vcpu, pdi, i, | |||||
offset, bytes); | |||||
else | |||||
(*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset, | |||||
bytes, *eax); | |||||
return (0); | |||||
} | |||||
} | |||||
return (-1); | |||||
} | |||||
void | |||||
unregister_bar_gvt_d(struct pci_devinst *pi, int idx) | |||||
{ | |||||
int error; | |||||
struct passthru_softc *sc; | |||||
struct inout_port iop; | |||||
if (pi->pi_bar[idx].addr == 0) | |||||
return; | |||||
sc = pi->pi_arg; | |||||
switch (pi->pi_bar[idx].type) { | |||||
case PCIBAR_NONE: | |||||
case PCIBAR_MEMHI64: | |||||
break; | |||||
case PCIBAR_IO: | |||||
/* | |||||
* ToDo: Passthrough IO | |||||
* | |||||
* Use IO-Bitmap to emulate access to IO ports | |||||
* This would prevent VM_EXIT on access to specified IO ports | |||||
*/ | |||||
bzero(&iop, sizeof(struct inout_port)); | |||||
iop.name = pi->pi_name; | |||||
iop.port = pi->pi_bar[idx].addr; | |||||
iop.size = pi->pi_bar[idx].size; | |||||
error = unregister_inout(&iop); | |||||
break; | |||||
case PCIBAR_MEM32: | |||||
case PCIBAR_MEM64: | |||||
if (idx != pci_msix_table_bar(pi)) { | |||||
error = vm_unmap_pptdev_mmio(pi->pi_vmctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, pi->pi_bar[idx].addr, pi->pi_bar[idx].size); | |||||
} | |||||
// special handling for msix table | |||||
else { | |||||
uint32_t table_offset, table_size; | |||||
uint32_t gpa, len; | |||||
table_offset = rounddown2(pi->pi_msix.table_offset, 4096); | |||||
table_size = pi->pi_msix.table_offset - table_offset; | |||||
table_size += pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE; | |||||
table_size = roundup2(table_size, 4096); | |||||
gpa = pi->pi_bar[idx].addr; | |||||
len = table_offset; | |||||
// unmap everything bevor MSI-X table | |||||
if (len > 0) { | |||||
if ((error = vm_unmap_pptdev_mmio(pi->pi_vmctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, gpa, len)) != 0) | |||||
goto done; | |||||
} | |||||
gpa += table_offset + table_size; | |||||
len = pi->pi_bar[idx].size - (table_offset + table_size); | |||||
// unmap everything behind MSI-X table | |||||
if (len > 0) { | |||||
if ((error = vm_unmap_pptdev_mmio(pi->pi_vmctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, gpa, len)) != 0) | |||||
goto done; | |||||
} | |||||
} | |||||
break; | |||||
} | |||||
done: | |||||
if (error != 0) | |||||
err(1, __func__); | |||||
} | |||||
void | |||||
register_bar_gvt_d(struct pci_devinst *pi, int idx) | |||||
{ | |||||
int error; | |||||
struct passthru_softc *sc; | |||||
struct inout_port iop; | |||||
sc = pi->pi_arg; | |||||
switch (pi->pi_bar[idx].type) { | |||||
case PCIBAR_NONE: | |||||
case PCIBAR_MEMHI64: | |||||
break; | |||||
case PCIBAR_IO: | |||||
/* | |||||
* ToDo: Passthrough IO | |||||
* | |||||
* Use IO-Bitmap to emulate access to IO ports | |||||
* Prevent VM_EXIT on access to specified IO ports | |||||
*/ | |||||
bzero(&iop, sizeof(struct inout_port)); | |||||
iop.name = pi->pi_name; | |||||
iop.port = pi->pi_bar[idx].addr; | |||||
iop.size = pi->pi_bar[idx].size; | |||||
iop.flags = IOPORT_F_INOUT; | |||||
iop.handler = gvt_d_io_handler; | |||||
iop.arg = pi; | |||||
error = register_inout(&iop); | |||||
break; | |||||
case PCIBAR_MEM32: | |||||
case PCIBAR_MEM64: | |||||
if (idx != pci_msix_table_bar(pi)) { | |||||
error = vm_map_pptdev_mmio(pi->pi_vmctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, pi->pi_bar[idx].addr, pi->pi_bar[idx].size, sc->psc_bar[idx].addr); | |||||
/* | |||||
* If the guest writes a new value to a 64-bit BAR, two writes are neccessary. | |||||
* vm_map_pptdev_mmio can fail in that case due to an invalid address after the first write. | |||||
*/ | |||||
if (error != 0) { | |||||
pi->pi_bar[idx].addr = 0; | |||||
error = 0; | |||||
} | |||||
} | |||||
// special handling for msix table | |||||
else { | |||||
uint32_t table_offset, table_size; | |||||
uint32_t gpa, len, hpa; | |||||
table_offset = rounddown2(pi->pi_msix.table_offset, 4096); | |||||
table_size = pi->pi_msix.table_offset - table_offset; | |||||
table_size += pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE; | |||||
table_size = roundup2(table_size, 4096); | |||||
hpa = sc->psc_bar[idx].addr; | |||||
gpa = pi->pi_bar[idx].addr; | |||||
len = table_offset; | |||||
// map everything bevor MSI-X table | |||||
if (len > 0) { | |||||
if ((error = vm_map_pptdev_mmio(pi->pi_vmctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, gpa, len, hpa)) != 0) | |||||
goto done; | |||||
} | |||||
hpa += table_offset + table_size; | |||||
gpa += table_offset + table_size; | |||||
len = pi->pi_bar[idx].size - (table_offset + table_size); | |||||
// map everything behind MSI-X table | |||||
if (len > 0) { | |||||
if ((error = vm_map_pptdev_mmio(pi->pi_vmctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, gpa, len, hpa)) != 0) | |||||
goto done; | |||||
} | |||||
} | |||||
break; | |||||
} | |||||
done: | |||||
if (error != 0) | |||||
err(1, __func__); | |||||
} | |||||
/* | |||||
* GVT-d: Handler for passthru of igd | |||||
*/ | |||||
struct igd_funcs { | |||||
uint64_t (*get_opregion_hpa)(struct vmctx *ctx, struct passthru_softc *sc); | |||||
uint64_t (*get_gsm_hpa)(struct vmctx *ctx, struct passthru_softc *sc); | |||||
uint64_t (*get_opregion_size)(struct vmctx *ctx, struct passthru_softc *sc); | |||||
uint64_t (*get_gsm_size)(struct vmctx *ctx, struct passthru_softc *sc); | |||||
void (*set_opregion_gpa)(struct vmctx *ctx, struct passthru_softc *sc, uint64_t gpa); | |||||
void (*set_gsm_gpa)(struct vmctx *ctx, struct passthru_softc *sc, uint64_t gpa); | |||||
}; | |||||
/* | |||||
* GVT-d: Handler for igd of gen5.75 (Westmere) | |||||
*/ | |||||
static uint64_t | |||||
igd_gen5_75_get_opregion_hpa(struct vmctx *ctx, struct passthru_softc *sc) | |||||
{ | |||||
return read_config(&sc->psc_sel, PCIR_ASLS_CTL, 4) & PCIM_ASLS_OPREGION_MASK; | |||||
} | |||||
static uint64_t | |||||
igd_gen5_75_get_gsm_hpa(struct vmctx *ctx, struct passthru_softc *sc) | |||||
{ | |||||
return read_config(&sc->psc_sel, PCIR_BDSM, 4) & PCIM_BDSM_GSM_MASK; | |||||
} | |||||
static uint64_t | |||||
igd_gen5_75_get_opregion_size(struct vmctx *ctx, struct passthru_softc *sc) | |||||
{ | |||||
return GPU_OPREGION_SIZE; | |||||
} | |||||
static uint64_t | |||||
igd_gen5_75_get_gsm_size(struct vmctx *ctx, struct passthru_softc *sc) | |||||
{ | |||||
uint64_t gsm_size; | |||||
uint16_t ggc_val = read_config(&sc->psc_sel, PCIR_GGC, 2); | |||||
uint8_t gms_val = (ggc_val & PCIM_GEN5_75_GGC_GMS_MASK) >> 4; /* Bits 7:4 contain Graphics Mode Select */ | |||||
switch (gms_val) { | |||||
case 0x05: | |||||
gsm_size = 32*MB; | |||||
break; | |||||
case 0x06: | |||||
gsm_size = 48*MB; | |||||
break; | |||||
case 0x07: | |||||
gsm_size = 64*MB; | |||||
break; | |||||
case 0x08: | |||||
gsm_size = 128*MB; | |||||
break; | |||||
case 0x09: | |||||
gsm_size = 256*MB; | |||||
break; | |||||
case 0x0A: | |||||
gsm_size = 96*MB; | |||||
break; | |||||
case 0x0B: | |||||
gsm_size = 160*MB; | |||||
break; | |||||
case 0x0C: | |||||
gsm_size = 224*MB; | |||||
break; | |||||
case 0x0D: | |||||
gsm_size = 352*MB; | |||||
break; | |||||
default: | |||||
gsm_size = GPU_GSM_SIZE; | |||||
warnx("Unknown Graphic Mode (%x): Fallback to %lu MB of Graphics Stolen Memory.", gms_val, gsm_size / MB); | |||||
break; | |||||
} | |||||
return gsm_size; | |||||
} | |||||
static void | |||||
igd_gen5_75_set_opregion_gpa(struct vmctx *ctx, struct passthru_softc *sc, uint64_t gpa) | |||||
{ | |||||
uint32_t asls_val = read_config(&sc->psc_sel, PCIR_ASLS_CTL, 4); | |||||
pci_set_cfgdata32(sc->psc_pi, PCIR_ASLS_CTL, gpa | (asls_val & ~PCIM_ASLS_OPREGION_MASK)); | |||||
} | |||||
static void | |||||
igd_gen5_75_set_gsm_gpa(struct vmctx *ctx, struct passthru_softc *sc, uint64_t gpa) | |||||
{ | |||||
uint32_t bdsm_val = read_config(&sc->psc_sel, PCIR_BDSM, 4); | |||||
pci_set_cfgdata32(sc->psc_pi, PCIR_BDSM, gpa | (bdsm_val & ~PCIM_BDSM_GSM_MASK)); | |||||
} | |||||
/* | |||||
* GVT-d: Handler for igd of gen6 (Sandy Bridge) | |||||
*/ | |||||
static uint64_t | |||||
igd_gen6_get_gsm_size(struct vmctx *ctx, struct passthru_softc *sc) | |||||
{ | |||||
uint64_t gsm_size; | |||||
uint16_t ggc_val = read_config(&sc->psc_sel, PCIR_GGC, 2); | |||||
uint8_t gms_val = (ggc_val & PCIM_GEN6_GGC_GMS_MASK) >> 3; /* Bits 7:3 contain Graphics Mode Select */ | |||||
if (gms_val <= 0x10) | |||||
gsm_size = gms_val * 32*MB; | |||||
else { | |||||
gsm_size = GPU_GSM_SIZE; | |||||
warnx("Unknown Graphic Mode (%x): Fallback to %lu MB of Graphics Stolen Memory.", gms_val, gsm_size / MB); | |||||
} | |||||
return gsm_size; | |||||
} | |||||
/* | |||||
* GVT-d: Handler for igd of gen8 (Broadwell) | |||||
*/ | |||||
static uint64_t | |||||
igd_gen8_get_gsm_size(struct vmctx *ctx, struct passthru_softc *sc) | |||||
{ | |||||
uint64_t gsm_size; | |||||
uint16_t ggc_val = read_config(&sc->psc_sel, PCIR_GGC, 2); | |||||
uint8_t gms_val = (ggc_val & PCIM_GEN8_GGC_GMS_MASK) >> 8; /* Bits 15:8 contain Graphics Mode Select */ | |||||
if (gms_val <= 0x10) | |||||
gsm_size = gms_val * 32*MB; | |||||
else if (gms_val == 0x20) | |||||
gsm_size = 1024*MB; | |||||
else if (gms_val == 0x30) | |||||
gsm_size = 1536*MB; | |||||
else if (gms_val == 0x3F) | |||||
gsm_size = 2016*MB; | |||||
else { | |||||
gsm_size = GPU_GSM_SIZE; | |||||
warnx("Unknown Graphic Mode (%x): Fallback to %lu MB of Graphics Stolen Memory.", gms_val, gsm_size / MB); | |||||
} | |||||
return gsm_size; | |||||
} | |||||
/* | |||||
* GVT-d: Handler for igd of gen9 (Skylake) | |||||
*/ | |||||
static uint64_t | |||||
igd_gen9_get_gsm_size(struct vmctx *ctx, struct passthru_softc *sc) | |||||
{ | |||||
uint64_t gsm_size; | |||||
uint16_t ggc_val = read_config(&sc->psc_sel, PCIR_GGC, 2); | |||||
uint8_t gms_val = (ggc_val & PCIM_GEN8_GGC_GMS_MASK) >> 8; /* Bits 15:8 contain Graphics Mode Select */ | |||||
if (gms_val <= 0x10) | |||||
gsm_size = gms_val * 32*MB; | |||||
else if (gms_val == 0x20) | |||||
gsm_size = 1024*MB; | |||||
else if (gms_val == 0x30) | |||||
gsm_size = 1536*MB; | |||||
else if (gms_val == 0x40) | |||||
gsm_size = 2048*MB; | |||||
else if (gms_val >= 0xF0 && gms_val <= 0xFE) | |||||
gsm_size = gms_val * 4*MB; | |||||
else { | |||||
gsm_size = GPU_GSM_SIZE; | |||||
warnx("Unknown Graphic Mode (%x): Fallback to %lu MB of Graphics Stolen Memory.", gms_val, gsm_size / MB); | |||||
} | |||||
return gsm_size; | |||||
} | |||||
// Westmere | |||||
struct igd_funcs igd_gen5_75 = { | |||||
.get_opregion_hpa = igd_gen5_75_get_opregion_hpa, | |||||
.get_gsm_hpa = igd_gen5_75_get_gsm_hpa, | |||||
.get_opregion_size = igd_gen5_75_get_opregion_size, | |||||
.get_gsm_size = igd_gen5_75_get_gsm_size, | |||||
.set_opregion_gpa = igd_gen5_75_set_opregion_gpa, | |||||
.set_gsm_gpa = igd_gen5_75_set_gsm_gpa | |||||
}; | |||||
// Sandy Bridge | |||||
struct igd_funcs igd_gen6 = { | |||||
.get_opregion_hpa = igd_gen5_75_get_opregion_hpa, | |||||
.get_gsm_hpa = igd_gen5_75_get_gsm_hpa, | |||||
.get_opregion_size = igd_gen5_75_get_opregion_size, | |||||
.get_gsm_size = igd_gen6_get_gsm_size, | |||||
.set_opregion_gpa = igd_gen5_75_set_opregion_gpa, | |||||
.set_gsm_gpa = igd_gen5_75_set_gsm_gpa | |||||
}; | |||||
// Ivy Bridge | |||||
struct igd_funcs igd_gen7 = { | |||||
.get_opregion_hpa = igd_gen5_75_get_opregion_hpa, | |||||
.get_gsm_hpa = igd_gen5_75_get_gsm_hpa, | |||||
.get_opregion_size = igd_gen5_75_get_opregion_size, | |||||
.get_gsm_size = igd_gen6_get_gsm_size, | |||||
.set_opregion_gpa = igd_gen5_75_set_opregion_gpa, | |||||
.set_gsm_gpa = igd_gen5_75_set_gsm_gpa | |||||
}; | |||||
// Haswell | |||||
struct igd_funcs igd_gen7_5 = { | |||||
.get_opregion_hpa = igd_gen5_75_get_opregion_hpa, | |||||
.get_gsm_hpa = igd_gen5_75_get_gsm_hpa, | |||||
.get_opregion_size = igd_gen5_75_get_opregion_size, | |||||
.get_gsm_size = igd_gen6_get_gsm_size, | |||||
.set_opregion_gpa = igd_gen5_75_set_opregion_gpa, | |||||
.set_gsm_gpa = igd_gen5_75_set_gsm_gpa | |||||
}; | |||||
// Broadwell | |||||
struct igd_funcs igd_gen8 = { | |||||
.get_opregion_hpa = igd_gen5_75_get_opregion_hpa, | |||||
.get_gsm_hpa = igd_gen5_75_get_gsm_hpa, | |||||
.get_opregion_size = igd_gen5_75_get_opregion_size, | |||||
.get_gsm_size = igd_gen8_get_gsm_size, | |||||
.set_opregion_gpa = igd_gen5_75_set_opregion_gpa, | |||||
.set_gsm_gpa = igd_gen5_75_set_gsm_gpa | |||||
}; | |||||
// Skylake | |||||
struct igd_funcs igd_gen9 = { | |||||
.get_opregion_hpa = igd_gen5_75_get_opregion_hpa, | |||||
.get_gsm_hpa = igd_gen5_75_get_gsm_hpa, | |||||
.get_opregion_size = igd_gen5_75_get_opregion_size, | |||||
.get_gsm_size = igd_gen9_get_gsm_size, | |||||
.set_opregion_gpa = igd_gen5_75_set_opregion_gpa, | |||||
.set_gsm_gpa = igd_gen5_75_set_gsm_gpa | |||||
}; | |||||
// Kabylake & Coffeelake | |||||
struct igd_funcs igd_gen9_5 = { | |||||
.get_opregion_hpa = igd_gen5_75_get_opregion_hpa, | |||||
.get_gsm_hpa = igd_gen5_75_get_gsm_hpa, | |||||
.get_opregion_size = igd_gen5_75_get_opregion_size, | |||||
.get_gsm_size = igd_gen9_get_gsm_size, | |||||
.set_opregion_gpa = igd_gen5_75_set_opregion_gpa, | |||||
.set_gsm_gpa = igd_gen5_75_set_gsm_gpa | |||||
}; | |||||
static int | |||||
gvt_d_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) | |||||
{ | |||||
int error; | |||||
uint32_t opregion_hpa, opregion_gpa, opregion_size, gsm_hpa, gsm_gpa, gsm_size; | |||||
struct passthru_softc *sc; | |||||
error = 1; | |||||
if ((error = passthru_init(ctx, pi, opts)) != 0) | |||||
goto done; | |||||
sc = pi->pi_arg; | |||||
/* Enable Memory and IO Space for device */ | |||||
uint16_t cmd = read_config(&sc->psc_sel, PCIR_COMMAND, 0x02); | |||||
cmd |= PCIM_CMD_MEMEN | PCIM_CMD_PORTEN; | |||||
write_config(&sc->psc_sel, PCIR_COMMAND, 0x02, cmd); | |||||
pci_set_cfgdata16(pi, PCIR_COMMAND, cmd); | |||||
/* Use same prefetchable property as physical BAR */ | |||||
for (int i = 0; i < PCI_BARMAX; ++i) { | |||||
uint32_t bar = pci_get_cfgdata32(pi, PCIR_BAR(i)); | |||||
switch (pi->pi_bar[i].type) { | |||||
case PCIBAR_IO: | |||||
bar &= PCIM_BAR_IO_BASE; | |||||
bar |= sc->psc_bar[i].lobits; | |||||
break; | |||||
case PCIBAR_MEM32: | |||||
case PCIBAR_MEM64: | |||||
bar &= PCIM_BAR_MEM_BASE; | |||||
bar |= sc->psc_bar[i].lobits; | |||||
break; | |||||
} | |||||
pci_set_cfgdata32(pi, PCIR_BAR(i), bar); | |||||
} | |||||
sc = pi->pi_arg; | |||||
uint16_t dev_vendor = read_config(&sc->psc_sel, PCIR_VENDOR, 2); | |||||
uint16_t dev_id = read_config(&sc->psc_sel, PCIR_DEVICE, 2); | |||||
if (dev_vendor != 0x8086) { | |||||
error = -ENODEV; | |||||
warnx("Unknown vendor (%x) of igd", dev_vendor); | |||||
goto done; | |||||
} | |||||
/* | |||||
* GVT-d: Create LPC-Device at 0:1f.0 | |||||
* | |||||
* Otherwise GOP-Driver wouldn't work for Windows | |||||
*/ | |||||
printf("Add igd-lpc at slot 0:1f.0 to enable GVT-d for igd\n"); | |||||
if ((error = pci_parse_slot("0:31:0,igd-lpc")) != 0) { | |||||
warnx("Failed to add igd-lpc"); | |||||
goto done; | |||||
} | |||||
/* | |||||
* GVT-d: Get IGD funcs | |||||
*/ | |||||
struct igd_funcs *igd; | |||||
switch (dev_id & 0xFFF0) { | |||||
case IGD_DEVID_WESTMERE: | |||||
igd = &igd_gen5_75; | |||||
break; | |||||
case IGD_DEVID_SANDYBRIDGE_0: | |||||
case IGD_DEVID_SANDYBRIDGE_1: | |||||
case IGD_DEVID_SANDYBRIDGE_2: | |||||
igd = &igd_gen6; | |||||
break; | |||||
case IGD_DEVID_IVYBRIDGE_0: | |||||
case IGD_DEVID_IVYBRIDGE_1: | |||||
igd = &igd_gen7; | |||||
break; | |||||
default: | |||||
switch (dev_id & 0xFF00) { | |||||
case IGD_DEVID_HASWELL: | |||||
igd = &igd_gen7_5; | |||||
break; | |||||
case IGD_DEVID_BROADWELL: | |||||
igd = &igd_gen8; | |||||
break; | |||||
case IGD_DEVID_SKYLAKE: | |||||
igd = &igd_gen9; | |||||
break; | |||||
case IGD_DEVID_KABYLAKE: | |||||
case IGD_DEVID_COFFEELAKE: | |||||
igd = &igd_gen9_5; | |||||
break; | |||||
default: | |||||
warnx("Unsupported igd-device (%x): Try using gen9 graphics code path.", dev_id); | |||||
igd = &igd_gen9; | |||||
break; | |||||
} | |||||
break; | |||||
} | |||||
/* | |||||
* GVT-d: Get hpa and size of Opregion and GSM | |||||
*/ | |||||
opregion_hpa = igd->get_opregion_hpa(ctx, sc); | |||||
gsm_hpa = igd->get_gsm_hpa(ctx, sc); | |||||
opregion_size = igd->get_opregion_size(ctx, sc); | |||||
gsm_size = igd->get_gsm_size(ctx, sc); | |||||
/* | |||||
* GVT-d: Allocate Opregion and GSM in guest space | |||||
*/ | |||||
if ((opregion_gpa = pci_emul_alloc_mmio(PCIBAR_MEM32, opregion_size, ~PCIM_ASLS_OPREGION_MASK)) == 0) { | |||||
error = -ENOMEM; | |||||
goto done; | |||||
} | |||||
if ((gsm_gpa = pci_emul_alloc_mmio(PCIBAR_MEM32, gsm_size, ~PCIM_BDSM_GSM_MASK)) == 0) { | |||||
error = -ENOMEM; | |||||
goto done; | |||||
} | |||||
/* | |||||
* GVT-d: Write address of Opregion and GSM into PCI register | |||||
*/ | |||||
igd->set_opregion_gpa(ctx, sc, opregion_gpa); | |||||
igd->set_gsm_gpa(ctx, sc, gsm_gpa); | |||||
/* | |||||
* GVT-d: Map Opregion and GSM into guest space | |||||
*/ | |||||
if ((error = vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, opregion_gpa, opregion_size, opregion_hpa)) != 0) | |||||
goto done; | |||||
if ((error = vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, gsm_gpa, gsm_size, gsm_hpa)) != 0) | |||||
goto done; | |||||
done: | |||||
if (error) { | |||||
vm_unmap_pptdev_mmio(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, opregion_gpa, opregion_size); | |||||
vm_unmap_pptdev_mmio(ctx, sc->psc_sel.pc_bus, sc->psc_sel.pc_dev, sc->psc_sel.pc_func, gsm_gpa, gsm_size); | |||||
int bus, slot, func; | |||||
if (sc) { | |||||
bus = sc->psc_sel.pc_bus; | |||||
slot = sc->psc_sel.pc_dev; | |||||
func = sc->psc_sel.pc_func; | |||||
free(sc); | |||||
} | |||||
if (bus != 0 || slot != 0 || func != 0) | |||||
vm_unassign_pptdev(ctx, bus, slot, func); | |||||
} | |||||
return error; | |||||
} | |||||
static int | |||||
gvt_d_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, int bytes, uint32_t val) | |||||
{ | |||||
/* | |||||
* Prevent write to BDSM and ASLS_CTL | |||||
* | |||||
* BDSM: contains Base of Data Stolen Memory | |||||
* ASLS_CTL: contains address of Opregion | |||||
*/ | |||||
if (coff == PCIR_BDSM || coff == PCIR_ASLS_CTL) { | |||||
return (0); | |||||
} else if (bar_access(coff)) { | |||||
struct passthru_softc *sc; | |||||
int idx, update_idx; | |||||
sc = pi->pi_arg; | |||||
idx = (coff - PCIR_BAR(0)) / 4; | |||||
switch (pi->pi_bar[idx].type) | |||||
{ | |||||
case PCIBAR_NONE: | |||||
pi->pi_bar[idx].addr = 0; | |||||
break; | |||||
case PCIBAR_IO: | |||||
case PCIBAR_MEM32: | |||||
case PCIBAR_MEM64: | |||||
case PCIBAR_MEMHI64: | |||||
if (pi->pi_bar[idx].type == PCIBAR_MEMHI64) | |||||
update_idx = idx - 1; | |||||
else | |||||
update_idx = idx; | |||||
uint16_t cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); | |||||
if ((cmd & PCIM_CMD_MEMEN && pi->pi_bar[idx].type != PCIBAR_IO) || | |||||
(cmd & PCIM_CMD_PORTEN && pi->pi_bar[idx].type == PCIBAR_IO)) { | |||||
unregister_bar_gvt_d(pi, update_idx); | |||||
} | |||||
if (val != ~0U) { | |||||
uint64_t mask, bar; | |||||
mask = ~(pi->pi_bar[update_idx].size - 1); | |||||
if (pi->pi_bar[idx].type == PCIBAR_MEMHI64) | |||||
mask >>= 32; | |||||
bar = val & mask; | |||||
if (pi->pi_bar[idx].type != PCIBAR_MEMHI64) | |||||
bar |= sc->psc_bar[update_idx].lobits; | |||||
pci_set_cfgdata32(pi, coff, bar); | |||||
uint32_t lo, hi; | |||||
lo = pci_get_cfgdata32(pi, PCIR_BAR(update_idx)) & ~0x0F; | |||||
if (pi->pi_bar[update_idx].type == PCIBAR_MEM64) | |||||
hi = pci_get_cfgdata32(pi, PCIR_BAR(update_idx + 1)); | |||||
else | |||||
hi = 0; | |||||
if (lo != ~0U && hi != ~0U) { | |||||
pi->pi_bar[update_idx].addr = (uint64_t)lo | ((uint64_t)hi << 32U); | |||||
if ((cmd & PCIM_CMD_MEMEN && pi->pi_bar[idx].type != PCIBAR_IO) || | |||||
(cmd & PCIM_CMD_PORTEN && pi->pi_bar[idx].type == PCIBAR_IO)) { | |||||
register_bar_gvt_d(pi, update_idx); | |||||
} | |||||
} | |||||
else | |||||
pi->pi_bar[update_idx].addr = 0; | |||||
} | |||||
else { | |||||
pci_set_cfgdata32(pi, coff, ~0U); | |||||
pi->pi_bar[update_idx].addr = 0; | |||||
} | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
return (0); | |||||
} else { | |||||
return passthru_cfgwrite(ctx, vcpu, pi, coff, bytes, val); | |||||
} | |||||
} | |||||
static int | |||||
gvt_d_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int coff, int bytes, uint32_t *rv) | |||||
{ | |||||
/* | |||||
* Emulate BDSM and ASLS_CTL | |||||
* | |||||
* BDSM: contains Base of Data Stolen Memory | |||||
* ASLS_CTL: contains address of Opregion | |||||
*/ | |||||
if ((coff >= PCIR_BDSM && coff < PCIR_BDSM + 4) || (coff >= PCIR_ASLS_CTL && coff < PCIR_ASLS_CTL + 4)) { | |||||
return (-1); | |||||
} else if (bar_access(coff)) { | |||||
struct passthru_softc *sc; | |||||
int idx, update_idx; | |||||
sc = pi->pi_arg; | |||||
idx = (coff - PCIR_BAR(0)) / 4; | |||||
if (pi->pi_bar[idx].type == PCIBAR_MEMHI64) | |||||
update_idx = idx - 1; | |||||
else | |||||
update_idx = idx; | |||||
if (pci_get_cfgdata32(pi, 0x10 + idx * 0x04) == ~0U) { | |||||
uint64_t size = ~(uint64_t)(pi->pi_bar[update_idx].size - 1); | |||||
size |= sc->psc_bar[update_idx].lobits; | |||||
if (pi->pi_bar[idx].type == PCIBAR_MEMHI64) | |||||
*rv = size >> 32; | |||||
else | |||||
*rv = size; | |||||
if (bytes == 1) | |||||
*rv = *rv >> (coff & 0x3); | |||||
else if (bytes == 2) | |||||
*rv = *rv >> (coff & 0x1); | |||||
else | |||||
*rv = *rv; | |||||
} | |||||
else { | |||||
if (bytes == 1) | |||||
*rv = pci_get_cfgdata8(pi, coff); | |||||
else if (bytes == 2) | |||||
*rv = pci_get_cfgdata16(pi, coff); | |||||
else | |||||
*rv = pci_get_cfgdata32(pi, coff); | |||||
} | |||||
return (0); | |||||
} else { | |||||
return passthru_cfgread(ctx, vcpu, pi, coff, bytes, rv); | |||||
} | |||||
} | |||||
static void | |||||
gvt_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 | |||||
gvt_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 gvt_d = { | |||||
.pe_emu = "gvt-d", | |||||
.pe_init = gvt_d_init, | |||||
.pe_cfgwrite = gvt_d_cfgwrite, | |||||
.pe_cfgread = gvt_d_cfgread, | |||||
.pe_barwrite = gvt_d_write, | |||||
.pe_barread = gvt_d_read | |||||
}; | |||||
PCI_EMUL_SET(gvt_d); |