summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mooney <pmooney@pfmooney.com>2020-07-08 03:04:47 +0000
committerPatrick Mooney <pmooney@oxide.computer>2020-09-04 14:45:15 +0000
commit5365b8a5984eca60a3cedc7f9f738e51fb056bec (patch)
treee8c9f6c4b7b88b07c5f87ce79a8bfcda02746b1f
parentfbfe962ed9b45c98bebaea25573c8b8f1630d482 (diff)
downloadillumos-joyent-5365b8a5984eca60a3cedc7f9f738e51fb056bec.tar.gz
12936 bhyve vlapic needs ability to bypass isrvec_stk
Reviewed by: Mike Zeller <mike.zeller@joyent.com> Reviewed by: Robert Mustacchi <rm@fingolfin.org> Approved by: Dan McDonald <danmcd@joyent.com>
-rw-r--r--usr/src/uts/i86pc/io/vmm/io/vlapic.c210
-rw-r--r--usr/src/uts/i86pc/io/vmm/io/vlapic_priv.h38
2 files changed, 162 insertions, 86 deletions
diff --git a/usr/src/uts/i86pc/io/vmm/io/vlapic.c b/usr/src/uts/i86pc/io/vmm/io/vlapic.c
index c1825f4264..1288f69321 100644
--- a/usr/src/uts/i86pc/io/vmm/io/vlapic.c
+++ b/usr/src/uts/i86pc/io/vmm/io/vlapic.c
@@ -96,6 +96,12 @@ __FBSDID("$FreeBSD$");
static void vlapic_set_error(struct vlapic *, uint32_t, bool);
static void vlapic_tmr_reset(struct vlapic *);
+#ifdef __ISRVEC_DEBUG
+static void vlapic_isrstk_accept(struct vlapic *, int);
+static void vlapic_isrstk_eoi(struct vlapic *, int);
+static void vlapic_isrstk_verify(const struct vlapic *);
+#endif /* __ISRVEC_DEBUG */
+
static __inline uint32_t
vlapic_get_id(struct vlapic *vlapic)
{
@@ -495,21 +501,33 @@ vlapic_fire_lvt(struct vlapic *vlapic, u_int lvt)
return (1);
}
-#if 1
-static void
-dump_isrvec_stk(struct vlapic *vlapic)
+static uint_t
+vlapic_active_isr(struct vlapic *vlapic)
{
int i;
- uint32_t *isrptr;
+ uint32_t *isrp;
- isrptr = &vlapic->apic_page->isr0;
- for (i = 0; i < 8; i++)
- printf("ISR%d 0x%08x\n", i, isrptr[i * 4]);
+ isrp = &vlapic->apic_page->isr7;
- for (i = 0; i <= vlapic->isrvec_stk_top; i++)
- printf("isrvec_stk[%d] = %d\n", i, vlapic->isrvec_stk[i]);
+ for (i = 7; i >= 0; i--, isrp -= 4) {
+ uint32_t reg = *isrp;
+
+ if (reg != 0) {
+ uint_t vec = (i * 32) + bsrl(reg);
+
+ if (vec < 16) {
+ /*
+ * Truncate the illegal low vectors to value of
+ * 0, indicating that no active ISR was found.
+ */
+ return (0);
+ }
+ return (vec);
+ }
+ }
+
+ return (0);
}
-#endif
/*
* Algorithm adopted from section "Interrupt, Task and Processor Priority"
@@ -520,55 +538,11 @@ vlapic_update_ppr(struct vlapic *vlapic)
{
int isrvec, tpr, ppr;
- /*
- * Note that the value on the stack at index 0 is always 0.
- *
- * This is a placeholder for the value of ISRV when none of the
- * bits is set in the ISRx registers.
- */
- isrvec = vlapic->isrvec_stk[vlapic->isrvec_stk_top];
+ isrvec = vlapic_active_isr(vlapic);
tpr = vlapic->apic_page->tpr;
-#if 1
- {
- int i, lastprio, curprio, vector, idx;
- uint32_t *isrptr;
-
- if (vlapic->isrvec_stk_top == 0 && isrvec != 0)
- panic("isrvec_stk is corrupted: %d", isrvec);
-
- /*
- * Make sure that the priority of the nested interrupts is
- * always increasing.
- */
- lastprio = -1;
- for (i = 1; i <= vlapic->isrvec_stk_top; i++) {
- curprio = PRIO(vlapic->isrvec_stk[i]);
- if (curprio <= lastprio) {
- dump_isrvec_stk(vlapic);
- panic("isrvec_stk does not satisfy invariant");
- }
- lastprio = curprio;
- }
-
- /*
- * Make sure that each bit set in the ISRx registers has a
- * corresponding entry on the isrvec stack.
- */
- i = 1;
- isrptr = &vlapic->apic_page->isr0;
- for (vector = 0; vector < 256; vector++) {
- idx = (vector / 32) * 4;
- if (isrptr[idx] & (1 << (vector % 32))) {
- if (i > vlapic->isrvec_stk_top ||
- vlapic->isrvec_stk[i] != vector) {
- dump_isrvec_stk(vlapic);
- panic("ISR and isrvec_stk out of sync");
- }
- i++;
- }
- }
- }
+#ifdef __ISRVEC_DEBUG
+ vlapic_isrstk_verify(vlapic);
#endif
if (PRIO(tpr) >= PRIO(isrvec))
@@ -593,25 +567,25 @@ vlapic_process_eoi(struct vlapic *vlapic)
{
struct LAPIC *lapic = vlapic->apic_page;
uint32_t *isrptr, *tmrptr;
- int i, idx, bitpos, vector;
+ int i;
+ uint_t idx, bitpos, vector;
isrptr = &lapic->isr0;
tmrptr = &lapic->tmr0;
for (i = 7; i >= 0; i--) {
idx = i * 4;
- bitpos = fls(isrptr[idx]);
- if (bitpos-- != 0) {
- if (vlapic->isrvec_stk_top <= 0) {
- panic("invalid vlapic isrvec_stk_top %d",
- vlapic->isrvec_stk_top);
- }
- isrptr[idx] &= ~(1 << bitpos);
+ if (isrptr[idx] != 0) {
+ bitpos = bsrl(isrptr[idx]);
vector = i * 32 + bitpos;
+
+ isrptr[idx] &= ~(1 << bitpos);
VCPU_CTR1(vlapic->vm, vlapic->vcpuid, "EOI vector %d",
vector);
VLAPIC_CTR_ISR(vlapic, "vlapic_process_eoi");
- vlapic->isrvec_stk_top--;
+#ifdef __ISRVEC_DEBUG
+ vlapic_isrstk_eoi(vlapic, vector);
+#endif
vlapic_update_ppr(vlapic);
if ((tmrptr[idx] & (1 << bitpos)) != 0) {
vioapic_process_eoi(vlapic->vm, vlapic->vcpuid,
@@ -1143,7 +1117,7 @@ vlapic_intr_accepted(struct vlapic *vlapic, int vector)
{
struct LAPIC *lapic = vlapic->apic_page;
uint32_t *irrptr, *isrptr;
- int idx, stk_top;
+ int idx;
if (vlapic->ops.intr_accepted)
return ((*vlapic->ops.intr_accepted)(vlapic, vector));
@@ -1162,16 +1136,9 @@ vlapic_intr_accepted(struct vlapic *vlapic, int vector)
isrptr[idx] |= 1 << (vector % 32);
VLAPIC_CTR_ISR(vlapic, "vlapic_intr_accepted");
- /*
- * Update the PPR
- */
- vlapic->isrvec_stk_top++;
-
- stk_top = vlapic->isrvec_stk_top;
- if (stk_top >= ISRVEC_STK_SIZE)
- panic("isrvec_stk_top overflow %d", stk_top);
-
- vlapic->isrvec_stk[stk_top] = vector;
+#ifdef __ISRVEC_DEBUG
+ vlapic_isrstk_accept(vlapic, vector);
+#endif
}
void
@@ -1708,3 +1675,92 @@ vlapic_localize_resources(struct vlapic *vlapic)
vmm_glue_callout_localize(&vlapic->callout);
}
#endif /* __FreeBSD */
+
+#ifdef __ISRVEC_DEBUG
+static void
+vlapic_isrstk_eoi(struct vlapic *vlapic, int vector)
+{
+ if (vlapic->isrvec_stk_top <= 0) {
+ panic("invalid vlapic isrvec_stk_top %d",
+ vlapic->isrvec_stk_top);
+ }
+ vlapic->isrvec_stk_top--;
+}
+
+static void
+vlapic_isrstk_accept(struct vlapic *vlapic, int vector)
+{
+ int stk_top;
+
+ vlapic->isrvec_stk_top++;
+
+ stk_top = vlapic->isrvec_stk_top;
+ if (stk_top >= ISRVEC_STK_SIZE)
+ panic("isrvec_stk_top overflow %d", stk_top);
+
+ vlapic->isrvec_stk[stk_top] = vector;
+}
+
+static void
+vlapic_isrstk_dump(const struct vlapic *vlapic)
+{
+ int i;
+ uint32_t *isrptr;
+
+ isrptr = &vlapic->apic_page->isr0;
+ for (i = 0; i < 8; i++)
+ printf("ISR%d 0x%08x\n", i, isrptr[i * 4]);
+
+ for (i = 0; i <= vlapic->isrvec_stk_top; i++)
+ printf("isrvec_stk[%d] = %d\n", i, vlapic->isrvec_stk[i]);
+}
+
+static void
+vlapic_isrstk_verify(const struct vlapic *vlapic)
+{
+ int i, lastprio, curprio, vector, idx;
+ uint32_t *isrptr;
+
+ /*
+ * Note: The value at index 0 in isrvec_stk is always 0.
+ *
+ * It is a placeholder for the value of ISR vector when no bits are set
+ * in the ISRx registers.
+ */
+ if (vlapic->isrvec_stk_top == 0 && vlapic->isrvec_stk[0] != 0) {
+ panic("isrvec_stk is corrupted: %d", vlapic->isrvec_stk[0]);
+ }
+
+ /*
+ * Make sure that the priority of the nested interrupts is
+ * always increasing.
+ */
+ lastprio = -1;
+ for (i = 1; i <= vlapic->isrvec_stk_top; i++) {
+ curprio = PRIO(vlapic->isrvec_stk[i]);
+ if (curprio <= lastprio) {
+ vlapic_isrstk_dump(vlapic);
+ panic("isrvec_stk does not satisfy invariant");
+ }
+ lastprio = curprio;
+ }
+
+ /*
+ * Make sure that each bit set in the ISRx registers has a
+ * corresponding entry on the isrvec stack.
+ */
+ i = 1;
+ isrptr = &vlapic->apic_page->isr0;
+ for (vector = 0; vector < 256; vector++) {
+ idx = (vector / 32) * 4;
+ if (isrptr[idx] & (1 << (vector % 32))) {
+ if (i > vlapic->isrvec_stk_top ||
+ vlapic->isrvec_stk[i] != vector) {
+ vlapic_isrstk_dump(vlapic);
+ panic("ISR and isrvec_stk out of sync");
+ }
+ i++;
+ }
+ }
+}
+#endif
diff --git a/usr/src/uts/i86pc/io/vmm/io/vlapic_priv.h b/usr/src/uts/i86pc/io/vmm/io/vlapic_priv.h
index 5795d48d52..8a0d594de3 100644
--- a/usr/src/uts/i86pc/io/vmm/io/vlapic_priv.h
+++ b/usr/src/uts/i86pc/io/vmm/io/vlapic_priv.h
@@ -27,6 +27,18 @@
*
* $FreeBSD$
*/
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * Copyright 2020 Oxide Computer Company
+ */
#ifndef _VLAPIC_PRIV_H_
#define _VLAPIC_PRIV_H_
@@ -140,6 +152,10 @@ enum boot_state {
#define VLAPIC_TMR_CNT 8
+#ifdef DEBUG
+#define __ISRVEC_DEBUG
+#endif
+
struct vlapic;
struct vlapic_ops {
@@ -166,15 +182,6 @@ struct vlapic {
struct bintime timer_period_bt; /* timer period */
struct mtx timer_mtx;
- /*
- * The 'isrvec_stk' is a stack of vectors injected by the local apic.
- * A vector is popped from the stack when the processor does an EOI.
- * The vector on the top of the stack is used to compute the
- * Processor Priority in conjunction with the TPR.
- */
- uint8_t isrvec_stk[ISRVEC_STK_SIZE];
- int isrvec_stk_top;
-
uint64_t msr_apicbase;
enum boot_state boot_state;
@@ -199,6 +206,19 @@ struct vlapic {
*/
uint32_t tmr_vec_deassert[VLAPIC_TMR_CNT];
uint32_t tmr_vec_assert[VLAPIC_TMR_CNT];
+
+#ifdef __ISRVEC_DEBUG
+ /*
+ * The 'isrvec_stk' is a stack of vectors injected by the local APIC.
+ * It is used as a debugging method to double-check the behavior of the
+ * emulation. Vectors are pushed to the stack when they are accepted
+ * for injection and popped from the stack when the processor performs
+ * an EOI. The vector on the top of the stack is used to verify the
+ * computed Processor Priority.
+ */
+ uint8_t isrvec_stk[ISRVEC_STK_SIZE];
+ int isrvec_stk_top;
+#endif
};
void vlapic_init(struct vlapic *vlapic);