Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_passthru.c
Show First 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
#include <errno.h> | #include <errno.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <sysexits.h> | #include <sysexits.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <machine/vmm.h> | #include <machine/vmm.h> | ||||
#include <vmmapi.h> | #include <vmmapi.h> | ||||
#include "pci_emul.h" | #include "pci_emul.h" | ||||
#include "inout.h" | |||||
#include "mem.h" | #include "mem.h" | ||||
#ifndef _PATH_DEVPCI | #ifndef _PATH_DEVPCI | ||||
#define _PATH_DEVPCI "/dev/pci" | #define _PATH_DEVPCI "/dev/pci" | ||||
#endif | #endif | ||||
#ifndef _PATH_DEVIO | #ifndef _PATH_DEVIO | ||||
#define _PATH_DEVIO "/dev/io" | #define _PATH_DEVIO "/dev/io" | ||||
#endif | #endif | ||||
#ifndef _PATH_MEM | #ifndef _PATH_MEM | ||||
#define _PATH_MEM "/dev/mem" | #define _PATH_MEM "/dev/mem" | ||||
#endif | #endif | ||||
#define LEGACY_SUPPORT 1 | #define LEGACY_SUPPORT 1 | ||||
#define MSIX_TABLE_COUNT(ctrl) (((ctrl) & PCIM_MSIXCTRL_TABLE_SIZE) + 1) | #define MSIX_TABLE_COUNT(ctrl) (((ctrl) & PCIM_MSIXCTRL_TABLE_SIZE) + 1) | ||||
#define MSIX_CAPLEN 12 | #define MSIX_CAPLEN 12 | ||||
#define KB (1024UL) | |||||
#define MB (1024 * 1024UL) | |||||
#define GB (1024 * 1024 * 1024UL) | |||||
static int pcifd = -1; | static int pcifd = -1; | ||||
static int iofd = -1; | static int iofd = -1; | ||||
static int memfd = -1; | static int memfd = -1; | ||||
enum passthru_type { | |||||
PASSTHRU_DEFAULT = 0, | |||||
PASSTHRU_IGD | |||||
}; | |||||
struct passthru_softc { | struct passthru_softc { | ||||
struct pci_devinst *psc_pi; | struct pci_devinst *psc_pi; | ||||
struct pcibar psc_bar[PCI_BARMAX + 1]; | struct pcibar psc_bar[PCI_BARMAX + 1]; | ||||
struct { | struct { | ||||
int capoff; | int capoff; | ||||
int msgctrl; | int msgctrl; | ||||
int emulated; | int emulated; | ||||
} psc_msi; | } psc_msi; | ||||
struct { | struct { | ||||
int capoff; | int capoff; | ||||
} psc_msix; | } psc_msix; | ||||
struct pcisel psc_sel; | struct pcisel psc_sel; | ||||
enum passthru_type psc_type; | |||||
}; | }; | ||||
static int | static int | ||||
msi_caplen(int msgctrl) | msi_caplen(int msgctrl) | ||||
{ | { | ||||
int len; | int len; | ||||
len = 10; /* minimum length of msi capability */ | len = 10; /* minimum length of msi capability */ | ||||
Show All 38 Lines | write_config(const struct pcisel *sel, long reg, int width, uint32_t data) | ||||
pi.pi_sel = *sel; | pi.pi_sel = *sel; | ||||
pi.pi_reg = reg; | pi.pi_reg = reg; | ||||
pi.pi_width = width; | pi.pi_width = width; | ||||
pi.pi_data = data; | pi.pi_data = data; | ||||
(void)ioctl(pcifd, PCIOCWRITE, &pi); /* XXX */ | (void)ioctl(pcifd, PCIOCWRITE, &pi); /* XXX */ | ||||
} | } | ||||
static int | |||||
pci_emul_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_passthru(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 | |||||
* 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_passthru(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 = pci_emul_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__); | |||||
} | |||||
#ifdef LEGACY_SUPPORT | #ifdef LEGACY_SUPPORT | ||||
static int | static int | ||||
passthru_add_msicap(struct pci_devinst *pi, int msgnum, int nextptr) | passthru_add_msicap(struct pci_devinst *pi, int msgnum, int nextptr) | ||||
{ | { | ||||
int capoff, i; | int capoff, i; | ||||
struct msicap msicap; | struct msicap msicap; | ||||
u_char *capdata; | u_char *capdata; | ||||
▲ Show 20 Lines • Show All 416 Lines • ▼ Show 20 Lines | if (bartype != PCIBAR_IO) { | ||||
return (-1); | return (-1); | ||||
} | } | ||||
} | } | ||||
/* Cache information about the "real" BAR */ | /* Cache information about the "real" BAR */ | ||||
sc->psc_bar[i].type = bartype; | sc->psc_bar[i].type = bartype; | ||||
sc->psc_bar[i].size = size; | sc->psc_bar[i].size = size; | ||||
sc->psc_bar[i].addr = base; | sc->psc_bar[i].addr = base; | ||||
sc->psc_bar[i].lobits = 0; | |||||
/* Allocate the BAR in the guest I/O or MMIO space */ | /* Allocate the BAR in the guest I/O or MMIO space */ | ||||
error = pci_emul_alloc_pbar(pi, i, base, bartype, size); | error = pci_emul_alloc_pbar(pi, i, base, bartype, size); | ||||
if (error) | if (error) | ||||
return (-1); | return (-1); | ||||
/* | |||||
* For passthru devices use same prefetchable property as physical bar | |||||
*/ | |||||
if (bartype == PCIBAR_MEM32 || bartype == PCIBAR_MEM64) | |||||
{ | |||||
uint8_t lobits = pci_get_cfgdata8(pi, 0x10 + i * 0x04); | |||||
if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH) | |||||
lobits |= PCIM_BAR_MEM_PREFETCH; | |||||
else | |||||
lobits &= ~PCIM_BAR_MEM_PREFETCH; | |||||
sc->psc_bar[i].lobits = lobits & 0xF; | |||||
pci_set_cfgdata8(pi, 0x10 + i * 0x04, lobits); | |||||
} | |||||
else | |||||
sc->psc_bar[i].lobits = PCIM_BAR_IO_SPACE; | |||||
/* The MSI-X table needs special handling */ | /* The MSI-X table needs special handling */ | ||||
if (i == pci_msix_table_bar(pi)) { | if (i == pci_msix_table_bar(pi)) { | ||||
error = init_msix_table(ctx, sc, base); | error = init_msix_table(ctx, sc, base); | ||||
if (error) | if (error) | ||||
return (-1); | return (-1); | ||||
} else if (bartype != PCIBAR_IO) { | |||||
/* Map the physical BAR in the guest MMIO space */ | |||||
error = vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, | |||||
sc->psc_sel.pc_dev, sc->psc_sel.pc_func, | |||||
pi->pi_bar[i].addr, pi->pi_bar[i].size, base); | |||||
if (error) | |||||
return (-1); | |||||
} | } | ||||
/* | /* | ||||
* 64-bit BAR takes up two slots so skip the next one. | * 64-bit BAR takes up two slots so skip the next one. | ||||
*/ | */ | ||||
if (bartype == PCIBAR_MEM64) { | if (bartype == PCIBAR_MEM64) { | ||||
i++; | i++; | ||||
assert(i <= PCI_BARMAX); | assert(i <= PCI_BARMAX); | ||||
Show All 18 Lines | cfginit(struct vmctx *ctx, struct pci_devinst *pi, int bus, int slot, int func) | ||||
sc->psc_sel.pc_func = func; | sc->psc_sel.pc_func = func; | ||||
if (cfginitmsi(sc) != 0) { | if (cfginitmsi(sc) != 0) { | ||||
warnx("failed to initialize MSI for PCI %d/%d/%d", | warnx("failed to initialize MSI for PCI %d/%d/%d", | ||||
bus, slot, func); | bus, slot, func); | ||||
goto done; | goto done; | ||||
} | } | ||||
/* | |||||
* Set command register before init of BARs | |||||
* | |||||
* cfginitbar checks command register to decide whether to register a new BAR or not | |||||
*/ | |||||
pci_set_cfgdata16(pi, PCIR_COMMAND, read_config(&sc->psc_sel, | |||||
PCIR_COMMAND, 2)); | |||||
if (cfginitbar(ctx, sc) != 0) { | if (cfginitbar(ctx, sc) != 0) { | ||||
warnx("failed to initialize BARs for PCI %d/%d/%d", | warnx("failed to initialize BARs for PCI %d/%d/%d", | ||||
bus, slot, func); | bus, slot, func); | ||||
goto done; | goto done; | ||||
} | } | ||||
pci_set_cfgdata16(pi, PCIR_COMMAND, read_config(&sc->psc_sel, | |||||
PCIR_COMMAND, 2)); | |||||
error = 0; /* success */ | error = 0; /* success */ | ||||
done: | done: | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | |||||
* GVT-d: Declare modified funcs for passthrough of igd-device | |||||
*/ | |||||
static int | static int | ||||
passthru_init_igd(struct vmctx *ctx, struct passthru_softc *sc); | |||||
static int | |||||
passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) | passthru_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) | ||||
{ | { | ||||
char *opt; | |||||
int bus, slot, func, error, memflags; | int bus, slot, func, error, memflags; | ||||
struct passthru_softc *sc; | struct passthru_softc *sc; | ||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
cap_rights_t rights; | cap_rights_t rights; | ||||
cap_ioctl_t pci_ioctls[] = { PCIOCREAD, PCIOCWRITE, PCIOCGETBAR }; | cap_ioctl_t pci_ioctls[] = { PCIOCREAD, PCIOCWRITE, PCIOCGETBAR }; | ||||
cap_ioctl_t io_ioctls[] = { IODEV_PIO }; | cap_ioctl_t io_ioctls[] = { IODEV_PIO }; | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | if (caph_rights_limit(memfd, &rights) == -1) | ||||
errx(EX_OSERR, "Unable to apply rights for sandbox"); | errx(EX_OSERR, "Unable to apply rights for sandbox"); | ||||
#endif | #endif | ||||
if (opts == NULL || | if (opts == NULL || | ||||
sscanf(opts, "%d/%d/%d", &bus, &slot, &func) != 3) { | sscanf(opts, "%d/%d/%d", &bus, &slot, &func) != 3) { | ||||
warnx("invalid passthru options"); | warnx("invalid passthru options"); | ||||
return (error); | return (error); | ||||
} | } | ||||
if ((opt = strchr(opts, ',')) != NULL) | |||||
{ | |||||
*opt = '\0'; | |||||
opt = opt + 1; | |||||
} | |||||
if (vm_assign_pptdev(ctx, bus, slot, func) != 0) { | if (vm_assign_pptdev(ctx, bus, slot, func) != 0) { | ||||
warnx("PCI device at %d/%d/%d is not using the ppt(4) driver", | warnx("PCI device at %d/%d/%d is not using the ppt(4) driver", | ||||
bus, slot, func); | bus, slot, func); | ||||
goto done; | goto done; | ||||
} | } | ||||
sc = calloc(1, sizeof(struct passthru_softc)); | sc = calloc(1, sizeof(struct passthru_softc)); | ||||
pi->pi_arg = sc; | pi->pi_arg = sc; | ||||
sc->psc_pi = pi; | sc->psc_pi = pi; | ||||
/* initialize config space */ | /* initialize config space */ | ||||
error = cfginit(ctx, pi, bus, slot, func); | if ((error = cfginit(ctx, pi, bus, slot, func)) != 0) | ||||
goto done; | |||||
// init igd (integrated graphics device) | |||||
if (opt != NULL && strcmp(opt, "igd") == 0) { | |||||
if ((error = passthru_init_igd(ctx, sc)) != 0) { | |||||
warnx("Failed to init igd"); | |||||
goto done; | |||||
} | |||||
} | |||||
error = 0; /* success */ | |||||
done: | done: | ||||
if (error) { | if (error) { | ||||
free(sc); | free(sc); | ||||
vm_unassign_pptdev(ctx, bus, slot, func); | vm_unassign_pptdev(ctx, bus, slot, func); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | |||||
* 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 | static int | ||||
passthru_init_igd(struct vmctx *ctx, struct passthru_softc *sc) | |||||
{ | |||||
int error; | |||||
uint32_t opregion_hpa, opregion_gpa, opregion_size, gsm_hpa, gsm_gpa, gsm_size; | |||||
error = 1; | |||||
sc->psc_type = PASSTHRU_IGD; | |||||
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) { | |||||
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 and protect their 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); | |||||
} | |||||
return (error); | |||||
} | |||||
static int | |||||
bar_access(int coff) | bar_access(int coff) | ||||
{ | { | ||||
if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) | if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) | ||||
return (1); | return (1); | ||||
else | else | ||||
return (0); | return (0); | ||||
} | } | ||||
Show All 19 Lines | msixcap_access(struct passthru_softc *sc, int coff) | ||||
if (sc->psc_msix.capoff == 0) | if (sc->psc_msix.capoff == 0) | ||||
return (0); | return (0); | ||||
return (coff >= sc->psc_msix.capoff && | return (coff >= sc->psc_msix.capoff && | ||||
coff < sc->psc_msix.capoff + MSIX_CAPLEN); | coff < sc->psc_msix.capoff + MSIX_CAPLEN); | ||||
} | } | ||||
static int | static int | ||||
passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | passthru_cfgread_default(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | ||||
int coff, int bytes, uint32_t *rv) | int coff, int bytes, uint32_t *rv) | ||||
{ | { | ||||
struct passthru_softc *sc; | struct passthru_softc *sc; | ||||
sc = pi->pi_arg; | sc = pi->pi_arg; | ||||
/* | /* | ||||
* PCI BARs and MSI capability is emulated. | * MSI capability is emulated. | ||||
*/ | */ | ||||
if (bar_access(coff) || msicap_access(sc, coff)) | if (msicap_access(sc, coff)) | ||||
return (-1); | return (-1); | ||||
if (bar_access(coff)) | |||||
{ | |||||
int idx, update_idx; | |||||
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); | |||||
} | |||||
#ifdef LEGACY_SUPPORT | #ifdef LEGACY_SUPPORT | ||||
/* | /* | ||||
* Emulate PCIR_CAP_PTR if this device does not support MSI capability | * Emulate PCIR_CAP_PTR if this device does not support MSI capability | ||||
* natively. | * natively. | ||||
*/ | */ | ||||
if (sc->psc_msi.emulated) { | if (sc->psc_msi.emulated) { | ||||
if (coff >= PCIR_CAP_PTR && coff < PCIR_CAP_PTR + 4) | if (coff >= PCIR_CAP_PTR && coff < PCIR_CAP_PTR + 4) | ||||
return (-1); | return (-1); | ||||
} | } | ||||
#endif | #endif | ||||
/* Everything else just read from the device's config space */ | |||||
*rv = read_config(&sc->psc_sel, coff, bytes); | |||||
return (0); | |||||
} | |||||
static int | |||||
passthru_cfgread_igd(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | |||||
int coff, int bytes, uint32_t *rv) | |||||
{ | |||||
/* | /* | ||||
* Emulate the command register. If a single read reads both the | * GVT-d: Emulate BDSM and ASLS_CTL | ||||
* command and status registers, read the status register from the | * | ||||
* device's config space. | * BDSM: contains Base of Data Stolen Memory | ||||
* ASLS_CTL: contains address of Opregion | |||||
*/ | */ | ||||
if (coff == PCIR_COMMAND) { | if ((coff >= PCIR_BDSM && coff < PCIR_BDSM + 4) || (coff >= PCIR_ASLS_CTL && coff < PCIR_ASLS_CTL + 4)) | ||||
if (bytes <= 2) | |||||
return (-1); | return (-1); | ||||
*rv = read_config(&sc->psc_sel, PCIR_STATUS, 2) << 16 | | else | ||||
pci_get_cfgdata16(pi, PCIR_COMMAND); | return passthru_cfgread_default(ctx, vcpu, pi, coff, bytes, rv); | ||||
return (0); | |||||
} | } | ||||
/* Everything else just read from the device's config space */ | static int | ||||
*rv = read_config(&sc->psc_sel, coff, bytes); | passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | ||||
int coff, int bytes, uint32_t *rv) | |||||
return (0); | { | ||||
switch (((struct passthru_softc *)pi->pi_arg)->psc_type) { | |||||
case PASSTHRU_IGD: | |||||
return passthru_cfgread_igd(ctx, vcpu, pi, coff, bytes, rv); | |||||
default: | |||||
return passthru_cfgread_default(ctx, vcpu, pi, coff, bytes, rv); | |||||
} | } | ||||
} | |||||
static int | static int | ||||
passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | passthru_cfgwrite_default(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | ||||
int coff, int bytes, uint32_t val) | int coff, int bytes, uint32_t val) | ||||
{ | { | ||||
int error, msix_table_entries, i; | int error, msix_table_entries, i; | ||||
struct passthru_softc *sc; | struct passthru_softc *sc; | ||||
uint16_t cmd_old; | uint16_t cmd_old; | ||||
sc = pi->pi_arg; | sc = pi->pi_arg; | ||||
/* | |||||
* PCI BARs are emulated | |||||
*/ | |||||
if (bar_access(coff)) | if (bar_access(coff)) | ||||
return (-1); | { | ||||
int idx, update_idx; | |||||
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 = read_config(&sc->psc_sel, PCIR_COMMAND, 2); | |||||
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_passthru(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, 0x10 + update_idx * 0x04) & ~0x0F; | |||||
if (pi->pi_bar[update_idx].type == PCIBAR_MEM64) | |||||
hi = pci_get_cfgdata32(pi, 0x10 + (update_idx + 1) * 0x04); | |||||
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_passthru(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); | |||||
} | |||||
/* | /* | ||||
* MSI capability is emulated | * MSI capability is emulated | ||||
*/ | */ | ||||
if (msicap_access(sc, coff)) { | if (msicap_access(sc, coff)) { | ||||
pci_emul_capwrite(pi, coff, bytes, val, sc->psc_msi.capoff, | pci_emul_capwrite(pi, coff, bytes, val, sc->psc_msi.capoff, | ||||
PCIY_MSI); | PCIY_MSI); | ||||
error = vm_setup_pptdev_msi(ctx, vcpu, sc->psc_sel.pc_bus, | error = vm_setup_pptdev_msi(ctx, vcpu, sc->psc_sel.pc_bus, | ||||
sc->psc_sel.pc_dev, sc->psc_sel.pc_func, | sc->psc_sel.pc_dev, sc->psc_sel.pc_func, | ||||
Show All 38 Lines | #endif | ||||
write_config(&sc->psc_sel, coff, bytes, val); | write_config(&sc->psc_sel, coff, bytes, val); | ||||
if (coff == PCIR_COMMAND) { | if (coff == PCIR_COMMAND) { | ||||
cmd_old = pci_get_cfgdata16(pi, PCIR_COMMAND); | cmd_old = pci_get_cfgdata16(pi, PCIR_COMMAND); | ||||
if (bytes == 1) | if (bytes == 1) | ||||
pci_set_cfgdata8(pi, PCIR_COMMAND, val); | pci_set_cfgdata8(pi, PCIR_COMMAND, val); | ||||
else if (bytes == 2) | else if (bytes == 2) | ||||
pci_set_cfgdata16(pi, PCIR_COMMAND, val); | pci_set_cfgdata16(pi, PCIR_COMMAND, val); | ||||
else | |||||
pci_set_cfgdata32(pi, PCIR_COMMAND, val); | |||||
pci_emul_cmd_changed(pi, cmd_old); | pci_emul_cmd_changed(pi, cmd_old); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | |||||
static int | |||||
passthru_cfgwrite_igd(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | |||||
int coff, int bytes, uint32_t val) | |||||
{ | |||||
/* | |||||
* GVT-d: 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 | |||||
return passthru_cfgwrite_default(ctx, vcpu, pi, coff, bytes, val); | |||||
} | |||||
static int | |||||
passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | |||||
int coff, int bytes, uint32_t val) | |||||
{ | |||||
switch (((struct passthru_softc *)pi->pi_arg)->psc_type) { | |||||
case PASSTHRU_IGD: | |||||
return passthru_cfgwrite_igd(ctx, vcpu, pi, coff, bytes, val); | |||||
default: | |||||
return passthru_cfgwrite_default(ctx, vcpu, pi, coff, bytes, val); | |||||
} | |||||
} | } | ||||
static void | static void | ||||
passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, | passthru_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, | ||||
uint64_t offset, int size, uint64_t value) | uint64_t offset, int size, uint64_t value) | ||||
{ | { | ||||
struct passthru_softc *sc; | struct passthru_softc *sc; | ||||
struct iodev_pio_req pio; | struct iodev_pio_req pio; | ||||
▲ Show 20 Lines • Show All 54 Lines • Show Last 20 Lines |