* [PATCH 00/11] RISC-V: support CLIC v0.9 specification
[not found] <https://lists.gnu.org/archive/html/qemu-riscv/2024-08/msg00234.html>
@ 2024-08-14 8:27 ` Ian Brockbank
2024-08-14 8:27 ` [PATCH 03/11] hw/intc: Add CLIC device Ian Brockbank
` (7 more replies)
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
1 sibling, 8 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-14 8:27 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank
[Resending to include qemu-devel and add numbers to the patches]
This patch set gives an implementation of "RISC-V Core-Local Interrupt
Controller(CLIC) Version 0.9-draft-20210217". It comes from [1], where
you can find the pdf format or the source code.
This is based on the implementation from 2021 by Liu Zhiwei [3], who took
over the job from Michael Clark, who gave the first implementation of
clic-v0.7 specification [2]. I believe this implementation addresses all
the comments in Liu Zhiwei's RFC patch thread.
This implementation follows the CLIC 0.9-stable draft at 14 March 2024,
with the following exceptions and implementation details:
- the CLIC control registers are memory-mapped as per earlier drafts (in
particular version 0.9-draft, 20 June 2023)
- the indirect CSR control in 0.9-stable is not implemented
- the vector table can be either handler addresses (as per the spec)
or a jump table where each entry is processed as an instruction,
selectable with version number v0.9-jmp
- each hart is assigned its own CLIC block
- if PRV_S and/or PRV_M are supported, they are currently assumed to follow
the PRV_M registers; a subsequent update will address this
- support for PRV_S and PRV_M is selectable at CLIC instantiation
- PRV_S and PRV_U registers are currently separate from PRV_M; a subsequent
update will turn them into filtered views onto the PRV_M registers
- each hart is assigned its own CLIC block
- support for PRV_S and PRV_M is selectable at CLIC instantiation by
passing in a base address for the given modes; a base address of 0 is
treated as not supported
- PRV_S and PRV_U registers are mapped onto the PRV_M controls with
appropriate filtering for the access mode
- the RISCV virt machine has been updated to allow CLIC emulation by
passing "machine=virt,clic=on" on the command line; various other
parameters have been added to allow finer control of the CLIC behavior
The implementation (in jump-table mode) has been verified to match the
Cirrus Logic silicon (PRV_M only), which is based upon the Pulp
implementation [4] as of June 2023.
The implementation also includes a selection of qtests designed to verify
operation in all possible combinations of PRV_M, PRV_S and PRV_U.
[1] specification website: https://github.com/riscv/riscv-fast-interrupt.
[2] Michael Clark origin work:
https://github.com/sifive/riscv-qemu/tree/sifive-clic.
[3] RFC Patch submission by Liu Zhiwei:
https://lists.gnu.org/archive/html/qemu-devel/2021-04/msg01417.html
[4] Pulp implementation of CLIC: https://github.com/pulp-platform/clic
Ian Brockbank (11):
target/riscv: Add CLIC CSR mintstatus
target/riscv: Update CSR xintthresh in CLIC mode
hw/intc: Add CLIC device
target/riscv: Update CSR xie in CLIC mode
target/riscv: Update CSR xip in CLIC mode
target/riscv: Update CSR xtvec in CLIC mode
target/riscv: Update CSR xnxti in CLIC mode
target/riscv: Update interrupt handling in CLIC mode
target/riscv: Update interrupt return in CLIC mode
hw/riscv: add CLIC into virt machine
tests: add riscv clic qtest case and a function in qtest
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 03/11] hw/intc: Add CLIC device
2024-08-14 8:27 ` [PATCH 00/11] RISC-V: support CLIC v0.9 specification Ian Brockbank
@ 2024-08-14 8:27 ` Ian Brockbank
2024-08-14 8:27 ` [PATCH 02/11] target/riscv: Update CSR xintthresh in CLIC mode Ian Brockbank
` (6 subsequent siblings)
7 siblings, 0 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-14 8:27 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
The Core-Local Interrupt Controller (CLIC) provides low-latency,
vectored, pre-emptive interrupts for RISC-V systems.
The CLIC also supports a new Selective Hardware Vectoring feature
that allow users to optimize each interrupt for either faster
response or smaller code size.
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
---
hw/intc/Kconfig | 3 +
hw/intc/meson.build | 3 +-
hw/intc/riscv_clic.c | 1037 ++++++++++++++++++++++++++++++++++
hw/riscv/Kconfig | 1 +
include/hw/intc/riscv_clic.h | 213 +++++++
target/riscv/cpu.h | 2 +
target/riscv/cpu_bits.h | 17 +
7 files changed, 1275 insertions(+), 1 deletion(-)
create mode 100644 hw/intc/riscv_clic.c
create mode 100644 include/hw/intc/riscv_clic.h
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index dd405bdb5d..1cd4c2f58c 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -81,6 +81,9 @@ config SIFIVE_PLIC
bool
select MSI_NONBROKEN
+config RISCV_CLIC
+ bool
+
config GOLDFISH_PIC
bool
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index f4d81eb8e4..6e0df09265 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -61,7 +61,8 @@ specific_ss.add(when: 'CONFIG_RISCV_ACLINT', if_true: files('riscv_aclint.c'))
specific_ss.add(when: 'CONFIG_RISCV_APLIC', if_true: files('riscv_aplic.c'))
specific_ss.add(when: 'CONFIG_RISCV_IMSIC', if_true: files('riscv_imsic.c'))
specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.c'))
-specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c', 'xive2.c'))
+specific_ss.add(when: 'CONFIG_RISCV_CLIC', if_true: files('riscv_clic.c'))
+specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c'))
specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'],
if_true: files('xics_kvm.c'))
specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('xics_spapr.c', 'spapr_xive.c'))
diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
new file mode 100644
index 0000000000..1800e84dfd
--- /dev/null
+++ b/hw/intc/riscv_clic.c
@@ -0,0 +1,1037 @@
+/*
+ * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
+ * Copyright (c) 2024 Cirrus Logic, Inc and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This implementation follows the CLIC 0.9-stable draft at 14 March 2024,
+ * with the following exceptions and implementation details:
+ * - the CLIC control registers are memory-mapped as per earlier drafts (in
+ * particular version 0.9-draft, 20 June 2023)
+ * - the indirect CSR control in 0.9-stable is not implemented
+ * - the vector table can be either handler addresses (as per the spec)
+ or a jump table where each entry is processed as an instruction,
+ selectable with version number v0.9-jmp
+ * - each hart is assigned its own CLIC block
+ * - support for PRV_S and PRV_M is selectable at CLIC instantiation by
+ * passing in a base address for the given modes; a base address of 0 is
+ * treated as not supported
+ * - PRV_S and PRV_U registers are mapped onto the PRV_M controls with
+ * appropriate filtering for the access mode
+ *
+ * The implementation has a RISCVCLICState per hart, with a RISCVCLICView
+ * for each mode subsidiary to that. Each view knows its access mode and base
+ * address, as well as the RISCVCLICState with which it is associated.
+ *
+ * MMIO accesses go through the view, allowing the appropriate permissions to
+ * be enforced when accessing the parent RISCVCLICState for the settings.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "qemu/main-loop.h"
+#include "hw/sysbus.h"
+#include "sysemu/qtest.h"
+#include "target/riscv/cpu.h"
+#include "hw/qdev-properties.h"
+#include "hw/intc/riscv_clic.h"
+
+static const char *modeview_name[] = {
+ TYPE_RISCV_CLIC "_prv_u", /* PRV_U */
+ TYPE_RISCV_CLIC "_prv_s", /* PRV_S */
+ NULL, /* reserved */
+ TYPE_RISCV_CLIC "_prv_m", /* PRV_M */
+};
+
+/*
+ * The 2-bit trig WARL field specifies the trigger type and polarity for each
+ * interrupt input. Bit 1, trig[0], is defined as "edge-triggered"
+ * (0: level-triggered, 1: edge-triggered); while bit 2, trig[1], is defined as
+ * "negative-edge" (0: positive-edge, 1: negative-edge). (Section 3.6)
+ */
+
+static inline TRIG_TYPE
+riscv_clic_get_trigger_type(RISCVCLICState *clic, size_t irq)
+{
+ return get_field(clic->clicintattr[irq], CLIC_INTATTR_TRIG);
+}
+
+static inline bool
+riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq)
+{
+ TRIG_TYPE trig_type = riscv_clic_get_trigger_type(clic, irq);
+ return trig_type & CLIC_INTATTR_TRIG_EDGE;
+}
+
+static inline bool
+riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq)
+{
+ uint32_t shv = get_field(clic->clicintattr[irq], CLIC_INTATTR_SHV);
+ return shv && clic->shv_enabled;
+}
+
+static uint8_t
+riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
+{
+ int nlbits = min(clic->mnlbits, clic->clicintctlbits);
+
+ uint8_t mask_il = ((1 << nlbits) - 1) << (8 - nlbits);
+ uint8_t mask_padding = (1 << (8 - nlbits)) - 1;
+ /* unused level bits are set to 1 */
+ return (intctl & mask_il) | mask_padding;
+}
+
+static uint8_t
+riscv_clic_get_interrupt_priority(RISCVCLICState *clic, uint8_t intctl)
+{
+ int npbits = clic->clicintctlbits - clic->mnlbits;
+ uint8_t mask_priority = ((1 << npbits) - 1) << (8 - npbits);
+ uint8_t mask_padding = (1 << (8 - npbits)) - 1;
+
+ if (npbits < 0) {
+ return UINT8_MAX;
+ }
+ /* unused priority bits are set to 1 */
+ return (intctl & mask_priority) | mask_padding;
+}
+
+static void
+riscv_clic_intcfg_decode(RISCVCLICState *clic, uint16_t intcfg,
+ uint8_t *mode, uint8_t *level,
+ uint8_t *priority)
+{
+ *mode = intcfg >> 8;
+ *level = riscv_clic_get_interrupt_level(clic, intcfg & 0xff);
+ *priority = riscv_clic_get_interrupt_priority(clic, intcfg & 0xff);
+}
+
+static void riscv_clic_next_interrupt(void *opaque)
+{
+ /*
+ * Scan active list for highest priority pending interrupts
+ * comparing against this harts mintstatus register and interrupt
+ * the core if we have a higher priority interrupt to deliver
+ */
+ RISCVCLICState *clic = (RISCVCLICState *)opaque;
+ CPUState *cpu = cpu_by_arch_id(clic->hartid);
+ CPURISCVState *env = cpu ? cpu_env(cpu) : NULL;
+
+ if (!env) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "aclint-swi: invalid hartid: %u", clic->hartid);
+ return;
+ }
+
+ BQL_LOCK_GUARD();
+
+ int il[4] = {
+ MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
+ clic->uintthresh & 0xff), /* PRV_U */
+ MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
+ clic->sintthresh & 0xff), /* PRV_S */
+ 0, /* reserved */
+ MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
+ clic->mintthresh & 0xff) /* PRV_M */
+ };
+
+ /* Get sorted list of enabled interrupts for this hart */
+ CLICActiveInterrupt *active = clic->active_list;
+ size_t active_count = clic->active_count;
+ uint8_t mode, level, priority;
+
+ /* Loop through the enabled interrupts sorted by mode+priority+level */
+ while (active_count) {
+ riscv_clic_intcfg_decode(clic, active->intcfg, &mode, &level,
+ &priority);
+ if (mode < env->priv || (mode == env->priv && level < il[mode])) {
+ /*
+ * No pending interrupts with high enough mode+priority+level
+ * break and clear pending interrupt for this hart
+ */
+ break;
+ }
+ /* Check pending interrupt with high enough mode+priority+level */
+ if (clic->clicintip[active->irq]) {
+ /* Clean vector edge-triggered pending */
+ if (riscv_clic_is_edge_triggered(clic, active->irq) &&
+ riscv_clic_is_shv_interrupt(clic, active->irq)) {
+ clic->clicintip[active->irq] = 0;
+ }
+ /* Post pending interrupt for this hart */
+ clic->exccode = active->irq |
+ mode << RISCV_EXCP_CLIC_MODE_SHIFT |
+ level << RISCV_EXCP_CLIC_LEVEL_SHIFT;
+ qemu_set_irq(clic->cpu_irq, 1);
+ return;
+ }
+ /* Check next enabled interrupt */
+ active_count--;
+ active++;
+ }
+}
+
+/*
+ * Any interrupt i that is not accessible to S-mode or U-Mode
+ * appears as hard-wired zeros in clicintip[i], clicintie[i],
+ * clicintattr[i], and clicintctl[i].(Section 3.9)(Section 3.10)
+ */
+static bool
+riscv_clic_check_visible(RISCVCLICState *clic, int mode, int irq)
+{
+ uint8_t intattr_mode = get_field(clic->clicintattr[irq],
+ CLIC_INTATTR_MODE);
+
+ if (!clic->prv_s && !clic->prv_u) { /* M */
+ return mode == PRV_M;
+ } else if (clic->prv_s && clic->prv_u) { /* M/S/U */
+ switch (clic->nmbits) {
+ case 0:
+ return mode == PRV_M;
+ case 1:
+ return (mode == PRV_M) || (intattr_mode <= PRV_S);
+ case 2:
+ return mode >= intattr_mode;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: nmbits can only be 0 or 1 or 2 for M/S/U hart");
+ exit(1);
+ }
+ } else { /* M/S or M/U */
+ switch (clic->nmbits) {
+ case 0:
+ return mode == PRV_M;
+ case 1:
+ return (mode == PRV_M) || (intattr_mode <= PRV_S);
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: nmbits can only be 0 or 1 for M/S or M/U hart");
+ exit(1);
+ }
+ }
+ return false;
+}
+
+/*
+ * For level-triggered interrupts, software writes to pending bits are
+ * ignored completely. (Section 3.4)
+ */
+static bool
+riscv_clic_validate_intip(RISCVCLICState *clic, int irq)
+{
+ return riscv_clic_is_edge_triggered(clic, irq);
+}
+
+static void
+riscv_clic_update_intip(RISCVCLICState *clic, int irq, uint64_t value)
+{
+ clic->clicintip[irq] = !!value;
+ riscv_clic_next_interrupt(clic);
+}
+
+/*
+ * For security purpose, the field can only be set to a privilege
+ * level that is equal mode to or lower than the currently running
+ * privilege level.(Section 3.6)
+ */
+static bool riscv_clic_validate_intattr(RISCVCLICState *clic, uint8_t value)
+{
+ int mode = extract64(value, CLIC_INTATTR_MODE_SHIFT,
+ CLIC_INTATTR_MODE_WIDTH);
+
+ if (!qtest_enabled()) {
+ CPURISCVState *env = cpu_env(current_cpu);
+ if (env->priv < mode) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ * Work out the effective requested mode based on the number of nmbits.
+ *
+ * priv-modes nmbits mode Interpretation
+ * M 0 xx M-mode interrupt
+ *
+ * M/U 0 xx M-mode interrupt
+ * M/U 1 0x U-mode interrupt
+ * M/U 1 1x M-mode interrupt
+ *
+ * M/S 0 xx M-mode interrupt
+ * M/S 1 0x S-mode interrupt
+ * M/S 1 1x M-mode interrupt
+ *
+ * M/S/U 0 xx M-mode interrupt
+ * M/S/U 1 0x S-mode interrupt
+ * M/S/U 1 1x M-mode interrupt
+ * M/S/U 2 00 U-mode interrupt
+ * M/S/U 2 01 S-mode interrupt
+ * M/S/U 2 10 Reserved
+ * M/S/U 2 11 M-mode interrupt
+ *
+ * M/S/U 3 xx Reserved
+ */
+static uint8_t riscv_clic_effective_mode(RISCVCLICState *clic, uint8_t intattr)
+{
+ uint8_t mode = get_field(intattr, CLIC_INTATTR_MODE);
+
+ switch (clic->nmbits) {
+ case 0:
+ mode = PRV_M;
+ break;
+
+ case 1:
+ if (mode <= PRV_S) {
+ if (clic->prv_s) {
+ mode = PRV_S;
+ } else {
+ assert(clic->prv_u);
+ mode = PRV_U;
+ }
+ } else {
+ mode = PRV_M;
+ }
+ break;
+
+ case 2:
+ /* no modification required */
+ break;
+
+ default:
+ /* We validate nmbits so this shouldn't be possible */
+ assert(clic->nmbits <= 2);
+ }
+
+ return mode;
+}
+
+/* Return target interrupt number */
+static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
+{
+ return addr / 4;
+}
+
+/* Encode the priority and IRQ as a single sortable value */
+static inline int riscv_clic_encode_priority(const CLICActiveInterrupt *i)
+{
+ /* Highest mode+level+priority */
+ int priority = (i->intcfg & CLIC_INTCFG_MASK) << CLIC_IRQ_BITS;
+ /* Highest irq number */
+ int irq = i->irq & CLIC_IRQ_MASK;
+ /* Combined */
+ return priority | irq;
+}
+
+static int riscv_clic_active_compare(const void *a, const void *b)
+{
+ return riscv_clic_encode_priority(b) - riscv_clic_encode_priority(a);
+}
+
+static void
+riscv_clic_update_intie(RISCVCLICState *clic, int mode,
+ int irq, uint64_t new_intie)
+{
+ CLICActiveInterrupt *active_list = clic->active_list;
+
+ uint8_t old_intie = clic->clicintie[irq];
+ clic->clicintie[irq] = !!new_intie;
+
+ /* Add to or remove from list of active interrupts */
+ if (new_intie && !old_intie) {
+ uint16_t intcfg = (mode << CLIC_INTCFG_MODE_SHIFT) |
+ clic->clicintctl[irq];
+ active_list[clic->active_count].intcfg = intcfg;
+ active_list[clic->active_count].irq = irq;
+ clic->active_count++;
+ } else if (!new_intie && old_intie) {
+ CLICActiveInterrupt key = {
+ (mode << 8) | clic->clicintctl[irq], irq
+ };
+ CLICActiveInterrupt *result = bsearch(&key,
+ active_list, clic->active_count,
+ sizeof(CLICActiveInterrupt),
+ riscv_clic_active_compare);
+ assert(result);
+ size_t elem = result - active_list;
+ size_t sz = --clic->active_count - elem;
+ memmove(&result[0], &result[1], sz);
+ }
+
+ /* Sort list of active interrupts */
+ qsort(active_list, clic->active_count,
+ sizeof(CLICActiveInterrupt),
+ riscv_clic_active_compare);
+
+ riscv_clic_next_interrupt(clic);
+}
+
+static void
+riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
+ uint64_t value, unsigned size,
+ int mode, int irq)
+{
+ int req = extract32(addr, 0, 2);
+
+ /* visibility is checked in riscv_clic_write */
+
+ if (irq >= clic->num_sources) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid irq %u: 0x%" HWADDR_PRIx "\n", irq, addr);
+ return;
+ }
+
+ switch (req) {
+ case 0: /* clicintip[i] */
+ if (riscv_clic_validate_intip(clic, irq)) {
+ /*
+ * The actual pending bit is located at bit 0 (i.e., the
+ * least significant bit). In case future extensions expand the bit
+ * field, from FW perspective clicintip[i]=zero means no interrupt
+ * pending, and clicintip[i]!=0 (not just 1) indicates an
+ * interrupt is pending. (Section 3.4)
+ */
+ if (value != clic->clicintip[irq]) {
+ riscv_clic_update_intip(clic, irq, value);
+ }
+ }
+ /* Handle a 32-bit write */
+ if (size > 1) {
+ unsigned width = min(size, 4);
+ unsigned i;
+ for (i = 1; i < width; i++) {
+ uint64_t local_value = (value >> (i * 8)) & 0xFF;
+ riscv_clic_hart_write(clic, addr + i, local_value,
+ 1, mode, irq);
+ }
+ }
+ break;
+
+ case 1: /* clicintie[i] */
+ if (clic->clicintie[irq] != value) {
+ riscv_clic_update_intie(clic, mode, irq, value);
+ }
+ break;
+
+ case 2: /* clicintattr[i] */
+ uint8_t field_mode = riscv_clic_effective_mode(clic, value);
+ if (PRV_RESERVED == field_mode) {
+ field_mode = get_field(clic->clicintattr[irq],
+ CLIC_INTATTR_MODE);
+ }
+ value = set_field(value, CLIC_INTATTR_MODE, field_mode);
+ if (riscv_clic_validate_intattr(clic, value)) {
+ if (clic->clicintattr[irq] != value) {
+ clic->clicintattr[irq] = value;
+ riscv_clic_next_interrupt(clic);
+ }
+ }
+ break;
+
+ case 3: /* clicintctl[i] */
+ if (value != clic->clicintctl[irq]) {
+ clic->clicintctl[irq] = value;
+ riscv_clic_next_interrupt(clic);
+ }
+ break;
+ }
+}
+
+static uint64_t
+riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, unsigned size,
+ int mode, int irq)
+{
+ int req = extract32(addr, 0, 2);
+ int i;
+
+ /* visibility is checked in riscv_clic_read */
+
+ if (irq >= clic->num_sources) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid irq %u: 0x%" HWADDR_PRIx "\n", irq, addr);
+ return 0;
+ }
+
+ switch (req) {
+ case 0: /* clicintip[i] */
+ uint64_t retval = clic->clicintip[irq];
+ if (size > 1) {
+ /* Handle a multi-part read */
+ for (i = 1; i < size; ++i) {
+ uint64_t subval =
+ (riscv_clic_hart_read(clic, addr + i, 1, mode, irq) & 0xFF);
+ retval |= subval << (i * 8);
+ }
+ }
+ return retval;
+
+ case 1: /* clicintie[i] */
+ return clic->clicintie[irq];
+ case 2: /* clicintattr[i] */
+ /*
+ * clicintattr register layout
+ * Bits Field
+ * 7:6 mode
+ * 5:3 reserved (WPRI 0)
+ * 2:1 trig
+ * 0 shv
+ */
+ uint8_t intattr = clic->clicintattr[irq] & CLIC_INTATTR_MASK;
+ int field_mode = riscv_clic_effective_mode(clic, intattr);
+ intattr = set_field(intattr, CLIC_INTATTR_MODE, field_mode);
+ return intattr;
+
+ case 3: /* clicintctl[i] */
+ /*
+ * The implemented bits are kept left-justified in the most-significant
+ * bits of each 8-bit clicintctl[i] register, with the lower
+ * unimplemented bits treated as hardwired to 1.(Section 3.7)
+ */
+ return clic->clicintctl[irq] |
+ ((1 << (8 - clic->clicintctlbits)) - 1);
+ }
+
+ return 0;
+}
+
+static void
+riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
+{
+ RISCVCLICView *clicview = opaque;
+ RISCVCLICState *clic = clicview->clic;
+ CPUState *cpu = cpu_by_arch_id(clic->hartid);
+ CPURISCVState *env = cpu ? cpu_env(cpu) : NULL;
+ hwaddr clic_size = clic->clic_size;
+ int mode = clicview->mode, irq;
+ const char *current_mode_str = (PRV_M == env->priv) ? "PRV_M" :
+ (PRV_S == env->priv) ? "PRV_S" :
+ (PRV_U == env->priv) ? "PRV_U" :
+ "unknown";
+ const char *access_mode_str = (PRV_M == mode) ? "PRV_M" :
+ (PRV_S == mode) ? "PRV_S" :
+ (PRV_U == mode) ? "PRV_U" :
+ "unknown";
+
+ assert(addr < clic_size);
+
+ if (mode > env->priv) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid write to %s CLIC registers in %s mode\n",
+ access_mode_str, current_mode_str);
+ return;
+ }
+
+ if (addr < CLIC_INTCTL_BASE) {
+ assert(addr % 4 == 0);
+ int index = addr / 4;
+ switch (index) {
+ case 0: /* cliccfg */
+ {
+ uint8_t mnlbits = extract32(value, 0, 4);
+ uint8_t nmbits = extract32(value, 4, 2);
+ uint8_t snlbits = extract32(value, 16, 4);
+ uint8_t unlbits = extract32(value, 24, 4);
+
+ /*
+ * The 4-bit cliccfg.mnlbits WARL field.
+ * Valid values are 0—8.
+ */
+ if (mnlbits <= 8 && PRV_M == mode) {
+ clic->mnlbits = mnlbits;
+ }
+ if (clic->prv_s && snlbits <= 8 && mode >= PRV_S) {
+ clic->snlbits = snlbits;
+ }
+ if (clic->prv_u && unlbits <= 8) {
+ clic->unlbits = unlbits;
+ }
+
+ /*
+ * The nmbits field - the number of bits for the mode.
+ * Valid values are given by implemented privileges.
+ * This is only accessible in PRV_M.
+ */
+ if (PRV_M == mode) {
+ if (clic->prv_s && clic->prv_u) {
+ if (nmbits <= 2) {
+ clic->nmbits = nmbits;
+ }
+ } else if (clic->prv_s || clic->prv_u) {
+ if (nmbits <= 1) {
+ clic->nmbits = nmbits;
+ }
+ } else {
+ if (nmbits == 0) {
+ clic->nmbits = 0;
+ }
+ }
+ }
+
+ break;
+ }
+ case CLIC_INTTRIG_START ... CLIC_INTTRIG_END: /* clicinttrig */
+ {
+ uint32_t interrupt_number = value & CLIC_INTTRIG_IRQN;
+ if (interrupt_number <= clic->num_sources) {
+ value &= CLIC_INTTRIG_MASK;
+ clic->clicinttrig[index - CLIC_INTTRIG_START] = value;
+ /* TODO: How does this cause the interrupt to trigger? */
+ }
+ break;
+ }
+ case 2: /* mintthresh - only in CLIC spec v0.8 */
+ if (0 == strcmp(clic->version, "v0.8")) {
+ clic->mintthresh = value;
+ break;
+ }
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid write addr: 0x%" HWADDR_PRIx "\n",
+ addr);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid write addr: 0x%" HWADDR_PRIx "\n",
+ addr);
+ return;
+ }
+ } else {
+ addr -= CLIC_INTCTL_BASE;
+ irq = riscv_clic_get_irq(clic, addr);
+
+ if (riscv_clic_check_visible(clic, mode, irq)) {
+ riscv_clic_hart_write(clic, addr, value, size, mode, irq);
+ }
+ }
+}
+
+static uint64_t
+riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
+{
+ RISCVCLICView *clicview = opaque;
+ RISCVCLICState *clic = clicview->clic;
+ CPUState *cpu = cpu_by_arch_id(clic->hartid);
+ CPURISCVState *env = cpu ? cpu_env(cpu) : NULL;
+ hwaddr clic_size = clic->clic_size;
+ int mode = clicview->mode, irq;
+
+ assert(addr < clic_size);
+
+ if (mode > env->priv) {
+ const char *current_mode_str = (PRV_M == env->priv) ? "PRV_M" :
+ (PRV_S == env->priv) ? "PRV_S" :
+ (PRV_U == env->priv) ? "PRV_U" :
+ "unknown";
+ const char *access_mode_str = (PRV_M == mode) ? "PRV_M" :
+ (PRV_S == mode) ? "PRV_S" :
+ (PRV_U == mode) ? "PRV_U" :
+ "unknown";
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid write to %s CLIC registers in %s mode\n",
+ access_mode_str, current_mode_str);
+ return 0;
+ }
+
+ if (addr < CLIC_INTCTL_BASE) {
+ assert(addr % 4 == 0);
+ int index = addr / 4;
+ switch (index) {
+ case 0:
+ /*
+ * cliccfg register layout
+ *
+ * Bits Field
+ * 31:28 reserved (WPRI 0)
+ * 27:24 unlbits
+ * 23:20 reserved (WPRI 0)
+ * 19:16 snlbits
+ * 15:6 reserved (WPRI 0)
+ * 5:4 nmbits
+ * 3:0 mnlbits
+ */
+ uint64_t cliccfg = 0;
+ if (PRV_M == mode) {
+ cliccfg = clic->mnlbits | (clic->nmbits << 4);
+ }
+ if (clic->prv_s && mode >= PRV_S) {
+ cliccfg |= clic->snlbits << 16;
+ }
+ if (clic->prv_u && mode >= PRV_U) {
+ cliccfg |= clic->unlbits << 24;
+ }
+ return cliccfg;
+
+ case CLIC_INTTRIG_START ... CLIC_INTTRIG_END: /* clicinttrig */
+ /*
+ * clicinttrig register layout
+ *
+ * Bits Field
+ * 31 enable
+ * 30:13 reserved (WARL 0)
+ * 12:0 interrupt_number
+ */
+ uint64_t inttrig = clic->clicinttrig[index - CLIC_INTTRIG_START];
+ return inttrig & CLIC_INTTRIG_MASK;
+
+ case 2: /* mintthresh - only in CLIC spec v0.8 */
+ if (0 == strcmp(clic->version, "v0.8")) {
+ return clic->mintthresh;
+ break;
+ }
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid read : 0x%" HWADDR_PRIx "\n",
+ addr);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid read : 0x%" HWADDR_PRIx "\n",
+ addr);
+ break;
+ }
+ } else {
+ addr -= CLIC_INTCTL_BASE;
+ irq = riscv_clic_get_irq(clic, addr);
+
+ if (riscv_clic_check_visible(clic, mode, irq)) {
+ return riscv_clic_hart_read(clic, addr, size, mode, irq);
+ }
+ }
+
+ return 0;
+}
+
+static void riscv_clic_set_irq(void *opaque, int id, int level)
+{
+ RISCVCLICState *clic = opaque;
+ TRIG_TYPE type;
+
+ type = riscv_clic_get_trigger_type(clic, id);
+
+ /*
+ * In general, the edge-triggered interrupt state should be kept in pending
+ * bit, while the level-triggered interrupt should be kept in the level
+ * state of the incoming wire.
+ *
+ * For CLIC, model the level-triggered interrupt by read-only pending bit.
+ */
+ if (level) {
+ switch (type) {
+ case POSITIVE_LEVEL:
+ case POSITIVE_EDGE:
+ riscv_clic_update_intip(clic, id, level);
+ break;
+ case NEG_LEVEL:
+ riscv_clic_update_intip(clic, id, !level);
+ break;
+ case NEG_EDGE:
+ break;
+ default:
+ /* It's a 2-bit field so this shouldn't be possible */
+ assert(type <= 3);
+ }
+ } else {
+ switch (type) {
+ case POSITIVE_LEVEL:
+ riscv_clic_update_intip(clic, id, level);
+ break;
+ case POSITIVE_EDGE:
+ break;
+ case NEG_LEVEL:
+ case NEG_EDGE:
+ riscv_clic_update_intip(clic, id, !level);
+ break;
+ default:
+ /* It's a 2-bit field so this shouldn't be possible */
+ assert(type <= 3);
+ }
+ }
+}
+
+static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level)
+{
+ CPURISCVState *env = (CPURISCVState *)opaque;
+ RISCVCLICState *clic = env->clic;
+
+ if (level) {
+ env->exccode = clic->exccode;
+ cpu_interrupt(env_cpu(env), CPU_INTERRUPT_CLIC);
+ }
+}
+
+static const MemoryRegionOps riscv_clic_ops = {
+ .read = riscv_clic_read,
+ .write = riscv_clic_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 8
+ }
+};
+
+static void riscv_clic_realize(DeviceState *dev, Error **errp)
+{
+ RISCVCLICState *clic = RISCV_CLIC(dev);
+ size_t irqs = clic->num_sources;
+
+ if (clic->prv_s && clic->prv_u) {
+ clic->nmbits = 2;
+ } else if (clic->prv_s || clic->prv_u) {
+ clic->nmbits = 1;
+ } else {
+ clic->nmbits = 0;
+ }
+
+ clic->clicintip = g_new0(uint8_t, irqs);
+ clic->clicintie = g_new0(uint8_t, irqs);
+ clic->clicintattr = g_new0(uint8_t, irqs);
+ clic->clicintctl = g_new0(uint8_t, irqs);
+ clic->active_list = g_new0(CLICActiveInterrupt, irqs);
+
+ if (!clic->prv_s) {
+ clic->snlbits = 0;
+ }
+ if (!clic->prv_u) {
+ clic->unlbits = 0;
+ }
+
+ /* Allocate irqs through gpio, so that we can use qtest */
+ qdev_init_gpio_in(dev, riscv_clic_set_irq, irqs);
+ qdev_init_gpio_out(dev, &clic->cpu_irq, 1);
+
+ assert(cpu_exists(clic->hartid));
+ RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(clic->hartid));
+ qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler, &cpu->env, 1);
+ qdev_connect_gpio_out(dev, 0, irq);
+ cpu->env.clic = clic;
+}
+
+static void riscv_clic_view_realize(DeviceState *dev, Error **errp)
+{
+ RISCVCLICView *clicview = RISCV_CLIC_VIEW(dev);
+ RISCVCLICState *clic = clicview->clic;
+
+ memory_region_init_io(&clicview->mmio, OBJECT(clicview), &riscv_clic_ops,
+ clicview, TYPE_RISCV_CLIC_VIEW, clic->clic_size);
+ sysbus_init_mmio(SYS_BUS_DEVICE(clicview), &clicview->mmio);
+}
+
+static Property riscv_clic_properties[] = {
+ DEFINE_PROP_BOOL("shv-enabled", RISCVCLICState, shv_enabled, true),
+ DEFINE_PROP_BOOL("jump-table", RISCVCLICState, jump_table, false),
+ DEFINE_PROP_UINT8("mnlbits", RISCVCLICState, mnlbits, 8),
+ DEFINE_PROP_UINT8("snlbits", RISCVCLICState, snlbits, 8),
+ DEFINE_PROP_UINT8("unlbits", RISCVCLICState, unlbits, 8),
+ DEFINE_PROP_INT32("hartid", RISCVCLICState, hartid, 0),
+ DEFINE_PROP_UINT32("num-sources", RISCVCLICState, num_sources, 0),
+ DEFINE_PROP_UINT32("clic-size", RISCVCLICState, clic_size, 0),
+ DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICState, clicintctlbits, 0),
+ DEFINE_PROP_STRING("version", RISCVCLICState, version),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static Property riscv_clic_view_properties[] = {
+ DEFINE_PROP_LINK("clic", RISCVCLICView, clic,
+ TYPE_RISCV_CLIC, RISCVCLICState *),
+ DEFINE_PROP_UINT8("mode", RISCVCLICView, mode, PRV_U),
+ DEFINE_PROP_UINT64("clicbase", RISCVCLICView, clicbase, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void riscv_clic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = riscv_clic_realize;
+ device_class_set_props(dc, riscv_clic_properties);
+}
+
+static void riscv_clic_view_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = riscv_clic_view_realize;
+ device_class_set_props(dc, riscv_clic_view_properties);
+}
+
+static const TypeInfo riscv_clic_info = {
+ .name = TYPE_RISCV_CLIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RISCVCLICState),
+ .class_init = riscv_clic_class_init,
+};
+
+static const TypeInfo riscv_clic_view_info = {
+ .name = TYPE_RISCV_CLIC_VIEW,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RISCVCLICView),
+ .class_init = riscv_clic_view_init,
+};
+
+static void riscv_clic_register_types(void)
+{
+ type_register_static(&riscv_clic_info);
+ type_register_static(&riscv_clic_view_info);
+}
+
+type_init(riscv_clic_register_types)
+
+/*
+ * riscv_clic_view_create:
+ *
+ * @clic: machine-mode CLIC this is an view onto
+ * @clicbase: base address of this view CLIC memory-mapped registers
+ * @mode: the mode of the view - PRV_S or PRV_U
+ *
+ * Returns: the new view
+ */
+static RISCVCLICView *riscv_clic_view_create(RISCVCLICState *clic,
+ hwaddr clicbase, int mode)
+{
+ DeviceState *dev = qdev_new(TYPE_RISCV_CLIC_VIEW);
+ RISCVCLICView *clicview = RISCV_CLIC_VIEW(dev);
+ Object *obj = OBJECT(dev);
+ Object *clicobj = OBJECT(clic);
+
+ assert(0 != clic); /* this should exist */
+ assert(0 != clicbase); /* this should exist */
+ assert(0 == (clicbase & 0xfff)); /* base should be 4KiB-aligned */
+ assert(PRV_M == mode || PRV_S == mode || PRV_U == mode);
+
+ object_property_add_child(clicobj, modeview_name[mode], obj);
+ clicview->clic = clic;
+
+ qdev_prop_set_uint8(dev, "mode", mode);
+ qdev_prop_set_uint64(dev, "clicbase", clicbase);
+
+ if (!sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal)) {
+ object_unparent(obj);
+ return NULL;
+ }
+
+ memory_region_init_io(&clicview->mmio, OBJECT(dev), &riscv_clic_ops,
+ clicview, TYPE_RISCV_CLIC_VIEW, clic->clic_size);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, clicbase);
+
+ return clicview;
+}
+
+/*
+ * riscv_clic_create:
+ *
+ * @mclicbase: base address of PRV_M CLIC memory-mapped registers
+ * @sclicbase: base address of PRV_S CLIC memory-mapped registers
+ * @uclicbase: base address of PRV_U CLIC memory-mapped registers
+ * @hartid: the HART ID this CLIC is serving
+ * @num_sources: number of interrupts supporting by each aperture
+ * @clicintctlbits: bits are actually implemented in the clicintctl registers
+ * @version: clic version, such as "v0.9"; append "-jmp" for jump table instead
+ * of function pointers
+ *
+ * Returns: the device object
+ */
+DeviceState *riscv_clic_create(hwaddr mclicbase, hwaddr sclicbase,
+ hwaddr uclicbase, uint32_t hartid,
+ uint32_t num_sources, uint8_t clicintctlbits,
+ const char *version)
+{
+ DeviceState *dev = qdev_new(TYPE_RISCV_CLIC);
+ RISCVCLICState *s = RISCV_CLIC(dev);
+ g_autofree char **tokens = NULL;
+ char *base_version;
+ bool jump_table = false;
+
+ assert(num_sources <= CLIC_MAX_IRQ_COUNT);
+ assert(cpu_exists(hartid));
+ assert(clicintctlbits <= MAX_CLIC_INTCTLBITS);
+ assert(0 == (mclicbase & 0xfff)); /* base should be 4KiB-aligned */
+
+ /* Parse the version */
+ tokens = g_strsplit(version, "-", 2);
+ base_version = g_strdup(tokens[0]);
+ assert(0 == strcmp(base_version, "v0.9"));
+ if (tokens[1]) {
+ assert(0 == strcmp(tokens[1], "jmp"));
+ jump_table = true;
+ }
+
+ qdev_prop_set_uint32(dev, "hartid", hartid);
+ qdev_prop_set_uint32(dev, "num-sources", num_sources);
+ qdev_prop_set_uint32(dev, "clic-size", num_sources * 4 + CLIC_INTCTL_BASE);
+ qdev_prop_set_uint32(dev, "clicintctlbits", clicintctlbits);
+ qdev_prop_set_string(dev, "version", base_version);
+ qdev_prop_set_bit(dev, "jump-table", jump_table);
+
+ s->prv_m = riscv_clic_view_create(s, mclicbase, PRV_M);
+ if (sclicbase) {
+ s->prv_s = riscv_clic_view_create(s, sclicbase, PRV_S);
+ }
+ if (uclicbase) {
+ s->prv_u = riscv_clic_view_create(s, uclicbase, PRV_U);
+ }
+
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ return dev;
+}
+
+void riscv_clic_get_next_interrupt(void *opaque)
+{
+ RISCVCLICState *clic = opaque;
+ riscv_clic_next_interrupt(clic);
+}
+
+bool riscv_clic_shv_interrupt(void *opaque, int irq)
+{
+ RISCVCLICState *clic = opaque;
+ return riscv_clic_is_shv_interrupt(clic, irq);
+}
+
+bool riscv_clic_edge_triggered(void *opaque, int irq)
+{
+ RISCVCLICState *clic = opaque;
+ return riscv_clic_is_edge_triggered(clic, irq);
+}
+
+bool riscv_clic_use_jump_table(void *opaque)
+{
+ RISCVCLICState *clic = opaque;
+ return clic->jump_table;
+}
+
+void riscv_clic_clean_pending(void *opaque, int irq)
+{
+ RISCVCLICState *clic = opaque;
+ clic->clicintip[irq] = 0;
+}
+
+/*
+ * The new CLIC interrupt-handling mode is encoded as a new state in
+ * the existing WARL xtvec register, where the low two bits are 11.
+ */
+bool riscv_clic_is_clic_mode(CPURISCVState *env)
+{
+ target_ulong xtvec = (env->priv == PRV_M) ? env->mtvec : env->stvec;
+ return env->clic && ((xtvec & XTVEC_MODE) == XTVEC_CLIC);
+}
+
+void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
+ int *il, int *irq)
+{
+ *irq = get_field(exccode, RISCV_EXCP_CLIC_IRQ);
+ *mode = get_field(exccode, RISCV_EXCP_CLIC_MODE);
+ *il = get_field(exccode, RISCV_EXCP_CLIC_LEVEL);
+}
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index a2030e3a6f..18454df8bd 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -47,6 +47,7 @@ config RISCV_VIRT
select SERIAL
select RISCV_ACLINT
select RISCV_APLIC
+ select RISCV_CLIC
select RISCV_IMSIC
select SIFIVE_PLIC
select SIFIVE_TEST
diff --git a/include/hw/intc/riscv_clic.h b/include/hw/intc/riscv_clic.h
new file mode 100644
index 0000000000..b38a576308
--- /dev/null
+++ b/include/hw/intc/riscv_clic.h
@@ -0,0 +1,213 @@
+/*
+ * RISC-V CLIC(Core Local Interrupt Controller) interface.
+ *
+ * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
+ * Copyright (c) 2024 Cirrus Logic, Inc
+ * and Cirrus Logic International Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This implementation follows the CLIC 0.9-stable draft at 14 March 2024,
+ * with the following exceptions and implementation details:
+ * - the CLIC control registers are memory-mapped as per earlier drafts (in
+ * particular version 0.9-draft, 20 June 2023)
+ * - the indirect CSR control in 0.9-stable is not implemented
+ * - the vector table can be either handler addresses (as per the spec)
+ or a jump table where each entry is processed as an instruction,
+ selectable with version number v0.9-jmp
+ * - each hart is assigned its own CLIC block
+ * - if PRV_S and/or PRV_M are supported, they are currently assumed to follow
+ * the PRV_M registers; a subsequent update will address this
+ * - support for PRV_S and PRV_M is selectable at CLIC instantiation
+ * - PRV_S and PRV_U registers are currently separate from PRV_M; a subsequent
+ * update will turn them into filtered views onto the PRV_M registers
+ * - each hart is assigned its own CLIC block
+ * - support for PRV_S and PRV_M is selectable at CLIC instantiation by
+ * passing in a base address for the given modes; a base address of 0 is
+ * treated as not supported
+ * - PRV_S and PRV_U registers are mapped onto the PRV_M controls with
+ * appropriate filtering for the access mode
+ *
+ * The implementation has a RISCVCLICState per hart, with a RISCVCLICView
+ * for each mode subsidiary to that. Each view knows its access mode and base
+ * address, as well as the RISCVCLICState with which it is associated.
+ *
+ * MMIO accesses go through the view, allowing the appropriate permissions to
+ * be enforced when accessing the parent RISCVCLICState for the settings.
+ */
+
+#ifndef RISCV_CLIC_H
+#define RISCV_CLIC_H
+
+#include "hw/irq.h"
+#include "hw/sysbus.h"
+
+#define TYPE_RISCV_CLIC "riscv_clic"
+#define TYPE_RISCV_CLIC_VIEW "riscv_clic_view"
+#define RISCV_CLIC(obj) \
+ OBJECT_CHECK(RISCVCLICState, (obj), TYPE_RISCV_CLIC)
+#define RISCV_CLIC_VIEW(obj) \
+ OBJECT_CHECK(RISCVCLICView, (obj), TYPE_RISCV_CLIC_VIEW)
+
+/*
+ * CLIC per hart active interrupts
+ *
+ * We maintain per hart lists of enabled interrupts sorted by
+ * mode+level+priority. The sorting is done on the configuration path
+ * so that the interrupt delivery fastpath can linear scan enabled
+ * interrupts in priority order.
+ */
+typedef struct CLICActiveInterrupt {
+ uint16_t intcfg;
+ uint16_t irq;
+} CLICActiveInterrupt;
+
+typedef enum TRIG_TYPE {
+ POSITIVE_LEVEL,
+ POSITIVE_EDGE,
+ NEG_LEVEL,
+ NEG_EDGE,
+} TRIG_TYPE;
+
+#define CLIC_INTCTL_BASE 0x1000 /* start offset of intctl registers */
+#define MAX_CLIC_INTCTLBITS 8 /* maximum value for intctlbits */
+
+/* maximum of 4096 IRQs */
+#define CLIC_IRQ_BITS 12
+#define CLIC_MAX_IRQ_COUNT (1 << CLIC_IRQ_BITS)
+#define CLIC_MAX_IRQ (CLIC_MAX_IRQ_COUNT - 1)
+#define CLIC_IRQ_MASK CLIC_MAX_IRQ
+
+/*
+ * clicinttrig registers
+ * 31 interrupt_trap_enable
+ * 30 nxti_enable
+ * 29:13 reserved (WARL 0)
+ * 12:0 interrupt_number
+ */
+#define CLIC_INTTRIG_REGS 32 /* inttrig register count */
+#define CLIC_INTTRIG_START 0x10 /* first inttrig register */
+#define CLIC_INTTRIG_END (CLIC_INTTRIG_START + CLIC_INTTRIG_REGS - 1)
+#define CLIC_INTTRIG_TRAP_ENA 0x80000000
+#define CLIC_INTTRIG_NXTI_ENA 0x40000000
+#define CLIC_INTTRIG_IRQN 0x00001fff
+#define CLIC_INTTRIG_MASK (CLIC_INTTRIG_TRAP_ENA | \
+ CLIC_INTTRIG_NXTI_ENA | CLIC_INTTRIG_IRQN)
+
+/*
+ * We combine the mode and intctl to a number so that higher modes come first.
+ * 9:8 machine mode
+ * 7:0 clicintctl
+ */
+#define CLIC_INTCFG_MODE_SHIFT 8
+#define CLIC_INTCFG_MODE 0x300
+#define CLIC_INTCFG_CTL 0xff
+#define CLIC_INTCFG_MASK (CLIC_INTCFG_MODE | CLIC_INTCFG_CTL)
+
+/*
+ * clicintattr layout
+ * 7:6 mode
+ * 5:3 reserved (WPRI 0)
+ * 2:1 trig
+ * 0 shv
+ */
+#define CLIC_INTATTR_MODE_SHIFT 6
+#define CLIC_INTATTR_MODE_WIDTH 2
+#define CLIC_INTATTR_MODE 0xc0
+#define CLIC_INTATTR_TRIG_SHIFT 1
+#define CLIC_INTATTR_TRIG_WIDTH 2
+#define CLIC_INTATTR_TRIG 0x06
+#define CLIC_INTATTR_SHV 0x01
+#define CLIC_INTATTR_MASK (CLIC_INTATTR_MODE | CLIC_INTATTR_TRIG | \
+ CLIC_INTATTR_SHV)
+/* The clicintattr value */
+#define CLIC_INTATTR_TRIG_EDGE 0b01 /* trig decode edge-triggered */
+#define CLIC_INTATTR_TRIG_INV 0b10 /* trig decode negative polarity */
+
+/* Forward declaration */
+typedef struct RISCVCLICView RISCVCLICView;
+
+/*
+ * The main CLIC state (PRV_M mode) for a hart.
+ */
+typedef struct RISCVCLICState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+
+ /* Implementation parameters */
+ bool shv_enabled; /* hardware-vectoring enabled */
+ bool jump_table; /* vector with jump table, not handler addresses */
+ int hartid;
+ uint32_t num_sources;
+ uint32_t clic_size;
+ uint32_t clic_mmode_base;
+ uint32_t clicintctlbits;
+ RISCVCLICView *prv_m; /* our PRV_M view */
+ RISCVCLICView *prv_s; /* our PRV_S view */
+ RISCVCLICView *prv_u; /* our PRV_U view */
+ char *version;
+
+ /* Global configuration */
+ uint8_t nmbits; /* mode bits */
+ uint8_t mnlbits; /* level bits for M-mode */
+ uint8_t snlbits; /* level bits for S-mode, if present */
+ uint8_t unlbits; /* level bits for U-mode, if present */
+ uint32_t clicinttrig[CLIC_INTTRIG_REGS];
+
+ /* Aperture configuration */
+ uint8_t *clicintip;
+ uint8_t *clicintie;
+ uint8_t *clicintattr;
+ uint8_t *clicintctl;
+
+ /* Compatible with v0.8 */
+ uint32_t mintthresh;
+ uint32_t sintthresh;
+ uint32_t uintthresh;
+
+ /* QEMU implementation related fields */
+ uint32_t exccode;
+ CLICActiveInterrupt *active_list;
+ size_t active_count;
+ qemu_irq cpu_irq;
+} RISCVCLICState;
+
+/*
+ * A PRV_S or PRV_U overlay onto the main RISCVCLICState.
+ */
+typedef struct RISCVCLICView {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ RISCVCLICState *clic; /* the CLIC this is a view onto */
+ MemoryRegion mmio;
+ uint64_t clicbase;
+ uint8_t mode;
+} RISCVCLICView;
+
+DeviceState *riscv_clic_create(hwaddr mclicbase, hwaddr sclicbase,
+ hwaddr uclicbase, uint32_t hartid,
+ uint32_t num_sources, uint8_t clicintctlbits,
+ const char *version);
+
+void riscv_clic_decode_exccode(uint32_t exccode, int *mode, int *il, int *irq);
+void riscv_clic_clean_pending(void *opaque, int irq);
+bool riscv_clic_edge_triggered(void *opaque, int irq);
+bool riscv_clic_shv_interrupt(void *opaque, int irq);
+bool riscv_clic_use_jump_table(void *opaque);
+void riscv_clic_get_next_interrupt(void *opaque);
+bool riscv_clic_is_clic_mode(CPURISCVState *env);
+#endif
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 9b5f36ad0a..12aa8cf6b1 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -36,6 +36,7 @@
typedef struct CPUArchState CPURISCVState;
#define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
+#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0
#if defined(TARGET_RISCV32)
# define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE32
@@ -465,6 +466,7 @@ struct CPUArchState {
bool vstime_irq;
void *clic; /* clic interrupt controller */
+ uint32_t exccode; /* clic irq encode */
hwaddr kernel_addr;
hwaddr fdt_addr;
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index ad45402370..0ed44ec0a8 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -694,6 +694,12 @@ typedef enum RISCVException {
} RISCVException;
#define RISCV_EXCP_INT_FLAG 0x80000000
+#define RISCV_EXCP_CLIC 0x40000000
+#define RISCV_EXCP_CLIC_LEVEL_SHIFT 14
+#define RISCV_EXCP_CLIC_LEVEL (0xff << RISCV_EXCP_CLIC_LEVEL_SHIFT)
+#define RISCV_EXCP_CLIC_MODE_SHIFT 12
+#define RISCV_EXCP_CLIC_MODE (3 << RISCV_EXCP_CLIC_MODE_SHIFT)
+#define RISCV_EXCP_CLIC_IRQ 0x00000fff
#define RISCV_EXCP_INT_MASK 0x7fffffff
/* Interrupt causes */
@@ -746,6 +752,17 @@ typedef enum RISCVException {
#define SINTSTATUS_SIL 0x0000ff00 /* sil[15:8] */
#define SINTSTATUS_UIL 0x000000ff /* uil[7:0] */
+/* mtvec & stvec */
+#define XTVEC_MODE 0x03
+#define XTVEC_SUBMODE 0x3c
+#define XTVEC_FULL_MODE (XTVEC_MODE | XTVEC_SUBMODE)
+#define XTVEC_OBASE (~XTVEC_MODE)
+#define XTVEC_NBASE (~XTVEC_FULL_MODE)
+
+#define XTVEC_CLINT_DIRECT 0x0
+#define XTVEC_CLINT_VECTORED 0x1
+#define XTVEC_CLIC 0x3
+
/* MIE masks */
#define MIE_SEIE (1 << IRQ_S_EXT)
#define MIE_UEIE (1 << IRQ_U_EXT)
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 02/11] target/riscv: Update CSR xintthresh in CLIC mode
2024-08-14 8:27 ` [PATCH 00/11] RISC-V: support CLIC v0.9 specification Ian Brockbank
2024-08-14 8:27 ` [PATCH 03/11] hw/intc: Add CLIC device Ian Brockbank
@ 2024-08-14 8:27 ` Ian Brockbank
2024-08-14 8:27 ` [PATCH 01/11] target/riscv: Add CLIC CSR mintstatus Ian Brockbank
` (5 subsequent siblings)
7 siblings, 0 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-14 8:27 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
The interrupt-level threshold (xintthresh) CSR holds an 8-bit field
for the threshold level of the associated privilege mode.
For horizontal interrupts, only the ones with higher interrupt levels
than the threshold level are allowed to preempt.
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
---
target/riscv/cpu.h | 2 ++
target/riscv/cpu_bits.h | 2 ++
target/riscv/csr.c | 28 ++++++++++++++++++++++++++++
3 files changed, 32 insertions(+)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 95303f50d3..9b5f36ad0a 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -260,6 +260,7 @@ struct CPUArchState {
uint64_t miclaim;
uint64_t mintstatus; /* clic-spec */
+ target_ulong mintthresh; /* clic-spec */
uint64_t mie;
uint64_t mideleg;
@@ -283,6 +284,7 @@ struct CPUArchState {
target_ulong stvec;
target_ulong sepc;
target_ulong scause;
+ target_ulong sintthresh; /* clic-spec */
target_ulong mtvec;
target_ulong mepc;
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 2e65495b54..ad45402370 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -166,6 +166,7 @@
#define CSR_MTVAL 0x343
#define CSR_MIP 0x344
#define CSR_MINTSTATUS 0xfb1 /* clic-spec-draft */
+#define CSR_MINTTHRESH 0x347 /* clic-spec-draft */
/* Machine-Level Window to Indirectly Accessed Registers (AIA) */
#define CSR_MISELECT 0x350
@@ -208,6 +209,7 @@
#define CSR_STVAL 0x143
#define CSR_SIP 0x144
#define CSR_SINTSTATUS 0xdb1 /* clic-spec-draft */
+#define CSR_SINTTHRESH 0x147 /* clic-spec-draft */
/* Sstc supervisor CSRs */
#define CSR_STIMECMP 0x14D
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index f9ed7b9079..9c824c0d8f 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -2903,6 +2903,18 @@ static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
return RISCV_EXCP_NONE;
}
+static int read_mintthresh(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mintthresh;
+ return 0;
+}
+
+static int write_mintthresh(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mintthresh = val;
+ return 0;
+}
+
/* Supervisor Trap Setup */
static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno,
Int128 *val)
@@ -3322,6 +3334,18 @@ static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
return RISCV_EXCP_NONE;
}
+static int read_sintthresh(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->sintthresh;
+ return 0;
+}
+
+static int write_sintthresh(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->sintthresh = val;
+ return 0;
+}
+
/* Supervisor Protection and Translation */
static RISCVException read_satp(CPURISCVState *env, int csrno,
target_ulong *val)
@@ -5621,9 +5645,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
/* Machine Mode Core Level Interrupt Controller */
[CSR_MINTSTATUS] = { "mintstatus", clic, read_mintstatus },
+ [CSR_MINTTHRESH] = { "mintthresh", clic, read_mintthresh,
+ write_mintthresh },
/* Supervisor Mode Core Level Interrupt Controller */
[CSR_SINTSTATUS] = { "sintstatus", clic, read_sintstatus },
+ [CSR_SINTTHRESH] = { "sintthresh", clic, read_sintthresh,
+ write_sintthresh },
[CSR_SCOUNTOVF] = { "scountovf", sscofpmf, read_scountovf,
.min_priv_ver = PRIV_VERSION_1_12_0 },
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 01/11] target/riscv: Add CLIC CSR mintstatus
2024-08-14 8:27 ` [PATCH 00/11] RISC-V: support CLIC v0.9 specification Ian Brockbank
2024-08-14 8:27 ` [PATCH 03/11] hw/intc: Add CLIC device Ian Brockbank
2024-08-14 8:27 ` [PATCH 02/11] target/riscv: Update CSR xintthresh in CLIC mode Ian Brockbank
@ 2024-08-14 8:27 ` Ian Brockbank
2024-08-14 8:27 ` [PATCH 04/11] target/riscv: Update CSR xie in CLIC mode Ian Brockbank
` (4 subsequent siblings)
7 siblings, 0 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-14 8:27 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
CSR mintstatus holds the active interrupt level for each supported
privilege mode. sintstatus, and user, uintstatus, provide restricted
views of mintstatus.
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
---
target/riscv/cpu.h | 3 +++
target/riscv/cpu_bits.h | 11 +++++++++++
target/riscv/csr.c | 31 +++++++++++++++++++++++++++++++
3 files changed, 45 insertions(+)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 1619c3acb6..95303f50d3 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -259,6 +259,7 @@ struct CPUArchState {
bool software_seip;
uint64_t miclaim;
+ uint64_t mintstatus; /* clic-spec */
uint64_t mie;
uint64_t mideleg;
@@ -461,6 +462,8 @@ struct CPUArchState {
QEMUTimer *vstimer; /* Internal timer for VS-mode interrupt */
bool vstime_irq;
+ void *clic; /* clic interrupt controller */
+
hwaddr kernel_addr;
hwaddr fdt_addr;
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 32b068f18a..2e65495b54 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -165,6 +165,7 @@
#define CSR_MCAUSE 0x342
#define CSR_MTVAL 0x343
#define CSR_MIP 0x344
+#define CSR_MINTSTATUS 0xfb1 /* clic-spec-draft */
/* Machine-Level Window to Indirectly Accessed Registers (AIA) */
#define CSR_MISELECT 0x350
@@ -206,6 +207,7 @@
#define CSR_SCAUSE 0x142
#define CSR_STVAL 0x143
#define CSR_SIP 0x144
+#define CSR_SINTSTATUS 0xdb1 /* clic-spec-draft */
/* Sstc supervisor CSRs */
#define CSR_STIMECMP 0x14D
@@ -733,6 +735,15 @@ typedef enum RISCVException {
#define SIP_SEIP MIP_SEIP
#define SIP_LCOFIP MIP_LCOFIP
+/* mintstatus */
+#define MINTSTATUS_MIL 0xff000000 /* mil[31:24] */
+#define MINTSTATUS_SIL 0x0000ff00 /* sil[15:8] */
+#define MINTSTATUS_UIL 0x000000ff /* uil[7:0] */
+
+/* sintstatus */
+#define SINTSTATUS_SIL 0x0000ff00 /* sil[15:8] */
+#define SINTSTATUS_UIL 0x000000ff /* uil[7:0] */
+
/* MIE masks */
#define MIE_SEIE (1 << IRQ_S_EXT)
#define MIE_UEIE (1 << IRQ_U_EXT)
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index ea3560342c..f9ed7b9079 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -578,6 +578,16 @@ static RISCVException debug(CPURISCVState *env, int csrno)
return RISCV_EXCP_ILLEGAL_INST;
}
+
+static int clic(CPURISCVState *env, int csrno)
+{
+ if (env->clic) {
+ return RISCV_EXCP_NONE;
+ }
+
+ return RISCV_EXCP_ILLEGAL_INST;
+}
+
#endif
static RISCVException seed(CPURISCVState *env, int csrno)
@@ -2887,6 +2897,12 @@ static RISCVException rmw_mviph(CPURISCVState *env, int csrno,
return ret;
}
+static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mintstatus;
+ return RISCV_EXCP_NONE;
+}
+
/* Supervisor Trap Setup */
static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno,
Int128 *val)
@@ -3298,6 +3314,14 @@ static RISCVException rmw_siph(CPURISCVState *env, int csrno,
return ret;
}
+static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ /* sintstatus is a filtered view of mintstatus with the PRV_M removed */
+ target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
+ *val = env->mintstatus & mask;
+ return RISCV_EXCP_NONE;
+}
+
/* Supervisor Protection and Translation */
static RISCVException read_satp(CPURISCVState *env, int csrno,
target_ulong *val)
@@ -5594,6 +5618,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
write_mhpmcounterh },
[CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", mctr32, read_hpmcounterh,
write_mhpmcounterh },
+
+ /* Machine Mode Core Level Interrupt Controller */
+ [CSR_MINTSTATUS] = { "mintstatus", clic, read_mintstatus },
+
+ /* Supervisor Mode Core Level Interrupt Controller */
+ [CSR_SINTSTATUS] = { "sintstatus", clic, read_sintstatus },
+
[CSR_SCOUNTOVF] = { "scountovf", sscofpmf, read_scountovf,
.min_priv_ver = PRIV_VERSION_1_12_0 },
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 04/11] target/riscv: Update CSR xie in CLIC mode
2024-08-14 8:27 ` [PATCH 00/11] RISC-V: support CLIC v0.9 specification Ian Brockbank
` (2 preceding siblings ...)
2024-08-14 8:27 ` [PATCH 01/11] target/riscv: Add CLIC CSR mintstatus Ian Brockbank
@ 2024-08-14 8:27 ` Ian Brockbank
2024-08-14 8:27 ` [PATCH 05/11] target/riscv: Update CSR xip " Ian Brockbank
` (3 subsequent siblings)
7 siblings, 0 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-14 8:27 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
The xie CSR appears hardwired to zero in CLIC mode, replaced by separate
memory-mapped interrupt enables (clicintie[i]). Writes to xie will be
ignored and will not trap (i.e., no access faults).
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
---
target/riscv/csr.c | 34 ++++++++++++++++++++++------------
1 file changed, 22 insertions(+), 12 deletions(-)
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 9c824c0d8f..a5978e0929 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -30,6 +30,10 @@
#include "qemu/guest-random.h"
#include "qapi/error.h"
+#if !defined(CONFIG_USER_ONLY)
+#include "hw/intc/riscv_clic.h"
+#endif
+
/* CSR function table public API */
void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops)
{
@@ -1805,16 +1809,19 @@ static RISCVException rmw_mie64(CPURISCVState *env, int csrno,
uint64_t *ret_val,
uint64_t new_val, uint64_t wr_mask)
{
- uint64_t mask = wr_mask & all_ints;
+ /* Access to xie will be ignored in CLIC mode and will not trap. */
+ if (!riscv_clic_is_clic_mode(env)) {
+ uint64_t mask = wr_mask & all_ints;
- if (ret_val) {
- *ret_val = env->mie;
- }
+ if (ret_val) {
+ *ret_val = env->mie;
+ }
- env->mie = (env->mie & ~mask) | (new_val & mask);
+ env->mie = (env->mie & ~mask) | (new_val & mask);
- if (!riscv_has_ext(env, RVH)) {
- env->mie &= ~((uint64_t)HS_MODE_INTERRUPTS);
+ if (!riscv_has_ext(env, RVH)) {
+ env->mie &= ~((uint64_t)HS_MODE_INTERRUPTS);
+ }
}
return RISCV_EXCP_NONE;
@@ -2906,13 +2913,13 @@ static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
static int read_mintthresh(CPURISCVState *env, int csrno, target_ulong *val)
{
*val = env->mintthresh;
- return 0;
+ return RISCV_EXCP_NONE;
}
static int write_mintthresh(CPURISCVState *env, int csrno, target_ulong val)
{
env->mintthresh = val;
- return 0;
+ return RISCV_EXCP_NONE;
}
/* Supervisor Trap Setup */
@@ -3059,7 +3066,10 @@ static RISCVException rmw_sie64(CPURISCVState *env, int csrno,
*ret_val |= env->sie & nalias_mask;
}
- env->sie = (env->sie & ~sie_mask) | (new_val & sie_mask);
+ /* Writes to xie will be ignored in CLIC mode and will not trap. */
+ if (!riscv_clic_is_clic_mode(env)) {
+ env->sie = (env->sie & ~sie_mask) | (new_val & sie_mask);
+ }
}
return ret;
@@ -3337,13 +3347,13 @@ static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
static int read_sintthresh(CPURISCVState *env, int csrno, target_ulong *val)
{
*val = env->sintthresh;
- return 0;
+ return RISCV_EXCP_NONE;
}
static int write_sintthresh(CPURISCVState *env, int csrno, target_ulong val)
{
env->sintthresh = val;
- return 0;
+ return RISCV_EXCP_NONE;
}
/* Supervisor Protection and Translation */
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 05/11] target/riscv: Update CSR xip in CLIC mode
2024-08-14 8:27 ` [PATCH 00/11] RISC-V: support CLIC v0.9 specification Ian Brockbank
` (3 preceding siblings ...)
2024-08-14 8:27 ` [PATCH 04/11] target/riscv: Update CSR xie in CLIC mode Ian Brockbank
@ 2024-08-14 8:27 ` Ian Brockbank
2024-08-14 8:27 ` [PATCH 07/11] target/riscv: Update CSR xnxti " Ian Brockbank
` (2 subsequent siblings)
7 siblings, 0 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-14 8:27 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
The xip CSR appears hardwired to zero in CLIC mode, replaced by separate
memory-mapped interrupt pendings (clicintip[i]). Writes to xip will be
ignored and will not trap (i.e., no access faults).
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
---
target/riscv/csr.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index a5978e0929..276ef7856e 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -2743,6 +2743,12 @@ static RISCVException rmw_mip(CPURISCVState *env, int csrno,
uint64_t rval;
RISCVException ret;
+ /* The xip CSR appears hardwired to zero in CLIC mode. */
+ if (riscv_clic_is_clic_mode(env)) {
+ *ret_val = 0;
+ return RISCV_EXCP_NONE;
+ }
+
ret = rmw_mip64(env, csrno, &rval, new_val, wr_mask);
if (ret_val) {
*ret_val = rval;
@@ -3294,6 +3300,12 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno,
}
ret = rmw_vsip64(env, CSR_VSIP, ret_val, new_val, wr_mask);
} else {
+ /* The xip CSR appears hardwired to zero in CLIC mode. */
+ if (riscv_clic_is_clic_mode(env)) {
+ *ret_val = 0;
+ return RISCV_EXCP_NONE;
+ }
+
ret = rmw_mvip64(env, csrno, ret_val, new_val, wr_mask & mask);
}
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 07/11] target/riscv: Update CSR xnxti in CLIC mode
2024-08-14 8:27 ` [PATCH 00/11] RISC-V: support CLIC v0.9 specification Ian Brockbank
` (4 preceding siblings ...)
2024-08-14 8:27 ` [PATCH 05/11] target/riscv: Update CSR xip " Ian Brockbank
@ 2024-08-14 8:27 ` Ian Brockbank
2024-08-14 8:27 ` [PATCH 06/11] target/riscv: Update CSR xtvec " Ian Brockbank
2024-08-14 14:11 ` [PATCH 00/11] RISC-V: support CLIC v0.9 specification Ian Brockbank
7 siblings, 0 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-14 8:27 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
The CSR can be used by software to service the next horizontal interrupt
when it has greater level than the saved interrupt context
(held in xcause`.pil`) and greater level than the interrupt threshold of
the corresponding privilege mode,
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
---
target/riscv/cpu_bits.h | 25 +++++++++
target/riscv/csr.c | 111 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 136 insertions(+)
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 279a6f889b..3744b34504 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -166,6 +166,7 @@
#define CSR_MCAUSE 0x342
#define CSR_MTVAL 0x343
#define CSR_MIP 0x344
+#define CSR_MNXTI 0x345 /* clic-spec-draft */
#define CSR_MINTSTATUS 0xfb1 /* clic-spec-draft */
#define CSR_MINTTHRESH 0x347 /* clic-spec-draft */
@@ -210,6 +211,7 @@
#define CSR_SCAUSE 0x142
#define CSR_STVAL 0x143
#define CSR_SIP 0x144
+#define CSR_SNXTI 0x145 /* clic-spec-draft */
#define CSR_SINTSTATUS 0xdb1 /* clic-spec-draft */
#define CSR_SINTTHRESH 0x147 /* clic-spec-draft */
@@ -561,6 +563,8 @@
#define MSTATUS_GVA 0x4000000000ULL
#define MSTATUS_MPV 0x8000000000ULL
+#define MSTATUS_WRITE_MASK 0x0000001f
+
#define MSTATUS64_UXL 0x0000000300000000ULL
#define MSTATUS64_SXL 0x0000000C00000000ULL
@@ -754,6 +758,27 @@ typedef enum RISCVException {
#define SINTSTATUS_SIL 0x0000ff00 /* sil[15:8] */
#define SINTSTATUS_UIL 0x000000ff /* uil[7:0] */
+/* mcause */
+#define MCAUSE_INT (1 << (TARGET_LONG_BITS - 1))
+#define MCAUSE_MINHV 0x40000000 /* minhv */
+#define MCAUSE_MPP 0x30000000 /* mpp[1:0] */
+#define MCAUSE_MPIE 0x08000000 /* mpie */
+#define MCAUSE_MPIL 0x00ff0000 /* mpil[7:0] */
+#define MCAUSE_EXCCODE 0x00000fff /* exccode[11:0] */
+
+/* scause */
+#define SCAUSE_INT (1 << (TARGET_LONG_BITS - 1))
+#define SCAUSE_SINHV 0x40000000 /* sinhv */
+#define SCAUSE_SPP 0x10000000 /* spp */
+#define SCAUSE_SPIE 0x08000000 /* spie */
+#define SCAUSE_SPIL 0x00ff0000 /* spil[7:0] */
+#define SCAUSE_EXCCODE 0x00000fff /* exccode[11:0] */
+
+/* mcause & scause */
+#define XCAUSE_XPP_SHIFT 28
+#define XCAUSE_XPIE_SHIFT 27
+#define XCAUSE_XPIL_SHIFT 16
+
/* mtvec & stvec */
#define XTVEC_MODE 0x03
#define XTVEC_SUBMODE 0x3c
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index be0071fd25..813a5b927f 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -19,6 +19,7 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
+#include "qemu/main-loop.h"
#include "qemu/timer.h"
#include "cpu.h"
#include "tcg/tcg-cpu.h"
@@ -2936,6 +2937,77 @@ static RISCVException rmw_mviph(CPURISCVState *env, int csrno,
return ret;
}
+static bool get_xnxti_status(CPURISCVState *env)
+{
+ int clic_irq, clic_priv, clic_il, pil;
+
+ if (!env->exccode) { /* No interrupt */
+ return false;
+ }
+ /* The system is not in a CLIC mode */
+ if (!riscv_clic_is_clic_mode(env)) {
+ return false;
+ } else {
+ riscv_clic_decode_exccode(env->exccode, &clic_priv, &clic_il,
+ &clic_irq);
+
+ if (env->priv == PRV_M) {
+ pil = MAX(get_field(env->mcause, MCAUSE_MPIL), env->mintthresh);
+ } else if (env->priv == PRV_S) {
+ pil = MAX(get_field(env->scause, SCAUSE_SPIL), env->sintthresh);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CSR: rmw xnxti with unsupported mode\n");
+ exit(1);
+ }
+
+ if ((clic_priv != env->priv) || /* No horizontal interrupt */
+ (clic_il <= pil) || /* No higher level interrupt */
+ (riscv_clic_shv_interrupt(env->clic, clic_irq))) {
+ /* CLIC vector mode */
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
+
+static int rmw_mnxti(CPURISCVState *env, int csrno, target_ulong *ret_value,
+ target_ulong new_value, target_ulong write_mask)
+{
+ int clic_priv, clic_il, clic_irq;
+ bool ready;
+ if (write_mask) {
+ env->mstatus |= new_value & (write_mask & MSTATUS_WRITE_MASK);
+ }
+
+ BQL_LOCK_GUARD();
+
+ ready = get_xnxti_status(env);
+ if (ready) {
+ riscv_clic_decode_exccode(env->exccode, &clic_priv, &clic_il,
+ &clic_irq);
+ if (write_mask) {
+ bool edge = riscv_clic_edge_triggered(env->clic, clic_irq);
+ if (edge) {
+ riscv_clic_clean_pending(env->clic, clic_irq);
+ }
+ env->mintstatus = set_field(env->mintstatus,
+ MINTSTATUS_MIL, clic_il);
+ env->mcause = set_field(env->mcause, MCAUSE_EXCCODE, clic_irq);
+ }
+ if (ret_value) {
+ *ret_value = (env->mtvt & ~0x3f) + sizeof(target_ulong) * clic_irq;
+ }
+ } else {
+ if (ret_value) {
+ *ret_value = 0;
+ }
+ }
+
+ return RISCV_EXCP_NONE;
+}
+
static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
{
*val = env->mintstatus;
@@ -3401,6 +3473,43 @@ static RISCVException rmw_siph(CPURISCVState *env, int csrno,
return ret;
}
+static int rmw_snxti(CPURISCVState *env, int csrno, target_ulong *ret_value,
+ target_ulong new_value, target_ulong write_mask)
+{
+ int clic_priv, clic_il, clic_irq;
+ bool ready;
+ if (write_mask) {
+ env->mstatus |= new_value & (write_mask & MSTATUS_WRITE_MASK);
+ }
+
+ BQL_LOCK_GUARD();
+
+ ready = get_xnxti_status(env);
+ if (ready) {
+ riscv_clic_decode_exccode(env->exccode, &clic_priv, &clic_il,
+ &clic_irq);
+ if (write_mask) {
+ bool edge = riscv_clic_edge_triggered(env->clic, clic_irq);
+ if (edge) {
+ riscv_clic_clean_pending(env->clic, clic_irq);
+ }
+ /* update the PRV_S parts of mintstatus */
+ env->mintstatus = set_field(env->mintstatus,
+ MINTSTATUS_SIL, clic_il);
+ env->scause = set_field(env->scause, SCAUSE_EXCCODE, clic_irq);
+ }
+ if (ret_value) {
+ *ret_value = (env->stvt & ~0x3f) + sizeof(target_ulong) * clic_irq;
+ }
+ } else {
+ if (ret_value) {
+ *ret_value = 0;
+ }
+ }
+
+ return RISCV_EXCP_NONE;
+}
+
static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
{
/* sintstatus is a filtered view of mintstatus with the PRV_M removed */
@@ -5720,12 +5829,14 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
/* Machine Mode Core Level Interrupt Controller */
[CSR_MTVT] = { "mtvt", clic, read_mtvt, write_mtvt },
+ [CSR_MNXTI] = { "mnxti", clic, NULL, NULL, rmw_mnxti },
[CSR_MINTSTATUS] = { "mintstatus", clic, read_mintstatus },
[CSR_MINTTHRESH] = { "mintthresh", clic, read_mintthresh,
write_mintthresh },
/* Supervisor Mode Core Level Interrupt Controller */
[CSR_STVT] = { "stvt", clic, read_stvt, write_stvt },
+ [CSR_SNXTI] = { "snxti", clic, NULL, NULL, rmw_snxti },
[CSR_SINTSTATUS] = { "sintstatus", clic, read_sintstatus },
[CSR_SINTTHRESH] = { "sintthresh", clic, read_sintthresh,
write_sintthresh },
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 06/11] target/riscv: Update CSR xtvec in CLIC mode
2024-08-14 8:27 ` [PATCH 00/11] RISC-V: support CLIC v0.9 specification Ian Brockbank
` (5 preceding siblings ...)
2024-08-14 8:27 ` [PATCH 07/11] target/riscv: Update CSR xnxti " Ian Brockbank
@ 2024-08-14 8:27 ` Ian Brockbank
2024-08-14 14:11 ` [PATCH 00/11] RISC-V: support CLIC v0.9 specification Ian Brockbank
7 siblings, 0 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-14 8:27 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
The new CLIC interrupt-handling mode is encoded as a new state in the
existing WARL xtvec register, where the low two bits of are 11.
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
---
target/riscv/cpu.h | 2 ++
target/riscv/cpu_bits.h | 2 ++
target/riscv/csr.c | 63 ++++++++++++++++++++++++++++++++++++++---
3 files changed, 63 insertions(+), 4 deletions(-)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 12aa8cf6b1..05a014db03 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -283,11 +283,13 @@ struct CPUArchState {
target_ulong medeleg;
target_ulong stvec;
+ target_ulong stvt; /* clic-spec */
target_ulong sepc;
target_ulong scause;
target_ulong sintthresh; /* clic-spec */
target_ulong mtvec;
+ target_ulong mtvt; /* clic-spec */
target_ulong mepc;
target_ulong mcause;
target_ulong mtval; /* since: priv-1.10.0 */
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 0ed44ec0a8..279a6f889b 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -153,6 +153,7 @@
#define CSR_MIE 0x304
#define CSR_MTVEC 0x305
#define CSR_MCOUNTEREN 0x306
+#define CSR_MTVT 0x307 /* clic-spec-draft */
/* 32-bit only */
#define CSR_MSTATUSH 0x310
@@ -192,6 +193,7 @@
#define CSR_SIE 0x104
#define CSR_STVEC 0x105
#define CSR_SCOUNTEREN 0x106
+#define CSR_STVT 0x107 /* clic-spec-draft */
/* Supervisor Configuration CSRs */
#define CSR_SENVCFG 0x10A
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 276ef7856e..be0071fd25 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -2170,9 +2170,23 @@ static RISCVException read_mtvec(CPURISCVState *env, int csrno,
static RISCVException write_mtvec(CPURISCVState *env, int csrno,
target_ulong val)
{
- /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
- if ((val & 3) < 2) {
+ /*
+ * bits [1:0] encode mode; 0 = direct, 1 = vectored, 3 = CLIC,
+ * others reserved
+ */
+ target_ulong mode = get_field(val, XTVEC_MODE);
+ target_ulong fullmode = val & XTVEC_FULL_MODE;
+ if (mode <= XTVEC_CLINT_VECTORED) {
env->mtvec = val;
+ } else if (XTVEC_CLIC == fullmode && env->clic) {
+ /*
+ * CLIC mode hardwires xtvec bits 2-5 to zero.
+ * Layout:
+ * XLEN-1:6 base (WARL)
+ * 5:2 submode (WARL) - 0000 for CLIC
+ * 1:0 mode (WARL) - 11 for CLIC
+ */
+ env->mtvec = (val & XTVEC_NBASE) | XTVEC_CLIC;
} else {
qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: reserved mode not supported\n");
}
@@ -2271,6 +2285,18 @@ static RISCVException write_mcounteren(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
+static int read_mtvt(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mtvt;
+ return RISCV_EXCP_NONE;
+}
+
+static int write_mtvt(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mtvt = val & XTVEC_NBASE;
+ return RISCV_EXCP_NONE;
+}
+
/* Machine Trap Handling */
static RISCVException read_mscratch_i128(CPURISCVState *env, int csrno,
Int128 *val)
@@ -3122,9 +3148,24 @@ static RISCVException read_stvec(CPURISCVState *env, int csrno,
static RISCVException write_stvec(CPURISCVState *env, int csrno,
target_ulong val)
{
- /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
- if ((val & 3) < 2) {
+ /*
+ * bits [1:0] encode mode; 0 = direct, 1 = vectored, 3 = CLIC,
+ * others reserved
+ */
+ target_ulong mode = val & XTVEC_MODE;
+ target_ulong fullmode = val & XTVEC_FULL_MODE;
+ if (mode <= XTVEC_CLINT_VECTORED) {
env->stvec = val;
+ } else if (XTVEC_CLIC == fullmode && env->clic) {
+ /*
+ * If only CLIC mode is supported, writes to bit 1 are also ignored and
+ * it is always set to one. CLIC mode hardwires xtvec bits 2-5 to zero.
+ * Layout:
+ * XLEN-1:6 base (WARL)
+ * 5:2 submode (WARL) - 0000 for CLIC
+ * 1:0 mode (WARL) - 11 for CLIC
+ */
+ env->stvec = (val & XTVEC_NBASE) | XTVEC_CLIC;
} else {
qemu_log_mask(LOG_UNIMP, "CSR_STVEC: reserved mode not supported\n");
}
@@ -3149,6 +3190,18 @@ static RISCVException write_scounteren(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
+static int read_stvt(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->stvt;
+ return RISCV_EXCP_NONE;
+}
+
+static int write_stvt(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->stvt = val & XTVEC_NBASE;
+ return RISCV_EXCP_NONE;
+}
+
/* Supervisor Trap Handling */
static RISCVException read_sscratch_i128(CPURISCVState *env, int csrno,
Int128 *val)
@@ -5666,11 +5719,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
write_mhpmcounterh },
/* Machine Mode Core Level Interrupt Controller */
+ [CSR_MTVT] = { "mtvt", clic, read_mtvt, write_mtvt },
[CSR_MINTSTATUS] = { "mintstatus", clic, read_mintstatus },
[CSR_MINTTHRESH] = { "mintthresh", clic, read_mintthresh,
write_mintthresh },
/* Supervisor Mode Core Level Interrupt Controller */
+ [CSR_STVT] = { "stvt", clic, read_stvt, write_stvt },
[CSR_SINTSTATUS] = { "sintstatus", clic, read_sintstatus },
[CSR_SINTTHRESH] = { "sintthresh", clic, read_sintthresh,
write_sintthresh },
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* RE: [PATCH 00/11] RISC-V: support CLIC v0.9 specification
2024-08-14 8:27 ` [PATCH 00/11] RISC-V: support CLIC v0.9 specification Ian Brockbank
` (6 preceding siblings ...)
2024-08-14 8:27 ` [PATCH 06/11] target/riscv: Update CSR xtvec " Ian Brockbank
@ 2024-08-14 14:11 ` Ian Brockbank
7 siblings, 0 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-14 14:11 UTC (permalink / raw)
To: Ian Brockbank, qemu-devel@nongnu.org, qemu-riscv@nongnu.org
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei
Apologies. Please ignore this patchset.
Something seems to have gone wrong during the preparation and merge to current; I am working on a replacement, and will send v2 once I am happy with it.
Ian Brockbank C.Eng.
Senior Staff Software Engineer
Cirrus Logic | cirrus.com | t: +44 131 272 7145 | m: +44 7554 008061 |@badgertaming
> -----Original Message-----
> From: Ian Brockbank <Ian.Brockbank@cirrus.com>
> Sent: Wednesday, August 14, 2024 9:27 AM
> To: qemu-devel@nongnu.org; qemu-riscv@nongnu.org
> Cc: Palmer Dabbelt <palmer@dabbelt.com>; Alistair Francis
> <alistair.francis@wdc.com>; Bin Meng <bmeng.cn@gmail.com>; Weiwei Li
> <liwei1518@gmail.com>; Daniel Henrique Barboza
> <dbarboza@ventanamicro.com>; Liu Zhiwei <zhiwei_liu@linux.alibaba.com>;
> Ian Brockbank <Ian.Brockbank@cirrus.com>
> Subject: [PATCH 00/11] RISC-V: support CLIC v0.9 specification
>
> [Resending to include qemu-devel and add numbers to the patches]
>
> This patch set gives an implementation of "RISC-V Core-Local Interrupt
> Controller(CLIC) Version 0.9-draft-20210217". It comes from [1], where
> you can find the pdf format or the source code.
>
> This is based on the implementation from 2021 by Liu Zhiwei [3], who took
> over the job from Michael Clark, who gave the first implementation of
> clic-v0.7 specification [2]. I believe this implementation addresses all
> the comments in Liu Zhiwei's RFC patch thread.
>
> This implementation follows the CLIC 0.9-stable draft at 14 March 2024,
> with the following exceptions and implementation details:
> - the CLIC control registers are memory-mapped as per earlier drafts (in
> particular version 0.9-draft, 20 June 2023)
> - the indirect CSR control in 0.9-stable is not implemented
> - the vector table can be either handler addresses (as per the spec)
> or a jump table where each entry is processed as an instruction,
> selectable with version number v0.9-jmp
> - each hart is assigned its own CLIC block
> - if PRV_S and/or PRV_M are supported, they are currently assumed to
> follow
> the PRV_M registers; a subsequent update will address this
> - support for PRV_S and PRV_M is selectable at CLIC instantiation
> - PRV_S and PRV_U registers are currently separate from PRV_M; a
> subsequent
> update will turn them into filtered views onto the PRV_M registers
> - each hart is assigned its own CLIC block
> - support for PRV_S and PRV_M is selectable at CLIC instantiation by
> passing in a base address for the given modes; a base address of 0 is
> treated as not supported
> - PRV_S and PRV_U registers are mapped onto the PRV_M controls with
> appropriate filtering for the access mode
> - the RISCV virt machine has been updated to allow CLIC emulation by
> passing "machine=virt,clic=on" on the command line; various other
> parameters have been added to allow finer control of the CLIC behavior
>
> The implementation (in jump-table mode) has been verified to match the
> Cirrus Logic silicon (PRV_M only), which is based upon the Pulp
> implementation [4] as of June 2023.
>
> The implementation also includes a selection of qtests designed to verify
> operation in all possible combinations of PRV_M, PRV_S and PRV_U.
>
> [1] specification website: https://github.com/riscv/riscv-fast-interrupt.
> [2] Michael Clark origin work:
> https://github.com/sifive/riscv-qemu/tree/sifive-clic.
> [3] RFC Patch submission by Liu Zhiwei:
> https://lists.gnu.org/archive/html/qemu-devel/2021-04/msg01417.html
> [4] Pulp implementation of CLIC: https://github.com/pulp-platform/clic
>
> Ian Brockbank (11):
> target/riscv: Add CLIC CSR mintstatus
> target/riscv: Update CSR xintthresh in CLIC mode
> hw/intc: Add CLIC device
> target/riscv: Update CSR xie in CLIC mode
> target/riscv: Update CSR xip in CLIC mode
> target/riscv: Update CSR xtvec in CLIC mode
> target/riscv: Update CSR xnxti in CLIC mode
> target/riscv: Update interrupt handling in CLIC mode
> target/riscv: Update interrupt return in CLIC mode
> hw/riscv: add CLIC into virt machine
> tests: add riscv clic qtest case and a function in qtest
This message may contain privileged and/or confidential information. If it appears you received this message in error, please notify us by reply email and then delete the message. Thank you.
Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK.
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 00/11 v2] RISC-V: support CLIC v0.9 specification
[not found] <https://lists.gnu.org/archive/html/qemu-riscv/2024-08/msg00234.html>
2024-08-14 8:27 ` [PATCH 00/11] RISC-V: support CLIC v0.9 specification Ian Brockbank
@ 2024-08-19 16:02 ` Ian Brockbank
2024-08-19 16:02 ` [PATCH 01/11 v2] target/riscv: Add CLIC CSR mintstatus Ian Brockbank
` (13 more replies)
1 sibling, 14 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-19 16:02 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank
[Resubmission now the merge is correct]
This patch set gives an implementation of "RISC-V Core-Local Interrupt
Controller(CLIC) Version 0.9-draft-20210217". It comes from [1], where
you can find the pdf format or the source code.
This is based on the implementation from 2021 by Liu Zhiwei [3], who took
over the job from Michael Clark, who gave the first implementation of
clic-v0.7 specification [2]. I believe this implementation addresses all
the comments in Liu Zhiwei's RFC patch thread.
This implementation follows the CLIC 0.9-stable draft at 14 March 2024,
with the following exceptions and implementation details:
- the CLIC control registers are memory-mapped as per earlier drafts (in
particular version 0.9-draft, 20 June 2023)
- the indirect CSR control in 0.9-stable is not implemented
- the vector table can be either handler addresses (as per the spec)
or a jump table where each entry is processed as an instruction,
selectable with version number v0.9-jmp
- each hart is assigned its own CLIC block
- if PRV_S and/or PRV_M are supported, they are currently assumed to follow
the PRV_M registers; a subsequent update will address this
- support for PRV_S and PRV_M is selectable at CLIC instantiation
- PRV_S and PRV_U registers are currently separate from PRV_M; a subsequent
update will turn them into filtered views onto the PRV_M registers
- each hart is assigned its own CLIC block
- support for PRV_S and PRV_M is selectable at CLIC instantiation by
passing in a base address for the given modes; a base address of 0 is
treated as not supported
- PRV_S and PRV_U registers are mapped onto the PRV_M controls with
appropriate filtering for the access mode
- the RISCV virt machine has been updated to allow CLIC emulation by
passing "machine=virt,clic=on" on the command line; various other
parameters have been added to allow finer control of the CLIC behavior
The implementation (in jump-table mode) has been verified to match the
Cirrus Logic silicon (PRV_M only), which is based upon the Pulp
implementation [4] as of June 2023.
The implementation also includes a selection of qtests designed to verify
operation in all possible combinations of PRV_M, PRV_S and PRV_U.
[1] specification website: https://github.com/riscv/riscv-fast-interrupt.
[2] Michael Clark origin work:
https://github.com/sifive/riscv-qemu/tree/sifive-clic.
[3] RFC Patch submission by Liu Zhiwei:
https://lists.gnu.org/archive/html/qemu-devel/2021-04/msg01417.html
[4] Pulp implementation of CLIC: https://github.com/pulp-platform/clic
Ian Brockbank (11):
target/riscv: Add CLIC CSR mintstatus
target/riscv: Update CSR xintthresh in CLIC mode
hw/intc: Add CLIC device
target/riscv: Update CSR xie in CLIC mode
target/riscv: Update CSR xip in CLIC mode
target/riscv: Update CSR xtvec in CLIC mode
target/riscv: Update CSR xnxti in CLIC mode
target/riscv: Update interrupt handling in CLIC mode
target/riscv: Update interrupt return in CLIC mode
hw/riscv: add CLIC into virt machine
tests: add riscv clic qtest case and a function in qtest
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH 01/11 v2] target/riscv: Add CLIC CSR mintstatus
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
@ 2024-08-19 16:02 ` Ian Brockbank
2024-09-06 2:44 ` Alistair Francis
2024-08-19 16:02 ` [PATCH 02/11 v2] target/riscv: Update CSR xintthresh in CLIC mode Ian Brockbank
` (12 subsequent siblings)
13 siblings, 1 reply; 32+ messages in thread
From: Ian Brockbank @ 2024-08-19 16:02 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
CSR mintstatus holds the active interrupt level for each supported
privilege mode. sintstatus, and user, uintstatus, provide restricted
views of mintstatus.
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
---
target/riscv/cpu.h | 3 +++
target/riscv/cpu_bits.h | 11 +++++++++++
target/riscv/csr.c | 31 +++++++++++++++++++++++++++++++
3 files changed, 45 insertions(+)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 1619c3acb6..95303f50d3 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -259,6 +259,7 @@ struct CPUArchState {
bool software_seip;
uint64_t miclaim;
+ uint64_t mintstatus; /* clic-spec */
uint64_t mie;
uint64_t mideleg;
@@ -461,6 +462,8 @@ struct CPUArchState {
QEMUTimer *vstimer; /* Internal timer for VS-mode interrupt */
bool vstime_irq;
+ void *clic; /* clic interrupt controller */
+
hwaddr kernel_addr;
hwaddr fdt_addr;
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 32b068f18a..2e65495b54 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -165,6 +165,7 @@
#define CSR_MCAUSE 0x342
#define CSR_MTVAL 0x343
#define CSR_MIP 0x344
+#define CSR_MINTSTATUS 0xfb1 /* clic-spec-draft */
/* Machine-Level Window to Indirectly Accessed Registers (AIA) */
#define CSR_MISELECT 0x350
@@ -206,6 +207,7 @@
#define CSR_SCAUSE 0x142
#define CSR_STVAL 0x143
#define CSR_SIP 0x144
+#define CSR_SINTSTATUS 0xdb1 /* clic-spec-draft */
/* Sstc supervisor CSRs */
#define CSR_STIMECMP 0x14D
@@ -733,6 +735,15 @@ typedef enum RISCVException {
#define SIP_SEIP MIP_SEIP
#define SIP_LCOFIP MIP_LCOFIP
+/* mintstatus */
+#define MINTSTATUS_MIL 0xff000000 /* mil[31:24] */
+#define MINTSTATUS_SIL 0x0000ff00 /* sil[15:8] */
+#define MINTSTATUS_UIL 0x000000ff /* uil[7:0] */
+
+/* sintstatus */
+#define SINTSTATUS_SIL 0x0000ff00 /* sil[15:8] */
+#define SINTSTATUS_UIL 0x000000ff /* uil[7:0] */
+
/* MIE masks */
#define MIE_SEIE (1 << IRQ_S_EXT)
#define MIE_UEIE (1 << IRQ_U_EXT)
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index ea3560342c..f9ed7b9079 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -578,6 +578,16 @@ static RISCVException debug(CPURISCVState *env, int csrno)
return RISCV_EXCP_ILLEGAL_INST;
}
+
+static int clic(CPURISCVState *env, int csrno)
+{
+ if (env->clic) {
+ return RISCV_EXCP_NONE;
+ }
+
+ return RISCV_EXCP_ILLEGAL_INST;
+}
+
#endif
static RISCVException seed(CPURISCVState *env, int csrno)
@@ -2887,6 +2897,12 @@ static RISCVException rmw_mviph(CPURISCVState *env, int csrno,
return ret;
}
+static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mintstatus;
+ return RISCV_EXCP_NONE;
+}
+
/* Supervisor Trap Setup */
static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno,
Int128 *val)
@@ -3298,6 +3314,14 @@ static RISCVException rmw_siph(CPURISCVState *env, int csrno,
return ret;
}
+static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ /* sintstatus is a filtered view of mintstatus with the PRV_M removed */
+ target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
+ *val = env->mintstatus & mask;
+ return RISCV_EXCP_NONE;
+}
+
/* Supervisor Protection and Translation */
static RISCVException read_satp(CPURISCVState *env, int csrno,
target_ulong *val)
@@ -5594,6 +5618,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
write_mhpmcounterh },
[CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", mctr32, read_hpmcounterh,
write_mhpmcounterh },
+
+ /* Machine Mode Core Level Interrupt Controller */
+ [CSR_MINTSTATUS] = { "mintstatus", clic, read_mintstatus },
+
+ /* Supervisor Mode Core Level Interrupt Controller */
+ [CSR_SINTSTATUS] = { "sintstatus", clic, read_sintstatus },
+
[CSR_SCOUNTOVF] = { "scountovf", sscofpmf, read_scountovf,
.min_priv_ver = PRIV_VERSION_1_12_0 },
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 02/11 v2] target/riscv: Update CSR xintthresh in CLIC mode
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
2024-08-19 16:02 ` [PATCH 01/11 v2] target/riscv: Add CLIC CSR mintstatus Ian Brockbank
@ 2024-08-19 16:02 ` Ian Brockbank
2024-09-06 2:52 ` Alistair Francis
2024-08-19 16:02 ` [PATCH 03/11 v2] hw/intc: Add CLIC device Ian Brockbank
` (11 subsequent siblings)
13 siblings, 1 reply; 32+ messages in thread
From: Ian Brockbank @ 2024-08-19 16:02 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
The interrupt-level threshold (xintthresh) CSR holds an 8-bit field
for the threshold level of the associated privilege mode.
For horizontal interrupts, only the ones with higher interrupt levels
than the threshold level are allowed to preempt.
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
---
target/riscv/cpu.h | 2 ++
target/riscv/cpu_bits.h | 2 ++
target/riscv/csr.c | 28 ++++++++++++++++++++++++++++
3 files changed, 32 insertions(+)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 95303f50d3..9b5f36ad0a 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -260,6 +260,7 @@ struct CPUArchState {
uint64_t miclaim;
uint64_t mintstatus; /* clic-spec */
+ target_ulong mintthresh; /* clic-spec */
uint64_t mie;
uint64_t mideleg;
@@ -283,6 +284,7 @@ struct CPUArchState {
target_ulong stvec;
target_ulong sepc;
target_ulong scause;
+ target_ulong sintthresh; /* clic-spec */
target_ulong mtvec;
target_ulong mepc;
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 2e65495b54..ad45402370 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -166,6 +166,7 @@
#define CSR_MTVAL 0x343
#define CSR_MIP 0x344
#define CSR_MINTSTATUS 0xfb1 /* clic-spec-draft */
+#define CSR_MINTTHRESH 0x347 /* clic-spec-draft */
/* Machine-Level Window to Indirectly Accessed Registers (AIA) */
#define CSR_MISELECT 0x350
@@ -208,6 +209,7 @@
#define CSR_STVAL 0x143
#define CSR_SIP 0x144
#define CSR_SINTSTATUS 0xdb1 /* clic-spec-draft */
+#define CSR_SINTTHRESH 0x147 /* clic-spec-draft */
/* Sstc supervisor CSRs */
#define CSR_STIMECMP 0x14D
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index f9ed7b9079..9c824c0d8f 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -2903,6 +2903,18 @@ static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
return RISCV_EXCP_NONE;
}
+static int read_mintthresh(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mintthresh;
+ return 0;
+}
+
+static int write_mintthresh(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mintthresh = val;
+ return 0;
+}
+
/* Supervisor Trap Setup */
static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno,
Int128 *val)
@@ -3322,6 +3334,18 @@ static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
return RISCV_EXCP_NONE;
}
+static int read_sintthresh(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->sintthresh;
+ return 0;
+}
+
+static int write_sintthresh(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->sintthresh = val;
+ return 0;
+}
+
/* Supervisor Protection and Translation */
static RISCVException read_satp(CPURISCVState *env, int csrno,
target_ulong *val)
@@ -5621,9 +5645,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
/* Machine Mode Core Level Interrupt Controller */
[CSR_MINTSTATUS] = { "mintstatus", clic, read_mintstatus },
+ [CSR_MINTTHRESH] = { "mintthresh", clic, read_mintthresh,
+ write_mintthresh },
/* Supervisor Mode Core Level Interrupt Controller */
[CSR_SINTSTATUS] = { "sintstatus", clic, read_sintstatus },
+ [CSR_SINTTHRESH] = { "sintthresh", clic, read_sintthresh,
+ write_sintthresh },
[CSR_SCOUNTOVF] = { "scountovf", sscofpmf, read_scountovf,
.min_priv_ver = PRIV_VERSION_1_12_0 },
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 03/11 v2] hw/intc: Add CLIC device
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
2024-08-19 16:02 ` [PATCH 01/11 v2] target/riscv: Add CLIC CSR mintstatus Ian Brockbank
2024-08-19 16:02 ` [PATCH 02/11 v2] target/riscv: Update CSR xintthresh in CLIC mode Ian Brockbank
@ 2024-08-19 16:02 ` Ian Brockbank
2024-08-19 16:02 ` [PATCH 04/11 v2] target/riscv: Update CSR xie in CLIC mode Ian Brockbank
` (10 subsequent siblings)
13 siblings, 0 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-19 16:02 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
The Core-Local Interrupt Controller (CLIC) provides low-latency,
vectored, pre-emptive interrupts for RISC-V systems.
The CLIC also supports a new Selective Hardware Vectoring feature
that allow users to optimize each interrupt for either faster
response or smaller code size.
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
---
hw/intc/Kconfig | 3 +
hw/intc/meson.build | 3 +-
hw/intc/riscv_clic.c | 1037 ++++++++++++++++++++++++++++++++++
hw/riscv/Kconfig | 1 +
include/hw/intc/riscv_clic.h | 213 +++++++
target/riscv/cpu.h | 2 +
target/riscv/cpu_bits.h | 17 +
7 files changed, 1275 insertions(+), 1 deletion(-)
create mode 100644 hw/intc/riscv_clic.c
create mode 100644 include/hw/intc/riscv_clic.h
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index dd405bdb5d..1cd4c2f58c 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -81,6 +81,9 @@ config SIFIVE_PLIC
bool
select MSI_NONBROKEN
+config RISCV_CLIC
+ bool
+
config GOLDFISH_PIC
bool
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index f4d81eb8e4..a9207dfb9e 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -59,9 +59,10 @@ specific_ss.add(when: 'CONFIG_S390_FLIC_KVM', if_true: files('s390_flic_kvm.c'))
specific_ss.add(when: 'CONFIG_SH_INTC', if_true: files('sh_intc.c'))
specific_ss.add(when: 'CONFIG_RISCV_ACLINT', if_true: files('riscv_aclint.c'))
specific_ss.add(when: 'CONFIG_RISCV_APLIC', if_true: files('riscv_aplic.c'))
+specific_ss.add(when: 'CONFIG_RISCV_CLIC', if_true: files('riscv_clic.c'))
specific_ss.add(when: 'CONFIG_RISCV_IMSIC', if_true: files('riscv_imsic.c'))
specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.c'))
-specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c', 'xive2.c'))
+specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c'))
specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'],
if_true: files('xics_kvm.c'))
specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('xics_spapr.c', 'spapr_xive.c'))
diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
new file mode 100644
index 0000000000..1800e84dfd
--- /dev/null
+++ b/hw/intc/riscv_clic.c
@@ -0,0 +1,1037 @@
+/*
+ * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
+ * Copyright (c) 2024 Cirrus Logic, Inc and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This implementation follows the CLIC 0.9-stable draft at 14 March 2024,
+ * with the following exceptions and implementation details:
+ * - the CLIC control registers are memory-mapped as per earlier drafts (in
+ * particular version 0.9-draft, 20 June 2023)
+ * - the indirect CSR control in 0.9-stable is not implemented
+ * - the vector table can be either handler addresses (as per the spec)
+ or a jump table where each entry is processed as an instruction,
+ selectable with version number v0.9-jmp
+ * - each hart is assigned its own CLIC block
+ * - support for PRV_S and PRV_M is selectable at CLIC instantiation by
+ * passing in a base address for the given modes; a base address of 0 is
+ * treated as not supported
+ * - PRV_S and PRV_U registers are mapped onto the PRV_M controls with
+ * appropriate filtering for the access mode
+ *
+ * The implementation has a RISCVCLICState per hart, with a RISCVCLICView
+ * for each mode subsidiary to that. Each view knows its access mode and base
+ * address, as well as the RISCVCLICState with which it is associated.
+ *
+ * MMIO accesses go through the view, allowing the appropriate permissions to
+ * be enforced when accessing the parent RISCVCLICState for the settings.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "qemu/main-loop.h"
+#include "hw/sysbus.h"
+#include "sysemu/qtest.h"
+#include "target/riscv/cpu.h"
+#include "hw/qdev-properties.h"
+#include "hw/intc/riscv_clic.h"
+
+static const char *modeview_name[] = {
+ TYPE_RISCV_CLIC "_prv_u", /* PRV_U */
+ TYPE_RISCV_CLIC "_prv_s", /* PRV_S */
+ NULL, /* reserved */
+ TYPE_RISCV_CLIC "_prv_m", /* PRV_M */
+};
+
+/*
+ * The 2-bit trig WARL field specifies the trigger type and polarity for each
+ * interrupt input. Bit 1, trig[0], is defined as "edge-triggered"
+ * (0: level-triggered, 1: edge-triggered); while bit 2, trig[1], is defined as
+ * "negative-edge" (0: positive-edge, 1: negative-edge). (Section 3.6)
+ */
+
+static inline TRIG_TYPE
+riscv_clic_get_trigger_type(RISCVCLICState *clic, size_t irq)
+{
+ return get_field(clic->clicintattr[irq], CLIC_INTATTR_TRIG);
+}
+
+static inline bool
+riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq)
+{
+ TRIG_TYPE trig_type = riscv_clic_get_trigger_type(clic, irq);
+ return trig_type & CLIC_INTATTR_TRIG_EDGE;
+}
+
+static inline bool
+riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq)
+{
+ uint32_t shv = get_field(clic->clicintattr[irq], CLIC_INTATTR_SHV);
+ return shv && clic->shv_enabled;
+}
+
+static uint8_t
+riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
+{
+ int nlbits = min(clic->mnlbits, clic->clicintctlbits);
+
+ uint8_t mask_il = ((1 << nlbits) - 1) << (8 - nlbits);
+ uint8_t mask_padding = (1 << (8 - nlbits)) - 1;
+ /* unused level bits are set to 1 */
+ return (intctl & mask_il) | mask_padding;
+}
+
+static uint8_t
+riscv_clic_get_interrupt_priority(RISCVCLICState *clic, uint8_t intctl)
+{
+ int npbits = clic->clicintctlbits - clic->mnlbits;
+ uint8_t mask_priority = ((1 << npbits) - 1) << (8 - npbits);
+ uint8_t mask_padding = (1 << (8 - npbits)) - 1;
+
+ if (npbits < 0) {
+ return UINT8_MAX;
+ }
+ /* unused priority bits are set to 1 */
+ return (intctl & mask_priority) | mask_padding;
+}
+
+static void
+riscv_clic_intcfg_decode(RISCVCLICState *clic, uint16_t intcfg,
+ uint8_t *mode, uint8_t *level,
+ uint8_t *priority)
+{
+ *mode = intcfg >> 8;
+ *level = riscv_clic_get_interrupt_level(clic, intcfg & 0xff);
+ *priority = riscv_clic_get_interrupt_priority(clic, intcfg & 0xff);
+}
+
+static void riscv_clic_next_interrupt(void *opaque)
+{
+ /*
+ * Scan active list for highest priority pending interrupts
+ * comparing against this harts mintstatus register and interrupt
+ * the core if we have a higher priority interrupt to deliver
+ */
+ RISCVCLICState *clic = (RISCVCLICState *)opaque;
+ CPUState *cpu = cpu_by_arch_id(clic->hartid);
+ CPURISCVState *env = cpu ? cpu_env(cpu) : NULL;
+
+ if (!env) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "aclint-swi: invalid hartid: %u", clic->hartid);
+ return;
+ }
+
+ BQL_LOCK_GUARD();
+
+ int il[4] = {
+ MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
+ clic->uintthresh & 0xff), /* PRV_U */
+ MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
+ clic->sintthresh & 0xff), /* PRV_S */
+ 0, /* reserved */
+ MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
+ clic->mintthresh & 0xff) /* PRV_M */
+ };
+
+ /* Get sorted list of enabled interrupts for this hart */
+ CLICActiveInterrupt *active = clic->active_list;
+ size_t active_count = clic->active_count;
+ uint8_t mode, level, priority;
+
+ /* Loop through the enabled interrupts sorted by mode+priority+level */
+ while (active_count) {
+ riscv_clic_intcfg_decode(clic, active->intcfg, &mode, &level,
+ &priority);
+ if (mode < env->priv || (mode == env->priv && level < il[mode])) {
+ /*
+ * No pending interrupts with high enough mode+priority+level
+ * break and clear pending interrupt for this hart
+ */
+ break;
+ }
+ /* Check pending interrupt with high enough mode+priority+level */
+ if (clic->clicintip[active->irq]) {
+ /* Clean vector edge-triggered pending */
+ if (riscv_clic_is_edge_triggered(clic, active->irq) &&
+ riscv_clic_is_shv_interrupt(clic, active->irq)) {
+ clic->clicintip[active->irq] = 0;
+ }
+ /* Post pending interrupt for this hart */
+ clic->exccode = active->irq |
+ mode << RISCV_EXCP_CLIC_MODE_SHIFT |
+ level << RISCV_EXCP_CLIC_LEVEL_SHIFT;
+ qemu_set_irq(clic->cpu_irq, 1);
+ return;
+ }
+ /* Check next enabled interrupt */
+ active_count--;
+ active++;
+ }
+}
+
+/*
+ * Any interrupt i that is not accessible to S-mode or U-Mode
+ * appears as hard-wired zeros in clicintip[i], clicintie[i],
+ * clicintattr[i], and clicintctl[i].(Section 3.9)(Section 3.10)
+ */
+static bool
+riscv_clic_check_visible(RISCVCLICState *clic, int mode, int irq)
+{
+ uint8_t intattr_mode = get_field(clic->clicintattr[irq],
+ CLIC_INTATTR_MODE);
+
+ if (!clic->prv_s && !clic->prv_u) { /* M */
+ return mode == PRV_M;
+ } else if (clic->prv_s && clic->prv_u) { /* M/S/U */
+ switch (clic->nmbits) {
+ case 0:
+ return mode == PRV_M;
+ case 1:
+ return (mode == PRV_M) || (intattr_mode <= PRV_S);
+ case 2:
+ return mode >= intattr_mode;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: nmbits can only be 0 or 1 or 2 for M/S/U hart");
+ exit(1);
+ }
+ } else { /* M/S or M/U */
+ switch (clic->nmbits) {
+ case 0:
+ return mode == PRV_M;
+ case 1:
+ return (mode == PRV_M) || (intattr_mode <= PRV_S);
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: nmbits can only be 0 or 1 for M/S or M/U hart");
+ exit(1);
+ }
+ }
+ return false;
+}
+
+/*
+ * For level-triggered interrupts, software writes to pending bits are
+ * ignored completely. (Section 3.4)
+ */
+static bool
+riscv_clic_validate_intip(RISCVCLICState *clic, int irq)
+{
+ return riscv_clic_is_edge_triggered(clic, irq);
+}
+
+static void
+riscv_clic_update_intip(RISCVCLICState *clic, int irq, uint64_t value)
+{
+ clic->clicintip[irq] = !!value;
+ riscv_clic_next_interrupt(clic);
+}
+
+/*
+ * For security purpose, the field can only be set to a privilege
+ * level that is equal mode to or lower than the currently running
+ * privilege level.(Section 3.6)
+ */
+static bool riscv_clic_validate_intattr(RISCVCLICState *clic, uint8_t value)
+{
+ int mode = extract64(value, CLIC_INTATTR_MODE_SHIFT,
+ CLIC_INTATTR_MODE_WIDTH);
+
+ if (!qtest_enabled()) {
+ CPURISCVState *env = cpu_env(current_cpu);
+ if (env->priv < mode) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ * Work out the effective requested mode based on the number of nmbits.
+ *
+ * priv-modes nmbits mode Interpretation
+ * M 0 xx M-mode interrupt
+ *
+ * M/U 0 xx M-mode interrupt
+ * M/U 1 0x U-mode interrupt
+ * M/U 1 1x M-mode interrupt
+ *
+ * M/S 0 xx M-mode interrupt
+ * M/S 1 0x S-mode interrupt
+ * M/S 1 1x M-mode interrupt
+ *
+ * M/S/U 0 xx M-mode interrupt
+ * M/S/U 1 0x S-mode interrupt
+ * M/S/U 1 1x M-mode interrupt
+ * M/S/U 2 00 U-mode interrupt
+ * M/S/U 2 01 S-mode interrupt
+ * M/S/U 2 10 Reserved
+ * M/S/U 2 11 M-mode interrupt
+ *
+ * M/S/U 3 xx Reserved
+ */
+static uint8_t riscv_clic_effective_mode(RISCVCLICState *clic, uint8_t intattr)
+{
+ uint8_t mode = get_field(intattr, CLIC_INTATTR_MODE);
+
+ switch (clic->nmbits) {
+ case 0:
+ mode = PRV_M;
+ break;
+
+ case 1:
+ if (mode <= PRV_S) {
+ if (clic->prv_s) {
+ mode = PRV_S;
+ } else {
+ assert(clic->prv_u);
+ mode = PRV_U;
+ }
+ } else {
+ mode = PRV_M;
+ }
+ break;
+
+ case 2:
+ /* no modification required */
+ break;
+
+ default:
+ /* We validate nmbits so this shouldn't be possible */
+ assert(clic->nmbits <= 2);
+ }
+
+ return mode;
+}
+
+/* Return target interrupt number */
+static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
+{
+ return addr / 4;
+}
+
+/* Encode the priority and IRQ as a single sortable value */
+static inline int riscv_clic_encode_priority(const CLICActiveInterrupt *i)
+{
+ /* Highest mode+level+priority */
+ int priority = (i->intcfg & CLIC_INTCFG_MASK) << CLIC_IRQ_BITS;
+ /* Highest irq number */
+ int irq = i->irq & CLIC_IRQ_MASK;
+ /* Combined */
+ return priority | irq;
+}
+
+static int riscv_clic_active_compare(const void *a, const void *b)
+{
+ return riscv_clic_encode_priority(b) - riscv_clic_encode_priority(a);
+}
+
+static void
+riscv_clic_update_intie(RISCVCLICState *clic, int mode,
+ int irq, uint64_t new_intie)
+{
+ CLICActiveInterrupt *active_list = clic->active_list;
+
+ uint8_t old_intie = clic->clicintie[irq];
+ clic->clicintie[irq] = !!new_intie;
+
+ /* Add to or remove from list of active interrupts */
+ if (new_intie && !old_intie) {
+ uint16_t intcfg = (mode << CLIC_INTCFG_MODE_SHIFT) |
+ clic->clicintctl[irq];
+ active_list[clic->active_count].intcfg = intcfg;
+ active_list[clic->active_count].irq = irq;
+ clic->active_count++;
+ } else if (!new_intie && old_intie) {
+ CLICActiveInterrupt key = {
+ (mode << 8) | clic->clicintctl[irq], irq
+ };
+ CLICActiveInterrupt *result = bsearch(&key,
+ active_list, clic->active_count,
+ sizeof(CLICActiveInterrupt),
+ riscv_clic_active_compare);
+ assert(result);
+ size_t elem = result - active_list;
+ size_t sz = --clic->active_count - elem;
+ memmove(&result[0], &result[1], sz);
+ }
+
+ /* Sort list of active interrupts */
+ qsort(active_list, clic->active_count,
+ sizeof(CLICActiveInterrupt),
+ riscv_clic_active_compare);
+
+ riscv_clic_next_interrupt(clic);
+}
+
+static void
+riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
+ uint64_t value, unsigned size,
+ int mode, int irq)
+{
+ int req = extract32(addr, 0, 2);
+
+ /* visibility is checked in riscv_clic_write */
+
+ if (irq >= clic->num_sources) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid irq %u: 0x%" HWADDR_PRIx "\n", irq, addr);
+ return;
+ }
+
+ switch (req) {
+ case 0: /* clicintip[i] */
+ if (riscv_clic_validate_intip(clic, irq)) {
+ /*
+ * The actual pending bit is located at bit 0 (i.e., the
+ * least significant bit). In case future extensions expand the bit
+ * field, from FW perspective clicintip[i]=zero means no interrupt
+ * pending, and clicintip[i]!=0 (not just 1) indicates an
+ * interrupt is pending. (Section 3.4)
+ */
+ if (value != clic->clicintip[irq]) {
+ riscv_clic_update_intip(clic, irq, value);
+ }
+ }
+ /* Handle a 32-bit write */
+ if (size > 1) {
+ unsigned width = min(size, 4);
+ unsigned i;
+ for (i = 1; i < width; i++) {
+ uint64_t local_value = (value >> (i * 8)) & 0xFF;
+ riscv_clic_hart_write(clic, addr + i, local_value,
+ 1, mode, irq);
+ }
+ }
+ break;
+
+ case 1: /* clicintie[i] */
+ if (clic->clicintie[irq] != value) {
+ riscv_clic_update_intie(clic, mode, irq, value);
+ }
+ break;
+
+ case 2: /* clicintattr[i] */
+ uint8_t field_mode = riscv_clic_effective_mode(clic, value);
+ if (PRV_RESERVED == field_mode) {
+ field_mode = get_field(clic->clicintattr[irq],
+ CLIC_INTATTR_MODE);
+ }
+ value = set_field(value, CLIC_INTATTR_MODE, field_mode);
+ if (riscv_clic_validate_intattr(clic, value)) {
+ if (clic->clicintattr[irq] != value) {
+ clic->clicintattr[irq] = value;
+ riscv_clic_next_interrupt(clic);
+ }
+ }
+ break;
+
+ case 3: /* clicintctl[i] */
+ if (value != clic->clicintctl[irq]) {
+ clic->clicintctl[irq] = value;
+ riscv_clic_next_interrupt(clic);
+ }
+ break;
+ }
+}
+
+static uint64_t
+riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, unsigned size,
+ int mode, int irq)
+{
+ int req = extract32(addr, 0, 2);
+ int i;
+
+ /* visibility is checked in riscv_clic_read */
+
+ if (irq >= clic->num_sources) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid irq %u: 0x%" HWADDR_PRIx "\n", irq, addr);
+ return 0;
+ }
+
+ switch (req) {
+ case 0: /* clicintip[i] */
+ uint64_t retval = clic->clicintip[irq];
+ if (size > 1) {
+ /* Handle a multi-part read */
+ for (i = 1; i < size; ++i) {
+ uint64_t subval =
+ (riscv_clic_hart_read(clic, addr + i, 1, mode, irq) & 0xFF);
+ retval |= subval << (i * 8);
+ }
+ }
+ return retval;
+
+ case 1: /* clicintie[i] */
+ return clic->clicintie[irq];
+ case 2: /* clicintattr[i] */
+ /*
+ * clicintattr register layout
+ * Bits Field
+ * 7:6 mode
+ * 5:3 reserved (WPRI 0)
+ * 2:1 trig
+ * 0 shv
+ */
+ uint8_t intattr = clic->clicintattr[irq] & CLIC_INTATTR_MASK;
+ int field_mode = riscv_clic_effective_mode(clic, intattr);
+ intattr = set_field(intattr, CLIC_INTATTR_MODE, field_mode);
+ return intattr;
+
+ case 3: /* clicintctl[i] */
+ /*
+ * The implemented bits are kept left-justified in the most-significant
+ * bits of each 8-bit clicintctl[i] register, with the lower
+ * unimplemented bits treated as hardwired to 1.(Section 3.7)
+ */
+ return clic->clicintctl[irq] |
+ ((1 << (8 - clic->clicintctlbits)) - 1);
+ }
+
+ return 0;
+}
+
+static void
+riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
+{
+ RISCVCLICView *clicview = opaque;
+ RISCVCLICState *clic = clicview->clic;
+ CPUState *cpu = cpu_by_arch_id(clic->hartid);
+ CPURISCVState *env = cpu ? cpu_env(cpu) : NULL;
+ hwaddr clic_size = clic->clic_size;
+ int mode = clicview->mode, irq;
+ const char *current_mode_str = (PRV_M == env->priv) ? "PRV_M" :
+ (PRV_S == env->priv) ? "PRV_S" :
+ (PRV_U == env->priv) ? "PRV_U" :
+ "unknown";
+ const char *access_mode_str = (PRV_M == mode) ? "PRV_M" :
+ (PRV_S == mode) ? "PRV_S" :
+ (PRV_U == mode) ? "PRV_U" :
+ "unknown";
+
+ assert(addr < clic_size);
+
+ if (mode > env->priv) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid write to %s CLIC registers in %s mode\n",
+ access_mode_str, current_mode_str);
+ return;
+ }
+
+ if (addr < CLIC_INTCTL_BASE) {
+ assert(addr % 4 == 0);
+ int index = addr / 4;
+ switch (index) {
+ case 0: /* cliccfg */
+ {
+ uint8_t mnlbits = extract32(value, 0, 4);
+ uint8_t nmbits = extract32(value, 4, 2);
+ uint8_t snlbits = extract32(value, 16, 4);
+ uint8_t unlbits = extract32(value, 24, 4);
+
+ /*
+ * The 4-bit cliccfg.mnlbits WARL field.
+ * Valid values are 0—8.
+ */
+ if (mnlbits <= 8 && PRV_M == mode) {
+ clic->mnlbits = mnlbits;
+ }
+ if (clic->prv_s && snlbits <= 8 && mode >= PRV_S) {
+ clic->snlbits = snlbits;
+ }
+ if (clic->prv_u && unlbits <= 8) {
+ clic->unlbits = unlbits;
+ }
+
+ /*
+ * The nmbits field - the number of bits for the mode.
+ * Valid values are given by implemented privileges.
+ * This is only accessible in PRV_M.
+ */
+ if (PRV_M == mode) {
+ if (clic->prv_s && clic->prv_u) {
+ if (nmbits <= 2) {
+ clic->nmbits = nmbits;
+ }
+ } else if (clic->prv_s || clic->prv_u) {
+ if (nmbits <= 1) {
+ clic->nmbits = nmbits;
+ }
+ } else {
+ if (nmbits == 0) {
+ clic->nmbits = 0;
+ }
+ }
+ }
+
+ break;
+ }
+ case CLIC_INTTRIG_START ... CLIC_INTTRIG_END: /* clicinttrig */
+ {
+ uint32_t interrupt_number = value & CLIC_INTTRIG_IRQN;
+ if (interrupt_number <= clic->num_sources) {
+ value &= CLIC_INTTRIG_MASK;
+ clic->clicinttrig[index - CLIC_INTTRIG_START] = value;
+ /* TODO: How does this cause the interrupt to trigger? */
+ }
+ break;
+ }
+ case 2: /* mintthresh - only in CLIC spec v0.8 */
+ if (0 == strcmp(clic->version, "v0.8")) {
+ clic->mintthresh = value;
+ break;
+ }
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid write addr: 0x%" HWADDR_PRIx "\n",
+ addr);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid write addr: 0x%" HWADDR_PRIx "\n",
+ addr);
+ return;
+ }
+ } else {
+ addr -= CLIC_INTCTL_BASE;
+ irq = riscv_clic_get_irq(clic, addr);
+
+ if (riscv_clic_check_visible(clic, mode, irq)) {
+ riscv_clic_hart_write(clic, addr, value, size, mode, irq);
+ }
+ }
+}
+
+static uint64_t
+riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
+{
+ RISCVCLICView *clicview = opaque;
+ RISCVCLICState *clic = clicview->clic;
+ CPUState *cpu = cpu_by_arch_id(clic->hartid);
+ CPURISCVState *env = cpu ? cpu_env(cpu) : NULL;
+ hwaddr clic_size = clic->clic_size;
+ int mode = clicview->mode, irq;
+
+ assert(addr < clic_size);
+
+ if (mode > env->priv) {
+ const char *current_mode_str = (PRV_M == env->priv) ? "PRV_M" :
+ (PRV_S == env->priv) ? "PRV_S" :
+ (PRV_U == env->priv) ? "PRV_U" :
+ "unknown";
+ const char *access_mode_str = (PRV_M == mode) ? "PRV_M" :
+ (PRV_S == mode) ? "PRV_S" :
+ (PRV_U == mode) ? "PRV_U" :
+ "unknown";
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid write to %s CLIC registers in %s mode\n",
+ access_mode_str, current_mode_str);
+ return 0;
+ }
+
+ if (addr < CLIC_INTCTL_BASE) {
+ assert(addr % 4 == 0);
+ int index = addr / 4;
+ switch (index) {
+ case 0:
+ /*
+ * cliccfg register layout
+ *
+ * Bits Field
+ * 31:28 reserved (WPRI 0)
+ * 27:24 unlbits
+ * 23:20 reserved (WPRI 0)
+ * 19:16 snlbits
+ * 15:6 reserved (WPRI 0)
+ * 5:4 nmbits
+ * 3:0 mnlbits
+ */
+ uint64_t cliccfg = 0;
+ if (PRV_M == mode) {
+ cliccfg = clic->mnlbits | (clic->nmbits << 4);
+ }
+ if (clic->prv_s && mode >= PRV_S) {
+ cliccfg |= clic->snlbits << 16;
+ }
+ if (clic->prv_u && mode >= PRV_U) {
+ cliccfg |= clic->unlbits << 24;
+ }
+ return cliccfg;
+
+ case CLIC_INTTRIG_START ... CLIC_INTTRIG_END: /* clicinttrig */
+ /*
+ * clicinttrig register layout
+ *
+ * Bits Field
+ * 31 enable
+ * 30:13 reserved (WARL 0)
+ * 12:0 interrupt_number
+ */
+ uint64_t inttrig = clic->clicinttrig[index - CLIC_INTTRIG_START];
+ return inttrig & CLIC_INTTRIG_MASK;
+
+ case 2: /* mintthresh - only in CLIC spec v0.8 */
+ if (0 == strcmp(clic->version, "v0.8")) {
+ return clic->mintthresh;
+ break;
+ }
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid read : 0x%" HWADDR_PRIx "\n",
+ addr);
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "clic: invalid read : 0x%" HWADDR_PRIx "\n",
+ addr);
+ break;
+ }
+ } else {
+ addr -= CLIC_INTCTL_BASE;
+ irq = riscv_clic_get_irq(clic, addr);
+
+ if (riscv_clic_check_visible(clic, mode, irq)) {
+ return riscv_clic_hart_read(clic, addr, size, mode, irq);
+ }
+ }
+
+ return 0;
+}
+
+static void riscv_clic_set_irq(void *opaque, int id, int level)
+{
+ RISCVCLICState *clic = opaque;
+ TRIG_TYPE type;
+
+ type = riscv_clic_get_trigger_type(clic, id);
+
+ /*
+ * In general, the edge-triggered interrupt state should be kept in pending
+ * bit, while the level-triggered interrupt should be kept in the level
+ * state of the incoming wire.
+ *
+ * For CLIC, model the level-triggered interrupt by read-only pending bit.
+ */
+ if (level) {
+ switch (type) {
+ case POSITIVE_LEVEL:
+ case POSITIVE_EDGE:
+ riscv_clic_update_intip(clic, id, level);
+ break;
+ case NEG_LEVEL:
+ riscv_clic_update_intip(clic, id, !level);
+ break;
+ case NEG_EDGE:
+ break;
+ default:
+ /* It's a 2-bit field so this shouldn't be possible */
+ assert(type <= 3);
+ }
+ } else {
+ switch (type) {
+ case POSITIVE_LEVEL:
+ riscv_clic_update_intip(clic, id, level);
+ break;
+ case POSITIVE_EDGE:
+ break;
+ case NEG_LEVEL:
+ case NEG_EDGE:
+ riscv_clic_update_intip(clic, id, !level);
+ break;
+ default:
+ /* It's a 2-bit field so this shouldn't be possible */
+ assert(type <= 3);
+ }
+ }
+}
+
+static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level)
+{
+ CPURISCVState *env = (CPURISCVState *)opaque;
+ RISCVCLICState *clic = env->clic;
+
+ if (level) {
+ env->exccode = clic->exccode;
+ cpu_interrupt(env_cpu(env), CPU_INTERRUPT_CLIC);
+ }
+}
+
+static const MemoryRegionOps riscv_clic_ops = {
+ .read = riscv_clic_read,
+ .write = riscv_clic_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 8
+ }
+};
+
+static void riscv_clic_realize(DeviceState *dev, Error **errp)
+{
+ RISCVCLICState *clic = RISCV_CLIC(dev);
+ size_t irqs = clic->num_sources;
+
+ if (clic->prv_s && clic->prv_u) {
+ clic->nmbits = 2;
+ } else if (clic->prv_s || clic->prv_u) {
+ clic->nmbits = 1;
+ } else {
+ clic->nmbits = 0;
+ }
+
+ clic->clicintip = g_new0(uint8_t, irqs);
+ clic->clicintie = g_new0(uint8_t, irqs);
+ clic->clicintattr = g_new0(uint8_t, irqs);
+ clic->clicintctl = g_new0(uint8_t, irqs);
+ clic->active_list = g_new0(CLICActiveInterrupt, irqs);
+
+ if (!clic->prv_s) {
+ clic->snlbits = 0;
+ }
+ if (!clic->prv_u) {
+ clic->unlbits = 0;
+ }
+
+ /* Allocate irqs through gpio, so that we can use qtest */
+ qdev_init_gpio_in(dev, riscv_clic_set_irq, irqs);
+ qdev_init_gpio_out(dev, &clic->cpu_irq, 1);
+
+ assert(cpu_exists(clic->hartid));
+ RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(clic->hartid));
+ qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler, &cpu->env, 1);
+ qdev_connect_gpio_out(dev, 0, irq);
+ cpu->env.clic = clic;
+}
+
+static void riscv_clic_view_realize(DeviceState *dev, Error **errp)
+{
+ RISCVCLICView *clicview = RISCV_CLIC_VIEW(dev);
+ RISCVCLICState *clic = clicview->clic;
+
+ memory_region_init_io(&clicview->mmio, OBJECT(clicview), &riscv_clic_ops,
+ clicview, TYPE_RISCV_CLIC_VIEW, clic->clic_size);
+ sysbus_init_mmio(SYS_BUS_DEVICE(clicview), &clicview->mmio);
+}
+
+static Property riscv_clic_properties[] = {
+ DEFINE_PROP_BOOL("shv-enabled", RISCVCLICState, shv_enabled, true),
+ DEFINE_PROP_BOOL("jump-table", RISCVCLICState, jump_table, false),
+ DEFINE_PROP_UINT8("mnlbits", RISCVCLICState, mnlbits, 8),
+ DEFINE_PROP_UINT8("snlbits", RISCVCLICState, snlbits, 8),
+ DEFINE_PROP_UINT8("unlbits", RISCVCLICState, unlbits, 8),
+ DEFINE_PROP_INT32("hartid", RISCVCLICState, hartid, 0),
+ DEFINE_PROP_UINT32("num-sources", RISCVCLICState, num_sources, 0),
+ DEFINE_PROP_UINT32("clic-size", RISCVCLICState, clic_size, 0),
+ DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICState, clicintctlbits, 0),
+ DEFINE_PROP_STRING("version", RISCVCLICState, version),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static Property riscv_clic_view_properties[] = {
+ DEFINE_PROP_LINK("clic", RISCVCLICView, clic,
+ TYPE_RISCV_CLIC, RISCVCLICState *),
+ DEFINE_PROP_UINT8("mode", RISCVCLICView, mode, PRV_U),
+ DEFINE_PROP_UINT64("clicbase", RISCVCLICView, clicbase, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void riscv_clic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = riscv_clic_realize;
+ device_class_set_props(dc, riscv_clic_properties);
+}
+
+static void riscv_clic_view_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = riscv_clic_view_realize;
+ device_class_set_props(dc, riscv_clic_view_properties);
+}
+
+static const TypeInfo riscv_clic_info = {
+ .name = TYPE_RISCV_CLIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RISCVCLICState),
+ .class_init = riscv_clic_class_init,
+};
+
+static const TypeInfo riscv_clic_view_info = {
+ .name = TYPE_RISCV_CLIC_VIEW,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RISCVCLICView),
+ .class_init = riscv_clic_view_init,
+};
+
+static void riscv_clic_register_types(void)
+{
+ type_register_static(&riscv_clic_info);
+ type_register_static(&riscv_clic_view_info);
+}
+
+type_init(riscv_clic_register_types)
+
+/*
+ * riscv_clic_view_create:
+ *
+ * @clic: machine-mode CLIC this is an view onto
+ * @clicbase: base address of this view CLIC memory-mapped registers
+ * @mode: the mode of the view - PRV_S or PRV_U
+ *
+ * Returns: the new view
+ */
+static RISCVCLICView *riscv_clic_view_create(RISCVCLICState *clic,
+ hwaddr clicbase, int mode)
+{
+ DeviceState *dev = qdev_new(TYPE_RISCV_CLIC_VIEW);
+ RISCVCLICView *clicview = RISCV_CLIC_VIEW(dev);
+ Object *obj = OBJECT(dev);
+ Object *clicobj = OBJECT(clic);
+
+ assert(0 != clic); /* this should exist */
+ assert(0 != clicbase); /* this should exist */
+ assert(0 == (clicbase & 0xfff)); /* base should be 4KiB-aligned */
+ assert(PRV_M == mode || PRV_S == mode || PRV_U == mode);
+
+ object_property_add_child(clicobj, modeview_name[mode], obj);
+ clicview->clic = clic;
+
+ qdev_prop_set_uint8(dev, "mode", mode);
+ qdev_prop_set_uint64(dev, "clicbase", clicbase);
+
+ if (!sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal)) {
+ object_unparent(obj);
+ return NULL;
+ }
+
+ memory_region_init_io(&clicview->mmio, OBJECT(dev), &riscv_clic_ops,
+ clicview, TYPE_RISCV_CLIC_VIEW, clic->clic_size);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, clicbase);
+
+ return clicview;
+}
+
+/*
+ * riscv_clic_create:
+ *
+ * @mclicbase: base address of PRV_M CLIC memory-mapped registers
+ * @sclicbase: base address of PRV_S CLIC memory-mapped registers
+ * @uclicbase: base address of PRV_U CLIC memory-mapped registers
+ * @hartid: the HART ID this CLIC is serving
+ * @num_sources: number of interrupts supporting by each aperture
+ * @clicintctlbits: bits are actually implemented in the clicintctl registers
+ * @version: clic version, such as "v0.9"; append "-jmp" for jump table instead
+ * of function pointers
+ *
+ * Returns: the device object
+ */
+DeviceState *riscv_clic_create(hwaddr mclicbase, hwaddr sclicbase,
+ hwaddr uclicbase, uint32_t hartid,
+ uint32_t num_sources, uint8_t clicintctlbits,
+ const char *version)
+{
+ DeviceState *dev = qdev_new(TYPE_RISCV_CLIC);
+ RISCVCLICState *s = RISCV_CLIC(dev);
+ g_autofree char **tokens = NULL;
+ char *base_version;
+ bool jump_table = false;
+
+ assert(num_sources <= CLIC_MAX_IRQ_COUNT);
+ assert(cpu_exists(hartid));
+ assert(clicintctlbits <= MAX_CLIC_INTCTLBITS);
+ assert(0 == (mclicbase & 0xfff)); /* base should be 4KiB-aligned */
+
+ /* Parse the version */
+ tokens = g_strsplit(version, "-", 2);
+ base_version = g_strdup(tokens[0]);
+ assert(0 == strcmp(base_version, "v0.9"));
+ if (tokens[1]) {
+ assert(0 == strcmp(tokens[1], "jmp"));
+ jump_table = true;
+ }
+
+ qdev_prop_set_uint32(dev, "hartid", hartid);
+ qdev_prop_set_uint32(dev, "num-sources", num_sources);
+ qdev_prop_set_uint32(dev, "clic-size", num_sources * 4 + CLIC_INTCTL_BASE);
+ qdev_prop_set_uint32(dev, "clicintctlbits", clicintctlbits);
+ qdev_prop_set_string(dev, "version", base_version);
+ qdev_prop_set_bit(dev, "jump-table", jump_table);
+
+ s->prv_m = riscv_clic_view_create(s, mclicbase, PRV_M);
+ if (sclicbase) {
+ s->prv_s = riscv_clic_view_create(s, sclicbase, PRV_S);
+ }
+ if (uclicbase) {
+ s->prv_u = riscv_clic_view_create(s, uclicbase, PRV_U);
+ }
+
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ return dev;
+}
+
+void riscv_clic_get_next_interrupt(void *opaque)
+{
+ RISCVCLICState *clic = opaque;
+ riscv_clic_next_interrupt(clic);
+}
+
+bool riscv_clic_shv_interrupt(void *opaque, int irq)
+{
+ RISCVCLICState *clic = opaque;
+ return riscv_clic_is_shv_interrupt(clic, irq);
+}
+
+bool riscv_clic_edge_triggered(void *opaque, int irq)
+{
+ RISCVCLICState *clic = opaque;
+ return riscv_clic_is_edge_triggered(clic, irq);
+}
+
+bool riscv_clic_use_jump_table(void *opaque)
+{
+ RISCVCLICState *clic = opaque;
+ return clic->jump_table;
+}
+
+void riscv_clic_clean_pending(void *opaque, int irq)
+{
+ RISCVCLICState *clic = opaque;
+ clic->clicintip[irq] = 0;
+}
+
+/*
+ * The new CLIC interrupt-handling mode is encoded as a new state in
+ * the existing WARL xtvec register, where the low two bits are 11.
+ */
+bool riscv_clic_is_clic_mode(CPURISCVState *env)
+{
+ target_ulong xtvec = (env->priv == PRV_M) ? env->mtvec : env->stvec;
+ return env->clic && ((xtvec & XTVEC_MODE) == XTVEC_CLIC);
+}
+
+void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
+ int *il, int *irq)
+{
+ *irq = get_field(exccode, RISCV_EXCP_CLIC_IRQ);
+ *mode = get_field(exccode, RISCV_EXCP_CLIC_MODE);
+ *il = get_field(exccode, RISCV_EXCP_CLIC_LEVEL);
+}
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index a2030e3a6f..18454df8bd 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -47,6 +47,7 @@ config RISCV_VIRT
select SERIAL
select RISCV_ACLINT
select RISCV_APLIC
+ select RISCV_CLIC
select RISCV_IMSIC
select SIFIVE_PLIC
select SIFIVE_TEST
diff --git a/include/hw/intc/riscv_clic.h b/include/hw/intc/riscv_clic.h
new file mode 100644
index 0000000000..b38a576308
--- /dev/null
+++ b/include/hw/intc/riscv_clic.h
@@ -0,0 +1,213 @@
+/*
+ * RISC-V CLIC(Core Local Interrupt Controller) interface.
+ *
+ * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
+ * Copyright (c) 2024 Cirrus Logic, Inc
+ * and Cirrus Logic International Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This implementation follows the CLIC 0.9-stable draft at 14 March 2024,
+ * with the following exceptions and implementation details:
+ * - the CLIC control registers are memory-mapped as per earlier drafts (in
+ * particular version 0.9-draft, 20 June 2023)
+ * - the indirect CSR control in 0.9-stable is not implemented
+ * - the vector table can be either handler addresses (as per the spec)
+ or a jump table where each entry is processed as an instruction,
+ selectable with version number v0.9-jmp
+ * - each hart is assigned its own CLIC block
+ * - if PRV_S and/or PRV_M are supported, they are currently assumed to follow
+ * the PRV_M registers; a subsequent update will address this
+ * - support for PRV_S and PRV_M is selectable at CLIC instantiation
+ * - PRV_S and PRV_U registers are currently separate from PRV_M; a subsequent
+ * update will turn them into filtered views onto the PRV_M registers
+ * - each hart is assigned its own CLIC block
+ * - support for PRV_S and PRV_M is selectable at CLIC instantiation by
+ * passing in a base address for the given modes; a base address of 0 is
+ * treated as not supported
+ * - PRV_S and PRV_U registers are mapped onto the PRV_M controls with
+ * appropriate filtering for the access mode
+ *
+ * The implementation has a RISCVCLICState per hart, with a RISCVCLICView
+ * for each mode subsidiary to that. Each view knows its access mode and base
+ * address, as well as the RISCVCLICState with which it is associated.
+ *
+ * MMIO accesses go through the view, allowing the appropriate permissions to
+ * be enforced when accessing the parent RISCVCLICState for the settings.
+ */
+
+#ifndef RISCV_CLIC_H
+#define RISCV_CLIC_H
+
+#include "hw/irq.h"
+#include "hw/sysbus.h"
+
+#define TYPE_RISCV_CLIC "riscv_clic"
+#define TYPE_RISCV_CLIC_VIEW "riscv_clic_view"
+#define RISCV_CLIC(obj) \
+ OBJECT_CHECK(RISCVCLICState, (obj), TYPE_RISCV_CLIC)
+#define RISCV_CLIC_VIEW(obj) \
+ OBJECT_CHECK(RISCVCLICView, (obj), TYPE_RISCV_CLIC_VIEW)
+
+/*
+ * CLIC per hart active interrupts
+ *
+ * We maintain per hart lists of enabled interrupts sorted by
+ * mode+level+priority. The sorting is done on the configuration path
+ * so that the interrupt delivery fastpath can linear scan enabled
+ * interrupts in priority order.
+ */
+typedef struct CLICActiveInterrupt {
+ uint16_t intcfg;
+ uint16_t irq;
+} CLICActiveInterrupt;
+
+typedef enum TRIG_TYPE {
+ POSITIVE_LEVEL,
+ POSITIVE_EDGE,
+ NEG_LEVEL,
+ NEG_EDGE,
+} TRIG_TYPE;
+
+#define CLIC_INTCTL_BASE 0x1000 /* start offset of intctl registers */
+#define MAX_CLIC_INTCTLBITS 8 /* maximum value for intctlbits */
+
+/* maximum of 4096 IRQs */
+#define CLIC_IRQ_BITS 12
+#define CLIC_MAX_IRQ_COUNT (1 << CLIC_IRQ_BITS)
+#define CLIC_MAX_IRQ (CLIC_MAX_IRQ_COUNT - 1)
+#define CLIC_IRQ_MASK CLIC_MAX_IRQ
+
+/*
+ * clicinttrig registers
+ * 31 interrupt_trap_enable
+ * 30 nxti_enable
+ * 29:13 reserved (WARL 0)
+ * 12:0 interrupt_number
+ */
+#define CLIC_INTTRIG_REGS 32 /* inttrig register count */
+#define CLIC_INTTRIG_START 0x10 /* first inttrig register */
+#define CLIC_INTTRIG_END (CLIC_INTTRIG_START + CLIC_INTTRIG_REGS - 1)
+#define CLIC_INTTRIG_TRAP_ENA 0x80000000
+#define CLIC_INTTRIG_NXTI_ENA 0x40000000
+#define CLIC_INTTRIG_IRQN 0x00001fff
+#define CLIC_INTTRIG_MASK (CLIC_INTTRIG_TRAP_ENA | \
+ CLIC_INTTRIG_NXTI_ENA | CLIC_INTTRIG_IRQN)
+
+/*
+ * We combine the mode and intctl to a number so that higher modes come first.
+ * 9:8 machine mode
+ * 7:0 clicintctl
+ */
+#define CLIC_INTCFG_MODE_SHIFT 8
+#define CLIC_INTCFG_MODE 0x300
+#define CLIC_INTCFG_CTL 0xff
+#define CLIC_INTCFG_MASK (CLIC_INTCFG_MODE | CLIC_INTCFG_CTL)
+
+/*
+ * clicintattr layout
+ * 7:6 mode
+ * 5:3 reserved (WPRI 0)
+ * 2:1 trig
+ * 0 shv
+ */
+#define CLIC_INTATTR_MODE_SHIFT 6
+#define CLIC_INTATTR_MODE_WIDTH 2
+#define CLIC_INTATTR_MODE 0xc0
+#define CLIC_INTATTR_TRIG_SHIFT 1
+#define CLIC_INTATTR_TRIG_WIDTH 2
+#define CLIC_INTATTR_TRIG 0x06
+#define CLIC_INTATTR_SHV 0x01
+#define CLIC_INTATTR_MASK (CLIC_INTATTR_MODE | CLIC_INTATTR_TRIG | \
+ CLIC_INTATTR_SHV)
+/* The clicintattr value */
+#define CLIC_INTATTR_TRIG_EDGE 0b01 /* trig decode edge-triggered */
+#define CLIC_INTATTR_TRIG_INV 0b10 /* trig decode negative polarity */
+
+/* Forward declaration */
+typedef struct RISCVCLICView RISCVCLICView;
+
+/*
+ * The main CLIC state (PRV_M mode) for a hart.
+ */
+typedef struct RISCVCLICState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+
+ /* Implementation parameters */
+ bool shv_enabled; /* hardware-vectoring enabled */
+ bool jump_table; /* vector with jump table, not handler addresses */
+ int hartid;
+ uint32_t num_sources;
+ uint32_t clic_size;
+ uint32_t clic_mmode_base;
+ uint32_t clicintctlbits;
+ RISCVCLICView *prv_m; /* our PRV_M view */
+ RISCVCLICView *prv_s; /* our PRV_S view */
+ RISCVCLICView *prv_u; /* our PRV_U view */
+ char *version;
+
+ /* Global configuration */
+ uint8_t nmbits; /* mode bits */
+ uint8_t mnlbits; /* level bits for M-mode */
+ uint8_t snlbits; /* level bits for S-mode, if present */
+ uint8_t unlbits; /* level bits for U-mode, if present */
+ uint32_t clicinttrig[CLIC_INTTRIG_REGS];
+
+ /* Aperture configuration */
+ uint8_t *clicintip;
+ uint8_t *clicintie;
+ uint8_t *clicintattr;
+ uint8_t *clicintctl;
+
+ /* Compatible with v0.8 */
+ uint32_t mintthresh;
+ uint32_t sintthresh;
+ uint32_t uintthresh;
+
+ /* QEMU implementation related fields */
+ uint32_t exccode;
+ CLICActiveInterrupt *active_list;
+ size_t active_count;
+ qemu_irq cpu_irq;
+} RISCVCLICState;
+
+/*
+ * A PRV_S or PRV_U overlay onto the main RISCVCLICState.
+ */
+typedef struct RISCVCLICView {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ RISCVCLICState *clic; /* the CLIC this is a view onto */
+ MemoryRegion mmio;
+ uint64_t clicbase;
+ uint8_t mode;
+} RISCVCLICView;
+
+DeviceState *riscv_clic_create(hwaddr mclicbase, hwaddr sclicbase,
+ hwaddr uclicbase, uint32_t hartid,
+ uint32_t num_sources, uint8_t clicintctlbits,
+ const char *version);
+
+void riscv_clic_decode_exccode(uint32_t exccode, int *mode, int *il, int *irq);
+void riscv_clic_clean_pending(void *opaque, int irq);
+bool riscv_clic_edge_triggered(void *opaque, int irq);
+bool riscv_clic_shv_interrupt(void *opaque, int irq);
+bool riscv_clic_use_jump_table(void *opaque);
+void riscv_clic_get_next_interrupt(void *opaque);
+bool riscv_clic_is_clic_mode(CPURISCVState *env);
+#endif
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 9b5f36ad0a..12aa8cf6b1 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -36,6 +36,7 @@
typedef struct CPUArchState CPURISCVState;
#define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
+#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0
#if defined(TARGET_RISCV32)
# define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE32
@@ -465,6 +466,7 @@ struct CPUArchState {
bool vstime_irq;
void *clic; /* clic interrupt controller */
+ uint32_t exccode; /* clic irq encode */
hwaddr kernel_addr;
hwaddr fdt_addr;
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index ad45402370..0ed44ec0a8 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -694,6 +694,12 @@ typedef enum RISCVException {
} RISCVException;
#define RISCV_EXCP_INT_FLAG 0x80000000
+#define RISCV_EXCP_CLIC 0x40000000
+#define RISCV_EXCP_CLIC_LEVEL_SHIFT 14
+#define RISCV_EXCP_CLIC_LEVEL (0xff << RISCV_EXCP_CLIC_LEVEL_SHIFT)
+#define RISCV_EXCP_CLIC_MODE_SHIFT 12
+#define RISCV_EXCP_CLIC_MODE (3 << RISCV_EXCP_CLIC_MODE_SHIFT)
+#define RISCV_EXCP_CLIC_IRQ 0x00000fff
#define RISCV_EXCP_INT_MASK 0x7fffffff
/* Interrupt causes */
@@ -746,6 +752,17 @@ typedef enum RISCVException {
#define SINTSTATUS_SIL 0x0000ff00 /* sil[15:8] */
#define SINTSTATUS_UIL 0x000000ff /* uil[7:0] */
+/* mtvec & stvec */
+#define XTVEC_MODE 0x03
+#define XTVEC_SUBMODE 0x3c
+#define XTVEC_FULL_MODE (XTVEC_MODE | XTVEC_SUBMODE)
+#define XTVEC_OBASE (~XTVEC_MODE)
+#define XTVEC_NBASE (~XTVEC_FULL_MODE)
+
+#define XTVEC_CLINT_DIRECT 0x0
+#define XTVEC_CLINT_VECTORED 0x1
+#define XTVEC_CLIC 0x3
+
/* MIE masks */
#define MIE_SEIE (1 << IRQ_S_EXT)
#define MIE_UEIE (1 << IRQ_U_EXT)
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 04/11 v2] target/riscv: Update CSR xie in CLIC mode
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
` (2 preceding siblings ...)
2024-08-19 16:02 ` [PATCH 03/11 v2] hw/intc: Add CLIC device Ian Brockbank
@ 2024-08-19 16:02 ` Ian Brockbank
2024-09-06 2:58 ` Alistair Francis
2024-08-19 16:02 ` [PATCH 05/11 v2] target/riscv: Update CSR xip " Ian Brockbank
` (9 subsequent siblings)
13 siblings, 1 reply; 32+ messages in thread
From: Ian Brockbank @ 2024-08-19 16:02 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
The xie CSR appears hardwired to zero in CLIC mode, replaced by separate
memory-mapped interrupt enables (clicintie[i]). Writes to xie will be
ignored and will not trap (i.e., no access faults).
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
---
target/riscv/csr.c | 34 ++++++++++++++++++++++------------
1 file changed, 22 insertions(+), 12 deletions(-)
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 9c824c0d8f..a5978e0929 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -30,6 +30,10 @@
#include "qemu/guest-random.h"
#include "qapi/error.h"
+#if !defined(CONFIG_USER_ONLY)
+#include "hw/intc/riscv_clic.h"
+#endif
+
/* CSR function table public API */
void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops)
{
@@ -1805,16 +1809,19 @@ static RISCVException rmw_mie64(CPURISCVState *env, int csrno,
uint64_t *ret_val,
uint64_t new_val, uint64_t wr_mask)
{
- uint64_t mask = wr_mask & all_ints;
+ /* Access to xie will be ignored in CLIC mode and will not trap. */
+ if (!riscv_clic_is_clic_mode(env)) {
+ uint64_t mask = wr_mask & all_ints;
- if (ret_val) {
- *ret_val = env->mie;
- }
+ if (ret_val) {
+ *ret_val = env->mie;
+ }
- env->mie = (env->mie & ~mask) | (new_val & mask);
+ env->mie = (env->mie & ~mask) | (new_val & mask);
- if (!riscv_has_ext(env, RVH)) {
- env->mie &= ~((uint64_t)HS_MODE_INTERRUPTS);
+ if (!riscv_has_ext(env, RVH)) {
+ env->mie &= ~((uint64_t)HS_MODE_INTERRUPTS);
+ }
}
return RISCV_EXCP_NONE;
@@ -2906,13 +2913,13 @@ static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
static int read_mintthresh(CPURISCVState *env, int csrno, target_ulong *val)
{
*val = env->mintthresh;
- return 0;
+ return RISCV_EXCP_NONE;
}
static int write_mintthresh(CPURISCVState *env, int csrno, target_ulong val)
{
env->mintthresh = val;
- return 0;
+ return RISCV_EXCP_NONE;
}
/* Supervisor Trap Setup */
@@ -3059,7 +3066,10 @@ static RISCVException rmw_sie64(CPURISCVState *env, int csrno,
*ret_val |= env->sie & nalias_mask;
}
- env->sie = (env->sie & ~sie_mask) | (new_val & sie_mask);
+ /* Writes to xie will be ignored in CLIC mode and will not trap. */
+ if (!riscv_clic_is_clic_mode(env)) {
+ env->sie = (env->sie & ~sie_mask) | (new_val & sie_mask);
+ }
}
return ret;
@@ -3337,13 +3347,13 @@ static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
static int read_sintthresh(CPURISCVState *env, int csrno, target_ulong *val)
{
*val = env->sintthresh;
- return 0;
+ return RISCV_EXCP_NONE;
}
static int write_sintthresh(CPURISCVState *env, int csrno, target_ulong val)
{
env->sintthresh = val;
- return 0;
+ return RISCV_EXCP_NONE;
}
/* Supervisor Protection and Translation */
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 05/11 v2] target/riscv: Update CSR xip in CLIC mode
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
` (3 preceding siblings ...)
2024-08-19 16:02 ` [PATCH 04/11 v2] target/riscv: Update CSR xie in CLIC mode Ian Brockbank
@ 2024-08-19 16:02 ` Ian Brockbank
2024-08-19 16:02 ` [PATCH 06/11 v2] target/riscv: Update CSR xtvec " Ian Brockbank
` (8 subsequent siblings)
13 siblings, 0 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-19 16:02 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
The xip CSR appears hardwired to zero in CLIC mode, replaced by separate
memory-mapped interrupt pendings (clicintip[i]). Writes to xip will be
ignored and will not trap (i.e., no access faults).
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
---
target/riscv/csr.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index a5978e0929..276ef7856e 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -2743,6 +2743,12 @@ static RISCVException rmw_mip(CPURISCVState *env, int csrno,
uint64_t rval;
RISCVException ret;
+ /* The xip CSR appears hardwired to zero in CLIC mode. */
+ if (riscv_clic_is_clic_mode(env)) {
+ *ret_val = 0;
+ return RISCV_EXCP_NONE;
+ }
+
ret = rmw_mip64(env, csrno, &rval, new_val, wr_mask);
if (ret_val) {
*ret_val = rval;
@@ -3294,6 +3300,12 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno,
}
ret = rmw_vsip64(env, CSR_VSIP, ret_val, new_val, wr_mask);
} else {
+ /* The xip CSR appears hardwired to zero in CLIC mode. */
+ if (riscv_clic_is_clic_mode(env)) {
+ *ret_val = 0;
+ return RISCV_EXCP_NONE;
+ }
+
ret = rmw_mvip64(env, csrno, ret_val, new_val, wr_mask & mask);
}
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 06/11 v2] target/riscv: Update CSR xtvec in CLIC mode
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
` (4 preceding siblings ...)
2024-08-19 16:02 ` [PATCH 05/11 v2] target/riscv: Update CSR xip " Ian Brockbank
@ 2024-08-19 16:02 ` Ian Brockbank
2024-09-06 3:02 ` Alistair Francis
2024-08-19 16:02 ` [PATCH 07/11 v2] target/riscv: Update CSR xnxti " Ian Brockbank
` (7 subsequent siblings)
13 siblings, 1 reply; 32+ messages in thread
From: Ian Brockbank @ 2024-08-19 16:02 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
The new CLIC interrupt-handling mode is encoded as a new state in the
existing WARL xtvec register, where the low two bits of are 11.
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
---
target/riscv/cpu.h | 2 ++
target/riscv/cpu_bits.h | 2 ++
target/riscv/csr.c | 63 ++++++++++++++++++++++++++++++++++++++---
3 files changed, 63 insertions(+), 4 deletions(-)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 12aa8cf6b1..05a014db03 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -283,11 +283,13 @@ struct CPUArchState {
target_ulong medeleg;
target_ulong stvec;
+ target_ulong stvt; /* clic-spec */
target_ulong sepc;
target_ulong scause;
target_ulong sintthresh; /* clic-spec */
target_ulong mtvec;
+ target_ulong mtvt; /* clic-spec */
target_ulong mepc;
target_ulong mcause;
target_ulong mtval; /* since: priv-1.10.0 */
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 0ed44ec0a8..279a6f889b 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -153,6 +153,7 @@
#define CSR_MIE 0x304
#define CSR_MTVEC 0x305
#define CSR_MCOUNTEREN 0x306
+#define CSR_MTVT 0x307 /* clic-spec-draft */
/* 32-bit only */
#define CSR_MSTATUSH 0x310
@@ -192,6 +193,7 @@
#define CSR_SIE 0x104
#define CSR_STVEC 0x105
#define CSR_SCOUNTEREN 0x106
+#define CSR_STVT 0x107 /* clic-spec-draft */
/* Supervisor Configuration CSRs */
#define CSR_SENVCFG 0x10A
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 276ef7856e..be0071fd25 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -2170,9 +2170,23 @@ static RISCVException read_mtvec(CPURISCVState *env, int csrno,
static RISCVException write_mtvec(CPURISCVState *env, int csrno,
target_ulong val)
{
- /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
- if ((val & 3) < 2) {
+ /*
+ * bits [1:0] encode mode; 0 = direct, 1 = vectored, 3 = CLIC,
+ * others reserved
+ */
+ target_ulong mode = get_field(val, XTVEC_MODE);
+ target_ulong fullmode = val & XTVEC_FULL_MODE;
+ if (mode <= XTVEC_CLINT_VECTORED) {
env->mtvec = val;
+ } else if (XTVEC_CLIC == fullmode && env->clic) {
+ /*
+ * CLIC mode hardwires xtvec bits 2-5 to zero.
+ * Layout:
+ * XLEN-1:6 base (WARL)
+ * 5:2 submode (WARL) - 0000 for CLIC
+ * 1:0 mode (WARL) - 11 for CLIC
+ */
+ env->mtvec = (val & XTVEC_NBASE) | XTVEC_CLIC;
} else {
qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: reserved mode not supported\n");
}
@@ -2271,6 +2285,18 @@ static RISCVException write_mcounteren(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
+static int read_mtvt(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mtvt;
+ return RISCV_EXCP_NONE;
+}
+
+static int write_mtvt(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mtvt = val & XTVEC_NBASE;
+ return RISCV_EXCP_NONE;
+}
+
/* Machine Trap Handling */
static RISCVException read_mscratch_i128(CPURISCVState *env, int csrno,
Int128 *val)
@@ -3122,9 +3148,24 @@ static RISCVException read_stvec(CPURISCVState *env, int csrno,
static RISCVException write_stvec(CPURISCVState *env, int csrno,
target_ulong val)
{
- /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
- if ((val & 3) < 2) {
+ /*
+ * bits [1:0] encode mode; 0 = direct, 1 = vectored, 3 = CLIC,
+ * others reserved
+ */
+ target_ulong mode = val & XTVEC_MODE;
+ target_ulong fullmode = val & XTVEC_FULL_MODE;
+ if (mode <= XTVEC_CLINT_VECTORED) {
env->stvec = val;
+ } else if (XTVEC_CLIC == fullmode && env->clic) {
+ /*
+ * If only CLIC mode is supported, writes to bit 1 are also ignored and
+ * it is always set to one. CLIC mode hardwires xtvec bits 2-5 to zero.
+ * Layout:
+ * XLEN-1:6 base (WARL)
+ * 5:2 submode (WARL) - 0000 for CLIC
+ * 1:0 mode (WARL) - 11 for CLIC
+ */
+ env->stvec = (val & XTVEC_NBASE) | XTVEC_CLIC;
} else {
qemu_log_mask(LOG_UNIMP, "CSR_STVEC: reserved mode not supported\n");
}
@@ -3149,6 +3190,18 @@ static RISCVException write_scounteren(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
+static int read_stvt(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->stvt;
+ return RISCV_EXCP_NONE;
+}
+
+static int write_stvt(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->stvt = val & XTVEC_NBASE;
+ return RISCV_EXCP_NONE;
+}
+
/* Supervisor Trap Handling */
static RISCVException read_sscratch_i128(CPURISCVState *env, int csrno,
Int128 *val)
@@ -5666,11 +5719,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
write_mhpmcounterh },
/* Machine Mode Core Level Interrupt Controller */
+ [CSR_MTVT] = { "mtvt", clic, read_mtvt, write_mtvt },
[CSR_MINTSTATUS] = { "mintstatus", clic, read_mintstatus },
[CSR_MINTTHRESH] = { "mintthresh", clic, read_mintthresh,
write_mintthresh },
/* Supervisor Mode Core Level Interrupt Controller */
+ [CSR_STVT] = { "stvt", clic, read_stvt, write_stvt },
[CSR_SINTSTATUS] = { "sintstatus", clic, read_sintstatus },
[CSR_SINTTHRESH] = { "sintthresh", clic, read_sintthresh,
write_sintthresh },
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 07/11 v2] target/riscv: Update CSR xnxti in CLIC mode
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
` (5 preceding siblings ...)
2024-08-19 16:02 ` [PATCH 06/11 v2] target/riscv: Update CSR xtvec " Ian Brockbank
@ 2024-08-19 16:02 ` Ian Brockbank
2024-08-19 16:02 ` [PATCH 08/11 v2] target/riscv: Update interrupt handling " Ian Brockbank
` (6 subsequent siblings)
13 siblings, 0 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-19 16:02 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
The CSR can be used by software to service the next horizontal interrupt
when it has greater level than the saved interrupt context
(held in xcause`.pil`) and greater level than the interrupt threshold of
the corresponding privilege mode,
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
---
target/riscv/cpu_bits.h | 25 +++++++++
target/riscv/csr.c | 111 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 136 insertions(+)
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 279a6f889b..3744b34504 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -166,6 +166,7 @@
#define CSR_MCAUSE 0x342
#define CSR_MTVAL 0x343
#define CSR_MIP 0x344
+#define CSR_MNXTI 0x345 /* clic-spec-draft */
#define CSR_MINTSTATUS 0xfb1 /* clic-spec-draft */
#define CSR_MINTTHRESH 0x347 /* clic-spec-draft */
@@ -210,6 +211,7 @@
#define CSR_SCAUSE 0x142
#define CSR_STVAL 0x143
#define CSR_SIP 0x144
+#define CSR_SNXTI 0x145 /* clic-spec-draft */
#define CSR_SINTSTATUS 0xdb1 /* clic-spec-draft */
#define CSR_SINTTHRESH 0x147 /* clic-spec-draft */
@@ -561,6 +563,8 @@
#define MSTATUS_GVA 0x4000000000ULL
#define MSTATUS_MPV 0x8000000000ULL
+#define MSTATUS_WRITE_MASK 0x0000001f
+
#define MSTATUS64_UXL 0x0000000300000000ULL
#define MSTATUS64_SXL 0x0000000C00000000ULL
@@ -754,6 +758,27 @@ typedef enum RISCVException {
#define SINTSTATUS_SIL 0x0000ff00 /* sil[15:8] */
#define SINTSTATUS_UIL 0x000000ff /* uil[7:0] */
+/* mcause */
+#define MCAUSE_INT (1 << (TARGET_LONG_BITS - 1))
+#define MCAUSE_MINHV 0x40000000 /* minhv */
+#define MCAUSE_MPP 0x30000000 /* mpp[1:0] */
+#define MCAUSE_MPIE 0x08000000 /* mpie */
+#define MCAUSE_MPIL 0x00ff0000 /* mpil[7:0] */
+#define MCAUSE_EXCCODE 0x00000fff /* exccode[11:0] */
+
+/* scause */
+#define SCAUSE_INT (1 << (TARGET_LONG_BITS - 1))
+#define SCAUSE_SINHV 0x40000000 /* sinhv */
+#define SCAUSE_SPP 0x10000000 /* spp */
+#define SCAUSE_SPIE 0x08000000 /* spie */
+#define SCAUSE_SPIL 0x00ff0000 /* spil[7:0] */
+#define SCAUSE_EXCCODE 0x00000fff /* exccode[11:0] */
+
+/* mcause & scause */
+#define XCAUSE_XPP_SHIFT 28
+#define XCAUSE_XPIE_SHIFT 27
+#define XCAUSE_XPIL_SHIFT 16
+
/* mtvec & stvec */
#define XTVEC_MODE 0x03
#define XTVEC_SUBMODE 0x3c
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index be0071fd25..813a5b927f 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -19,6 +19,7 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
+#include "qemu/main-loop.h"
#include "qemu/timer.h"
#include "cpu.h"
#include "tcg/tcg-cpu.h"
@@ -2936,6 +2937,77 @@ static RISCVException rmw_mviph(CPURISCVState *env, int csrno,
return ret;
}
+static bool get_xnxti_status(CPURISCVState *env)
+{
+ int clic_irq, clic_priv, clic_il, pil;
+
+ if (!env->exccode) { /* No interrupt */
+ return false;
+ }
+ /* The system is not in a CLIC mode */
+ if (!riscv_clic_is_clic_mode(env)) {
+ return false;
+ } else {
+ riscv_clic_decode_exccode(env->exccode, &clic_priv, &clic_il,
+ &clic_irq);
+
+ if (env->priv == PRV_M) {
+ pil = MAX(get_field(env->mcause, MCAUSE_MPIL), env->mintthresh);
+ } else if (env->priv == PRV_S) {
+ pil = MAX(get_field(env->scause, SCAUSE_SPIL), env->sintthresh);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CSR: rmw xnxti with unsupported mode\n");
+ exit(1);
+ }
+
+ if ((clic_priv != env->priv) || /* No horizontal interrupt */
+ (clic_il <= pil) || /* No higher level interrupt */
+ (riscv_clic_shv_interrupt(env->clic, clic_irq))) {
+ /* CLIC vector mode */
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
+
+static int rmw_mnxti(CPURISCVState *env, int csrno, target_ulong *ret_value,
+ target_ulong new_value, target_ulong write_mask)
+{
+ int clic_priv, clic_il, clic_irq;
+ bool ready;
+ if (write_mask) {
+ env->mstatus |= new_value & (write_mask & MSTATUS_WRITE_MASK);
+ }
+
+ BQL_LOCK_GUARD();
+
+ ready = get_xnxti_status(env);
+ if (ready) {
+ riscv_clic_decode_exccode(env->exccode, &clic_priv, &clic_il,
+ &clic_irq);
+ if (write_mask) {
+ bool edge = riscv_clic_edge_triggered(env->clic, clic_irq);
+ if (edge) {
+ riscv_clic_clean_pending(env->clic, clic_irq);
+ }
+ env->mintstatus = set_field(env->mintstatus,
+ MINTSTATUS_MIL, clic_il);
+ env->mcause = set_field(env->mcause, MCAUSE_EXCCODE, clic_irq);
+ }
+ if (ret_value) {
+ *ret_value = (env->mtvt & ~0x3f) + sizeof(target_ulong) * clic_irq;
+ }
+ } else {
+ if (ret_value) {
+ *ret_value = 0;
+ }
+ }
+
+ return RISCV_EXCP_NONE;
+}
+
static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
{
*val = env->mintstatus;
@@ -3401,6 +3473,43 @@ static RISCVException rmw_siph(CPURISCVState *env, int csrno,
return ret;
}
+static int rmw_snxti(CPURISCVState *env, int csrno, target_ulong *ret_value,
+ target_ulong new_value, target_ulong write_mask)
+{
+ int clic_priv, clic_il, clic_irq;
+ bool ready;
+ if (write_mask) {
+ env->mstatus |= new_value & (write_mask & MSTATUS_WRITE_MASK);
+ }
+
+ BQL_LOCK_GUARD();
+
+ ready = get_xnxti_status(env);
+ if (ready) {
+ riscv_clic_decode_exccode(env->exccode, &clic_priv, &clic_il,
+ &clic_irq);
+ if (write_mask) {
+ bool edge = riscv_clic_edge_triggered(env->clic, clic_irq);
+ if (edge) {
+ riscv_clic_clean_pending(env->clic, clic_irq);
+ }
+ /* update the PRV_S parts of mintstatus */
+ env->mintstatus = set_field(env->mintstatus,
+ MINTSTATUS_SIL, clic_il);
+ env->scause = set_field(env->scause, SCAUSE_EXCCODE, clic_irq);
+ }
+ if (ret_value) {
+ *ret_value = (env->stvt & ~0x3f) + sizeof(target_ulong) * clic_irq;
+ }
+ } else {
+ if (ret_value) {
+ *ret_value = 0;
+ }
+ }
+
+ return RISCV_EXCP_NONE;
+}
+
static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
{
/* sintstatus is a filtered view of mintstatus with the PRV_M removed */
@@ -5720,12 +5829,14 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
/* Machine Mode Core Level Interrupt Controller */
[CSR_MTVT] = { "mtvt", clic, read_mtvt, write_mtvt },
+ [CSR_MNXTI] = { "mnxti", clic, NULL, NULL, rmw_mnxti },
[CSR_MINTSTATUS] = { "mintstatus", clic, read_mintstatus },
[CSR_MINTTHRESH] = { "mintthresh", clic, read_mintthresh,
write_mintthresh },
/* Supervisor Mode Core Level Interrupt Controller */
[CSR_STVT] = { "stvt", clic, read_stvt, write_stvt },
+ [CSR_SNXTI] = { "snxti", clic, NULL, NULL, rmw_snxti },
[CSR_SINTSTATUS] = { "sintstatus", clic, read_sintstatus },
[CSR_SINTTHRESH] = { "sintthresh", clic, read_sintthresh,
write_sintthresh },
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 08/11 v2] target/riscv: Update interrupt handling in CLIC mode
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
` (6 preceding siblings ...)
2024-08-19 16:02 ` [PATCH 07/11 v2] target/riscv: Update CSR xnxti " Ian Brockbank
@ 2024-08-19 16:02 ` Ian Brockbank
2024-09-06 3:49 ` Alistair Francis
2024-08-19 16:02 ` [PATCH 09/11 v2] target/riscv: Update interrupt return " Ian Brockbank
` (5 subsequent siblings)
13 siblings, 1 reply; 32+ messages in thread
From: Ian Brockbank @ 2024-08-19 16:02 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
Decode CLIC interrupt information from exccode, includes interrupt
privilege mode, interrupt level, and irq number.
Then update CSRs xcause, xstatus, xepc, xintstatus and jump to
correct PC according to the CLIC specification.
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
---
target/riscv/cpu_helper.c | 129 +++++++++++++++++++++++++++++++++++---
1 file changed, 119 insertions(+), 10 deletions(-)
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 395a1d9140..944afb68d2 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -24,6 +24,7 @@
#include "internals.h"
#include "pmu.h"
#include "exec/exec-all.h"
+#include "exec/cpu_ldst.h"
#include "exec/page-protection.h"
#include "instmap.h"
#include "tcg/tcg-op.h"
@@ -33,6 +34,7 @@
#include "cpu_bits.h"
#include "debug.h"
#include "tcg/oversized-guest.h"
+#include "hw/intc/riscv_clic.h"
int riscv_env_mmu_index(CPURISCVState *env, bool ifetch)
{
@@ -428,6 +430,20 @@ int riscv_cpu_vsirq_pending(CPURISCVState *env)
(irqs | irqs_f_vs), env->hviprio);
}
+static int riscv_cpu_local_irq_mode_enabled(CPURISCVState *env, int mode)
+{
+ switch (mode) {
+ case PRV_M:
+ return env->priv < PRV_M ||
+ (env->priv == PRV_M && get_field(env->mstatus, MSTATUS_MIE));
+ case PRV_S:
+ return env->priv < PRV_S ||
+ (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_SIE));
+ default:
+ return false;
+ }
+}
+
static int riscv_cpu_local_irq_pending(CPURISCVState *env)
{
uint64_t irqs, pending, mie, hsie, vsie, irqs_f, irqs_f_vs;
@@ -506,6 +522,18 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
return true;
}
}
+ if (interrupt_request & CPU_INTERRUPT_CLIC) {
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ int mode = get_field(env->exccode, RISCV_EXCP_CLIC_MODE);
+ int enabled = riscv_cpu_local_irq_mode_enabled(env, mode);
+ if (enabled) {
+ cs->exception_index = RISCV_EXCP_CLIC | env->exccode;
+ cs->interrupt_request = cs->interrupt_request & ~CPU_INTERRUPT_CLIC;
+ riscv_cpu_do_interrupt(cs);
+ return true;
+ }
+ }
return false;
}
@@ -1641,6 +1669,60 @@ static target_ulong riscv_transformed_insn(CPURISCVState *env,
return xinsn;
}
+static target_ulong riscv_intr_pc(CPURISCVState *env, target_ulong tvec,
+ target_ulong tvt, bool async,
+ int cause, int mode)
+{
+ int mode1 = tvec & XTVEC_MODE;
+ int mode2 = tvec & XTVEC_FULL_MODE;
+
+ if (!async) {
+ return tvec & XTVEC_OBASE;
+ }
+ /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
+ switch (mode1) {
+ case XTVEC_CLINT_DIRECT:
+ return tvec & XTVEC_OBASE;
+ case XTVEC_CLINT_VECTORED:
+ return (tvec & XTVEC_OBASE) + cause * 4;
+ default:
+ if (env->clic && (mode2 == XTVEC_CLIC)) {
+ /* Non-vectored, clicintattr[i].shv = 0 || cliccfg.nvbits = 0 */
+ if (!riscv_clic_shv_interrupt(env->clic, cause)) {
+ /* NBASE = mtvec[XLEN-1:6]<<6 */
+ return tvec & XTVEC_NBASE;
+ } else {
+ /*
+ * pc := M[TBASE + XLEN/8 * exccode)] & ~1,
+ * TBASE = mtvt[XLEN-1:6]<<6
+ */
+ int size = TARGET_LONG_BITS / 8;
+ target_ulong tbase = (tvt & XTVEC_NBASE) + size * cause;
+ void *host = tlb_vaddr_to_host(env, tbase, MMU_DATA_LOAD, mode);
+ if (host != NULL) {
+ target_ulong new_pc = tbase;
+ if (!riscv_clic_use_jump_table(env->clic)) {
+ /*
+ * Standard CLIC: the vector entry is a function pointer
+ * so look up the destination.
+ */
+ new_pc = ldn_p(host, size);
+ host = tlb_vaddr_to_host(env, new_pc,
+ MMU_INST_FETCH, mode);
+ }
+ if (host) {
+ return new_pc;
+ }
+ }
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CLIC: load trap handler error!\n");
+ exit(1);
+ }
+ }
+ g_assert_not_reached();
+ }
+}
+
/*
* Handle Traps
*
@@ -1654,12 +1736,14 @@ void riscv_cpu_do_interrupt(CPUState *cs)
bool virt = env->virt_enabled;
bool write_gva = false;
uint64_t s;
+ int mode, level, irq;
/*
* cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide
* so we mask off the MSB and separate into trap type and cause.
*/
- bool async = !!(cs->exception_index & RISCV_EXCP_INT_FLAG);
+ bool clic = !!(cs->exception_index & RISCV_EXCP_CLIC);
+ bool async = !!(cs->exception_index & RISCV_EXCP_INT_FLAG) || clic;
target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK;
uint64_t deleg = async ? env->mideleg : env->medeleg;
bool s_injected = env->mvip & (1 << cause) & env->mvien &&
@@ -1746,6 +1830,28 @@ void riscv_cpu_do_interrupt(CPUState *cs)
}
}
+ if (clic) {
+ riscv_clic_decode_exccode(cause, &mode, &level, &irq);
+ cause = irq | get_field(env->mstatus, MSTATUS_MPP) << XCAUSE_XPP_SHIFT;
+ switch (mode) {
+ case PRV_M:
+ cause |= get_field(env->mintstatus, MINTSTATUS_MIL)
+ << XCAUSE_XPIL_SHIFT;
+ cause |= get_field(env->mstatus, MSTATUS_MIE) << XCAUSE_XPIE_SHIFT;
+ env->mintstatus = set_field(env->mintstatus, MINTSTATUS_MIL, level);
+ break;
+ case PRV_S:
+ cause |= get_field(env->mintstatus, MINTSTATUS_SIL)
+ << XCAUSE_XPIL_SHIFT;
+ cause |= get_field(env->mstatus, MSTATUS_SPIE) << XCAUSE_XPIE_SHIFT;
+ env->mintstatus = set_field(env->mintstatus, MINTSTATUS_SIL, level);
+ break;
+ }
+ } else {
+ mode = env->priv <= PRV_S && cause < 64 &&
+ ((deleg >> cause) & 1 || s_injected || vs_injected) ? PRV_S : PRV_M;
+ }
+
trace_riscv_trap(env->mhartid, async, cause, env->pc, tval,
riscv_cpu_get_trap_name(cause, async));
@@ -1755,8 +1861,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
__func__, env->mhartid, async, cause, env->pc, tval,
riscv_cpu_get_trap_name(cause, async));
- if (env->priv <= PRV_S && cause < 64 &&
- (((deleg >> cause) & 1) || s_injected || vs_injected)) {
+ if (PRV_S == mode) {
/* handle the trap in S-mode */
if (riscv_has_ext(env, RVH)) {
uint64_t hdeleg = async ? env->hideleg : env->hedeleg;
@@ -1765,7 +1870,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
(((hdeleg >> cause) & 1) || vs_injected)) {
/* Trap to VS mode */
/*
- * See if we need to adjust cause. Yes if its VS mode interrupt
+ * See if we need to adjust cause. Yes if it's VS mode interrupt
* no if hypervisor has delegated one of hs mode's interrupt
*/
if (async && (cause == IRQ_VS_TIMER || cause == IRQ_VS_SOFT ||
@@ -1796,13 +1901,15 @@ void riscv_cpu_do_interrupt(CPUState *cs)
s = set_field(s, MSTATUS_SPP, env->priv);
s = set_field(s, MSTATUS_SIE, 0);
env->mstatus = s;
- env->scause = cause | ((target_ulong)async << (TARGET_LONG_BITS - 1));
+ if (async) {
+ env->scause = cause | SCAUSE_INT;
+ }
env->sepc = env->pc;
env->stval = tval;
env->htval = htval;
env->htinst = tinst;
- env->pc = (env->stvec >> 2 << 2) +
- ((async && (env->stvec & 3) == 1) ? cause * 4 : 0);
+ env->pc = riscv_intr_pc(env, env->stvec, env->stvt, async,
+ cause & SCAUSE_EXCCODE, PRV_S);
riscv_cpu_set_mode(env, PRV_S, virt);
} else {
/* handle the trap in M-mode */
@@ -1827,13 +1934,15 @@ void riscv_cpu_do_interrupt(CPUState *cs)
s = set_field(s, MSTATUS_MPP, env->priv);
s = set_field(s, MSTATUS_MIE, 0);
env->mstatus = s;
- env->mcause = cause | ~(((target_ulong)-1) >> async);
+ if (async) {
+ env->mcause = cause | MCAUSE_INT;
+ }
env->mepc = env->pc;
env->mtval = tval;
env->mtval2 = mtval2;
env->mtinst = tinst;
- env->pc = (env->mtvec >> 2 << 2) +
- ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
+ env->pc = riscv_intr_pc(env, env->mtvec, env->mtvt, async,
+ cause & MCAUSE_EXCCODE, PRV_M);
riscv_cpu_set_mode(env, PRV_M, virt);
}
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 09/11 v2] target/riscv: Update interrupt return in CLIC mode
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
` (7 preceding siblings ...)
2024-08-19 16:02 ` [PATCH 08/11 v2] target/riscv: Update interrupt handling " Ian Brockbank
@ 2024-08-19 16:02 ` Ian Brockbank
2024-08-19 16:02 ` [PATCH 10/11 v2] hw/riscv: add CLIC into virt machine Ian Brockbank
` (4 subsequent siblings)
13 siblings, 0 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-19 16:02 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, LIU Zhiwei
From: Ian Brockbank <ian.brockbank@cirrus.com>
When a vectored interrupt is selected and serviced, the hardware will
automatically clear the corresponding pending bit in edge-triggered mode.
This may lead to a lower privilege interrupt pending forever.
Therefore when interrupts return, pull a pending interrupt to service.
Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
---
target/riscv/op_helper.c | 29 +++++++++++++++++++++++++++--
1 file changed, 27 insertions(+), 2 deletions(-)
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 25a5263573..b6ca3ad598 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -25,7 +25,11 @@
#include "exec/cpu_ldst.h"
#include "exec/helper-proto.h"
-/* Exceptions processing helpers */
+#if !defined(CONFIG_USER_ONLY)
+#include "hw/intc/riscv_clic.h"
+#endif
+
+/* Exception processing helpers */
G_NORETURN void riscv_raise_exception(CPURISCVState *env,
uint32_t exception, uintptr_t pc)
{
@@ -259,6 +263,7 @@ void helper_cbo_inval(CPURISCVState *env, target_ulong address)
#ifndef CONFIG_USER_ONLY
+/* Return from PRV_S interrupt */
target_ulong helper_sret(CPURISCVState *env)
{
uint64_t mstatus;
@@ -292,8 +297,17 @@ target_ulong helper_sret(CPURISCVState *env)
}
env->mstatus = mstatus;
+ if (riscv_clic_is_clic_mode(env)) {
+ /* Update mintstatus with the PRV_S information */
+ target_ulong spil = get_field(env->scause, SCAUSE_SPIL);
+ env->mintstatus = set_field(env->mintstatus, MINTSTATUS_SIL, spil);
+ env->scause = set_field(env->scause, SCAUSE_SPIE, 1);
+ env->scause = set_field(env->scause, SCAUSE_SPP, PRV_U);
+ riscv_clic_get_next_interrupt(env->clic);
+ }
+
if (riscv_has_ext(env, RVH) && !env->virt_enabled) {
- /* We support Hypervisor extensions and virtulisation is disabled */
+ /* We support Hypervisor extensions and virtualization is disabled */
target_ulong hstatus = env->hstatus;
prev_virt = get_field(hstatus, HSTATUS_SPV);
@@ -312,6 +326,7 @@ target_ulong helper_sret(CPURISCVState *env)
return retpc;
}
+/* Return from PRV_M interrupt */
target_ulong helper_mret(CPURISCVState *env)
{
if (!(env->priv >= PRV_M)) {
@@ -344,6 +359,16 @@ target_ulong helper_mret(CPURISCVState *env)
}
env->mstatus = mstatus;
+ if (riscv_clic_is_clic_mode(env)) {
+ /* Update mintstatus with the PRV_M information */
+ target_ulong mpil = get_field(env->mcause, MCAUSE_MPIL);
+ env->mintstatus = set_field(env->mintstatus, MINTSTATUS_MIL, mpil);
+ env->mcause = set_field(env->mcause, MCAUSE_MPIE, 1);
+ env->mcause = set_field(env->mcause, MCAUSE_MPP,
+ riscv_has_ext(env, RVU) ? PRV_U : PRV_M);
+ riscv_clic_get_next_interrupt(env->clic);
+ }
+
if (riscv_has_ext(env, RVH) && prev_virt) {
riscv_cpu_swap_hypervisor_regs(env);
}
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 10/11 v2] hw/riscv: add CLIC into virt machine
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
` (8 preceding siblings ...)
2024-08-19 16:02 ` [PATCH 09/11 v2] target/riscv: Update interrupt return " Ian Brockbank
@ 2024-08-19 16:02 ` Ian Brockbank
2024-08-19 16:02 ` [PATCH 11/11 v2] tests: add riscv clic qtest case and a function in qtest Ian Brockbank
` (3 subsequent siblings)
13 siblings, 0 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-08-19 16:02 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, Ian Brockbank
Signed-off-by: Ian Brockbank<ian.brockbank@cirrus.com>
---
hw/riscv/virt.c | 235 +++++++++++++++++++++++++++++++++++++++-
include/hw/riscv/virt.h | 35 ++++++
2 files changed, 267 insertions(+), 3 deletions(-)
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index cef41c150a..68d614ad5c 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -1,4 +1,4 @@
-/*
+ /*
* QEMU RISC-V VirtIO Board
*
* Copyright (c) 2017 SiFive, Inc.
@@ -39,6 +39,7 @@
#include "hw/firmware/smbios.h"
#include "hw/intc/riscv_aclint.h"
#include "hw/intc/riscv_aplic.h"
+#include "hw/intc/riscv_clic.h"
#include "hw/intc/sifive_plic.h"
#include "hw/misc/sifive_test.h"
#include "hw/platform-bus.h"
@@ -72,6 +73,7 @@ static const MemMapEntry virt_memmap[] = {
[VIRT_MROM] = { 0x1000, 0xf000 },
[VIRT_TEST] = { 0x100000, 0x1000 },
[VIRT_RTC] = { 0x101000, 0x1000 },
+ [VIRT_CLIC] = { 0x2000000, VIRT_CLIC_MAX_SIZE(VIRT_CPUS_MAX)},
[VIRT_CLINT] = { 0x2000000, 0x10000 },
[VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 },
[VIRT_PCIE_PIO] = { 0x3000000, 0x10000 },
@@ -424,6 +426,37 @@ static void create_fdt_socket_aclint(RISCVVirtState *s,
}
}
+static void create_fdt_socket_clic(RISCVVirtState *s,
+ const MemMapEntry *memmap, int socket)
+{
+ g_autofree char *clic_name = NULL;
+ g_autofree uint32_t *clic_cells = NULL;
+ unsigned long mclicbase;
+ MachineState *ms = MACHINE(s);
+ static const char * const clic_compat[1] = {
+ "riscv,clic-0.9"
+ };
+
+ /*
+ * The spec doesn't define a memory layout, other than to say that each
+ * CLIC should be on a 4KiB boundary if memory-mapped.
+ * This implementation makes all the CLICs contiguous, in the order M, S, U,
+ * and assumes the worst-case size.
+ * TODO: create entries for each CLIC on the system.
+ */
+ mclicbase = memmap[VIRT_CLIC].base;
+ clic_name = g_strdup_printf("/soc/clic@%lx", mclicbase);
+ qemu_fdt_add_subnode(ms->fdt, clic_name);
+ qemu_fdt_setprop_string_array(ms->fdt, clic_name, "compatible",
+ (char **)&clic_compat,
+ ARRAY_SIZE(clic_compat));
+ qemu_fdt_setprop_cells(ms->fdt, clic_name, "regs",
+ 0x0, mclicbase, 0x0, memmap[VIRT_CLIC].size);
+ qemu_fdt_setprop_cell(ms->fdt, clic_name, "riscv,num-sources",
+ VIRT_IRQCHIP_NUM_SOURCES);
+ riscv_socket_fdt_write_id(ms, clic_name, socket);
+}
+
static void create_fdt_socket_plic(RISCVVirtState *s,
const MemMapEntry *memmap, int socket,
uint32_t *phandle, uint32_t *intc_phandles,
@@ -759,7 +792,10 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap,
create_fdt_socket_memory(s, memmap, socket);
- if (virt_aclint_allowed() && s->have_aclint) {
+
+ if (s->have_clic) {
+ create_fdt_socket_clic(s, memmap, socket);
+ } else if (virt_aclint_allowed() && s->have_aclint) {
create_fdt_socket_aclint(s, memmap, socket,
&intc_phandles[phandle_pos]);
} else if (tcg_enabled()) {
@@ -1206,6 +1242,37 @@ static DeviceState *virt_create_plic(const MemMapEntry *memmap, int socket,
return ret;
}
+static DeviceState *virt_create_clic(RISCVVirtState *s, uint64_t clic_base,
+ int hartid)
+{
+ DeviceState *ret;
+ uint32_t block_size = VIRT_CLIC_HART_SIZE(s->clic_prv_s, s->clic_prv_u);
+ uint64_t mclicbase = clic_base + hartid * block_size;
+ uint64_t sclicbase = 0;
+ uint64_t uclicbase = 0;
+
+ /*
+ * The spec doesn't define a memory layout, other than to say that each
+ * CLIC should be on a 4KiB boundary if memory-mapped.
+ * This implementation makes all the CLICs contiguous, in the order M, S, U.
+ */
+ if (s->clic_prv_s) {
+ sclicbase = mclicbase + VIRT_CLIC_BLOCK_SIZE;
+ }
+ if (s->clic_prv_u) {
+ uclicbase = mclicbase + VIRT_CLIC_BLOCK_SIZE;
+ if (s->clic_prv_s) {
+ uclicbase += VIRT_CLIC_BLOCK_SIZE;
+ }
+ }
+ ret = riscv_clic_create(mclicbase, sclicbase, uclicbase,
+ hartid, VIRT_IRQCHIP_NUM_SOURCES,
+ s->clic_intctlbits,
+ s->clic_version);
+
+ return ret;
+}
+
static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests,
const MemMapEntry *memmap, int socket,
int base_hartid, int hart_count)
@@ -1505,7 +1572,7 @@ static void virt_machine_init(MachineState *machine)
i * memmap[VIRT_ACLINT_SSWI].size,
base_hartid, hart_count, true);
}
- } else if (tcg_enabled()) {
+ } else if (tcg_enabled() && !s->have_clic) {
/* Per-socket SiFive CLINT */
riscv_aclint_swi_create(
memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size,
@@ -1527,6 +1594,12 @@ static void virt_machine_init(MachineState *machine)
hart_count);
}
+ /* CLIC if present - needs to come after PLIC */
+ if (s->have_clic) {
+ s->irqchip[i] = virt_create_clic(s, memmap[VIRT_CLIC].base,
+ base_hartid);
+ }
+
/* Try to use different IRQCHIP instance based device type */
if (i == 0) {
mmio_irqchip = s->irqchip[i];
@@ -1710,6 +1783,134 @@ static void virt_set_aclint(Object *obj, bool value, Error **errp)
s->have_aclint = value;
}
+static bool virt_get_clic(Object *obj, Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+
+ return s->have_clic;
+}
+
+static void virt_set_clic(Object *obj, bool value, Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+
+ s->have_clic = value;
+}
+
+static char *virt_get_clic_mode(Object *obj, Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+ const char *val;
+
+ if (s->have_clic) {
+ if (s->clic_prv_s && s->clic_prv_u) {
+ val = "msu";
+ } else if (s->clic_prv_s) {
+ val = "ms";
+ } else if (s->clic_prv_u) {
+ val = "mu";
+ } else {
+ val = "m";
+ }
+ } else {
+ val = "none";
+ }
+
+ return g_strdup(val);
+}
+
+static void virt_set_clic_mode(Object *obj, const char *val, Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+ const char *c = val;
+
+ s->have_clic = true;
+
+ s->clic_prv_s = false;
+ s->clic_prv_u = false;
+
+ /* The mode is encoded with m = machine, s = PRV_S, u = PRV_U */
+ while (*c && !*errp) {
+ switch (*c) {
+ case 'm':
+ case 'M':
+ /* Machine mode is implicit and always enabled */
+ break;
+ case 's':
+ case 'S':
+ s->clic_prv_s = true;
+ break;
+ case 'u':
+ case 'U':
+ s->clic_prv_u = true;
+ break;
+ default:
+ error_setg(errp, "Invalid CLIC interrupt mode %c in %s", *c, val);
+ error_append_hint(errp, "Valid values are m, s and u.\n");
+ break;
+ }
+ ++c;
+ }
+}
+
+static char *virt_get_clic_version(Object *obj, Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+ return g_strdup(s->clic_version);
+}
+
+static void virt_set_clic_version(Object *obj, const char *val, Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+ g_autofree char **tokens = g_strsplit(val, "-", 2);
+
+ if (0 != strcmp(tokens[0], "v0.9")) {
+ error_setg(errp, "Invalid CLIC version %s", tokens[0]);
+ error_append_hint(errp,
+ "Only v0.9 is supported. The 'v' is required.\n");
+ return;
+ }
+
+ if (tokens[1]) {
+ if (0 != strcmp(tokens[1], "jmp")) {
+ error_setg(errp, "Invalid CLIC version suffix -%s", tokens[1]);
+ error_append_hint(errp, "Only -jmp is supported.\n");
+ return;
+ }
+ }
+
+ s->clic_version = g_strdup(val);
+ s->have_clic = true;
+}
+
+static void virt_get_clicintctlbits(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+ uint8_t value = s->clic_intctlbits;
+
+ visit_type_uint8(v, name, &value, errp);
+}
+
+static void virt_set_clicintctlbits(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+ uint8_t value;
+
+ if (!visit_type_uint8(v, name, &value, errp)) {
+ return;
+ }
+ if (value > MAX_CLIC_INTCTLBITS) {
+ error_setg(errp, "CLIC intctlbits must be <= %d; was %d",
+ MAX_CLIC_INTCTLBITS, value);
+ }
+
+ s->clic_intctlbits = value;
+}
+
bool virt_is_acpi_enabled(RISCVVirtState *s)
{
return s->acpi != ON_OFF_AUTO_OFF;
@@ -1767,6 +1968,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
+ ObjectProperty *op = NULL;
mc->desc = "RISC-V VirtIO board";
mc->init = virt_machine_init;
@@ -1799,6 +2001,33 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
"enable/disable emulating "
"ACLINT devices");
+ object_class_property_add_bool(oc, "clic", virt_get_clic,
+ virt_set_clic);
+ object_class_property_set_description(oc, "clic",
+ "Set on/off to enable/disable "
+ "emulating CLIC devices");
+ object_class_property_add_str(oc, "clic-mode", virt_get_clic_mode,
+ virt_set_clic_mode);
+ object_class_property_set_description(oc, "clic-mode",
+ "Specify supported CLIC modes: "
+ "m = machine (always enabled), "
+ "s = PRV_S, u = PRV_U");
+ op = object_class_property_add_str(oc, "clic-version",
+ virt_get_clic_version,
+ virt_set_clic_version);
+ object_property_set_default_str(op, VIRT_CLIC_VERSION);
+ object_class_property_set_description(oc, "clic-version",
+ "Set the CLIC version to use. "
+ "Valid values are v0.9 and v0.9-jmp");
+ op = object_class_property_add(oc, "clic-intctlbits", "uint8",
+ virt_get_clicintctlbits,
+ virt_set_clicintctlbits,
+ NULL, NULL);
+ object_property_set_default_uint(op, 8);
+ object_class_property_set_description(oc, "clic-intctlbits",
+ "The number of intctl bits used in "
+ "this CLIC implementation");
+
object_class_property_add_str(oc, "aia", virt_get_aia,
virt_set_aia);
object_class_property_set_description(oc, "aia",
diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
index c0dc41ff9a..d0a245608c 100644
--- a/include/hw/riscv/virt.h
+++ b/include/hw/riscv/virt.h
@@ -55,6 +55,11 @@ struct RISCVVirtState {
int fdt_size;
bool have_aclint;
+ bool have_clic;
+ bool clic_prv_s;
+ bool clic_prv_u;
+ uint8_t clic_intctlbits;
+ char *clic_version;
RISCVVirtAIAType aia_type;
int aia_guests;
char *oem_id;
@@ -71,6 +76,7 @@ enum {
VIRT_RTC,
VIRT_CLINT,
VIRT_ACLINT_SSWI,
+ VIRT_CLIC,
VIRT_PLIC,
VIRT_APLIC_M,
VIRT_APLIC_S,
@@ -113,6 +119,35 @@ enum {
#define VIRT_PLIC_SIZE(__num_context) \
(VIRT_PLIC_CONTEXT_BASE + (__num_context) * VIRT_PLIC_CONTEXT_STRIDE)
+#define VIRT_CLIC_INTCLTBITS 3
+#define VIRT_CLIC_VERSION "v0.9"
+#define VIRT_CLIC_MAX_IRQS 0x1000
+#define VIRT_CLIC_CONTEXT_BASE 0x1000
+#define VIRT_CLIC_CONTEXT_COUNT(_prv_s, _prv_u) \
+ (1 + ((_prv_s) ? 1 : 0) + ((_prv_u) ? 1 : 0))
+#define VIRT_CLIC_FULL_CONTEXT_COUNT VIRT_CLIC_CONTEXT_COUNT(1, 1)
+#define VIRT_CLIC_ALIGN_BITS 12
+#define VIRT_CLIC_ALIGN_MASK ((1U << VIRT_CLIC_ALIGN_BITS) - 1)
+/* Round up to next 4KiB alignment boundary */
+#define VIRT_CLIC_ALIGN(_base_addr) \
+ (((_base_addr) + VIRT_CLIC_ALIGN_MASK) & VIRT_CLIC_ALIGN_MASK)
+#define VIRT_CLIC_INT_SIZE(_irq_count) ((_irq_count) * 4)
+/*
+ * The spec doesn't define a memory layout, other than to say that each
+ * CLIC should be on a 4KiB boundary if memory-mapped.
+ * This implementation makes all the CLICs contiguous, in the order M, S, U,
+ * and assumes the worst-case size.
+ */
+#define VIRT_CLIC_BLOCK_SIZE \
+ (VIRT_CLIC_CONTEXT_BASE + VIRT_CLIC_INT_SIZE(VIRT_CLIC_MAX_IRQS))
+#define VIRT_CLIC_HART_SIZE(_prv_s, _prv_u) \
+ (VIRT_CLIC_CONTEXT_COUNT(_prv_s, _prv_u) * VIRT_CLIC_BLOCK_SIZE)
+#define VIRT_CLIC_SIZE(_hart_count, _prv_s, _prv_u) \
+ ((_hart_count) * VIRT_CLIC_HART_SIZE(_prv_s, _prv_u))
+#define VIRT_CLIC_MAX_HART_SIZE VIRT_CLIC_HART_SIZE(1, 1)
+#define VIRT_CLIC_MAX_SIZE(_hart_count) \
+ ((_hart_count) * VIRT_CLIC_MAX_HART_SIZE)
+
#define FDT_PCI_ADDR_CELLS 3
#define FDT_PCI_INT_CELLS 1
#define FDT_PLIC_ADDR_CELLS 0
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH 11/11 v2] tests: add riscv clic qtest case and a function in qtest
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
` (9 preceding siblings ...)
2024-08-19 16:02 ` [PATCH 10/11 v2] hw/riscv: add CLIC into virt machine Ian Brockbank
@ 2024-08-19 16:02 ` Ian Brockbank
2024-09-06 3:52 ` Alistair Francis
2024-09-04 7:57 ` [PATCH 00/11 v2] RISC-V: support CLIC v0.9 specification Ian Brockbank
` (2 subsequent siblings)
13 siblings, 1 reply; 32+ messages in thread
From: Ian Brockbank @ 2024-08-19 16:02 UTC (permalink / raw)
To: qemu-devel, qemu-riscv
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Ian Brockbank, Ian Brockbank,
Troy Song
This adds riscv32-clic-test.c, containing qtest test cases for configuring
CLIC (via virt machine) and for triggering interrupts.
In order to detect the interrupts, qtest.c has been updated to send interrupt
information back to the test about the IRQ being delivered. Since we need to
both trigger and detect the interrupt, qtest has also been updated to allow
both an input and an output GPIO to be intercepted.
Signed-off-by: Troy Song <wb-szh830103@alibaba-inc.com>
Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
---
hw/intc/riscv_clic.c | 4 +
include/sysemu/qtest.h | 2 +
system/qtest.c | 72 +-
tests/qtest/libqtest.c | 9 +
tests/qtest/libqtest.h | 9 +
tests/qtest/meson.build | 3 +-
tests/qtest/riscv32-clic-test.c | 1928 +++++++++++++++++++++++++++++++
7 files changed, 2010 insertions(+), 17 deletions(-)
create mode 100644 tests/qtest/riscv32-clic-test.c
diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
index 1800e84dfd..155ba65492 100644
--- a/hw/intc/riscv_clic.c
+++ b/hw/intc/riscv_clic.c
@@ -174,6 +174,10 @@ static void riscv_clic_next_interrupt(void *opaque)
clic->clicintip[active->irq] = 0;
}
/* Post pending interrupt for this hart */
+ if (qtest_enabled()) {
+ qemu_set_irq(clic->cpu_irq, qtest_encode_irq(active->irq, 1));
+ return;
+ }
clic->exccode = active->irq |
mode << RISCV_EXCP_CLIC_MODE_SHIFT |
level << RISCV_EXCP_CLIC_LEVEL_SHIFT;
diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
index c161d75165..1a34d27c6d 100644
--- a/include/sysemu/qtest.h
+++ b/include/sysemu/qtest.h
@@ -34,6 +34,8 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **
void qtest_server_set_send_handler(void (*send)(void *, const char *),
void *opaque);
void qtest_server_inproc_recv(void *opaque, const char *buf);
+
+int qtest_encode_irq(int irqn, int level);
#endif
#endif
diff --git a/system/qtest.c b/system/qtest.c
index 12703a2045..0ba6e0fcbe 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -49,7 +49,8 @@ struct QTest {
bool qtest_allowed;
-static DeviceState *irq_intercept_dev;
+static DeviceState *irq_intercept_dev_in;
+static DeviceState *irq_intercept_dev_out;
static FILE *qtest_log_fp;
static QTest *qtest;
static GString *inbuf;
@@ -61,6 +62,14 @@ static void *qtest_server_send_opaque;
#define FMT_timeval "%.06f"
+/*
+ * Encoding for passing the specific IRQ information from an interrupt handler
+ * to QTest. This needs to support CLIC, which has a 12-bit interrupt number.
+ */
+#define QTEST_IRQN 0x0fff
+#define QTEST_IRQN_SHIFT 0
+#define QTEST_IRQ_LEVEL_SHIFT 12
+
/**
* DOC: QTest Protocol
*
@@ -311,6 +320,18 @@ void qtest_sendf(CharBackend *chr, const char *fmt, ...)
va_end(ap);
}
+/* Encode the IRQ number and level for QTest */
+int qtest_encode_irq(int irqn, int level)
+{
+ return (irqn & QTEST_IRQN) | (level << QTEST_IRQ_LEVEL_SHIFT);
+}
+
+static void qtest_decode_irq(int value, int *irqn, int *level)
+{
+ *irqn = value & QTEST_IRQN;
+ *level = value >> QTEST_IRQ_LEVEL_SHIFT;
+}
+
static void qtest_irq_handler(void *opaque, int n, int level)
{
qemu_irq old_irq = *(qemu_irq *)opaque;
@@ -320,6 +341,16 @@ static void qtest_irq_handler(void *opaque, int n, int level)
CharBackend *chr = &qtest->qtest_chr;
irq_levels[n] = level;
qtest_send_prefix(chr);
+ if (level > 1) {
+ int delivered_irq_num, pin_level;
+ qtest_decode_irq(level, &delivered_irq_num, &pin_level);
+ qtest_sendf(chr, "IRQ %s %d\n",
+ "delivered", delivered_irq_num);
+ qtest_send_prefix(chr);
+ qtest_sendf(chr, "IRQ %s %d\n",
+ pin_level ? "raise" : "lower", n);
+ return;
+ }
qtest_sendf(chr, "IRQ %s %d\n",
level ? "raise" : "lower", n);
}
@@ -369,6 +400,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
bool is_named;
bool is_outbound;
bool interception_succeeded = false;
+ bool interception_duplicated = false;
g_assert(words[1]);
is_named = words[2] != NULL;
@@ -386,38 +418,46 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
return;
}
- if (irq_intercept_dev) {
- qtest_send_prefix(chr);
- if (irq_intercept_dev != dev) {
- qtest_send(chr, "FAIL IRQ intercept already enabled\n");
- } else {
- qtest_send(chr, "OK\n");
- }
- return;
- }
-
QLIST_FOREACH(ngl, &dev->gpios, node) {
/* We don't support inbound interception of named GPIOs yet */
if (is_outbound) {
+ if (irq_intercept_dev_out) {
+ if (irq_intercept_dev_out == dev) {
+ interception_succeeded = true;
+ } else {
+ interception_duplicated = true;
+ }
+ }
/* NULL is valid and matchable, for "unnamed GPIO" */
- if (g_strcmp0(ngl->name, words[2]) == 0) {
+ else if (g_strcmp0(ngl->name, words[2]) == 0) {
int i;
for (i = 0; i < ngl->num_out; ++i) {
qtest_install_gpio_out_intercept(dev, ngl->name, i);
}
+ irq_intercept_dev_out = dev;
interception_succeeded = true;
}
} else {
- qemu_irq_intercept_in(ngl->in, qtest_irq_handler,
- ngl->num_in);
- interception_succeeded = true;
+ if (irq_intercept_dev_in) {
+ if (irq_intercept_dev_in == dev) {
+ interception_succeeded = true;
+ } else {
+ interception_duplicated = true;
+ }
+ } else {
+ qemu_irq_intercept_in(ngl->in, qtest_irq_handler,
+ ngl->num_in);
+ irq_intercept_dev_in = dev;
+ interception_succeeded = true;
+ }
}
}
qtest_send_prefix(chr);
if (interception_succeeded) {
- irq_intercept_dev = dev;
qtest_send(chr, "OK\n");
+ } else if (interception_duplicated) {
+ qtest_send(chr, "FAIL IRQ intercept already enabled\n");
} else {
qtest_send(chr, "FAIL No intercepts installed\n");
}
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index 1326e34291..754c78dad7 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -83,6 +83,7 @@ struct QTestState
int expected_status;
bool big_endian;
bool irq_level[MAX_IRQ];
+ bool is_delivered[MAX_IRQ];
GString *rx;
QTestTransportOps ops;
GList *pending_events;
@@ -499,6 +500,7 @@ static QTestState *qtest_init_internal(const char *qemu_bin,
s->rx = g_string_new("");
for (i = 0; i < MAX_IRQ; i++) {
s->irq_level[i] = false;
+ s->is_delivered[i] = false;
}
/*
@@ -696,6 +698,8 @@ redo:
if (strcmp(words[1], "raise") == 0) {
s->irq_level[irq] = true;
+ } else if (strcmp(words[1], "delivered") == 0) {
+ s->is_delivered[irq] = true;
} else {
s->irq_level[irq] = false;
}
@@ -988,6 +992,11 @@ bool qtest_get_irq(QTestState *s, int num)
return s->irq_level[num];
}
+bool qtest_irq_delivered(QTestState *s, int num)
+{
+ return s->is_delivered[num];
+}
+
void qtest_module_load(QTestState *s, const char *prefix, const char *libname)
{
qtest_sendf(s, "module_load %s %s\n", prefix, libname);
diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
index c261b7e0b3..6bd0ada1b2 100644
--- a/tests/qtest/libqtest.h
+++ b/tests/qtest/libqtest.h
@@ -366,6 +366,15 @@ void qtest_module_load(QTestState *s, const char *prefix, const char *libname);
*/
bool qtest_get_irq(QTestState *s, int num);
+/**
+ * qtest_irq_delivered:
+ * @s: #QTestState instance to operate on.
+ * @num: Interrupt to observe.
+ *
+ * Returns: Is @num interrupt delivered or not.
+ */
+bool qtest_irq_delivered(QTestState *s, int num);
+
/**
* qtest_irq_intercept_in:
* @s: #QTestState instance to operate on.
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 2f0d3ef080..3170c1b386 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -258,7 +258,8 @@ qtests_s390x = \
'migration-test']
qtests_riscv32 = \
- (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : [])
+ (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_RISCV_CLIC') ? ['riscv32-clic-test'] : [])
qtests_riscv64 = \
(unpack_edk2_blobs ? ['bios-tables-test'] : [])
diff --git a/tests/qtest/riscv32-clic-test.c b/tests/qtest/riscv32-clic-test.c
new file mode 100644
index 0000000000..7b0102142f
--- /dev/null
+++ b/tests/qtest/riscv32-clic-test.c
@@ -0,0 +1,1928 @@
+/*
+ * QTest testcase for the RISC-V CLIC (Core Local Interrupt Controller)
+ *
+ * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
+ * Copyright (c) 2024 Cirrus Logic, Inc
+ * and Cirrus Logic International Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "libqtest-single.h"
+
+/*
+ * Standard arguments to qtest_start.
+ * This finishes with the machine so that machine parameters can be passed
+ * by appending the string with them. E.g. QEMU_BASE_ARGS ",clic-mode=m".
+ * QEMU_ARGS makes use of this to give more normal-looking code.
+ */
+#define QEMU_BASE_ARGS "-bios none -cpu rv32 -d guest_errors " \
+ "-machine virt,clic=on"
+#define QEMU_ARGS(_machine_params) QEMU_BASE_ARGS _machine_params
+
+/*
+ * CLIC register addresses.
+ * The spec doesn't define a memory layout, other than to say that each
+ * CLIC should be on a 4KiB boundary if memory-mapped.
+ * This implementation makes all the CLICs contiguous, in the order M, S, U,
+ * and assumes the worst-case size. If there is only PRV_M and PRV_U, the PRV_U
+ * registers will appear instead of the PRV_S.
+ */
+#define VIRT_CLIC_MAX_IRQS 0x1000
+#define VIRT_CLIC_CONTEXT_BASE 0x1000
+#define VIRT_CLIC_INT_SIZE(_irq_count) ((_irq_count) * 4)
+#define VIRT_CLIC_BLOCK_SIZE \
+ (VIRT_CLIC_CONTEXT_BASE + VIRT_CLIC_INT_SIZE(VIRT_CLIC_MAX_IRQS))
+
+#define VIRT_CLIC_MMODE_BASE 0x2000000
+#define VIRT_CLIC_SMODE_BASE (VIRT_CLIC_MMODE_BASE + VIRT_CLIC_BLOCK_SIZE)
+#define VIRT_CLIC_UMODE_BASE (VIRT_CLIC_SMODE_BASE + VIRT_CLIC_BLOCK_SIZE)
+
+#define MCLICCFG_ADDR (VIRT_CLIC_MMODE_BASE + 0)
+#define MCLICINFO_ADDR (VIRT_CLIC_MMODE_BASE + 4)
+#define SCLICCFG_ADDR (VIRT_CLIC_SMODE_BASE + 0)
+#define SCLICINFO_ADDR (VIRT_CLIC_SMODE_BASE + 4)
+#define UCLICCFG_ADDR (VIRT_CLIC_UMODE_BASE + 0)
+#define UCLICINFO_ADDR (VIRT_CLIC_UMODE_BASE + 4)
+
+/*
+ * Generate control register addresses for an irq
+ *
+ * @param irq_num IRQ to generate the addresses for
+ *
+ * Defines symbolic names for the clicintip, clicintie, clicintattr and
+ * clicintctl registers for interrupt irq_num.
+ */
+#define GEN_CLIC_IRQ_REG(irq_num) \
+ uint64_t clicint##irq_num##_addr = \
+ VIRT_CLIC_MMODE_BASE + 0x1000 + 4 * irq_num; \
+ uint64_t clicintip##irq_num##_addr = \
+ VIRT_CLIC_MMODE_BASE + 0x1000 + 4 * irq_num; \
+ uint64_t clicintie##irq_num##_addr = \
+ VIRT_CLIC_MMODE_BASE + 0x1001 + 4 * irq_num; \
+ uint64_t clicintattr##irq_num##_addr = \
+ VIRT_CLIC_MMODE_BASE + 0x1002 + 4 * irq_num; \
+ uint64_t clicintctl##irq_num##_addr = \
+ VIRT_CLIC_MMODE_BASE + 0x1003 + 4 * irq_num; \
+ uint64_t clicint##irq_num##_addr_s = \
+ VIRT_CLIC_SMODE_BASE + 0x1000 + 4 * irq_num; \
+ uint64_t clicintip##irq_num##_addr_s = \
+ VIRT_CLIC_SMODE_BASE + 0x1000 + 4 * irq_num; \
+ uint64_t clicintie##irq_num##_addr_s = \
+ VIRT_CLIC_SMODE_BASE + 0x1001 + 4 * irq_num; \
+ uint64_t clicintattr##irq_num##_addr_s = \
+ VIRT_CLIC_SMODE_BASE + 0x1002 + 4 * irq_num; \
+ uint64_t clicintctl##irq_num##_addr_s = \
+ VIRT_CLIC_SMODE_BASE + 0x1003 + 4 * irq_num; \
+ uint64_t clicint##irq_num##_addr_u = \
+ VIRT_CLIC_UMODE_BASE + 0x1000 + 4 * irq_num; \
+ uint64_t clicintip##irq_num##_addr_u = \
+ VIRT_CLIC_UMODE_BASE + 0x1000 + 4 * irq_num; \
+ uint64_t clicintie##irq_num##_addr_u = \
+ VIRT_CLIC_UMODE_BASE + 0x1001 + 4 * irq_num; \
+ uint64_t clicintattr##irq_num##_addr_u = \
+ VIRT_CLIC_UMODE_BASE + 0x1002 + 4 * irq_num; \
+ uint64_t clicintctl##irq_num##_addr_u = \
+ VIRT_CLIC_UMODE_BASE + 0x1003 + 4 * irq_num;
+
+/* test variable for configure case and we use 12 irq to test */
+GEN_CLIC_IRQ_REG(12)
+
+/* test variable for interrupt case we use irq 25 and irq 26 to test */
+GEN_CLIC_IRQ_REG(25)
+GEN_CLIC_IRQ_REG(26)
+
+/*
+ * Register decodes
+ */
+#define INTIP_SHIFT 0
+#define INTIE_SHIFT 8
+#define INTATTR_SHIFT 16
+#define INTCTL_SHIFT 24
+
+/* CLICCFG field definitions */
+#define MNL_MASK 0x0000000f
+#define MNL_SHIFT 0
+#define NMBITS_MASK_1 0x00000000 /* Only PRV_M mode */
+#define NMBITS_MASK_2 0x00000010 /* PRV_M plus either PRV_S or PRV_U */
+#define NMBITS_MASK_3 0x00000030 /* PRV_M, PRV_S and PRV_U */
+#define NMBITS_SHIFT 4
+#define SNL_MASK 0x000f0000
+#define SNL_SHIFT 16
+#define UNL_MASK 0x0f000000
+#define UNL_SHIFT 24
+
+/* The bits available in the different privilege modes */
+#define MCFG_MASK_1 (MNL_MASK | NMBITS_MASK_1)
+#define MCFG_MASK_2 (MNL_MASK | NMBITS_MASK_2)
+#define MCFG_MASK_3 (MNL_MASK | NMBITS_MASK_3)
+#define MCFG_MASK MCFG_MASK_1
+#define SCFG_MASK SNL_MASK
+#define UCFG_MASK UNL_MASK
+#define SUCFG_MASK (SCFG_MASK | UCFG_MASK)
+#define MUCFG_MASK (MCFG_MASK_2 | UCFG_MASK)
+#define MSCFG_MASK (MCFG_MASK_2 | SCFG_MASK)
+#define MSUCFG_MASK (MCFG_MASK_3 | SCFG_MASK | UCFG_MASK)
+
+/* CLICINTATTR field definitions */
+#define INTATTR_SHV 0x1
+enum INTATTR_TRIG {
+ TRIG_LEVEL = 0b00,
+ TRIG_EDGE = 0b01,
+ TRIG_POS = 0b00,
+ TRIG_NEG = 0b10,
+
+ TRIG_HIGH = TRIG_LEVEL | TRIG_POS,
+ TRIG_LOW = TRIG_LEVEL | TRIG_NEG,
+ TRIG_RISING = TRIG_EDGE | TRIG_POS,
+ TRIG_FALLING = TRIG_EDGE | TRIG_NEG,
+};
+enum INTATTR_MODE {
+ PRV_U = 0,
+ PRV_S = 1,
+ PRV_M = 3
+};
+#define INTATTR_TRIG_MASK 0x06
+#define INTATTR_TRIG_SHIFT 1
+#define INTATTR_MODE_MASK 0xC0
+#define INTATTR_MODE_SHIFT 6
+
+/* Convert the byte register definitions to the 32-bit register */
+#define REG_INTIP 0x00000001
+#define REG_INTIE 0x00000100
+#define REG_SHV (INTATTR_SHV << INTATTR_SHIFT)
+#define REG_TRIG_MASK (INTATTR_TRIG_MASK << INTATTR_SHIFT)
+#define REG_TRIG_SHIFT (INTATTR_TRIG_SHIFT + INTATTR_SHIFT)
+#define REG_MODE_MASK (INTATTR_MODE_MASK << INTATTR_SHIFT)
+#define REG_MODE_SHIFT (INTATTR_MODE_SHIFT + INTATTR_SHIFT)
+#define REG_INTCTL_MASK (0xff << INTCTL_SHIFT)
+
+/*
+ * Some test values, based on nmbits (_nmb)
+ */
+#define TEST_CFG(_nmb) ((7 << UNL_SHIFT) | (7 << SNL_SHIFT) | \
+ (7 << MNL_SHIFT) | ((_nmb) << NMBITS_SHIFT))
+#define TEST_CFG_M(_nmb) (TEST_CFG(_nmb) & MCFG_MASK) /* PRV_M only */
+#define TEST_CFG_S(_nmb) (TEST_CFG(_nmb) & SCFG_MASK) /* PRV_S in MS */
+#define TEST_CFG_U(_nmb) (TEST_CFG(_nmb) & UCFG_MASK) /* PRV_U */
+#define TEST_CFG_SU(_nmb) (TEST_CFG(_nmb) & SUCFG_MASK) /* PRV_S in MSU */
+#define TEST_CFG_MU(_nmb) (TEST_CFG(_nmb) & MUCFG_MASK) /* PRV_M in MU */
+#define TEST_CFG_MS(_nmb) (TEST_CFG(_nmb) & MSCFG_MASK) /* PRV_M in MS */
+#define TEST_CFG_MSU(_nmb) (TEST_CFG(_nmb) & MSUCFG_MASK) /* PRV_M in MSU */
+
+/*
+ * Generate a test function
+ *
+ * This writes to the given register, reads it back, and checks it has the
+ * expected value (which may be different from the write).
+ *
+ * @param CASE_NAME test case name
+ * @param WIDTH register access width - one of b, w, l or q
+ * @param WIDTH_BITS value width in bits
+ * @param reg_addr register to write and read
+ * @param set_value value to write to the register
+ * @param expected expected value to read back from the register
+ */
+#define GEN_CHECK_REG_MMIO(CASE_NAME, WIDTH, WIDTH_BITS, reg_addr, \
+ set_value, expected) \
+static void test_configure_##CASE_NAME(void) \
+{ \
+ uint##WIDTH_BITS##_t _set_value = set_value; \
+ uint##WIDTH_BITS##_t _expected = expected; \
+ write##WIDTH(reg_addr, _set_value); \
+ uint##WIDTH_BITS##_t result = read##WIDTH(reg_addr); \
+ g_assert_cmpuint(result, ==, _expected); \
+}
+
+/* test function for an 8-bit value */
+#define GEN_CHECK_REG_MMIO_B(CASE_NAME, reg_addr, set_value, expected) \
+ GEN_CHECK_REG_MMIO(CASE_NAME, b, 8, reg_addr, set_value, expected)
+/* test function for a 16-bit value */
+#define GEN_CHECK_REG_MMIO_W(CASE_NAME, reg_addr, set_value, expected) \
+ GEN_CHECK_REG_MMIO(CASE_NAME, w, 16, reg_addr, set_value, expected)
+/* test function for a 32-bit value */
+#define GEN_CHECK_REG_MMIO_L(CASE_NAME, reg_addr, set_value, expected) \
+ GEN_CHECK_REG_MMIO(CASE_NAME, l, 32, reg_addr, set_value, expected)
+/* test function for a 64-bit value */
+#define GEN_CHECK_REG_MMIO_Q(CASE_NAME, reg_addr, set_value, expected) \
+ GEN_CHECK_REG_MMIO(CASE_NAME, q, 64, reg_addr, set_value, expected)
+
+
+ /* test case definitions */
+
+/*
+ * cliccfg tests
+ *
+ * Layout:
+ * 31:28 reserved (WPRI 0)
+ * 27:24 unlbits
+ * 23:20 reserved (WPRI 0)
+ * 19:16 snlbits
+ * 15:6 reserved (WPRI 0)
+ * 5:4 nmbits
+ * 3:0 mnlbits
+ */
+
+/*
+ * Set the minimum mnlbits
+ * set nmbits = 0, mnlbits = 0, snlbits = 0, unlbits = 0
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_min_mnlbits, MCLICCFG_ADDR, 0x0, 0x0)
+
+/*
+ * Set the max supported mnlbits
+ * set nmbits = 0, mnlbits = 8, snlbits = 0, unlbits = 0
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_supported_max_mnlbits, MCLICCFG_ADDR, 0x8, 0x8)
+
+/*
+ * Set mnlbits to an unsupported value
+ * set nmbits = 0, mnlbits = 10, snlbits = 0, unlbits = 0
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_mnlbits, MCLICCFG_ADDR, 0xA, 0x8)
+
+/*
+ * Set the minimum snlbits
+ * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 0
+ * Requires PRV_S mode
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_min_snlbits_s, MCLICCFG_ADDR, 0x00004, 0x00004)
+
+/*
+ * Set the max supported snlbits
+ * set nmbits = 0, mnlbits = 4, snlbits = 8, unlbits = 0
+ * Requires PRV_S mode
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_supported_max_snlbits_s, MCLICCFG_ADDR,
+ 0x80004, 0x80004)
+
+/*
+ * Set snlbits to an unsupported value
+ * set nmbits = 0, mnlbits = 4, snlbits = 10, unlbits = 0
+ * Requires PRV_S mode
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_snlbits_s, MCLICCFG_ADDR,
+ 0xA0004, 0x80004)
+
+/*
+ * Set snlbits with no PRV_S support
+ * set nmbits = 0, mnlbits = 4, snlbits = 8, unlbits = 0
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_snlbits_no_s, MCLICCFG_ADDR, 0x80004, 0x00004)
+
+/*
+ * Set the minimum unlbits
+ * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 0
+ * Requires PRV_S mode
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_min_unlbits_u, MCLICCFG_ADDR, 0x0000004, 0x0000004)
+
+/*
+ * Set the max supported unlbits
+ * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 8
+ * Requires PRV_U mode
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_supported_max_unlbits_u, MCLICCFG_ADDR,
+ 0x8000004, 0x8000004)
+
+/*
+ * Set unlbits to an unsupported value
+ * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 10
+ * Requires PRV_U mode
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_unlbits_u, MCLICCFG_ADDR,
+ 0xA000004, 0x8000004)
+
+/*
+ * Set unlbits with no PRV_U support
+ * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 8
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_unlbits_no_u, MCLICCFG_ADDR, 0x8000004, 0x0000004)
+
+/*
+ * Set all modes
+ * set nmbits = 0, mnlbits = 4, snlbits = 2, unlbits = 2
+ * Requires PRV_S + PRV_U mode
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_xnlbits, MCLICCFG_ADDR, 0x2020004, 0x2020004)
+
+/*
+ * nmbits = 0
+ * set nmbits = 0, mnlbits = 8
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_nmbits_0, MCLICCFG_ADDR, 0x08, 0x08)
+
+/*
+ * nmbits = 1 needs PRV_S or PRV_U
+ * set nmbits = 1, mnlbits = 8
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_nmbits_1, MCLICCFG_ADDR, 0x18, 0x18)
+GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_nmbits_1, MCLICCFG_ADDR, 0x18, 0x08)
+
+/*
+ * nmbits = 2 needs PRV_S and PRV_U
+ * set nmbits = 2, mnlbits = 8
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_nmbits_2, MCLICCFG_ADDR, 0x28, 0x28)
+GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_nmbits_2, MCLICCFG_ADDR, 0x28, 0x08)
+
+/*
+ * nmbits = 3 is not supported
+ * set nmbits = 3, mnlbits = 8
+ */
+GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_nmbits_3, MCLICCFG_ADDR, 0x38, 0x08)
+
+/*
+ * clicintie tests
+ *
+ * Layout:
+ * [0] enable: 1 = enabled, 0 = disabled
+ */
+
+/* set clicintie[i] = 0x1 and compare */
+GEN_CHECK_REG_MMIO_B(clicintie_enable, clicintie12_addr, 0x1, 0x1)
+
+/* set clicintie[i] = 0x0 and compare */
+GEN_CHECK_REG_MMIO_B(clicintie_disable, clicintie12_addr, 0, 0)
+
+/*
+ * clicintattr tests
+ *
+ * Layout:
+ * [7:6] mode b00 = U, b01 = S, b10 = reserved, b11 = M
+ * [5:3] reserved
+ * [2:1] trig trig[0]: 0 = level, 1 = edge; trig[1]: 0 = pos, 1 = neg
+ * [0] shv 0 = non-vectored, 1 = vectored
+ */
+
+/* Mode tests - note these deliberately use different trig and int settings */
+/*
+ * Set mode 3 - PRV_M
+ * mode = b11, trig = b11, shv = b1
+ * clicintattr[i] = 0xc7
+ * expected
+ * mode = b11, tri = b11, shv = b1
+ * clicintattr[i] = 0xc7
+ */
+GEN_CHECK_REG_MMIO_B(clicintattr_prv_m, clicintattr12_addr,
+ 0xc7, 0xc7)
+
+/*
+ * Set mode 1 - PRV_S
+ * mode = b01, trig = b10, shv = b0
+ * clicintattr[i] = 0x44
+ * expected
+ * mode = b01, tri = b10, shv = b0
+ * clicintattr[i] = 0x44
+ */
+GEN_CHECK_REG_MMIO_B(clicintattr_prv_s_supported, clicintattr12_addr,
+ 0x44, 0x44)
+
+/*
+ * Set mode 0 - PRV_U
+ * mode = b00, trig = b01, shv = b1
+ * clicintattr[i] = 0x03
+ * expected
+ * mode = b00, tri = b01, shv = b1
+ * clicintattr[i] = 0x03
+ */
+GEN_CHECK_REG_MMIO_B(clicintattr_prv_u_supported, clicintattr12_addr,
+ 0x03, 0x03)
+
+/*
+ * WARL: clicintattr should return PRV_M for PRV_S if PRV_U and PRV_S are
+ * both unsupported or nmbits = 0.
+ *
+ * mode = b01, tri = b10, shv = b0
+ * clicintattr[i] = 0x44
+ * expected
+ * mode = b11, tri = b10, shv = b0
+ * clicintattr[i] = 0xc4
+ */
+GEN_CHECK_REG_MMIO_B(clicintattr_prv_s_to_m_warl, clicintattr12_addr,
+ 0x44, 0xc4)
+
+/*
+ * WARL: clicintattr should return PRV_U for PRV_S if PRV_S is unsupported and
+ * nmbits = 1.
+ *
+ * mode = b01, tri = b10, shv = b0
+ * clicintattr[i] = 0x44
+ * expected
+ * mode = b11, tri = b10, shv = b0
+ * clicintattr[i] = 0x04
+ */
+GEN_CHECK_REG_MMIO_B(clicintattr_prv_s_to_u_warl, clicintattr12_addr,
+ 0x44, 0x04)
+
+/*
+ * WARL: clicintattr should return PRV_M for PRV_U if PRV_U and PRV_S are
+ * both unsupported or nmbits = 0.
+ *
+ * mode = b00, tri = b01, shv = b1
+ * clicintattr[i] = 0x03
+ * expected
+ * mode = b11, tri = b01, shv = b1
+ * clicintattr[i] = 0xc3
+ */
+GEN_CHECK_REG_MMIO_B(clicintattr_prv_u_to_m_warl, clicintattr12_addr,
+ 0x03, 0xc3)
+
+/*
+ * WARL: clicintattr should return PRV_S for PRV_U if PRV_U is unsupported and
+ * nmbits = 1.
+ *
+ * mode = b00, tri = b01, shv = b1
+ * clicintattr[i] = 0x03
+ * expected
+ * mode = b01, tri = b01, shv = b1
+ * clicintattr[i] = 0x43
+ */
+GEN_CHECK_REG_MMIO_B(clicintattr_prv_u_to_s_warl, clicintattr12_addr,
+ 0x03, 0x43)
+
+/*
+ * Mode 2 is invalid
+ * mode = b10, trig = b00, shv = b0
+ * clicintattr[i] = 0x80
+ * expected
+ * mode = b11, tri = b00, shv = b1
+ * clicintattr[i] = 0xc1
+ */
+GEN_CHECK_REG_MMIO_B(clicintattr_unsupported_mode_10, clicintattr12_addr,
+ 0x81, 0xc1)
+
+/*
+ * set positive edge-triggered, vectored
+ * mode = b11, tri = b01, shv = b1
+ * clicintattr[i] = 0xc1
+ * expected
+ * mode = b11, tri = b01, shv = b1
+ * clicintattr[i] = 0xc1
+ */
+GEN_CHECK_REG_MMIO_B(clicintattr_positive_edge_triggered, clicintattr12_addr,
+ 0xc3, 0xc3)
+
+/*
+ * set negative edge-triggered, vectored
+ * mode = b11, tri = b11, shv = b1
+ * clicintattr[i] = 0xc7
+ * expected
+ * mode = b11, tri = b11, shv = b1
+ * clicintattr[i] = 0xc7
+ */
+GEN_CHECK_REG_MMIO_B(clicintattr_negative_edge_triggered, clicintattr12_addr,
+ 0xc7, 0xc7)
+
+/*
+ * set positive level-triggered, vectored
+ * mode = b11, tri = b00, shv = b1
+ * clicintattr[i] = 0xc1
+ * expected
+ * mode = b11, tri = b00, shv = b1
+ * clicintattr[i] = 0xc1
+ */
+GEN_CHECK_REG_MMIO_B(clicintattr_positive_level_triggered, clicintattr12_addr,
+ 0xc1, 0xc1)
+
+/*
+ * set negative level-triggered, vectored
+ * mode = b11, tri = b10, shv = b1
+ * clicintattr[i] = 0xc5
+ * expected
+ * mode = b11, tri = b00, shv = b1
+ * clicintattr[i] = 0xc5
+ */
+GEN_CHECK_REG_MMIO_B(clicintattr_negative_level_triggered, clicintattr12_addr,
+ 0xc5, 0xc5)
+
+/*
+ * set non-vectored
+ * mode = b11, tri = b11, shv = b0
+ * clicintattr[i] = 0xc6
+ * expected
+ * mode = b11, tri = b11, shv = b0
+ * clicintattr[i] = 0xc6
+ */
+GEN_CHECK_REG_MMIO_B(clicintattr_non_vectored, clicintattr12_addr,
+ 0xc6, 0xc6)
+
+/*
+ * clicintctl tests
+ *
+ * Layout depends on mnlbits/snlbits/unlbits in mcliccfg.
+ *
+ */
+
+/*
+ * Test with 0 intctlbits - mask 0xff
+ * Everything rounds up
+ * clicintctl[i] = 0xFF
+ */
+GEN_CHECK_REG_MMIO_B(clicintctl_set_0_0_bits, clicintctl12_addr,
+ 0x00, 0xff)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_33_0_bits, clicintctl12_addr,
+ 0x21, 0xff)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_88_0_bits, clicintctl12_addr,
+ 0x58, 0xff)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_128_0_bits, clicintctl12_addr,
+ 0x80, 0xff)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_204_0_bits, clicintctl12_addr,
+ 0xcc, 0xff)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_240_0_bits, clicintctl12_addr,
+ 0xf0, 0xff)
+
+/*
+ * Test with 1 intctlbit - mask 0x7f
+ * The top bit is used, everything else rounds up
+ */
+GEN_CHECK_REG_MMIO_B(clicintctl_set_0_1_bits, clicintctl12_addr,
+ 0x00, 0x7f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_33_1_bits, clicintctl12_addr,
+ 0x21, 0x7f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_88_1_bits, clicintctl12_addr,
+ 0x58, 0x7f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_128_1_bits, clicintctl12_addr,
+ 0x80, 0xff)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_204_1_bits, clicintctl12_addr,
+ 0xcc, 0xff)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_240_1_bits, clicintctl12_addr,
+ 0xf0, 0xff)
+
+/*
+ * Test with 2 intctlbits - mask 0x3f
+ * The top 2 bits are used, everything else rounds up
+ */
+GEN_CHECK_REG_MMIO_B(clicintctl_set_0_2_bits, clicintctl12_addr,
+ 0x00, 0x3f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_33_2_bits, clicintctl12_addr,
+ 0x21, 0x3f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_88_2_bits, clicintctl12_addr,
+ 0x58, 0x7f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_128_2_bits, clicintctl12_addr,
+ 0x80, 0xbf)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_204_2_bits, clicintctl12_addr,
+ 0xcc, 0xff)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_240_2_bits, clicintctl12_addr,
+ 0xf0, 0xff)
+
+/*
+ * Test with 3 intctlbits - mask 0x1f
+ * The top 3 bits are used, everything else rounds up
+ */
+GEN_CHECK_REG_MMIO_B(clicintctl_set_0_3_bits, clicintctl12_addr,
+ 0x00, 0x1f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_33_3_bits, clicintctl12_addr,
+ 0x21, 0x3f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_88_3_bits, clicintctl12_addr,
+ 0x58, 0x5f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_128_3_bits, clicintctl12_addr,
+ 0x80, 0x9f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_204_3_bits, clicintctl12_addr,
+ 0xcc, 0xdf)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_240_3_bits, clicintctl12_addr,
+ 0xf0, 0xff)
+
+/*
+ * Test with 4 intctlbits - mask 0x0f
+ * The top 4 bits are used, everything else rounds up
+ */
+GEN_CHECK_REG_MMIO_B(clicintctl_set_0_4_bits, clicintctl12_addr,
+ 0x00, 0x0f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_33_4_bits, clicintctl12_addr,
+ 0x21, 0x2f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_88_4_bits, clicintctl12_addr,
+ 0x58, 0x5f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_128_4_bits, clicintctl12_addr,
+ 0x80, 0x8f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_204_4_bits, clicintctl12_addr,
+ 0xcc, 0xcf)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_240_4_bits, clicintctl12_addr,
+ 0xf0, 0xff)
+
+/*
+ * Test with 5 intctlbits - mask 0x07
+ * The top 5 bits are used, everything else rounds up
+ */
+GEN_CHECK_REG_MMIO_B(clicintctl_set_0_5_bits, clicintctl12_addr,
+ 0x00, 0x07)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_33_5_bits, clicintctl12_addr,
+ 0x21, 0x27)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_88_5_bits, clicintctl12_addr,
+ 0x58, 0x5f)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_128_5_bits, clicintctl12_addr,
+ 0x80, 0x87)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_204_5_bits, clicintctl12_addr,
+ 0xcc, 0xcf)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_240_5_bits, clicintctl12_addr,
+ 0xf0, 0xf7)
+
+/*
+ * Test with 6 intctlbits - mask 0x03
+ * The top 6 bits are used, everything else rounds up
+ */
+GEN_CHECK_REG_MMIO_B(clicintctl_set_0_6_bits, clicintctl12_addr,
+ 0x00, 0x03)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_33_6_bits, clicintctl12_addr,
+ 0x21, 0x23)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_88_6_bits, clicintctl12_addr,
+ 0x58, 0x5b)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_128_6_bits, clicintctl12_addr,
+ 0x80, 0x83)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_204_6_bits, clicintctl12_addr,
+ 0xcc, 0xcf)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_240_6_bits, clicintctl12_addr,
+ 0xf0, 0xf3)
+
+/*
+ * Test with 7 intctlbits - mask 0x01
+ * The top 7 bits are used, everything else rounds up
+ */
+GEN_CHECK_REG_MMIO_B(clicintctl_set_0_7_bits, clicintctl12_addr,
+ 0x00, 0x01)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_33_7_bits, clicintctl12_addr,
+ 0x21, 0x21)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_88_7_bits, clicintctl12_addr,
+ 0x58, 0x59)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_128_7_bits, clicintctl12_addr,
+ 0x80, 0x81)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_204_7_bits, clicintctl12_addr,
+ 0xcc, 0xcd)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_240_7_bits, clicintctl12_addr,
+ 0xf0, 0xf1)
+
+/*
+ * Test with 8 intctlbits - mask 0x00
+ * All bits are used
+ */
+GEN_CHECK_REG_MMIO_B(clicintctl_set_0_8_bits, clicintctl12_addr,
+ 0x00, 0x00)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_33_8_bits, clicintctl12_addr,
+ 0x21, 0x21)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_88_8_bits, clicintctl12_addr,
+ 0x58, 0x58)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_128_8_bits, clicintctl12_addr,
+ 0x80, 0x80)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_204_8_bits, clicintctl12_addr,
+ 0xcc, 0xcc)
+GEN_CHECK_REG_MMIO_B(clicintctl_set_240_8_bits, clicintctl12_addr,
+ 0xf0, 0xf0)
+
+/*
+ * read clicintip[i] to result
+ * set cliccfg = 0x11, clicintattr[i] = 0x31, clicintip[i] = 0x1
+ * check the value of clicintip[i] that is changed or not
+ */
+static void test_configure_clicintip_level_triggered_read_only(void)
+{
+ /* configure level-triggered mode */
+ writeb(clicintattr12_addr, 0xc1);
+ g_assert_cmpuint(readb(clicintattr12_addr), ==, 0xc1);
+
+ uint8_t orig_value = readb(clicintip12_addr);
+ writeb(clicintip12_addr, 0x1);
+ uint32_t result = readb(clicintip12_addr);
+
+ g_assert_cmpuint(orig_value, ==, result);
+}
+
+static void boot_qemu_m(void)
+{
+ global_qtest = qtest_start(QEMU_ARGS(",clic-mode=m"));
+}
+
+static void boot_qemu_ms(void)
+{
+ global_qtest = qtest_start(QEMU_ARGS(",clic-mode=ms"));
+}
+
+static void boot_qemu_mu(void)
+{
+ global_qtest = qtest_start(QEMU_ARGS(",clic-mode=mu"));
+}
+
+static void boot_qemu_msu(void)
+{
+ global_qtest = qtest_start(QEMU_ARGS(",clic-mode=msu"));
+}
+
+#define GEN_BOOT_QEMU_INTCTL(_nbits) \
+static void boot_qemu_##_nbits##_bits(void) \
+{ \
+ global_qtest = qtest_initf(QEMU_BASE_ARGS \
+ ",clic-mode=msu,clic-intctlbits=%d", \
+ _nbits); \
+}
+
+static void shut_down_qemu(void)
+{
+ qtest_quit(global_qtest);
+}
+
+/*
+ * test vectored positive level triggered interrupt
+ * test points:
+ * 1. we use interrupt 25 and 26 to test arbitration in two situation:
+ * same level, different level.
+ * 2. within level triggered mode, we can only use device to clear pending.
+ * 3. within positive level triggered mode, set gpio-in rise
+ * to trigger interrupt.
+ */
+static void test_vectored_positive_level_triggered_interrupt(void)
+{
+ QTestState *qts = qtest_start(QEMU_BASE_ARGS);
+ /* intercept in and out irq */
+ qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
+ qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
+
+ /* configure */
+ qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
+ qtest_writeb(qts, clicintattr25_addr, 0xc1);
+ qtest_writeb(qts, clicintattr26_addr, 0xc1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
+
+ /* set pending */
+ /* arbitration will be made and 26 will be delivered */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
+ qtest_writeb(qts, clicintie25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
+ qtest_writeb(qts, clicintie26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
+ /* trigger arbitration */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ g_assert_true(qtest_irq_delivered(qts, 26));
+ g_assert_true(qtest_get_irq(qts, 0));
+
+ /*
+ * level trigger wouldn't auto clear clear pending,
+ * so we need to manually do it.
+ */
+ qtest_writeb(qts, clicintie25_addr, 0);
+ qtest_writeb(qts, clicintie26_addr, 0);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
+
+ /* set interrupt 25 level 255, interrupt 26 level 127 */
+ /* arbitration will be made and 25 will rise */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
+ qtest_writeb(qts, clicintctl25_addr, 0xbf);
+ qtest_writeb(qts, clicintctl26_addr, 0x3f);
+ qtest_writeb(qts, clicintie25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
+ qtest_writeb(qts, clicintie26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
+ /* trigger arbitration */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ g_assert_true(qtest_irq_delivered(qts, 25));
+ g_assert_true(qtest_get_irq(qts, 0));
+
+ qtest_quit(qts);
+}
+
+/*
+ * test vectored negative level triggered interrupt
+ * test points:
+ * 1. we use interrupt 25 and 26 to test arbitration in two situation:
+ * same level, different level.
+ * 2. within level triggered mode, we can only use device to clear pending.
+ * 3. within negative level triggered mode,
+ * set gpio-in lower to trigger interrupt.
+ */
+static void test_vectored_negative_level_triggered_interrupt(void)
+{
+ QTestState *qts = qtest_start(QEMU_BASE_ARGS);
+ /* intercept in and out irq */
+ qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
+ qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
+
+ /* configure */
+ qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
+ qtest_writeb(qts, clicintattr25_addr, 0xc5);
+ qtest_readb(qts, clicintattr25_addr);
+ qtest_writeb(qts, clicintattr26_addr, 0xc5);
+ qtest_readb(qts, clicintattr26_addr);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
+
+ /* set pending */
+ /* arbitration will be made and 26 will be delivered */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
+ qtest_writeb(qts, clicintie25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
+ qtest_writeb(qts, clicintie26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
+ /* trigger arbitration */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ g_assert_true(qtest_irq_delivered(qts, 26));
+ g_assert_true(qtest_get_irq(qts, 0));
+
+ /*
+ * level trigger wouldn't auto clear clear pending,
+ * so we need to manually do it.
+ */
+ qtest_writeb(qts, clicintie25_addr, 0);
+ qtest_writeb(qts, clicintie26_addr, 0);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
+
+ /* set interrupt 25 level 255, interrupt 26 level 127 */
+ /* arbitration will be made and 25 will rise */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
+ qtest_writeb(qts, clicintctl25_addr, 0xbf);
+ qtest_writeb(qts, clicintctl26_addr, 0x3f);
+ qtest_writeb(qts, clicintie25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
+ qtest_writeb(qts, clicintie26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
+ /* trigger arbitration */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ g_assert_true(qtest_irq_delivered(qts, 25));
+ g_assert_true(qtest_get_irq(qts, 0));
+
+ qtest_quit(qts);
+}
+
+/*
+ * test vectored positive edge triggered interrupt
+ * test points:
+ * 1. we use interrupt 25 and 26 to test arbitration in two situation:
+ * same level, different level.
+ * 2. within vectored edge triggered mode, pending bit will be
+ * automatically cleared.
+ * 3. within positive edge triggered mode, set gpio-in from
+ * lower to rise to trigger interrupt.
+ */
+static void test_vectored_positive_edge_triggered_interrupt(void)
+{
+ QTestState *qts = qtest_start(QEMU_BASE_ARGS);
+ /* intercept in and out irq */
+ qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
+ qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
+
+ /* configure */
+ qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
+ qtest_writeb(qts, clicintattr25_addr, 0xc3);
+ qtest_writeb(qts, clicintattr26_addr, 0xc3);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
+
+ /* set pending */
+ /* arbitration will be made and 26 will be delivered */
+ qtest_writeb(qts, clicintip25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
+ qtest_writeb(qts, clicintip26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
+ qtest_writeb(qts, clicintie25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
+ qtest_writeb(qts, clicintie26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
+ /* trigger arbitration */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ g_assert_true(qtest_irq_delivered(qts, 26));
+ g_assert_true(qtest_get_irq(qts, 0));
+
+ /* vectored edge trigger will auto clear clear pending */
+ qtest_writeb(qts, clicintie25_addr, 0);
+ qtest_writeb(qts, clicintie26_addr, 0);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
+
+ /* set interrupt 25 level 255, interrupt 26 level 127 */
+ /* arbitration will be made and 25 will rise */
+ qtest_writeb(qts, clicintip25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
+ qtest_writeb(qts, clicintip26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
+ qtest_writeb(qts, clicintctl25_addr, 0xbf);
+ qtest_writeb(qts, clicintctl26_addr, 0x3f);
+ qtest_writeb(qts, clicintie25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
+ qtest_writeb(qts, clicintie26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
+ /* trigger arbitration */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ g_assert_true(qtest_irq_delivered(qts, 25));
+ g_assert_true(qtest_get_irq(qts, 0));
+
+ qtest_quit(qts);
+}
+
+/*
+ * test vectored negative edge triggered interrupt
+ * test points:
+ * 1. we use interrupt 25 and 26 to test arbitration in two situation:
+ * same level, different level.
+ * 2. within vectored edge triggered mode, pending bit will be
+ * automatically cleared.
+ * 3. within negative edge triggered mode, set gpio-in from
+ * rise to lower to trigger interrupt.
+ */
+static void test_vectored_negative_edge_triggered_interrupt(void)
+{
+ QTestState *qts = qtest_start(QEMU_BASE_ARGS);
+ /* intercept in and out irq */
+ qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
+ qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
+
+ /* configure */
+ qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
+ qtest_writeb(qts, clicintattr25_addr, 0xc7);
+ qtest_writeb(qts, clicintattr26_addr, 0xc7);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
+
+ /* set pending */
+ /* arbitration will be made and 26 will be delivered */
+ qtest_writeb(qts, clicintip25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
+ qtest_writeb(qts, clicintip26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
+ qtest_writeb(qts, clicintie25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
+ qtest_writeb(qts, clicintie26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
+ /* trigger arbitration */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ g_assert_true(qtest_irq_delivered(qts, 26));
+ g_assert_true(qtest_get_irq(qts, 0));
+
+ /* vectored edge trigger will auto clear clear pending */
+ qtest_writeb(qts, clicintie25_addr, 0);
+ qtest_writeb(qts, clicintie26_addr, 0);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
+
+ /* set interrupt 25 level 255, interrupt 26 level 127 */
+ /* arbitration will be made and 25 will rise */
+ qtest_writeb(qts, clicintip25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
+ qtest_writeb(qts, clicintip26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
+ qtest_writeb(qts, clicintctl25_addr, 0xbf);
+ qtest_writeb(qts, clicintctl26_addr, 0x3f);
+ qtest_writeb(qts, clicintie25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
+ qtest_writeb(qts, clicintie26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
+ /* trigger arbitration */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ g_assert_true(qtest_irq_delivered(qts, 25));
+ g_assert_true(qtest_get_irq(qts, 0));
+
+ qtest_quit(qts);
+}
+
+/*
+ * test unvectored positive level triggered interrupt
+ * test points:
+ * 1. we use interrupt 25 and 26 to test arbitration in two situation:
+ * same level, different level.
+ * 2. within level triggered mode, we can only use device to clear pending.
+ * 3. within positive level triggered mode, set gpio-in rise to
+ * trigger interrupt.
+ */
+static void test_unvectored_positive_level_triggered_interrupt(void)
+{
+ QTestState *qts = qtest_start(QEMU_BASE_ARGS);
+ /* intercept in and out irq */
+ qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
+ qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
+
+ /* configure */
+ qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
+ qtest_writeb(qts, clicintattr25_addr, 0xc0);
+ qtest_writeb(qts, clicintattr26_addr, 0xc0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
+
+ /* set pending */
+ /* arbitration will be made and 26 will be delivered */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
+ qtest_writeb(qts, clicintie25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
+ qtest_writeb(qts, clicintie26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
+ /* trigger arbitration */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ g_assert_true(qtest_irq_delivered(qts, 26));
+ g_assert_true(qtest_get_irq(qts, 0));
+
+ /*
+ * level trigger wouldn't auto clear clear pending,
+ * so we need to manually do it.
+ */
+ qtest_writeb(qts, clicintie25_addr, 0);
+ qtest_writeb(qts, clicintie26_addr, 0);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
+
+ /* set interrupt 25 level 255, interrupt 26 level 127 */
+ /* arbitration will be made and 25 will rise */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
+ qtest_writeb(qts, clicintctl25_addr, 0xbf);
+ qtest_writeb(qts, clicintctl26_addr, 0x3f);
+ qtest_writeb(qts, clicintie25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
+ qtest_writeb(qts, clicintie26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
+ /* trigger arbitration */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ g_assert_true(qtest_irq_delivered(qts, 25));
+ g_assert_true(qtest_get_irq(qts, 0));
+
+ qtest_quit(qts);
+}
+
+/*
+ * test unvectored negative level triggered interrupt
+ * test points:
+ * 1. we use interrupt 25 and 26 to test arbitration in two situation:
+ * same level, different level.
+ * 2. within level triggered mode, we can only use device to clear pending.
+ * 3. within negative level triggered mode, set gpio-in lower
+ * to trigger interrupt.
+ */
+static void test_unvectored_negative_level_triggered_interrupt(void)
+{
+ QTestState *qts = qtest_start(QEMU_BASE_ARGS);
+ /* intercept in and out irq */
+ qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
+ qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
+
+ /* configure */
+ qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
+ qtest_writeb(qts, clicintattr25_addr, 0xc4);
+ qtest_writeb(qts, clicintattr26_addr, 0xc4);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
+
+ /* set pending */
+ /* arbitration will be made and 26 will be delivered */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
+ qtest_writeb(qts, clicintie25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
+ qtest_writeb(qts, clicintie26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
+ /* trigger arbitration */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ g_assert_true(qtest_irq_delivered(qts, 26));
+ g_assert_true(qtest_get_irq(qts, 0));
+
+ /*
+ * level trigger wouldn't auto clear clear pending,
+ * so we need to manually do it.
+ */
+ qtest_writeb(qts, clicintie25_addr, 0);
+ qtest_writeb(qts, clicintie26_addr, 0);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
+
+ /* set interrupt 25 level 255, interrupt 26 level 127 */
+ /* arbitration will be made and 25 will rise */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
+ qtest_writeb(qts, clicintctl25_addr, 0xbf);
+ qtest_writeb(qts, clicintctl26_addr, 0x3f);
+ qtest_writeb(qts, clicintie25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
+ qtest_writeb(qts, clicintie26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
+ /* trigger arbitration */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ g_assert_true(qtest_irq_delivered(qts, 25));
+ g_assert_true(qtest_get_irq(qts, 0));
+
+ qtest_quit(qts);
+}
+
+/*
+ * test unvectored positive edge triggered interrupt
+ * test points:
+ * 1. we use interrupt 25 and 26 to test arbitration in same level
+ * 2. within unvectored edge triggered mode, pending bit can be
+ * cleared by using nxti instruction which can't be tested in qtest.
+ * 3. within positive edge triggered mode, set gpio-in
+ * from lower to rise to trigger interrupt.
+ */
+static void test_unvectored_positive_edge_triggered_interrupt(void)
+{
+ QTestState *qts = qtest_start(QEMU_BASE_ARGS);
+ /* intercept in and out irq */
+ qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
+ qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
+
+ /* configure */
+ qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
+ qtest_writeb(qts, clicintattr25_addr, 0xc2);
+ qtest_writeb(qts, clicintattr26_addr, 0xc2);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
+
+ /* set pending */
+ /* arbitration will be made and 26 will be delivered */
+ qtest_writeb(qts, clicintip25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
+ qtest_writeb(qts, clicintip26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
+ qtest_writeb(qts, clicintie25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
+ qtest_writeb(qts, clicintie26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
+ /* trigger arbitration */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ g_assert_true(qtest_irq_delivered(qts, 26));
+ g_assert_true(qtest_get_irq(qts, 0));
+
+ qtest_quit(qts);
+}
+
+/*
+ * test unvectored negative edge triggered interrupt
+ * test points:
+ * 1. we use interrupt 25 and 26 to test arbitration in same level
+ * 2. within unvectored edge triggered mode, pending bit can be cleared
+ * by using nxti instruction which can't be tested in qtest.
+ * 3. within positive edge triggered mode, set gpio-in from
+ * rise to lower to trigger interrupt.
+ */
+static void test_unvectored_negative_edge_triggered_interrupt(void)
+{
+ QTestState *qts = qtest_start(QEMU_BASE_ARGS);
+ /* intercept in and out irq */
+ qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
+ qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
+
+ /* configure */
+ qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
+ qtest_writeb(qts, clicintattr25_addr, 0xc6);
+ qtest_writeb(qts, clicintattr26_addr, 0xc6);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
+
+ /* set pending */
+ /* arbitration will be made and 26 will be delivered */
+ qtest_writeb(qts, clicintip25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
+ qtest_writeb(qts, clicintip26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
+ qtest_writeb(qts, clicintie25_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
+ qtest_writeb(qts, clicintie26_addr, 1);
+ g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
+ /* trigger arbitration */
+ qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
+ g_assert_true(qtest_irq_delivered(qts, 26));
+ g_assert_true(qtest_get_irq(qts, 0));
+
+ qtest_quit(qts);
+}
+
+/*
+ * Test that PRV_S is a filtered view of PRV_M.
+ *
+ * IRQs configured as PRV_M in the mode field of intattr are not visible via
+ * the PRV_S registers, and all registers appear as hard-wired zeros.
+ */
+static void test_prv_s_access(void)
+{
+ QTestState *qts = qtest_start(QEMU_ARGS(",clic-mode=ms"));
+ int default_intattr = INTATTR_SHV | (TRIG_LEVEL << INTATTR_TRIG_SHIFT);
+ int default_reg_value = (default_intattr << INTATTR_SHIFT) |
+ (PRV_M << REG_MODE_SHIFT) | (0x7 << INTCTL_SHIFT);
+ int reg_value_2 = (TRIG_FALLING << REG_TRIG_SHIFT) |
+ (PRV_S << REG_MODE_SHIFT) | (0x5 << INTCTL_SHIFT);
+ int value;
+
+ /*
+ * Make sure of our base state using the PRV_M registers
+ */
+ qtest_writel(qts, MCLICCFG_ADDR, TEST_CFG(1));
+ /* No PRV_U, so no UNLBITS */
+ g_assert_cmpuint(qtest_readl(qts, MCLICCFG_ADDR), == , TEST_CFG_MS(1));
+
+ qtest_writel(qts, clicint12_addr, default_reg_value);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value);
+ qtest_writel(qts, clicint25_addr, default_reg_value);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
+ qtest_writel(qts, clicint26_addr, default_reg_value);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
+
+ /*
+ * Now check the PRV_S view
+ */
+
+ /* We should only see the PRV_S part of CLICCFG */
+ g_assert_cmpuint(qtest_readl(qts, SCLICCFG_ADDR), == , TEST_CFG_S(1));
+
+ /* These are all PRV_M mode so reading via PRV_S should see them all as 0 */
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , 0);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0);
+
+ /* Writing should leave them unchanged */
+ qtest_writel(qts, clicint12_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value);
+ qtest_writel(qts, clicint25_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
+ qtest_writel(qts, clicint26_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
+
+ /*
+ * If we change IRQ 12 to PRV_S mode, we should now be able to see it
+ */
+ value = (default_reg_value & ~REG_MODE_MASK) | (PRV_S << REG_MODE_SHIFT);
+ qtest_writel(qts, clicint12_addr, value);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , value);
+
+ /* ...but not the others */
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0);
+
+ /* We should also be able to write to it */
+ qtest_writel(qts, clicint12_addr_s, reg_value_2);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , reg_value_2);
+
+ /* ...but not the others */
+ qtest_writel(qts, clicint25_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
+ qtest_writel(qts, clicint26_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
+
+ qtest_quit(qts);
+}
+
+/*
+ * Test that PRV_U is a filtered view of PRV_M.
+ *
+ * IRQs configured as PRV_M in the mode field of intattr are not visible via
+ * the PRV_U registers, and all registers appear as hard-wired zeros.
+ */
+static void test_prv_u_access(void)
+{
+ QTestState *qts = qtest_start(QEMU_ARGS(",clic-mode=mu"));
+ int default_intattr = INTATTR_SHV | (TRIG_LEVEL << INTATTR_TRIG_SHIFT);
+ int default_reg_value = (default_intattr << INTATTR_SHIFT) |
+ (PRV_M << REG_MODE_SHIFT) | (0x7 << INTCTL_SHIFT);
+ int reg_value_2 = (TRIG_FALLING << REG_TRIG_SHIFT) |
+ (PRV_U << REG_MODE_SHIFT) | (0x5 << INTCTL_SHIFT);
+ int value;
+
+ /*
+ * Make sure of our base state using the PRV_M registers
+ */
+ qtest_writel(qts, MCLICCFG_ADDR, TEST_CFG(1));
+ /* No PRV_S, so no SNLBITS */
+ g_assert_cmpuint(qtest_readl(qts, MCLICCFG_ADDR), == , TEST_CFG_MU(1));
+
+ qtest_writel(qts, clicint12_addr, default_reg_value);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value);
+ qtest_writel(qts, clicint25_addr, default_reg_value);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
+ qtest_writel(qts, clicint26_addr, default_reg_value);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
+
+ /*
+ * Now check the PRV_U view. Note we only have one additional mode, so we
+ * use the xxx_addr_s register bank.
+ */
+
+ /* We should only see the PRV_U part of CLICCFG */
+ g_assert_cmpuint(qtest_readl(qts, SCLICCFG_ADDR), == , TEST_CFG_U(1));
+
+ /* These are all PRV_M mode so reading via PRV_S should see them all as 0 */
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , 0);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0);
+
+ /* Writing should leave them unchanged */
+ qtest_writel(qts, clicint12_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value);
+ qtest_writel(qts, clicint25_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
+ qtest_writel(qts, clicint26_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
+
+ /*
+ * If we change IRQ 12 to PRV_U mode, we should now be able to see it
+ */
+ value = (default_reg_value & ~REG_MODE_MASK) | (PRV_U << REG_MODE_SHIFT);
+ qtest_writel(qts, clicint12_addr, value);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , value);
+
+ /* ...but not the others */
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0);
+
+ /* We should also be able to write to it */
+ qtest_writel(qts, clicint12_addr_s, reg_value_2);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , reg_value_2);
+
+ /* ...but not the others */
+ qtest_writel(qts, clicint25_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
+ qtest_writel(qts, clicint26_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
+
+ qtest_quit(qts);
+}
+
+/*
+ * Test that PRV_S and PRV_U are filtered views of PRV_M.
+ *
+ * IRQs configured as PRV_M in the mode field of intattr are not visible via
+ * the PRV_S or PRV_U registers, and all registers appear as hard-wired zeros.
+ */
+static void test_prv_su_access(void)
+{
+ QTestState *qts = qtest_start(QEMU_ARGS(",clic-mode=msu"));
+ int default_intattr = INTATTR_SHV | (TRIG_LEVEL << INTATTR_TRIG_SHIFT);
+ int default_reg_value = (default_intattr << INTATTR_SHIFT) |
+ (PRV_M << REG_MODE_SHIFT) | (0x7 << INTCTL_SHIFT);
+ int reg_value_2 = (TRIG_FALLING << REG_TRIG_SHIFT) |
+ (PRV_S << REG_MODE_SHIFT) | (0x5 << INTCTL_SHIFT);
+ int reg_value_3 = (TRIG_RISING << REG_TRIG_SHIFT) |
+ (PRV_U << REG_MODE_SHIFT) | (0x2 << INTCTL_SHIFT);
+ int reg_value_4 = INTATTR_SHV | (TRIG_HIGH << REG_TRIG_SHIFT) |
+ (PRV_U << REG_MODE_SHIFT) | (0x3 << INTCTL_SHIFT);
+ int value;
+
+ /*
+ * Make sure of our base state using the PRV_M registers
+ */
+ qtest_writel(qts, MCLICCFG_ADDR, TEST_CFG(2));
+ g_assert_cmpuint(qtest_readl(qts, MCLICCFG_ADDR), == , TEST_CFG_MSU(2));
+
+ qtest_writel(qts, clicint12_addr, default_reg_value);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value);
+ qtest_writel(qts, clicint25_addr, default_reg_value);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
+ qtest_writel(qts, clicint26_addr, default_reg_value);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
+
+ /*
+ * Now check the PRV_S view
+ */
+
+ /* We should only see the PRV_S and PRV_U parts of CLICCFG */
+ g_assert_cmpuint(qtest_readl(qts, SCLICCFG_ADDR), == , TEST_CFG_SU(2));
+
+ /* These are all PRV_M mode so reading via PRV_S should see them all as 0 */
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , 0);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0);
+
+ /* Writing should leave them unchanged */
+ qtest_writel(qts, clicint12_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value);
+ qtest_writel(qts, clicint25_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
+ qtest_writel(qts, clicint26_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
+
+ /*
+ * If we change IRQ 12 to PRV_S mode, we should now be able to see it
+ */
+ value = (default_reg_value & ~REG_MODE_MASK) | (PRV_S << REG_MODE_SHIFT);
+ qtest_writel(qts, clicint12_addr, value);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , value);
+
+ /* ...but not the others */
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0);
+
+ /* We should also be able to write to it */
+ qtest_writel(qts, clicint12_addr_s, reg_value_2);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , reg_value_2);
+
+ /* ...but not the others */
+ qtest_writel(qts, clicint25_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
+ qtest_writel(qts, clicint26_addr_s, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
+
+ /*
+ * Now check the PRV_U view
+ */
+
+ /* We should only see the PRV_U part of CLICCFG */
+ g_assert_cmpuint(qtest_readl(qts, UCLICCFG_ADDR), == , TEST_CFG_U(2));
+
+ /* These are all PRV_M or PRV_S so reading via PRV_U should see them as 0 */
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr_u), == , 0);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr_u), == , 0);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr_u), == , 0);
+
+ /* Writing should leave them unchanged */
+ qtest_writel(qts, clicint12_addr_u, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2);
+ qtest_writel(qts, clicint25_addr_u, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
+ qtest_writel(qts, clicint26_addr_u, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
+
+ /*
+ * If we change IRQ 25 to PRV_U mode, we should now be able to see it
+ * in both PRV_S and PRV_U modes
+ */
+ value = (default_reg_value & ~REG_MODE_MASK) | (PRV_U << REG_MODE_SHIFT);
+ qtest_writel(qts, clicint25_addr, value);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , value);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr_u), == , value);
+
+ /* ...we can't see the others in PRV_U */
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr_u), == , 0);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr_u), == , 0);
+
+ /* We should also be able to write to it from both PRV_S and PRV_U */
+ qtest_writel(qts, clicint25_addr_s, reg_value_3);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , reg_value_3);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , reg_value_3);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr_u), == , reg_value_3);
+ qtest_writel(qts, clicint25_addr_u, reg_value_4);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , reg_value_4);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , reg_value_4);
+ g_assert_cmpuint(qtest_readl(qts, clicint25_addr_u), == , reg_value_4);
+
+ /* ...but we still can't write to the others in PRV_U */
+ qtest_writel(qts, clicint12_addr_u, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2);
+ qtest_writel(qts, clicint26_addr_u, 0x55555555);
+ g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
+
+ qtest_quit(qts);
+}
+
+/* Test configuration in PRV_M-only mode */
+static void clic_configure_reg_mmio_test_case_m(void)
+{
+ /* Start QEMU */
+ qtest_add_func("virt/clic/prv_m/boot_qemu_m",
+ boot_qemu_m);
+
+ /* cliccfg configure case */
+ qtest_add_func("virt/clic/prv_m/cliccfg_min_mnlbits",
+ test_configure_cliccfg_min_mnlbits);
+ qtest_add_func("virt/clic/prv_m/cliccfg_supported_max_mnlbits",
+ test_configure_cliccfg_supported_max_mnlbits);
+ qtest_add_func("virt/clic/prv_m/cliccfg_unsupported_mnlbits",
+ test_configure_cliccfg_unsupported_mnlbits);
+ /* snlbits and unlbits should not work */
+ qtest_add_func("virt/clic/prv_m/cliccfg_snlbits_no_s",
+ test_configure_cliccfg_snlbits_no_s);
+ qtest_add_func("virt/clic/prv_m/cliccfg_snlbits_no_u",
+ test_configure_cliccfg_unlbits_no_u);
+ /* clicintip configure case */
+ qtest_add_func("virt/clic/prv_m/clicintip_level_triggered_readonly",
+ test_configure_clicintip_level_triggered_read_only);
+
+ /* clicintie configure case */
+ qtest_add_func("virt/clic/prv_m/clicintie_enable",
+ test_configure_clicintie_enable);
+ qtest_add_func("virt/clic/prv_m/clicintie_disable",
+ test_configure_clicintie_disable);
+
+ /* clicintattr mode configure cases are all PRV_M WARL - nmbits == 0*/
+ qtest_add_func("virt/clic/prv_m/cliccfg_nmbits_0_m",
+ test_configure_cliccfg_nmbits_0);
+ qtest_add_func("virt/clic/prv_m/intattr_prv_m",
+ test_configure_clicintattr_prv_m);
+ qtest_add_func("virt/clic/prv_m/intattr_prv_s_to_m_warl",
+ test_configure_clicintattr_prv_s_to_m_warl);
+ qtest_add_func("virt/clic/prv_m/intattr_prv_u_to_m_warl",
+ test_configure_clicintattr_prv_u_to_m_warl);
+ qtest_add_func("virt/clic/prv_m/intattr_unsupported_mode_10",
+ test_configure_clicintattr_unsupported_mode_10);
+
+ /* unsupported nmbits */
+ qtest_add_func("virt/clic/prv_m/cliccfg_unsupported_nmbits_1_m",
+ test_configure_cliccfg_unsupported_nmbits_1);
+ qtest_add_func("virt/clic/prv_m/cliccfg_unsupported_nmbits_2_m",
+ test_configure_cliccfg_unsupported_nmbits_2);
+ qtest_add_func("virt/clic/prv_m/cliccfg_unsupported_nmbits_3_m",
+ test_configure_cliccfg_unsupported_nmbits_3);
+
+ /* clicintattr TRIG and SHV */
+ qtest_add_func("virt/clic/prv_m/intattr_positive_edge_triggered",
+ test_configure_clicintattr_positive_edge_triggered);
+ qtest_add_func("virt/clic/prv_m/clicintattr_negative_edge_triggered",
+ test_configure_clicintattr_negative_edge_triggered);
+ qtest_add_func("virt/clic/prv_m/clicintattr_positive_level_triggered",
+ test_configure_clicintattr_positive_level_triggered);
+ qtest_add_func("virt/clic/prv_m/clicintattr_negative_level_triggered",
+ test_configure_clicintattr_negative_level_triggered);
+ qtest_add_func("virt/clic/prv_m/clicintattr_non_vectored",
+ test_configure_clicintattr_non_vectored);
+
+ /* Shut down QEMU */
+ qtest_add_func("virt/clic/prv_m/shut_down_qemu_m",
+ shut_down_qemu);
+}
+
+/* Test configuration in PRV_M + PRV_S mode */
+static void clic_configure_reg_mmio_test_case_ms(void)
+{
+ /* Start QEMU */
+ qtest_add_func("virt/clic/prv_ms/boot_qemu_ms",
+ boot_qemu_ms);
+
+ /* cliccfg configure cases */
+
+ /* mnlbits should be unaffected */
+ qtest_add_func("virt/clic/prv_ms/cliccfg_min_mnlbits_ms",
+ test_configure_cliccfg_min_mnlbits);
+ qtest_add_func("virt/clic/prv_ms/cliccfg_supported_max_mnlbits_ms",
+ test_configure_cliccfg_supported_max_mnlbits);
+ qtest_add_func("virt/clic/prv_ms/cliccfg_unsupported_mnlbits_ms",
+ test_configure_cliccfg_unsupported_mnlbits);
+ /* snlbits should work*/
+ qtest_add_func("virt/clic/prv_ms/cliccfg_min_snlbits_s_ms",
+ test_configure_cliccfg_min_snlbits_s);
+ qtest_add_func("virt/clic/prv_ms/cliccfg_supported_max_snlbits_s_ms",
+ test_configure_cliccfg_supported_max_snlbits_s);
+ qtest_add_func("virt/clic/prv_ms/cliccfg_unsupported_snlbits_s_ms",
+ test_configure_cliccfg_unsupported_snlbits_s);
+ /* unlbits should not work */
+ qtest_add_func("virt/clic/prv_ms/cliccfg_unlbits_no_u_ms",
+ test_configure_cliccfg_unlbits_no_u);
+
+ /* clicintattr mode configure cases with nmbits = 1 */
+ qtest_add_func("virt/clic/prv_ms/cliccfg_nmbits_1_ms",
+ test_configure_cliccfg_nmbits_1);
+ qtest_add_func("virt/clic/prv_ms/intattr_prv_m_nmbits_1",
+ test_configure_clicintattr_prv_m);
+ qtest_add_func("virt/clic/prv_ms/intattr_unsupported_mode_10_nmbits_1",
+ test_configure_clicintattr_unsupported_mode_10);
+ qtest_add_func("virt/clic/prv_ms/intattr_prv_u_to_s_warl_nmbits_1_prv_m",
+ test_configure_clicintattr_prv_u_to_s_warl);
+ qtest_add_func("virt/clic/prv_ms/intattr_prv_s_supported_nmbits_1",
+ test_configure_clicintattr_prv_s_supported);
+ qtest_add_func("virt/clic/prv_ms/intattr_prv_u_to_s_warl_nmbits_1_prv_s",
+ test_configure_clicintattr_prv_u_to_s_warl);
+
+ /* clicintattr mode configure cases with nmbits = 0 - PRV_M only */
+ qtest_add_func("virt/clic/prv_ms/cliccfg_nmbits_0_ms",
+ test_configure_cliccfg_nmbits_0);
+ qtest_add_func("virt/clic/prv_ms/intattr_prv_m_nmbits_0",
+ test_configure_clicintattr_prv_m);
+ qtest_add_func("virt/clic/prv_ms/intattr_unsupported_mode_10_nmbits_0",
+ test_configure_clicintattr_unsupported_mode_10);
+ qtest_add_func("virt/clic/prv_ms/intattr_prv_u_to_m_warl_nmbits_0",
+ test_configure_clicintattr_prv_u_to_m_warl);
+ qtest_add_func("virt/clic/prv_ms/intattr_prv_s_to_m_warl_nmbits_0",
+ test_configure_clicintattr_prv_s_to_m_warl);
+
+ /* unsupported nmbits */
+ qtest_add_func("virt/clic/prv_ms/cliccfg_unsupported_nmbits_2_ms",
+ test_configure_cliccfg_unsupported_nmbits_2);
+ qtest_add_func("virt/clic/prv_ms/cliccfg_unsupported_nmbits_3_ms",
+ test_configure_cliccfg_unsupported_nmbits_3);
+
+ /* clicintattr TRIG and SHV */
+ qtest_add_func("virt/clic/prv_ms/intattr_positive_edge_triggered",
+ test_configure_clicintattr_positive_edge_triggered);
+ qtest_add_func("virt/clic/prv_ms/clicintattr_negative_edge_triggered",
+ test_configure_clicintattr_negative_edge_triggered);
+ qtest_add_func("virt/clic/prv_ms/clicintattr_positive_level_triggered",
+ test_configure_clicintattr_positive_level_triggered);
+ qtest_add_func("virt/clic/prv_ms/clicintattr_negative_level_triggered",
+ test_configure_clicintattr_negative_level_triggered);
+ qtest_add_func("virt/clic/prv_ms/clicintattr_non_vectored",
+ test_configure_clicintattr_non_vectored);
+
+ /* Shut down QEMU */
+ qtest_add_func("virt/clic/prv_ms/shut_down_qemu_ms",
+ shut_down_qemu);
+}
+
+/* Test configuration in PRV_M + PRV_U mode */
+static void clic_configure_reg_mmio_test_case_mu(void)
+{
+ /* Start QEMU */
+ qtest_add_func("virt/clic/prv_mu/boot_qemu_mu",
+ boot_qemu_mu);
+
+ /* cliccfg configure cases */
+
+ /* mnlbits should be unaffected */
+ qtest_add_func("virt/clic/prv_mu/cliccfg_min_mnlbits_mu",
+ test_configure_cliccfg_min_mnlbits);
+ qtest_add_func("virt/clic/prv_mu/cliccfg_supported_max_mnlbits_mu",
+ test_configure_cliccfg_supported_max_mnlbits);
+ qtest_add_func("virt/clic/prv_mu/cliccfg_unsupported_mnlbits_mu",
+ test_configure_cliccfg_unsupported_mnlbits);
+ /* snlbits should not work */
+ qtest_add_func("virt/clic/prv_mu/cliccfg_snlbits_no_s_mu",
+ test_configure_cliccfg_snlbits_no_s);
+ /* unlbits should work*/
+ qtest_add_func("virt/clic/prv_mu/cliccfg_min_unlbits_u_mu",
+ test_configure_cliccfg_min_unlbits_u);
+ qtest_add_func("virt/clic/prv_mu/cliccfg_uupported_max_unlbits_u_mu",
+ test_configure_cliccfg_supported_max_unlbits_u);
+ qtest_add_func("virt/clic/prv_mu/cliccfg_unsupported_unlbits_u_mu",
+ test_configure_cliccfg_unsupported_unlbits_u);
+
+ /* clicintattr mode configure cases with nmbits = 1 */
+ qtest_add_func("virt/clic/prv_mu/cliccfg_nmbits_1_ms",
+ test_configure_cliccfg_nmbits_1);
+ qtest_add_func("virt/clic/prv_mu/intattr_prv_m_nmbits_1",
+ test_configure_clicintattr_prv_m);
+ qtest_add_func("virt/clic/prv_mu/intattr_unsupported_mode_10_nmbits_1",
+ test_configure_clicintattr_unsupported_mode_10);
+ qtest_add_func("virt/clic/prv_mu/intattr_prv_s_to_u_warl_nmbits_1_prv_m",
+ test_configure_clicintattr_prv_s_to_u_warl);
+ qtest_add_func("virt/clic/prv_mu/intattr_prv_u_supported_nmbits_1",
+ test_configure_clicintattr_prv_u_supported);
+ qtest_add_func("virt/clic/prv_mu/intattr_prv_s_to_u_warl_nmbits_1_prv_u",
+ test_configure_clicintattr_prv_s_to_u_warl);
+
+ /* clicintattr mode configure cases with nmbits = 0 - PRV_M only */
+ qtest_add_func("virt/clic/prv_mu/cliccfg_nmbits_0_ms",
+ test_configure_cliccfg_nmbits_0);
+ qtest_add_func("virt/clic/prv_mu/intattr_prv_m_nmbits_0",
+ test_configure_clicintattr_prv_m);
+ qtest_add_func("virt/clic/prv_mu/intattr_unsupported_mode_10_nmbits_0",
+ test_configure_clicintattr_unsupported_mode_10);
+ qtest_add_func("virt/clic/prv_mu/intattr_prv_u_to_m_warl_nmbits_0",
+ test_configure_clicintattr_prv_u_to_m_warl);
+ qtest_add_func("virt/clic/prv_mu/intattr_prv_s_to_m_warl_nmbits_0",
+ test_configure_clicintattr_prv_s_to_m_warl);
+
+ /* unsupported nmbits */
+ qtest_add_func("virt/clic/prv_mu/cliccfg_unsupported_nmbits_2_ms",
+ test_configure_cliccfg_unsupported_nmbits_2);
+ qtest_add_func("virt/clic/prv_mu/cliccfg_unsupported_nmbits_3_ms",
+ test_configure_cliccfg_unsupported_nmbits_3);
+
+ /* clicintattr TRIG and SHV */
+ qtest_add_func("virt/clic/prv_mu/intattr_positive_edge_triggered",
+ test_configure_clicintattr_positive_edge_triggered);
+ qtest_add_func("virt/clic/prv_mu/clicintattr_negative_edge_triggered",
+ test_configure_clicintattr_negative_edge_triggered);
+ qtest_add_func("virt/clic/prv_mu/clicintattr_positive_level_triggered",
+ test_configure_clicintattr_positive_level_triggered);
+ qtest_add_func("virt/clic/prv_mu/clicintattr_negative_level_triggered",
+ test_configure_clicintattr_negative_level_triggered);
+ qtest_add_func("virt/clic/prv_mu/clicintattr_non_vectored",
+ test_configure_clicintattr_non_vectored);
+
+ /* Shut down QEMU */
+ qtest_add_func("virt/clic/prv_mu/shut_down_qemu_mu",
+ shut_down_qemu);
+}
+
+/* Test configuration in PRV_M + PRV_S + PRV_U mode */
+static void clic_configure_reg_mmio_test_case_msu(void)
+{
+ /* Start QEMU */
+ qtest_add_func("virt/clic/prv_msu/boot_qemu_msu",
+ boot_qemu_msu);
+
+ /* cliccfg configure cases */
+
+ /* mnlbits should be unaffected */
+ qtest_add_func("virt/clic/prv_msu/cliccfg_min_mnlbits_msu",
+ test_configure_cliccfg_min_mnlbits);
+ qtest_add_func("virt/clic/prv_msu/cliccfg_supported_max_mnlbits_msu",
+ test_configure_cliccfg_supported_max_mnlbits);
+ qtest_add_func("virt/clic/prv_msu/cliccfg_unsupported_mnlbits_msu",
+ test_configure_cliccfg_unsupported_mnlbits);
+ /* snlbits should work*/
+ qtest_add_func("virt/clic/prv_msu/cliccfg_min_snlbits_s_msu",
+ test_configure_cliccfg_min_snlbits_s);
+ qtest_add_func("virt/clic/prv_msu/cliccfg_supported_max_snlbits_s_msu",
+ test_configure_cliccfg_supported_max_snlbits_s);
+ qtest_add_func("virt/clic/prv_msu/cliccfg_unsupported_snlbits_s_msu",
+ test_configure_cliccfg_unsupported_snlbits_s);
+ /* unlbits should work*/
+ qtest_add_func("virt/clic/prv_msu/cliccfg_min_unlbits_u_msu",
+ test_configure_cliccfg_min_unlbits_u);
+ qtest_add_func("virt/clic/prv_msu/cliccfg_uupported_max_unlbits_u_msu",
+ test_configure_cliccfg_supported_max_unlbits_u);
+ qtest_add_func("virt/clic/prv_msu/cliccfg_unsupported_unlbits_u_msu",
+ test_configure_cliccfg_unsupported_unlbits_u);
+ /* all bits should work */
+ qtest_add_func("virt/clic/prv_msu/cliccfg_xnlbits_msu",
+ test_configure_cliccfg_xnlbits);
+
+ /* clicintattr mode configure cases with nmbits = 2 => all modes */
+ qtest_add_func("virt/clic/prv_msu/cliccfg_nmbits_2_ms",
+ test_configure_cliccfg_nmbits_2);
+ qtest_add_func("virt/clic/prv_msu/intattr_prv_m_nmbits_2",
+ test_configure_clicintattr_prv_m);
+ qtest_add_func("virt/clic/prv_msu/intattr_unsupported_mode_10_nmbits_2",
+ test_configure_clicintattr_unsupported_mode_10);
+ qtest_add_func("virt/clic/prv_msu/intattr_prv_s_supported_nmbits_2",
+ test_configure_clicintattr_prv_s_supported);
+ qtest_add_func("virt/clic/prv_msu/intattr_prv_u_supported_nmbits_2",
+ test_configure_clicintattr_prv_u_supported);
+
+ /* clicintattr mode configure cases with nmbits = 1 => PRV_M and PRV_S */
+ qtest_add_func("virt/clic/prv_msu/cliccfg_nmbits_1_ms",
+ test_configure_cliccfg_nmbits_1);
+ qtest_add_func("virt/clic/prv_msu/intattr_prv_m_nmbits_1",
+ test_configure_clicintattr_prv_m);
+ qtest_add_func("virt/clic/prv_msu/intattr_unsupported_mode_10_nmbits_1",
+ test_configure_clicintattr_unsupported_mode_10);
+ qtest_add_func("virt/clic/prv_msu/intattr_prv_u_to_s_warl_nmbits_1_prv_m",
+ test_configure_clicintattr_prv_u_to_s_warl);
+ qtest_add_func("virt/clic/prv_msu/intattr_prv_s_supported_nmbits_1",
+ test_configure_clicintattr_prv_s_supported);
+ qtest_add_func("virt/clic/prv_msu/intattr_prv_u_to_s_warl_nmbits_1_prv_s",
+ test_configure_clicintattr_prv_u_to_s_warl);
+
+ /* clicintattr mode configure cases with nmbits = 0 - PRV_M only */
+ qtest_add_func("virt/clic/prv_msu/cliccfg_nmbits_0_ms",
+ test_configure_cliccfg_nmbits_0);
+ qtest_add_func("virt/clic/prv_msu/intattr_prv_m_nmbits_0",
+ test_configure_clicintattr_prv_m);
+ qtest_add_func("virt/clic/prv_msu/intattr_unsupported_mode_10_nmbits_0",
+ test_configure_clicintattr_unsupported_mode_10);
+ qtest_add_func("virt/clic/prv_msu/intattr_prv_u_to_m_warl_nmbits_0",
+ test_configure_clicintattr_prv_u_to_m_warl);
+ qtest_add_func("virt/clic/prv_msu/intattr_prv_s_to_m_warl_nmbits_0",
+ test_configure_clicintattr_prv_s_to_m_warl);
+
+ /* unsupported nmbits */
+ qtest_add_func("virt/clic/prv_msu/cliccfg_unsupported_nmbits_3_ms",
+ test_configure_cliccfg_unsupported_nmbits_3);
+
+ /* clicintattr TRIG and SHV */
+ qtest_add_func("virt/clic/prv_msu/intattr_positive_edge_triggered",
+ test_configure_clicintattr_positive_edge_triggered);
+ qtest_add_func("virt/clic/prv_msu/clicintattr_negative_edge_triggered",
+ test_configure_clicintattr_negative_edge_triggered);
+ qtest_add_func("virt/clic/prv_msu/clicintattr_positive_level_triggered",
+ test_configure_clicintattr_positive_level_triggered);
+ qtest_add_func("virt/clic/prv_msu/clicintattr_negative_level_triggered",
+ test_configure_clicintattr_negative_level_triggered);
+ qtest_add_func("virt/clic/prv_msu/clicintattr_non_vectored",
+ test_configure_clicintattr_non_vectored);
+
+ /* Shut down QEMU */
+ qtest_add_func("virt/clic/prv_msu/shut_down_qemu_msu",
+ shut_down_qemu);
+}
+
+#define GEN_INTCTL_TEST(_nbits) \
+GEN_BOOT_QEMU_INTCTL(_nbits) \
+static void clic_configure_clicintctl_test_case_##_nbits##_bits(void) \
+{ \
+ g_autofree char *prefix = g_strdup_printf("virt/clic/clicintl_%d_bits", \
+ _nbits); \
+ g_autofree char *path; \
+ \
+ path = g_strdup_printf("%s/boot_qemu", prefix); \
+ qtest_add_func(path, boot_qemu_##_nbits##_bits); \
+ \
+ path = g_strdup_printf("%s/intctl_0_%d_bits", prefix, _nbits); \
+ qtest_add_func(path, \
+ test_configure_clicintctl_set_0_##_nbits##_bits); \
+ path = g_strdup_printf("%s/intctl_33_%d_bits", prefix, _nbits); \
+ qtest_add_func(path, \
+ test_configure_clicintctl_set_33_##_nbits##_bits); \
+ path = g_strdup_printf("%s/intctl_88_%d_bits", prefix, _nbits); \
+ qtest_add_func(path, \
+ test_configure_clicintctl_set_88_##_nbits##_bits); \
+ path = g_strdup_printf("%s/intctl_128_%d_bits", prefix, _nbits); \
+ qtest_add_func(path, \
+ test_configure_clicintctl_set_128_##_nbits##_bits); \
+ path = g_strdup_printf("%s/intctl_204_%d_bits", prefix, _nbits); \
+ qtest_add_func(path, \
+ test_configure_clicintctl_set_204_##_nbits##_bits); \
+ path = g_strdup_printf("%s/intctl_240_%d_bits", prefix, _nbits); \
+ qtest_add_func(path, \
+ test_configure_clicintctl_set_240_##_nbits##_bits); \
+ \
+ path = g_strdup_printf("%s/shut_down_qemu", prefix); \
+ qtest_add_func(path, shut_down_qemu); \
+}
+
+GEN_INTCTL_TEST(0)
+GEN_INTCTL_TEST(1)
+GEN_INTCTL_TEST(2)
+GEN_INTCTL_TEST(3)
+GEN_INTCTL_TEST(4)
+GEN_INTCTL_TEST(5)
+GEN_INTCTL_TEST(6)
+GEN_INTCTL_TEST(7)
+GEN_INTCTL_TEST(8)
+
+static void clic_irq_test_case(void)
+{
+ /* interrupt test case */
+ qtest_add_func("virt/clic/vectored_positive_level_triggered_interrupt",
+ test_vectored_positive_level_triggered_interrupt);
+ qtest_add_func("virt/clic/vectored_negative_level_triggered_interrupt",
+ test_vectored_negative_level_triggered_interrupt);
+ qtest_add_func("virt/clic/vectored_positive_edge_triggered_interrupt",
+ test_vectored_positive_edge_triggered_interrupt);
+ qtest_add_func("virt/clic/vectored_negative_edge_triggered_interrupt",
+ test_vectored_negative_edge_triggered_interrupt);
+ qtest_add_func("virt/clic/unvectored_positive_level_triggered_interrupt",
+ test_unvectored_positive_level_triggered_interrupt);
+ qtest_add_func("virt/clic/unvectored_negative_level_triggered_interrupt",
+ test_unvectored_negative_level_triggered_interrupt);
+ qtest_add_func("virt/clic/unvectored_positive_edge_triggered_interrupt",
+ test_unvectored_positive_edge_triggered_interrupt);
+ qtest_add_func("virt/clic/unvectored_negative_edge_triggered_interrupt",
+ test_unvectored_negative_edge_triggered_interrupt);
+}
+
+static void clic_mode_access_test_case(void)
+{
+ qtest_add_func("virt/clic/test_prv_s_access", test_prv_s_access);
+ qtest_add_func("virt/clic/test_prv_u_access", test_prv_u_access);
+ qtest_add_func("virt/clic/test_prv_su_access", test_prv_su_access);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ g_test_set_nonfatal_assertions();
+
+ /* test cases */
+ clic_configure_reg_mmio_test_case_m();
+ clic_configure_reg_mmio_test_case_ms();
+ clic_configure_reg_mmio_test_case_mu();
+ clic_configure_reg_mmio_test_case_msu();
+ clic_configure_clicintctl_test_case_0_bits();
+ clic_configure_clicintctl_test_case_1_bits();
+ clic_configure_clicintctl_test_case_2_bits();
+ clic_configure_clicintctl_test_case_3_bits();
+ clic_configure_clicintctl_test_case_4_bits();
+ clic_configure_clicintctl_test_case_5_bits();
+ clic_configure_clicintctl_test_case_6_bits();
+ clic_configure_clicintctl_test_case_7_bits();
+ clic_configure_clicintctl_test_case_8_bits();
+ clic_irq_test_case();
+ clic_mode_access_test_case();
+
+ /* Run the tests */
+ int ret = g_test_run();
+
+ return ret;
+}
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
^ permalink raw reply related [flat|nested] 32+ messages in thread
* RE: [PATCH 00/11 v2] RISC-V: support CLIC v0.9 specification
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
` (10 preceding siblings ...)
2024-08-19 16:02 ` [PATCH 11/11 v2] tests: add riscv clic qtest case and a function in qtest Ian Brockbank
@ 2024-09-04 7:57 ` Ian Brockbank
2024-09-06 2:48 ` Alistair Francis
2024-09-06 3:56 ` Alistair Francis
13 siblings, 0 replies; 32+ messages in thread
From: Ian Brockbank @ 2024-09-04 7:57 UTC (permalink / raw)
To: Ian Brockbank, qemu-devel@nongnu.org, qemu-riscv@nongnu.org
Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, dan.smathers@seagate.com,
jefro@linuxfoundation.org
Hi All,
Has anyone had time to look at this patch set yet? Is there anything I can do to help?
This implements https://wiki.riscv.org/display/HOME/Fast+Interrupts+TG, bringing the draft implementation listed on that page (almost) up to date with v0.9-draft-20240314. The implementation has been verified to match the Cirrus Logic silicon (PRV_M only), which is based upon the Pulp implementation [4] as of June 2023.
How can I help move this forward to get it merged into the mainline?
Thank you,
Ian Brockbank C.Eng.
Senior Staff Software Engineer
Cirrus Logic | cirrus.com | t: +44 131 272 7145 | m: +44 7554 008061 |@badgertaming
> -----Original Message-----
> From: Ian Brockbank <Ian.Brockbank@cirrus.com>
> Sent: Monday, August 19, 2024 5:02 PM
> To: qemu-devel@nongnu.org; qemu-riscv@nongnu.org
> Cc: Palmer Dabbelt <palmer@dabbelt.com>; Alistair Francis
> <alistair.francis@wdc.com>; Bin Meng <bmeng.cn@gmail.com>; Weiwei Li
> <liwei1518@gmail.com>; Daniel Henrique Barboza
> <dbarboza@ventanamicro.com>; Liu Zhiwei <zhiwei_liu@linux.alibaba.com>;
> Ian Brockbank <Ian.Brockbank@cirrus.com>
> Subject: [PATCH 00/11 v2] RISC-V: support CLIC v0.9 specification
>
> [Resubmission now the merge is correct]
>
> This patch set gives an implementation of "RISC-V Core-Local Interrupt
> Controller(CLIC) Version 0.9-draft-20240314". It comes from [1], where
> you can find the pdf format or the source code.
>
> This is based on the implementation from 2021 by Liu Zhiwei [3], who took
> over the job from Michael Clark, who gave the first implementation of
> clic-v0.7 specification [2]. I believe this implementation addresses all
> the comments in Liu Zhiwei's RFC patch thread.
>
> This implementation follows the CLIC 0.9-stable draft at 14 March 2024,
> with the following exceptions and implementation details:
> - the CLIC control registers are memory-mapped as per earlier drafts (in
> particular version 0.9-draft, 20 June 2023)
> - the indirect CSR control in 0.9-stable is not implemented
> - the vector table can be either handler addresses (as per the spec)
> or a jump table where each entry is processed as an instruction,
> selectable with version number v0.9-jmp
> - each hart is assigned its own CLIC block
> - if PRV_S and/or PRV_M are supported, they are currently assumed to
> follow
> the PRV_M registers; a subsequent update will address this
> - support for PRV_S and PRV_M is selectable at CLIC instantiation
> - PRV_S and PRV_U registers are currently separate from PRV_M; a
> subsequent
> update will turn them into filtered views onto the PRV_M registers
> - each hart is assigned its own CLIC block
> - support for PRV_S and PRV_M is selectable at CLIC instantiation by
> passing in a base address for the given modes; a base address of 0 is
> treated as not supported
> - PRV_S and PRV_U registers are mapped onto the PRV_M controls with
> appropriate filtering for the access mode
> - the RISCV virt machine has been updated to allow CLIC emulation by
> passing "machine=virt,clic=on" on the command line; various other
> parameters have been added to allow finer control of the CLIC behavior
>
> The implementation (in jump-table mode) has been verified to match the
> Cirrus Logic silicon (PRV_M only), which is based upon the Pulp
> implementation [4] as of June 2023.
>
> The implementation also includes a selection of qtests designed to verify
> operation in all possible combinations of PRV_M, PRV_S and PRV_U.
>
> [1] specification website: https://github.com/riscv/riscv-fast-interrupt.
> [2] Michael Clark origin work:
> https://github.com/sifive/riscv-qemu/tree/sifive-clic.
> [3] RFC Patch submission by Liu Zhiwei:
> https://lists.gnu.org/archive/html/qemu-devel/2021-04/msg01417.html
> [4] Pulp implementation of CLIC: https://github.com/pulp-platform/clic
>
> Ian Brockbank (11):
> target/riscv: Add CLIC CSR mintstatus
> target/riscv: Update CSR xintthresh in CLIC mode
> hw/intc: Add CLIC device
> target/riscv: Update CSR xie in CLIC mode
> target/riscv: Update CSR xip in CLIC mode
> target/riscv: Update CSR xtvec in CLIC mode
> target/riscv: Update CSR xnxti in CLIC mode
> target/riscv: Update interrupt handling in CLIC mode
> target/riscv: Update interrupt return in CLIC mode
> hw/riscv: add CLIC into virt machine
> tests: add riscv clic qtest case and a function in qtest
This message may contain privileged and/or confidential information. If it appears you received this message in error, please notify us by reply email and then delete the message. Thank you.
Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 01/11 v2] target/riscv: Add CLIC CSR mintstatus
2024-08-19 16:02 ` [PATCH 01/11 v2] target/riscv: Add CLIC CSR mintstatus Ian Brockbank
@ 2024-09-06 2:44 ` Alistair Francis
2024-09-06 3:04 ` Alistair Francis
0 siblings, 1 reply; 32+ messages in thread
From: Alistair Francis @ 2024-09-06 2:44 UTC (permalink / raw)
To: Ian Brockbank
Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
Bin Meng, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei,
LIU Zhiwei
On Tue, Aug 20, 2024 at 2:11 AM Ian Brockbank <Ian.Brockbank@cirrus.com> wrote:
>
> From: Ian Brockbank <ian.brockbank@cirrus.com>
>
> CSR mintstatus holds the active interrupt level for each supported
> privilege mode. sintstatus, and user, uintstatus, provide restricted
> views of mintstatus.
>
> Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
> Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
> ---
> target/riscv/cpu.h | 3 +++
> target/riscv/cpu_bits.h | 11 +++++++++++
> target/riscv/csr.c | 31 +++++++++++++++++++++++++++++++
> 3 files changed, 45 insertions(+)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 1619c3acb6..95303f50d3 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -259,6 +259,7 @@ struct CPUArchState {
> bool software_seip;
>
> uint64_t miclaim;
> + uint64_t mintstatus; /* clic-spec */
>
> uint64_t mie;
> uint64_t mideleg;
> @@ -461,6 +462,8 @@ struct CPUArchState {
> QEMUTimer *vstimer; /* Internal timer for VS-mode interrupt */
> bool vstime_irq;
>
> + void *clic; /* clic interrupt controller */
> +
> hwaddr kernel_addr;
> hwaddr fdt_addr;
>
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index 32b068f18a..2e65495b54 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -165,6 +165,7 @@
> #define CSR_MCAUSE 0x342
> #define CSR_MTVAL 0x343
> #define CSR_MIP 0x344
> +#define CSR_MINTSTATUS 0xfb1 /* clic-spec-draft */
>
> /* Machine-Level Window to Indirectly Accessed Registers (AIA) */
> #define CSR_MISELECT 0x350
> @@ -206,6 +207,7 @@
> #define CSR_SCAUSE 0x142
> #define CSR_STVAL 0x143
> #define CSR_SIP 0x144
> +#define CSR_SINTSTATUS 0xdb1 /* clic-spec-draft */
>
> /* Sstc supervisor CSRs */
> #define CSR_STIMECMP 0x14D
> @@ -733,6 +735,15 @@ typedef enum RISCVException {
> #define SIP_SEIP MIP_SEIP
> #define SIP_LCOFIP MIP_LCOFIP
>
> +/* mintstatus */
> +#define MINTSTATUS_MIL 0xff000000 /* mil[31:24] */
> +#define MINTSTATUS_SIL 0x0000ff00 /* sil[15:8] */
> +#define MINTSTATUS_UIL 0x000000ff /* uil[7:0] */
> +
> +/* sintstatus */
> +#define SINTSTATUS_SIL 0x0000ff00 /* sil[15:8] */
> +#define SINTSTATUS_UIL 0x000000ff /* uil[7:0] */
> +
> /* MIE masks */
> #define MIE_SEIE (1 << IRQ_S_EXT)
> #define MIE_UEIE (1 << IRQ_U_EXT)
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index ea3560342c..f9ed7b9079 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -578,6 +578,16 @@ static RISCVException debug(CPURISCVState *env, int csrno)
>
> return RISCV_EXCP_ILLEGAL_INST;
> }
> +
> +static int clic(CPURISCVState *env, int csrno)
> +{
> + if (env->clic) {
> + return RISCV_EXCP_NONE;
> + }
> +
> + return RISCV_EXCP_ILLEGAL_INST;
> +}
> +
> #endif
>
> static RISCVException seed(CPURISCVState *env, int csrno)
> @@ -2887,6 +2897,12 @@ static RISCVException rmw_mviph(CPURISCVState *env, int csrno,
> return ret;
> }
>
> +static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> + *val = env->mintstatus;
> + return RISCV_EXCP_NONE;
> +}
> +
> /* Supervisor Trap Setup */
> static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno,
> Int128 *val)
> @@ -3298,6 +3314,14 @@ static RISCVException rmw_siph(CPURISCVState *env, int csrno,
> return ret;
> }
>
> +static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> + /* sintstatus is a filtered view of mintstatus with the PRV_M removed */
> + target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
> + *val = env->mintstatus & mask;
> + return RISCV_EXCP_NONE;
> +}
> +
> /* Supervisor Protection and Translation */
> static RISCVException read_satp(CPURISCVState *env, int csrno,
> target_ulong *val)
> @@ -5594,6 +5618,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
> write_mhpmcounterh },
> [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", mctr32, read_hpmcounterh,
> write_mhpmcounterh },
> +
> + /* Machine Mode Core Level Interrupt Controller */
> + [CSR_MINTSTATUS] = { "mintstatus", clic, read_mintstatus },
> +
> + /* Supervisor Mode Core Level Interrupt Controller */
> + [CSR_SINTSTATUS] = { "sintstatus", clic, read_sintstatus },
> +
> [CSR_SCOUNTOVF] = { "scountovf", sscofpmf, read_scountovf,
> .min_priv_ver = PRIV_VERSION_1_12_0 },
>
> --
> 2.46.0.windows.1
> This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
>
It looks like some patch mangling is going on here
Alistair
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 00/11 v2] RISC-V: support CLIC v0.9 specification
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
` (11 preceding siblings ...)
2024-09-04 7:57 ` [PATCH 00/11 v2] RISC-V: support CLIC v0.9 specification Ian Brockbank
@ 2024-09-06 2:48 ` Alistair Francis
2024-09-06 3:56 ` Alistair Francis
13 siblings, 0 replies; 32+ messages in thread
From: Alistair Francis @ 2024-09-06 2:48 UTC (permalink / raw)
To: Ian Brockbank
Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
Bin Meng, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei
On Tue, Aug 20, 2024 at 2:08 AM Ian Brockbank <Ian.Brockbank@cirrus.com> wrote:
>
> [Resubmission now the merge is correct]
>
> This patch set gives an implementation of "RISC-V Core-Local Interrupt
> Controller(CLIC) Version 0.9-draft-20210217". It comes from [1], where
> you can find the pdf format or the source code.
Can you please point to the *exact* spec that is used (see below)
>
> This is based on the implementation from 2021 by Liu Zhiwei [3], who took
> over the job from Michael Clark, who gave the first implementation of
> clic-v0.7 specification [2]. I believe this implementation addresses all
> the comments in Liu Zhiwei's RFC patch thread.
>
> This implementation follows the CLIC 0.9-stable draft at 14 March 2024,
> with the following exceptions and implementation details:
I don't follow, the 0.9 spec was released in June
https://github.com/riscv/riscv-fast-interrupt/releases/tag/v0.9
> - the CLIC control registers are memory-mapped as per earlier drafts (in
> particular version 0.9-draft, 20 June 2023)
So it's different to the 0.9 spec?
> - the indirect CSR control in 0.9-stable is not implemented
> - the vector table can be either handler addresses (as per the spec)
> or a jump table where each entry is processed as an instruction,
> selectable with version number v0.9-jmp
We only want to support what is in the spec
Alistair
> - each hart is assigned its own CLIC block
> - if PRV_S and/or PRV_M are supported, they are currently assumed to follow
> the PRV_M registers; a subsequent update will address this
> - support for PRV_S and PRV_M is selectable at CLIC instantiation
> - PRV_S and PRV_U registers are currently separate from PRV_M; a subsequent
> update will turn them into filtered views onto the PRV_M registers
> - each hart is assigned its own CLIC block
> - support for PRV_S and PRV_M is selectable at CLIC instantiation by
> passing in a base address for the given modes; a base address of 0 is
> treated as not supported
> - PRV_S and PRV_U registers are mapped onto the PRV_M controls with
> appropriate filtering for the access mode
> - the RISCV virt machine has been updated to allow CLIC emulation by
> passing "machine=virt,clic=on" on the command line; various other
> parameters have been added to allow finer control of the CLIC behavior
>
> The implementation (in jump-table mode) has been verified to match the
> Cirrus Logic silicon (PRV_M only), which is based upon the Pulp
> implementation [4] as of June 2023.
>
> The implementation also includes a selection of qtests designed to verify
> operation in all possible combinations of PRV_M, PRV_S and PRV_U.
>
> [1] specification website: https://github.com/riscv/riscv-fast-interrupt.
> [2] Michael Clark origin work:
> https://github.com/sifive/riscv-qemu/tree/sifive-clic.
> [3] RFC Patch submission by Liu Zhiwei:
> https://lists.gnu.org/archive/html/qemu-devel/2021-04/msg01417.html
> [4] Pulp implementation of CLIC: https://github.com/pulp-platform/clic
>
> Ian Brockbank (11):
> target/riscv: Add CLIC CSR mintstatus
> target/riscv: Update CSR xintthresh in CLIC mode
> hw/intc: Add CLIC device
> target/riscv: Update CSR xie in CLIC mode
> target/riscv: Update CSR xip in CLIC mode
> target/riscv: Update CSR xtvec in CLIC mode
> target/riscv: Update CSR xnxti in CLIC mode
> target/riscv: Update interrupt handling in CLIC mode
> target/riscv: Update interrupt return in CLIC mode
> hw/riscv: add CLIC into virt machine
> tests: add riscv clic qtest case and a function in qtest
>
> This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 02/11 v2] target/riscv: Update CSR xintthresh in CLIC mode
2024-08-19 16:02 ` [PATCH 02/11 v2] target/riscv: Update CSR xintthresh in CLIC mode Ian Brockbank
@ 2024-09-06 2:52 ` Alistair Francis
0 siblings, 0 replies; 32+ messages in thread
From: Alistair Francis @ 2024-09-06 2:52 UTC (permalink / raw)
To: Ian Brockbank
Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
Bin Meng, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei,
LIU Zhiwei
On Tue, Aug 20, 2024 at 2:11 AM Ian Brockbank <Ian.Brockbank@cirrus.com> wrote:
>
> From: Ian Brockbank <ian.brockbank@cirrus.com>
>
> The interrupt-level threshold (xintthresh) CSR holds an 8-bit field
> for the threshold level of the associated privilege mode.
>
> For horizontal interrupts, only the ones with higher interrupt levels
> than the threshold level are allowed to preempt.
>
> Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
> Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
> ---
> target/riscv/cpu.h | 2 ++
> target/riscv/cpu_bits.h | 2 ++
> target/riscv/csr.c | 28 ++++++++++++++++++++++++++++
> 3 files changed, 32 insertions(+)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 95303f50d3..9b5f36ad0a 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -260,6 +260,7 @@ struct CPUArchState {
>
> uint64_t miclaim;
> uint64_t mintstatus; /* clic-spec */
> + target_ulong mintthresh; /* clic-spec */
>
> uint64_t mie;
> uint64_t mideleg;
> @@ -283,6 +284,7 @@ struct CPUArchState {
> target_ulong stvec;
> target_ulong sepc;
> target_ulong scause;
> + target_ulong sintthresh; /* clic-spec */
>
> target_ulong mtvec;
> target_ulong mepc;
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index 2e65495b54..ad45402370 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -166,6 +166,7 @@
> #define CSR_MTVAL 0x343
> #define CSR_MIP 0x344
> #define CSR_MINTSTATUS 0xfb1 /* clic-spec-draft */
> +#define CSR_MINTTHRESH 0x347 /* clic-spec-draft */
>
> /* Machine-Level Window to Indirectly Accessed Registers (AIA) */
> #define CSR_MISELECT 0x350
> @@ -208,6 +209,7 @@
> #define CSR_STVAL 0x143
> #define CSR_SIP 0x144
> #define CSR_SINTSTATUS 0xdb1 /* clic-spec-draft */
> +#define CSR_SINTTHRESH 0x147 /* clic-spec-draft */
>
> /* Sstc supervisor CSRs */
> #define CSR_STIMECMP 0x14D
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index f9ed7b9079..9c824c0d8f 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -2903,6 +2903,18 @@ static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
> return RISCV_EXCP_NONE;
> }
>
> +static int read_mintthresh(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> + *val = env->mintthresh;
> + return 0;
> +}
> +
> +static int write_mintthresh(CPURISCVState *env, int csrno, target_ulong val)
> +{
> + env->mintthresh = val;
We should be clearing the upper bits on a write
Alistair
> + return 0;
> +}
> +
> /* Supervisor Trap Setup */
> static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno,
> Int128 *val)
> @@ -3322,6 +3334,18 @@ static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
> return RISCV_EXCP_NONE;
> }
>
> +static int read_sintthresh(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> + *val = env->sintthresh;
> + return 0;
> +}
> +
> +static int write_sintthresh(CPURISCVState *env, int csrno, target_ulong val)
> +{
> + env->sintthresh = val;
> + return 0;
> +}
> +
> /* Supervisor Protection and Translation */
> static RISCVException read_satp(CPURISCVState *env, int csrno,
> target_ulong *val)
> @@ -5621,9 +5645,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>
> /* Machine Mode Core Level Interrupt Controller */
> [CSR_MINTSTATUS] = { "mintstatus", clic, read_mintstatus },
> + [CSR_MINTTHRESH] = { "mintthresh", clic, read_mintthresh,
> + write_mintthresh },
>
> /* Supervisor Mode Core Level Interrupt Controller */
> [CSR_SINTSTATUS] = { "sintstatus", clic, read_sintstatus },
> + [CSR_SINTTHRESH] = { "sintthresh", clic, read_sintthresh,
> + write_sintthresh },
>
> [CSR_SCOUNTOVF] = { "scountovf", sscofpmf, read_scountovf,
> .min_priv_ver = PRIV_VERSION_1_12_0 },
> --
> 2.46.0.windows.1
> This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 04/11 v2] target/riscv: Update CSR xie in CLIC mode
2024-08-19 16:02 ` [PATCH 04/11 v2] target/riscv: Update CSR xie in CLIC mode Ian Brockbank
@ 2024-09-06 2:58 ` Alistair Francis
2024-09-06 3:20 ` Alistair Francis
0 siblings, 1 reply; 32+ messages in thread
From: Alistair Francis @ 2024-09-06 2:58 UTC (permalink / raw)
To: Ian Brockbank
Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
Bin Meng, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei,
LIU Zhiwei
On Tue, Aug 20, 2024 at 2:15 AM Ian Brockbank <Ian.Brockbank@cirrus.com> wrote:
>
> From: Ian Brockbank <ian.brockbank@cirrus.com>
>
> The xie CSR appears hardwired to zero in CLIC mode, replaced by separate
> memory-mapped interrupt enables (clicintie[i]). Writes to xie will be
> ignored and will not trap (i.e., no access faults).
>
> Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
> Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
> ---
> target/riscv/csr.c | 34 ++++++++++++++++++++++------------
> 1 file changed, 22 insertions(+), 12 deletions(-)
>
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index 9c824c0d8f..a5978e0929 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -30,6 +30,10 @@
> #include "qemu/guest-random.h"
> #include "qapi/error.h"
>
> +#if !defined(CONFIG_USER_ONLY)
> +#include "hw/intc/riscv_clic.h"
> +#endif
This doesn't seem like the way to go
> +
> /* CSR function table public API */
> void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops)
> {
> @@ -1805,16 +1809,19 @@ static RISCVException rmw_mie64(CPURISCVState *env, int csrno,
> uint64_t *ret_val,
> uint64_t new_val, uint64_t wr_mask)
> {
> - uint64_t mask = wr_mask & all_ints;
> + /* Access to xie will be ignored in CLIC mode and will not trap. */
> + if (!riscv_clic_is_clic_mode(env)) {
We can just implement this check, it's only two lines
> + uint64_t mask = wr_mask & all_ints;
>
> - if (ret_val) {
> - *ret_val = env->mie;
> - }
> + if (ret_val) {
> + *ret_val = env->mie;
> + }
>
> - env->mie = (env->mie & ~mask) | (new_val & mask);
> + env->mie = (env->mie & ~mask) | (new_val & mask);
>
> - if (!riscv_has_ext(env, RVH)) {
> - env->mie &= ~((uint64_t)HS_MODE_INTERRUPTS);
> + if (!riscv_has_ext(env, RVH)) {
> + env->mie &= ~((uint64_t)HS_MODE_INTERRUPTS);
> + }
> }
>
> return RISCV_EXCP_NONE;
> @@ -2906,13 +2913,13 @@ static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
> static int read_mintthresh(CPURISCVState *env, int csrno, target_ulong *val)
> {
> *val = env->mintthresh;
> - return 0;
> + return RISCV_EXCP_NONE;
This change should be made when these functions are added
Alistair
> }
>
> static int write_mintthresh(CPURISCVState *env, int csrno, target_ulong val)
> {
> env->mintthresh = val;
> - return 0;
> + return RISCV_EXCP_NONE;
> }
>
> /* Supervisor Trap Setup */
> @@ -3059,7 +3066,10 @@ static RISCVException rmw_sie64(CPURISCVState *env, int csrno,
> *ret_val |= env->sie & nalias_mask;
> }
>
> - env->sie = (env->sie & ~sie_mask) | (new_val & sie_mask);
> + /* Writes to xie will be ignored in CLIC mode and will not trap. */
> + if (!riscv_clic_is_clic_mode(env)) {
> + env->sie = (env->sie & ~sie_mask) | (new_val & sie_mask);
> + }
> }
>
> return ret;
> @@ -3337,13 +3347,13 @@ static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
> static int read_sintthresh(CPURISCVState *env, int csrno, target_ulong *val)
> {
> *val = env->sintthresh;
> - return 0;
> + return RISCV_EXCP_NONE;
> }
>
> static int write_sintthresh(CPURISCVState *env, int csrno, target_ulong val)
> {
> env->sintthresh = val;
> - return 0;
> + return RISCV_EXCP_NONE;
> }
>
> /* Supervisor Protection and Translation */
> --
> 2.46.0.windows.1
> This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 06/11 v2] target/riscv: Update CSR xtvec in CLIC mode
2024-08-19 16:02 ` [PATCH 06/11 v2] target/riscv: Update CSR xtvec " Ian Brockbank
@ 2024-09-06 3:02 ` Alistair Francis
0 siblings, 0 replies; 32+ messages in thread
From: Alistair Francis @ 2024-09-06 3:02 UTC (permalink / raw)
To: Ian Brockbank
Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
Bin Meng, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei,
LIU Zhiwei
On Tue, Aug 20, 2024 at 2:15 AM Ian Brockbank <Ian.Brockbank@cirrus.com> wrote:
>
> From: Ian Brockbank <ian.brockbank@cirrus.com>
>
> The new CLIC interrupt-handling mode is encoded as a new state in the
> existing WARL xtvec register, where the low two bits of are 11.
>
> Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
> Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
> ---
> target/riscv/cpu.h | 2 ++
> target/riscv/cpu_bits.h | 2 ++
> target/riscv/csr.c | 63 ++++++++++++++++++++++++++++++++++++++---
> 3 files changed, 63 insertions(+), 4 deletions(-)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 12aa8cf6b1..05a014db03 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -283,11 +283,13 @@ struct CPUArchState {
> target_ulong medeleg;
>
> target_ulong stvec;
> + target_ulong stvt; /* clic-spec */
> target_ulong sepc;
> target_ulong scause;
> target_ulong sintthresh; /* clic-spec */
>
> target_ulong mtvec;
> + target_ulong mtvt; /* clic-spec */
> target_ulong mepc;
> target_ulong mcause;
> target_ulong mtval; /* since: priv-1.10.0 */
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index 0ed44ec0a8..279a6f889b 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -153,6 +153,7 @@
> #define CSR_MIE 0x304
> #define CSR_MTVEC 0x305
> #define CSR_MCOUNTEREN 0x306
> +#define CSR_MTVT 0x307 /* clic-spec-draft */
>
> /* 32-bit only */
> #define CSR_MSTATUSH 0x310
> @@ -192,6 +193,7 @@
> #define CSR_SIE 0x104
> #define CSR_STVEC 0x105
> #define CSR_SCOUNTEREN 0x106
> +#define CSR_STVT 0x107 /* clic-spec-draft */
>
> /* Supervisor Configuration CSRs */
> #define CSR_SENVCFG 0x10A
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index 276ef7856e..be0071fd25 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -2170,9 +2170,23 @@ static RISCVException read_mtvec(CPURISCVState *env, int csrno,
> static RISCVException write_mtvec(CPURISCVState *env, int csrno,
> target_ulong val)
> {
> - /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
> - if ((val & 3) < 2) {
> + /*
> + * bits [1:0] encode mode; 0 = direct, 1 = vectored, 3 = CLIC,
> + * others reserved
You aren't checking if the CLIC extension (smclic) is enabled though.
You need to guard these changes with a smclic extension check
Alistair
> + */
> + target_ulong mode = get_field(val, XTVEC_MODE);
> + target_ulong fullmode = val & XTVEC_FULL_MODE;
> + if (mode <= XTVEC_CLINT_VECTORED) {
> env->mtvec = val;
> + } else if (XTVEC_CLIC == fullmode && env->clic) {
> + /*
> + * CLIC mode hardwires xtvec bits 2-5 to zero.
> + * Layout:
> + * XLEN-1:6 base (WARL)
> + * 5:2 submode (WARL) - 0000 for CLIC
> + * 1:0 mode (WARL) - 11 for CLIC
> + */
> + env->mtvec = (val & XTVEC_NBASE) | XTVEC_CLIC;
> } else {
> qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: reserved mode not supported\n");
> }
> @@ -2271,6 +2285,18 @@ static RISCVException write_mcounteren(CPURISCVState *env, int csrno,
> return RISCV_EXCP_NONE;
> }
>
> +static int read_mtvt(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> + *val = env->mtvt;
> + return RISCV_EXCP_NONE;
> +}
> +
> +static int write_mtvt(CPURISCVState *env, int csrno, target_ulong val)
> +{
> + env->mtvt = val & XTVEC_NBASE;
> + return RISCV_EXCP_NONE;
> +}
> +
> /* Machine Trap Handling */
> static RISCVException read_mscratch_i128(CPURISCVState *env, int csrno,
> Int128 *val)
> @@ -3122,9 +3148,24 @@ static RISCVException read_stvec(CPURISCVState *env, int csrno,
> static RISCVException write_stvec(CPURISCVState *env, int csrno,
> target_ulong val)
> {
> - /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
> - if ((val & 3) < 2) {
> + /*
> + * bits [1:0] encode mode; 0 = direct, 1 = vectored, 3 = CLIC,
> + * others reserved
> + */
> + target_ulong mode = val & XTVEC_MODE;
> + target_ulong fullmode = val & XTVEC_FULL_MODE;
> + if (mode <= XTVEC_CLINT_VECTORED) {
> env->stvec = val;
> + } else if (XTVEC_CLIC == fullmode && env->clic) {
> + /*
> + * If only CLIC mode is supported, writes to bit 1 are also ignored and
> + * it is always set to one. CLIC mode hardwires xtvec bits 2-5 to zero.
> + * Layout:
> + * XLEN-1:6 base (WARL)
> + * 5:2 submode (WARL) - 0000 for CLIC
> + * 1:0 mode (WARL) - 11 for CLIC
> + */
> + env->stvec = (val & XTVEC_NBASE) | XTVEC_CLIC;
> } else {
> qemu_log_mask(LOG_UNIMP, "CSR_STVEC: reserved mode not supported\n");
> }
> @@ -3149,6 +3190,18 @@ static RISCVException write_scounteren(CPURISCVState *env, int csrno,
> return RISCV_EXCP_NONE;
> }
>
> +static int read_stvt(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> + *val = env->stvt;
> + return RISCV_EXCP_NONE;
> +}
> +
> +static int write_stvt(CPURISCVState *env, int csrno, target_ulong val)
> +{
> + env->stvt = val & XTVEC_NBASE;
> + return RISCV_EXCP_NONE;
> +}
> +
> /* Supervisor Trap Handling */
> static RISCVException read_sscratch_i128(CPURISCVState *env, int csrno,
> Int128 *val)
> @@ -5666,11 +5719,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
> write_mhpmcounterh },
>
> /* Machine Mode Core Level Interrupt Controller */
> + [CSR_MTVT] = { "mtvt", clic, read_mtvt, write_mtvt },
> [CSR_MINTSTATUS] = { "mintstatus", clic, read_mintstatus },
> [CSR_MINTTHRESH] = { "mintthresh", clic, read_mintthresh,
> write_mintthresh },
>
> /* Supervisor Mode Core Level Interrupt Controller */
> + [CSR_STVT] = { "stvt", clic, read_stvt, write_stvt },
> [CSR_SINTSTATUS] = { "sintstatus", clic, read_sintstatus },
> [CSR_SINTTHRESH] = { "sintthresh", clic, read_sintthresh,
> write_sintthresh },
> --
> 2.46.0.windows.1
> This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 01/11 v2] target/riscv: Add CLIC CSR mintstatus
2024-09-06 2:44 ` Alistair Francis
@ 2024-09-06 3:04 ` Alistair Francis
0 siblings, 0 replies; 32+ messages in thread
From: Alistair Francis @ 2024-09-06 3:04 UTC (permalink / raw)
To: Ian Brockbank
Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
Bin Meng, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei,
LIU Zhiwei
On Fri, Sep 6, 2024 at 12:44 PM Alistair Francis <alistair23@gmail.com> wrote:
>
> On Tue, Aug 20, 2024 at 2:11 AM Ian Brockbank <Ian.Brockbank@cirrus.com> wrote:
> >
> > From: Ian Brockbank <ian.brockbank@cirrus.com>
> >
> > CSR mintstatus holds the active interrupt level for each supported
> > privilege mode. sintstatus, and user, uintstatus, provide restricted
> > views of mintstatus.
> >
> > Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
> > Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
>
> Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Whoops! Scratch that
>
> > ---
> > target/riscv/cpu.h | 3 +++
> > target/riscv/cpu_bits.h | 11 +++++++++++
> > target/riscv/csr.c | 31 +++++++++++++++++++++++++++++++
> > 3 files changed, 45 insertions(+)
> >
> > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> > index 1619c3acb6..95303f50d3 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -259,6 +259,7 @@ struct CPUArchState {
> > bool software_seip;
> >
> > uint64_t miclaim;
> > + uint64_t mintstatus; /* clic-spec */
> >
> > uint64_t mie;
> > uint64_t mideleg;
> > @@ -461,6 +462,8 @@ struct CPUArchState {
> > QEMUTimer *vstimer; /* Internal timer for VS-mode interrupt */
> > bool vstime_irq;
> >
> > + void *clic; /* clic interrupt controller */
> > +
> > hwaddr kernel_addr;
> > hwaddr fdt_addr;
> >
> > diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> > index 32b068f18a..2e65495b54 100644
> > --- a/target/riscv/cpu_bits.h
> > +++ b/target/riscv/cpu_bits.h
> > @@ -165,6 +165,7 @@
> > #define CSR_MCAUSE 0x342
> > #define CSR_MTVAL 0x343
> > #define CSR_MIP 0x344
> > +#define CSR_MINTSTATUS 0xfb1 /* clic-spec-draft */
> >
> > /* Machine-Level Window to Indirectly Accessed Registers (AIA) */
> > #define CSR_MISELECT 0x350
> > @@ -206,6 +207,7 @@
> > #define CSR_SCAUSE 0x142
> > #define CSR_STVAL 0x143
> > #define CSR_SIP 0x144
> > +#define CSR_SINTSTATUS 0xdb1 /* clic-spec-draft */
> >
> > /* Sstc supervisor CSRs */
> > #define CSR_STIMECMP 0x14D
> > @@ -733,6 +735,15 @@ typedef enum RISCVException {
> > #define SIP_SEIP MIP_SEIP
> > #define SIP_LCOFIP MIP_LCOFIP
> >
> > +/* mintstatus */
> > +#define MINTSTATUS_MIL 0xff000000 /* mil[31:24] */
> > +#define MINTSTATUS_SIL 0x0000ff00 /* sil[15:8] */
> > +#define MINTSTATUS_UIL 0x000000ff /* uil[7:0] */
> > +
> > +/* sintstatus */
> > +#define SINTSTATUS_SIL 0x0000ff00 /* sil[15:8] */
> > +#define SINTSTATUS_UIL 0x000000ff /* uil[7:0] */
> > +
> > /* MIE masks */
> > #define MIE_SEIE (1 << IRQ_S_EXT)
> > #define MIE_UEIE (1 << IRQ_U_EXT)
> > diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> > index ea3560342c..f9ed7b9079 100644
> > --- a/target/riscv/csr.c
> > +++ b/target/riscv/csr.c
> > @@ -578,6 +578,16 @@ static RISCVException debug(CPURISCVState *env, int csrno)
> >
> > return RISCV_EXCP_ILLEGAL_INST;
> > }
> > +
> > +static int clic(CPURISCVState *env, int csrno)
> > +{
> > + if (env->clic) {
This isn't enough. There are smclic (M-mode) and ssclic (S-mode)
extensions that need to be checked against here to access the CSRs.
At the end of the series they can then be exposed as CPU properties
(which the virt machine can enable when required)
Alistair
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 04/11 v2] target/riscv: Update CSR xie in CLIC mode
2024-09-06 2:58 ` Alistair Francis
@ 2024-09-06 3:20 ` Alistair Francis
0 siblings, 0 replies; 32+ messages in thread
From: Alistair Francis @ 2024-09-06 3:20 UTC (permalink / raw)
To: Ian Brockbank
Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
Bin Meng, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei,
LIU Zhiwei
On Fri, Sep 6, 2024 at 12:58 PM Alistair Francis <alistair23@gmail.com> wrote:
>
> On Tue, Aug 20, 2024 at 2:15 AM Ian Brockbank <Ian.Brockbank@cirrus.com> wrote:
> >
> > From: Ian Brockbank <ian.brockbank@cirrus.com>
> >
> > The xie CSR appears hardwired to zero in CLIC mode, replaced by separate
> > memory-mapped interrupt enables (clicintie[i]). Writes to xie will be
> > ignored and will not trap (i.e., no access faults).
> >
> > Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
> > Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
> > ---
> > target/riscv/csr.c | 34 ++++++++++++++++++++++------------
> > 1 file changed, 22 insertions(+), 12 deletions(-)
> >
> > diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> > index 9c824c0d8f..a5978e0929 100644
> > --- a/target/riscv/csr.c
> > +++ b/target/riscv/csr.c
> > @@ -30,6 +30,10 @@
> > #include "qemu/guest-random.h"
> > #include "qapi/error.h"
> >
> > +#if !defined(CONFIG_USER_ONLY)
> > +#include "hw/intc/riscv_clic.h"
> > +#endif
>
> This doesn't seem like the way to go
Urgh! Ok, it's trickier than that.
I think ideally we don't want to pull in a bunch of CLIC stuff, just
the bare minimum.
It's probably better to implement the CLIC functions for CSR access in
the target/riscv directory instead of in hw/intc
Also, to make it easier to review it would be great to add the
functions when they are used. Then patch 3 will be smaller and the
other patches easier to see what is being added
Alistair
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 08/11 v2] target/riscv: Update interrupt handling in CLIC mode
2024-08-19 16:02 ` [PATCH 08/11 v2] target/riscv: Update interrupt handling " Ian Brockbank
@ 2024-09-06 3:49 ` Alistair Francis
0 siblings, 0 replies; 32+ messages in thread
From: Alistair Francis @ 2024-09-06 3:49 UTC (permalink / raw)
To: Ian Brockbank
Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
Bin Meng, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei,
LIU Zhiwei
On Tue, Aug 20, 2024 at 2:14 AM Ian Brockbank <Ian.Brockbank@cirrus.com> wrote:
>
> From: Ian Brockbank <ian.brockbank@cirrus.com>
>
> Decode CLIC interrupt information from exccode, includes interrupt
> privilege mode, interrupt level, and irq number.
>
> Then update CSRs xcause, xstatus, xepc, xintstatus and jump to
> correct PC according to the CLIC specification.
>
> Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
> Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
> ---
> target/riscv/cpu_helper.c | 129 +++++++++++++++++++++++++++++++++++---
> 1 file changed, 119 insertions(+), 10 deletions(-)
>
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 395a1d9140..944afb68d2 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -24,6 +24,7 @@
> #include "internals.h"
> #include "pmu.h"
> #include "exec/exec-all.h"
> +#include "exec/cpu_ldst.h"
> #include "exec/page-protection.h"
> #include "instmap.h"
> #include "tcg/tcg-op.h"
> @@ -33,6 +34,7 @@
> #include "cpu_bits.h"
> #include "debug.h"
> #include "tcg/oversized-guest.h"
> +#include "hw/intc/riscv_clic.h"
>
> int riscv_env_mmu_index(CPURISCVState *env, bool ifetch)
> {
> @@ -428,6 +430,20 @@ int riscv_cpu_vsirq_pending(CPURISCVState *env)
> (irqs | irqs_f_vs), env->hviprio);
> }
>
> +static int riscv_cpu_local_irq_mode_enabled(CPURISCVState *env, int mode)
> +{
> + switch (mode) {
> + case PRV_M:
> + return env->priv < PRV_M ||
> + (env->priv == PRV_M && get_field(env->mstatus, MSTATUS_MIE));
> + case PRV_S:
> + return env->priv < PRV_S ||
> + (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_SIE));
> + default:
> + return false;
> + }
> +}
> +
> static int riscv_cpu_local_irq_pending(CPURISCVState *env)
> {
> uint64_t irqs, pending, mie, hsie, vsie, irqs_f, irqs_f_vs;
> @@ -506,6 +522,18 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
> return true;
> }
> }
> + if (interrupt_request & CPU_INTERRUPT_CLIC) {
> + RISCVCPU *cpu = RISCV_CPU(cs);
> + CPURISCVState *env = &cpu->env;
> + int mode = get_field(env->exccode, RISCV_EXCP_CLIC_MODE);
> + int enabled = riscv_cpu_local_irq_mode_enabled(env, mode);
> + if (enabled) {
> + cs->exception_index = RISCV_EXCP_CLIC | env->exccode;
> + cs->interrupt_request = cs->interrupt_request & ~CPU_INTERRUPT_CLIC;
> + riscv_cpu_do_interrupt(cs);
> + return true;
> + }
> + }
> return false;
> }
>
> @@ -1641,6 +1669,60 @@ static target_ulong riscv_transformed_insn(CPURISCVState *env,
> return xinsn;
> }
>
> +static target_ulong riscv_intr_pc(CPURISCVState *env, target_ulong tvec,
> + target_ulong tvt, bool async,
> + int cause, int mode)
> +{
> + int mode1 = tvec & XTVEC_MODE;
> + int mode2 = tvec & XTVEC_FULL_MODE;
> +
This is going to need extension checks
> + if (!async) {
> + return tvec & XTVEC_OBASE;
> + }
> + /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
> + switch (mode1) {
> + case XTVEC_CLINT_DIRECT:
> + return tvec & XTVEC_OBASE;
> + case XTVEC_CLINT_VECTORED:
> + return (tvec & XTVEC_OBASE) + cause * 4;
> + default:
> + if (env->clic && (mode2 == XTVEC_CLIC)) {
> + /* Non-vectored, clicintattr[i].shv = 0 || cliccfg.nvbits = 0 */
> + if (!riscv_clic_shv_interrupt(env->clic, cause)) {
> + /* NBASE = mtvec[XLEN-1:6]<<6 */
> + return tvec & XTVEC_NBASE;
> + } else {
> + /*
> + * pc := M[TBASE + XLEN/8 * exccode)] & ~1,
> + * TBASE = mtvt[XLEN-1:6]<<6
> + */
> + int size = TARGET_LONG_BITS / 8;
> + target_ulong tbase = (tvt & XTVEC_NBASE) + size * cause;
> + void *host = tlb_vaddr_to_host(env, tbase, MMU_DATA_LOAD, mode);
This doesn't look right.
I think you want cpu_ l*_mmuidx_ra(). That will raise an exception on
an access failure
> + if (host != NULL) {
> + target_ulong new_pc = tbase;
> + if (!riscv_clic_use_jump_table(env->clic)) {
> + /*
> + * Standard CLIC: the vector entry is a function pointer
> + * so look up the destination.
> + */
> + new_pc = ldn_p(host, size);
> + host = tlb_vaddr_to_host(env, new_pc,
> + MMU_INST_FETCH, mode);
At xtvt is the base address of a table of pointers, you also then want
to call cpu_ l*_mmuidx_ra() a second time and that value should be
returned as the next PC
> + }
> + if (host) {
> + return new_pc;
> + }
> + }
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "CLIC: load trap handler error!\n");
> + exit(1);
Don't allow the guest to exit. Print a guest error and keep going
Alistair
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 11/11 v2] tests: add riscv clic qtest case and a function in qtest
2024-08-19 16:02 ` [PATCH 11/11 v2] tests: add riscv clic qtest case and a function in qtest Ian Brockbank
@ 2024-09-06 3:52 ` Alistair Francis
0 siblings, 0 replies; 32+ messages in thread
From: Alistair Francis @ 2024-09-06 3:52 UTC (permalink / raw)
To: Ian Brockbank
Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
Bin Meng, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei,
Troy Song
On Tue, Aug 20, 2024 at 2:13 AM Ian Brockbank <Ian.Brockbank@cirrus.com> wrote:
>
> This adds riscv32-clic-test.c, containing qtest test cases for configuring
> CLIC (via virt machine) and for triggering interrupts.
>
> In order to detect the interrupts, qtest.c has been updated to send interrupt
> information back to the test about the IRQ being delivered. Since we need to
> both trigger and detect the interrupt, qtest has also been updated to allow
> both an input and an output GPIO to be intercepted.
>
> Signed-off-by: Troy Song <wb-szh830103@alibaba-inc.com>
> Signed-off-by: Ian Brockbank <ian.brockbank@cirrus.com>
> ---
> hw/intc/riscv_clic.c | 4 +
> include/sysemu/qtest.h | 2 +
> system/qtest.c | 72 +-
> tests/qtest/libqtest.c | 9 +
> tests/qtest/libqtest.h | 9 +
> tests/qtest/meson.build | 3 +-
> tests/qtest/riscv32-clic-test.c | 1928 +++++++++++++++++++++++++++++++
Wow! Lots of tests.
The general qtest changes should be split into their own patches so
the qtest maintainers can easily review/ack them without worrying
about the CLIC
Alistair
> 7 files changed, 2010 insertions(+), 17 deletions(-)
> create mode 100644 tests/qtest/riscv32-clic-test.c
>
> diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
> index 1800e84dfd..155ba65492 100644
> --- a/hw/intc/riscv_clic.c
> +++ b/hw/intc/riscv_clic.c
> @@ -174,6 +174,10 @@ static void riscv_clic_next_interrupt(void *opaque)
> clic->clicintip[active->irq] = 0;
> }
> /* Post pending interrupt for this hart */
> + if (qtest_enabled()) {
> + qemu_set_irq(clic->cpu_irq, qtest_encode_irq(active->irq, 1));
> + return;
> + }
> clic->exccode = active->irq |
> mode << RISCV_EXCP_CLIC_MODE_SHIFT |
> level << RISCV_EXCP_CLIC_LEVEL_SHIFT;
> diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
> index c161d75165..1a34d27c6d 100644
> --- a/include/sysemu/qtest.h
> +++ b/include/sysemu/qtest.h
> @@ -34,6 +34,8 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **
> void qtest_server_set_send_handler(void (*send)(void *, const char *),
> void *opaque);
> void qtest_server_inproc_recv(void *opaque, const char *buf);
> +
> +int qtest_encode_irq(int irqn, int level);
> #endif
>
> #endif
> diff --git a/system/qtest.c b/system/qtest.c
> index 12703a2045..0ba6e0fcbe 100644
> --- a/system/qtest.c
> +++ b/system/qtest.c
> @@ -49,7 +49,8 @@ struct QTest {
>
> bool qtest_allowed;
>
> -static DeviceState *irq_intercept_dev;
> +static DeviceState *irq_intercept_dev_in;
> +static DeviceState *irq_intercept_dev_out;
> static FILE *qtest_log_fp;
> static QTest *qtest;
> static GString *inbuf;
> @@ -61,6 +62,14 @@ static void *qtest_server_send_opaque;
>
> #define FMT_timeval "%.06f"
>
> +/*
> + * Encoding for passing the specific IRQ information from an interrupt handler
> + * to QTest. This needs to support CLIC, which has a 12-bit interrupt number.
> + */
> +#define QTEST_IRQN 0x0fff
> +#define QTEST_IRQN_SHIFT 0
> +#define QTEST_IRQ_LEVEL_SHIFT 12
> +
> /**
> * DOC: QTest Protocol
> *
> @@ -311,6 +320,18 @@ void qtest_sendf(CharBackend *chr, const char *fmt, ...)
> va_end(ap);
> }
>
> +/* Encode the IRQ number and level for QTest */
> +int qtest_encode_irq(int irqn, int level)
> +{
> + return (irqn & QTEST_IRQN) | (level << QTEST_IRQ_LEVEL_SHIFT);
> +}
> +
> +static void qtest_decode_irq(int value, int *irqn, int *level)
> +{
> + *irqn = value & QTEST_IRQN;
> + *level = value >> QTEST_IRQ_LEVEL_SHIFT;
> +}
> +
> static void qtest_irq_handler(void *opaque, int n, int level)
> {
> qemu_irq old_irq = *(qemu_irq *)opaque;
> @@ -320,6 +341,16 @@ static void qtest_irq_handler(void *opaque, int n, int level)
> CharBackend *chr = &qtest->qtest_chr;
> irq_levels[n] = level;
> qtest_send_prefix(chr);
> + if (level > 1) {
> + int delivered_irq_num, pin_level;
> + qtest_decode_irq(level, &delivered_irq_num, &pin_level);
> + qtest_sendf(chr, "IRQ %s %d\n",
> + "delivered", delivered_irq_num);
> + qtest_send_prefix(chr);
> + qtest_sendf(chr, "IRQ %s %d\n",
> + pin_level ? "raise" : "lower", n);
> + return;
> + }
> qtest_sendf(chr, "IRQ %s %d\n",
> level ? "raise" : "lower", n);
> }
> @@ -369,6 +400,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
> bool is_named;
> bool is_outbound;
> bool interception_succeeded = false;
> + bool interception_duplicated = false;
>
> g_assert(words[1]);
> is_named = words[2] != NULL;
> @@ -386,38 +418,46 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
> return;
> }
>
> - if (irq_intercept_dev) {
> - qtest_send_prefix(chr);
> - if (irq_intercept_dev != dev) {
> - qtest_send(chr, "FAIL IRQ intercept already enabled\n");
> - } else {
> - qtest_send(chr, "OK\n");
> - }
> - return;
> - }
> -
> QLIST_FOREACH(ngl, &dev->gpios, node) {
> /* We don't support inbound interception of named GPIOs yet */
> if (is_outbound) {
> + if (irq_intercept_dev_out) {
> + if (irq_intercept_dev_out == dev) {
> + interception_succeeded = true;
> + } else {
> + interception_duplicated = true;
> + }
> + }
> /* NULL is valid and matchable, for "unnamed GPIO" */
> - if (g_strcmp0(ngl->name, words[2]) == 0) {
> + else if (g_strcmp0(ngl->name, words[2]) == 0) {
> int i;
> for (i = 0; i < ngl->num_out; ++i) {
> qtest_install_gpio_out_intercept(dev, ngl->name, i);
> }
> + irq_intercept_dev_out = dev;
> interception_succeeded = true;
> }
> } else {
> - qemu_irq_intercept_in(ngl->in, qtest_irq_handler,
> - ngl->num_in);
> - interception_succeeded = true;
> + if (irq_intercept_dev_in) {
> + if (irq_intercept_dev_in == dev) {
> + interception_succeeded = true;
> + } else {
> + interception_duplicated = true;
> + }
> + } else {
> + qemu_irq_intercept_in(ngl->in, qtest_irq_handler,
> + ngl->num_in);
> + irq_intercept_dev_in = dev;
> + interception_succeeded = true;
> + }
> }
> }
>
> qtest_send_prefix(chr);
> if (interception_succeeded) {
> - irq_intercept_dev = dev;
> qtest_send(chr, "OK\n");
> + } else if (interception_duplicated) {
> + qtest_send(chr, "FAIL IRQ intercept already enabled\n");
> } else {
> qtest_send(chr, "FAIL No intercepts installed\n");
> }
> diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
> index 1326e34291..754c78dad7 100644
> --- a/tests/qtest/libqtest.c
> +++ b/tests/qtest/libqtest.c
> @@ -83,6 +83,7 @@ struct QTestState
> int expected_status;
> bool big_endian;
> bool irq_level[MAX_IRQ];
> + bool is_delivered[MAX_IRQ];
> GString *rx;
> QTestTransportOps ops;
> GList *pending_events;
> @@ -499,6 +500,7 @@ static QTestState *qtest_init_internal(const char *qemu_bin,
> s->rx = g_string_new("");
> for (i = 0; i < MAX_IRQ; i++) {
> s->irq_level[i] = false;
> + s->is_delivered[i] = false;
> }
>
> /*
> @@ -696,6 +698,8 @@ redo:
>
> if (strcmp(words[1], "raise") == 0) {
> s->irq_level[irq] = true;
> + } else if (strcmp(words[1], "delivered") == 0) {
> + s->is_delivered[irq] = true;
> } else {
> s->irq_level[irq] = false;
> }
> @@ -988,6 +992,11 @@ bool qtest_get_irq(QTestState *s, int num)
> return s->irq_level[num];
> }
>
> +bool qtest_irq_delivered(QTestState *s, int num)
> +{
> + return s->is_delivered[num];
> +}
> +
> void qtest_module_load(QTestState *s, const char *prefix, const char *libname)
> {
> qtest_sendf(s, "module_load %s %s\n", prefix, libname);
> diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
> index c261b7e0b3..6bd0ada1b2 100644
> --- a/tests/qtest/libqtest.h
> +++ b/tests/qtest/libqtest.h
> @@ -366,6 +366,15 @@ void qtest_module_load(QTestState *s, const char *prefix, const char *libname);
> */
> bool qtest_get_irq(QTestState *s, int num);
>
> +/**
> + * qtest_irq_delivered:
> + * @s: #QTestState instance to operate on.
> + * @num: Interrupt to observe.
> + *
> + * Returns: Is @num interrupt delivered or not.
> + */
> +bool qtest_irq_delivered(QTestState *s, int num);
> +
> /**
> * qtest_irq_intercept_in:
> * @s: #QTestState instance to operate on.
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index 2f0d3ef080..3170c1b386 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -258,7 +258,8 @@ qtests_s390x = \
> 'migration-test']
>
> qtests_riscv32 = \
> - (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : [])
> + (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : []) + \
> + (config_all_devices.has_key('CONFIG_RISCV_CLIC') ? ['riscv32-clic-test'] : [])
>
> qtests_riscv64 = \
> (unpack_edk2_blobs ? ['bios-tables-test'] : [])
> diff --git a/tests/qtest/riscv32-clic-test.c b/tests/qtest/riscv32-clic-test.c
> new file mode 100644
> index 0000000000..7b0102142f
> --- /dev/null
> +++ b/tests/qtest/riscv32-clic-test.c
> @@ -0,0 +1,1928 @@
> +/*
> + * QTest testcase for the RISC-V CLIC (Core Local Interrupt Controller)
> + *
> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
> + * Copyright (c) 2024 Cirrus Logic, Inc
> + * and Cirrus Logic International Semiconductor Ltd.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/cutils.h"
> +#include "libqtest-single.h"
> +
> +/*
> + * Standard arguments to qtest_start.
> + * This finishes with the machine so that machine parameters can be passed
> + * by appending the string with them. E.g. QEMU_BASE_ARGS ",clic-mode=m".
> + * QEMU_ARGS makes use of this to give more normal-looking code.
> + */
> +#define QEMU_BASE_ARGS "-bios none -cpu rv32 -d guest_errors " \
> + "-machine virt,clic=on"
> +#define QEMU_ARGS(_machine_params) QEMU_BASE_ARGS _machine_params
> +
> +/*
> + * CLIC register addresses.
> + * The spec doesn't define a memory layout, other than to say that each
> + * CLIC should be on a 4KiB boundary if memory-mapped.
> + * This implementation makes all the CLICs contiguous, in the order M, S, U,
> + * and assumes the worst-case size. If there is only PRV_M and PRV_U, the PRV_U
> + * registers will appear instead of the PRV_S.
> + */
> +#define VIRT_CLIC_MAX_IRQS 0x1000
> +#define VIRT_CLIC_CONTEXT_BASE 0x1000
> +#define VIRT_CLIC_INT_SIZE(_irq_count) ((_irq_count) * 4)
> +#define VIRT_CLIC_BLOCK_SIZE \
> + (VIRT_CLIC_CONTEXT_BASE + VIRT_CLIC_INT_SIZE(VIRT_CLIC_MAX_IRQS))
> +
> +#define VIRT_CLIC_MMODE_BASE 0x2000000
> +#define VIRT_CLIC_SMODE_BASE (VIRT_CLIC_MMODE_BASE + VIRT_CLIC_BLOCK_SIZE)
> +#define VIRT_CLIC_UMODE_BASE (VIRT_CLIC_SMODE_BASE + VIRT_CLIC_BLOCK_SIZE)
> +
> +#define MCLICCFG_ADDR (VIRT_CLIC_MMODE_BASE + 0)
> +#define MCLICINFO_ADDR (VIRT_CLIC_MMODE_BASE + 4)
> +#define SCLICCFG_ADDR (VIRT_CLIC_SMODE_BASE + 0)
> +#define SCLICINFO_ADDR (VIRT_CLIC_SMODE_BASE + 4)
> +#define UCLICCFG_ADDR (VIRT_CLIC_UMODE_BASE + 0)
> +#define UCLICINFO_ADDR (VIRT_CLIC_UMODE_BASE + 4)
> +
> +/*
> + * Generate control register addresses for an irq
> + *
> + * @param irq_num IRQ to generate the addresses for
> + *
> + * Defines symbolic names for the clicintip, clicintie, clicintattr and
> + * clicintctl registers for interrupt irq_num.
> + */
> +#define GEN_CLIC_IRQ_REG(irq_num) \
> + uint64_t clicint##irq_num##_addr = \
> + VIRT_CLIC_MMODE_BASE + 0x1000 + 4 * irq_num; \
> + uint64_t clicintip##irq_num##_addr = \
> + VIRT_CLIC_MMODE_BASE + 0x1000 + 4 * irq_num; \
> + uint64_t clicintie##irq_num##_addr = \
> + VIRT_CLIC_MMODE_BASE + 0x1001 + 4 * irq_num; \
> + uint64_t clicintattr##irq_num##_addr = \
> + VIRT_CLIC_MMODE_BASE + 0x1002 + 4 * irq_num; \
> + uint64_t clicintctl##irq_num##_addr = \
> + VIRT_CLIC_MMODE_BASE + 0x1003 + 4 * irq_num; \
> + uint64_t clicint##irq_num##_addr_s = \
> + VIRT_CLIC_SMODE_BASE + 0x1000 + 4 * irq_num; \
> + uint64_t clicintip##irq_num##_addr_s = \
> + VIRT_CLIC_SMODE_BASE + 0x1000 + 4 * irq_num; \
> + uint64_t clicintie##irq_num##_addr_s = \
> + VIRT_CLIC_SMODE_BASE + 0x1001 + 4 * irq_num; \
> + uint64_t clicintattr##irq_num##_addr_s = \
> + VIRT_CLIC_SMODE_BASE + 0x1002 + 4 * irq_num; \
> + uint64_t clicintctl##irq_num##_addr_s = \
> + VIRT_CLIC_SMODE_BASE + 0x1003 + 4 * irq_num; \
> + uint64_t clicint##irq_num##_addr_u = \
> + VIRT_CLIC_UMODE_BASE + 0x1000 + 4 * irq_num; \
> + uint64_t clicintip##irq_num##_addr_u = \
> + VIRT_CLIC_UMODE_BASE + 0x1000 + 4 * irq_num; \
> + uint64_t clicintie##irq_num##_addr_u = \
> + VIRT_CLIC_UMODE_BASE + 0x1001 + 4 * irq_num; \
> + uint64_t clicintattr##irq_num##_addr_u = \
> + VIRT_CLIC_UMODE_BASE + 0x1002 + 4 * irq_num; \
> + uint64_t clicintctl##irq_num##_addr_u = \
> + VIRT_CLIC_UMODE_BASE + 0x1003 + 4 * irq_num;
> +
> +/* test variable for configure case and we use 12 irq to test */
> +GEN_CLIC_IRQ_REG(12)
> +
> +/* test variable for interrupt case we use irq 25 and irq 26 to test */
> +GEN_CLIC_IRQ_REG(25)
> +GEN_CLIC_IRQ_REG(26)
> +
> +/*
> + * Register decodes
> + */
> +#define INTIP_SHIFT 0
> +#define INTIE_SHIFT 8
> +#define INTATTR_SHIFT 16
> +#define INTCTL_SHIFT 24
> +
> +/* CLICCFG field definitions */
> +#define MNL_MASK 0x0000000f
> +#define MNL_SHIFT 0
> +#define NMBITS_MASK_1 0x00000000 /* Only PRV_M mode */
> +#define NMBITS_MASK_2 0x00000010 /* PRV_M plus either PRV_S or PRV_U */
> +#define NMBITS_MASK_3 0x00000030 /* PRV_M, PRV_S and PRV_U */
> +#define NMBITS_SHIFT 4
> +#define SNL_MASK 0x000f0000
> +#define SNL_SHIFT 16
> +#define UNL_MASK 0x0f000000
> +#define UNL_SHIFT 24
> +
> +/* The bits available in the different privilege modes */
> +#define MCFG_MASK_1 (MNL_MASK | NMBITS_MASK_1)
> +#define MCFG_MASK_2 (MNL_MASK | NMBITS_MASK_2)
> +#define MCFG_MASK_3 (MNL_MASK | NMBITS_MASK_3)
> +#define MCFG_MASK MCFG_MASK_1
> +#define SCFG_MASK SNL_MASK
> +#define UCFG_MASK UNL_MASK
> +#define SUCFG_MASK (SCFG_MASK | UCFG_MASK)
> +#define MUCFG_MASK (MCFG_MASK_2 | UCFG_MASK)
> +#define MSCFG_MASK (MCFG_MASK_2 | SCFG_MASK)
> +#define MSUCFG_MASK (MCFG_MASK_3 | SCFG_MASK | UCFG_MASK)
> +
> +/* CLICINTATTR field definitions */
> +#define INTATTR_SHV 0x1
> +enum INTATTR_TRIG {
> + TRIG_LEVEL = 0b00,
> + TRIG_EDGE = 0b01,
> + TRIG_POS = 0b00,
> + TRIG_NEG = 0b10,
> +
> + TRIG_HIGH = TRIG_LEVEL | TRIG_POS,
> + TRIG_LOW = TRIG_LEVEL | TRIG_NEG,
> + TRIG_RISING = TRIG_EDGE | TRIG_POS,
> + TRIG_FALLING = TRIG_EDGE | TRIG_NEG,
> +};
> +enum INTATTR_MODE {
> + PRV_U = 0,
> + PRV_S = 1,
> + PRV_M = 3
> +};
> +#define INTATTR_TRIG_MASK 0x06
> +#define INTATTR_TRIG_SHIFT 1
> +#define INTATTR_MODE_MASK 0xC0
> +#define INTATTR_MODE_SHIFT 6
> +
> +/* Convert the byte register definitions to the 32-bit register */
> +#define REG_INTIP 0x00000001
> +#define REG_INTIE 0x00000100
> +#define REG_SHV (INTATTR_SHV << INTATTR_SHIFT)
> +#define REG_TRIG_MASK (INTATTR_TRIG_MASK << INTATTR_SHIFT)
> +#define REG_TRIG_SHIFT (INTATTR_TRIG_SHIFT + INTATTR_SHIFT)
> +#define REG_MODE_MASK (INTATTR_MODE_MASK << INTATTR_SHIFT)
> +#define REG_MODE_SHIFT (INTATTR_MODE_SHIFT + INTATTR_SHIFT)
> +#define REG_INTCTL_MASK (0xff << INTCTL_SHIFT)
> +
> +/*
> + * Some test values, based on nmbits (_nmb)
> + */
> +#define TEST_CFG(_nmb) ((7 << UNL_SHIFT) | (7 << SNL_SHIFT) | \
> + (7 << MNL_SHIFT) | ((_nmb) << NMBITS_SHIFT))
> +#define TEST_CFG_M(_nmb) (TEST_CFG(_nmb) & MCFG_MASK) /* PRV_M only */
> +#define TEST_CFG_S(_nmb) (TEST_CFG(_nmb) & SCFG_MASK) /* PRV_S in MS */
> +#define TEST_CFG_U(_nmb) (TEST_CFG(_nmb) & UCFG_MASK) /* PRV_U */
> +#define TEST_CFG_SU(_nmb) (TEST_CFG(_nmb) & SUCFG_MASK) /* PRV_S in MSU */
> +#define TEST_CFG_MU(_nmb) (TEST_CFG(_nmb) & MUCFG_MASK) /* PRV_M in MU */
> +#define TEST_CFG_MS(_nmb) (TEST_CFG(_nmb) & MSCFG_MASK) /* PRV_M in MS */
> +#define TEST_CFG_MSU(_nmb) (TEST_CFG(_nmb) & MSUCFG_MASK) /* PRV_M in MSU */
> +
> +/*
> + * Generate a test function
> + *
> + * This writes to the given register, reads it back, and checks it has the
> + * expected value (which may be different from the write).
> + *
> + * @param CASE_NAME test case name
> + * @param WIDTH register access width - one of b, w, l or q
> + * @param WIDTH_BITS value width in bits
> + * @param reg_addr register to write and read
> + * @param set_value value to write to the register
> + * @param expected expected value to read back from the register
> + */
> +#define GEN_CHECK_REG_MMIO(CASE_NAME, WIDTH, WIDTH_BITS, reg_addr, \
> + set_value, expected) \
> +static void test_configure_##CASE_NAME(void) \
> +{ \
> + uint##WIDTH_BITS##_t _set_value = set_value; \
> + uint##WIDTH_BITS##_t _expected = expected; \
> + write##WIDTH(reg_addr, _set_value); \
> + uint##WIDTH_BITS##_t result = read##WIDTH(reg_addr); \
> + g_assert_cmpuint(result, ==, _expected); \
> +}
> +
> +/* test function for an 8-bit value */
> +#define GEN_CHECK_REG_MMIO_B(CASE_NAME, reg_addr, set_value, expected) \
> + GEN_CHECK_REG_MMIO(CASE_NAME, b, 8, reg_addr, set_value, expected)
> +/* test function for a 16-bit value */
> +#define GEN_CHECK_REG_MMIO_W(CASE_NAME, reg_addr, set_value, expected) \
> + GEN_CHECK_REG_MMIO(CASE_NAME, w, 16, reg_addr, set_value, expected)
> +/* test function for a 32-bit value */
> +#define GEN_CHECK_REG_MMIO_L(CASE_NAME, reg_addr, set_value, expected) \
> + GEN_CHECK_REG_MMIO(CASE_NAME, l, 32, reg_addr, set_value, expected)
> +/* test function for a 64-bit value */
> +#define GEN_CHECK_REG_MMIO_Q(CASE_NAME, reg_addr, set_value, expected) \
> + GEN_CHECK_REG_MMIO(CASE_NAME, q, 64, reg_addr, set_value, expected)
> +
> +
> + /* test case definitions */
> +
> +/*
> + * cliccfg tests
> + *
> + * Layout:
> + * 31:28 reserved (WPRI 0)
> + * 27:24 unlbits
> + * 23:20 reserved (WPRI 0)
> + * 19:16 snlbits
> + * 15:6 reserved (WPRI 0)
> + * 5:4 nmbits
> + * 3:0 mnlbits
> + */
> +
> +/*
> + * Set the minimum mnlbits
> + * set nmbits = 0, mnlbits = 0, snlbits = 0, unlbits = 0
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_min_mnlbits, MCLICCFG_ADDR, 0x0, 0x0)
> +
> +/*
> + * Set the max supported mnlbits
> + * set nmbits = 0, mnlbits = 8, snlbits = 0, unlbits = 0
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_supported_max_mnlbits, MCLICCFG_ADDR, 0x8, 0x8)
> +
> +/*
> + * Set mnlbits to an unsupported value
> + * set nmbits = 0, mnlbits = 10, snlbits = 0, unlbits = 0
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_mnlbits, MCLICCFG_ADDR, 0xA, 0x8)
> +
> +/*
> + * Set the minimum snlbits
> + * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 0
> + * Requires PRV_S mode
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_min_snlbits_s, MCLICCFG_ADDR, 0x00004, 0x00004)
> +
> +/*
> + * Set the max supported snlbits
> + * set nmbits = 0, mnlbits = 4, snlbits = 8, unlbits = 0
> + * Requires PRV_S mode
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_supported_max_snlbits_s, MCLICCFG_ADDR,
> + 0x80004, 0x80004)
> +
> +/*
> + * Set snlbits to an unsupported value
> + * set nmbits = 0, mnlbits = 4, snlbits = 10, unlbits = 0
> + * Requires PRV_S mode
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_snlbits_s, MCLICCFG_ADDR,
> + 0xA0004, 0x80004)
> +
> +/*
> + * Set snlbits with no PRV_S support
> + * set nmbits = 0, mnlbits = 4, snlbits = 8, unlbits = 0
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_snlbits_no_s, MCLICCFG_ADDR, 0x80004, 0x00004)
> +
> +/*
> + * Set the minimum unlbits
> + * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 0
> + * Requires PRV_S mode
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_min_unlbits_u, MCLICCFG_ADDR, 0x0000004, 0x0000004)
> +
> +/*
> + * Set the max supported unlbits
> + * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 8
> + * Requires PRV_U mode
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_supported_max_unlbits_u, MCLICCFG_ADDR,
> + 0x8000004, 0x8000004)
> +
> +/*
> + * Set unlbits to an unsupported value
> + * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 10
> + * Requires PRV_U mode
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_unlbits_u, MCLICCFG_ADDR,
> + 0xA000004, 0x8000004)
> +
> +/*
> + * Set unlbits with no PRV_U support
> + * set nmbits = 0, mnlbits = 4, snlbits = 0, unlbits = 8
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_unlbits_no_u, MCLICCFG_ADDR, 0x8000004, 0x0000004)
> +
> +/*
> + * Set all modes
> + * set nmbits = 0, mnlbits = 4, snlbits = 2, unlbits = 2
> + * Requires PRV_S + PRV_U mode
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_xnlbits, MCLICCFG_ADDR, 0x2020004, 0x2020004)
> +
> +/*
> + * nmbits = 0
> + * set nmbits = 0, mnlbits = 8
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_nmbits_0, MCLICCFG_ADDR, 0x08, 0x08)
> +
> +/*
> + * nmbits = 1 needs PRV_S or PRV_U
> + * set nmbits = 1, mnlbits = 8
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_nmbits_1, MCLICCFG_ADDR, 0x18, 0x18)
> +GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_nmbits_1, MCLICCFG_ADDR, 0x18, 0x08)
> +
> +/*
> + * nmbits = 2 needs PRV_S and PRV_U
> + * set nmbits = 2, mnlbits = 8
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_nmbits_2, MCLICCFG_ADDR, 0x28, 0x28)
> +GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_nmbits_2, MCLICCFG_ADDR, 0x28, 0x08)
> +
> +/*
> + * nmbits = 3 is not supported
> + * set nmbits = 3, mnlbits = 8
> + */
> +GEN_CHECK_REG_MMIO_L(cliccfg_unsupported_nmbits_3, MCLICCFG_ADDR, 0x38, 0x08)
> +
> +/*
> + * clicintie tests
> + *
> + * Layout:
> + * [0] enable: 1 = enabled, 0 = disabled
> + */
> +
> +/* set clicintie[i] = 0x1 and compare */
> +GEN_CHECK_REG_MMIO_B(clicintie_enable, clicintie12_addr, 0x1, 0x1)
> +
> +/* set clicintie[i] = 0x0 and compare */
> +GEN_CHECK_REG_MMIO_B(clicintie_disable, clicintie12_addr, 0, 0)
> +
> +/*
> + * clicintattr tests
> + *
> + * Layout:
> + * [7:6] mode b00 = U, b01 = S, b10 = reserved, b11 = M
> + * [5:3] reserved
> + * [2:1] trig trig[0]: 0 = level, 1 = edge; trig[1]: 0 = pos, 1 = neg
> + * [0] shv 0 = non-vectored, 1 = vectored
> + */
> +
> +/* Mode tests - note these deliberately use different trig and int settings */
> +/*
> + * Set mode 3 - PRV_M
> + * mode = b11, trig = b11, shv = b1
> + * clicintattr[i] = 0xc7
> + * expected
> + * mode = b11, tri = b11, shv = b1
> + * clicintattr[i] = 0xc7
> + */
> +GEN_CHECK_REG_MMIO_B(clicintattr_prv_m, clicintattr12_addr,
> + 0xc7, 0xc7)
> +
> +/*
> + * Set mode 1 - PRV_S
> + * mode = b01, trig = b10, shv = b0
> + * clicintattr[i] = 0x44
> + * expected
> + * mode = b01, tri = b10, shv = b0
> + * clicintattr[i] = 0x44
> + */
> +GEN_CHECK_REG_MMIO_B(clicintattr_prv_s_supported, clicintattr12_addr,
> + 0x44, 0x44)
> +
> +/*
> + * Set mode 0 - PRV_U
> + * mode = b00, trig = b01, shv = b1
> + * clicintattr[i] = 0x03
> + * expected
> + * mode = b00, tri = b01, shv = b1
> + * clicintattr[i] = 0x03
> + */
> +GEN_CHECK_REG_MMIO_B(clicintattr_prv_u_supported, clicintattr12_addr,
> + 0x03, 0x03)
> +
> +/*
> + * WARL: clicintattr should return PRV_M for PRV_S if PRV_U and PRV_S are
> + * both unsupported or nmbits = 0.
> + *
> + * mode = b01, tri = b10, shv = b0
> + * clicintattr[i] = 0x44
> + * expected
> + * mode = b11, tri = b10, shv = b0
> + * clicintattr[i] = 0xc4
> + */
> +GEN_CHECK_REG_MMIO_B(clicintattr_prv_s_to_m_warl, clicintattr12_addr,
> + 0x44, 0xc4)
> +
> +/*
> + * WARL: clicintattr should return PRV_U for PRV_S if PRV_S is unsupported and
> + * nmbits = 1.
> + *
> + * mode = b01, tri = b10, shv = b0
> + * clicintattr[i] = 0x44
> + * expected
> + * mode = b11, tri = b10, shv = b0
> + * clicintattr[i] = 0x04
> + */
> +GEN_CHECK_REG_MMIO_B(clicintattr_prv_s_to_u_warl, clicintattr12_addr,
> + 0x44, 0x04)
> +
> +/*
> + * WARL: clicintattr should return PRV_M for PRV_U if PRV_U and PRV_S are
> + * both unsupported or nmbits = 0.
> + *
> + * mode = b00, tri = b01, shv = b1
> + * clicintattr[i] = 0x03
> + * expected
> + * mode = b11, tri = b01, shv = b1
> + * clicintattr[i] = 0xc3
> + */
> +GEN_CHECK_REG_MMIO_B(clicintattr_prv_u_to_m_warl, clicintattr12_addr,
> + 0x03, 0xc3)
> +
> +/*
> + * WARL: clicintattr should return PRV_S for PRV_U if PRV_U is unsupported and
> + * nmbits = 1.
> + *
> + * mode = b00, tri = b01, shv = b1
> + * clicintattr[i] = 0x03
> + * expected
> + * mode = b01, tri = b01, shv = b1
> + * clicintattr[i] = 0x43
> + */
> +GEN_CHECK_REG_MMIO_B(clicintattr_prv_u_to_s_warl, clicintattr12_addr,
> + 0x03, 0x43)
> +
> +/*
> + * Mode 2 is invalid
> + * mode = b10, trig = b00, shv = b0
> + * clicintattr[i] = 0x80
> + * expected
> + * mode = b11, tri = b00, shv = b1
> + * clicintattr[i] = 0xc1
> + */
> +GEN_CHECK_REG_MMIO_B(clicintattr_unsupported_mode_10, clicintattr12_addr,
> + 0x81, 0xc1)
> +
> +/*
> + * set positive edge-triggered, vectored
> + * mode = b11, tri = b01, shv = b1
> + * clicintattr[i] = 0xc1
> + * expected
> + * mode = b11, tri = b01, shv = b1
> + * clicintattr[i] = 0xc1
> + */
> +GEN_CHECK_REG_MMIO_B(clicintattr_positive_edge_triggered, clicintattr12_addr,
> + 0xc3, 0xc3)
> +
> +/*
> + * set negative edge-triggered, vectored
> + * mode = b11, tri = b11, shv = b1
> + * clicintattr[i] = 0xc7
> + * expected
> + * mode = b11, tri = b11, shv = b1
> + * clicintattr[i] = 0xc7
> + */
> +GEN_CHECK_REG_MMIO_B(clicintattr_negative_edge_triggered, clicintattr12_addr,
> + 0xc7, 0xc7)
> +
> +/*
> + * set positive level-triggered, vectored
> + * mode = b11, tri = b00, shv = b1
> + * clicintattr[i] = 0xc1
> + * expected
> + * mode = b11, tri = b00, shv = b1
> + * clicintattr[i] = 0xc1
> + */
> +GEN_CHECK_REG_MMIO_B(clicintattr_positive_level_triggered, clicintattr12_addr,
> + 0xc1, 0xc1)
> +
> +/*
> + * set negative level-triggered, vectored
> + * mode = b11, tri = b10, shv = b1
> + * clicintattr[i] = 0xc5
> + * expected
> + * mode = b11, tri = b00, shv = b1
> + * clicintattr[i] = 0xc5
> + */
> +GEN_CHECK_REG_MMIO_B(clicintattr_negative_level_triggered, clicintattr12_addr,
> + 0xc5, 0xc5)
> +
> +/*
> + * set non-vectored
> + * mode = b11, tri = b11, shv = b0
> + * clicintattr[i] = 0xc6
> + * expected
> + * mode = b11, tri = b11, shv = b0
> + * clicintattr[i] = 0xc6
> + */
> +GEN_CHECK_REG_MMIO_B(clicintattr_non_vectored, clicintattr12_addr,
> + 0xc6, 0xc6)
> +
> +/*
> + * clicintctl tests
> + *
> + * Layout depends on mnlbits/snlbits/unlbits in mcliccfg.
> + *
> + */
> +
> +/*
> + * Test with 0 intctlbits - mask 0xff
> + * Everything rounds up
> + * clicintctl[i] = 0xFF
> + */
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_0_bits, clicintctl12_addr,
> + 0x00, 0xff)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_0_bits, clicintctl12_addr,
> + 0x21, 0xff)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_0_bits, clicintctl12_addr,
> + 0x58, 0xff)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_0_bits, clicintctl12_addr,
> + 0x80, 0xff)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_0_bits, clicintctl12_addr,
> + 0xcc, 0xff)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_0_bits, clicintctl12_addr,
> + 0xf0, 0xff)
> +
> +/*
> + * Test with 1 intctlbit - mask 0x7f
> + * The top bit is used, everything else rounds up
> + */
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_1_bits, clicintctl12_addr,
> + 0x00, 0x7f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_1_bits, clicintctl12_addr,
> + 0x21, 0x7f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_1_bits, clicintctl12_addr,
> + 0x58, 0x7f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_1_bits, clicintctl12_addr,
> + 0x80, 0xff)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_1_bits, clicintctl12_addr,
> + 0xcc, 0xff)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_1_bits, clicintctl12_addr,
> + 0xf0, 0xff)
> +
> +/*
> + * Test with 2 intctlbits - mask 0x3f
> + * The top 2 bits are used, everything else rounds up
> + */
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_2_bits, clicintctl12_addr,
> + 0x00, 0x3f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_2_bits, clicintctl12_addr,
> + 0x21, 0x3f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_2_bits, clicintctl12_addr,
> + 0x58, 0x7f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_2_bits, clicintctl12_addr,
> + 0x80, 0xbf)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_2_bits, clicintctl12_addr,
> + 0xcc, 0xff)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_2_bits, clicintctl12_addr,
> + 0xf0, 0xff)
> +
> +/*
> + * Test with 3 intctlbits - mask 0x1f
> + * The top 3 bits are used, everything else rounds up
> + */
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_3_bits, clicintctl12_addr,
> + 0x00, 0x1f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_3_bits, clicintctl12_addr,
> + 0x21, 0x3f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_3_bits, clicintctl12_addr,
> + 0x58, 0x5f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_3_bits, clicintctl12_addr,
> + 0x80, 0x9f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_3_bits, clicintctl12_addr,
> + 0xcc, 0xdf)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_3_bits, clicintctl12_addr,
> + 0xf0, 0xff)
> +
> +/*
> + * Test with 4 intctlbits - mask 0x0f
> + * The top 4 bits are used, everything else rounds up
> + */
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_4_bits, clicintctl12_addr,
> + 0x00, 0x0f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_4_bits, clicintctl12_addr,
> + 0x21, 0x2f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_4_bits, clicintctl12_addr,
> + 0x58, 0x5f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_4_bits, clicintctl12_addr,
> + 0x80, 0x8f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_4_bits, clicintctl12_addr,
> + 0xcc, 0xcf)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_4_bits, clicintctl12_addr,
> + 0xf0, 0xff)
> +
> +/*
> + * Test with 5 intctlbits - mask 0x07
> + * The top 5 bits are used, everything else rounds up
> + */
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_5_bits, clicintctl12_addr,
> + 0x00, 0x07)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_5_bits, clicintctl12_addr,
> + 0x21, 0x27)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_5_bits, clicintctl12_addr,
> + 0x58, 0x5f)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_5_bits, clicintctl12_addr,
> + 0x80, 0x87)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_5_bits, clicintctl12_addr,
> + 0xcc, 0xcf)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_5_bits, clicintctl12_addr,
> + 0xf0, 0xf7)
> +
> +/*
> + * Test with 6 intctlbits - mask 0x03
> + * The top 6 bits are used, everything else rounds up
> + */
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_6_bits, clicintctl12_addr,
> + 0x00, 0x03)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_6_bits, clicintctl12_addr,
> + 0x21, 0x23)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_6_bits, clicintctl12_addr,
> + 0x58, 0x5b)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_6_bits, clicintctl12_addr,
> + 0x80, 0x83)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_6_bits, clicintctl12_addr,
> + 0xcc, 0xcf)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_6_bits, clicintctl12_addr,
> + 0xf0, 0xf3)
> +
> +/*
> + * Test with 7 intctlbits - mask 0x01
> + * The top 7 bits are used, everything else rounds up
> + */
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_7_bits, clicintctl12_addr,
> + 0x00, 0x01)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_7_bits, clicintctl12_addr,
> + 0x21, 0x21)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_7_bits, clicintctl12_addr,
> + 0x58, 0x59)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_7_bits, clicintctl12_addr,
> + 0x80, 0x81)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_7_bits, clicintctl12_addr,
> + 0xcc, 0xcd)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_7_bits, clicintctl12_addr,
> + 0xf0, 0xf1)
> +
> +/*
> + * Test with 8 intctlbits - mask 0x00
> + * All bits are used
> + */
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_0_8_bits, clicintctl12_addr,
> + 0x00, 0x00)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_33_8_bits, clicintctl12_addr,
> + 0x21, 0x21)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_88_8_bits, clicintctl12_addr,
> + 0x58, 0x58)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_128_8_bits, clicintctl12_addr,
> + 0x80, 0x80)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_204_8_bits, clicintctl12_addr,
> + 0xcc, 0xcc)
> +GEN_CHECK_REG_MMIO_B(clicintctl_set_240_8_bits, clicintctl12_addr,
> + 0xf0, 0xf0)
> +
> +/*
> + * read clicintip[i] to result
> + * set cliccfg = 0x11, clicintattr[i] = 0x31, clicintip[i] = 0x1
> + * check the value of clicintip[i] that is changed or not
> + */
> +static void test_configure_clicintip_level_triggered_read_only(void)
> +{
> + /* configure level-triggered mode */
> + writeb(clicintattr12_addr, 0xc1);
> + g_assert_cmpuint(readb(clicintattr12_addr), ==, 0xc1);
> +
> + uint8_t orig_value = readb(clicintip12_addr);
> + writeb(clicintip12_addr, 0x1);
> + uint32_t result = readb(clicintip12_addr);
> +
> + g_assert_cmpuint(orig_value, ==, result);
> +}
> +
> +static void boot_qemu_m(void)
> +{
> + global_qtest = qtest_start(QEMU_ARGS(",clic-mode=m"));
> +}
> +
> +static void boot_qemu_ms(void)
> +{
> + global_qtest = qtest_start(QEMU_ARGS(",clic-mode=ms"));
> +}
> +
> +static void boot_qemu_mu(void)
> +{
> + global_qtest = qtest_start(QEMU_ARGS(",clic-mode=mu"));
> +}
> +
> +static void boot_qemu_msu(void)
> +{
> + global_qtest = qtest_start(QEMU_ARGS(",clic-mode=msu"));
> +}
> +
> +#define GEN_BOOT_QEMU_INTCTL(_nbits) \
> +static void boot_qemu_##_nbits##_bits(void) \
> +{ \
> + global_qtest = qtest_initf(QEMU_BASE_ARGS \
> + ",clic-mode=msu,clic-intctlbits=%d", \
> + _nbits); \
> +}
> +
> +static void shut_down_qemu(void)
> +{
> + qtest_quit(global_qtest);
> +}
> +
> +/*
> + * test vectored positive level triggered interrupt
> + * test points:
> + * 1. we use interrupt 25 and 26 to test arbitration in two situation:
> + * same level, different level.
> + * 2. within level triggered mode, we can only use device to clear pending.
> + * 3. within positive level triggered mode, set gpio-in rise
> + * to trigger interrupt.
> + */
> +static void test_vectored_positive_level_triggered_interrupt(void)
> +{
> + QTestState *qts = qtest_start(QEMU_BASE_ARGS);
> + /* intercept in and out irq */
> + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
> + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
> +
> + /* configure */
> + qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
> + qtest_writeb(qts, clicintattr25_addr, 0xc1);
> + qtest_writeb(qts, clicintattr26_addr, 0xc1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
> +
> + /* set pending */
> + /* arbitration will be made and 26 will be delivered */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
> + qtest_writeb(qts, clicintie25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
> + qtest_writeb(qts, clicintie26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
> + /* trigger arbitration */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + g_assert_true(qtest_irq_delivered(qts, 26));
> + g_assert_true(qtest_get_irq(qts, 0));
> +
> + /*
> + * level trigger wouldn't auto clear clear pending,
> + * so we need to manually do it.
> + */
> + qtest_writeb(qts, clicintie25_addr, 0);
> + qtest_writeb(qts, clicintie26_addr, 0);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
> +
> + /* set interrupt 25 level 255, interrupt 26 level 127 */
> + /* arbitration will be made and 25 will rise */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
> + qtest_writeb(qts, clicintctl25_addr, 0xbf);
> + qtest_writeb(qts, clicintctl26_addr, 0x3f);
> + qtest_writeb(qts, clicintie25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
> + qtest_writeb(qts, clicintie26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
> + /* trigger arbitration */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + g_assert_true(qtest_irq_delivered(qts, 25));
> + g_assert_true(qtest_get_irq(qts, 0));
> +
> + qtest_quit(qts);
> +}
> +
> +/*
> + * test vectored negative level triggered interrupt
> + * test points:
> + * 1. we use interrupt 25 and 26 to test arbitration in two situation:
> + * same level, different level.
> + * 2. within level triggered mode, we can only use device to clear pending.
> + * 3. within negative level triggered mode,
> + * set gpio-in lower to trigger interrupt.
> + */
> +static void test_vectored_negative_level_triggered_interrupt(void)
> +{
> + QTestState *qts = qtest_start(QEMU_BASE_ARGS);
> + /* intercept in and out irq */
> + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
> + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
> +
> + /* configure */
> + qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
> + qtest_writeb(qts, clicintattr25_addr, 0xc5);
> + qtest_readb(qts, clicintattr25_addr);
> + qtest_writeb(qts, clicintattr26_addr, 0xc5);
> + qtest_readb(qts, clicintattr26_addr);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
> +
> + /* set pending */
> + /* arbitration will be made and 26 will be delivered */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
> + qtest_writeb(qts, clicintie25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
> + qtest_writeb(qts, clicintie26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
> + /* trigger arbitration */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + g_assert_true(qtest_irq_delivered(qts, 26));
> + g_assert_true(qtest_get_irq(qts, 0));
> +
> + /*
> + * level trigger wouldn't auto clear clear pending,
> + * so we need to manually do it.
> + */
> + qtest_writeb(qts, clicintie25_addr, 0);
> + qtest_writeb(qts, clicintie26_addr, 0);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
> +
> + /* set interrupt 25 level 255, interrupt 26 level 127 */
> + /* arbitration will be made and 25 will rise */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
> + qtest_writeb(qts, clicintctl25_addr, 0xbf);
> + qtest_writeb(qts, clicintctl26_addr, 0x3f);
> + qtest_writeb(qts, clicintie25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
> + qtest_writeb(qts, clicintie26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
> + /* trigger arbitration */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + g_assert_true(qtest_irq_delivered(qts, 25));
> + g_assert_true(qtest_get_irq(qts, 0));
> +
> + qtest_quit(qts);
> +}
> +
> +/*
> + * test vectored positive edge triggered interrupt
> + * test points:
> + * 1. we use interrupt 25 and 26 to test arbitration in two situation:
> + * same level, different level.
> + * 2. within vectored edge triggered mode, pending bit will be
> + * automatically cleared.
> + * 3. within positive edge triggered mode, set gpio-in from
> + * lower to rise to trigger interrupt.
> + */
> +static void test_vectored_positive_edge_triggered_interrupt(void)
> +{
> + QTestState *qts = qtest_start(QEMU_BASE_ARGS);
> + /* intercept in and out irq */
> + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
> + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
> +
> + /* configure */
> + qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
> + qtest_writeb(qts, clicintattr25_addr, 0xc3);
> + qtest_writeb(qts, clicintattr26_addr, 0xc3);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
> +
> + /* set pending */
> + /* arbitration will be made and 26 will be delivered */
> + qtest_writeb(qts, clicintip25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
> + qtest_writeb(qts, clicintip26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
> + qtest_writeb(qts, clicintie25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
> + qtest_writeb(qts, clicintie26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
> + /* trigger arbitration */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + g_assert_true(qtest_irq_delivered(qts, 26));
> + g_assert_true(qtest_get_irq(qts, 0));
> +
> + /* vectored edge trigger will auto clear clear pending */
> + qtest_writeb(qts, clicintie25_addr, 0);
> + qtest_writeb(qts, clicintie26_addr, 0);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
> +
> + /* set interrupt 25 level 255, interrupt 26 level 127 */
> + /* arbitration will be made and 25 will rise */
> + qtest_writeb(qts, clicintip25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
> + qtest_writeb(qts, clicintip26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
> + qtest_writeb(qts, clicintctl25_addr, 0xbf);
> + qtest_writeb(qts, clicintctl26_addr, 0x3f);
> + qtest_writeb(qts, clicintie25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
> + qtest_writeb(qts, clicintie26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
> + /* trigger arbitration */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + g_assert_true(qtest_irq_delivered(qts, 25));
> + g_assert_true(qtest_get_irq(qts, 0));
> +
> + qtest_quit(qts);
> +}
> +
> +/*
> + * test vectored negative edge triggered interrupt
> + * test points:
> + * 1. we use interrupt 25 and 26 to test arbitration in two situation:
> + * same level, different level.
> + * 2. within vectored edge triggered mode, pending bit will be
> + * automatically cleared.
> + * 3. within negative edge triggered mode, set gpio-in from
> + * rise to lower to trigger interrupt.
> + */
> +static void test_vectored_negative_edge_triggered_interrupt(void)
> +{
> + QTestState *qts = qtest_start(QEMU_BASE_ARGS);
> + /* intercept in and out irq */
> + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
> + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
> +
> + /* configure */
> + qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
> + qtest_writeb(qts, clicintattr25_addr, 0xc7);
> + qtest_writeb(qts, clicintattr26_addr, 0xc7);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
> +
> + /* set pending */
> + /* arbitration will be made and 26 will be delivered */
> + qtest_writeb(qts, clicintip25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
> + qtest_writeb(qts, clicintip26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
> + qtest_writeb(qts, clicintie25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
> + qtest_writeb(qts, clicintie26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
> + /* trigger arbitration */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + g_assert_true(qtest_irq_delivered(qts, 26));
> + g_assert_true(qtest_get_irq(qts, 0));
> +
> + /* vectored edge trigger will auto clear clear pending */
> + qtest_writeb(qts, clicintie25_addr, 0);
> + qtest_writeb(qts, clicintie26_addr, 0);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
> +
> + /* set interrupt 25 level 255, interrupt 26 level 127 */
> + /* arbitration will be made and 25 will rise */
> + qtest_writeb(qts, clicintip25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
> + qtest_writeb(qts, clicintip26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
> + qtest_writeb(qts, clicintctl25_addr, 0xbf);
> + qtest_writeb(qts, clicintctl26_addr, 0x3f);
> + qtest_writeb(qts, clicintie25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
> + qtest_writeb(qts, clicintie26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
> + /* trigger arbitration */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + g_assert_true(qtest_irq_delivered(qts, 25));
> + g_assert_true(qtest_get_irq(qts, 0));
> +
> + qtest_quit(qts);
> +}
> +
> +/*
> + * test unvectored positive level triggered interrupt
> + * test points:
> + * 1. we use interrupt 25 and 26 to test arbitration in two situation:
> + * same level, different level.
> + * 2. within level triggered mode, we can only use device to clear pending.
> + * 3. within positive level triggered mode, set gpio-in rise to
> + * trigger interrupt.
> + */
> +static void test_unvectored_positive_level_triggered_interrupt(void)
> +{
> + QTestState *qts = qtest_start(QEMU_BASE_ARGS);
> + /* intercept in and out irq */
> + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
> + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
> +
> + /* configure */
> + qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
> + qtest_writeb(qts, clicintattr25_addr, 0xc0);
> + qtest_writeb(qts, clicintattr26_addr, 0xc0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
> +
> + /* set pending */
> + /* arbitration will be made and 26 will be delivered */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
> + qtest_writeb(qts, clicintie25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
> + qtest_writeb(qts, clicintie26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
> + /* trigger arbitration */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + g_assert_true(qtest_irq_delivered(qts, 26));
> + g_assert_true(qtest_get_irq(qts, 0));
> +
> + /*
> + * level trigger wouldn't auto clear clear pending,
> + * so we need to manually do it.
> + */
> + qtest_writeb(qts, clicintie25_addr, 0);
> + qtest_writeb(qts, clicintie26_addr, 0);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
> +
> + /* set interrupt 25 level 255, interrupt 26 level 127 */
> + /* arbitration will be made and 25 will rise */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
> + qtest_writeb(qts, clicintctl25_addr, 0xbf);
> + qtest_writeb(qts, clicintctl26_addr, 0x3f);
> + qtest_writeb(qts, clicintie25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
> + qtest_writeb(qts, clicintie26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
> + /* trigger arbitration */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + g_assert_true(qtest_irq_delivered(qts, 25));
> + g_assert_true(qtest_get_irq(qts, 0));
> +
> + qtest_quit(qts);
> +}
> +
> +/*
> + * test unvectored negative level triggered interrupt
> + * test points:
> + * 1. we use interrupt 25 and 26 to test arbitration in two situation:
> + * same level, different level.
> + * 2. within level triggered mode, we can only use device to clear pending.
> + * 3. within negative level triggered mode, set gpio-in lower
> + * to trigger interrupt.
> + */
> +static void test_unvectored_negative_level_triggered_interrupt(void)
> +{
> + QTestState *qts = qtest_start(QEMU_BASE_ARGS);
> + /* intercept in and out irq */
> + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
> + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
> +
> + /* configure */
> + qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
> + qtest_writeb(qts, clicintattr25_addr, 0xc4);
> + qtest_writeb(qts, clicintattr26_addr, 0xc4);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
> +
> + /* set pending */
> + /* arbitration will be made and 26 will be delivered */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
> + qtest_writeb(qts, clicintie25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
> + qtest_writeb(qts, clicintie26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
> + /* trigger arbitration */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + g_assert_true(qtest_irq_delivered(qts, 26));
> + g_assert_true(qtest_get_irq(qts, 0));
> +
> + /*
> + * level trigger wouldn't auto clear clear pending,
> + * so we need to manually do it.
> + */
> + qtest_writeb(qts, clicintie25_addr, 0);
> + qtest_writeb(qts, clicintie26_addr, 0);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 0, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
> +
> + /* set interrupt 25 level 255, interrupt 26 level 127 */
> + /* arbitration will be made and 25 will rise */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
> + qtest_writeb(qts, clicintctl25_addr, 0xbf);
> + qtest_writeb(qts, clicintctl26_addr, 0x3f);
> + qtest_writeb(qts, clicintie25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
> + qtest_writeb(qts, clicintie26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
> + /* trigger arbitration */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + g_assert_true(qtest_irq_delivered(qts, 25));
> + g_assert_true(qtest_get_irq(qts, 0));
> +
> + qtest_quit(qts);
> +}
> +
> +/*
> + * test unvectored positive edge triggered interrupt
> + * test points:
> + * 1. we use interrupt 25 and 26 to test arbitration in same level
> + * 2. within unvectored edge triggered mode, pending bit can be
> + * cleared by using nxti instruction which can't be tested in qtest.
> + * 3. within positive edge triggered mode, set gpio-in
> + * from lower to rise to trigger interrupt.
> + */
> +static void test_unvectored_positive_edge_triggered_interrupt(void)
> +{
> + QTestState *qts = qtest_start(QEMU_BASE_ARGS);
> + /* intercept in and out irq */
> + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
> + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
> +
> + /* configure */
> + qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
> + qtest_writeb(qts, clicintattr25_addr, 0xc2);
> + qtest_writeb(qts, clicintattr26_addr, 0xc2);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
> +
> + /* set pending */
> + /* arbitration will be made and 26 will be delivered */
> + qtest_writeb(qts, clicintip25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
> + qtest_writeb(qts, clicintip26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
> + qtest_writeb(qts, clicintie25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
> + qtest_writeb(qts, clicintie26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
> + /* trigger arbitration */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + g_assert_true(qtest_irq_delivered(qts, 26));
> + g_assert_true(qtest_get_irq(qts, 0));
> +
> + qtest_quit(qts);
> +}
> +
> +/*
> + * test unvectored negative edge triggered interrupt
> + * test points:
> + * 1. we use interrupt 25 and 26 to test arbitration in same level
> + * 2. within unvectored edge triggered mode, pending bit can be cleared
> + * by using nxti instruction which can't be tested in qtest.
> + * 3. within positive edge triggered mode, set gpio-in from
> + * rise to lower to trigger interrupt.
> + */
> +static void test_unvectored_negative_edge_triggered_interrupt(void)
> +{
> + QTestState *qts = qtest_start(QEMU_BASE_ARGS);
> + /* intercept in and out irq */
> + qtest_irq_intercept_out(qts, "/machine/unattached/device[1]");
> + qtest_irq_intercept_in(qts, "/machine/unattached/device[1]");
> +
> + /* configure */
> + qtest_writeb(qts, MCLICCFG_ADDR, 0x1);
> + qtest_writeb(qts, clicintattr25_addr, 0xc6);
> + qtest_writeb(qts, clicintattr26_addr, 0xc6);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 1);
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 26, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 0);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 0);
> +
> + /* set pending */
> + /* arbitration will be made and 26 will be delivered */
> + qtest_writeb(qts, clicintip25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip25_addr), ==, 1);
> + qtest_writeb(qts, clicintip26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintip26_addr), ==, 1);
> + qtest_writeb(qts, clicintie25_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie25_addr), ==, 1);
> + qtest_writeb(qts, clicintie26_addr, 1);
> + g_assert_cmpuint(qtest_readb(qts, clicintie26_addr), ==, 1);
> + /* trigger arbitration */
> + qtest_set_irq_in(qts, "/machine/unattached/device[1]", NULL, 25, 0);
> + g_assert_true(qtest_irq_delivered(qts, 26));
> + g_assert_true(qtest_get_irq(qts, 0));
> +
> + qtest_quit(qts);
> +}
> +
> +/*
> + * Test that PRV_S is a filtered view of PRV_M.
> + *
> + * IRQs configured as PRV_M in the mode field of intattr are not visible via
> + * the PRV_S registers, and all registers appear as hard-wired zeros.
> + */
> +static void test_prv_s_access(void)
> +{
> + QTestState *qts = qtest_start(QEMU_ARGS(",clic-mode=ms"));
> + int default_intattr = INTATTR_SHV | (TRIG_LEVEL << INTATTR_TRIG_SHIFT);
> + int default_reg_value = (default_intattr << INTATTR_SHIFT) |
> + (PRV_M << REG_MODE_SHIFT) | (0x7 << INTCTL_SHIFT);
> + int reg_value_2 = (TRIG_FALLING << REG_TRIG_SHIFT) |
> + (PRV_S << REG_MODE_SHIFT) | (0x5 << INTCTL_SHIFT);
> + int value;
> +
> + /*
> + * Make sure of our base state using the PRV_M registers
> + */
> + qtest_writel(qts, MCLICCFG_ADDR, TEST_CFG(1));
> + /* No PRV_U, so no UNLBITS */
> + g_assert_cmpuint(qtest_readl(qts, MCLICCFG_ADDR), == , TEST_CFG_MS(1));
> +
> + qtest_writel(qts, clicint12_addr, default_reg_value);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value);
> + qtest_writel(qts, clicint25_addr, default_reg_value);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
> + qtest_writel(qts, clicint26_addr, default_reg_value);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
> +
> + /*
> + * Now check the PRV_S view
> + */
> +
> + /* We should only see the PRV_S part of CLICCFG */
> + g_assert_cmpuint(qtest_readl(qts, SCLICCFG_ADDR), == , TEST_CFG_S(1));
> +
> + /* These are all PRV_M mode so reading via PRV_S should see them all as 0 */
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , 0);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0);
> +
> + /* Writing should leave them unchanged */
> + qtest_writel(qts, clicint12_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value);
> + qtest_writel(qts, clicint25_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
> + qtest_writel(qts, clicint26_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
> +
> + /*
> + * If we change IRQ 12 to PRV_S mode, we should now be able to see it
> + */
> + value = (default_reg_value & ~REG_MODE_MASK) | (PRV_S << REG_MODE_SHIFT);
> + qtest_writel(qts, clicint12_addr, value);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , value);
> +
> + /* ...but not the others */
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0);
> +
> + /* We should also be able to write to it */
> + qtest_writel(qts, clicint12_addr_s, reg_value_2);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , reg_value_2);
> +
> + /* ...but not the others */
> + qtest_writel(qts, clicint25_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
> + qtest_writel(qts, clicint26_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
> +
> + qtest_quit(qts);
> +}
> +
> +/*
> + * Test that PRV_U is a filtered view of PRV_M.
> + *
> + * IRQs configured as PRV_M in the mode field of intattr are not visible via
> + * the PRV_U registers, and all registers appear as hard-wired zeros.
> + */
> +static void test_prv_u_access(void)
> +{
> + QTestState *qts = qtest_start(QEMU_ARGS(",clic-mode=mu"));
> + int default_intattr = INTATTR_SHV | (TRIG_LEVEL << INTATTR_TRIG_SHIFT);
> + int default_reg_value = (default_intattr << INTATTR_SHIFT) |
> + (PRV_M << REG_MODE_SHIFT) | (0x7 << INTCTL_SHIFT);
> + int reg_value_2 = (TRIG_FALLING << REG_TRIG_SHIFT) |
> + (PRV_U << REG_MODE_SHIFT) | (0x5 << INTCTL_SHIFT);
> + int value;
> +
> + /*
> + * Make sure of our base state using the PRV_M registers
> + */
> + qtest_writel(qts, MCLICCFG_ADDR, TEST_CFG(1));
> + /* No PRV_S, so no SNLBITS */
> + g_assert_cmpuint(qtest_readl(qts, MCLICCFG_ADDR), == , TEST_CFG_MU(1));
> +
> + qtest_writel(qts, clicint12_addr, default_reg_value);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value);
> + qtest_writel(qts, clicint25_addr, default_reg_value);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
> + qtest_writel(qts, clicint26_addr, default_reg_value);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
> +
> + /*
> + * Now check the PRV_U view. Note we only have one additional mode, so we
> + * use the xxx_addr_s register bank.
> + */
> +
> + /* We should only see the PRV_U part of CLICCFG */
> + g_assert_cmpuint(qtest_readl(qts, SCLICCFG_ADDR), == , TEST_CFG_U(1));
> +
> + /* These are all PRV_M mode so reading via PRV_S should see them all as 0 */
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , 0);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0);
> +
> + /* Writing should leave them unchanged */
> + qtest_writel(qts, clicint12_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value);
> + qtest_writel(qts, clicint25_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
> + qtest_writel(qts, clicint26_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
> +
> + /*
> + * If we change IRQ 12 to PRV_U mode, we should now be able to see it
> + */
> + value = (default_reg_value & ~REG_MODE_MASK) | (PRV_U << REG_MODE_SHIFT);
> + qtest_writel(qts, clicint12_addr, value);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , value);
> +
> + /* ...but not the others */
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0);
> +
> + /* We should also be able to write to it */
> + qtest_writel(qts, clicint12_addr_s, reg_value_2);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , reg_value_2);
> +
> + /* ...but not the others */
> + qtest_writel(qts, clicint25_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
> + qtest_writel(qts, clicint26_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
> +
> + qtest_quit(qts);
> +}
> +
> +/*
> + * Test that PRV_S and PRV_U are filtered views of PRV_M.
> + *
> + * IRQs configured as PRV_M in the mode field of intattr are not visible via
> + * the PRV_S or PRV_U registers, and all registers appear as hard-wired zeros.
> + */
> +static void test_prv_su_access(void)
> +{
> + QTestState *qts = qtest_start(QEMU_ARGS(",clic-mode=msu"));
> + int default_intattr = INTATTR_SHV | (TRIG_LEVEL << INTATTR_TRIG_SHIFT);
> + int default_reg_value = (default_intattr << INTATTR_SHIFT) |
> + (PRV_M << REG_MODE_SHIFT) | (0x7 << INTCTL_SHIFT);
> + int reg_value_2 = (TRIG_FALLING << REG_TRIG_SHIFT) |
> + (PRV_S << REG_MODE_SHIFT) | (0x5 << INTCTL_SHIFT);
> + int reg_value_3 = (TRIG_RISING << REG_TRIG_SHIFT) |
> + (PRV_U << REG_MODE_SHIFT) | (0x2 << INTCTL_SHIFT);
> + int reg_value_4 = INTATTR_SHV | (TRIG_HIGH << REG_TRIG_SHIFT) |
> + (PRV_U << REG_MODE_SHIFT) | (0x3 << INTCTL_SHIFT);
> + int value;
> +
> + /*
> + * Make sure of our base state using the PRV_M registers
> + */
> + qtest_writel(qts, MCLICCFG_ADDR, TEST_CFG(2));
> + g_assert_cmpuint(qtest_readl(qts, MCLICCFG_ADDR), == , TEST_CFG_MSU(2));
> +
> + qtest_writel(qts, clicint12_addr, default_reg_value);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value);
> + qtest_writel(qts, clicint25_addr, default_reg_value);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
> + qtest_writel(qts, clicint26_addr, default_reg_value);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
> +
> + /*
> + * Now check the PRV_S view
> + */
> +
> + /* We should only see the PRV_S and PRV_U parts of CLICCFG */
> + g_assert_cmpuint(qtest_readl(qts, SCLICCFG_ADDR), == , TEST_CFG_SU(2));
> +
> + /* These are all PRV_M mode so reading via PRV_S should see them all as 0 */
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , 0);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0);
> +
> + /* Writing should leave them unchanged */
> + qtest_writel(qts, clicint12_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , default_reg_value);
> + qtest_writel(qts, clicint25_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
> + qtest_writel(qts, clicint26_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
> +
> + /*
> + * If we change IRQ 12 to PRV_S mode, we should now be able to see it
> + */
> + value = (default_reg_value & ~REG_MODE_MASK) | (PRV_S << REG_MODE_SHIFT);
> + qtest_writel(qts, clicint12_addr, value);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , value);
> +
> + /* ...but not the others */
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , 0);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_s), == , 0);
> +
> + /* We should also be able to write to it */
> + qtest_writel(qts, clicint12_addr_s, reg_value_2);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_s), == , reg_value_2);
> +
> + /* ...but not the others */
> + qtest_writel(qts, clicint25_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
> + qtest_writel(qts, clicint26_addr_s, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
> +
> + /*
> + * Now check the PRV_U view
> + */
> +
> + /* We should only see the PRV_U part of CLICCFG */
> + g_assert_cmpuint(qtest_readl(qts, UCLICCFG_ADDR), == , TEST_CFG_U(2));
> +
> + /* These are all PRV_M or PRV_S so reading via PRV_U should see them as 0 */
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_u), == , 0);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_u), == , 0);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_u), == , 0);
> +
> + /* Writing should leave them unchanged */
> + qtest_writel(qts, clicint12_addr_u, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2);
> + qtest_writel(qts, clicint25_addr_u, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , default_reg_value);
> + qtest_writel(qts, clicint26_addr_u, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
> +
> + /*
> + * If we change IRQ 25 to PRV_U mode, we should now be able to see it
> + * in both PRV_S and PRV_U modes
> + */
> + value = (default_reg_value & ~REG_MODE_MASK) | (PRV_U << REG_MODE_SHIFT);
> + qtest_writel(qts, clicint25_addr, value);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , value);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_u), == , value);
> +
> + /* ...we can't see the others in PRV_U */
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr_u), == , 0);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr_u), == , 0);
> +
> + /* We should also be able to write to it from both PRV_S and PRV_U */
> + qtest_writel(qts, clicint25_addr_s, reg_value_3);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , reg_value_3);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , reg_value_3);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_u), == , reg_value_3);
> + qtest_writel(qts, clicint25_addr_u, reg_value_4);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr), == , reg_value_4);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_s), == , reg_value_4);
> + g_assert_cmpuint(qtest_readl(qts, clicint25_addr_u), == , reg_value_4);
> +
> + /* ...but we still can't write to the others in PRV_U */
> + qtest_writel(qts, clicint12_addr_u, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint12_addr), == , reg_value_2);
> + qtest_writel(qts, clicint26_addr_u, 0x55555555);
> + g_assert_cmpuint(qtest_readl(qts, clicint26_addr), == , default_reg_value);
> +
> + qtest_quit(qts);
> +}
> +
> +/* Test configuration in PRV_M-only mode */
> +static void clic_configure_reg_mmio_test_case_m(void)
> +{
> + /* Start QEMU */
> + qtest_add_func("virt/clic/prv_m/boot_qemu_m",
> + boot_qemu_m);
> +
> + /* cliccfg configure case */
> + qtest_add_func("virt/clic/prv_m/cliccfg_min_mnlbits",
> + test_configure_cliccfg_min_mnlbits);
> + qtest_add_func("virt/clic/prv_m/cliccfg_supported_max_mnlbits",
> + test_configure_cliccfg_supported_max_mnlbits);
> + qtest_add_func("virt/clic/prv_m/cliccfg_unsupported_mnlbits",
> + test_configure_cliccfg_unsupported_mnlbits);
> + /* snlbits and unlbits should not work */
> + qtest_add_func("virt/clic/prv_m/cliccfg_snlbits_no_s",
> + test_configure_cliccfg_snlbits_no_s);
> + qtest_add_func("virt/clic/prv_m/cliccfg_snlbits_no_u",
> + test_configure_cliccfg_unlbits_no_u);
> + /* clicintip configure case */
> + qtest_add_func("virt/clic/prv_m/clicintip_level_triggered_readonly",
> + test_configure_clicintip_level_triggered_read_only);
> +
> + /* clicintie configure case */
> + qtest_add_func("virt/clic/prv_m/clicintie_enable",
> + test_configure_clicintie_enable);
> + qtest_add_func("virt/clic/prv_m/clicintie_disable",
> + test_configure_clicintie_disable);
> +
> + /* clicintattr mode configure cases are all PRV_M WARL - nmbits == 0*/
> + qtest_add_func("virt/clic/prv_m/cliccfg_nmbits_0_m",
> + test_configure_cliccfg_nmbits_0);
> + qtest_add_func("virt/clic/prv_m/intattr_prv_m",
> + test_configure_clicintattr_prv_m);
> + qtest_add_func("virt/clic/prv_m/intattr_prv_s_to_m_warl",
> + test_configure_clicintattr_prv_s_to_m_warl);
> + qtest_add_func("virt/clic/prv_m/intattr_prv_u_to_m_warl",
> + test_configure_clicintattr_prv_u_to_m_warl);
> + qtest_add_func("virt/clic/prv_m/intattr_unsupported_mode_10",
> + test_configure_clicintattr_unsupported_mode_10);
> +
> + /* unsupported nmbits */
> + qtest_add_func("virt/clic/prv_m/cliccfg_unsupported_nmbits_1_m",
> + test_configure_cliccfg_unsupported_nmbits_1);
> + qtest_add_func("virt/clic/prv_m/cliccfg_unsupported_nmbits_2_m",
> + test_configure_cliccfg_unsupported_nmbits_2);
> + qtest_add_func("virt/clic/prv_m/cliccfg_unsupported_nmbits_3_m",
> + test_configure_cliccfg_unsupported_nmbits_3);
> +
> + /* clicintattr TRIG and SHV */
> + qtest_add_func("virt/clic/prv_m/intattr_positive_edge_triggered",
> + test_configure_clicintattr_positive_edge_triggered);
> + qtest_add_func("virt/clic/prv_m/clicintattr_negative_edge_triggered",
> + test_configure_clicintattr_negative_edge_triggered);
> + qtest_add_func("virt/clic/prv_m/clicintattr_positive_level_triggered",
> + test_configure_clicintattr_positive_level_triggered);
> + qtest_add_func("virt/clic/prv_m/clicintattr_negative_level_triggered",
> + test_configure_clicintattr_negative_level_triggered);
> + qtest_add_func("virt/clic/prv_m/clicintattr_non_vectored",
> + test_configure_clicintattr_non_vectored);
> +
> + /* Shut down QEMU */
> + qtest_add_func("virt/clic/prv_m/shut_down_qemu_m",
> + shut_down_qemu);
> +}
> +
> +/* Test configuration in PRV_M + PRV_S mode */
> +static void clic_configure_reg_mmio_test_case_ms(void)
> +{
> + /* Start QEMU */
> + qtest_add_func("virt/clic/prv_ms/boot_qemu_ms",
> + boot_qemu_ms);
> +
> + /* cliccfg configure cases */
> +
> + /* mnlbits should be unaffected */
> + qtest_add_func("virt/clic/prv_ms/cliccfg_min_mnlbits_ms",
> + test_configure_cliccfg_min_mnlbits);
> + qtest_add_func("virt/clic/prv_ms/cliccfg_supported_max_mnlbits_ms",
> + test_configure_cliccfg_supported_max_mnlbits);
> + qtest_add_func("virt/clic/prv_ms/cliccfg_unsupported_mnlbits_ms",
> + test_configure_cliccfg_unsupported_mnlbits);
> + /* snlbits should work*/
> + qtest_add_func("virt/clic/prv_ms/cliccfg_min_snlbits_s_ms",
> + test_configure_cliccfg_min_snlbits_s);
> + qtest_add_func("virt/clic/prv_ms/cliccfg_supported_max_snlbits_s_ms",
> + test_configure_cliccfg_supported_max_snlbits_s);
> + qtest_add_func("virt/clic/prv_ms/cliccfg_unsupported_snlbits_s_ms",
> + test_configure_cliccfg_unsupported_snlbits_s);
> + /* unlbits should not work */
> + qtest_add_func("virt/clic/prv_ms/cliccfg_unlbits_no_u_ms",
> + test_configure_cliccfg_unlbits_no_u);
> +
> + /* clicintattr mode configure cases with nmbits = 1 */
> + qtest_add_func("virt/clic/prv_ms/cliccfg_nmbits_1_ms",
> + test_configure_cliccfg_nmbits_1);
> + qtest_add_func("virt/clic/prv_ms/intattr_prv_m_nmbits_1",
> + test_configure_clicintattr_prv_m);
> + qtest_add_func("virt/clic/prv_ms/intattr_unsupported_mode_10_nmbits_1",
> + test_configure_clicintattr_unsupported_mode_10);
> + qtest_add_func("virt/clic/prv_ms/intattr_prv_u_to_s_warl_nmbits_1_prv_m",
> + test_configure_clicintattr_prv_u_to_s_warl);
> + qtest_add_func("virt/clic/prv_ms/intattr_prv_s_supported_nmbits_1",
> + test_configure_clicintattr_prv_s_supported);
> + qtest_add_func("virt/clic/prv_ms/intattr_prv_u_to_s_warl_nmbits_1_prv_s",
> + test_configure_clicintattr_prv_u_to_s_warl);
> +
> + /* clicintattr mode configure cases with nmbits = 0 - PRV_M only */
> + qtest_add_func("virt/clic/prv_ms/cliccfg_nmbits_0_ms",
> + test_configure_cliccfg_nmbits_0);
> + qtest_add_func("virt/clic/prv_ms/intattr_prv_m_nmbits_0",
> + test_configure_clicintattr_prv_m);
> + qtest_add_func("virt/clic/prv_ms/intattr_unsupported_mode_10_nmbits_0",
> + test_configure_clicintattr_unsupported_mode_10);
> + qtest_add_func("virt/clic/prv_ms/intattr_prv_u_to_m_warl_nmbits_0",
> + test_configure_clicintattr_prv_u_to_m_warl);
> + qtest_add_func("virt/clic/prv_ms/intattr_prv_s_to_m_warl_nmbits_0",
> + test_configure_clicintattr_prv_s_to_m_warl);
> +
> + /* unsupported nmbits */
> + qtest_add_func("virt/clic/prv_ms/cliccfg_unsupported_nmbits_2_ms",
> + test_configure_cliccfg_unsupported_nmbits_2);
> + qtest_add_func("virt/clic/prv_ms/cliccfg_unsupported_nmbits_3_ms",
> + test_configure_cliccfg_unsupported_nmbits_3);
> +
> + /* clicintattr TRIG and SHV */
> + qtest_add_func("virt/clic/prv_ms/intattr_positive_edge_triggered",
> + test_configure_clicintattr_positive_edge_triggered);
> + qtest_add_func("virt/clic/prv_ms/clicintattr_negative_edge_triggered",
> + test_configure_clicintattr_negative_edge_triggered);
> + qtest_add_func("virt/clic/prv_ms/clicintattr_positive_level_triggered",
> + test_configure_clicintattr_positive_level_triggered);
> + qtest_add_func("virt/clic/prv_ms/clicintattr_negative_level_triggered",
> + test_configure_clicintattr_negative_level_triggered);
> + qtest_add_func("virt/clic/prv_ms/clicintattr_non_vectored",
> + test_configure_clicintattr_non_vectored);
> +
> + /* Shut down QEMU */
> + qtest_add_func("virt/clic/prv_ms/shut_down_qemu_ms",
> + shut_down_qemu);
> +}
> +
> +/* Test configuration in PRV_M + PRV_U mode */
> +static void clic_configure_reg_mmio_test_case_mu(void)
> +{
> + /* Start QEMU */
> + qtest_add_func("virt/clic/prv_mu/boot_qemu_mu",
> + boot_qemu_mu);
> +
> + /* cliccfg configure cases */
> +
> + /* mnlbits should be unaffected */
> + qtest_add_func("virt/clic/prv_mu/cliccfg_min_mnlbits_mu",
> + test_configure_cliccfg_min_mnlbits);
> + qtest_add_func("virt/clic/prv_mu/cliccfg_supported_max_mnlbits_mu",
> + test_configure_cliccfg_supported_max_mnlbits);
> + qtest_add_func("virt/clic/prv_mu/cliccfg_unsupported_mnlbits_mu",
> + test_configure_cliccfg_unsupported_mnlbits);
> + /* snlbits should not work */
> + qtest_add_func("virt/clic/prv_mu/cliccfg_snlbits_no_s_mu",
> + test_configure_cliccfg_snlbits_no_s);
> + /* unlbits should work*/
> + qtest_add_func("virt/clic/prv_mu/cliccfg_min_unlbits_u_mu",
> + test_configure_cliccfg_min_unlbits_u);
> + qtest_add_func("virt/clic/prv_mu/cliccfg_uupported_max_unlbits_u_mu",
> + test_configure_cliccfg_supported_max_unlbits_u);
> + qtest_add_func("virt/clic/prv_mu/cliccfg_unsupported_unlbits_u_mu",
> + test_configure_cliccfg_unsupported_unlbits_u);
> +
> + /* clicintattr mode configure cases with nmbits = 1 */
> + qtest_add_func("virt/clic/prv_mu/cliccfg_nmbits_1_ms",
> + test_configure_cliccfg_nmbits_1);
> + qtest_add_func("virt/clic/prv_mu/intattr_prv_m_nmbits_1",
> + test_configure_clicintattr_prv_m);
> + qtest_add_func("virt/clic/prv_mu/intattr_unsupported_mode_10_nmbits_1",
> + test_configure_clicintattr_unsupported_mode_10);
> + qtest_add_func("virt/clic/prv_mu/intattr_prv_s_to_u_warl_nmbits_1_prv_m",
> + test_configure_clicintattr_prv_s_to_u_warl);
> + qtest_add_func("virt/clic/prv_mu/intattr_prv_u_supported_nmbits_1",
> + test_configure_clicintattr_prv_u_supported);
> + qtest_add_func("virt/clic/prv_mu/intattr_prv_s_to_u_warl_nmbits_1_prv_u",
> + test_configure_clicintattr_prv_s_to_u_warl);
> +
> + /* clicintattr mode configure cases with nmbits = 0 - PRV_M only */
> + qtest_add_func("virt/clic/prv_mu/cliccfg_nmbits_0_ms",
> + test_configure_cliccfg_nmbits_0);
> + qtest_add_func("virt/clic/prv_mu/intattr_prv_m_nmbits_0",
> + test_configure_clicintattr_prv_m);
> + qtest_add_func("virt/clic/prv_mu/intattr_unsupported_mode_10_nmbits_0",
> + test_configure_clicintattr_unsupported_mode_10);
> + qtest_add_func("virt/clic/prv_mu/intattr_prv_u_to_m_warl_nmbits_0",
> + test_configure_clicintattr_prv_u_to_m_warl);
> + qtest_add_func("virt/clic/prv_mu/intattr_prv_s_to_m_warl_nmbits_0",
> + test_configure_clicintattr_prv_s_to_m_warl);
> +
> + /* unsupported nmbits */
> + qtest_add_func("virt/clic/prv_mu/cliccfg_unsupported_nmbits_2_ms",
> + test_configure_cliccfg_unsupported_nmbits_2);
> + qtest_add_func("virt/clic/prv_mu/cliccfg_unsupported_nmbits_3_ms",
> + test_configure_cliccfg_unsupported_nmbits_3);
> +
> + /* clicintattr TRIG and SHV */
> + qtest_add_func("virt/clic/prv_mu/intattr_positive_edge_triggered",
> + test_configure_clicintattr_positive_edge_triggered);
> + qtest_add_func("virt/clic/prv_mu/clicintattr_negative_edge_triggered",
> + test_configure_clicintattr_negative_edge_triggered);
> + qtest_add_func("virt/clic/prv_mu/clicintattr_positive_level_triggered",
> + test_configure_clicintattr_positive_level_triggered);
> + qtest_add_func("virt/clic/prv_mu/clicintattr_negative_level_triggered",
> + test_configure_clicintattr_negative_level_triggered);
> + qtest_add_func("virt/clic/prv_mu/clicintattr_non_vectored",
> + test_configure_clicintattr_non_vectored);
> +
> + /* Shut down QEMU */
> + qtest_add_func("virt/clic/prv_mu/shut_down_qemu_mu",
> + shut_down_qemu);
> +}
> +
> +/* Test configuration in PRV_M + PRV_S + PRV_U mode */
> +static void clic_configure_reg_mmio_test_case_msu(void)
> +{
> + /* Start QEMU */
> + qtest_add_func("virt/clic/prv_msu/boot_qemu_msu",
> + boot_qemu_msu);
> +
> + /* cliccfg configure cases */
> +
> + /* mnlbits should be unaffected */
> + qtest_add_func("virt/clic/prv_msu/cliccfg_min_mnlbits_msu",
> + test_configure_cliccfg_min_mnlbits);
> + qtest_add_func("virt/clic/prv_msu/cliccfg_supported_max_mnlbits_msu",
> + test_configure_cliccfg_supported_max_mnlbits);
> + qtest_add_func("virt/clic/prv_msu/cliccfg_unsupported_mnlbits_msu",
> + test_configure_cliccfg_unsupported_mnlbits);
> + /* snlbits should work*/
> + qtest_add_func("virt/clic/prv_msu/cliccfg_min_snlbits_s_msu",
> + test_configure_cliccfg_min_snlbits_s);
> + qtest_add_func("virt/clic/prv_msu/cliccfg_supported_max_snlbits_s_msu",
> + test_configure_cliccfg_supported_max_snlbits_s);
> + qtest_add_func("virt/clic/prv_msu/cliccfg_unsupported_snlbits_s_msu",
> + test_configure_cliccfg_unsupported_snlbits_s);
> + /* unlbits should work*/
> + qtest_add_func("virt/clic/prv_msu/cliccfg_min_unlbits_u_msu",
> + test_configure_cliccfg_min_unlbits_u);
> + qtest_add_func("virt/clic/prv_msu/cliccfg_uupported_max_unlbits_u_msu",
> + test_configure_cliccfg_supported_max_unlbits_u);
> + qtest_add_func("virt/clic/prv_msu/cliccfg_unsupported_unlbits_u_msu",
> + test_configure_cliccfg_unsupported_unlbits_u);
> + /* all bits should work */
> + qtest_add_func("virt/clic/prv_msu/cliccfg_xnlbits_msu",
> + test_configure_cliccfg_xnlbits);
> +
> + /* clicintattr mode configure cases with nmbits = 2 => all modes */
> + qtest_add_func("virt/clic/prv_msu/cliccfg_nmbits_2_ms",
> + test_configure_cliccfg_nmbits_2);
> + qtest_add_func("virt/clic/prv_msu/intattr_prv_m_nmbits_2",
> + test_configure_clicintattr_prv_m);
> + qtest_add_func("virt/clic/prv_msu/intattr_unsupported_mode_10_nmbits_2",
> + test_configure_clicintattr_unsupported_mode_10);
> + qtest_add_func("virt/clic/prv_msu/intattr_prv_s_supported_nmbits_2",
> + test_configure_clicintattr_prv_s_supported);
> + qtest_add_func("virt/clic/prv_msu/intattr_prv_u_supported_nmbits_2",
> + test_configure_clicintattr_prv_u_supported);
> +
> + /* clicintattr mode configure cases with nmbits = 1 => PRV_M and PRV_S */
> + qtest_add_func("virt/clic/prv_msu/cliccfg_nmbits_1_ms",
> + test_configure_cliccfg_nmbits_1);
> + qtest_add_func("virt/clic/prv_msu/intattr_prv_m_nmbits_1",
> + test_configure_clicintattr_prv_m);
> + qtest_add_func("virt/clic/prv_msu/intattr_unsupported_mode_10_nmbits_1",
> + test_configure_clicintattr_unsupported_mode_10);
> + qtest_add_func("virt/clic/prv_msu/intattr_prv_u_to_s_warl_nmbits_1_prv_m",
> + test_configure_clicintattr_prv_u_to_s_warl);
> + qtest_add_func("virt/clic/prv_msu/intattr_prv_s_supported_nmbits_1",
> + test_configure_clicintattr_prv_s_supported);
> + qtest_add_func("virt/clic/prv_msu/intattr_prv_u_to_s_warl_nmbits_1_prv_s",
> + test_configure_clicintattr_prv_u_to_s_warl);
> +
> + /* clicintattr mode configure cases with nmbits = 0 - PRV_M only */
> + qtest_add_func("virt/clic/prv_msu/cliccfg_nmbits_0_ms",
> + test_configure_cliccfg_nmbits_0);
> + qtest_add_func("virt/clic/prv_msu/intattr_prv_m_nmbits_0",
> + test_configure_clicintattr_prv_m);
> + qtest_add_func("virt/clic/prv_msu/intattr_unsupported_mode_10_nmbits_0",
> + test_configure_clicintattr_unsupported_mode_10);
> + qtest_add_func("virt/clic/prv_msu/intattr_prv_u_to_m_warl_nmbits_0",
> + test_configure_clicintattr_prv_u_to_m_warl);
> + qtest_add_func("virt/clic/prv_msu/intattr_prv_s_to_m_warl_nmbits_0",
> + test_configure_clicintattr_prv_s_to_m_warl);
> +
> + /* unsupported nmbits */
> + qtest_add_func("virt/clic/prv_msu/cliccfg_unsupported_nmbits_3_ms",
> + test_configure_cliccfg_unsupported_nmbits_3);
> +
> + /* clicintattr TRIG and SHV */
> + qtest_add_func("virt/clic/prv_msu/intattr_positive_edge_triggered",
> + test_configure_clicintattr_positive_edge_triggered);
> + qtest_add_func("virt/clic/prv_msu/clicintattr_negative_edge_triggered",
> + test_configure_clicintattr_negative_edge_triggered);
> + qtest_add_func("virt/clic/prv_msu/clicintattr_positive_level_triggered",
> + test_configure_clicintattr_positive_level_triggered);
> + qtest_add_func("virt/clic/prv_msu/clicintattr_negative_level_triggered",
> + test_configure_clicintattr_negative_level_triggered);
> + qtest_add_func("virt/clic/prv_msu/clicintattr_non_vectored",
> + test_configure_clicintattr_non_vectored);
> +
> + /* Shut down QEMU */
> + qtest_add_func("virt/clic/prv_msu/shut_down_qemu_msu",
> + shut_down_qemu);
> +}
> +
> +#define GEN_INTCTL_TEST(_nbits) \
> +GEN_BOOT_QEMU_INTCTL(_nbits) \
> +static void clic_configure_clicintctl_test_case_##_nbits##_bits(void) \
> +{ \
> + g_autofree char *prefix = g_strdup_printf("virt/clic/clicintl_%d_bits", \
> + _nbits); \
> + g_autofree char *path; \
> + \
> + path = g_strdup_printf("%s/boot_qemu", prefix); \
> + qtest_add_func(path, boot_qemu_##_nbits##_bits); \
> + \
> + path = g_strdup_printf("%s/intctl_0_%d_bits", prefix, _nbits); \
> + qtest_add_func(path, \
> + test_configure_clicintctl_set_0_##_nbits##_bits); \
> + path = g_strdup_printf("%s/intctl_33_%d_bits", prefix, _nbits); \
> + qtest_add_func(path, \
> + test_configure_clicintctl_set_33_##_nbits##_bits); \
> + path = g_strdup_printf("%s/intctl_88_%d_bits", prefix, _nbits); \
> + qtest_add_func(path, \
> + test_configure_clicintctl_set_88_##_nbits##_bits); \
> + path = g_strdup_printf("%s/intctl_128_%d_bits", prefix, _nbits); \
> + qtest_add_func(path, \
> + test_configure_clicintctl_set_128_##_nbits##_bits); \
> + path = g_strdup_printf("%s/intctl_204_%d_bits", prefix, _nbits); \
> + qtest_add_func(path, \
> + test_configure_clicintctl_set_204_##_nbits##_bits); \
> + path = g_strdup_printf("%s/intctl_240_%d_bits", prefix, _nbits); \
> + qtest_add_func(path, \
> + test_configure_clicintctl_set_240_##_nbits##_bits); \
> + \
> + path = g_strdup_printf("%s/shut_down_qemu", prefix); \
> + qtest_add_func(path, shut_down_qemu); \
> +}
> +
> +GEN_INTCTL_TEST(0)
> +GEN_INTCTL_TEST(1)
> +GEN_INTCTL_TEST(2)
> +GEN_INTCTL_TEST(3)
> +GEN_INTCTL_TEST(4)
> +GEN_INTCTL_TEST(5)
> +GEN_INTCTL_TEST(6)
> +GEN_INTCTL_TEST(7)
> +GEN_INTCTL_TEST(8)
> +
> +static void clic_irq_test_case(void)
> +{
> + /* interrupt test case */
> + qtest_add_func("virt/clic/vectored_positive_level_triggered_interrupt",
> + test_vectored_positive_level_triggered_interrupt);
> + qtest_add_func("virt/clic/vectored_negative_level_triggered_interrupt",
> + test_vectored_negative_level_triggered_interrupt);
> + qtest_add_func("virt/clic/vectored_positive_edge_triggered_interrupt",
> + test_vectored_positive_edge_triggered_interrupt);
> + qtest_add_func("virt/clic/vectored_negative_edge_triggered_interrupt",
> + test_vectored_negative_edge_triggered_interrupt);
> + qtest_add_func("virt/clic/unvectored_positive_level_triggered_interrupt",
> + test_unvectored_positive_level_triggered_interrupt);
> + qtest_add_func("virt/clic/unvectored_negative_level_triggered_interrupt",
> + test_unvectored_negative_level_triggered_interrupt);
> + qtest_add_func("virt/clic/unvectored_positive_edge_triggered_interrupt",
> + test_unvectored_positive_edge_triggered_interrupt);
> + qtest_add_func("virt/clic/unvectored_negative_edge_triggered_interrupt",
> + test_unvectored_negative_edge_triggered_interrupt);
> +}
> +
> +static void clic_mode_access_test_case(void)
> +{
> + qtest_add_func("virt/clic/test_prv_s_access", test_prv_s_access);
> + qtest_add_func("virt/clic/test_prv_u_access", test_prv_u_access);
> + qtest_add_func("virt/clic/test_prv_su_access", test_prv_su_access);
> +}
> +
> +int main(int argc, char **argv)
> +{
> + g_test_init(&argc, &argv, NULL);
> + g_test_set_nonfatal_assertions();
> +
> + /* test cases */
> + clic_configure_reg_mmio_test_case_m();
> + clic_configure_reg_mmio_test_case_ms();
> + clic_configure_reg_mmio_test_case_mu();
> + clic_configure_reg_mmio_test_case_msu();
> + clic_configure_clicintctl_test_case_0_bits();
> + clic_configure_clicintctl_test_case_1_bits();
> + clic_configure_clicintctl_test_case_2_bits();
> + clic_configure_clicintctl_test_case_3_bits();
> + clic_configure_clicintctl_test_case_4_bits();
> + clic_configure_clicintctl_test_case_5_bits();
> + clic_configure_clicintctl_test_case_6_bits();
> + clic_configure_clicintctl_test_case_7_bits();
> + clic_configure_clicintctl_test_case_8_bits();
> + clic_irq_test_case();
> + clic_mode_access_test_case();
> +
> + /* Run the tests */
> + int ret = g_test_run();
> +
> + return ret;
> +}
> --
> 2.46.0.windows.1
> This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH 00/11 v2] RISC-V: support CLIC v0.9 specification
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
` (12 preceding siblings ...)
2024-09-06 2:48 ` Alistair Francis
@ 2024-09-06 3:56 ` Alistair Francis
13 siblings, 0 replies; 32+ messages in thread
From: Alistair Francis @ 2024-09-06 3:56 UTC (permalink / raw)
To: Ian Brockbank
Cc: qemu-devel, qemu-riscv, Palmer Dabbelt, Alistair Francis,
Bin Meng, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei
On Tue, Aug 20, 2024 at 2:08 AM Ian Brockbank <Ian.Brockbank@cirrus.com> wrote:
>
> [Resubmission now the merge is correct]
>
> This patch set gives an implementation of "RISC-V Core-Local Interrupt
> Controller(CLIC) Version 0.9-draft-20210217". It comes from [1], where
> you can find the pdf format or the source code.
Thanks for the patches!
I left some comments on the cover letter and throughout the series.
The main points are that we need to support a release spec (draft is
ok, but it needs to be tagged as a version). On top of that we want to
try and keep the CSR changes in target/riscv. We don't want to be
including the hw/intc code all over the place. time_helper.c for
example is how the ACLINT works, where we try to keep as much of the
CPU code in target/riscv.
Also we will need extension checks for all of the relevant CLIC
extensions. Guest code should not notice any changes after this series
unless they enable the CLIC extensions.
Alistair
>
> This is based on the implementation from 2021 by Liu Zhiwei [3], who took
> over the job from Michael Clark, who gave the first implementation of
> clic-v0.7 specification [2]. I believe this implementation addresses all
> the comments in Liu Zhiwei's RFC patch thread.
>
> This implementation follows the CLIC 0.9-stable draft at 14 March 2024,
> with the following exceptions and implementation details:
> - the CLIC control registers are memory-mapped as per earlier drafts (in
> particular version 0.9-draft, 20 June 2023)
> - the indirect CSR control in 0.9-stable is not implemented
> - the vector table can be either handler addresses (as per the spec)
> or a jump table where each entry is processed as an instruction,
> selectable with version number v0.9-jmp
> - each hart is assigned its own CLIC block
> - if PRV_S and/or PRV_M are supported, they are currently assumed to follow
> the PRV_M registers; a subsequent update will address this
> - support for PRV_S and PRV_M is selectable at CLIC instantiation
> - PRV_S and PRV_U registers are currently separate from PRV_M; a subsequent
> update will turn them into filtered views onto the PRV_M registers
> - each hart is assigned its own CLIC block
> - support for PRV_S and PRV_M is selectable at CLIC instantiation by
> passing in a base address for the given modes; a base address of 0 is
> treated as not supported
> - PRV_S and PRV_U registers are mapped onto the PRV_M controls with
> appropriate filtering for the access mode
> - the RISCV virt machine has been updated to allow CLIC emulation by
> passing "machine=virt,clic=on" on the command line; various other
> parameters have been added to allow finer control of the CLIC behavior
>
> The implementation (in jump-table mode) has been verified to match the
> Cirrus Logic silicon (PRV_M only), which is based upon the Pulp
> implementation [4] as of June 2023.
>
> The implementation also includes a selection of qtests designed to verify
> operation in all possible combinations of PRV_M, PRV_S and PRV_U.
>
> [1] specification website: https://github.com/riscv/riscv-fast-interrupt.
> [2] Michael Clark origin work:
> https://github.com/sifive/riscv-qemu/tree/sifive-clic.
> [3] RFC Patch submission by Liu Zhiwei:
> https://lists.gnu.org/archive/html/qemu-devel/2021-04/msg01417.html
> [4] Pulp implementation of CLIC: https://github.com/pulp-platform/clic
>
> Ian Brockbank (11):
> target/riscv: Add CLIC CSR mintstatus
> target/riscv: Update CSR xintthresh in CLIC mode
> hw/intc: Add CLIC device
> target/riscv: Update CSR xie in CLIC mode
> target/riscv: Update CSR xip in CLIC mode
> target/riscv: Update CSR xtvec in CLIC mode
> target/riscv: Update CSR xnxti in CLIC mode
> target/riscv: Update interrupt handling in CLIC mode
> target/riscv: Update interrupt return in CLIC mode
> hw/riscv: add CLIC into virt machine
> tests: add riscv clic qtest case and a function in qtest
>
> This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
>
^ permalink raw reply [flat|nested] 32+ messages in thread
end of thread, other threads:[~2024-09-06 3:57 UTC | newest]
Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <https://lists.gnu.org/archive/html/qemu-riscv/2024-08/msg00234.html>
2024-08-14 8:27 ` [PATCH 00/11] RISC-V: support CLIC v0.9 specification Ian Brockbank
2024-08-14 8:27 ` [PATCH 03/11] hw/intc: Add CLIC device Ian Brockbank
2024-08-14 8:27 ` [PATCH 02/11] target/riscv: Update CSR xintthresh in CLIC mode Ian Brockbank
2024-08-14 8:27 ` [PATCH 01/11] target/riscv: Add CLIC CSR mintstatus Ian Brockbank
2024-08-14 8:27 ` [PATCH 04/11] target/riscv: Update CSR xie in CLIC mode Ian Brockbank
2024-08-14 8:27 ` [PATCH 05/11] target/riscv: Update CSR xip " Ian Brockbank
2024-08-14 8:27 ` [PATCH 07/11] target/riscv: Update CSR xnxti " Ian Brockbank
2024-08-14 8:27 ` [PATCH 06/11] target/riscv: Update CSR xtvec " Ian Brockbank
2024-08-14 14:11 ` [PATCH 00/11] RISC-V: support CLIC v0.9 specification Ian Brockbank
2024-08-19 16:02 ` [PATCH 00/11 v2] " Ian Brockbank
2024-08-19 16:02 ` [PATCH 01/11 v2] target/riscv: Add CLIC CSR mintstatus Ian Brockbank
2024-09-06 2:44 ` Alistair Francis
2024-09-06 3:04 ` Alistair Francis
2024-08-19 16:02 ` [PATCH 02/11 v2] target/riscv: Update CSR xintthresh in CLIC mode Ian Brockbank
2024-09-06 2:52 ` Alistair Francis
2024-08-19 16:02 ` [PATCH 03/11 v2] hw/intc: Add CLIC device Ian Brockbank
2024-08-19 16:02 ` [PATCH 04/11 v2] target/riscv: Update CSR xie in CLIC mode Ian Brockbank
2024-09-06 2:58 ` Alistair Francis
2024-09-06 3:20 ` Alistair Francis
2024-08-19 16:02 ` [PATCH 05/11 v2] target/riscv: Update CSR xip " Ian Brockbank
2024-08-19 16:02 ` [PATCH 06/11 v2] target/riscv: Update CSR xtvec " Ian Brockbank
2024-09-06 3:02 ` Alistair Francis
2024-08-19 16:02 ` [PATCH 07/11 v2] target/riscv: Update CSR xnxti " Ian Brockbank
2024-08-19 16:02 ` [PATCH 08/11 v2] target/riscv: Update interrupt handling " Ian Brockbank
2024-09-06 3:49 ` Alistair Francis
2024-08-19 16:02 ` [PATCH 09/11 v2] target/riscv: Update interrupt return " Ian Brockbank
2024-08-19 16:02 ` [PATCH 10/11 v2] hw/riscv: add CLIC into virt machine Ian Brockbank
2024-08-19 16:02 ` [PATCH 11/11 v2] tests: add riscv clic qtest case and a function in qtest Ian Brockbank
2024-09-06 3:52 ` Alistair Francis
2024-09-04 7:57 ` [PATCH 00/11 v2] RISC-V: support CLIC v0.9 specification Ian Brockbank
2024-09-06 2:48 ` Alistair Francis
2024-09-06 3:56 ` Alistair Francis
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).