Allow PCI devices to be configured on all valid bus numbers from 0 to 255.

This is done by representing each bus as root PCI device in ACPI. The device
implements the _BBN method to return the PCI bus number to the guest OS.

Each PCI bus keeps track of the resources that is decodes for devices
configured on the bus: i/o, mmio (32-bit) and mmio (64-bit). These windows
are advertised to the guest via the _CRS object of the root device.

Bus 0 is treated specially since it consumes the I/O ports to access the
PCI config space [0xcf8-0xcff]. It also decodes the legacy I/O ports that
are consumed by devices on the LPC bus. For this reason the LPC bridge can
be configured only on bus 0.

The bus number can be specified using the following command line option
to bhyve(8): "-s <bus>:<slot>:<func>,<emul>[,<config>]"

Discussed with:	grehan@
Reviewed by:	jhb@
This commit is contained in:
Neel Natu 2014-02-14 21:34:08 +00:00
parent 177ccd3c47
commit d84882ca8f
5 changed files with 269 additions and 106 deletions

View File

@ -713,7 +713,7 @@ basl_fwrite_dsdt(FILE *fp)
pci_write_dsdt();
dsdt_line("");
dsdt_line(" Scope (_SB.PCI0)");
dsdt_line(" Scope (_SB.PC00)");
dsdt_line(" {");
dsdt_line(" Device (HPET)");
dsdt_line(" {");

View File

@ -196,16 +196,21 @@ mpt_build_ioapic_entries(io_apic_entry_ptr mpei, int id)
static int
mpt_count_ioint_entries(void)
{
int bus, count;
count = 0;
for (bus = 0; bus <= PCI_BUSMAX; bus++)
count += pci_count_lintr(bus);
/*
* Always include entries for the first 16 pins along with a entry
* for each active PCI INTx pin.
*/
return (16 + pci_count_lintr());
return (16 + count);
}
static void
mpt_generate_pci_int(int slot, int pin, int ioapic_irq, void *arg)
mpt_generate_pci_int(int bus, int slot, int pin, int ioapic_irq, void *arg)
{
int_entry_ptr *mpiep, mpie;
@ -219,7 +224,7 @@ mpt_generate_pci_int(int slot, int pin, int ioapic_irq, void *arg)
*/
mpie->type = MPCT_ENTRY_INT;
mpie->int_type = INTENTRY_TYPE_INT;
mpie->src_bus_id = 0;
mpie->src_bus_id = bus;
mpie->src_bus_irq = slot << 2 | (pin - 1);
mpie->dst_apic_id = mpie[-1].dst_apic_id;
mpie->dst_apic_int = ioapic_irq;
@ -230,7 +235,7 @@ mpt_generate_pci_int(int slot, int pin, int ioapic_irq, void *arg)
static void
mpt_build_ioint_entries(int_entry_ptr mpie, int id)
{
int pin;
int pin, bus;
/*
* The following config is taken from kernel mptable.c
@ -277,7 +282,8 @@ mpt_build_ioint_entries(int_entry_ptr mpie, int id)
}
/* Next, generate entries for any PCI INTx interrupts. */
pci_walk_lintr(mpt_generate_pci_int, &mpie);
for (bus = 0; bus <= PCI_BUSMAX; bus++)
pci_walk_lintr(bus, mpt_generate_pci_int, &mpie);
}
void

View File

@ -69,6 +69,7 @@ do { \
} \
} while (0)
#define MAXBUSES (PCI_BUSMAX + 1)
#define MAXSLOTS (PCI_SLOTMAX + 1)
#define MAXFUNCS (PCI_FUNCMAX + 1)
@ -86,7 +87,16 @@ struct intxinfo {
struct slotinfo {
struct intxinfo si_intpins[4];
struct funcinfo si_funcs[MAXFUNCS];
} pci_slotinfo[MAXSLOTS];
};
struct businfo {
uint16_t iobase, iolimit; /* I/O window */
uint32_t membase32, memlimit32; /* mmio window below 4GB */
uint64_t membase64, memlimit64; /* mmio window above 4GB */
struct slotinfo slotinfo[MAXSLOTS];
};
static struct businfo *pci_businfo[MAXBUSES];
SET_DECLARE(pci_devemu_set, struct pci_devemu);
@ -97,7 +107,7 @@ static uint64_t pci_emul_membase64;
#define PCI_EMUL_IOBASE 0x2000
#define PCI_EMUL_IOLIMIT 0x10000
#define PCI_EMUL_MEMLIMIT32 0xE0000000 /* 3.5GB */
#define PCI_EMUL_MEMLIMIT32 0xE0000000 /* 3.5GB */
#define PCI_EMUL_MEMBASE64 0xD000000000UL
#define PCI_EMUL_MEMLIMIT64 0xFD00000000UL
@ -105,7 +115,6 @@ static uint64_t pci_emul_membase64;
static struct pci_devemu *pci_emul_finddev(char *name);
static void pci_lintr_update(struct pci_devinst *pi);
static int pci_emul_devices;
static struct mem_range pci_mem_hole;
/*
@ -115,6 +124,7 @@ static struct mem_range pci_mem_hole;
/*
* Slot options are in the form:
*
* <bus>:<slot>:<func>,<emul>[,<config>]
* <slot>[:<func>],<emul>[,<config>]
*
* slot is 0..31
@ -136,37 +146,53 @@ pci_parse_slot_usage(char *aopt)
int
pci_parse_slot(char *opt)
{
char *slot, *func, *emul, *config;
char *str, *cpy;
int error, snum, fnum;
struct businfo *bi;
struct slotinfo *si;
char *emul, *config, *str, *cp;
int error, bnum, snum, fnum;
error = -1;
str = cpy = strdup(opt);
str = strdup(opt);
slot = strsep(&str, ",");
func = NULL;
if (strchr(slot, ':') != NULL) {
func = cpy;
(void) strsep(&func, ":");
}
emul = strsep(&str, ",");
config = str;
if (emul == NULL) {
emul = config = NULL;
if ((cp = strchr(str, ',')) != NULL) {
*cp = '\0';
emul = cp + 1;
if ((cp = strchr(emul, ',')) != NULL) {
*cp = '\0';
config = cp + 1;
}
} else {
pci_parse_slot_usage(opt);
goto done;
}
snum = atoi(slot);
fnum = func ? atoi(func) : 0;
/* <bus>:<slot>:<func> */
if (sscanf(str, "%d:%d:%d", &bnum, &snum, &fnum) != 3) {
bnum = 0;
/* <slot>:<func> */
if (sscanf(str, "%d:%d", &snum, &fnum) != 2) {
fnum = 0;
/* <slot> */
if (sscanf(str, "%d", &snum) != 1) {
snum = -1;
}
}
}
if (snum < 0 || snum >= MAXSLOTS || fnum < 0 || fnum >= MAXFUNCS) {
if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS ||
fnum < 0 || fnum >= MAXFUNCS) {
pci_parse_slot_usage(opt);
goto done;
}
if (pci_slotinfo[snum].si_funcs[fnum].fi_name != NULL) {
if (pci_businfo[bnum] == NULL)
pci_businfo[bnum] = calloc(1, sizeof(struct businfo));
bi = pci_businfo[bnum];
si = &bi->slotinfo[snum];
if (si->si_funcs[fnum].fi_name != NULL) {
fprintf(stderr, "pci slot %d:%d already occupied!\n",
snum, fnum);
goto done;
@ -179,12 +205,12 @@ pci_parse_slot(char *opt)
}
error = 0;
pci_slotinfo[snum].si_funcs[fnum].fi_name = emul;
pci_slotinfo[snum].si_funcs[fnum].fi_param = config;
si->si_funcs[fnum].fi_name = emul;
si->si_funcs[fnum].fi_param = config;
done:
if (error)
free(cpy);
free(str);
return (error);
}
@ -665,8 +691,8 @@ pci_emul_finddev(char *name)
}
static int
pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func,
char *params)
pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot,
int func, struct funcinfo *fi)
{
struct pci_devinst *pdi;
int err;
@ -675,7 +701,7 @@ pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func,
bzero(pdi, sizeof(*pdi));
pdi->pi_vmctx = ctx;
pdi->pi_bus = 0;
pdi->pi_bus = bus;
pdi->pi_slot = slot;
pdi->pi_func = func;
pthread_mutex_init(&pdi->pi_lintr.lock, NULL);
@ -692,13 +718,11 @@ pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func,
pci_set_cfgdata8(pdi, PCIR_COMMAND,
PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
err = (*pde->pe_init)(ctx, pdi, params);
if (err != 0) {
err = (*pde->pe_init)(ctx, pdi, fi->fi_param);
if (err == 0)
fi->fi_devi = pdi;
else
free(pdi);
} else {
pci_emul_devices++;
pci_slotinfo[slot].si_funcs[func].fi_devi = pdi;
}
return (err);
}
@ -1006,31 +1030,68 @@ pci_emul_fallback_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr,
return (0);
}
#define BUSIO_ROUNDUP 32
#define BUSMEM_ROUNDUP (1024 * 1024)
int
init_pci(struct vmctx *ctx)
{
struct pci_devemu *pde;
struct businfo *bi;
struct slotinfo *si;
struct funcinfo *fi;
size_t lowmem;
int slot, func;
int bus, slot, func;
int error;
pci_emul_iobase = PCI_EMUL_IOBASE;
pci_emul_membase32 = vm_get_lowmem_limit(ctx);
pci_emul_membase64 = PCI_EMUL_MEMBASE64;
for (slot = 0; slot < MAXSLOTS; slot++) {
for (func = 0; func < MAXFUNCS; func++) {
fi = &pci_slotinfo[slot].si_funcs[func];
if (fi->fi_name != NULL) {
for (bus = 0; bus < MAXBUSES; bus++) {
if ((bi = pci_businfo[bus]) == NULL)
continue;
/*
* Keep track of the i/o and memory resources allocated to
* this bus.
*/
bi->iobase = pci_emul_iobase;
bi->membase32 = pci_emul_membase32;
bi->membase64 = pci_emul_membase64;
for (slot = 0; slot < MAXSLOTS; slot++) {
si = &bi->slotinfo[slot];
for (func = 0; func < MAXFUNCS; func++) {
fi = &si->si_funcs[func];
if (fi->fi_name == NULL)
continue;
pde = pci_emul_finddev(fi->fi_name);
assert(pde != NULL);
error = pci_emul_init(ctx, pde, slot, func,
fi->fi_param);
error = pci_emul_init(ctx, pde, bus, slot,
func, fi);
if (error)
return (error);
}
}
/*
* Add some slop to the I/O and memory resources decoded by
* this bus to give a guest some flexibility if it wants to
* reprogram the BARs.
*/
pci_emul_iobase += BUSIO_ROUNDUP;
pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP);
bi->iolimit = pci_emul_iobase;
pci_emul_membase32 += BUSMEM_ROUNDUP;
pci_emul_membase32 = roundup2(pci_emul_membase32,
BUSMEM_ROUNDUP);
bi->memlimit32 = pci_emul_membase32;
pci_emul_membase64 += BUSMEM_ROUNDUP;
pci_emul_membase64 = roundup2(pci_emul_membase64,
BUSMEM_ROUNDUP);
bi->memlimit64 = pci_emul_membase64;
}
/*
@ -1060,7 +1121,7 @@ init_pci(struct vmctx *ctx)
}
static void
pci_prt_entry(int slot, int pin, int ioapic_irq, void *arg)
pci_prt_entry(int bus, int slot, int pin, int ioapic_irq, void *arg)
{
int *count;
@ -1075,96 +1136,158 @@ pci_prt_entry(int slot, int pin, int ioapic_irq, void *arg)
(*count)--;
}
void
pci_write_dsdt(void)
/*
* A bhyve virtual machine has a flat PCI hierarchy with a root port
* corresponding to each PCI bus.
*/
static void
pci_bus_write_dsdt(int bus)
{
struct businfo *bi;
struct slotinfo *si;
struct pci_devinst *pi;
int count, slot, func;
/*
* If there are no devices on this 'bus' then just return.
*/
if ((bi = pci_businfo[bus]) == NULL) {
/*
* Bus 0 is special because it decodes the I/O ports used
* for PCI config space access even if there are no devices
* on it.
*/
if (bus != 0)
return;
}
dsdt_indent(1);
dsdt_line("Scope (_SB)");
dsdt_line("{");
dsdt_line(" Device (PCI0)");
dsdt_line(" Device (PC%02X)", bus);
dsdt_line(" {");
dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))");
dsdt_line(" Name (_ADR, Zero)");
dsdt_line(" Method (_BBN, 0, NotSerialized)");
dsdt_line(" {");
dsdt_line(" Return (0x%08X)", bus);
dsdt_line(" }");
dsdt_line(" Name (_CRS, ResourceTemplate ()");
dsdt_line(" {");
dsdt_line(" WordBusNumber (ResourceProducer, MinFixed, "
"MaxFixed, PosDecode,");
dsdt_line(" 0x0000, // Granularity");
dsdt_line(" 0x0000, // Range Minimum");
dsdt_line(" 0x00FF, // Range Maximum");
dsdt_line(" 0x%04X, // Range Minimum", bus);
dsdt_line(" 0x%04X, // Range Maximum", bus);
dsdt_line(" 0x0000, // Translation Offset");
dsdt_line(" 0x0100, // Length");
dsdt_line(" 0x0001, // Length");
dsdt_line(" ,, )");
dsdt_indent(3);
dsdt_fixed_ioport(0xCF8, 8);
dsdt_unindent(3);
if (bus == 0) {
dsdt_indent(3);
dsdt_fixed_ioport(0xCF8, 8);
dsdt_unindent(3);
dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, "
"PosDecode, EntireRange,");
dsdt_line(" 0x0000, // Granularity");
dsdt_line(" 0x0000, // Range Minimum");
dsdt_line(" 0x0CF7, // Range Maximum");
dsdt_line(" 0x0000, // Translation Offset");
dsdt_line(" 0x0CF8, // Length");
dsdt_line(" ,, , TypeStatic)");
dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, "
"PosDecode, EntireRange,");
dsdt_line(" 0x0000, // Granularity");
dsdt_line(" 0x0D00, // Range Minimum");
dsdt_line(" 0x%04X, // Range Maximum",
PCI_EMUL_IOBASE - 1);
dsdt_line(" 0x0000, // Translation Offset");
dsdt_line(" 0x%04X, // Length",
PCI_EMUL_IOBASE - 0x0D00);
dsdt_line(" ,, , TypeStatic)");
if (bi == NULL) {
dsdt_line(" })");
goto done;
}
}
assert(bi != NULL);
/* i/o window */
dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, "
"PosDecode, EntireRange,");
dsdt_line(" 0x0000, // Granularity");
dsdt_line(" 0x0000, // Range Minimum");
dsdt_line(" 0x0CF7, // Range Maximum");
dsdt_line(" 0x%04X, // Range Minimum", bi->iobase);
dsdt_line(" 0x%04X, // Range Maximum",
bi->iolimit - 1);
dsdt_line(" 0x0000, // Translation Offset");
dsdt_line(" 0x0CF8, // Length");
dsdt_line(" ,, , TypeStatic)");
dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, "
"PosDecode, EntireRange,");
dsdt_line(" 0x0000, // Granularity");
dsdt_line(" 0x0D00, // Range Minimum");
dsdt_line(" 0xFFFF, // Range Maximum");
dsdt_line(" 0x0000, // Translation Offset");
dsdt_line(" 0xF300, // Length");
dsdt_line(" 0x%04X, // Length",
bi->iolimit - bi->iobase);
dsdt_line(" ,, , TypeStatic)");
/* mmio window (32-bit) */
dsdt_line(" DWordMemory (ResourceProducer, PosDecode, "
"MinFixed, MaxFixed, NonCacheable, ReadWrite,");
dsdt_line(" 0x00000000, // Granularity");
dsdt_line(" 0x%08lX, // Range Minimum\n",
pci_mem_hole.base);
dsdt_line(" 0x%08X, // Range Minimum\n", bi->membase32);
dsdt_line(" 0x%08X, // Range Maximum\n",
PCI_EMUL_MEMLIMIT32 - 1);
bi->memlimit32 - 1);
dsdt_line(" 0x00000000, // Translation Offset");
dsdt_line(" 0x%08lX, // Length\n",
PCI_EMUL_MEMLIMIT32 - pci_mem_hole.base);
dsdt_line(" 0x%08X, // Length\n",
bi->memlimit32 - bi->membase32);
dsdt_line(" ,, , AddressRangeMemory, TypeStatic)");
/* mmio window (64-bit) */
dsdt_line(" QWordMemory (ResourceProducer, PosDecode, "
"MinFixed, MaxFixed, NonCacheable, ReadWrite,");
dsdt_line(" 0x0000000000000000, // Granularity");
dsdt_line(" 0x%016lX, // Range Minimum\n",
PCI_EMUL_MEMBASE64);
dsdt_line(" 0x%016lX, // Range Minimum\n", bi->membase64);
dsdt_line(" 0x%016lX, // Range Maximum\n",
PCI_EMUL_MEMLIMIT64 - 1);
bi->memlimit64 - 1);
dsdt_line(" 0x0000000000000000, // Translation Offset");
dsdt_line(" 0x%016lX, // Length\n",
PCI_EMUL_MEMLIMIT64 - PCI_EMUL_MEMBASE64);
bi->memlimit64 - bi->membase64);
dsdt_line(" ,, , AddressRangeMemory, TypeStatic)");
dsdt_line(" })");
count = pci_count_lintr();
count = pci_count_lintr(bus);
if (count != 0) {
dsdt_indent(2);
dsdt_line("Name (_PRT, Package (0x%02X)", count);
dsdt_line("{");
pci_walk_lintr(pci_prt_entry, &count);
pci_walk_lintr(bus, pci_prt_entry, &count);
dsdt_line("})");
dsdt_unindent(2);
}
dsdt_indent(2);
for (slot = 0; slot < MAXSLOTS; slot++) {
si = &bi->slotinfo[slot];
for (func = 0; func < MAXFUNCS; func++) {
pi = pci_slotinfo[slot].si_funcs[func].fi_devi;
pi = si->si_funcs[func].fi_devi;
if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL)
pi->pi_d->pe_write_dsdt(pi);
}
}
dsdt_unindent(2);
done:
dsdt_line(" }");
dsdt_line("}");
dsdt_unindent(1);
}
void
pci_write_dsdt(void)
{
int bus;
for (bus = 0; bus < MAXBUSES; bus++)
pci_bus_write_dsdt(bus);
}
int
pci_msi_enabled(struct pci_devinst *pi)
{
@ -1231,13 +1354,17 @@ pci_lintr_permitted(struct pci_devinst *pi)
int
pci_lintr_request(struct pci_devinst *pi)
{
struct businfo *bi;
struct slotinfo *si;
int bestpin, bestcount, irq, pin;
bi = pci_businfo[pi->pi_bus];
assert(bi != NULL);
/*
* First, allocate a pin from our slot.
*/
si = &pci_slotinfo[pi->pi_slot];
si = &bi->slotinfo[pi->pi_slot];
bestpin = 0;
bestcount = si->si_intpins[0].ii_count;
for (pin = 1; pin < 4; pin++) {
@ -1318,31 +1445,41 @@ pci_lintr_update(struct pci_devinst *pi)
}
int
pci_count_lintr(void)
pci_count_lintr(int bus)
{
int count, slot, pin;
struct slotinfo *slotinfo;
count = 0;
for (slot = 0; slot < MAXSLOTS; slot++) {
for (pin = 0; pin < 4; pin++) {
if (pci_slotinfo[slot].si_intpins[pin].ii_count != 0)
count++;
if (pci_businfo[bus] != NULL) {
for (slot = 0; slot < MAXSLOTS; slot++) {
slotinfo = &pci_businfo[bus]->slotinfo[slot];
for (pin = 0; pin < 4; pin++) {
if (slotinfo->si_intpins[pin].ii_count != 0)
count++;
}
}
}
return (count);
}
void
pci_walk_lintr(pci_lintr_cb cb, void *arg)
pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg)
{
struct businfo *bi;
struct slotinfo *si;
struct intxinfo *ii;
int slot, pin;
if ((bi = pci_businfo[bus]) == NULL)
return;
for (slot = 0; slot < MAXSLOTS; slot++) {
si = &bi->slotinfo[slot];
for (pin = 0; pin < 4; pin++) {
ii = &pci_slotinfo[slot].si_intpins[pin];
ii = &si->si_intpins[pin];
if (ii->ii_count != 0)
cb(slot, pin + 1, ii->ii_ioapic_irq, arg);
cb(bus, slot, pin + 1, ii->ii_ioapic_irq, arg);
}
}
}
@ -1352,14 +1489,19 @@ pci_walk_lintr(pci_lintr_cb cb, void *arg)
* Return 0 otherwise.
*/
static int
pci_emul_is_mfdev(int slot)
pci_emul_is_mfdev(int bus, int slot)
{
struct businfo *bi;
struct slotinfo *si;
int f, numfuncs;
numfuncs = 0;
for (f = 0; f < MAXFUNCS; f++) {
if (pci_slotinfo[slot].si_funcs[f].fi_devi != NULL) {
numfuncs++;
if ((bi = pci_businfo[bus]) != NULL) {
si = &bi->slotinfo[slot];
for (f = 0; f < MAXFUNCS; f++) {
if (si->si_funcs[f].fi_devi != NULL) {
numfuncs++;
}
}
}
return (numfuncs > 1);
@ -1370,12 +1512,12 @@ pci_emul_is_mfdev(int slot)
* 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)
pci_emul_hdrtype_fixup(int bus, 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);
mfdev = pci_emul_is_mfdev(bus, slot);
switch (bytes) {
case 1:
case 2:
@ -1492,16 +1634,19 @@ static int
pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
struct businfo *bi;
struct slotinfo *si;
struct pci_devinst *pi;
struct pci_devemu *pe;
int coff, idx, needcfg;
uint64_t addr, bar, mask;
assert(bytes == 1 || bytes == 2 || bytes == 4);
if (cfgbus == 0)
pi = pci_slotinfo[cfgslot].si_funcs[cfgfunc].fi_devi;
else
if ((bi = pci_businfo[cfgbus]) != NULL) {
si = &bi->slotinfo[cfgslot];
pi = si->si_funcs[cfgfunc].fi_devi;
} else
pi = NULL;
coff = cfgoff + (port - CONF1_DATA_PORT);
@ -1544,7 +1689,7 @@ pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
*eax = pci_get_cfgdata32(pi, coff);
}
pci_emul_hdrtype_fixup(cfgslot, coff, bytes, eax);
pci_emul_hdrtype_fixup(cfgbus, cfgslot, coff, bytes, eax);
} else {
/* Let the device emulation override the default handler */
if (pe->pe_cfgwrite != NULL &&
@ -1646,7 +1791,7 @@ SYSRES_IO(0xC00, 2);
/*
* Define a dummy test device
*/
#define DIOSZ 20
#define DIOSZ 8
#define DMEMSZ 4096
struct pci_emul_dsoftc {
uint8_t ioregs[DIOSZ];

View File

@ -199,7 +199,7 @@ struct pciecap {
uint16_t slot_status2;
} __packed;
typedef void (*pci_lintr_cb)(int slot, int pin, int ioapic_irq, void *arg);
typedef void (*pci_lintr_cb)(int b, int s, int pin, int ioapic_irq, void *arg);
int init_pci(struct vmctx *ctx);
void msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
@ -229,8 +229,8 @@ int pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum);
int pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size,
uint64_t value);
uint64_t pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size);
int pci_count_lintr(void);
void pci_walk_lintr(pci_lintr_cb cb, void *arg);
int pci_count_lintr(int bus);
void pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg);
void pci_write_dsdt(void);
static __inline void

View File

@ -277,8 +277,20 @@ pci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
/*
* Do not allow more than one LPC bridge to be configured.
*/
if (lpc_bridge != NULL)
if (lpc_bridge != NULL) {
fprintf(stderr, "Only one LPC bridge is allowed.\n");
return (-1);
}
/*
* Enforce that the LPC can only be configured on bus 0. This
* simplifies the ACPI DSDT because it can provide a decode for
* all legacy i/o ports behind bus 0.
*/
if (pi->pi_bus != 0) {
fprintf(stderr, "LPC bridge can be present only on bus 0.\n");
return (-1);
}
if (lpc_init() != 0)
return (-1);