*
* Copyright (c) 2011 NetApp, Inc.
* All rights reserved.
+ * Copyright (c) 2019 Joyent, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
*/
#define VLAPIC_BUS_FREQ (128 * 1024 * 1024)
+static void vlapic_set_error(struct vlapic *, uint32_t, bool);
+
static __inline uint32_t
vlapic_get_id(struct vlapic *vlapic)
{
}
if (vector < 16) {
- vlapic_set_error(vlapic, APIC_ESR_RECEIVE_ILLEGAL_VECTOR);
+ vlapic_set_error(vlapic, APIC_ESR_RECEIVE_ILLEGAL_VECTOR,
+ false);
VLAPIC_CTR1(vlapic, "vlapic ignoring interrupt to vector %d",
vector);
return (1);
}
static int
-vlapic_fire_lvt(struct vlapic *vlapic, uint32_t lvt)
+vlapic_fire_lvt(struct vlapic *vlapic, u_int lvt)
{
- uint32_t vec, mode;
+ uint32_t mode, reg, vec;
- if (lvt & APIC_LVT_M)
- return (0);
+ reg = atomic_load_acq_32(&vlapic->lvt_last[lvt]);
- vec = lvt & APIC_LVT_VECTOR;
- mode = lvt & APIC_LVT_DM;
+ if (reg & APIC_LVT_M)
+ return (0);
+ vec = reg & APIC_LVT_VECTOR;
+ mode = reg & APIC_LVT_DM;
switch (mode) {
case APIC_LVT_DM_FIXED:
if (vec < 16) {
- vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR);
+ vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR,
+ lvt == APIC_LVT_ERROR);
return (0);
}
if (vlapic_set_intr_ready(vlapic, vec, false))
static VMM_STAT(VLAPIC_INTR_ERROR, "error interrupts generated by vlapic");
-void
-vlapic_set_error(struct vlapic *vlapic, uint32_t mask)
+static void
+vlapic_set_error(struct vlapic *vlapic, uint32_t mask, bool lvt_error)
{
- uint32_t lvt;
vlapic->esr_pending |= mask;
- if (vlapic->esr_firing)
+
+ /*
+ * Avoid infinite recursion if the error LVT itself is configured with
+ * an illegal vector.
+ */
+ if (lvt_error)
return;
- vlapic->esr_firing = 1;
- // The error LVT always uses the fixed delivery mode.
- lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_ERROR_LVT);
- if (vlapic_fire_lvt(vlapic, lvt | APIC_LVT_DM_FIXED)) {
+ if (vlapic_fire_lvt(vlapic, APIC_LVT_ERROR)) {
vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_ERROR, 1);
}
- vlapic->esr_firing = 0;
}
static VMM_STAT(VLAPIC_INTR_TIMER, "timer interrupts generated by vlapic");
static void
vlapic_fire_timer(struct vlapic *vlapic)
{
- uint32_t lvt;
KASSERT(VLAPIC_TIMER_LOCKED(vlapic), ("vlapic_fire_timer not locked"));
-
- // The timer LVT always uses the fixed delivery mode.
- lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT);
- if (vlapic_fire_lvt(vlapic, lvt | APIC_LVT_DM_FIXED)) {
+
+ if (vlapic_fire_lvt(vlapic, APIC_LVT_TIMER)) {
VLAPIC_CTR0(vlapic, "vlapic timer fired");
vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_TIMER, 1);
}
void
vlapic_fire_cmci(struct vlapic *vlapic)
{
- uint32_t lvt;
- lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_CMCI_LVT);
- if (vlapic_fire_lvt(vlapic, lvt)) {
+ if (vlapic_fire_lvt(vlapic, APIC_LVT_CMCI)) {
vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_CMC, 1);
}
}
int
vlapic_trigger_lvt(struct vlapic *vlapic, int vector)
{
- uint32_t lvt;
if (vlapic_enabled(vlapic) == false) {
/*
switch (vector) {
case APIC_LVT_LINT0:
- lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_LINT0_LVT);
- break;
case APIC_LVT_LINT1:
- lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_LINT1_LVT);
- break;
case APIC_LVT_TIMER:
- lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT);
- lvt |= APIC_LVT_DM_FIXED;
- break;
case APIC_LVT_ERROR:
- lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_ERROR_LVT);
- lvt |= APIC_LVT_DM_FIXED;
- break;
case APIC_LVT_PMC:
- lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_PERF_LVT);
- break;
case APIC_LVT_THERMAL:
- lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_THERM_LVT);
- break;
case APIC_LVT_CMCI:
- lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_CMCI_LVT);
+ if (vlapic_fire_lvt(vlapic, vector)) {
+ vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid,
+ LVTS_TRIGGERRED, vector, 1);
+ }
break;
default:
return (EINVAL);
}
- if (vlapic_fire_lvt(vlapic, lvt)) {
- vmm_stat_array_incr(vlapic->vm, vlapic->vcpuid,
- LVTS_TRIGGERRED, vector, 1);
- }
return (0);
}
mode = icrval & APIC_DELMODE_MASK;
if (mode == APIC_DELMODE_FIXED && vec < 16) {
- vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR);
+ vlapic_set_error(vlapic, APIC_ESR_SEND_ILLEGAL_VECTOR, false);
VLAPIC_CTR1(vlapic, "Ignoring invalid IPI %d", vec);
return (0);
}