summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bhyve
diff options
context:
space:
mode:
authorneel <neel@FreeBSD.org>2012-08-06 06:51:27 +0000
committerneel <neel@FreeBSD.org>2012-08-06 06:51:27 +0000
commitd4dec7419041f5934130a79960faf398446e95dd (patch)
treea829eac788411d512a3bc80c135f90070f538620 /usr.sbin/bhyve
parent5bfba16f732de1d8697ff76a41623687b4d8f156 (diff)
downloadFreeBSD-src-d4dec7419041f5934130a79960faf398446e95dd.zip
FreeBSD-src-d4dec7419041f5934130a79960faf398446e95dd.tar.gz
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
Diffstat (limited to 'usr.sbin/bhyve')
-rw-r--r--usr.sbin/bhyve/pci_emul.c200
1 files changed, 146 insertions, 54 deletions
diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c
index 5f8436c..92c8715 100644
--- a/usr.sbin/bhyve/pci_emul.c
+++ b/usr.sbin/bhyve/pci_emul.c
@@ -45,7 +45,6 @@ __FBSDID("$FreeBSD$");
#include "fbsdrun.h"
#include "inout.h"
#include "pci_emul.h"
-#include "instruction_emul.h"
#include "ioapic.h"
#define CONF1_ADDR_PORT 0x0cf8
@@ -62,7 +61,8 @@ do { \
} \
} while (0)
-#define MAXSLOTS 32
+#define MAXSLOTS (PCI_SLOTMAX + 1)
+#define MAXFUNCS (PCI_FUNCMAX + 1)
static struct slotinfo {
char *si_name;
@@ -73,7 +73,7 @@ static struct slotinfo {
char si_prefix;
char si_suffix;
int si_legacy;
-} pci_slotinfo[MAXSLOTS];
+} pci_slotinfo[MAXSLOTS][MAXFUNCS];
/*
* 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_prefix[4];
uint32_t mds_rsvd[3];
- } md_slotinfo[MAXSLOTS];
+ } md_slotinfo[MAXSLOTS * MAXFUNCS];
} pci_devnames;
SET_DECLARE(pci_devemu_set, struct pci_devemu);
@@ -142,15 +142,16 @@ static int devname_elems;
/*
* Slot options are in the form:
*
- * <slot>,<emul>[,<config>]
+ * <slot>[:<func>],<emul>[,<config>]
*
* slot is 0..31
+ * func is 0..7
* 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
* used for configuration.
* Examples are:
* 1,virtio-net,tap0
- * 3,dummy
+ * 3:0,dummy
*/
static void
pci_parse_slot_usage(char *aopt)
@@ -162,14 +163,22 @@ pci_parse_slot_usage(char *aopt)
void
pci_parse_slot(char *opt, int legacy)
{
- char *slot, *emul, *config;
+ char *slot, *func, *emul, *config;
char *str, *cpy;
- int snum;
+ int snum, fnum;
str = cpy = strdup(opt);
+
config = NULL;
- slot = strsep(&str, ",");
+ if (strchr(str, ':') != NULL) {
+ slot = strsep(&str, ":");
+ func = strsep(&str, ",");
+ } else {
+ slot = strsep(&str, ",");
+ func = NULL;
+ }
+
emul = strsep(&str, ",");
if (str != NULL) {
config = strsep(&str, ",");
@@ -180,34 +189,33 @@ pci_parse_slot(char *opt, int legacy)
return;
}
- snum = 255;
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);
} else {
- pci_slotinfo[snum].si_name = emul;
- pci_slotinfo[snum].si_param = config;
- pci_slotinfo[snum].si_legacy = legacy;
+ pci_slotinfo[snum][fnum].si_name = emul;
+ pci_slotinfo[snum][fnum].si_param = config;
+ pci_slotinfo[snum][fnum].si_legacy = legacy;
}
}
-
/*
*
* 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,
* and <suffix> a single char.
*
* Examples:
* 1,e0c
- * 4,e0P
+ * 4:0,e0P
+ * 4:1,e0M
* 6,43a
* 7,0f
* 10,1
- * 12,e0M
* 2,12a
*
* 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 *namestr;
- char *slotend;
+ char *slotend, *funcend, *funcstart;
char prefix, suffix;
int i;
int pslot;
- int snum;
+ int snum, fnum;
pslot = -1;
prefix = suffix = 0;
- slotend = strchr(opt, ',');
+
+ slotend = strchr(opt, ':');
+ if (slotend != NULL) {
+ funcstart = slotend + 1;
+ funcend = strchr(funcstart, ',');
+ } else {
+ slotend = strchr(opt, ',');
+ funcstart = funcend = NULL;
+ }
/*
* A comma must be present, and can't be the first character
@@ -248,14 +264,31 @@ pci_parse_name(char *opt)
}
csnum[i] = '\0';
- snum = 255;
snum = atoi(csnum);
if (snum < 0 || snum >= MAXSLOTS) {
pci_parse_name_usage(opt);
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) {
pci_parse_name_usage(opt);
@@ -276,11 +309,10 @@ pci_parse_name(char *opt)
}
if (isalpha(*namestr) && *(namestr + 1) == 0) {
suffix = *namestr;
- pci_slotinfo[snum].si_titled = 1;
- pci_slotinfo[snum].si_pslot = pslot;
- pci_slotinfo[snum].si_prefix = prefix;
- pci_slotinfo[snum].si_suffix = suffix;
-
+ pci_slotinfo[snum][fnum].si_titled = 1;
+ pci_slotinfo[snum][fnum].si_pslot = pslot;
+ pci_slotinfo[snum][fnum].si_prefix = prefix;
+ pci_slotinfo[snum][fnum].si_suffix = suffix;
} else {
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;
break;
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);
baseptr = &hostbase;
} else {
@@ -536,7 +569,8 @@ pci_emul_finddev(char *name)
}
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;
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_bus = 0;
pdi->pi_slot = slot;
- pdi->pi_func = 0;
+ pdi->pi_func = func;
pdi->pi_d = pde;
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);
} else {
pci_emul_devices++;
- pci_slotinfo[slot].si_devi = pdi;
+ pci_slotinfo[slot][func].si_devi = pdi;
}
}
@@ -730,20 +764,22 @@ init_pci(struct vmctx *ctx)
{
struct pci_devemu *pde;
struct slotinfo *si;
- int i;
+ int slot, func;
pci_emul_iobase = PCI_EMUL_IOBASE;
pci_emul_membase32 = PCI_EMUL_MEMBASE32;
pci_emul_membase64 = PCI_EMUL_MEMBASE64;
- si = pci_slotinfo;
-
- for (i = 0; i < MAXSLOTS; i++, si++) {
- if (si->si_name != NULL) {
- pde = pci_emul_finddev(si->si_name);
- if (pde != NULL) {
- pci_emul_init(ctx, pde, i, si->si_param);
- pci_add_mptable_name(si);
+ for (slot = 0; slot < MAXSLOTS; slot++) {
+ for (func = 0; func < MAXFUNCS; func++) {
+ si = &pci_slotinfo[slot][func];
+ if (si->si_name != NULL) {
+ pde = pci_emul_finddev(si->si_name);
+ if (pde != NULL) {
+ pci_emul_init(ctx, pde, slot, func,
+ si->si_param);
+ pci_add_mptable_name(si);
+ }
}
}
}
@@ -790,7 +826,7 @@ int
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
@@ -847,7 +883,52 @@ pci_lintr_deassert(struct pci_devinst *pi)
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;
@@ -878,12 +959,12 @@ pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
{
struct pci_devinst *pi;
struct pci_devemu *pe;
- int coff, idx;
+ int coff, idx, needcfg;
uint64_t mask, bar;
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);
#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);
#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)
*eax = 0xffffffff;
return (0);
@@ -904,16 +989,23 @@ pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
*/
if (in) {
/* Let the device emulation override the default handler */
- if (pe->pe_cfgread != NULL &&
- (*pe->pe_cfgread)(ctx, vcpu, pi, coff, bytes, eax) == 0)
- return (0);
+ if (pe->pe_cfgread != NULL) {
+ needcfg = pe->pe_cfgread(ctx, vcpu, pi,
+ coff, bytes, eax);
+ } else {
+ needcfg = 1;
+ }
- if (bytes == 1)
- *eax = pci_get_cfgdata8(pi, coff);
- else if (bytes == 2)
- *eax = pci_get_cfgdata16(pi, coff);
- else
- *eax = pci_get_cfgdata32(pi, coff);
+ if (needcfg) {
+ if (bytes == 1)
+ *eax = pci_get_cfgdata8(pi, coff);
+ else if (bytes == 2)
+ *eax = pci_get_cfgdata16(pi, coff);
+ else
+ *eax = pci_get_cfgdata32(pi, coff);
+ }
+
+ pci_emul_hdrtype_fixup(cfgslot, coff, bytes, eax);
} else {
/* Let the device emulation override the default handler */
if (pe->pe_cfgwrite != NULL &&
OpenPOWER on IntegriCloud