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 "config.h" | |||||
#include "debug.h" | #include "debug.h" | ||||
#include "pci_emul.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 | ||||
#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 GET_INT_CONFIG(var, name) \ | |||||
do { \ | |||||
const char *value = get_config_value_node(nvl, name); \ | |||||
if (value == NULL) { \ | |||||
EPRINTLN( \ | |||||
"passthru: missing required %s setting", name); \ | |||||
return (1); \ | |||||
} \ | |||||
var = atoi(value); \ | |||||
} while (0) | |||||
static int pcifd = -1; | static int pcifd = -1; | ||||
static int iofd = -1; | static int iofd = -1; | ||||
static int memfd = -1; | static int memfd = -1; | ||||
struct passthru_softc { | |||||
struct pci_devinst *psc_pi; | |||||
struct pcibar psc_bar[PCI_BARMAX + 1]; | |||||
struct { | |||||
int capoff; | |||||
int msgctrl; | |||||
int emulated; | |||||
} psc_msi; | |||||
struct { | |||||
int capoff; | |||||
} psc_msix; | |||||
struct pcisel psc_sel; | |||||
}; | |||||
static int | 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; | ||||
pi.pi_data = data; | pi.pi_data = data; | ||||
(void)ioctl(pcifd, PCIOCWRITE, &pi); /* XXX */ | (void)ioctl(pcifd, PCIOCWRITE, &pi); /* XXX */ | ||||
} | } | ||||
int | |||||
passthru_modify_pptdev_mmio(struct vmctx *ctx, struct passthru_softc *sc, | |||||
struct passthru_mmio_mapping *map, int registration) | |||||
{ | |||||
if (registration == PT_MAP_PPTDEV_MMIO) | |||||
return vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, | |||||
sc->psc_sel.pc_dev, sc->psc_sel.pc_func, map->gpa, map->len, | |||||
map->hpa); | |||||
else | |||||
return vm_unmap_pptdev_mmio(ctx, sc->psc_sel.pc_bus, | |||||
sc->psc_sel.pc_dev, sc->psc_sel.pc_func, map->gpa, | |||||
map->len); | |||||
} | |||||
static int | |||||
passthru_early_quirks(struct vmctx *ctx, const nvlist_t *nvl) | |||||
{ | |||||
int bus, slot, func; | |||||
GET_INT_CONFIG(bus, "bus"); | |||||
GET_INT_CONFIG(slot, "slot"); | |||||
GET_INT_CONFIG(func, "func"); | |||||
if (pcifd < 0) { | |||||
pcifd = open(_PATH_DEVPCI, O_RDWR, 0); | |||||
if (pcifd < 0) { | |||||
warn("failed to open %s", _PATH_DEVPCI); | |||||
return (-1); | |||||
} | |||||
} | |||||
struct pcisel sel = { .pc_bus = bus, .pc_dev = slot, .pc_func = func }; | |||||
uint16_t vendor = read_config(&sel, PCIR_VENDOR, 0x02); | |||||
uint8_t class = read_config(&sel, PCIR_CLASS, 0x01); | |||||
/* currently only display devices have quirks */ | |||||
if (class != PCIC_DISPLAY) | |||||
return (0); | |||||
if (vendor == PCI_VENDOR_INTEL) | |||||
return gvt_d_early_quirks(ctx, &sel); | |||||
return (0); | |||||
} | |||||
#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 391 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_bar(pi, i, bartype, size); | error = pci_emul_alloc_bar(pi, i, bartype, size); | ||||
if (error) | if (error) | ||||
return (-1); | return (-1); | ||||
/* Use same lobits as physical bar */ | |||||
uint8_t lobits = read_config(&sc->psc_sel, PCIR_BAR(i), 0x01); | |||||
if (bartype == PCIBAR_MEM32 || bartype == PCIBAR_MEM64) { | |||||
lobits &= ~PCIM_BAR_MEM_BASE; | |||||
} else { | |||||
lobits &= ~PCIM_BAR_IO_BASE; | |||||
} | |||||
sc->psc_bar[i].lobits = lobits; | |||||
pi->pi_bar[i].lobits = lobits; | |||||
/* 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); | ||||
} | } | ||||
/* | /* | ||||
Show All 29 Lines | cfginit(struct vmctx *ctx, struct pci_devinst *pi, int bus, int slot, int func) | ||||
} | } | ||||
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, | write_config( | ||||
PCIR_COMMAND, 2)); | &sc->psc_sel, PCIR_COMMAND, 2, pci_get_cfgdata16(pi, PCIR_COMMAND)); | ||||
error = 0; /* success */ | error = 0; /* success */ | ||||
done: | done: | ||||
return (error); | return (error); | ||||
} | } | ||||
#define PPT_PCIR_PROT(reg) \ | |||||
((sc->psc_pcir_prot_map[reg / 4] >> (reg & 0x03)) & PPT_PCIR_PROT_MASK) | |||||
int | |||||
set_pcir_prot( | |||||
struct passthru_softc *sc, uint32_t reg, uint32_t len, uint8_t prot) | |||||
{ | |||||
if (reg > PCI_REGMAX || reg + len > PCI_REGMAX + 1) | |||||
return (-1); | |||||
prot &= PPT_PCIR_PROT_MASK; | |||||
for (int i = reg; i < reg + len; ++i) { | |||||
/* delete old prot value */ | |||||
sc->psc_pcir_prot_map[i / 4] &= ~( | |||||
PPT_PCIR_PROT_MASK << (i & 0x03)); | |||||
/* set new prot value */ | |||||
sc->psc_pcir_prot_map[i / 4] |= prot << (i & 0x03); | |||||
} | |||||
return (0); | |||||
} | |||||
static int | static int | ||||
is_pcir_writable(struct passthru_softc *sc, uint32_t reg) | |||||
{ | |||||
if (reg > PCI_REGMAX) | |||||
return (0); | |||||
return ((PPT_PCIR_PROT(reg) & PPT_PCIR_PROT_WO) != 0); | |||||
} | |||||
static int | |||||
is_pcir_readable(struct passthru_softc *sc, uint32_t reg) | |||||
{ | |||||
if (reg > PCI_REGMAX) | |||||
return (0); | |||||
return ((PPT_PCIR_PROT(reg) & PPT_PCIR_PROT_RO) != 0); | |||||
} | |||||
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 (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
passthru_init_quirks(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) | |||||
{ | |||||
struct passthru_softc *sc = pi->pi_arg; | |||||
uint16_t vendor = read_config(&sc->psc_sel, PCIR_VENDOR, 0x02); | |||||
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 *ctx, struct pci_devinst *pi) | |||||
{ | |||||
struct passthru_softc *sc = pi->pi_arg; | |||||
if (sc == NULL) | |||||
return; | |||||
uint16_t vendor = read_config(&sc->psc_sel, PCIR_VENDOR, 0x02); | |||||
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; | |||||
#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 | ||||
sc = NULL; | sc = NULL; | ||||
error = 1; | error = 1; | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
cap_rights_clear(&rights, CAP_IOCTL); | cap_rights_clear(&rights, CAP_IOCTL); | ||||
cap_rights_set(&rights, CAP_MMAP_RW); | cap_rights_set(&rights, CAP_MMAP_RW); | ||||
if (caph_rights_limit(memfd, &rights) == -1) | 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 | ||||
#define GET_INT_CONFIG(var, name) do { \ | |||||
value = get_config_value_node(nvl, name); \ | |||||
if (value == NULL) { \ | |||||
EPRINTLN("passthru: missing required %s setting", name); \ | |||||
return (error); \ | |||||
} \ | |||||
var = atoi(value); \ | |||||
} while (0) | |||||
GET_INT_CONFIG(bus, "bus"); | GET_INT_CONFIG(bus, "bus"); | ||||
GET_INT_CONFIG(slot, "slot"); | GET_INT_CONFIG(slot, "slot"); | ||||
GET_INT_CONFIG(func, "func"); | GET_INT_CONFIG(func, "func"); | ||||
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; | |||||
/* allow access to all PCI registers */ | |||||
if ((error = set_pcir_prot(sc, 0, PCI_REGMAX + 1, PPT_PCIR_PROT_RW)) != | |||||
0) | |||||
goto done; | |||||
if ((error = passthru_init_quirks(ctx, pi, nvl)) != 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) | ||||
Show All 33 Lines | |||||
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 *sc; | struct passthru_softc *sc; | ||||
sc = pi->pi_arg; | sc = pi->pi_arg; | ||||
/* skip for protected PCI registers */ | |||||
if (!is_pcir_readable(sc, coff)) | |||||
return (-1); | |||||
/* | /* | ||||
* PCI BARs and MSI capability is emulated. | * PCI BARs and MSI capability is emulated. | ||||
*/ | */ | ||||
if (bar_access(coff) || msicap_access(sc, coff)) | if (bar_access(coff) || msicap_access(sc, coff)) | ||||
return (-1); | return (-1); | ||||
#ifdef LEGACY_SUPPORT | #ifdef LEGACY_SUPPORT | ||||
/* | /* | ||||
Show All 30 Lines | 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) | ||||
{ | { | ||||
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; | ||||
/* skip for protected PCI registers */ | |||||
if (!is_pcir_writable(sc, coff)) | |||||
return (-1); | |||||
/* | /* | ||||
* PCI BARs are emulated | * PCI BARs are emulated | ||||
*/ | */ | ||||
if (bar_access(coff)) | if (bar_access(coff)) | ||||
return (-1); | return (-1); | ||||
/* | /* | ||||
* MSI capability is emulated | * MSI capability is emulated | ||||
▲ Show 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | if (baridx == pci_msix_table_bar(pi)) { | ||||
val = pio.val; | val = pio.val; | ||||
} | } | ||||
return (val); | return (val); | ||||
} | } | ||||
static void | static void | ||||
passthru_msix_addr(struct vmctx *ctx, struct pci_devinst *pi, int baridx, | passthru_msix_addr(struct vmctx *ctx, struct pci_devinst *pi, int baridx, | ||||
int enabled, uint64_t address) | int enabled, uint64_t address) | ||||
{ | { | ||||
struct passthru_softc *sc; | struct passthru_softc *sc; | ||||
size_t remaining; | size_t remaining; | ||||
uint32_t table_size, table_offset; | uint32_t table_size, table_offset; | ||||
sc = pi->pi_arg; | sc = pi->pi_arg; | ||||
table_offset = rounddown2(pi->pi_msix.table_offset, 4096); | table_offset = rounddown2(pi->pi_msix.table_offset, 4096); | ||||
struct passthru_mmio_mapping map; | |||||
if (table_offset > 0) { | if (table_offset > 0) { | ||||
if (!enabled) { | map.gpa = address; | ||||
if (vm_unmap_pptdev_mmio(ctx, sc->psc_sel.pc_bus, | map.len = table_offset; | ||||
sc->psc_sel.pc_dev, | map.hpa = sc->psc_bar[baridx].addr; | ||||
sc->psc_sel.pc_func, address, | if (passthru_modify_pptdev_mmio(ctx, sc, &map, enabled) != 0) | ||||
table_offset) != 0) | warnx("pci_passthru: modify_pptdev_mmio failed"); | ||||
warnx("pci_passthru: unmap_pptdev_mmio failed"); | |||||
} else { | |||||
if (vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, | |||||
sc->psc_sel.pc_dev, | |||||
sc->psc_sel.pc_func, address, | |||||
table_offset, | |||||
sc->psc_bar[baridx].addr) != 0) | |||||
warnx("pci_passthru: map_pptdev_mmio failed"); | |||||
} | } | ||||
} | |||||
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); | ||||
remaining = pi->pi_bar[baridx].size - table_offset - table_size; | remaining = pi->pi_bar[baridx].size - table_offset - table_size; | ||||
if (remaining > 0) { | if (remaining > 0) { | ||||
address += table_offset + table_size; | address += table_offset + table_size; | ||||
if (!enabled) { | map.gpa = address; | ||||
if (vm_unmap_pptdev_mmio(ctx, sc->psc_sel.pc_bus, | map.len = remaining; | ||||
sc->psc_sel.pc_dev, | map.hpa = sc->psc_bar[baridx].addr + table_offset + table_size; | ||||
sc->psc_sel.pc_func, address, | if (passthru_modify_pptdev_mmio(ctx, sc, &map, enabled) != 0) | ||||
remaining) != 0) | warnx("pci_passthru: modify_pptdev_mmio failed"); | ||||
warnx("pci_passthru: unmap_pptdev_mmio failed"); | |||||
} else { | |||||
if (vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, | |||||
sc->psc_sel.pc_dev, | |||||
sc->psc_sel.pc_func, address, | |||||
remaining, | |||||
sc->psc_bar[baridx].addr + | |||||
table_offset + table_size) != 0) | |||||
warnx("pci_passthru: map_pptdev_mmio failed"); | |||||
} | } | ||||
} | } | ||||
} | |||||
static void | static void | ||||
passthru_mmio_addr(struct vmctx *ctx, struct pci_devinst *pi, int baridx, | passthru_mmio_addr(struct vmctx *ctx, struct pci_devinst *pi, int baridx, | ||||
int enabled, uint64_t address) | int enabled, uint64_t address) | ||||
{ | { | ||||
struct passthru_softc *sc; | struct passthru_softc *sc; | ||||
sc = pi->pi_arg; | sc = pi->pi_arg; | ||||
if (!enabled) { | |||||
if (vm_unmap_pptdev_mmio(ctx, sc->psc_sel.pc_bus, | struct passthru_mmio_mapping map; | ||||
sc->psc_sel.pc_dev, | map.gpa = address; | ||||
sc->psc_sel.pc_func, address, | map.len = sc->psc_bar[baridx].size; | ||||
sc->psc_bar[baridx].size) != 0) | map.hpa = sc->psc_bar[baridx].addr; | ||||
warnx("pci_passthru: unmap_pptdev_mmio failed"); | |||||
} else { | if (passthru_modify_pptdev_mmio(ctx, sc, &map, enabled) != 0) | ||||
if (vm_map_pptdev_mmio(ctx, sc->psc_sel.pc_bus, | warnx("pci_passthru: modify_pptdev_mmio failed"); | ||||
sc->psc_sel.pc_dev, | |||||
sc->psc_sel.pc_func, address, | |||||
sc->psc_bar[baridx].size, | |||||
sc->psc_bar[baridx].addr) != 0) | |||||
warnx("pci_passthru: map_pptdev_mmio failed"); | |||||
} | } | ||||
} | |||||
static void | static int | ||||
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) | ||||
{ | { | ||||
if (pi->pi_bar[baridx].type == PCIBAR_IO) | if (pi->pi_bar[baridx].type == PCIBAR_IO) | ||||
return; | return (-1); | ||||
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); | ||||
return (0); | |||||
} | } | ||||
struct pci_devemu passthru = { | struct pci_devemu passthru = { | ||||
.pe_emu = "passthru", | .pe_emu = "passthru", | ||||
.pe_early_quirks = passthru_early_quirks, | |||||
.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); |