Add support for emulating PCI multi-function devices.
These function number is specified by an optional [:<func>] after the slot number: -s 1:0,virtio-net,tap0 Ditto for the mptable naming: -n 1:0,e0a Obtained from: NetApp
This commit is contained in:
parent
b0b53d3af9
commit
99d653892b
@ -45,7 +45,6 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include "fbsdrun.h"
|
#include "fbsdrun.h"
|
||||||
#include "inout.h"
|
#include "inout.h"
|
||||||
#include "pci_emul.h"
|
#include "pci_emul.h"
|
||||||
#include "instruction_emul.h"
|
|
||||||
#include "ioapic.h"
|
#include "ioapic.h"
|
||||||
|
|
||||||
#define CONF1_ADDR_PORT 0x0cf8
|
#define CONF1_ADDR_PORT 0x0cf8
|
||||||
@ -62,7 +61,8 @@ do { \
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define MAXSLOTS 32
|
#define MAXSLOTS (PCI_SLOTMAX + 1)
|
||||||
|
#define MAXFUNCS (PCI_FUNCMAX + 1)
|
||||||
|
|
||||||
static struct slotinfo {
|
static struct slotinfo {
|
||||||
char *si_name;
|
char *si_name;
|
||||||
@ -73,7 +73,7 @@ static struct slotinfo {
|
|||||||
char si_prefix;
|
char si_prefix;
|
||||||
char si_suffix;
|
char si_suffix;
|
||||||
int si_legacy;
|
int si_legacy;
|
||||||
} pci_slotinfo[MAXSLOTS];
|
} pci_slotinfo[MAXSLOTS][MAXFUNCS];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used to keep track of legacy interrupt owners/requestors
|
* Used to keep track of legacy interrupt owners/requestors
|
||||||
@ -114,7 +114,7 @@ static struct mptable_pci_devnames {
|
|||||||
uint8_t mds_suffix[4];
|
uint8_t mds_suffix[4];
|
||||||
uint8_t mds_prefix[4];
|
uint8_t mds_prefix[4];
|
||||||
uint32_t mds_rsvd[3];
|
uint32_t mds_rsvd[3];
|
||||||
} md_slotinfo[MAXSLOTS];
|
} md_slotinfo[MAXSLOTS * MAXFUNCS];
|
||||||
} pci_devnames;
|
} pci_devnames;
|
||||||
|
|
||||||
SET_DECLARE(pci_devemu_set, struct pci_devemu);
|
SET_DECLARE(pci_devemu_set, struct pci_devemu);
|
||||||
@ -142,15 +142,16 @@ static int devname_elems;
|
|||||||
/*
|
/*
|
||||||
* Slot options are in the form:
|
* Slot options are in the form:
|
||||||
*
|
*
|
||||||
* <slot>,<emul>[,<config>]
|
* <slot>[:<func>],<emul>[,<config>]
|
||||||
*
|
*
|
||||||
* slot is 0..31
|
* slot is 0..31
|
||||||
|
* func is 0..7
|
||||||
* emul is a string describing the type of PCI device e.g. virtio-net
|
* emul is a string describing the type of PCI device e.g. virtio-net
|
||||||
* config is an optional string, depending on the device, that can be
|
* config is an optional string, depending on the device, that can be
|
||||||
* used for configuration.
|
* used for configuration.
|
||||||
* Examples are:
|
* Examples are:
|
||||||
* 1,virtio-net,tap0
|
* 1,virtio-net,tap0
|
||||||
* 3,dummy
|
* 3:0,dummy
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
pci_parse_slot_usage(char *aopt)
|
pci_parse_slot_usage(char *aopt)
|
||||||
@ -162,14 +163,22 @@ pci_parse_slot_usage(char *aopt)
|
|||||||
void
|
void
|
||||||
pci_parse_slot(char *opt, int legacy)
|
pci_parse_slot(char *opt, int legacy)
|
||||||
{
|
{
|
||||||
char *slot, *emul, *config;
|
char *slot, *func, *emul, *config;
|
||||||
char *str, *cpy;
|
char *str, *cpy;
|
||||||
int snum;
|
int snum, fnum;
|
||||||
|
|
||||||
str = cpy = strdup(opt);
|
str = cpy = strdup(opt);
|
||||||
|
|
||||||
config = NULL;
|
config = NULL;
|
||||||
|
|
||||||
|
if (strchr(str, ':') != NULL) {
|
||||||
|
slot = strsep(&str, ":");
|
||||||
|
func = strsep(&str, ",");
|
||||||
|
} else {
|
||||||
slot = strsep(&str, ",");
|
slot = strsep(&str, ",");
|
||||||
|
func = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
emul = strsep(&str, ",");
|
emul = strsep(&str, ",");
|
||||||
if (str != NULL) {
|
if (str != NULL) {
|
||||||
config = strsep(&str, ",");
|
config = strsep(&str, ",");
|
||||||
@ -180,34 +189,33 @@ pci_parse_slot(char *opt, int legacy)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
snum = 255;
|
|
||||||
snum = atoi(slot);
|
snum = atoi(slot);
|
||||||
if (snum < 0 || snum >= MAXSLOTS) {
|
fnum = func ? atoi(func) : 0;
|
||||||
|
if (snum < 0 || snum >= MAXSLOTS || fnum < 0 || fnum >= MAXFUNCS) {
|
||||||
pci_parse_slot_usage(cpy);
|
pci_parse_slot_usage(cpy);
|
||||||
} else {
|
} else {
|
||||||
pci_slotinfo[snum].si_name = emul;
|
pci_slotinfo[snum][fnum].si_name = emul;
|
||||||
pci_slotinfo[snum].si_param = config;
|
pci_slotinfo[snum][fnum].si_param = config;
|
||||||
pci_slotinfo[snum].si_legacy = legacy;
|
pci_slotinfo[snum][fnum].si_legacy = legacy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* PCI MPTable names are of the form:
|
* PCI MPTable names are of the form:
|
||||||
*
|
*
|
||||||
* <slot>,[prefix]<digit><suffix>
|
* <slot>[:<func>],[prefix]<digit><suffix>
|
||||||
*
|
*
|
||||||
* .. with <prefix> an alphabetic char, <digit> a 1 or 2-digit string,
|
* .. with <prefix> an alphabetic char, <digit> a 1 or 2-digit string,
|
||||||
* and <suffix> a single char.
|
* and <suffix> a single char.
|
||||||
*
|
*
|
||||||
* Examples:
|
* Examples:
|
||||||
* 1,e0c
|
* 1,e0c
|
||||||
* 4,e0P
|
* 4:0,e0P
|
||||||
|
* 4:1,e0M
|
||||||
* 6,43a
|
* 6,43a
|
||||||
* 7,0f
|
* 7,0f
|
||||||
* 10,1
|
* 10,1
|
||||||
* 12,e0M
|
|
||||||
* 2,12a
|
* 2,12a
|
||||||
*
|
*
|
||||||
* Note that this is NetApp-specific, but is ignored on other o/s's.
|
* Note that this is NetApp-specific, but is ignored on other o/s's.
|
||||||
@ -223,15 +231,23 @@ pci_parse_name(char *opt)
|
|||||||
{
|
{
|
||||||
char csnum[4];
|
char csnum[4];
|
||||||
char *namestr;
|
char *namestr;
|
||||||
char *slotend;
|
char *slotend, *funcend, *funcstart;
|
||||||
char prefix, suffix;
|
char prefix, suffix;
|
||||||
int i;
|
int i;
|
||||||
int pslot;
|
int pslot;
|
||||||
int snum;
|
int snum, fnum;
|
||||||
|
|
||||||
pslot = -1;
|
pslot = -1;
|
||||||
prefix = suffix = 0;
|
prefix = suffix = 0;
|
||||||
|
|
||||||
|
slotend = strchr(opt, ':');
|
||||||
|
if (slotend != NULL) {
|
||||||
|
funcstart = slotend + 1;
|
||||||
|
funcend = strchr(funcstart, ',');
|
||||||
|
} else {
|
||||||
slotend = strchr(opt, ',');
|
slotend = strchr(opt, ',');
|
||||||
|
funcstart = funcend = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A comma must be present, and can't be the first character
|
* A comma must be present, and can't be the first character
|
||||||
@ -248,14 +264,31 @@ pci_parse_name(char *opt)
|
|||||||
}
|
}
|
||||||
csnum[i] = '\0';
|
csnum[i] = '\0';
|
||||||
|
|
||||||
snum = 255;
|
|
||||||
snum = atoi(csnum);
|
snum = atoi(csnum);
|
||||||
if (snum < 0 || snum >= MAXSLOTS) {
|
if (snum < 0 || snum >= MAXSLOTS) {
|
||||||
pci_parse_name_usage(opt);
|
pci_parse_name_usage(opt);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
namestr = slotend + 1;
|
/*
|
||||||
|
* Parse the function number (if provided)
|
||||||
|
*
|
||||||
|
* A comma must be present and can't be the first character.
|
||||||
|
* The function cannot be greater than a single character and
|
||||||
|
* must be between '0' and '7' inclusive.
|
||||||
|
*/
|
||||||
|
if (funcstart != NULL) {
|
||||||
|
if (funcend == NULL || funcend != funcstart + 1 ||
|
||||||
|
*funcstart < '0' || *funcstart > '7') {
|
||||||
|
pci_parse_name_usage(opt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fnum = *funcstart - '0';
|
||||||
|
} else {
|
||||||
|
fnum = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
namestr = funcend ? funcend + 1 : slotend + 1;
|
||||||
|
|
||||||
if (strlen(namestr) > 3) {
|
if (strlen(namestr) > 3) {
|
||||||
pci_parse_name_usage(opt);
|
pci_parse_name_usage(opt);
|
||||||
@ -276,11 +309,10 @@ pci_parse_name(char *opt)
|
|||||||
}
|
}
|
||||||
if (isalpha(*namestr) && *(namestr + 1) == 0) {
|
if (isalpha(*namestr) && *(namestr + 1) == 0) {
|
||||||
suffix = *namestr;
|
suffix = *namestr;
|
||||||
pci_slotinfo[snum].si_titled = 1;
|
pci_slotinfo[snum][fnum].si_titled = 1;
|
||||||
pci_slotinfo[snum].si_pslot = pslot;
|
pci_slotinfo[snum][fnum].si_pslot = pslot;
|
||||||
pci_slotinfo[snum].si_prefix = prefix;
|
pci_slotinfo[snum][fnum].si_prefix = prefix;
|
||||||
pci_slotinfo[snum].si_suffix = suffix;
|
pci_slotinfo[snum][fnum].si_suffix = suffix;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
pci_parse_name_usage(opt);
|
pci_parse_name_usage(opt);
|
||||||
}
|
}
|
||||||
@ -391,7 +423,8 @@ pci_emul_alloc_bar(struct pci_devinst *pdi, int idx, uint64_t hostbase,
|
|||||||
addr = mask = lobits = 0;
|
addr = mask = lobits = 0;
|
||||||
break;
|
break;
|
||||||
case PCIBAR_IO:
|
case PCIBAR_IO:
|
||||||
if (hostbase && pci_slotinfo[pdi->pi_slot].si_legacy) {
|
if (hostbase &&
|
||||||
|
pci_slotinfo[pdi->pi_slot][pdi->pi_func].si_legacy) {
|
||||||
assert(hostbase < PCI_EMUL_IOBASE);
|
assert(hostbase < PCI_EMUL_IOBASE);
|
||||||
baseptr = &hostbase;
|
baseptr = &hostbase;
|
||||||
} else {
|
} else {
|
||||||
@ -536,7 +569,8 @@ pci_emul_finddev(char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, char *params)
|
pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func,
|
||||||
|
char *params)
|
||||||
{
|
{
|
||||||
struct pci_devinst *pdi;
|
struct pci_devinst *pdi;
|
||||||
pdi = malloc(sizeof(struct pci_devinst));
|
pdi = malloc(sizeof(struct pci_devinst));
|
||||||
@ -545,7 +579,7 @@ pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, char *params)
|
|||||||
pdi->pi_vmctx = ctx;
|
pdi->pi_vmctx = ctx;
|
||||||
pdi->pi_bus = 0;
|
pdi->pi_bus = 0;
|
||||||
pdi->pi_slot = slot;
|
pdi->pi_slot = slot;
|
||||||
pdi->pi_func = 0;
|
pdi->pi_func = func;
|
||||||
pdi->pi_d = pde;
|
pdi->pi_d = pde;
|
||||||
snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot);
|
snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot);
|
||||||
|
|
||||||
@ -560,7 +594,7 @@ pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, char *params)
|
|||||||
free(pdi);
|
free(pdi);
|
||||||
} else {
|
} else {
|
||||||
pci_emul_devices++;
|
pci_emul_devices++;
|
||||||
pci_slotinfo[slot].si_devi = pdi;
|
pci_slotinfo[slot][func].si_devi = pdi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -730,23 +764,25 @@ init_pci(struct vmctx *ctx)
|
|||||||
{
|
{
|
||||||
struct pci_devemu *pde;
|
struct pci_devemu *pde;
|
||||||
struct slotinfo *si;
|
struct slotinfo *si;
|
||||||
int i;
|
int slot, func;
|
||||||
|
|
||||||
pci_emul_iobase = PCI_EMUL_IOBASE;
|
pci_emul_iobase = PCI_EMUL_IOBASE;
|
||||||
pci_emul_membase32 = PCI_EMUL_MEMBASE32;
|
pci_emul_membase32 = PCI_EMUL_MEMBASE32;
|
||||||
pci_emul_membase64 = PCI_EMUL_MEMBASE64;
|
pci_emul_membase64 = PCI_EMUL_MEMBASE64;
|
||||||
|
|
||||||
si = pci_slotinfo;
|
for (slot = 0; slot < MAXSLOTS; slot++) {
|
||||||
|
for (func = 0; func < MAXFUNCS; func++) {
|
||||||
for (i = 0; i < MAXSLOTS; i++, si++) {
|
si = &pci_slotinfo[slot][func];
|
||||||
if (si->si_name != NULL) {
|
if (si->si_name != NULL) {
|
||||||
pde = pci_emul_finddev(si->si_name);
|
pde = pci_emul_finddev(si->si_name);
|
||||||
if (pde != NULL) {
|
if (pde != NULL) {
|
||||||
pci_emul_init(ctx, pde, i, si->si_param);
|
pci_emul_init(ctx, pde, slot, func,
|
||||||
|
si->si_param);
|
||||||
pci_add_mptable_name(si);
|
pci_add_mptable_name(si);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
pci_finish_mptable_names();
|
pci_finish_mptable_names();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -790,7 +826,7 @@ int
|
|||||||
pci_is_legacy(struct pci_devinst *pi)
|
pci_is_legacy(struct pci_devinst *pi)
|
||||||
{
|
{
|
||||||
|
|
||||||
return (pci_slotinfo[pi->pi_slot].si_legacy);
|
return (pci_slotinfo[pi->pi_slot][pi->pi_func].si_legacy);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -847,7 +883,52 @@ pci_lintr_deassert(struct pci_devinst *pi)
|
|||||||
ioapic_deassert_pin(pi->pi_vmctx, pi->pi_lintr_pin);
|
ioapic_deassert_pin(pi->pi_vmctx, pi->pi_lintr_pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return 1 if the emulated device in 'slot' is a multi-function device.
|
||||||
|
* Return 0 otherwise.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
pci_emul_is_mfdev(int slot)
|
||||||
|
{
|
||||||
|
int f, numfuncs;
|
||||||
|
|
||||||
|
numfuncs = 0;
|
||||||
|
for (f = 0; f < MAXFUNCS; f++) {
|
||||||
|
if (pci_slotinfo[slot][f].si_devi != NULL) {
|
||||||
|
numfuncs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (numfuncs > 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on
|
||||||
|
* whether or not is a multi-function being emulated in the pci 'slot'.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
pci_emul_hdrtype_fixup(int slot, int off, int bytes, uint32_t *rv)
|
||||||
|
{
|
||||||
|
int mfdev;
|
||||||
|
|
||||||
|
if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) {
|
||||||
|
mfdev = pci_emul_is_mfdev(slot);
|
||||||
|
switch (bytes) {
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
*rv &= ~PCIM_MFDEV;
|
||||||
|
if (mfdev) {
|
||||||
|
*rv |= PCIM_MFDEV;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
*rv &= ~(PCIM_MFDEV << 16);
|
||||||
|
if (mfdev) {
|
||||||
|
*rv |= (PCIM_MFDEV << 16);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int cfgbus, cfgslot, cfgfunc, cfgoff;
|
static int cfgbus, cfgslot, cfgfunc, cfgoff;
|
||||||
|
|
||||||
@ -878,12 +959,12 @@ pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
|||||||
{
|
{
|
||||||
struct pci_devinst *pi;
|
struct pci_devinst *pi;
|
||||||
struct pci_devemu *pe;
|
struct pci_devemu *pe;
|
||||||
int coff, idx;
|
int coff, idx, needcfg;
|
||||||
uint64_t mask, bar;
|
uint64_t mask, bar;
|
||||||
|
|
||||||
assert(bytes == 1 || bytes == 2 || bytes == 4);
|
assert(bytes == 1 || bytes == 2 || bytes == 4);
|
||||||
|
|
||||||
pi = pci_slotinfo[cfgslot].si_devi;
|
pi = pci_slotinfo[cfgslot][cfgfunc].si_devi;
|
||||||
coff = cfgoff + (port - CONF1_DATA_PORT);
|
coff = cfgoff + (port - CONF1_DATA_PORT);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
@ -891,7 +972,11 @@ pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
|||||||
in ? "read" : "write", coff, bytes, cfgbus, cfgslot, cfgfunc);
|
in ? "read" : "write", coff, bytes, cfgbus, cfgslot, cfgfunc);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (pi == NULL || cfgfunc != 0) {
|
/*
|
||||||
|
* Just return if there is no device at this cfgslot:cfgfunc or
|
||||||
|
* if the guest is doing an un-aligned access
|
||||||
|
*/
|
||||||
|
if (pi == NULL || (coff & (bytes - 1)) != 0) {
|
||||||
if (in)
|
if (in)
|
||||||
*eax = 0xffffffff;
|
*eax = 0xffffffff;
|
||||||
return (0);
|
return (0);
|
||||||
@ -904,16 +989,23 @@ pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
|||||||
*/
|
*/
|
||||||
if (in) {
|
if (in) {
|
||||||
/* Let the device emulation override the default handler */
|
/* Let the device emulation override the default handler */
|
||||||
if (pe->pe_cfgread != NULL &&
|
if (pe->pe_cfgread != NULL) {
|
||||||
(*pe->pe_cfgread)(ctx, vcpu, pi, coff, bytes, eax) == 0)
|
needcfg = pe->pe_cfgread(ctx, vcpu, pi,
|
||||||
return (0);
|
coff, bytes, eax);
|
||||||
|
} else {
|
||||||
|
needcfg = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needcfg) {
|
||||||
if (bytes == 1)
|
if (bytes == 1)
|
||||||
*eax = pci_get_cfgdata8(pi, coff);
|
*eax = pci_get_cfgdata8(pi, coff);
|
||||||
else if (bytes == 2)
|
else if (bytes == 2)
|
||||||
*eax = pci_get_cfgdata16(pi, coff);
|
*eax = pci_get_cfgdata16(pi, coff);
|
||||||
else
|
else
|
||||||
*eax = pci_get_cfgdata32(pi, coff);
|
*eax = pci_get_cfgdata32(pi, coff);
|
||||||
|
}
|
||||||
|
|
||||||
|
pci_emul_hdrtype_fixup(cfgslot, coff, bytes, eax);
|
||||||
} else {
|
} else {
|
||||||
/* Let the device emulation override the default handler */
|
/* Let the device emulation override the default handler */
|
||||||
if (pe->pe_cfgwrite != NULL &&
|
if (pe->pe_cfgwrite != NULL &&
|
||||||
|
Loading…
x
Reference in New Issue
Block a user