aboutsummaryrefslogtreecommitdiff
path: root/hw/intc/riscv_aplic.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/intc/riscv_aplic.c')
-rw-r--r--hw/intc/riscv_aplic.c198
1 files changed, 131 insertions, 67 deletions
diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c
index 32edd6d..8bcd9f4 100644
--- a/hw/intc/riscv_aplic.c
+++ b/hw/intc/riscv_aplic.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"
@@ -30,8 +30,9 @@
#include "hw/intc/riscv_aplic.h"
#include "hw/irq.h"
#include "target/riscv/cpu.h"
-#include "sysemu/sysemu.h"
-#include "sysemu/kvm.h"
+#include "system/system.h"
+#include "system/kvm.h"
+#include "system/tcg.h"
#include "kvm/kvm_riscv.h"
#include "migration/vmstate.h"
@@ -154,36 +155,76 @@
* KVM AIA only supports APLIC MSI, fallback to QEMU emulation if we want to use
* APLIC Wired.
*/
-static bool is_kvm_aia(bool msimode)
+bool riscv_is_kvm_aia_aplic_imsic(bool msimode)
{
return kvm_irqchip_in_kernel() && msimode;
}
+bool riscv_use_emulated_aplic(bool msimode)
+{
+#ifdef CONFIG_KVM
+ if (tcg_enabled()) {
+ return true;
+ }
+
+ if (!riscv_is_kvm_aia_aplic_imsic(msimode)) {
+ return true;
+ }
+
+ return kvm_kernel_irqchip_split();
+#else
+ return true;
+#endif
+}
+
+void riscv_aplic_set_kvm_msicfgaddr(RISCVAPLICState *aplic, hwaddr addr)
+{
+#ifdef CONFIG_KVM
+ if (riscv_use_emulated_aplic(aplic->msimode)) {
+ addr >>= APLIC_xMSICFGADDR_PPN_SHIFT;
+ aplic->kvm_msicfgaddr = extract64(addr, 0, 32);
+ aplic->kvm_msicfgaddrH = extract64(addr, 32, 32) &
+ APLIC_xMSICFGADDRH_VALID_MASK;
+ }
+#endif
+}
+
+static bool riscv_aplic_irq_rectified_val(RISCVAPLICState *aplic,
+ uint32_t irq)
+{
+ uint32_t sourcecfg, sm, raw_input, irq_inverted;
+
+ if (!irq || aplic->num_irqs <= irq) {
+ return false;
+ }
+
+ sourcecfg = aplic->sourcecfg[irq];
+ if (sourcecfg & APLIC_SOURCECFG_D) {
+ return false;
+ }
+
+ sm = sourcecfg & APLIC_SOURCECFG_SM_MASK;
+ if (sm == APLIC_SOURCECFG_SM_INACTIVE) {
+ return false;
+ }
+
+ raw_input = (aplic->state[irq] & APLIC_ISTATE_INPUT) ? 1 : 0;
+ irq_inverted = (sm == APLIC_SOURCECFG_SM_LEVEL_LOW ||
+ sm == APLIC_SOURCECFG_SM_EDGE_FALL) ? 1 : 0;
+
+ return !!(raw_input ^ irq_inverted);
+}
+
static uint32_t riscv_aplic_read_input_word(RISCVAPLICState *aplic,
uint32_t word)
{
- uint32_t i, irq, sourcecfg, sm, raw_input, irq_inverted, ret = 0;
+ uint32_t i, irq, rectified_val, ret = 0;
for (i = 0; i < 32; i++) {
irq = word * 32 + i;
- if (!irq || aplic->num_irqs <= irq) {
- continue;
- }
- sourcecfg = aplic->sourcecfg[irq];
- if (sourcecfg & APLIC_SOURCECFG_D) {
- continue;
- }
-
- sm = sourcecfg & APLIC_SOURCECFG_SM_MASK;
- if (sm == APLIC_SOURCECFG_SM_INACTIVE) {
- continue;
- }
-
- raw_input = (aplic->state[irq] & APLIC_ISTATE_INPUT) ? 1 : 0;
- irq_inverted = (sm == APLIC_SOURCECFG_SM_LEVEL_LOW ||
- sm == APLIC_SOURCECFG_SM_EDGE_FALL) ? 1 : 0;
- ret |= (raw_input ^ irq_inverted) << i;
+ rectified_val = riscv_aplic_irq_rectified_val(aplic, irq);
+ ret |= rectified_val << i;
}
return ret;
@@ -237,9 +278,12 @@ static void riscv_aplic_set_pending(RISCVAPLICState *aplic,
if ((sm == APLIC_SOURCECFG_SM_LEVEL_HIGH) ||
(sm == APLIC_SOURCECFG_SM_LEVEL_LOW)) {
- if (!aplic->msimode || (aplic->msimode && !pending)) {
+ if (!aplic->msimode) {
return;
}
+ if (aplic->msimode && !pending) {
+ goto noskip_write_pending;
+ }
if ((aplic->state[irq] & APLIC_ISTATE_INPUT) &&
(sm == APLIC_SOURCECFG_SM_LEVEL_LOW)) {
return;
@@ -250,6 +294,7 @@ static void riscv_aplic_set_pending(RISCVAPLICState *aplic,
}
}
+noskip_write_pending:
riscv_aplic_set_pending_raw(aplic, irq, pending);
}
@@ -348,21 +393,29 @@ static void riscv_aplic_msi_send(RISCVAPLICState *aplic,
uint32_t lhxs, lhxw, hhxs, hhxw, group_idx, msicfgaddr, msicfgaddrH;
aplic_m = aplic;
- while (aplic_m && !aplic_m->mmode) {
- aplic_m = aplic_m->parent;
- }
- if (!aplic_m) {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: m-level APLIC not found\n",
- __func__);
- return;
+
+ if (!aplic->kvm_splitmode) {
+ while (aplic_m && !aplic_m->mmode) {
+ aplic_m = aplic_m->parent;
+ }
+ if (!aplic_m) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: m-level APLIC not found\n",
+ __func__);
+ return;
+ }
}
- if (aplic->mmode) {
- msicfgaddr = aplic_m->mmsicfgaddr;
- msicfgaddrH = aplic_m->mmsicfgaddrH;
+ if (aplic->kvm_splitmode) {
+ msicfgaddr = aplic->kvm_msicfgaddr;
+ msicfgaddrH = ((uint64_t)aplic->kvm_msicfgaddrH << 32);
} else {
- msicfgaddr = aplic_m->smsicfgaddr;
- msicfgaddrH = aplic_m->smsicfgaddrH;
+ if (aplic->mmode) {
+ msicfgaddr = aplic_m->mmsicfgaddr;
+ msicfgaddrH = aplic_m->mmsicfgaddrH;
+ } else {
+ msicfgaddr = aplic_m->smsicfgaddr;
+ msicfgaddrH = aplic_m->smsicfgaddrH;
+ }
}
lhxs = (msicfgaddrH >> APLIC_xMSICFGADDRH_LHXS_SHIFT) &
@@ -375,7 +428,6 @@ static void riscv_aplic_msi_send(RISCVAPLICState *aplic,
APLIC_xMSICFGADDRH_HHXW_MASK;
group_idx = hart_idx >> lhxw;
- hart_idx &= APLIC_xMSICFGADDR_PPN_LHX_MASK(lhxw);
addr = msicfgaddr;
addr |= ((uint64_t)(msicfgaddrH & APLIC_xMSICFGADDRH_BAPPN_MASK)) << 32;
@@ -702,6 +754,10 @@ static void riscv_aplic_write(void *opaque, hwaddr addr, uint64_t value,
(aplic->sourcecfg[irq] == 0)) {
riscv_aplic_set_pending_raw(aplic, irq, false);
riscv_aplic_set_enabled_raw(aplic, irq, false);
+ } else {
+ if (riscv_aplic_irq_rectified_val(aplic, irq)) {
+ riscv_aplic_set_pending_raw(aplic, irq, true);
+ }
}
} else if (aplic->mmode && aplic->msimode &&
(addr == APLIC_MMSICFGADDR)) {
@@ -838,7 +894,27 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp)
uint32_t i;
RISCVAPLICState *aplic = RISCV_APLIC(dev);
- if (!is_kvm_aia(aplic->msimode)) {
+ if (riscv_use_emulated_aplic(aplic->msimode)) {
+ /* Create output IRQ lines for non-MSI mode */
+ if (!aplic->msimode) {
+ /* Claim the CPU interrupt to be triggered by this APLIC */
+ for (i = 0; i < aplic->num_harts; i++) {
+ RISCVCPU *cpu;
+
+ cpu = RISCV_CPU(cpu_by_arch_id(aplic->hartid_base + i));
+ if (riscv_cpu_claim_interrupts(cpu,
+ (aplic->mmode) ? MIP_MEIP : MIP_SEIP) < 0) {
+ error_report("%s already claimed",
+ (aplic->mmode) ? "MEIP" : "SEIP");
+ exit(1);
+ }
+ }
+
+ aplic->external_irqs = g_malloc(sizeof(qemu_irq) *
+ aplic->num_harts);
+ qdev_init_gpio_out(dev, aplic->external_irqs, aplic->num_harts);
+ }
+
aplic->bitfield_words = (aplic->num_irqs + 31) >> 5;
aplic->sourcecfg = g_new0(uint32_t, aplic->num_irqs);
aplic->state = g_new0(uint32_t, aplic->num_irqs);
@@ -855,6 +931,10 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp)
memory_region_init_io(&aplic->mmio, OBJECT(dev), &riscv_aplic_ops,
aplic, TYPE_RISCV_APLIC, aplic->aperture_size);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &aplic->mmio);
+
+ if (kvm_enabled()) {
+ aplic->kvm_splitmode = true;
+ }
}
/*
@@ -862,34 +942,17 @@ static void riscv_aplic_realize(DeviceState *dev, Error **errp)
* have IRQ lines delegated by their parent APLIC.
*/
if (!aplic->parent) {
- if (kvm_enabled() && is_kvm_aia(aplic->msimode)) {
+ if (kvm_enabled() && !riscv_use_emulated_aplic(aplic->msimode)) {
qdev_init_gpio_in(dev, riscv_kvm_aplic_request, aplic->num_irqs);
} else {
qdev_init_gpio_in(dev, riscv_aplic_request, aplic->num_irqs);
}
}
- /* Create output IRQ lines for non-MSI mode */
- if (!aplic->msimode) {
- aplic->external_irqs = g_malloc(sizeof(qemu_irq) * aplic->num_harts);
- qdev_init_gpio_out(dev, aplic->external_irqs, aplic->num_harts);
-
- /* Claim the CPU interrupt to be triggered by this APLIC */
- for (i = 0; i < aplic->num_harts; i++) {
- RISCVCPU *cpu = RISCV_CPU(cpu_by_arch_id(aplic->hartid_base + i));
- if (riscv_cpu_claim_interrupts(cpu,
- (aplic->mmode) ? MIP_MEIP : MIP_SEIP) < 0) {
- error_report("%s already claimed",
- (aplic->mmode) ? "MEIP" : "SEIP");
- exit(1);
- }
- }
- }
-
msi_nonbroken = true;
}
-static Property riscv_aplic_properties[] = {
+static const Property riscv_aplic_properties[] = {
DEFINE_PROP_UINT32("aperture-size", RISCVAPLICState, aperture_size, 0),
DEFINE_PROP_UINT32("hartid-base", RISCVAPLICState, hartid_base, 0),
DEFINE_PROP_UINT32("num-harts", RISCVAPLICState, num_harts, 0),
@@ -897,13 +960,12 @@ static Property riscv_aplic_properties[] = {
DEFINE_PROP_UINT32("num-irqs", RISCVAPLICState, num_irqs, 0),
DEFINE_PROP_BOOL("msimode", RISCVAPLICState, msimode, 0),
DEFINE_PROP_BOOL("mmode", RISCVAPLICState, mmode, 0),
- DEFINE_PROP_END_OF_LIST(),
};
static const VMStateDescription vmstate_riscv_aplic = {
.name = "riscv_aplic",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (const VMStateField[]) {
VMSTATE_UINT32(domaincfg, RISCVAPLICState),
VMSTATE_UINT32(mmsicfgaddr, RISCVAPLICState),
@@ -911,6 +973,8 @@ static const VMStateDescription vmstate_riscv_aplic = {
VMSTATE_UINT32(smsicfgaddr, RISCVAPLICState),
VMSTATE_UINT32(smsicfgaddrH, RISCVAPLICState),
VMSTATE_UINT32(genmsi, RISCVAPLICState),
+ VMSTATE_UINT32(kvm_msicfgaddr, RISCVAPLICState),
+ VMSTATE_UINT32(kvm_msicfgaddrH, RISCVAPLICState),
VMSTATE_VARRAY_UINT32(sourcecfg, RISCVAPLICState,
num_irqs, 0,
vmstate_info_uint32, uint32_t),
@@ -933,7 +997,7 @@ static const VMStateDescription vmstate_riscv_aplic = {
}
};
-static void riscv_aplic_class_init(ObjectClass *klass, void *data)
+static void riscv_aplic_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -1006,17 +1070,17 @@ DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size,
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
- if (!is_kvm_aia(msimode)) {
+ if (riscv_use_emulated_aplic(msimode)) {
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
- }
- if (!msimode) {
- for (i = 0; i < num_harts; i++) {
- CPUState *cpu = cpu_by_arch_id(hartid_base + i);
+ if (!msimode) {
+ for (i = 0; i < num_harts; i++) {
+ CPUState *cpu = cpu_by_arch_id(hartid_base + i);
- qdev_connect_gpio_out_named(dev, NULL, i,
- qdev_get_gpio_in(DEVICE(cpu),
+ qdev_connect_gpio_out_named(dev, NULL, i,
+ qdev_get_gpio_in(DEVICE(cpu),
(mmode) ? IRQ_M_EXT : IRQ_S_EXT));
+ }
}
}