aboutsummaryrefslogtreecommitdiff
path: root/hw/intc/riscv_imsic.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/intc/riscv_imsic.c')
-rw-r--r--hw/intc/riscv_imsic.c108
1 files changed, 63 insertions, 45 deletions
diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c
index b90f0d7..2169988 100644
--- a/hw/intc/riscv_imsic.c
+++ b/hw/intc/riscv_imsic.c
@@ -22,7 +22,7 @@
#include "qemu/module.h"
#include "qemu/error-report.h"
#include "qemu/bswap.h"
-#include "exec/address-spaces.h"
+#include "system/address-spaces.h"
#include "hw/sysbus.h"
#include "hw/pci/msi.h"
#include "hw/boards.h"
@@ -31,8 +31,8 @@
#include "hw/irq.h"
#include "target/riscv/cpu.h"
#include "target/riscv/cpu_bits.h"
-#include "sysemu/sysemu.h"
-#include "sysemu/kvm.h"
+#include "system/system.h"
+#include "system/kvm.h"
#include "migration/vmstate.h"
#define IMSIC_MMIO_PAGE_LE 0x00
@@ -55,7 +55,7 @@ static uint32_t riscv_imsic_topei(RISCVIMSICState *imsic, uint32_t page)
(imsic->eithreshold[page] <= imsic->num_irqs)) ?
imsic->eithreshold[page] : imsic->num_irqs;
for (i = 1; i < max_irq; i++) {
- if ((imsic->eistate[base + i] & IMSIC_EISTATE_ENPEND) ==
+ if ((qatomic_read(&imsic->eistate[base + i]) & IMSIC_EISTATE_ENPEND) ==
IMSIC_EISTATE_ENPEND) {
return (i << IMSIC_TOPEI_IID_SHIFT) | i;
}
@@ -66,10 +66,24 @@ static uint32_t riscv_imsic_topei(RISCVIMSICState *imsic, uint32_t page)
static void riscv_imsic_update(RISCVIMSICState *imsic, uint32_t page)
{
+ uint32_t base = page * imsic->num_irqs;
+
+ /*
+ * Lower the interrupt line if necessary, then evaluate the current
+ * IMSIC state.
+ * This sequence ensures that any race between evaluating the eistate and
+ * updating the interrupt line will not result in an incorrectly
+ * deactivated connected CPU IRQ line.
+ * If multiple interrupts are pending, this sequence functions identically
+ * to qemu_irq_pulse.
+ */
+
+ if (qatomic_fetch_and(&imsic->eistate[base], ~IMSIC_EISTATE_ENPEND)) {
+ qemu_irq_lower(imsic->external_irqs[page]);
+ }
if (imsic->eidelivery[page] && riscv_imsic_topei(imsic, page)) {
qemu_irq_raise(imsic->external_irqs[page]);
- } else {
- qemu_irq_lower(imsic->external_irqs[page]);
+ qatomic_or(&imsic->eistate[base], IMSIC_EISTATE_ENPEND);
}
}
@@ -125,12 +139,11 @@ static int riscv_imsic_topei_rmw(RISCVIMSICState *imsic, uint32_t page,
topei >>= IMSIC_TOPEI_IID_SHIFT;
base = page * imsic->num_irqs;
if (topei) {
- imsic->eistate[base + topei] &= ~IMSIC_EISTATE_PENDING;
+ qatomic_and(&imsic->eistate[base + topei], ~IMSIC_EISTATE_PENDING);
}
-
- riscv_imsic_update(imsic, page);
}
+ riscv_imsic_update(imsic, page);
return 0;
}
@@ -139,7 +152,7 @@ static int riscv_imsic_eix_rmw(RISCVIMSICState *imsic,
uint32_t num, bool pend, target_ulong *val,
target_ulong new_val, target_ulong wr_mask)
{
- uint32_t i, base;
+ uint32_t i, base, prev;
target_ulong mask;
uint32_t state = (pend) ? IMSIC_EISTATE_PENDING : IMSIC_EISTATE_ENABLED;
@@ -157,10 +170,6 @@ static int riscv_imsic_eix_rmw(RISCVIMSICState *imsic,
if (val) {
*val = 0;
- for (i = 0; i < xlen; i++) {
- mask = (target_ulong)1 << i;
- *val |= (imsic->eistate[base + i] & state) ? mask : 0;
- }
}
for (i = 0; i < xlen; i++) {
@@ -172,10 +181,15 @@ static int riscv_imsic_eix_rmw(RISCVIMSICState *imsic,
mask = (target_ulong)1 << i;
if (wr_mask & mask) {
if (new_val & mask) {
- imsic->eistate[base + i] |= state;
+ prev = qatomic_fetch_or(&imsic->eistate[base + i], state);
} else {
- imsic->eistate[base + i] &= ~state;
+ prev = qatomic_fetch_and(&imsic->eistate[base + i], ~state);
}
+ } else {
+ prev = qatomic_read(&imsic->eistate[base + i]);
+ }
+ if (val && (prev & state)) {
+ *val |= mask;
}
}
@@ -302,14 +316,14 @@ static void riscv_imsic_write(void *opaque, hwaddr addr, uint64_t value,
page = addr >> IMSIC_MMIO_PAGE_SHIFT;
if ((addr & (IMSIC_MMIO_PAGE_SZ - 1)) == IMSIC_MMIO_PAGE_LE) {
if (value && (value < imsic->num_irqs)) {
- imsic->eistate[(page * imsic->num_irqs) + value] |=
- IMSIC_EISTATE_PENDING;
+ qatomic_or(&imsic->eistate[(page * imsic->num_irqs) + value],
+ IMSIC_EISTATE_PENDING);
+
+ /* Update CPU external interrupt status */
+ riscv_imsic_update(imsic, page);
}
}
- /* Update CPU external interrupt status */
- riscv_imsic_update(imsic, page);
-
return;
err:
@@ -335,7 +349,19 @@ static void riscv_imsic_realize(DeviceState *dev, Error **errp)
CPUState *cpu = cpu_by_arch_id(imsic->hartid);
CPURISCVState *env = cpu ? cpu_env(cpu) : NULL;
+ /* Claim the CPU interrupt to be triggered by this IMSIC */
+ if (riscv_cpu_claim_interrupts(rcpu,
+ (imsic->mmode) ? MIP_MEIP : MIP_SEIP) < 0) {
+ error_setg(errp, "%s already claimed",
+ (imsic->mmode) ? "MEIP" : "SEIP");
+ return;
+ }
+
if (!kvm_irqchip_in_kernel()) {
+ /* Create output IRQ lines */
+ imsic->external_irqs = g_malloc(sizeof(qemu_irq) * imsic->num_pages);
+ qdev_init_gpio_out(dev, imsic->external_irqs, imsic->num_pages);
+
imsic->num_eistate = imsic->num_pages * imsic->num_irqs;
imsic->eidelivery = g_new0(uint32_t, imsic->num_pages);
imsic->eithreshold = g_new0(uint32_t, imsic->num_pages);
@@ -347,18 +373,6 @@ static void riscv_imsic_realize(DeviceState *dev, Error **errp)
IMSIC_MMIO_SIZE(imsic->num_pages));
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &imsic->mmio);
- /* Claim the CPU interrupt to be triggered by this IMSIC */
- if (riscv_cpu_claim_interrupts(rcpu,
- (imsic->mmode) ? MIP_MEIP : MIP_SEIP) < 0) {
- error_setg(errp, "%s already claimed",
- (imsic->mmode) ? "MEIP" : "SEIP");
- return;
- }
-
- /* Create output IRQ lines */
- imsic->external_irqs = g_malloc(sizeof(qemu_irq) * imsic->num_pages);
- qdev_init_gpio_out(dev, imsic->external_irqs, imsic->num_pages);
-
/* Force select AIA feature and setup CSR read-modify-write callback */
if (env) {
if (!imsic->mmode) {
@@ -367,19 +381,21 @@ static void riscv_imsic_realize(DeviceState *dev, Error **errp)
} else {
rcpu->cfg.ext_smaia = true;
}
- riscv_cpu_set_aia_ireg_rmw_fn(env, (imsic->mmode) ? PRV_M : PRV_S,
- riscv_imsic_rmw, imsic);
+
+ if (!kvm_irqchip_in_kernel()) {
+ riscv_cpu_set_aia_ireg_rmw_fn(env, (imsic->mmode) ? PRV_M : PRV_S,
+ riscv_imsic_rmw, imsic);
+ }
}
msi_nonbroken = true;
}
-static Property riscv_imsic_properties[] = {
+static const Property riscv_imsic_properties[] = {
DEFINE_PROP_BOOL("mmode", RISCVIMSICState, mmode, 0),
DEFINE_PROP_UINT32("hartid", RISCVIMSICState, hartid, 0),
DEFINE_PROP_UINT32("num-pages", RISCVIMSICState, num_pages, 0),
DEFINE_PROP_UINT32("num-irqs", RISCVIMSICState, num_irqs, 0),
- DEFINE_PROP_END_OF_LIST(),
};
static const VMStateDescription vmstate_riscv_imsic = {
@@ -400,7 +416,7 @@ static const VMStateDescription vmstate_riscv_imsic = {
}
};
-static void riscv_imsic_class_init(ObjectClass *klass, void *data)
+static void riscv_imsic_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -451,15 +467,17 @@ DeviceState *riscv_imsic_create(hwaddr addr, uint32_t hartid, bool mmode,
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
- for (i = 0; i < num_pages; i++) {
- if (!i) {
- qdev_connect_gpio_out_named(dev, NULL, i,
- qdev_get_gpio_in(DEVICE(cpu),
+ if (!kvm_irqchip_in_kernel()) {
+ for (i = 0; i < num_pages; i++) {
+ if (!i) {
+ qdev_connect_gpio_out_named(dev, NULL, i,
+ qdev_get_gpio_in(DEVICE(cpu),
(mmode) ? IRQ_M_EXT : IRQ_S_EXT));
- } else {
- qdev_connect_gpio_out_named(dev, NULL, i,
- qdev_get_gpio_in(DEVICE(cpu),
+ } else {
+ qdev_connect_gpio_out_named(dev, NULL, i,
+ qdev_get_gpio_in(DEVICE(cpu),
IRQ_LOCAL_MAX + i - 1));
+ }
}
}