summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2014-07-22 03:14:37 +0000
committerjhb <jhb@FreeBSD.org>2014-07-22 03:14:37 +0000
commitc1fe945ebd0d209b238eb98b47c40c115576f2e4 (patch)
tree60ebdda4ab095737aad1e22d1f84da4708e9a68c /usr.sbin
parent87624b14336f152a9daf40db972abf2f70cf951d (diff)
downloadFreeBSD-src-c1fe945ebd0d209b238eb98b47c40c115576f2e4.zip
FreeBSD-src-c1fe945ebd0d209b238eb98b47c40c115576f2e4.tar.gz
MFC 266125:
Implement a PCI interrupt router to route PCI legacy INTx interrupts to the legacy 8259A PICs.
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/bhyve/Makefile1
-rw-r--r--usr.sbin/bhyve/acpi.c2
-rw-r--r--usr.sbin/bhyve/acpi.h1
-rw-r--r--usr.sbin/bhyve/bhyverun.c3
-rw-r--r--usr.sbin/bhyve/mptbl.c3
-rw-r--r--usr.sbin/bhyve/pci_emul.c177
-rw-r--r--usr.sbin/bhyve/pci_emul.h6
-rw-r--r--usr.sbin/bhyve/pci_irq.c349
-rw-r--r--usr.sbin/bhyve/pci_irq.h45
-rw-r--r--usr.sbin/bhyve/pci_lpc.c68
-rw-r--r--usr.sbin/bhyve/pci_lpc.h2
-rw-r--r--usr.sbin/bhyve/pm.c13
12 files changed, 609 insertions, 61 deletions
diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile
index c73cb3d..23e16cb 100644
--- a/usr.sbin/bhyve/Makefile
+++ b/usr.sbin/bhyve/Makefile
@@ -23,6 +23,7 @@ SRCS= \
pci_ahci.c \
pci_emul.c \
pci_hostbridge.c \
+ pci_irq.c \
pci_lpc.c \
pci_passthru.c \
pci_virtio_block.c \
diff --git a/usr.sbin/bhyve/acpi.c b/usr.sbin/bhyve/acpi.c
index db7f0eb..c4ec020 100644
--- a/usr.sbin/bhyve/acpi.c
+++ b/usr.sbin/bhyve/acpi.c
@@ -704,7 +704,7 @@ basl_fwrite_dsdt(FILE *fp)
dsdt_line("DefinitionBlock (\"bhyve_dsdt.aml\", \"DSDT\", 2,"
"\"BHYVE \", \"BVDSDT \", 0x00000001)");
dsdt_line("{");
- dsdt_line(" Name (_S5, Package (0x02)");
+ dsdt_line(" Name (_S5, Package ()");
dsdt_line(" {");
dsdt_line(" 0x05,");
dsdt_line(" Zero,");
diff --git a/usr.sbin/bhyve/acpi.h b/usr.sbin/bhyve/acpi.h
index 57edc48..652164a 100644
--- a/usr.sbin/bhyve/acpi.h
+++ b/usr.sbin/bhyve/acpi.h
@@ -49,5 +49,6 @@ void dsdt_fixed_irq(uint8_t irq);
void dsdt_fixed_mem32(uint32_t base, uint32_t length);
void dsdt_indent(int levels);
void dsdt_unindent(int levels);
+void sci_init(struct vmctx *ctx);
#endif /* _ACPI_H_ */
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
index b2354c9..d9b4418 100644
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -60,6 +60,7 @@ __FBSDID("$FreeBSD$");
#include "mevent.h"
#include "mptbl.h"
#include "pci_emul.h"
+#include "pci_irq.h"
#include "pci_lpc.h"
#include "smbiostbl.h"
#include "xmsr.h"
@@ -770,9 +771,11 @@ main(int argc, char *argv[])
init_mem();
init_inout();
+ pci_irq_init(ctx);
ioapic_init(ctx);
rtc_init(ctx);
+ sci_init(ctx);
/*
* Exit if a device emulation finds an error in it's initilization
diff --git a/usr.sbin/bhyve/mptbl.c b/usr.sbin/bhyve/mptbl.c
index 4c2167e..904d103 100644
--- a/usr.sbin/bhyve/mptbl.c
+++ b/usr.sbin/bhyve/mptbl.c
@@ -210,7 +210,8 @@ mpt_count_ioint_entries(void)
}
static void
-mpt_generate_pci_int(int bus, int slot, int pin, int ioapic_irq, void *arg)
+mpt_generate_pci_int(int bus, int slot, int pin, int pirq_pin, int ioapic_irq,
+ void *arg)
{
int_entry_ptr *mpiep, mpie;
diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c
index 5455f40..a2c47ec 100644
--- a/usr.sbin/bhyve/pci_emul.c
+++ b/usr.sbin/bhyve/pci_emul.c
@@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
#include "ioapic.h"
#include "mem.h"
#include "pci_emul.h"
+#include "pci_irq.h"
#include "pci_lpc.h"
#define CONF1_ADDR_PORT 0x0cf8
@@ -81,6 +82,7 @@ struct funcinfo {
struct intxinfo {
int ii_count;
+ int ii_pirq_pin;
int ii_ioapic_irq;
};
@@ -113,6 +115,7 @@ static uint64_t pci_emul_membase64;
#define PCI_EMUL_MEMLIMIT64 0xFD00000000UL
static struct pci_devemu *pci_emul_finddev(char *name);
+static void pci_lintr_route(struct pci_devinst *pi);
static void pci_lintr_update(struct pci_devinst *pi);
static struct mem_range pci_mem_hole;
@@ -714,6 +717,7 @@ pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot,
pthread_mutex_init(&pdi->pi_lintr.lock, NULL);
pdi->pi_lintr.pin = 0;
pdi->pi_lintr.state = IDLE;
+ pdi->pi_lintr.pirq_pin = 0;
pdi->pi_lintr.ioapic_irq = 0;
pdi->pi_d = pde;
snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot);
@@ -1084,6 +1088,27 @@ init_pci(struct vmctx *ctx)
}
/*
+ * PCI backends are initialized before routing INTx interrupts
+ * so that LPC devices are able to reserve ISA IRQs before
+ * routing PIRQ pins.
+ */
+ for (bus = 0; bus < MAXBUSES; bus++) {
+ if ((bi = pci_businfo[bus]) == NULL)
+ continue;
+
+ for (slot = 0; slot < MAXSLOTS; slot++) {
+ si = &bi->slotinfo[slot];
+ for (func = 0; func < MAXFUNCS; func++) {
+ fi = &si->si_funcs[func];
+ if (fi->fi_devi == NULL)
+ continue;
+ pci_lintr_route(fi->fi_devi);
+ }
+ }
+ }
+ lpc_pirq_routed();
+
+ /*
* The guest physical memory map looks like the following:
* [0, lowmem) guest system memory
* [lowmem, lowmem_limit) memory hole (may be absent)
@@ -1110,19 +1135,36 @@ init_pci(struct vmctx *ctx)
}
static void
-pci_prt_entry(int bus, int slot, int pin, int ioapic_irq, void *arg)
+pci_apic_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq,
+ void *arg)
{
- int *count;
- count = arg;
- dsdt_line(" Package (0x04)");
+ dsdt_line(" Package ()");
dsdt_line(" {");
dsdt_line(" 0x%X,", slot << 16 | 0xffff);
dsdt_line(" 0x%02X,", pin - 1);
dsdt_line(" Zero,");
dsdt_line(" 0x%X", ioapic_irq);
- dsdt_line(" }%s", *count == 1 ? "" : ",");
- (*count)--;
+ dsdt_line(" },");
+}
+
+static void
+pci_pirq_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq,
+ void *arg)
+{
+ char *name;
+
+ name = lpc_pirq_name(pirq_pin);
+ if (name == NULL)
+ return;
+ dsdt_line(" Package ()");
+ dsdt_line(" {");
+ dsdt_line(" 0x%X,", slot << 16 | 0xffff);
+ dsdt_line(" 0x%02X,", pin - 1);
+ dsdt_line(" %s,", name);
+ dsdt_line(" 0x00");
+ dsdt_line(" },");
+ free(name);
}
/*
@@ -1135,7 +1177,7 @@ pci_bus_write_dsdt(int bus)
struct businfo *bi;
struct slotinfo *si;
struct pci_devinst *pi;
- int count, slot, func;
+ int count, func, slot;
/*
* If there are no devices on this 'bus' then just return.
@@ -1150,9 +1192,6 @@ pci_bus_write_dsdt(int bus)
return;
}
- dsdt_indent(1);
- dsdt_line("Scope (_SB)");
- dsdt_line("{");
dsdt_line(" Device (PC%02X)", bus);
dsdt_line(" {");
dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))");
@@ -1245,10 +1284,25 @@ pci_bus_write_dsdt(int bus)
count = pci_count_lintr(bus);
if (count != 0) {
dsdt_indent(2);
- dsdt_line("Name (_PRT, Package (0x%02X)", count);
+ dsdt_line("Name (PPRT, Package ()");
dsdt_line("{");
- pci_walk_lintr(bus, pci_prt_entry, &count);
- dsdt_line("})");
+ pci_walk_lintr(bus, pci_pirq_prt_entry, NULL);
+ dsdt_line("})");
+ dsdt_line("Name (APRT, Package ()");
+ dsdt_line("{");
+ pci_walk_lintr(bus, pci_apic_prt_entry, NULL);
+ dsdt_line("})");
+ dsdt_line("Method (_PRT, 0, NotSerialized)");
+ dsdt_line("{");
+ dsdt_line(" If (PICM)");
+ dsdt_line(" {");
+ dsdt_line(" Return (APRT)");
+ dsdt_line(" }");
+ dsdt_line(" Else");
+ dsdt_line(" {");
+ dsdt_line(" Return (PPRT)");
+ dsdt_line(" }");
+ dsdt_line("}");
dsdt_unindent(2);
}
@@ -1264,8 +1318,6 @@ pci_bus_write_dsdt(int bus)
dsdt_unindent(2);
done:
dsdt_line(" }");
- dsdt_line("}");
- dsdt_unindent(1);
}
void
@@ -1273,8 +1325,19 @@ pci_write_dsdt(void)
{
int bus;
+ dsdt_indent(1);
+ dsdt_line("Name (PICM, 0x00)");
+ dsdt_line("Method (_PIC, 1, NotSerialized)");
+ dsdt_line("{");
+ dsdt_line(" Store (Arg0, PICM)");
+ dsdt_line("}");
+ dsdt_line("");
+ dsdt_line("Scope (_SB)");
+ dsdt_line("{");
for (bus = 0; bus < MAXBUSES; bus++)
pci_bus_write_dsdt(bus);
+ dsdt_line("}");
+ dsdt_unindent(1);
}
int
@@ -1347,18 +1410,19 @@ pci_lintr_permitted(struct pci_devinst *pi)
(cmd & PCIM_CMD_INTxDIS)));
}
-int
+void
pci_lintr_request(struct pci_devinst *pi)
{
struct businfo *bi;
struct slotinfo *si;
- int bestpin, bestcount, irq, pin;
+ int bestpin, bestcount, pin;
bi = pci_businfo[pi->pi_bus];
assert(bi != NULL);
/*
- * First, allocate a pin from our slot.
+ * Just allocate a pin from our slot. The pin will be
+ * assigned IRQs later when interrupts are routed.
*/
si = &bi->slotinfo[pi->pi_slot];
bestpin = 0;
@@ -1370,26 +1434,43 @@ pci_lintr_request(struct pci_devinst *pi)
}
}
- /*
- * Attempt to allocate an I/O APIC pin for this intpin. If
- * 8259A support is added we will need a separate field to
- * assign the intpin to an input pin on the PCI interrupt
- * router.
- */
- if (si->si_intpins[bestpin].ii_count == 0) {
- irq = ioapic_pci_alloc_irq();
- if (irq < 0)
- return (-1);
- si->si_intpins[bestpin].ii_ioapic_irq = irq;
- } else
- irq = si->si_intpins[bestpin].ii_ioapic_irq;
si->si_intpins[bestpin].ii_count++;
-
pi->pi_lintr.pin = bestpin + 1;
- pi->pi_lintr.ioapic_irq = irq;
- pci_set_cfgdata8(pi, PCIR_INTLINE, irq);
pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1);
- return (0);
+}
+
+static void
+pci_lintr_route(struct pci_devinst *pi)
+{
+ struct businfo *bi;
+ struct intxinfo *ii;
+
+ if (pi->pi_lintr.pin == 0)
+ return;
+
+ bi = pci_businfo[pi->pi_bus];
+ assert(bi != NULL);
+ ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1];
+
+ /*
+ * Attempt to allocate an I/O APIC pin for this intpin if one
+ * is not yet assigned.
+ */
+ if (ii->ii_ioapic_irq == 0)
+ ii->ii_ioapic_irq = ioapic_pci_alloc_irq();
+ assert(ii->ii_ioapic_irq > 0);
+
+ /*
+ * Attempt to allocate a PIRQ pin for this intpin if one is
+ * not yet assigned.
+ */
+ if (ii->ii_pirq_pin == 0)
+ ii->ii_pirq_pin = pirq_alloc_pin(pi->pi_vmctx);
+ assert(ii->ii_pirq_pin > 0);
+
+ pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq;
+ pi->pi_lintr.pirq_pin = ii->ii_pirq_pin;
+ pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin));
}
void
@@ -1402,8 +1483,7 @@ pci_lintr_assert(struct pci_devinst *pi)
if (pi->pi_lintr.state == IDLE) {
if (pci_lintr_permitted(pi)) {
pi->pi_lintr.state = ASSERTED;
- vm_ioapic_assert_irq(pi->pi_vmctx,
- pi->pi_lintr.ioapic_irq);
+ pci_irq_assert(pi);
} else
pi->pi_lintr.state = PENDING;
}
@@ -1419,7 +1499,7 @@ pci_lintr_deassert(struct pci_devinst *pi)
pthread_mutex_lock(&pi->pi_lintr.lock);
if (pi->pi_lintr.state == ASSERTED) {
pi->pi_lintr.state = IDLE;
- vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
+ pci_irq_deassert(pi);
} else if (pi->pi_lintr.state == PENDING)
pi->pi_lintr.state = IDLE;
pthread_mutex_unlock(&pi->pi_lintr.lock);
@@ -1431,11 +1511,11 @@ pci_lintr_update(struct pci_devinst *pi)
pthread_mutex_lock(&pi->pi_lintr.lock);
if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) {
- vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
+ pci_irq_deassert(pi);
pi->pi_lintr.state = PENDING;
} else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) {
pi->pi_lintr.state = ASSERTED;
- vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
+ pci_irq_assert(pi);
}
pthread_mutex_unlock(&pi->pi_lintr.lock);
}
@@ -1475,7 +1555,8 @@ pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg)
for (pin = 0; pin < 4; pin++) {
ii = &si->si_intpins[pin];
if (ii->ii_count != 0)
- cb(bus, slot, pin + 1, ii->ii_ioapic_irq, arg);
+ cb(bus, slot, pin + 1, ii->ii_pirq_pin,
+ ii->ii_ioapic_irq, arg);
}
}
}
@@ -1772,20 +1853,6 @@ INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata);
INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata);
INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata);
-/*
- * I/O ports to configure PCI IRQ routing. We ignore all writes to it.
- */
-static int
-pci_irq_port_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
- uint32_t *eax, void *arg)
-{
- assert(in == 0);
- return (0);
-}
-INOUT_PORT(pci_irq, 0xC00, IOPORT_F_OUT, pci_irq_port_handler);
-INOUT_PORT(pci_irq, 0xC01, IOPORT_F_OUT, pci_irq_port_handler);
-SYSRES_IO(0xC00, 2);
-
#define PCI_EMUL_TEST
#ifdef PCI_EMUL_TEST
/*
diff --git a/usr.sbin/bhyve/pci_emul.h b/usr.sbin/bhyve/pci_emul.h
index e1040a8..866ffc5 100644
--- a/usr.sbin/bhyve/pci_emul.h
+++ b/usr.sbin/bhyve/pci_emul.h
@@ -120,6 +120,7 @@ struct pci_devinst {
struct {
int8_t pin;
enum lintr_stat state;
+ int pirq_pin;
int ioapic_irq;
pthread_mutex_t lock;
} pi_lintr;
@@ -200,7 +201,8 @@ struct pciecap {
uint16_t slot_status2;
} __packed;
-typedef void (*pci_lintr_cb)(int b, int s, int pin, int ioapic_irq, void *arg);
+typedef void (*pci_lintr_cb)(int b, int s, int pin, int pirq_pin,
+ int ioapic_irq, void *arg);
int init_pci(struct vmctx *ctx);
void msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
@@ -218,7 +220,7 @@ void pci_generate_msi(struct pci_devinst *pi, int msgnum);
void pci_generate_msix(struct pci_devinst *pi, int msgnum);
void pci_lintr_assert(struct pci_devinst *pi);
void pci_lintr_deassert(struct pci_devinst *pi);
-int pci_lintr_request(struct pci_devinst *pi);
+void pci_lintr_request(struct pci_devinst *pi);
int pci_msi_enabled(struct pci_devinst *pi);
int pci_msix_enabled(struct pci_devinst *pi);
int pci_msix_table_bar(struct pci_devinst *pi);
diff --git a/usr.sbin/bhyve/pci_irq.c b/usr.sbin/bhyve/pci_irq.c
new file mode 100644
index 0000000..653aeb0
--- /dev/null
+++ b/usr.sbin/bhyve/pci_irq.c
@@ -0,0 +1,349 @@
+/*-
+ * Copyright (c) 2014 Advanced Computing Technologies LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <machine/vmm.h>
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <vmmapi.h>
+
+#include "acpi.h"
+#include "inout.h"
+#include "pci_emul.h"
+#include "pci_irq.h"
+#include "pci_lpc.h"
+
+/*
+ * Implement an 8 pin PCI interrupt router compatible with the router
+ * present on Intel's ICH10 chip.
+ */
+
+/* Fields in each PIRQ register. */
+#define PIRQ_DIS 0x80
+#define PIRQ_IRQ 0x0f
+
+/* Only IRQs 3-7, 9-12, and 14-15 are permitted. */
+#define PERMITTED_IRQS 0xdef8
+#define IRQ_PERMITTED(irq) (((1U << (irq)) & PERMITTED_IRQS) != 0)
+
+/* IRQ count to disable an IRQ. */
+#define IRQ_DISABLED 0xff
+
+static struct pirq {
+ uint8_t reg;
+ int use_count;
+ int active_count;
+ pthread_mutex_t lock;
+} pirqs[8];
+
+static u_char irq_counts[16];
+static int pirq_cold = 1;
+
+/*
+ * Returns true if this pin is enabled with a valid IRQ. Setting the
+ * register to a reserved IRQ causes interrupts to not be asserted as
+ * if the pin was disabled.
+ */
+static bool
+pirq_valid_irq(int reg)
+{
+
+ if (reg & PIRQ_DIS)
+ return (false);
+ return (IRQ_PERMITTED(reg & PIRQ_IRQ));
+}
+
+uint8_t
+pirq_read(int pin)
+{
+
+ assert(pin > 0 && pin <= nitems(pirqs));
+ return (pirqs[pin - 1].reg);
+}
+
+void
+pirq_write(struct vmctx *ctx, int pin, uint8_t val)
+{
+ struct pirq *pirq;
+
+ assert(pin > 0 && pin <= nitems(pirqs));
+ pirq = &pirqs[pin - 1];
+ pthread_mutex_lock(&pirq->lock);
+ if (pirq->reg != (val & (PIRQ_DIS | PIRQ_IRQ))) {
+ if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
+ vm_isa_deassert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
+ pirq->reg = val & (PIRQ_DIS | PIRQ_IRQ);
+ if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
+ vm_isa_assert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
+ }
+ pthread_mutex_unlock(&pirq->lock);
+}
+
+void
+pci_irq_reserve(int irq)
+{
+
+ assert(irq < nitems(irq_counts));
+ assert(pirq_cold);
+ assert(irq_counts[irq] == 0 || irq_counts[irq] == IRQ_DISABLED);
+ irq_counts[irq] = IRQ_DISABLED;
+}
+
+void
+pci_irq_use(int irq)
+{
+
+ assert(irq < nitems(irq_counts));
+ assert(pirq_cold);
+ if (irq_counts[irq] != IRQ_DISABLED)
+ irq_counts[irq]++;
+}
+
+void
+pci_irq_init(struct vmctx *ctx)
+{
+ int i;
+
+ for (i = 0; i < nitems(pirqs); i++) {
+ pirqs[i].reg = PIRQ_DIS;
+ pirqs[i].use_count = 0;
+ pirqs[i].active_count = 0;
+ pthread_mutex_init(&pirqs[i].lock, NULL);
+ }
+ for (i = 0; i < nitems(irq_counts); i++) {
+ if (IRQ_PERMITTED(i))
+ irq_counts[i] = 0;
+ else
+ irq_counts[i] = IRQ_DISABLED;
+ }
+}
+
+void
+pci_irq_assert(struct pci_devinst *pi)
+{
+ struct pirq *pirq;
+
+ if (pi->pi_lintr.pirq_pin > 0) {
+ assert(pi->pi_lintr.pirq_pin <= nitems(pirqs));
+ pirq = &pirqs[pi->pi_lintr.pirq_pin - 1];
+ pthread_mutex_lock(&pirq->lock);
+ pirq->active_count++;
+ if (pirq->active_count == 1 && pirq_valid_irq(pirq->reg)) {
+ vm_isa_assert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
+ pi->pi_lintr.ioapic_irq);
+ pthread_mutex_unlock(&pirq->lock);
+ return;
+ }
+ pthread_mutex_unlock(&pirq->lock);
+ }
+ vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
+}
+
+void
+pci_irq_deassert(struct pci_devinst *pi)
+{
+ struct pirq *pirq;
+
+ if (pi->pi_lintr.pirq_pin > 0) {
+ assert(pi->pi_lintr.pirq_pin <= nitems(pirqs));
+ pirq = &pirqs[pi->pi_lintr.pirq_pin - 1];
+ pthread_mutex_lock(&pirq->lock);
+ pirq->active_count--;
+ if (pirq->active_count == 0 && pirq_valid_irq(pirq->reg)) {
+ vm_isa_deassert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
+ pi->pi_lintr.ioapic_irq);
+ pthread_mutex_unlock(&pirq->lock);
+ return;
+ }
+ pthread_mutex_unlock(&pirq->lock);
+ }
+ vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
+}
+
+int
+pirq_alloc_pin(struct vmctx *ctx)
+{
+ int best_count, best_irq, best_pin, irq, pin;
+
+ pirq_cold = 1;
+
+ /* First, find the least-used PIRQ pin. */
+ best_pin = 0;
+ best_count = pirqs[0].use_count;
+ for (pin = 1; pin < nitems(pirqs); pin++) {
+ if (pirqs[pin].use_count < best_count) {
+ best_pin = pin;
+ best_count = pirqs[pin].use_count;
+ }
+ }
+ pirqs[best_pin].use_count++;
+
+ /* Second, route this pin to an IRQ. */
+ if (pirqs[best_pin].reg == PIRQ_DIS) {
+ best_irq = -1;
+ best_count = 0;
+ for (irq = 0; irq < nitems(irq_counts); irq++) {
+ if (irq_counts[irq] == IRQ_DISABLED)
+ continue;
+ if (best_irq == -1 || irq_counts[irq] < best_count) {
+ best_irq = irq;
+ best_count = irq_counts[irq];
+ }
+ }
+ assert(best_irq != 0);
+ irq_counts[best_irq]++;
+ pirqs[best_pin].reg = best_irq;
+ vm_isa_set_irq_trigger(ctx, best_irq, LEVEL_TRIGGER);
+ }
+
+ return (best_pin + 1);
+}
+
+int
+pirq_irq(int pin)
+{
+
+ if (pin == -1)
+ return (255);
+ assert(pin > 0 && pin <= nitems(pirqs));
+ return (pirqs[pin - 1].reg & PIRQ_IRQ);
+}
+
+/* XXX: Generate $PIR table. */
+
+static void
+pirq_dsdt(void)
+{
+ char *irq_prs, *old;
+ int irq, pin;
+
+ irq_prs = NULL;
+ for (irq = 0; irq < nitems(irq_counts); irq++) {
+ if (!IRQ_PERMITTED(irq))
+ continue;
+ if (irq_prs == NULL)
+ asprintf(&irq_prs, "%d", irq);
+ else {
+ old = irq_prs;
+ asprintf(&irq_prs, "%s,%d", old, irq);
+ free(old);
+ }
+ }
+
+ /*
+ * A helper method to validate a link register's value. This
+ * duplicates pirq_valid_irq().
+ */
+ dsdt_line("");
+ dsdt_line("Method (PIRV, 1, NotSerialized)");
+ dsdt_line("{");
+ dsdt_line(" If (And (Arg0, 0x%02X))", PIRQ_DIS);
+ dsdt_line(" {");
+ dsdt_line(" Return (0x00)");
+ dsdt_line(" }");
+ dsdt_line(" And (Arg0, 0x%02X, Local0)", PIRQ_IRQ);
+ dsdt_line(" If (LLess (Local0, 0x03))");
+ dsdt_line(" {");
+ dsdt_line(" Return (0x00)");
+ dsdt_line(" }");
+ dsdt_line(" If (LEqual (Local0, 0x08))");
+ dsdt_line(" {");
+ dsdt_line(" Return (0x00)");
+ dsdt_line(" }");
+ dsdt_line(" If (LEqual (Local0, 0x0D))");
+ dsdt_line(" {");
+ dsdt_line(" Return (0x00)");
+ dsdt_line(" }");
+ dsdt_line(" Return (0x01)");
+ dsdt_line("}");
+
+ for (pin = 0; pin < nitems(pirqs); pin++) {
+ dsdt_line("");
+ dsdt_line("Device (LNK%c)", 'A' + pin);
+ dsdt_line("{");
+ dsdt_line(" Name (_HID, EisaId (\"PNP0C0F\"))");
+ dsdt_line(" Name (_UID, 0x%02X)", pin + 1);
+ dsdt_line(" Method (_STA, 0, NotSerialized)");
+ dsdt_line(" {");
+ dsdt_line(" If (PIRV (PIR%c))", 'A' + pin);
+ dsdt_line(" {");
+ dsdt_line(" Return (0x0B)");
+ dsdt_line(" }");
+ dsdt_line(" Else");
+ dsdt_line(" {");
+ dsdt_line(" Return (0x09)");
+ dsdt_line(" }");
+ dsdt_line(" }");
+ dsdt_line(" Name (_PRS, ResourceTemplate ()");
+ dsdt_line(" {");
+ dsdt_line(" IRQ (Level, ActiveLow, Shared, )");
+ dsdt_line(" {%s}", irq_prs);
+ dsdt_line(" })");
+ dsdt_line(" Name (CB%02X, ResourceTemplate ()", pin + 1);
+ dsdt_line(" {");
+ dsdt_line(" IRQ (Level, ActiveLow, Shared, )");
+ dsdt_line(" {}");
+ dsdt_line(" })");
+ dsdt_line(" CreateWordField (CB%02X, 0x01, CIR%c)",
+ pin + 1, 'A' + pin);
+ dsdt_line(" Method (_CRS, 0, NotSerialized)");
+ dsdt_line(" {");
+ dsdt_line(" And (PIR%c, 0x%02X, Local0)", 'A' + pin,
+ PIRQ_DIS | PIRQ_IRQ);
+ dsdt_line(" If (PIRV (Local0))");
+ dsdt_line(" {");
+ dsdt_line(" ShiftLeft (0x01, Local0, CIR%c)", 'A' + pin);
+ dsdt_line(" }");
+ dsdt_line(" Else");
+ dsdt_line(" {");
+ dsdt_line(" Store (0x00, CIR%c)", 'A' + pin);
+ dsdt_line(" }");
+ dsdt_line(" Return (CB%02X)", pin + 1);
+ dsdt_line(" }");
+ dsdt_line(" Method (_DIS, 0, NotSerialized)");
+ dsdt_line(" {");
+ dsdt_line(" Store (0x80, PIR%c)", 'A' + pin);
+ dsdt_line(" }");
+ dsdt_line(" Method (_SRS, 1, NotSerialized)");
+ dsdt_line(" {");
+ dsdt_line(" CreateWordField (Arg0, 0x01, SIR%c)", 'A' + pin);
+ dsdt_line(" FindSetRightBit (SIR%c, Local0)", 'A' + pin);
+ dsdt_line(" Store (Decrement (Local0), PIR%c)", 'A' + pin);
+ dsdt_line(" }");
+ dsdt_line("}");
+ }
+ free(irq_prs);
+}
+LPC_DSDT(pirq_dsdt);
diff --git a/usr.sbin/bhyve/pci_irq.h b/usr.sbin/bhyve/pci_irq.h
new file mode 100644
index 0000000..9d331a5
--- /dev/null
+++ b/usr.sbin/bhyve/pci_irq.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 2014 Advanced Computing Technologies LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __PCI_IRQ_H__
+#define __PCI_IRQ_H__
+
+struct pci_devinst;
+
+void pci_irq_assert(struct pci_devinst *pi);
+void pci_irq_deassert(struct pci_devinst *pi);
+void pci_irq_init(struct vmctx *ctx);
+void pci_irq_reserve(int irq);
+void pci_irq_use(int irq);
+int pirq_alloc_pin(struct vmctx *ctx);
+int pirq_irq(int pin);
+uint8_t pirq_read(int pin);
+void pirq_write(struct vmctx *ctx, int pin, uint8_t val);
+
+#endif
diff --git a/usr.sbin/bhyve/pci_lpc.c b/usr.sbin/bhyve/pci_lpc.c
index f5e4a69..6b61b7a 100644
--- a/usr.sbin/bhyve/pci_lpc.c
+++ b/usr.sbin/bhyve/pci_lpc.c
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
#include "acpi.h"
#include "inout.h"
#include "pci_emul.h"
+#include "pci_irq.h"
#include "pci_lpc.h"
#include "uart_emul.h"
@@ -173,6 +174,7 @@ lpc_init(void)
"LPC device %s\n", name);
return (-1);
}
+ pci_irq_reserve(sc->irq);
sc->uart_softc = uart_init(lpc_uart_intr_assert,
lpc_uart_intr_deassert, sc);
@@ -208,7 +210,21 @@ pci_lpc_write_dsdt(struct pci_devinst *pi)
dsdt_line("Device (ISA)");
dsdt_line("{");
dsdt_line(" Name (_ADR, 0x%04X%04X)", pi->pi_slot, pi->pi_func);
- dsdt_line(" OperationRegion (P40C, PCI_Config, 0x60, 0x04)");
+ dsdt_line(" OperationRegion (LPCR, PCI_Config, 0x00, 0x100)");
+ dsdt_line(" Field (LPCR, AnyAcc, NoLock, Preserve)");
+ dsdt_line(" {");
+ dsdt_line(" Offset (0x60),");
+ dsdt_line(" PIRA, 8,");
+ dsdt_line(" PIRB, 8,");
+ dsdt_line(" PIRC, 8,");
+ dsdt_line(" PIRD, 8,");
+ dsdt_line(" Offset (0x68),");
+ dsdt_line(" PIRE, 8,");
+ dsdt_line(" PIRF, 8,");
+ dsdt_line(" PIRG, 8,");
+ dsdt_line(" PIRH, 8");
+ dsdt_line(" }");
+ dsdt_line("");
dsdt_indent(1);
SET_FOREACH(ldpp, lpc_dsdt_set) {
@@ -305,13 +321,34 @@ pci_lpc_uart_dsdt(void)
}
LPC_DSDT(pci_lpc_uart_dsdt);
+static int
+pci_lpc_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
+ int coff, int bytes, uint32_t val)
+{
+ int pirq_pin;
+
+ if (bytes == 1) {
+ pirq_pin = 0;
+ if (coff >= 0x60 && coff <= 0x63)
+ pirq_pin = coff - 0x60 + 1;
+ if (coff >= 0x68 && coff <= 0x6b)
+ pirq_pin = coff - 0x68 + 5;
+ if (pirq_pin != 0) {
+ pirq_write(ctx, pirq_pin, val);
+ pci_set_cfgdata8(pi, coff, pirq_read(pirq_pin));
+ return (0);
+ }
+ }
+ return (-1);
+}
+
static void
pci_lpc_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
int baridx, uint64_t offset, int size, uint64_t value)
{
}
-uint64_t
+static uint64_t
pci_lpc_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
int baridx, uint64_t offset, int size)
{
@@ -324,6 +361,7 @@ pci_lpc_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
static int
pci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
{
+
/*
* Do not allow more than one LPC bridge to be configured.
*/
@@ -356,10 +394,36 @@ pci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
return (0);
}
+char *
+lpc_pirq_name(int pin)
+{
+ char *name;
+
+ if (lpc_bridge == NULL)
+ return (NULL);
+ asprintf(&name, "\\_SB.PC00.ISA.LNK%c,", 'A' + pin - 1);
+ return (name);
+}
+
+void
+lpc_pirq_routed(void)
+{
+ int pin;
+
+ if (lpc_bridge == NULL)
+ return;
+
+ for (pin = 0; pin < 4; pin++)
+ pci_set_cfgdata8(lpc_bridge, 0x60 + pin, pirq_read(pin + 1));
+ for (pin = 0; pin < 4; pin++)
+ pci_set_cfgdata8(lpc_bridge, 0x68 + pin, pirq_read(pin + 5));
+}
+
struct pci_devemu pci_de_lpc = {
.pe_emu = "lpc",
.pe_init = pci_lpc_init,
.pe_write_dsdt = pci_lpc_write_dsdt,
+ .pe_cfgwrite = pci_lpc_cfgwrite,
.pe_barwrite = pci_lpc_write,
.pe_barread = pci_lpc_read
};
diff --git a/usr.sbin/bhyve/pci_lpc.h b/usr.sbin/bhyve/pci_lpc.h
index e45bcb9..55a5865 100644
--- a/usr.sbin/bhyve/pci_lpc.h
+++ b/usr.sbin/bhyve/pci_lpc.h
@@ -66,5 +66,7 @@ struct lpc_sysres {
#define SYSRES_MEM(base, length) LPC_SYSRES(LPC_SYSRES_MEM, base, length)
int lpc_device_parse(const char *opt);
+char *lpc_pirq_name(int pin);
+void lpc_pirq_routed(void);
#endif
diff --git a/usr.sbin/bhyve/pm.c b/usr.sbin/bhyve/pm.c
index 99087e4..67126d8 100644
--- a/usr.sbin/bhyve/pm.c
+++ b/usr.sbin/bhyve/pm.c
@@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$");
#include "acpi.h"
#include "inout.h"
#include "mevent.h"
+#include "pci_irq.h"
#include "pci_lpc.h"
static pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -289,3 +290,15 @@ smi_cmd_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
}
INOUT_PORT(smi_cmd, SMI_CMD, IOPORT_F_OUT, smi_cmd_handler);
SYSRES_IO(SMI_CMD, 1);
+
+void
+sci_init(struct vmctx *ctx)
+{
+
+ /*
+ * Mark ACPI's SCI as level trigger and bump its use count
+ * in the PIRQ router.
+ */
+ pci_irq_use(SCI_INT);
+ vm_isa_set_irq_trigger(ctx, SCI_INT, LEVEL_TRIGGER);
+}
OpenPOWER on IntegriCloud