vmm: Check VPPR and interrupt-window-blocking with RVI We had been checking just the full byte of RVI. However, that is not what the hardware will treat as a condition to trigger an interrupt in the guest. I didn't have a specific problem with this, but it popped up as a potential issue when I was working on IPIs. Note that vectors less than 16 in RVI won't trigger a wakeup, which is in accordance with the SDM's algorithm. The only time I've seen the RVI field less than 16 so far is during an INIT/SIPI. Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/user/vmm/vmexit.c b/user/vmm/vmexit.c index dfef6c0..bb17559 100644 --- a/user/vmm/vmexit.c +++ b/user/vmm/vmexit.c
@@ -6,6 +6,7 @@ #include <vmm/virtio_mmio.h> #include <vmm/virtio_ids.h> #include <vmm/virtio_config.h> +#include <vmm/mmio.h> #include <vmm/vmm.h> #include <parlib/arch/trap.h> #include <parlib/bitmask.h> @@ -16,11 +17,22 @@ return GET_BITMASK_BIT(gpci->posted_irq_desc, VMX_POSTED_OUTSTANDING_NOTIF); } -static bool rvi_is_set(struct guest_thread *gth) +/* Returns true if the hardware will trigger an IRQ for the guest. These + * virtual IRQs are only processed under certain situations, like vmentry, and + * posted IRQs. See 'Evaluation of Pending Virtual Interrupts' in the SDM. */ +static bool virtual_irq_is_pending(struct guest_thread *gth) { - uint8_t rvi = gth_to_vmtf(gth)->tf_guest_intr_status & 0xff; + struct vmm_gpcore_init *gpci = gth_to_gpci(gth); + uint8_t rvi, vppr; - return rvi != 0; + /* Currently, the lower 4 bits are various ways to block IRQs, e.g. blocking + * by STI. The other bits are must be 0. Presumably any new bits are types + * of IRQ blocking. */ + if (gth_to_vmtf(gth)->tf_intrinfo1) + return false; + vppr = read_mmreg32((uintptr_t)gth_to_gpci(gth)->vapic_addr + 0xa0); + rvi = gth_to_vmtf(gth)->tf_guest_intr_status & 0xff; + return (rvi & 0xf0) > (vppr & 0xf0); } /* Blocks a guest pcore / thread until it has an IRQ pending. Syncs with @@ -46,20 +58,15 @@ * actually delivered. So checking OUTSTANDING_NOTIF and RVI should * suffice. * - * Generally, we should also check GUEST_INTERRUPTIBILITY_INFO to see if - * there's some reason to not deliver the interrupt and check things like - * the VPPR (priority register). But since we're emulating a halt, mwait, - * or something else that needs to be woken by an IRQ, we can ignore that - * and just wake them up. Note that we won't actually deliver the IRQ, - * we'll just restart the guest and the hardware will deliver the virtual - * IRQ at the appropriate time. So in the event that something weird - * happens, the halt/mwait just returns spuriously. + * Note that when we see a notif or pending virtual IRQ, we don't actually + * deliver the IRQ, we'll just restart the guest and the hardware will + * deliver the virtual IRQ at the appropriate time. * * The more traditional race here is if the halt starts concurrently with * the post; that's why we sync with the mutex to make sure there is an * ordering between the actual halt (this function) and the posting. */ uth_mutex_lock(gth->halt_mtx); - while (!(pir_notif_is_set(gpci) || rvi_is_set(gth))) + while (!(pir_notif_is_set(gpci) || virtual_irq_is_pending(gth))) uth_cond_var_wait(gth->halt_cv, gth->halt_mtx); uth_mutex_unlock(gth->halt_mtx); }