Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_passthru.c
Show All 31 Lines | |||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
#include <sys/capsicum.h> | #include <sys/capsicum.h> | ||||
#endif | #endif | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <sys/mman.h> | #include <sys/mman.h> | ||||
#include <sys/pciio.h> | |||||
#include <sys/ioctl.h> | #include <sys/ioctl.h> | ||||
#include <sys/stat.h> | |||||
#include <dev/io/iodev.h> | #include <dev/io/iodev.h> | ||||
#include <dev/pci/pcireg.h> | #include <dev/pci/pcireg.h> | ||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <machine/iodev.h> | #include <machine/iodev.h> | ||||
#include <machine/vm.h> | #include <machine/vm.h> | ||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
#include <capsicum_helpers.h> | #include <capsicum_helpers.h> | ||||
#endif | #endif | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <err.h> | #include <err.h> | ||||
#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 "config.h" | #include "config.h" | ||||
#include "debug.h" | #include "debug.h" | ||||
#include "pci_emul.h" | |||||
#include "mem.h" | #include "mem.h" | ||||
#include "pci_passthru.h" | |||||
#ifndef _PATH_DEVPCI | #ifndef _PATH_DEVPCI | ||||
#define _PATH_DEVPCI "/dev/pci" | #define _PATH_DEVPCI "/dev/pci" | ||||
#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 PCI_CAP_START_OFFSET 0x40 | |||||
static int pcifd = -1; | static int pcifd = -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 | 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 */ | ||||
if (msgctrl & PCIM_MSICTRL_64BIT) | if (msgctrl & PCIM_MSICTRL_64BIT) | ||||
len += 4; | len += 4; | ||||
#if 0 | #if 0 | ||||
/* | /* | ||||
* Ignore the 'mask' and 'pending' bits in the MSI capability. | * Ignore the 'mask' and 'pending' bits in the MSI capability. | ||||
* We'll let the guest manipulate them directly. | * We'll let the guest manipulate them directly. | ||||
*/ | */ | ||||
if (msgctrl & PCIM_MSICTRL_VECTOR) | if (msgctrl & PCIM_MSICTRL_VECTOR) | ||||
len += 10; | len += 10; | ||||
#endif | #endif | ||||
return (len); | return (len); | ||||
} | } | ||||
static uint32_t | uint32_t | ||||
read_config(const struct pcisel *sel, long reg, int width) | read_config(const struct pcisel *sel, long reg, int width) | ||||
{ | { | ||||
struct pci_io pi; | struct pci_io pi; | ||||
bzero(&pi, sizeof(pi)); | bzero(&pi, sizeof(pi)); | ||||
pi.pi_sel = *sel; | pi.pi_sel = *sel; | ||||
pi.pi_reg = reg; | pi.pi_reg = reg; | ||||
pi.pi_width = width; | pi.pi_width = width; | ||||
if (ioctl(pcifd, PCIOCREAD, &pi) < 0) | if (ioctl(pcifd, PCIOCREAD, &pi) < 0) | ||||
return (0); /* XXX */ | return (0); /* XXX */ | ||||
else | else | ||||
return (pi.pi_data); | return (pi.pi_data); | ||||
} | } | ||||
static void | void | ||||
write_config(const struct pcisel *sel, long reg, int width, uint32_t data) | write_config(const struct pcisel *sel, long reg, int width, uint32_t data) | ||||
{ | { | ||||
struct pci_io pi; | struct pci_io pi; | ||||
bzero(&pi, sizeof(pi)); | bzero(&pi, sizeof(pi)); | ||||
pi.pi_sel = *sel; | pi.pi_sel = *sel; | ||||
pi.pi_reg = reg; | pi.pi_reg = reg; | ||||
pi.pi_width = width; | pi.pi_width = width; | ||||
▲ Show 20 Lines • Show All 296 Lines • ▼ Show 20 Lines | init_msix_table(struct vmctx *ctx, struct passthru_softc *sc) | ||||
* 1. The PBA may reside in the first or last page containing the MSI-X | * 1. The PBA may reside in the first or last page containing the MSI-X | ||||
* table. | * table. | ||||
* 2. While PCI devices are not supposed to use the page(s) containing | * 2. While PCI devices are not supposed to use the page(s) containing | ||||
* the MSI-X table for other purposes, some do in practice. | * the MSI-X table for other purposes, some do in practice. | ||||
*/ | */ | ||||
memset(&pbm, 0, sizeof(pbm)); | memset(&pbm, 0, sizeof(pbm)); | ||||
pbm.pbm_sel = sc->psc_sel; | pbm.pbm_sel = sc->psc_sel; | ||||
pbm.pbm_flags = PCIIO_BAR_MMAP_RW; | pbm.pbm_flags = PCIIO_BAR_MMAP_RW; | ||||
pbm.pbm_reg = PCIR_BAR(pi->pi_msix.pba_bar); | pbm.pbm_reg = PCIR_BAR(pi->pi_msix.table_bar); | ||||
pbm.pbm_memattr = VM_MEMATTR_DEVICE; | pbm.pbm_memattr = VM_MEMATTR_DEVICE; | ||||
if (ioctl(pcifd, PCIOCBARMMAP, &pbm) != 0) { | if (ioctl(pcifd, PCIOCBARMMAP, &pbm) != 0) { | ||||
warn("Failed to map MSI-X table BAR on %d/%d/%d", b, s, f); | warn("Failed to map MSI-X table BAR on %d/%d/%d", b, s, f); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
assert(pbm.pbm_bar_off == 0); | assert(pbm.pbm_bar_off == 0); | ||||
pi->pi_msix.mapped_addr = (uint8_t *)(uintptr_t)pbm.pbm_map_base; | pi->pi_msix.mapped_addr = (uint8_t *)(uintptr_t)pbm.pbm_map_base; | ||||
pi->pi_msix.mapped_size = pbm.pbm_map_length; | pi->pi_msix.mapped_size = pbm.pbm_map_length; | ||||
table_offset = rounddown2(pi->pi_msix.table_offset, 4096); | table_offset = rounddown2(pi->pi_msix.table_offset, 4096); | ||||
table_size = pi->pi_msix.table_offset - table_offset; | table_size = pi->pi_msix.table_offset - table_offset; | ||||
table_size += pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE; | table_size += pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE; | ||||
table_size = roundup2(table_size, 4096); | table_size = roundup2(table_size, 4096); | ||||
/* | /* | ||||
* Unmap any pages not covered by the table, we do not need to emulate | * Unmap any pages not containing the table, we do not need to emulate | ||||
* accesses to them. Avoid releasing address space to help ensure that | * accesses to them. Avoid releasing address space to help ensure that | ||||
* a buggy out-of-bounds access causes a crash. | * a buggy out-of-bounds access causes a crash. | ||||
*/ | */ | ||||
if (table_offset != 0) | if (table_offset != 0) | ||||
if (mprotect(pi->pi_msix.mapped_addr, table_offset, | if (mprotect(pi->pi_msix.mapped_addr, table_offset, | ||||
PROT_NONE) != 0) | PROT_NONE) != 0) | ||||
warn("Failed to unmap MSI-X table BAR region"); | warn("Failed to unmap MSI-X table BAR region"); | ||||
if (table_offset + table_size != pi->pi_msix.mapped_size) | if (table_offset + table_size != pi->pi_msix.mapped_size) | ||||
if (mprotect(pi->pi_msix.mapped_addr, | if (mprotect( | ||||
pi->pi_msix.mapped_addr + table_offset + table_size, | |||||
pi->pi_msix.mapped_size - (table_offset + table_size), | pi->pi_msix.mapped_size - (table_offset + table_size), | ||||
PROT_NONE) != 0) | PROT_NONE) != 0) | ||||
warn("Failed to unmap MSI-X table BAR region"); | warn("Failed to unmap MSI-X table BAR region"); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | cfginit(struct vmctx *ctx, struct pci_devinst *pi, int bus, int slot, int func) | ||||
error = 1; | error = 1; | ||||
sc = pi->pi_arg; | sc = pi->pi_arg; | ||||
bzero(&sc->psc_sel, sizeof(struct pcisel)); | bzero(&sc->psc_sel, sizeof(struct pcisel)); | ||||
sc->psc_sel.pc_bus = bus; | sc->psc_sel.pc_bus = bus; | ||||
sc->psc_sel.pc_dev = slot; | sc->psc_sel.pc_dev = slot; | ||||
sc->psc_sel.pc_func = func; | sc->psc_sel.pc_func = func; | ||||
/* copy physical PCI header to virtual cfgspace */ | |||||
for (uint32_t i = 0; i < PCI_CAP_START_OFFSET; ++i) { | |||||
/* | |||||
* INTLINE and INTPIN shouldn't be aligned with it's physical | |||||
* value. They are already set by pci_emul_init. | |||||
*/ | |||||
if (i == PCIR_INTLINE || i == PCIR_INTPIN) | |||||
continue; | |||||
pci_set_cfgdata8(pi, i, read_config(&sc->psc_sel, i, 1)); | |||||
} | |||||
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; | ||||
} | } | ||||
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", | ||||
Show All 18 Lines | if (pci_msix_table_bar(pi) >= 0) { | ||||
} | } | ||||
} | } | ||||
error = 0; /* success */ | error = 0; /* success */ | ||||
done: | done: | ||||
return (error); | return (error); | ||||
} | } | ||||
int | |||||
set_pcir_handler(struct passthru_softc *const sc, const uint32_t reg, | |||||
const uint32_t len, const cfgread_handler rhandler, | |||||
const cfgwrite_handler whandler) | |||||
{ | |||||
if (reg > PCI_REGMAX || reg + len > PCI_REGMAX + 1) | |||||
return (-1); | |||||
for (uint32_t i = reg; i < reg + len; ++i) { | |||||
sc->psc_pcir_rhandler[i] = rhandler; | |||||
sc->psc_pcir_whandler[i] = whandler; | |||||
} | |||||
return 0; | |||||
} | |||||
static int | static int | ||||
passthru_legacy_config(nvlist_t *nvl, const char *opts) | passthru_legacy_config(nvlist_t *nvl, const char *opts) | ||||
{ | { | ||||
char value[16]; | char value[16]; | ||||
int bus, slot, func; | int bus, slot, func; | ||||
if (opts == NULL) | if (opts == NULL) | ||||
return (0); | return (0); | ||||
if (sscanf(opts, "%d/%d/%d", &bus, &slot, &func) != 3) { | if (sscanf(opts, "%d/%d/%d", &bus, &slot, &func) != 3) { | ||||
EPRINTLN("passthru: invalid options \"%s\"", opts); | EPRINTLN("passthru: invalid options \"%s\"", opts); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
snprintf(value, sizeof(value), "%d", bus); | snprintf(value, sizeof(value), "%d", bus); | ||||
set_config_value_node(nvl, "bus", value); | set_config_value_node(nvl, "bus", value); | ||||
snprintf(value, sizeof(value), "%d", slot); | snprintf(value, sizeof(value), "%d", slot); | ||||
set_config_value_node(nvl, "slot", value); | set_config_value_node(nvl, "slot", value); | ||||
snprintf(value, sizeof(value), "%d", func); | snprintf(value, sizeof(value), "%d", func); | ||||
set_config_value_node(nvl, "func", value); | set_config_value_node(nvl, "func", value); | ||||
return (pci_parse_legacy_config(nvl, strchr(opts, ','))); | |||||
} | |||||
static int | |||||
passthru_init_rom(struct vmctx *const ctx, struct passthru_softc *const sc, | |||||
const char *const romfile) | |||||
{ | |||||
if (romfile == NULL) { | |||||
return (0); | return (0); | ||||
} | } | ||||
const int fd = open(romfile, O_RDONLY); | |||||
if (fd < 0) { | |||||
warnx("%s: can't open romfile \"%s\"", __func__, romfile); | |||||
return (-1); | |||||
} | |||||
struct stat sbuf; | |||||
if (fstat(fd, &sbuf) < 0) { | |||||
warnx("%s: can't fstat romfile \"%s\"", __func__, romfile); | |||||
close(fd); | |||||
return (-1); | |||||
} | |||||
const uint64_t rom_size = sbuf.st_size; | |||||
void *const rom_data = mmap(NULL, rom_size, PROT_READ, MAP_SHARED, fd, | |||||
0); | |||||
if (rom_data == MAP_FAILED) { | |||||
warnx("%s: unable to mmap romfile \"%s\" (%d)", __func__, | |||||
romfile, errno); | |||||
close(fd); | |||||
return (-1); | |||||
} | |||||
void *rom_addr; | |||||
int error = pci_emul_alloc_rom(sc->psc_pi, rom_size, &rom_addr); | |||||
if (error) { | |||||
warnx("%s: failed to alloc rom segment", __func__); | |||||
munmap(rom_data, rom_size); | |||||
close(fd); | |||||
return (error); | |||||
} | |||||
memcpy(rom_addr, rom_data, rom_size); | |||||
sc->psc_bar[PCI_ROM_IDX].type = PCIBAR_ROM; | |||||
sc->psc_bar[PCI_ROM_IDX].addr = (uint64_t)rom_addr; | |||||
sc->psc_bar[PCI_ROM_IDX].size = rom_size; | |||||
munmap(rom_data, rom_size); | |||||
close(fd); | |||||
return (0); | |||||
} | |||||
static int | static int | ||||
passthru_init_quirks(struct vmctx *const ctx, struct pci_devinst *const pi, | |||||
nvlist_t *const nvl) | |||||
{ | |||||
struct passthru_softc *const sc = pi->pi_arg; | |||||
const uint16_t vendor = read_config(&sc->psc_sel, PCIR_VENDOR, 0x02); | |||||
const uint8_t class = read_config(&sc->psc_sel, PCIR_CLASS, 0x01); | |||||
/* currently only display devices have quirks */ | |||||
if (class != PCIC_DISPLAY) | |||||
return (0); | |||||
if (vendor == PCI_VENDOR_INTEL) | |||||
return gvt_d_init(ctx, pi, nvl); | |||||
return (0); | |||||
} | |||||
static void | |||||
passthru_deinit_quirks(struct vmctx *const ctx, struct pci_devinst *const pi) | |||||
{ | |||||
struct passthru_softc *const sc = pi->pi_arg; | |||||
if (sc == NULL) | |||||
return; | |||||
const uint16_t vendor = read_config(&sc->psc_sel, PCIR_VENDOR, 0x02); | |||||
const uint8_t class = read_config(&sc->psc_sel, PCIR_CLASS, 0x01); | |||||
/* currently only display devices have quirks */ | |||||
if (class != PCIC_DISPLAY) | |||||
return; | |||||
if (vendor == PCI_VENDOR_INTEL) | |||||
return gvt_d_deinit(ctx, pi); | |||||
return; | |||||
} | |||||
static int | |||||
passthru_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) | passthru_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) | ||||
{ | { | ||||
int bus, slot, func, error, memflags; | int bus, slot, func, error, memflags; | ||||
struct passthru_softc *sc; | struct passthru_softc *sc; | ||||
const char *value; | const char *value; | ||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
cap_rights_t rights; | cap_rights_t rights; | ||||
cap_ioctl_t pci_ioctls[] = | cap_ioctl_t pci_ioctls[] = | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | #define GET_INT_CONFIG(var, name) do { \ | ||||
} | } | ||||
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; | |||||
/* set default handler for all PCI registers */ | |||||
if ((error = set_pcir_handler(sc, 0, PCI_REGMAX + 1, | |||||
passthru_cfgread_default, passthru_cfgwrite_default)) != 0) | |||||
goto done; | |||||
/* protect PCI header */ | |||||
if ((error = set_pcir_handler(sc, 0, PCI_CAP_START_OFFSET, | |||||
passthru_cfgread_emulate, passthru_cfgwrite_emulate)) != 0) | |||||
goto done; | |||||
/* allow access to command and status register */ | |||||
if ((error = set_pcir_handler(sc, PCIR_COMMAND, 0x04, | |||||
passthru_cfgread_default, passthru_cfgwrite_default)) != 0) | |||||
goto done; | |||||
if ((error = passthru_init_quirks(ctx, pi, nvl)) != 0) | |||||
goto done; | |||||
/* initialize ROM */ | |||||
if ((error = passthru_init_rom(ctx, sc, | |||||
get_config_value_node(nvl, "rom"))) != 0) | |||||
goto done; | |||||
error = 0; /* success */ | |||||
done: | done: | ||||
if (error) { | if (error) { | ||||
passthru_deinit_quirks(ctx, pi); | |||||
free(sc); | free(sc); | ||||
vm_unassign_pptdev(ctx, bus, slot, func); | vm_unassign_pptdev(ctx, bus, slot, func); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | 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)) || | ||||
coff == PCIR_BIOS) | |||||
return (1); | return (1); | ||||
else | else | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
msicap_access(struct passthru_softc *sc, int coff) | msicap_access(struct passthru_softc *sc, int coff) | ||||
{ | { | ||||
Show All 19 Lines | msixcap_access(struct passthru_softc *sc, int coff) | ||||
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(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 *const sc = pi->pi_arg; | |||||
return sc->psc_pcir_rhandler[coff](ctx, vcpu, pi, coff, bytes, rv); | |||||
} | |||||
int | |||||
passthru_cfgread_default(struct vmctx *const ctx, const int vcpu, | |||||
struct pci_devinst *const pi, const int coff, const int bytes, | |||||
uint32_t *const 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) || msixcap_access(sc, coff)) | ||||
msixcap_access(sc, coff)) | |||||
return (-1); | return (-1); | ||||
#ifdef LEGACY_SUPPORT | |||||
/* | /* | ||||
* Emulate PCIR_CAP_PTR if this device does not support MSI capability | |||||
* natively. | |||||
*/ | |||||
if (sc->psc_msi.emulated) { | |||||
if (coff >= PCIR_CAP_PTR && coff < PCIR_CAP_PTR + 4) | |||||
return (-1); | |||||
} | |||||
#endif | |||||
/* | |||||
* Emulate the command register. If a single read reads both the | * Emulate the command register. If a single read reads both the | ||||
* command and status registers, read the status register from the | * command and status registers, read the status register from the | ||||
* device's config space. | * device's config space. | ||||
*/ | */ | ||||
if (coff == PCIR_COMMAND) { | if (coff == PCIR_COMMAND) { | ||||
if (bytes <= 2) | if (bytes <= 2) | ||||
return (-1); | return (-1); | ||||
*rv = read_config(&sc->psc_sel, PCIR_STATUS, 2) << 16 | | *rv = read_config(&sc->psc_sel, PCIR_STATUS, 2) << 16 | | ||||
pci_get_cfgdata16(pi, PCIR_COMMAND); | pci_get_cfgdata16(pi, PCIR_COMMAND); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* Everything else just read from the device's config space */ | /* Everything else just read from the device's config space */ | ||||
*rv = read_config(&sc->psc_sel, coff, bytes); | *rv = read_config(&sc->psc_sel, coff, bytes); | ||||
return (0); | return (0); | ||||
} | } | ||||
int | |||||
passthru_cfgread_emulate(struct vmctx *const ctx, const int vcpu, | |||||
struct pci_devinst *const pi, const int coff, const int bytes, | |||||
uint32_t *const rv) | |||||
{ | |||||
return (-1); | |||||
} | |||||
static int | static int | ||||
passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | ||||
int coff, int bytes, uint32_t val) | int coff, int bytes, uint32_t val) | ||||
{ | { | ||||
struct passthru_softc *const sc = pi->pi_arg; | |||||
return sc->psc_pcir_whandler[coff](ctx, vcpu, pi, coff, bytes, val); | |||||
} | |||||
int | |||||
passthru_cfgwrite_default(struct vmctx *const ctx, const int vcpu, | |||||
struct pci_devinst *const pi, const int coff, const int bytes, | |||||
const 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)) | |||||
return (-1); | |||||
/* | |||||
* 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, | ||||
pi->pi_msi.addr, pi->pi_msi.msg_data, | pi->pi_msi.addr, pi->pi_msi.msg_data, | ||||
Show All 23 Lines | if (pi->pi_msix.enabled) { | ||||
error = vm_disable_pptdev_msix(ctx, sc->psc_sel.pc_bus, | error = vm_disable_pptdev_msix(ctx, 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); | ||||
if (error) | if (error) | ||||
err(1, "vm_disable_pptdev_msix"); | err(1, "vm_disable_pptdev_msix"); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
uint32_t write_val = val; | |||||
#ifdef LEGACY_SUPPORT | #ifdef LEGACY_SUPPORT | ||||
/* | /* | ||||
* If this device does not support MSI natively then we cannot let | * If this device does not support MSI natively then we cannot let | ||||
* the guest disable legacy interrupts from the device. It is the | * the guest disable legacy interrupts from the device. It is the | ||||
* legacy interrupt that is triggering the virtual MSI to the guest. | * legacy interrupt that is triggering the virtual MSI to the guest. | ||||
*/ | */ | ||||
if (sc->psc_msi.emulated && pci_msi_enabled(pi)) { | if (sc->psc_msi.emulated && pci_msi_enabled(pi)) { | ||||
if (coff == PCIR_COMMAND && bytes == 2) | if (coff == PCIR_COMMAND && bytes == 2) | ||||
val &= ~PCIM_CMD_INTxDIS; | write_val &= ~PCIM_CMD_INTxDIS; | ||||
} | } | ||||
#endif | #endif | ||||
write_config(&sc->psc_sel, coff, bytes, val); | write_config(&sc->psc_sel, coff, bytes, write_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, write_val); | ||||
else if (bytes == 2) | else if (bytes == 2) | ||||
pci_set_cfgdata16(pi, PCIR_COMMAND, val); | pci_set_cfgdata16(pi, PCIR_COMMAND, write_val); | ||||
pci_emul_cmd_changed(pi, cmd_old); | pci_emul_cmd_changed(pi, cmd_old); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
int | |||||
passthru_cfgwrite_emulate(struct vmctx *const ctx, const int vcpu, | |||||
struct pci_devinst *const pi, const int coff, const int bytes, | |||||
const uint32_t val) | |||||
{ | |||||
return (-1); | |||||
} | |||||
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 pci_bar_ioreq pio; | struct pci_bar_ioreq pio; | ||||
sc = pi->pi_arg; | sc = pi->pi_arg; | ||||
▲ Show 20 Lines • Show All 118 Lines • ▼ Show 20 Lines | if (vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, | ||||
sc->psc_sel.pc_func, address, | sc->psc_sel.pc_func, address, | ||||
sc->psc_bar[baridx].size, | sc->psc_bar[baridx].size, | ||||
sc->psc_bar[baridx].addr) != 0) | sc->psc_bar[baridx].addr) != 0) | ||||
warnx("pci_passthru: map_pptdev_mmio failed"); | warnx("pci_passthru: map_pptdev_mmio failed"); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
passthru_addr_rom(struct pci_devinst *const pi, const int idx, | |||||
const int enabled) | |||||
{ | |||||
const uint64_t addr = pi->pi_bar[idx].addr; | |||||
const uint64_t size = pi->pi_bar[idx].size; | |||||
if (!enabled) { | |||||
if (vm_munmap_memseg(pi->pi_vmctx, addr, size) != 0) { | |||||
warnx("%s: munmap_memseg @ [%016lx - %016lx] failed", | |||||
__func__, addr, addr + size); | |||||
} | |||||
} else { | |||||
if (vm_mmap_memseg(pi->pi_vmctx, addr, VM_PCIROM, | |||||
pi->pi_romoffset, size, PROT_READ | PROT_EXEC) != 0) { | |||||
warnx("%s: mnmap_memseg @ [%016lx - %016lx] failed", | |||||
__func__, addr, addr + size); | |||||
} | |||||
} | |||||
} | |||||
static void | |||||
passthru_addr(struct vmctx *ctx, struct pci_devinst *pi, int baridx, | passthru_addr(struct vmctx *ctx, struct pci_devinst *pi, int baridx, | ||||
int enabled, uint64_t address) | int enabled, uint64_t address) | ||||
{ | { | ||||
switch (pi->pi_bar[baridx].type) { | |||||
if (pi->pi_bar[baridx].type == PCIBAR_IO) | case PCIBAR_IO: | ||||
return; | /* IO BARs are emulated */ | ||||
break; | |||||
case PCIBAR_ROM: | |||||
passthru_addr_rom(pi, baridx, enabled); | |||||
break; | |||||
case PCIBAR_MEM32: | |||||
case PCIBAR_MEM64: | |||||
if (baridx == pci_msix_table_bar(pi)) | if (baridx == pci_msix_table_bar(pi)) | ||||
passthru_msix_addr(ctx, pi, baridx, enabled, address); | passthru_msix_addr(ctx, pi, baridx, enabled, address); | ||||
else | else | ||||
passthru_mmio_addr(ctx, pi, baridx, enabled, address); | passthru_mmio_addr(ctx, pi, baridx, enabled, address); | ||||
break; | |||||
default: | |||||
errx(4, "%s: invalid BAR type %d", __func__, | |||||
pi->pi_bar[baridx].type); | |||||
} | |||||
} | } | ||||
struct pci_devemu passthru = { | struct pci_devemu passthru = { | ||||
.pe_emu = "passthru", | .pe_emu = "passthru", | ||||
.pe_init = passthru_init, | .pe_init = passthru_init, | ||||
.pe_legacy_config = passthru_legacy_config, | .pe_legacy_config = passthru_legacy_config, | ||||
.pe_cfgwrite = passthru_cfgwrite, | .pe_cfgwrite = passthru_cfgwrite, | ||||
.pe_cfgread = passthru_cfgread, | .pe_cfgread = passthru_cfgread, | ||||
.pe_barwrite = passthru_write, | .pe_barwrite = passthru_write, | ||||
.pe_barread = passthru_read, | .pe_barread = passthru_read, | ||||
.pe_baraddr = passthru_addr, | .pe_baraddr = passthru_addr, | ||||
}; | }; | ||||
PCI_EMUL_SET(passthru); | PCI_EMUL_SET(passthru); |