All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 00/11] RISC-V: support clic v0.9 specification
@ 2021-04-09  7:48 LIU Zhiwei
  2021-04-09  7:48 ` [RFC PATCH 01/11] target/riscv: Add CLIC CSR mintstatus LIU Zhiwei
                   ` (12 more replies)
  0 siblings, 13 replies; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-09  7:48 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv; +Cc: palmer, Alistair.Francis, LIU Zhiwei, wxy194768

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.

I take over the job from Michael Clark, who gave the first implementation
of clic-v0.7 specification. If there is any copyright question, please
let me know.

Features:
1. support four kinds of trigger types.
2. Preserve the CSR WARL/WPRI semantics. 
3. Option to select different modes, such as M/S/U or M/U.
4. At most 4096 interrupts.
5. At most 1024 apertures.

Todo:
1. Encode the interrupt trigger information to exccode.
2. Support complete CSR mclicbase when its number is fixed. 
3. Gave each aperture an independend address.

It have passed my qtest case and freertos test. Welcome to have a try
for your hardware.

Any advice is welcomed. Thanks very much.

Zhiwei

[1] specification website: https://github.com/riscv/riscv-fast-interrupt.
[2] Michael Clark origin work: https://github.com/sifive/riscv-qemu/tree/sifive-clic.


LIU Zhiwei (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 xtvt in CLIC mode
  target/riscv: Update CSR xnxti in CLIC mode
  target/riscv: Update CSR mclicbase in CLIC mode
  target/riscv: Update interrupt handling in CLIC mode
  target/riscv: Update interrupt return in CLIC mode

 default-configs/devices/riscv32-softmmu.mak |   1 +
 default-configs/devices/riscv64-softmmu.mak |   1 +
 hw/intc/Kconfig                             |   3 +
 hw/intc/meson.build                         |   1 +
 hw/intc/riscv_clic.c                        | 836 ++++++++++++++++++++
 include/hw/intc/riscv_clic.h                | 103 +++
 target/riscv/cpu.h                          |   9 +
 target/riscv/cpu_bits.h                     |  32 +
 target/riscv/cpu_helper.c                   | 117 ++-
 target/riscv/csr.c                          | 247 +++++-
 target/riscv/op_helper.c                    |  25 +
 11 files changed, 1363 insertions(+), 12 deletions(-)
 create mode 100644 hw/intc/riscv_clic.c
 create mode 100644 include/hw/intc/riscv_clic.h

-- 
2.25.1



^ permalink raw reply	[flat|nested] 81+ messages in thread

* [RFC PATCH 01/11] target/riscv: Add CLIC CSR mintstatus
  2021-04-09  7:48 [RFC PATCH 00/11] RISC-V: support clic v0.9 specification LIU Zhiwei
@ 2021-04-09  7:48 ` LIU Zhiwei
  2021-04-19 23:23   ` Alistair Francis
  2021-04-09  7:48 ` [RFC PATCH 02/11] target/riscv: Update CSR xintthresh in CLIC mode LIU Zhiwei
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-09  7:48 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv; +Cc: palmer, Alistair.Francis, LIU Zhiwei, wxy194768

CSR mintstatus holds the active interrupt level for each supported
privilege mode. sintstatus, and user, uintstatus, provide restricted
views of mintstatus.

Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
---
 target/riscv/cpu.h      |  2 ++
 target/riscv/cpu_bits.h | 11 +++++++++++
 target/riscv/csr.c      | 26 ++++++++++++++++++++++++++
 3 files changed, 39 insertions(+)

diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 0a33d387ba..1a44ca62c7 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -159,6 +159,7 @@ struct CPURISCVState {
     target_ulong mip;
 
     uint32_t miclaim;
+    uint32_t mintstatus; /* clic-spec */
 
     target_ulong mie;
     target_ulong mideleg;
@@ -243,6 +244,7 @@ struct CPURISCVState {
 
     /* Fields from here on are preserved across CPU reset. */
     QEMUTimer *timer; /* Internal timer */
+    void *clic;       /* clic interrupt controller */
 };
 
 OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index caf4599207..c4ce6ec3d9 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      0x346 /* clic-spec-draft */
 
 /* Legacy Machine Trap Handling (priv v1.9.1) */
 #define CSR_MBADADDR        0x343
@@ -183,6 +184,7 @@
 #define CSR_SCAUSE          0x142
 #define CSR_STVAL           0x143
 #define CSR_SIP             0x144
+#define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
 
 /* Legacy Supervisor Trap Handling (priv v1.9.1) */
 #define CSR_SBADADDR        0x143
@@ -585,6 +587,15 @@
 #define SIP_STIP                           MIP_STIP
 #define SIP_SEIP                           MIP_SEIP
 
+/* mintstatus */
+#define MINTSTATUS_MIL                     0xff000000 /* mil[7:0] */
+#define MINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
+#define MINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
+
+/* sintstatus */
+#define SINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
+#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 d2585395bf..320b18ab60 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -188,6 +188,12 @@ static int pmp(CPURISCVState *env, int csrno)
 {
     return -!riscv_feature(env, RISCV_FEATURE_PMP);
 }
+
+static int clic(CPURISCVState *env, int csrno)
+{
+    return !!env->clic;
+}
+
 #endif
 
 /* User Floating-Point CSRs */
@@ -734,6 +740,12 @@ static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value,
     return 0;
 }
 
+static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
+{
+    *val = env->mintstatus;
+    return 0;
+}
+
 /* Supervisor Trap Setup */
 static int read_sstatus(CPURISCVState *env, int csrno, target_ulong *val)
 {
@@ -893,6 +905,13 @@ static int rmw_sip(CPURISCVState *env, int csrno, target_ulong *ret_value,
     return ret;
 }
 
+static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
+{
+    target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
+    *val = env->mintstatus & mask;
+    return 0;
+}
+
 /* Supervisor Protection and Translation */
 static int read_satp(CPURISCVState *env, int csrno, target_ulong *val)
 {
@@ -1644,5 +1663,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
     [CSR_MHPMCOUNTER29H] = { "mhpmcounter29h", any32,  read_zero },
     [CSR_MHPMCOUNTER30H] = { "mhpmcounter30h", any32,  read_zero },
     [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", any32,  read_zero },
+
+    /* Machine Mode Core Level Interrupt Controller */
+    [CSR_MINTSTATUS] = { "mintstatus", clic,  read_mintstatus },
+
+    /* Supervisor Mode Core Level Interrupt Controller */
+    [CSR_SINTSTATUS] = { "sintstatus", clic,  read_sintstatus },
+
 #endif /* !CONFIG_USER_ONLY */
 };
-- 
2.25.1



^ permalink raw reply	[flat|nested] 81+ messages in thread

* [RFC PATCH 02/11] target/riscv: Update CSR xintthresh in CLIC mode
  2021-04-09  7:48 [RFC PATCH 00/11] RISC-V: support clic v0.9 specification LIU Zhiwei
  2021-04-09  7:48 ` [RFC PATCH 01/11] target/riscv: Add CLIC CSR mintstatus LIU Zhiwei
@ 2021-04-09  7:48 ` LIU Zhiwei
  2021-06-26 17:23   ` Frank Chang
  2021-04-09  7:48 ` [RFC PATCH 03/11] hw/intc: Add CLIC device LIU Zhiwei
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-09  7:48 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv; +Cc: palmer, Alistair.Francis, LIU Zhiwei, wxy194768

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: 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 1a44ca62c7..a5eab26a69 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -160,6 +160,7 @@ struct CPURISCVState {
 
     uint32_t miclaim;
     uint32_t mintstatus; /* clic-spec */
+    target_ulong mintthresh; /* clic-spec */
 
     target_ulong mie;
     target_ulong mideleg;
@@ -173,6 +174,7 @@ struct CPURISCVState {
     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 c4ce6ec3d9..9447801d22 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      0x346 /* clic-spec-draft */
+#define CSR_MINTTHRESH      0x347 /* clic-spec-draft */
 
 /* Legacy Machine Trap Handling (priv v1.9.1) */
 #define CSR_MBADADDR        0x343
@@ -185,6 +186,7 @@
 #define CSR_STVAL           0x143
 #define CSR_SIP             0x144
 #define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
+#define CSR_SINTTHRESH      0x147 /* clic-spec-draft */
 
 /* Legacy Supervisor Trap Handling (priv v1.9.1) */
 #define CSR_SBADADDR        0x143
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 320b18ab60..4c31364967 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -746,6 +746,18 @@ static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
     return 0;
 }
 
+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 int read_sstatus(CPURISCVState *env, int csrno, target_ulong *val)
 {
@@ -912,6 +924,18 @@ static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
     return 0;
 }
 
+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 int read_satp(CPURISCVState *env, int csrno, target_ulong *val)
 {
@@ -1666,9 +1690,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 },
 
 #endif /* !CONFIG_USER_ONLY */
 };
-- 
2.25.1



^ permalink raw reply	[flat|nested] 81+ messages in thread

* [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-04-09  7:48 [RFC PATCH 00/11] RISC-V: support clic v0.9 specification LIU Zhiwei
  2021-04-09  7:48 ` [RFC PATCH 01/11] target/riscv: Add CLIC CSR mintstatus LIU Zhiwei
  2021-04-09  7:48 ` [RFC PATCH 02/11] target/riscv: Update CSR xintthresh in CLIC mode LIU Zhiwei
@ 2021-04-09  7:48 ` LIU Zhiwei
  2021-04-19 23:25   ` Alistair Francis
                     ` (7 more replies)
  2021-04-09  7:48 ` [RFC PATCH 04/11] target/riscv: Update CSR xie in CLIC mode LIU Zhiwei
                   ` (9 subsequent siblings)
  12 siblings, 8 replies; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-09  7:48 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv; +Cc: palmer, Alistair.Francis, LIU Zhiwei, wxy194768

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>
---
 default-configs/devices/riscv32-softmmu.mak |   1 +
 default-configs/devices/riscv64-softmmu.mak |   1 +
 hw/intc/Kconfig                             |   3 +
 hw/intc/meson.build                         |   1 +
 hw/intc/riscv_clic.c                        | 835 ++++++++++++++++++++
 include/hw/intc/riscv_clic.h                | 103 +++
 target/riscv/cpu.h                          |   2 +
 7 files changed, 946 insertions(+)
 create mode 100644 hw/intc/riscv_clic.c
 create mode 100644 include/hw/intc/riscv_clic.h

diff --git a/default-configs/devices/riscv32-softmmu.mak b/default-configs/devices/riscv32-softmmu.mak
index d847bd5692..1430c30588 100644
--- a/default-configs/devices/riscv32-softmmu.mak
+++ b/default-configs/devices/riscv32-softmmu.mak
@@ -5,6 +5,7 @@
 #CONFIG_PCI_DEVICES=n
 CONFIG_SEMIHOSTING=y
 CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
+CONFIG_RISCV_CLIC=y
 
 # Boards:
 #
diff --git a/default-configs/devices/riscv64-softmmu.mak b/default-configs/devices/riscv64-softmmu.mak
index d5eec75f05..396800bbbd 100644
--- a/default-configs/devices/riscv64-softmmu.mak
+++ b/default-configs/devices/riscv64-softmmu.mak
@@ -5,6 +5,7 @@
 #CONFIG_PCI_DEVICES=n
 CONFIG_SEMIHOSTING=y
 CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
+CONFIG_RISCV_CLIC=y
 
 # Boards:
 #
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index f4694088a4..5bf492b48f 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -68,6 +68,9 @@ config SIFIVE_CLINT
 config SIFIVE_PLIC
     bool
 
+config RISCV_CLIC
+    bool
+
 config GOLDFISH_PIC
     bool
 
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index 1c299039f6..2aa71b6738 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -50,6 +50,7 @@ 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_SIFIVE_CLINT', if_true: files('sifive_clint.c'))
 specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.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'))
diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
new file mode 100644
index 0000000000..8ad534c506
--- /dev/null
+++ b/hw/intc/riscv_clic.c
@@ -0,0 +1,835 @@
+/*
+ * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
+ *
+ * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
+ *
+ * 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.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"
+
+/*
+ * 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_offset)
+{
+    return (clic->clicintattr[irq_offset] >> 1) & 0x3;
+}
+
+static inline bool
+riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
+{
+    return (clic->clicintattr[irq_offset] >> 1) & 0x1;
+}
+
+static inline bool
+riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
+{
+    return (clic->clicintattr[irq_offset] & 0x1) && clic->nvbits;
+}
+
+static uint8_t
+riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
+{
+    int nlbits = clic->nlbits;
+
+    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->nlbits;
+    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);
+}
+
+/*
+ * In a system with multiple harts, the M-mode CLIC regions for all the harts
+ * are placed contiguously in the memory space, followed by the S-mode CLIC
+ * regions for all harts. (Section 3.11)
+ */
+static size_t
+riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int hartid, int irq)
+{
+    size_t mode_offset = 0;
+    size_t unit = clic->num_harts * clic->num_sources;
+
+    switch (mode) {
+    case PRV_M:
+        mode_offset = 0;
+        break;
+    case PRV_S:
+        mode_offset = unit;
+        break;
+    case PRV_U:
+        mode_offset = clic->prv_s ? 2 * unit : unit;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "clic: invalid mode %d\n", mode);
+        exit(1);
+    }
+    return mode_offset + hartid * clic->num_sources + irq;
+}
+
+static void riscv_clic_next_interrupt(void *opaque, int hartid)
+{
+    /*
+     * 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
+     */
+    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(hartid));
+    CPURISCVState *env = &cpu->env;
+    RISCVCLICState *clic = (RISCVCLICState *)opaque;
+
+    int il[4] = {
+        MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
+            clic->mintthresh), /* PRV_U */
+        MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
+            clic->sintthresh), /* PRV_S */
+        0,                     /* reserverd */
+        MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
+            clic->uintthresh)  /* PRV_M */
+    };
+
+    /* Get sorted list of enabled interrupts for this hart */
+    size_t hart_offset = hartid * clic->num_sources;
+    CLICActiveInterrupt *active = &clic->active_list[hart_offset];
+    size_t active_count = clic->active_count[hartid];
+    uint8_t mode, level, priority;
+
+    /* Loop through the enabled interrupts sorted by mode+priority+level */
+    while (active_count) {
+        size_t irq_offset;
+        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;
+        }
+        irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, active->irq);
+        /* Check pending interrupt with high enough mode+priority+level */
+        if (clic->clicintip[irq_offset]) {
+            /* Clean vector edge-triggered pending */
+            if (riscv_clic_is_edge_triggered(clic, irq_offset) &&
+                riscv_clic_is_shv_interrupt(clic, irq_offset)) {
+                clic->clicintip[irq_offset] = 0;
+            }
+            /* Post pending interrupt for this hart */
+            clic->exccode[hartid] = active->irq | mode << 12 | level << 14;
+            qemu_set_irq(clic->cpu_irqs[hartid], 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 hartid, int irq)
+{
+    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
+    if (!clic->prv_s && !clic->prv_u) { /* M */
+        return mode == PRV_M;
+    } else if (!clic->prv_s) { /* M/U */
+        switch (clic->nmbits) {
+        case 0:
+            return mode == PRV_M;
+        case 1:
+            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M) :
+                                                          (mode == PRV_U);
+        default:
+            qemu_log_mask(LOG_GUEST_ERROR,
+                "clic: nmbits can only be 0 or 1 for M/U hart");
+            exit(1);
+        }
+    } else { /* M/S/U */
+        switch (clic->nmbits) {
+        case 0:
+            return mode == PRV_M;
+        case 1:
+            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M) :
+                                                          (mode == PRV_S);
+        case 2:
+            return mode == clic->clicintattr[irq_offset];
+        case 3:
+            qemu_log_mask(LOG_GUEST_ERROR,
+                "clic: nmbits can only be 0 or 1 or 2 for M/S/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 mode, int hartid, int irq)
+{
+    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
+    return riscv_clic_is_edge_triggered(clic, irq_offset);
+}
+
+static void
+riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
+                        int irq, uint64_t value)
+{
+    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
+    clic->clicintip[irq_offset] = !!value;
+    riscv_clic_next_interrupt(clic, hartid);
+}
+
+/*
+ * 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, uint64_t value)
+{
+    int mode = extract64(value, 6, 2);
+
+    if (!qtest_enabled()) {
+        CPURISCVState *env = current_cpu->env_ptr;
+        if (env->priv < mode) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static inline int riscv_clic_encode_priority(const CLICActiveInterrupt *i)
+{
+    return ((i->intcfg & 0x3ff) << 12) | /* Highest mode+level+priority */
+           (i->irq & 0xfff);             /* Highest irq number */
+}
+
+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 hartid,
+                        int irq, uint64_t new_intie)
+{
+    size_t hart_offset = hartid * clic->num_sources;
+    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
+    CLICActiveInterrupt *active_list = &clic->active_list[hart_offset];
+    size_t *active_count = &clic->active_count[hartid];
+
+    uint8_t old_intie = clic->clicintie[irq_offset];
+    clic->clicintie[irq_offset] = !!new_intie;
+
+    /* Add to or remove from list of active interrupts */
+    if (new_intie && !old_intie) {
+        active_list[*active_count].intcfg = (mode << 8) |
+                                            clic->clicintctl[irq_offset];
+        active_list[*active_count].irq = irq;
+        (*active_count)++;
+    } else if (!new_intie && old_intie) {
+        CLICActiveInterrupt key = {
+            (mode << 8) | clic->clicintctl[irq_offset], irq
+        };
+        CLICActiveInterrupt *result = bsearch(&key,
+                                              active_list, *active_count,
+                                              sizeof(CLICActiveInterrupt),
+                                              riscv_clic_active_compare);
+        size_t elem = (result - active_list) / sizeof(CLICActiveInterrupt);
+        size_t sz = (--(*active_count) - elem) * sizeof(CLICActiveInterrupt);
+        assert(result);
+        memmove(&result[0], &result[1], sz);
+    }
+
+    /* Sort list of active interrupts */
+    qsort(active_list, *active_count,
+          sizeof(CLICActiveInterrupt),
+          riscv_clic_active_compare);
+
+    riscv_clic_next_interrupt(clic, hartid);
+}
+
+static void
+riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
+                      uint64_t value, unsigned size,
+                      int mode, int hartid, int irq)
+{
+    int req = extract32(addr, 0, 2);
+    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
+
+    if (hartid >= clic->num_harts) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
+                      hartid, addr);
+        return;
+    }
+
+    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, mode, hartid, irq)) {
+            /*
+             * The actual pending bit is located at bit 0 (i.e., the
+             * leastsignificant 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_offset]) {
+                riscv_clic_update_intip(clic, mode, hartid, irq, value);
+            }
+        }
+        break;
+    case 1: /* clicintie[i] */
+        if (clic->clicintie[irq_offset] != value) {
+            riscv_clic_update_intie(clic, mode, hartid, irq, value);
+        }
+        break;
+    case 2: /* clicintattr[i] */
+        if (riscv_clic_validate_intattr(clic, value)) {
+            if (clic->clicintattr[irq_offset] != value) {
+                /* When nmbits=2, check WARL */
+                bool invalid = (clic->nmbits == 2) &&
+                               (extract64(value, 6, 2) == 0b10);
+                if (invalid) {
+                    uint8_t old_mode = extract32(clic->clicintattr[irq_offset],
+                                                 6, 2);
+                    value = deposit32(value, 6, 2, old_mode);
+                }
+                clic->clicintattr[irq_offset] = value;
+                riscv_clic_next_interrupt(clic, hartid);
+            }
+        }
+        break;
+    case 3: /* clicintctl[i] */
+        if (value != clic->clicintctl[irq_offset]) {
+            clic->clicintctl[irq_offset] = value;
+            riscv_clic_next_interrupt(clic, hartid);
+        }
+        break;
+    }
+}
+
+static uint64_t
+riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
+                     int hartid, int irq)
+{
+    int req = extract32(addr, 0, 2);
+    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
+
+    if (hartid >= clic->num_harts) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
+                      hartid, addr);
+        return 0;
+    }
+
+    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] */
+        return clic->clicintip[irq_offset];
+    case 1: /* clicintie[i] */
+        return clic->clicintie[irq_offset];
+    case 2: /* clicintattr[i] */
+        /*
+         * clicintattr register layout
+         * Bits Field
+         * 7:6 mode
+         * 5:3 reserved (WPRI 0)
+         * 2:1 trig
+         * 0 shv
+         */
+        return clic->clicintattr[irq_offset] & ~0x38;
+    case 3: /* clicintctrl */
+        /*
+         * 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_offset] |
+               ((1 << (8 - clic->clicintctlbits)) - 1);
+    }
+
+    return 0;
+}
+
+/* Return target interrupt mode */
+static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
+{
+    int mode = addr / (4 * clic->num_harts * clic->num_sources);
+    switch (mode) {
+    case 0:
+        return PRV_M;
+    case 1:
+        assert(clic->prv_s || clic->prv_u);
+        return clic->prv_s ? PRV_S : PRV_U;
+    case 2:
+        assert(clic->prv_s && clic->prv_u);
+        return PRV_U;
+    default:
+        g_assert_not_reached();
+        break;
+    }
+}
+
+/* Return target hart id */
+static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
+{
+    int mode_unit = 4 * clic->num_harts * clic->num_sources;
+    int hart_unit = 4 * clic->num_sources;
+
+    return (addr % mode_unit) / hart_unit;
+}
+
+/* Return target interrupt number */
+static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
+{
+    int hart_unit = 4 * clic->num_sources;
+    return (addr % hart_unit) / 4;
+}
+
+static void
+riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
+{
+    RISCVCLICState *clic = opaque;
+    hwaddr clic_size = clic->clic_size;
+    int hartid, mode, irq;
+
+    if (addr < clic_size) {
+        if (addr < 0x1000) {
+            assert(addr % 4 == 0);
+            int index = addr / 4;
+            switch (index) {
+            case 0: /* cliccfg */
+                {
+                    uint8_t nlbits = extract32(value, 1, 4);
+                    uint8_t nmbits = extract32(value, 5, 2);
+
+                    /*
+                     * The 4-bit cliccfg.nlbits WARL field.
+                     * Valid values are 0—8.
+                     */
+                    if (nlbits <= 8) {
+                        clic->nlbits = nlbits;
+                    }
+                    /* Valid values are given by implemented priviledges */
+                    if (clic->prv_s && clic->prv_u) {
+                        if (nmbits <= 2) {
+                            clic->nmbits = nmbits;
+                        }
+                    } else if (clic->prv_u) {
+                        if (nmbits <= 1) {
+                            clic->nmbits = nmbits;
+                        }
+                    } else {
+                        assert(!clic->prv_s);
+                        if (nmbits == 0) {
+                            clic->nmbits = 0;
+                        }
+                    }
+                    clic->nvbits = extract32(value, 0, 1);
+                    break;
+                }
+            case 1: /* clicinfo, read-only register */
+                qemu_log_mask(LOG_GUEST_ERROR,
+                              "clic: write read-only clicinfo.\n");
+                break;
+            case 0x10 ... 0x2F: /* clicinttrig */
+                {
+                    uint32_t interrupt_number = value & MAKE_64BIT_MASK(0, 13);
+                    if (interrupt_number <= clic->num_sources) {
+                        value &= ~MAKE_64BIT_MASK(13, 18);
+                        clic->clicinttrig[index - 0x10] = value;
+                    }
+                    break;
+                }
+            case 2: /* mintthresh */
+                if (!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 -= 0x1000;
+            hartid = riscv_clic_get_hartid(clic, addr);
+            mode = riscv_clic_get_mode(clic, addr);
+            irq = riscv_clic_get_irq(clic, addr);
+
+            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
+                riscv_clic_hart_write(clic, addr, value, size, mode,
+                                      hartid, irq);
+            }
+        }
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "clic: invalid write: 0x%" HWADDR_PRIx "\n", addr);
+    }
+}
+
+static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
+{
+    RISCVCLICState *clic = opaque;
+    hwaddr clic_size = clic->clic_size;
+    int hartid, mode, irq;
+
+    if (addr < clic_size) {
+        if (addr < 0x1000) {
+            assert(addr % 4 == 0);
+            int index = addr / 4;
+            switch (index) {
+            case 0: /* cliccfg */
+                return clic->nvbits |
+                       (clic->nlbits << 1) |
+                       (clic->nmbits << 5);
+            case 1: /* clicinfo */
+                /*
+                 * clicinfo register layout
+                 *
+                 * Bits Field
+                 * 31 reserved (WARL 0)
+                 * 30:25 num_trigger
+                 * 24:21 CLICINTCTLBITS
+                 * 20:13 version (for version control)
+                 * 12:0 num_interrupt
+                 */
+                return clic->clicinfo & ~INT32_MAX;
+            case 0x10 ... 0x2F: /* clicinttrig */
+                /*
+                 * clicinttrig register layout
+                 *
+                 * Bits Field
+                 * 31 enable
+                 * 30:13 reserved (WARL 0)
+                 * 12:0 interrupt_number
+                 */
+                return clic->clicinttrig[index - 0x10] &
+                       ~MAKE_64BIT_MASK(13, 18);
+            case 2: /* mintthresh */
+                if (!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 -= 0x1000;
+            hartid = riscv_clic_get_hartid(clic, addr);
+            mode = riscv_clic_get_mode(clic, addr);
+            irq = riscv_clic_get_irq(clic, addr);
+
+            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
+                return riscv_clic_hart_read(clic, addr, mode, hartid, irq);
+            }
+        }
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "clic: invalid read: 0x%" HWADDR_PRIx "\n", addr);
+    }
+    return 0;
+}
+
+static void riscv_clic_set_irq(void *opaque, int id, int level)
+{
+    RISCVCLICState *clic = opaque;
+    int irq, hartid, mode;
+    hwaddr addr = 4 * id;
+    TRIG_TYPE type;
+
+    hartid = riscv_clic_get_hartid(clic, addr);
+    mode = riscv_clic_get_mode(clic, addr);
+    irq = riscv_clic_get_irq(clic, addr);
+    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, mode, hartid, irq, level);
+            break;
+        case NEG_LEVEL:
+            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
+            break;
+        case NEG_EDGE:
+            break;
+        }
+    } else {
+        switch (type) {
+        case POSITIVE_LEVEL:
+            riscv_clic_update_intip(clic, mode, hartid, irq, level);
+            break;
+        case POSITIVE_EDGE:
+            break;
+        case NEG_LEVEL:
+        case NEG_EDGE:
+            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
+            break;
+        }
+    }
+}
+
+static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level)
+{
+    CPURISCVState *env = (CPURISCVState *)opaque;
+    RISCVCLICState *clic = env->clic;
+    CPUState *cpu = env_cpu(env);
+
+    if (level) {
+        env->exccode = clic->exccode[cpu->cpu_index];
+        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 harts_x_sources = clic->num_harts * clic->num_sources;
+    int irqs, i;
+
+    if (clic->prv_s && clic->prv_u) {
+        irqs = 3 * harts_x_sources;
+    } else if (clic->prv_s || clic->prv_u) {
+        irqs = 2 * harts_x_sources;
+    } else {
+        irqs = harts_x_sources;
+    }
+
+    clic->clic_size = irqs * 4 + 0x1000;
+    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops, clic,
+                          TYPE_RISCV_CLIC, clic->clic_size);
+
+    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);
+    clic->active_count = g_new0(size_t, clic->num_harts);
+    clic->exccode = g_new0(uint32_t, clic->num_harts);
+    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
+
+    /* Allocate irq 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_irqs, clic->num_harts);
+
+    for (i = 0; i < clic->num_harts; i++) {
+        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
+        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
+                                         &cpu->env, 1);
+        qdev_connect_gpio_out(dev, i, irq);
+        cpu->env.clic = clic;
+    }
+}
+
+static Property riscv_clic_properties[] = {
+    DEFINE_PROP_BOOL("prv-s", RISCVCLICState, prv_s, false),
+    DEFINE_PROP_BOOL("prv-u", RISCVCLICState, prv_u, false),
+    DEFINE_PROP_UINT32("num-harts", RISCVCLICState, num_harts, 0),
+    DEFINE_PROP_UINT32("num-sources", RISCVCLICState, num_sources, 0),
+    DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICState, clicintctlbits, 0),
+    DEFINE_PROP_UINT64("mclicbase", RISCVCLICState, mclicbase, 0),
+    DEFINE_PROP_STRING("version", RISCVCLICState, version),
+    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 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 void riscv_clic_register_types(void)
+{
+    type_register_static(&riscv_clic_info);
+}
+
+type_init(riscv_clic_register_types)
+
+/*
+ * riscv_clic_create:
+ *
+ * @addr: base address of M-Mode CLIC memory-mapped registers
+ * @prv_s: have smode region
+ * @prv_u: have umode region
+ * @num_harts: number of CPU harts
+ * @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"
+ *
+ * Returns: the device object
+ */
+DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
+                               uint32_t num_harts, uint32_t num_sources,
+                               uint8_t clicintctlbits,
+                               const char *version)
+{
+    DeviceState *dev = qdev_new(TYPE_RISCV_CLIC);
+
+    assert(num_sources <= 4096);
+    assert(num_harts <= 1024);
+    assert(clicintctlbits <= 8);
+    assert(!strcmp(version, "v0.8") || !strcmp(version, "v0.9"));
+
+    qdev_prop_set_bit(dev, "prv-s", prv_s);
+    qdev_prop_set_bit(dev, "prv-u", prv_u);
+    qdev_prop_set_uint32(dev, "num-harts", num_harts);
+    qdev_prop_set_uint32(dev, "num-sources", num_sources);
+    qdev_prop_set_uint32(dev, "clicintctlbits", clicintctlbits);
+    qdev_prop_set_uint64(dev, "mclicbase", addr);
+    qdev_prop_set_string(dev, "version", version);
+
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+    return dev;
+}
+
+void riscv_clic_get_next_interrupt(void *opaque, int hartid)
+{
+    RISCVCLICState *clic = opaque;
+    riscv_clic_next_interrupt(clic, hartid);
+}
+
+bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int irq)
+{
+    RISCVCLICState *clic = opaque;
+    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
+    return riscv_clic_is_shv_interrupt(clic, irq_offset);
+}
+
+bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int irq)
+{
+    RISCVCLICState *clic = opaque;
+    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
+    return riscv_clic_is_edge_triggered(clic, irq_offset);
+}
+
+void riscv_clic_clean_pending(void *opaque, int mode, int hartid, int irq)
+{
+    RISCVCLICState *clic = opaque;
+    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
+    clic->clicintip[irq_offset] = 0;
+}
+
+/*
+ * 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.
+ */
+bool riscv_clic_is_clic_mode(CPURISCVState *env)
+{
+    target_ulong xtvec = (env->priv == PRV_M) ? env->mtvec : env->stvec;
+    return env->clic && ((xtvec & 0x3) == 3);
+}
+
+void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
+                               int *il, int *irq)
+{
+    *irq = extract32(exccode, 0, 12);
+    *mode = extract32(exccode, 12, 2);
+    *il = extract32(exccode, 14, 8);
+}
diff --git a/include/hw/intc/riscv_clic.h b/include/hw/intc/riscv_clic.h
new file mode 100644
index 0000000000..e5f89672a6
--- /dev/null
+++ b/include/hw/intc/riscv_clic.h
@@ -0,0 +1,103 @@
+/*
+ * RISC-V CLIC(Core Local Interrupt Controller) interface.
+ *
+ * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
+ *
+ * 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/>.
+ */
+
+#ifndef RISCV_CLIC_H
+#define RISCV_CLIC_H
+
+#include "hw/irq.h"
+#include "hw/sysbus.h"
+
+#define TYPE_RISCV_CLIC "riscv_clic"
+#define RISCV_CLIC(obj) \
+    OBJECT_CHECK(RISCVCLICState, (obj), TYPE_RISCV_CLIC)
+
+/*
+ * 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;
+
+typedef struct RISCVCLICState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+
+    /* Implementaion parameters */
+    bool prv_s;
+    bool prv_u;
+    uint32_t num_harts;
+    uint32_t num_sources;
+    uint32_t clic_size;
+    uint32_t clic_mmode_base;
+    uint32_t clicintctlbits;
+    uint64_t mclicbase;
+    char *version;
+
+    /* Global configuration */
+    uint8_t nmbits;
+    uint8_t nlbits;
+    uint8_t nvbits;
+    uint32_t clicinfo;
+    uint32_t clicinttrig[32];
+
+    /* Aperture configuration */
+    uint8_t *clicintip;
+    uint8_t *clicintie;
+    uint8_t *clicintattr;
+    uint8_t *clicintctl;
+
+    /* Complatible with v0.8 */
+    uint32_t mintthresh;
+    uint32_t sintthresh;
+    uint32_t uintthresh;
+
+    /* QEMU implementaion related fields */
+    uint32_t *exccode;
+    CLICActiveInterrupt *active_list;
+    size_t *active_count;
+    MemoryRegion mmio;
+    qemu_irq *cpu_irqs;
+} RISCVCLICState;
+
+DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
+                               uint32_t num_harts, 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 mode, int hartid, int irq);
+bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int irq);
+bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int irq);
+void riscv_clic_get_next_interrupt(void *opaque, int hartid);
+bool riscv_clic_is_clic_mode(CPURISCVState *env);
+#endif
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index a5eab26a69..9e389d7bbf 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -33,6 +33,7 @@
 #define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
 #define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
 #define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
+#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0
 
 #define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
 #define TYPE_RISCV_CPU_BASE32           RISCV_CPU_TYPE_NAME("rv32")
@@ -247,6 +248,7 @@ struct CPURISCVState {
     /* Fields from here on are preserved across CPU reset. */
     QEMUTimer *timer; /* Internal timer */
     void *clic;       /* clic interrupt controller */
+    uint32_t exccode; /* clic irq encode */
 };
 
 OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
-- 
2.25.1



^ permalink raw reply	[flat|nested] 81+ messages in thread

* [RFC PATCH 04/11] target/riscv: Update CSR xie in CLIC mode
  2021-04-09  7:48 [RFC PATCH 00/11] RISC-V: support clic v0.9 specification LIU Zhiwei
                   ` (2 preceding siblings ...)
  2021-04-09  7:48 ` [RFC PATCH 03/11] hw/intc: Add CLIC device LIU Zhiwei
@ 2021-04-09  7:48 ` LIU Zhiwei
  2021-06-27  6:50   ` Frank Chang
  2021-04-09  7:48 ` [RFC PATCH 05/11] target/riscv: Update CSR xip " LIU Zhiwei
                   ` (8 subsequent siblings)
  12 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-09  7:48 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv; +Cc: palmer, Alistair.Francis, LIU Zhiwei, wxy194768

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>
---
 target/riscv/csr.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 4c31364967..74bc7a08aa 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -23,6 +23,10 @@
 #include "qemu/main-loop.h"
 #include "exec/exec-all.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)
 {
@@ -611,13 +615,17 @@ static int write_mideleg(CPURISCVState *env, int csrno, target_ulong val)
 
 static int read_mie(CPURISCVState *env, int csrno, target_ulong *val)
 {
-    *val = env->mie;
+    /* The xie CSR appears hardwired to zero in CLIC mode, (Section 4.3) */
+    *val = riscv_clic_is_clic_mode(env) ? 0 : env->mie;
     return 0;
 }
 
 static int write_mie(CPURISCVState *env, int csrno, target_ulong val)
 {
-    env->mie = (env->mie & ~all_ints) | (val & all_ints);
+    /* Writes to xie will be ignored and will not trap. (Section 4.3) */
+    if (!riscv_clic_is_clic_mode(env)) {
+        env->mie = (env->mie & ~all_ints) | (val & all_ints);
+    }
     return 0;
 }
 
@@ -785,7 +793,8 @@ static int read_sie(CPURISCVState *env, int csrno, target_ulong *val)
     if (riscv_cpu_virt_enabled(env)) {
         read_vsie(env, CSR_VSIE, val);
     } else {
-        *val = env->mie & env->mideleg;
+        /* The xie CSR appears hardwired to zero in CLIC mode. (Section 4.3) */
+        *val = riscv_clic_is_clic_mode(env) ? 0 : env->mie & env->mideleg;
     }
     return 0;
 }
@@ -805,6 +814,10 @@ static int write_sie(CPURISCVState *env, int csrno, target_ulong val)
     } else {
         target_ulong newval = (env->mie & ~S_MODE_INTERRUPTS) |
                               (val & S_MODE_INTERRUPTS);
+        /* Writes to xie will be ignored and will not trap. (Section 4.3) */
+        if (riscv_clic_is_clic_mode(env)) {
+            return 0;
+        }
         write_mie(env, CSR_MIE, newval);
     }
 
-- 
2.25.1



^ permalink raw reply	[flat|nested] 81+ messages in thread

* [RFC PATCH 05/11] target/riscv: Update CSR xip in CLIC mode
  2021-04-09  7:48 [RFC PATCH 00/11] RISC-V: support clic v0.9 specification LIU Zhiwei
                   ` (3 preceding siblings ...)
  2021-04-09  7:48 ` [RFC PATCH 04/11] target/riscv: Update CSR xie in CLIC mode LIU Zhiwei
@ 2021-04-09  7:48 ` LIU Zhiwei
  2021-06-27  6:45   ` Frank Chang
  2021-04-09  7:48 ` [RFC PATCH 06/11] target/riscv: Update CSR xtvec " LIU Zhiwei
                   ` (7 subsequent siblings)
  12 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-09  7:48 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv; +Cc: palmer, Alistair.Francis, LIU Zhiwei, wxy194768

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>
---
 target/riscv/csr.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 74bc7a08aa..f6c84b9fe4 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -735,6 +735,11 @@ static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value,
     target_ulong mask = write_mask & delegable_ints & ~env->miclaim;
     uint32_t old_mip;
 
+     /* The xip CSR appears hardwired to zero in CLIC mode. (Section 4.3) */
+    if (riscv_clic_is_clic_mode(env)) {
+        *ret_value = 0;
+        return 0;
+    }
     if (mask) {
         old_mip = riscv_cpu_update_mip(cpu, mask, (new_value & mask));
     } else {
@@ -922,6 +927,11 @@ static int rmw_sip(CPURISCVState *env, int csrno, target_ulong *ret_value,
     if (riscv_cpu_virt_enabled(env)) {
         ret = rmw_vsip(env, CSR_VSIP, ret_value, new_value, write_mask);
     } else {
+        /* The xip CSR appears hardwired to zero in CLIC mode. (Section 4.3) */
+        if (riscv_clic_is_clic_mode(env)) {
+            *ret_value = 0;
+            return 0;
+        }
         ret = rmw_mip(env, CSR_MSTATUS, ret_value, new_value,
                       write_mask & env->mideleg & sip_writable_mask);
     }
-- 
2.25.1



^ permalink raw reply	[flat|nested] 81+ messages in thread

* [RFC PATCH 06/11] target/riscv: Update CSR xtvec in CLIC mode
  2021-04-09  7:48 [RFC PATCH 00/11] RISC-V: support clic v0.9 specification LIU Zhiwei
                   ` (4 preceding siblings ...)
  2021-04-09  7:48 ` [RFC PATCH 05/11] target/riscv: Update CSR xip " LIU Zhiwei
@ 2021-04-09  7:48 ` LIU Zhiwei
  2021-06-27  8:59   ` Frank Chang
  2021-07-10 15:04   ` Frank Chang
  2021-04-09  7:48 ` [RFC PATCH 07/11] target/riscv: Update CSR xtvt " LIU Zhiwei
                   ` (6 subsequent siblings)
  12 siblings, 2 replies; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-09  7:48 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv; +Cc: palmer, Alistair.Francis, LIU Zhiwei, wxy194768

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>
---
 target/riscv/csr.c | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index f6c84b9fe4..39ff72041a 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -637,9 +637,18 @@ static int read_mtvec(CPURISCVState *env, int csrno, target_ulong *val)
 
 static int write_mtvec(CPURISCVState *env, int csrno, target_ulong val)
 {
-    /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
+    /*
+     * bits [1:0] encode mode; 0 = direct, 1 = vectored, 3 = CLIC,
+     * others reserved
+     */
     if ((val & 3) < 2) {
         env->mtvec = val;
+    } else if ((val & 1) && 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.
+         */
+        env->mtvec = ((val & ~0x3f) << 6) | (0b000011);
     } else {
         qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: reserved mode not supported\n");
     }
@@ -837,9 +846,18 @@ static int read_stvec(CPURISCVState *env, int csrno, target_ulong *val)
 
 static int write_stvec(CPURISCVState *env, int csrno, target_ulong val)
 {
-    /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
+    /*
+     * bits [1:0] encode mode; 0 = direct, 1 = vectored, 3 = CLIC,
+     * others reserved
+     */
     if ((val & 3) < 2) {
         env->stvec = val;
+    } else if ((val & 1) && 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.
+         */
+        env->stvec = ((val & ~0x3f) << 6) | (0b000011);
     } else {
         qemu_log_mask(LOG_UNIMP, "CSR_STVEC: reserved mode not supported\n");
     }
-- 
2.25.1



^ permalink raw reply	[flat|nested] 81+ messages in thread

* [RFC PATCH 07/11] target/riscv: Update CSR xtvt in CLIC mode
  2021-04-09  7:48 [RFC PATCH 00/11] RISC-V: support clic v0.9 specification LIU Zhiwei
                   ` (5 preceding siblings ...)
  2021-04-09  7:48 ` [RFC PATCH 06/11] target/riscv: Update CSR xtvec " LIU Zhiwei
@ 2021-04-09  7:48 ` LIU Zhiwei
  2021-06-27  8:33   ` Frank Chang
  2021-04-09  7:48 ` [RFC PATCH 08/11] target/riscv: Update CSR xnxti " LIU Zhiwei
                   ` (5 subsequent siblings)
  12 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-09  7:48 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv; +Cc: palmer, Alistair.Francis, LIU Zhiwei, wxy194768

The xtvt WARL XLEN-bit CSR holds the base address of the trap vector table,
aligned on a 64-byte or greater power-of-two boundary.

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 9e389d7bbf..b5fd796f98 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -173,11 +173,13 @@ struct CPURISCVState {
     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 9447801d22..7922097776 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -149,6 +149,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
@@ -178,6 +179,7 @@
 #define CSR_SIE             0x104
 #define CSR_STVEC           0x105
 #define CSR_SCOUNTEREN      0x106
+#define CSR_STVT            0x107 /* clic-spec-draft */
 
 /* Supervisor Trap Handling */
 #define CSR_SSCRATCH        0x140
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 39ff72041a..e12222b77f 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -667,6 +667,18 @@ static int write_mcounteren(CPURISCVState *env, int csrno, target_ulong val)
     return 0;
 }
 
+static int read_mtvt(CPURISCVState *env, int csrno, target_ulong *val)
+{
+    *val = env->mtvt;
+    return 0;
+}
+
+static int write_mtvt(CPURISCVState *env, int csrno, target_ulong val)
+{
+    env->mtvt = val & ~((1ULL << 6) - 1);
+    return 0;
+}
+
 /* This regiser is replaced with CSR_MCOUNTINHIBIT in 1.11.0 */
 static int read_mscounteren(CPURISCVState *env, int csrno, target_ulong *val)
 {
@@ -876,6 +888,18 @@ static int write_scounteren(CPURISCVState *env, int csrno, target_ulong val)
     return 0;
 }
 
+static int read_stvt(CPURISCVState *env, int csrno, target_ulong *val)
+{
+    *val = env->stvt;
+    return 0;
+}
+
+static int write_stvt(CPURISCVState *env, int csrno, target_ulong val)
+{
+    env->stvt = val & ~((1ULL << 6) - 1);
+    return 0;
+}
+
 /* Supervisor Trap Handling */
 static int read_sscratch(CPURISCVState *env, int csrno, target_ulong *val)
 {
@@ -1730,6 +1754,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
     [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", any32,  read_zero },
 
     /* 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 },
@@ -1739,5 +1764,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
     [CSR_SINTTHRESH] = { "sintthresh", clic,  read_sintthresh,
                          write_sintthresh },
 
+    /* Supervisor Mode Core Level Interrupt Controller */
+    [CSR_STVT] = { "stvt", clic,  read_stvt, write_stvt       },
+
 #endif /* !CONFIG_USER_ONLY */
 };
-- 
2.25.1



^ permalink raw reply	[flat|nested] 81+ messages in thread

* [RFC PATCH 08/11] target/riscv: Update CSR xnxti in CLIC mode
  2021-04-09  7:48 [RFC PATCH 00/11] RISC-V: support clic v0.9 specification LIU Zhiwei
                   ` (6 preceding siblings ...)
  2021-04-09  7:48 ` [RFC PATCH 07/11] target/riscv: Update CSR xtvt " LIU Zhiwei
@ 2021-04-09  7:48 ` LIU Zhiwei
  2021-06-11  8:15   ` Frank Chang
                     ` (2 more replies)
  2021-04-09  7:48 ` [RFC PATCH 09/11] target/riscv: Update CSR mclicbase " LIU Zhiwei
                   ` (4 subsequent siblings)
  12 siblings, 3 replies; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-09  7:48 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv; +Cc: palmer, Alistair.Francis, LIU Zhiwei, wxy194768

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>
---
 target/riscv/cpu_bits.h |  16 ++++++
 target/riscv/csr.c      | 114 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 130 insertions(+)

diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 7922097776..494e41edc9 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      0x346 /* clic-spec-draft */
 #define CSR_MINTTHRESH      0x347 /* clic-spec-draft */
 
@@ -187,6 +188,7 @@
 #define CSR_SCAUSE          0x142
 #define CSR_STVAL           0x143
 #define CSR_SIP             0x144
+#define CSR_SNXTI           0x145 /* clic-spec-draft */
 #define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
 #define CSR_SINTTHRESH      0x147 /* clic-spec-draft */
 
@@ -596,10 +598,24 @@
 #define MINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
 #define MINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
 
+/* mcause */
+#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] */
+
 /* sintstatus */
 #define SINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
 #define SINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
 
+/* scause */
+#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] */
+
 /* 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 e12222b77f..72cba080bf 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -774,6 +774,80 @@ static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value,
     return 0;
 }
 
+static bool get_xnxti_status(CPURISCVState *env)
+{
+    CPUState *cs = env_cpu(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_priv, cs->cpu_index,
+                                      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;
+    CPUState *cs = env_cpu(env);
+    if (write_mask) {
+        env->mstatus |= new_value & (write_mask & 0b11111);
+    }
+
+    qemu_mutex_lock_iothread();
+    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_priv,
+                                                  cs->cpu_index, clic_irq);
+            if (edge) {
+                riscv_clic_clean_pending(env->clic, clic_priv,
+                                         cs->cpu_index, 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;
+        }
+    }
+    qemu_mutex_unlock_iothread();
+    return 0;
+}
+
 static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
 {
     *val = env->mintstatus;
@@ -982,6 +1056,44 @@ static int rmw_sip(CPURISCVState *env, int csrno, target_ulong *ret_value,
     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;
+    CPUState *cs = env_cpu(env);
+    if (write_mask) {
+        env->mstatus |= new_value & (write_mask & 0b11111);
+    }
+
+    qemu_mutex_lock_iothread();
+    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_priv,
+                                                  cs->cpu_index, clic_irq);
+            if (edge) {
+                riscv_clic_clean_pending(env->clic, clic_priv,
+                                         cs->cpu_index, clic_irq);
+            }
+            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;
+        }
+    }
+    qemu_mutex_unlock_iothread();
+    return 0;
+}
+
 static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
 {
     target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
@@ -1755,6 +1867,7 @@ 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 },
@@ -1766,6 +1879,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
 
     /* Supervisor Mode Core Level Interrupt Controller */
     [CSR_STVT] = { "stvt", clic,  read_stvt, write_stvt       },
+    [CSR_SNXTI] = { "snxti", clic,  NULL,  NULL,  rmw_snxti   },
 
 #endif /* !CONFIG_USER_ONLY */
 };
-- 
2.25.1



^ permalink raw reply	[flat|nested] 81+ messages in thread

* [RFC PATCH 09/11] target/riscv: Update CSR mclicbase in CLIC mode
  2021-04-09  7:48 [RFC PATCH 00/11] RISC-V: support clic v0.9 specification LIU Zhiwei
                   ` (7 preceding siblings ...)
  2021-04-09  7:48 ` [RFC PATCH 08/11] target/riscv: Update CSR xnxti " LIU Zhiwei
@ 2021-04-09  7:48 ` LIU Zhiwei
  2021-06-26 15:31   ` Frank Chang
  2021-04-09  7:48 ` [RFC PATCH 10/11] target/riscv: Update interrupt handling " LIU Zhiwei
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-09  7:48 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv; +Cc: palmer, Alistair.Francis, LIU Zhiwei, wxy194768

The machine mode mclicbase CSR is an XLEN-bit read-only register providing
the base address of CLIC memory mapped registers.

Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
---
 hw/intc/riscv_clic.c | 1 +
 target/riscv/cpu.h   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
index 8ad534c506..e902dd4062 100644
--- a/hw/intc/riscv_clic.c
+++ b/hw/intc/riscv_clic.c
@@ -715,6 +715,7 @@ static void riscv_clic_realize(DeviceState *dev, Error **errp)
                                          &cpu->env, 1);
         qdev_connect_gpio_out(dev, i, irq);
         cpu->env.clic = clic;
+        cpu->env.mclicbase = clic->mclicbase;
     }
 }
 
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index b5fd796f98..b0b8565649 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -162,6 +162,7 @@ struct CPURISCVState {
     uint32_t miclaim;
     uint32_t mintstatus; /* clic-spec */
     target_ulong mintthresh; /* clic-spec */
+    target_ulong mclicbase; /* clic-spec */
 
     target_ulong mie;
     target_ulong mideleg;
-- 
2.25.1



^ permalink raw reply	[flat|nested] 81+ messages in thread

* [RFC PATCH 10/11] target/riscv: Update interrupt handling in CLIC mode
  2021-04-09  7:48 [RFC PATCH 00/11] RISC-V: support clic v0.9 specification LIU Zhiwei
                   ` (8 preceding siblings ...)
  2021-04-09  7:48 ` [RFC PATCH 09/11] target/riscv: Update CSR mclicbase " LIU Zhiwei
@ 2021-04-09  7:48 ` LIU Zhiwei
  2021-06-27 15:39   ` Frank Chang
  2021-04-09  7:48 ` [RFC PATCH 11/11] target/riscv: Update interrupt return " LIU Zhiwei
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-09  7:48 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv; +Cc: palmer, Alistair.Francis, LIU Zhiwei, wxy194768

Decode CLIC interrupt information from exccode, includes interrupt
priviledge 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>
---
 target/riscv/cpu_bits.h   |   1 +
 target/riscv/cpu_helper.c | 117 +++++++++++++++++++++++++++++++++++---
 2 files changed, 111 insertions(+), 7 deletions(-)

diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 494e41edc9..d8378d2384 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -557,6 +557,7 @@
 #define RISCV_EXCP_VIRT_INSTRUCTION_FAULT        0x16
 #define RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT  0x17
 
+#define RISCV_EXCP_INT_CLIC                0x40000000
 #define RISCV_EXCP_INT_FLAG                0x80000000
 #define RISCV_EXCP_INT_MASK                0x7fffffff
 
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 21c54ef561..998d1a2742 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -26,6 +26,10 @@
 #include "trace.h"
 #include "semihosting/common-semi.h"
 
+#if !defined(CONFIG_USER_ONLY)
+#include "hw/intc/riscv_clic.h"
+#endif
+
 int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
 {
 #ifdef CONFIG_USER_ONLY
@@ -36,6 +40,20 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
 }
 
 #ifndef CONFIG_USER_ONLY
+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)
 {
     target_ulong irqs;
@@ -90,6 +108,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 = (env->exccode >> 12) & 0b11;
+        int enabled = riscv_cpu_local_irq_mode_enabled(env, mode);
+        if (enabled) {
+            cs->exception_index = RISCV_EXCP_INT_CLIC | env->exccode;
+            cs->interrupt_request = cs->interrupt_request & ~CPU_INTERRUPT_CLIC;
+            riscv_cpu_do_interrupt(cs);
+            return true;
+        }
+    }
 #endif
     return false;
 }
@@ -884,6 +914,55 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
 #endif
 }
 
+
+#if !defined(CONFIG_USER_ONLY)
+static target_ulong riscv_intr_pc(CPURISCVState *env, target_ulong tvec,
+                                  target_ulong tvt, bool async, bool clic,
+                                  int cause, int mode)
+{
+    int mode1 = tvec & 0b11, mode2 = tvec & 0b111111;
+    CPUState *cs = env_cpu(env);
+
+    if (!(async || clic)) {
+        return tvec & ~0b11;
+    }
+    /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
+    switch (mode1) {
+    case 0b00:
+        return tvec & ~0b11;
+    case 0b01:
+        return (tvec & ~0b11) + cause * 4;
+    default:
+        if (env->clic && (mode2 == 0b000011)) {
+            /* Non-vectored, clicintattr[i].shv = 0 || cliccfg.nvbits = 0 */
+            if (!riscv_clic_shv_interrupt(env->clic, mode, cs->cpu_index,
+                                          cause)) {
+                /* NBASE = mtvec[XLEN-1:6]<<6 */
+                return tvec & ~0b111111;
+            } else {
+                /*
+                 * pc := M[TBASE + XLEN/8 * exccode)] & ~1,
+                 * TBASE = mtvt[XLEN-1:6]<<6
+                 */
+                int size = TARGET_LONG_BITS / 8;
+                target_ulong tbase = (tvt & ~0b111111) + size * cause;
+                void *host = tlb_vaddr_to_host(env, tbase, MMU_DATA_LOAD, mode);
+                if (host != NULL) {
+                    target_ulong new_pc = ldn_p(host, size);
+                    if (tlb_vaddr_to_host(env, new_pc, MMU_INST_FETCH, mode)) {
+                        return new_pc;
+                    }
+                }
+                qemu_log_mask(LOG_GUEST_ERROR,
+                              "CLIC: load trap handler error!\n");
+                exit(1);
+            }
+        }
+        g_assert_not_reached();
+    }
+}
+#endif
+
 /*
  * Handle Traps
  *
@@ -898,11 +977,13 @@ void riscv_cpu_do_interrupt(CPUState *cs)
     CPURISCVState *env = &cpu->env;
     bool force_hs_execp = riscv_cpu_force_hs_excep_enabled(env);
     uint64_t s;
+    int mode, level;
 
     /* 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_INT_CLIC);
     target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK;
     target_ulong deleg = async ? env->mideleg : env->medeleg;
     bool write_tval = false;
@@ -958,6 +1039,28 @@ void riscv_cpu_do_interrupt(CPUState *cs)
         }
     }
 
+    if (clic) {
+        mode = (cause >> 12) & 3;
+        level = (cause >> 14) & 0xff;
+        cause &= 0xfff;
+        cause |= get_field(env->mstatus, MSTATUS_MPP) << 28;
+        switch (mode) {
+        case PRV_M:
+            cause |= get_field(env->mintstatus, MINTSTATUS_MIL) << 16;
+            cause |= get_field(env->mstatus, MSTATUS_MIE) << 27;
+            env->mintstatus = set_field(env->mintstatus, MINTSTATUS_MIL, level);
+            break;
+        case PRV_S:
+            cause |= get_field(env->mintstatus, MINTSTATUS_SIL) << 16;
+            cause |= get_field(env->mstatus, MSTATUS_SPIE) << 27;
+            env->mintstatus = set_field(env->mintstatus, MINTSTATUS_SIL, level);
+            break;
+        }
+    } else {
+        mode = env->priv <= PRV_S &&
+            cause < TARGET_LONG_BITS && ((deleg >> cause) & 1) ? PRV_S : PRV_M;
+    }
+
     trace_riscv_trap(env->mhartid, async, cause, env->pc, tval,
                      riscv_cpu_get_trap_name(cause, async));
 
@@ -967,8 +1070,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 < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
+    if (mode == PRV_S) {
         /* handle the trap in S-mode */
         if (riscv_has_ext(env, RVH)) {
             target_ulong hdeleg = async ? env->hideleg : env->hedeleg;
@@ -1021,12 +1123,13 @@ 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));
+        env->scause = cause | ((target_ulong)(async | clic) <<
+                               (TARGET_LONG_BITS - 1));
         env->sepc = env->pc;
         env->sbadaddr = tval;
         env->htval = htval;
-        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,
+                                clic & 0xfff, cause, PRV_S);
         riscv_cpu_set_mode(env, PRV_S);
     } else {
         /* handle the trap in M-mode */
@@ -1056,8 +1159,8 @@ void riscv_cpu_do_interrupt(CPUState *cs)
         env->mepc = env->pc;
         env->mbadaddr = tval;
         env->mtval2 = mtval2;
-        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,
+                                clic, cause & 0xfff, PRV_M);
         riscv_cpu_set_mode(env, PRV_M);
     }
 
-- 
2.25.1



^ permalink raw reply	[flat|nested] 81+ messages in thread

* [RFC PATCH 11/11] target/riscv: Update interrupt return in CLIC mode
  2021-04-09  7:48 [RFC PATCH 00/11] RISC-V: support clic v0.9 specification LIU Zhiwei
                   ` (9 preceding siblings ...)
  2021-04-09  7:48 ` [RFC PATCH 10/11] target/riscv: Update interrupt handling " LIU Zhiwei
@ 2021-04-09  7:48 ` LIU Zhiwei
  2021-06-27 12:08   ` Frank Chang
  2021-07-13  7:15   ` Frank Chang
  2021-04-19 23:30 ` [RFC PATCH 00/11] RISC-V: support clic v0.9 specification Alistair Francis
  2021-06-27 15:55 ` Frank Chang
  12 siblings, 2 replies; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-09  7:48 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv; +Cc: palmer, Alistair.Francis, LIU Zhiwei, wxy194768

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 priviledge interrupt pending forever.

Therefore when interrupts return, pull a pending interrupt to service.

Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
---
 target/riscv/op_helper.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 1eddcb94de..42563b22ba 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -24,6 +24,10 @@
 #include "exec/exec-all.h"
 #include "exec/helper-proto.h"
 
+#if !defined(CONFIG_USER_ONLY)
+#include "hw/intc/riscv_clic.h"
+#endif
+
 /* Exceptions processing helpers */
 void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env,
                                           uint32_t exception, uintptr_t pc)
@@ -130,6 +134,17 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb)
         mstatus = set_field(mstatus, MSTATUS_SPIE, 1);
         mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U);
         env->mstatus = mstatus;
+
+        if (riscv_clic_is_clic_mode(env)) {
+            CPUState *cs = env_cpu(env);
+            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, 0);
+            env->scause = set_field(env->scause, SCAUSE_SPP, PRV_U);
+            qemu_mutex_lock_iothread();
+            riscv_clic_get_next_interrupt(env->clic, cs->cpu_index);
+            qemu_mutex_unlock_iothread();
+        }
     }
 
     riscv_cpu_set_mode(env, prev_priv);
@@ -172,6 +187,16 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
         riscv_cpu_set_virt_enabled(env, prev_virt);
     }
 
+    if (riscv_clic_is_clic_mode(env)) {
+        CPUState *cs = env_cpu(env);
+        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, 0);
+        env->mcause = set_field(env->mcause, MCAUSE_MPP, PRV_U);
+        qemu_mutex_lock_iothread();
+        riscv_clic_get_next_interrupt(env->clic, cs->cpu_index);
+        qemu_mutex_unlock_iothread();
+    }
     return retpc;
 }
 
-- 
2.25.1



^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 01/11] target/riscv: Add CLIC CSR mintstatus
  2021-04-09  7:48 ` [RFC PATCH 01/11] target/riscv: Add CLIC CSR mintstatus LIU Zhiwei
@ 2021-04-19 23:23   ` Alistair Francis
  2021-04-20  0:49     ` LIU Zhiwei
  0 siblings, 1 reply; 81+ messages in thread
From: Alistair Francis @ 2021-04-19 23:23 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

On Fri, Apr 9, 2021 at 5:52 PM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
>
> CSR mintstatus holds the active interrupt level for each supported
> privilege mode. sintstatus, and user, uintstatus, provide restricted
> views of mintstatus.
>
> Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
> ---
>  target/riscv/cpu.h      |  2 ++
>  target/riscv/cpu_bits.h | 11 +++++++++++
>  target/riscv/csr.c      | 26 ++++++++++++++++++++++++++
>  3 files changed, 39 insertions(+)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 0a33d387ba..1a44ca62c7 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -159,6 +159,7 @@ struct CPURISCVState {
>      target_ulong mip;
>
>      uint32_t miclaim;
> +    uint32_t mintstatus; /* clic-spec */
>
>      target_ulong mie;
>      target_ulong mideleg;
> @@ -243,6 +244,7 @@ struct CPURISCVState {
>
>      /* Fields from here on are preserved across CPU reset. */
>      QEMUTimer *timer; /* Internal timer */
> +    void *clic;       /* clic interrupt controller */

This should be the CLIC type.

>  };
>
>  OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index caf4599207..c4ce6ec3d9 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      0x346 /* clic-spec-draft */
>
>  /* Legacy Machine Trap Handling (priv v1.9.1) */
>  #define CSR_MBADADDR        0x343
> @@ -183,6 +184,7 @@
>  #define CSR_SCAUSE          0x142
>  #define CSR_STVAL           0x143
>  #define CSR_SIP             0x144
> +#define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
>
>  /* Legacy Supervisor Trap Handling (priv v1.9.1) */
>  #define CSR_SBADADDR        0x143
> @@ -585,6 +587,15 @@
>  #define SIP_STIP                           MIP_STIP
>  #define SIP_SEIP                           MIP_SEIP
>
> +/* mintstatus */
> +#define MINTSTATUS_MIL                     0xff000000 /* mil[7:0] */
> +#define MINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
> +#define MINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
> +
> +/* sintstatus */
> +#define SINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
> +#define SINTSTATUS_UIL                     0x000000ff /* uil[7:0] */

The bit fields in the comments are out of date.

Alistair

> +
>  /* 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 d2585395bf..320b18ab60 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -188,6 +188,12 @@ static int pmp(CPURISCVState *env, int csrno)
>  {
>      return -!riscv_feature(env, RISCV_FEATURE_PMP);
>  }
> +
> +static int clic(CPURISCVState *env, int csrno)
> +{
> +    return !!env->clic;
> +}
> +
>  #endif
>
>  /* User Floating-Point CSRs */
> @@ -734,6 +740,12 @@ static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value,
>      return 0;
>  }
>
> +static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> +    *val = env->mintstatus;
> +    return 0;
> +}
> +
>  /* Supervisor Trap Setup */
>  static int read_sstatus(CPURISCVState *env, int csrno, target_ulong *val)
>  {
> @@ -893,6 +905,13 @@ static int rmw_sip(CPURISCVState *env, int csrno, target_ulong *ret_value,
>      return ret;
>  }
>
> +static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> +    target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
> +    *val = env->mintstatus & mask;
> +    return 0;
> +}
> +
>  /* Supervisor Protection and Translation */
>  static int read_satp(CPURISCVState *env, int csrno, target_ulong *val)
>  {
> @@ -1644,5 +1663,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>      [CSR_MHPMCOUNTER29H] = { "mhpmcounter29h", any32,  read_zero },
>      [CSR_MHPMCOUNTER30H] = { "mhpmcounter30h", any32,  read_zero },
>      [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", any32,  read_zero },
> +
> +    /* Machine Mode Core Level Interrupt Controller */
> +    [CSR_MINTSTATUS] = { "mintstatus", clic,  read_mintstatus },
> +
> +    /* Supervisor Mode Core Level Interrupt Controller */
> +    [CSR_SINTSTATUS] = { "sintstatus", clic,  read_sintstatus },
> +
>  #endif /* !CONFIG_USER_ONLY */
>  };
> --
> 2.25.1
>
>


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-04-09  7:48 ` [RFC PATCH 03/11] hw/intc: Add CLIC device LIU Zhiwei
@ 2021-04-19 23:25   ` Alistair Francis
  2021-04-20  0:57     ` LIU Zhiwei
  2021-06-13 10:10   ` Frank Chang
                     ` (6 subsequent siblings)
  7 siblings, 1 reply; 81+ messages in thread
From: Alistair Francis @ 2021-04-19 23:25 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

On Fri, Apr 9, 2021 at 5:56 PM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
>
> 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>
> ---
>  default-configs/devices/riscv32-softmmu.mak |   1 +
>  default-configs/devices/riscv64-softmmu.mak |   1 +
>  hw/intc/Kconfig                             |   3 +
>  hw/intc/meson.build                         |   1 +
>  hw/intc/riscv_clic.c                        | 835 ++++++++++++++++++++
>  include/hw/intc/riscv_clic.h                | 103 +++
>  target/riscv/cpu.h                          |   2 +
>  7 files changed, 946 insertions(+)
>  create mode 100644 hw/intc/riscv_clic.c
>  create mode 100644 include/hw/intc/riscv_clic.h
>
> diff --git a/default-configs/devices/riscv32-softmmu.mak b/default-configs/devices/riscv32-softmmu.mak
> index d847bd5692..1430c30588 100644
> --- a/default-configs/devices/riscv32-softmmu.mak
> +++ b/default-configs/devices/riscv32-softmmu.mak
> @@ -5,6 +5,7 @@
>  #CONFIG_PCI_DEVICES=n
>  CONFIG_SEMIHOSTING=y
>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> +CONFIG_RISCV_CLIC=y
>
>  # Boards:
>  #
> diff --git a/default-configs/devices/riscv64-softmmu.mak b/default-configs/devices/riscv64-softmmu.mak
> index d5eec75f05..396800bbbd 100644
> --- a/default-configs/devices/riscv64-softmmu.mak
> +++ b/default-configs/devices/riscv64-softmmu.mak
> @@ -5,6 +5,7 @@
>  #CONFIG_PCI_DEVICES=n
>  CONFIG_SEMIHOSTING=y
>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> +CONFIG_RISCV_CLIC=y
>
>  # Boards:
>  #
> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
> index f4694088a4..5bf492b48f 100644
> --- a/hw/intc/Kconfig
> +++ b/hw/intc/Kconfig
> @@ -68,6 +68,9 @@ config SIFIVE_CLINT
>  config SIFIVE_PLIC
>      bool
>
> +config RISCV_CLIC
> +    bool
> +
>  config GOLDFISH_PIC
>      bool
>
> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> index 1c299039f6..2aa71b6738 100644
> --- a/hw/intc/meson.build
> +++ b/hw/intc/meson.build
> @@ -50,6 +50,7 @@ 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_SIFIVE_CLINT', if_true: files('sifive_clint.c'))
>  specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.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'))
> diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
> new file mode 100644
> index 0000000000..8ad534c506
> --- /dev/null
> +++ b/hw/intc/riscv_clic.c
> @@ -0,0 +1,835 @@
> +/*
> + * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
> + *
> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.

Shouldn't this maintain the original copyright?

Alistair

> + *
> + * 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/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/log.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"
> +
> +/*
> + * 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_offset)
> +{
> +    return (clic->clicintattr[irq_offset] >> 1) & 0x3;
> +}
> +
> +static inline bool
> +riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
> +{
> +    return (clic->clicintattr[irq_offset] >> 1) & 0x1;
> +}
> +
> +static inline bool
> +riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
> +{
> +    return (clic->clicintattr[irq_offset] & 0x1) && clic->nvbits;
> +}
> +
> +static uint8_t
> +riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
> +{
> +    int nlbits = clic->nlbits;
> +
> +    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->nlbits;
> +    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);
> +}
> +
> +/*
> + * In a system with multiple harts, the M-mode CLIC regions for all the harts
> + * are placed contiguously in the memory space, followed by the S-mode CLIC
> + * regions for all harts. (Section 3.11)
> + */
> +static size_t
> +riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int hartid, int irq)
> +{
> +    size_t mode_offset = 0;
> +    size_t unit = clic->num_harts * clic->num_sources;
> +
> +    switch (mode) {
> +    case PRV_M:
> +        mode_offset = 0;
> +        break;
> +    case PRV_S:
> +        mode_offset = unit;
> +        break;
> +    case PRV_U:
> +        mode_offset = clic->prv_s ? 2 * unit : unit;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid mode %d\n", mode);
> +        exit(1);
> +    }
> +    return mode_offset + hartid * clic->num_sources + irq;
> +}
> +
> +static void riscv_clic_next_interrupt(void *opaque, int hartid)
> +{
> +    /*
> +     * 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
> +     */
> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(hartid));
> +    CPURISCVState *env = &cpu->env;
> +    RISCVCLICState *clic = (RISCVCLICState *)opaque;
> +
> +    int il[4] = {
> +        MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
> +            clic->mintthresh), /* PRV_U */
> +        MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
> +            clic->sintthresh), /* PRV_S */
> +        0,                     /* reserverd */
> +        MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
> +            clic->uintthresh)  /* PRV_M */
> +    };
> +
> +    /* Get sorted list of enabled interrupts for this hart */
> +    size_t hart_offset = hartid * clic->num_sources;
> +    CLICActiveInterrupt *active = &clic->active_list[hart_offset];
> +    size_t active_count = clic->active_count[hartid];
> +    uint8_t mode, level, priority;
> +
> +    /* Loop through the enabled interrupts sorted by mode+priority+level */
> +    while (active_count) {
> +        size_t irq_offset;
> +        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;
> +        }
> +        irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, active->irq);
> +        /* Check pending interrupt with high enough mode+priority+level */
> +        if (clic->clicintip[irq_offset]) {
> +            /* Clean vector edge-triggered pending */
> +            if (riscv_clic_is_edge_triggered(clic, irq_offset) &&
> +                riscv_clic_is_shv_interrupt(clic, irq_offset)) {
> +                clic->clicintip[irq_offset] = 0;
> +            }
> +            /* Post pending interrupt for this hart */
> +            clic->exccode[hartid] = active->irq | mode << 12 | level << 14;
> +            qemu_set_irq(clic->cpu_irqs[hartid], 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 hartid, int irq)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> +    if (!clic->prv_s && !clic->prv_u) { /* M */
> +        return mode == PRV_M;
> +    } else if (!clic->prv_s) { /* M/U */
> +        switch (clic->nmbits) {
> +        case 0:
> +            return mode == PRV_M;
> +        case 1:
> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M) :
> +                                                          (mode == PRV_U);
> +        default:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "clic: nmbits can only be 0 or 1 for M/U hart");
> +            exit(1);
> +        }
> +    } else { /* M/S/U */
> +        switch (clic->nmbits) {
> +        case 0:
> +            return mode == PRV_M;
> +        case 1:
> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M) :
> +                                                          (mode == PRV_S);
> +        case 2:
> +            return mode == clic->clicintattr[irq_offset];
> +        case 3:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "clic: nmbits can only be 0 or 1 or 2 for M/S/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 mode, int hartid, int irq)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
> +}
> +
> +static void
> +riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
> +                        int irq, uint64_t value)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> +    clic->clicintip[irq_offset] = !!value;
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +/*
> + * 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, uint64_t value)
> +{
> +    int mode = extract64(value, 6, 2);
> +
> +    if (!qtest_enabled()) {
> +        CPURISCVState *env = current_cpu->env_ptr;
> +        if (env->priv < mode) {
> +            return false;
> +        }
> +    }
> +    return true;
> +}
> +
> +static inline int riscv_clic_encode_priority(const CLICActiveInterrupt *i)
> +{
> +    return ((i->intcfg & 0x3ff) << 12) | /* Highest mode+level+priority */
> +           (i->irq & 0xfff);             /* Highest irq number */
> +}
> +
> +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 hartid,
> +                        int irq, uint64_t new_intie)
> +{
> +    size_t hart_offset = hartid * clic->num_sources;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> +    CLICActiveInterrupt *active_list = &clic->active_list[hart_offset];
> +    size_t *active_count = &clic->active_count[hartid];
> +
> +    uint8_t old_intie = clic->clicintie[irq_offset];
> +    clic->clicintie[irq_offset] = !!new_intie;
> +
> +    /* Add to or remove from list of active interrupts */
> +    if (new_intie && !old_intie) {
> +        active_list[*active_count].intcfg = (mode << 8) |
> +                                            clic->clicintctl[irq_offset];
> +        active_list[*active_count].irq = irq;
> +        (*active_count)++;
> +    } else if (!new_intie && old_intie) {
> +        CLICActiveInterrupt key = {
> +            (mode << 8) | clic->clicintctl[irq_offset], irq
> +        };
> +        CLICActiveInterrupt *result = bsearch(&key,
> +                                              active_list, *active_count,
> +                                              sizeof(CLICActiveInterrupt),
> +                                              riscv_clic_active_compare);
> +        size_t elem = (result - active_list) / sizeof(CLICActiveInterrupt);
> +        size_t sz = (--(*active_count) - elem) * sizeof(CLICActiveInterrupt);
> +        assert(result);
> +        memmove(&result[0], &result[1], sz);
> +    }
> +
> +    /* Sort list of active interrupts */
> +    qsort(active_list, *active_count,
> +          sizeof(CLICActiveInterrupt),
> +          riscv_clic_active_compare);
> +
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +static void
> +riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
> +                      uint64_t value, unsigned size,
> +                      int mode, int hartid, int irq)
> +{
> +    int req = extract32(addr, 0, 2);
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> +
> +    if (hartid >= clic->num_harts) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
> +                      hartid, addr);
> +        return;
> +    }
> +
> +    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, mode, hartid, irq)) {
> +            /*
> +             * The actual pending bit is located at bit 0 (i.e., the
> +             * leastsignificant 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_offset]) {
> +                riscv_clic_update_intip(clic, mode, hartid, irq, value);
> +            }
> +        }
> +        break;
> +    case 1: /* clicintie[i] */
> +        if (clic->clicintie[irq_offset] != value) {
> +            riscv_clic_update_intie(clic, mode, hartid, irq, value);
> +        }
> +        break;
> +    case 2: /* clicintattr[i] */
> +        if (riscv_clic_validate_intattr(clic, value)) {
> +            if (clic->clicintattr[irq_offset] != value) {
> +                /* When nmbits=2, check WARL */
> +                bool invalid = (clic->nmbits == 2) &&
> +                               (extract64(value, 6, 2) == 0b10);
> +                if (invalid) {
> +                    uint8_t old_mode = extract32(clic->clicintattr[irq_offset],
> +                                                 6, 2);
> +                    value = deposit32(value, 6, 2, old_mode);
> +                }
> +                clic->clicintattr[irq_offset] = value;
> +                riscv_clic_next_interrupt(clic, hartid);
> +            }
> +        }
> +        break;
> +    case 3: /* clicintctl[i] */
> +        if (value != clic->clicintctl[irq_offset]) {
> +            clic->clicintctl[irq_offset] = value;
> +            riscv_clic_next_interrupt(clic, hartid);
> +        }
> +        break;
> +    }
> +}
> +
> +static uint64_t
> +riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
> +                     int hartid, int irq)
> +{
> +    int req = extract32(addr, 0, 2);
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> +
> +    if (hartid >= clic->num_harts) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
> +                      hartid, addr);
> +        return 0;
> +    }
> +
> +    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] */
> +        return clic->clicintip[irq_offset];
> +    case 1: /* clicintie[i] */
> +        return clic->clicintie[irq_offset];
> +    case 2: /* clicintattr[i] */
> +        /*
> +         * clicintattr register layout
> +         * Bits Field
> +         * 7:6 mode
> +         * 5:3 reserved (WPRI 0)
> +         * 2:1 trig
> +         * 0 shv
> +         */
> +        return clic->clicintattr[irq_offset] & ~0x38;
> +    case 3: /* clicintctrl */
> +        /*
> +         * 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_offset] |
> +               ((1 << (8 - clic->clicintctlbits)) - 1);
> +    }
> +
> +    return 0;
> +}
> +
> +/* Return target interrupt mode */
> +static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int mode = addr / (4 * clic->num_harts * clic->num_sources);
> +    switch (mode) {
> +    case 0:
> +        return PRV_M;
> +    case 1:
> +        assert(clic->prv_s || clic->prv_u);
> +        return clic->prv_s ? PRV_S : PRV_U;
> +    case 2:
> +        assert(clic->prv_s && clic->prv_u);
> +        return PRV_U;
> +    default:
> +        g_assert_not_reached();
> +        break;
> +    }
> +}
> +
> +/* Return target hart id */
> +static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int mode_unit = 4 * clic->num_harts * clic->num_sources;
> +    int hart_unit = 4 * clic->num_sources;
> +
> +    return (addr % mode_unit) / hart_unit;
> +}
> +
> +/* Return target interrupt number */
> +static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int hart_unit = 4 * clic->num_sources;
> +    return (addr % hart_unit) / 4;
> +}
> +
> +static void
> +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
> +{
> +    RISCVCLICState *clic = opaque;
> +    hwaddr clic_size = clic->clic_size;
> +    int hartid, mode, irq;
> +
> +    if (addr < clic_size) {
> +        if (addr < 0x1000) {
> +            assert(addr % 4 == 0);
> +            int index = addr / 4;
> +            switch (index) {
> +            case 0: /* cliccfg */
> +                {
> +                    uint8_t nlbits = extract32(value, 1, 4);
> +                    uint8_t nmbits = extract32(value, 5, 2);
> +
> +                    /*
> +                     * The 4-bit cliccfg.nlbits WARL field.
> +                     * Valid values are 0—8.
> +                     */
> +                    if (nlbits <= 8) {
> +                        clic->nlbits = nlbits;
> +                    }
> +                    /* Valid values are given by implemented priviledges */
> +                    if (clic->prv_s && clic->prv_u) {
> +                        if (nmbits <= 2) {
> +                            clic->nmbits = nmbits;
> +                        }
> +                    } else if (clic->prv_u) {
> +                        if (nmbits <= 1) {
> +                            clic->nmbits = nmbits;
> +                        }
> +                    } else {
> +                        assert(!clic->prv_s);
> +                        if (nmbits == 0) {
> +                            clic->nmbits = 0;
> +                        }
> +                    }
> +                    clic->nvbits = extract32(value, 0, 1);
> +                    break;
> +                }
> +            case 1: /* clicinfo, read-only register */
> +                qemu_log_mask(LOG_GUEST_ERROR,
> +                              "clic: write read-only clicinfo.\n");
> +                break;
> +            case 0x10 ... 0x2F: /* clicinttrig */
> +                {
> +                    uint32_t interrupt_number = value & MAKE_64BIT_MASK(0, 13);
> +                    if (interrupt_number <= clic->num_sources) {
> +                        value &= ~MAKE_64BIT_MASK(13, 18);
> +                        clic->clicinttrig[index - 0x10] = value;
> +                    }
> +                    break;
> +                }
> +            case 2: /* mintthresh */
> +                if (!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 -= 0x1000;
> +            hartid = riscv_clic_get_hartid(clic, addr);
> +            mode = riscv_clic_get_mode(clic, addr);
> +            irq = riscv_clic_get_irq(clic, addr);
> +
> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
> +                riscv_clic_hart_write(clic, addr, value, size, mode,
> +                                      hartid, irq);
> +            }
> +        }
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid write: 0x%" HWADDR_PRIx "\n", addr);
> +    }
> +}
> +
> +static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    RISCVCLICState *clic = opaque;
> +    hwaddr clic_size = clic->clic_size;
> +    int hartid, mode, irq;
> +
> +    if (addr < clic_size) {
> +        if (addr < 0x1000) {
> +            assert(addr % 4 == 0);
> +            int index = addr / 4;
> +            switch (index) {
> +            case 0: /* cliccfg */
> +                return clic->nvbits |
> +                       (clic->nlbits << 1) |
> +                       (clic->nmbits << 5);
> +            case 1: /* clicinfo */
> +                /*
> +                 * clicinfo register layout
> +                 *
> +                 * Bits Field
> +                 * 31 reserved (WARL 0)
> +                 * 30:25 num_trigger
> +                 * 24:21 CLICINTCTLBITS
> +                 * 20:13 version (for version control)
> +                 * 12:0 num_interrupt
> +                 */
> +                return clic->clicinfo & ~INT32_MAX;
> +            case 0x10 ... 0x2F: /* clicinttrig */
> +                /*
> +                 * clicinttrig register layout
> +                 *
> +                 * Bits Field
> +                 * 31 enable
> +                 * 30:13 reserved (WARL 0)
> +                 * 12:0 interrupt_number
> +                 */
> +                return clic->clicinttrig[index - 0x10] &
> +                       ~MAKE_64BIT_MASK(13, 18);
> +            case 2: /* mintthresh */
> +                if (!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 -= 0x1000;
> +            hartid = riscv_clic_get_hartid(clic, addr);
> +            mode = riscv_clic_get_mode(clic, addr);
> +            irq = riscv_clic_get_irq(clic, addr);
> +
> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
> +                return riscv_clic_hart_read(clic, addr, mode, hartid, irq);
> +            }
> +        }
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid read: 0x%" HWADDR_PRIx "\n", addr);
> +    }
> +    return 0;
> +}
> +
> +static void riscv_clic_set_irq(void *opaque, int id, int level)
> +{
> +    RISCVCLICState *clic = opaque;
> +    int irq, hartid, mode;
> +    hwaddr addr = 4 * id;
> +    TRIG_TYPE type;
> +
> +    hartid = riscv_clic_get_hartid(clic, addr);
> +    mode = riscv_clic_get_mode(clic, addr);
> +    irq = riscv_clic_get_irq(clic, addr);
> +    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, mode, hartid, irq, level);
> +            break;
> +        case NEG_LEVEL:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
> +            break;
> +        case NEG_EDGE:
> +            break;
> +        }
> +    } else {
> +        switch (type) {
> +        case POSITIVE_LEVEL:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, level);
> +            break;
> +        case POSITIVE_EDGE:
> +            break;
> +        case NEG_LEVEL:
> +        case NEG_EDGE:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
> +            break;
> +        }
> +    }
> +}
> +
> +static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level)
> +{
> +    CPURISCVState *env = (CPURISCVState *)opaque;
> +    RISCVCLICState *clic = env->clic;
> +    CPUState *cpu = env_cpu(env);
> +
> +    if (level) {
> +        env->exccode = clic->exccode[cpu->cpu_index];
> +        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 harts_x_sources = clic->num_harts * clic->num_sources;
> +    int irqs, i;
> +
> +    if (clic->prv_s && clic->prv_u) {
> +        irqs = 3 * harts_x_sources;
> +    } else if (clic->prv_s || clic->prv_u) {
> +        irqs = 2 * harts_x_sources;
> +    } else {
> +        irqs = harts_x_sources;
> +    }
> +
> +    clic->clic_size = irqs * 4 + 0x1000;
> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops, clic,
> +                          TYPE_RISCV_CLIC, clic->clic_size);
> +
> +    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);
> +    clic->active_count = g_new0(size_t, clic->num_harts);
> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
> +
> +    /* Allocate irq 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_irqs, clic->num_harts);
> +
> +    for (i = 0; i < clic->num_harts; i++) {
> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
> +                                         &cpu->env, 1);
> +        qdev_connect_gpio_out(dev, i, irq);
> +        cpu->env.clic = clic;
> +    }
> +}
> +
> +static Property riscv_clic_properties[] = {
> +    DEFINE_PROP_BOOL("prv-s", RISCVCLICState, prv_s, false),
> +    DEFINE_PROP_BOOL("prv-u", RISCVCLICState, prv_u, false),
> +    DEFINE_PROP_UINT32("num-harts", RISCVCLICState, num_harts, 0),
> +    DEFINE_PROP_UINT32("num-sources", RISCVCLICState, num_sources, 0),
> +    DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICState, clicintctlbits, 0),
> +    DEFINE_PROP_UINT64("mclicbase", RISCVCLICState, mclicbase, 0),
> +    DEFINE_PROP_STRING("version", RISCVCLICState, version),
> +    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 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 void riscv_clic_register_types(void)
> +{
> +    type_register_static(&riscv_clic_info);
> +}
> +
> +type_init(riscv_clic_register_types)
> +
> +/*
> + * riscv_clic_create:
> + *
> + * @addr: base address of M-Mode CLIC memory-mapped registers
> + * @prv_s: have smode region
> + * @prv_u: have umode region
> + * @num_harts: number of CPU harts
> + * @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"
> + *
> + * Returns: the device object
> + */
> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
> +                               uint32_t num_harts, uint32_t num_sources,
> +                               uint8_t clicintctlbits,
> +                               const char *version)
> +{
> +    DeviceState *dev = qdev_new(TYPE_RISCV_CLIC);
> +
> +    assert(num_sources <= 4096);
> +    assert(num_harts <= 1024);
> +    assert(clicintctlbits <= 8);
> +    assert(!strcmp(version, "v0.8") || !strcmp(version, "v0.9"));
> +
> +    qdev_prop_set_bit(dev, "prv-s", prv_s);
> +    qdev_prop_set_bit(dev, "prv-u", prv_u);
> +    qdev_prop_set_uint32(dev, "num-harts", num_harts);
> +    qdev_prop_set_uint32(dev, "num-sources", num_sources);
> +    qdev_prop_set_uint32(dev, "clicintctlbits", clicintctlbits);
> +    qdev_prop_set_uint64(dev, "mclicbase", addr);
> +    qdev_prop_set_string(dev, "version", version);
> +
> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
> +    return dev;
> +}
> +
> +void riscv_clic_get_next_interrupt(void *opaque, int hartid)
> +{
> +    RISCVCLICState *clic = opaque;
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> +    return riscv_clic_is_shv_interrupt(clic, irq_offset);
> +}
> +
> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
> +}
> +
> +void riscv_clic_clean_pending(void *opaque, int mode, int hartid, int irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> +    clic->clicintip[irq_offset] = 0;
> +}
> +
> +/*
> + * 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.
> + */
> +bool riscv_clic_is_clic_mode(CPURISCVState *env)
> +{
> +    target_ulong xtvec = (env->priv == PRV_M) ? env->mtvec : env->stvec;
> +    return env->clic && ((xtvec & 0x3) == 3);
> +}
> +
> +void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
> +                               int *il, int *irq)
> +{
> +    *irq = extract32(exccode, 0, 12);
> +    *mode = extract32(exccode, 12, 2);
> +    *il = extract32(exccode, 14, 8);
> +}
> diff --git a/include/hw/intc/riscv_clic.h b/include/hw/intc/riscv_clic.h
> new file mode 100644
> index 0000000000..e5f89672a6
> --- /dev/null
> +++ b/include/hw/intc/riscv_clic.h
> @@ -0,0 +1,103 @@
> +/*
> + * RISC-V CLIC(Core Local Interrupt Controller) interface.
> + *
> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
> + *
> + * 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/>.
> + */
> +
> +#ifndef RISCV_CLIC_H
> +#define RISCV_CLIC_H
> +
> +#include "hw/irq.h"
> +#include "hw/sysbus.h"
> +
> +#define TYPE_RISCV_CLIC "riscv_clic"
> +#define RISCV_CLIC(obj) \
> +    OBJECT_CHECK(RISCVCLICState, (obj), TYPE_RISCV_CLIC)
> +
> +/*
> + * 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;
> +
> +typedef struct RISCVCLICState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +
> +    /* Implementaion parameters */
> +    bool prv_s;
> +    bool prv_u;
> +    uint32_t num_harts;
> +    uint32_t num_sources;
> +    uint32_t clic_size;
> +    uint32_t clic_mmode_base;
> +    uint32_t clicintctlbits;
> +    uint64_t mclicbase;
> +    char *version;
> +
> +    /* Global configuration */
> +    uint8_t nmbits;
> +    uint8_t nlbits;
> +    uint8_t nvbits;
> +    uint32_t clicinfo;
> +    uint32_t clicinttrig[32];
> +
> +    /* Aperture configuration */
> +    uint8_t *clicintip;
> +    uint8_t *clicintie;
> +    uint8_t *clicintattr;
> +    uint8_t *clicintctl;
> +
> +    /* Complatible with v0.8 */
> +    uint32_t mintthresh;
> +    uint32_t sintthresh;
> +    uint32_t uintthresh;
> +
> +    /* QEMU implementaion related fields */
> +    uint32_t *exccode;
> +    CLICActiveInterrupt *active_list;
> +    size_t *active_count;
> +    MemoryRegion mmio;
> +    qemu_irq *cpu_irqs;
> +} RISCVCLICState;
> +
> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
> +                               uint32_t num_harts, 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 mode, int hartid, int irq);
> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int irq);
> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int irq);
> +void riscv_clic_get_next_interrupt(void *opaque, int hartid);
> +bool riscv_clic_is_clic_mode(CPURISCVState *env);
> +#endif
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index a5eab26a69..9e389d7bbf 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -33,6 +33,7 @@
>  #define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
>  #define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
>  #define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
> +#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0
>
>  #define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
>  #define TYPE_RISCV_CPU_BASE32           RISCV_CPU_TYPE_NAME("rv32")
> @@ -247,6 +248,7 @@ struct CPURISCVState {
>      /* Fields from here on are preserved across CPU reset. */
>      QEMUTimer *timer; /* Internal timer */
>      void *clic;       /* clic interrupt controller */
> +    uint32_t exccode; /* clic irq encode */
>  };
>
>  OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
> --
> 2.25.1
>
>


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 00/11] RISC-V: support clic v0.9 specification
  2021-04-09  7:48 [RFC PATCH 00/11] RISC-V: support clic v0.9 specification LIU Zhiwei
                   ` (10 preceding siblings ...)
  2021-04-09  7:48 ` [RFC PATCH 11/11] target/riscv: Update interrupt return " LIU Zhiwei
@ 2021-04-19 23:30 ` Alistair Francis
  2021-04-20  1:44   ` LIU Zhiwei
  2021-06-27 15:55 ` Frank Chang
  12 siblings, 1 reply; 81+ messages in thread
From: Alistair Francis @ 2021-04-19 23:30 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

On Fri, Apr 9, 2021 at 5:56 PM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
>
> 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.
>
> I take over the job from Michael Clark, who gave the first implementation
> of clic-v0.7 specification. If there is any copyright question, please
> let me know.

You need to make sure you leave all original copyright notices and SoB in place.

>
> Features:
> 1. support four kinds of trigger types.
> 2. Preserve the CSR WARL/WPRI semantics.
> 3. Option to select different modes, such as M/S/U or M/U.
> 4. At most 4096 interrupts.
> 5. At most 1024 apertures.
>
> Todo:
> 1. Encode the interrupt trigger information to exccode.
> 2. Support complete CSR mclicbase when its number is fixed.
> 3. Gave each aperture an independend address.
>
> It have passed my qtest case and freertos test. Welcome to have a try
> for your hardware.

It doesn't seem to be connected to any machine. How are you testing this?

Alistair

>
> Any advice is welcomed. Thanks very much.
>
> Zhiwei
>
> [1] specification website: https://github.com/riscv/riscv-fast-interrupt.
> [2] Michael Clark origin work: https://github.com/sifive/riscv-qemu/tree/sifive-clic.
>
>
> LIU Zhiwei (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 xtvt in CLIC mode
>   target/riscv: Update CSR xnxti in CLIC mode
>   target/riscv: Update CSR mclicbase in CLIC mode
>   target/riscv: Update interrupt handling in CLIC mode
>   target/riscv: Update interrupt return in CLIC mode
>
>  default-configs/devices/riscv32-softmmu.mak |   1 +
>  default-configs/devices/riscv64-softmmu.mak |   1 +
>  hw/intc/Kconfig                             |   3 +
>  hw/intc/meson.build                         |   1 +
>  hw/intc/riscv_clic.c                        | 836 ++++++++++++++++++++
>  include/hw/intc/riscv_clic.h                | 103 +++
>  target/riscv/cpu.h                          |   9 +
>  target/riscv/cpu_bits.h                     |  32 +
>  target/riscv/cpu_helper.c                   | 117 ++-
>  target/riscv/csr.c                          | 247 +++++-
>  target/riscv/op_helper.c                    |  25 +
>  11 files changed, 1363 insertions(+), 12 deletions(-)
>  create mode 100644 hw/intc/riscv_clic.c
>  create mode 100644 include/hw/intc/riscv_clic.h
>
> --
> 2.25.1
>
>


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 01/11] target/riscv: Add CLIC CSR mintstatus
  2021-04-19 23:23   ` Alistair Francis
@ 2021-04-20  0:49     ` LIU Zhiwei
  2021-07-01  8:45       ` Frank Chang
  0 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-20  0:49 UTC (permalink / raw)
  To: Alistair Francis
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768


On 2021/4/20 上午7:23, Alistair Francis wrote:
> On Fri, Apr 9, 2021 at 5:52 PM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
>> CSR mintstatus holds the active interrupt level for each supported
>> privilege mode. sintstatus, and user, uintstatus, provide restricted
>> views of mintstatus.
>>
>> Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
>> ---
>>   target/riscv/cpu.h      |  2 ++
>>   target/riscv/cpu_bits.h | 11 +++++++++++
>>   target/riscv/csr.c      | 26 ++++++++++++++++++++++++++
>>   3 files changed, 39 insertions(+)
>>
>> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>> index 0a33d387ba..1a44ca62c7 100644
>> --- a/target/riscv/cpu.h
>> +++ b/target/riscv/cpu.h
>> @@ -159,6 +159,7 @@ struct CPURISCVState {
>>       target_ulong mip;
>>
>>       uint32_t miclaim;
>> +    uint32_t mintstatus; /* clic-spec */
>>
>>       target_ulong mie;
>>       target_ulong mideleg;
>> @@ -243,6 +244,7 @@ struct CPURISCVState {
>>
>>       /* Fields from here on are preserved across CPU reset. */
>>       QEMUTimer *timer; /* Internal timer */
>> +    void *clic;       /* clic interrupt controller */
> This should be the CLIC type.

OK.

Actually there are many versions of CLIC in my branch as different 
devices. But it is better to use CLIC type for the upstream version.

>
>>   };
>>
>>   OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
>> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
>> index caf4599207..c4ce6ec3d9 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      0x346 /* clic-spec-draft */
>>
>>   /* Legacy Machine Trap Handling (priv v1.9.1) */
>>   #define CSR_MBADADDR        0x343
>> @@ -183,6 +184,7 @@
>>   #define CSR_SCAUSE          0x142
>>   #define CSR_STVAL           0x143
>>   #define CSR_SIP             0x144
>> +#define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
>>
>>   /* Legacy Supervisor Trap Handling (priv v1.9.1) */
>>   #define CSR_SBADADDR        0x143
>> @@ -585,6 +587,15 @@
>>   #define SIP_STIP                           MIP_STIP
>>   #define SIP_SEIP                           MIP_SEIP
>>
>> +/* mintstatus */
>> +#define MINTSTATUS_MIL                     0xff000000 /* mil[7:0] */
>> +#define MINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
>> +#define MINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
>> +
>> +/* sintstatus */
>> +#define SINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
>> +#define SINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
> The bit fields in the comments are out of date.

I didn't notice it.   Fix it in next version.

Thanks.

Zhiwei

>
> Alistair
>
>> +
>>   /* 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 d2585395bf..320b18ab60 100644
>> --- a/target/riscv/csr.c
>> +++ b/target/riscv/csr.c
>> @@ -188,6 +188,12 @@ static int pmp(CPURISCVState *env, int csrno)
>>   {
>>       return -!riscv_feature(env, RISCV_FEATURE_PMP);
>>   }
>> +
>> +static int clic(CPURISCVState *env, int csrno)
>> +{
>> +    return !!env->clic;
>> +}
>> +
>>   #endif
>>
>>   /* User Floating-Point CSRs */
>> @@ -734,6 +740,12 @@ static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value,
>>       return 0;
>>   }
>>
>> +static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
>> +{
>> +    *val = env->mintstatus;
>> +    return 0;
>> +}
>> +
>>   /* Supervisor Trap Setup */
>>   static int read_sstatus(CPURISCVState *env, int csrno, target_ulong *val)
>>   {
>> @@ -893,6 +905,13 @@ static int rmw_sip(CPURISCVState *env, int csrno, target_ulong *ret_value,
>>       return ret;
>>   }
>>
>> +static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
>> +{
>> +    target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
>> +    *val = env->mintstatus & mask;
>> +    return 0;
>> +}
>> +
>>   /* Supervisor Protection and Translation */
>>   static int read_satp(CPURISCVState *env, int csrno, target_ulong *val)
>>   {
>> @@ -1644,5 +1663,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>>       [CSR_MHPMCOUNTER29H] = { "mhpmcounter29h", any32,  read_zero },
>>       [CSR_MHPMCOUNTER30H] = { "mhpmcounter30h", any32,  read_zero },
>>       [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", any32,  read_zero },
>> +
>> +    /* Machine Mode Core Level Interrupt Controller */
>> +    [CSR_MINTSTATUS] = { "mintstatus", clic,  read_mintstatus },
>> +
>> +    /* Supervisor Mode Core Level Interrupt Controller */
>> +    [CSR_SINTSTATUS] = { "sintstatus", clic,  read_sintstatus },
>> +
>>   #endif /* !CONFIG_USER_ONLY */
>>   };
>> --
>> 2.25.1
>>
>>


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-04-19 23:25   ` Alistair Francis
@ 2021-04-20  0:57     ` LIU Zhiwei
  2021-04-22  0:16       ` Alistair Francis
  0 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-20  0:57 UTC (permalink / raw)
  To: Alistair Francis
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768


On 2021/4/20 上午7:25, Alistair Francis wrote:
> On Fri, Apr 9, 2021 at 5:56 PM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
>> 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>
>> ---
>>   default-configs/devices/riscv32-softmmu.mak |   1 +
>>   default-configs/devices/riscv64-softmmu.mak |   1 +
>>   hw/intc/Kconfig                             |   3 +
>>   hw/intc/meson.build                         |   1 +
>>   hw/intc/riscv_clic.c                        | 835 ++++++++++++++++++++
>>   include/hw/intc/riscv_clic.h                | 103 +++
>>   target/riscv/cpu.h                          |   2 +
>>   7 files changed, 946 insertions(+)
>>   create mode 100644 hw/intc/riscv_clic.c
>>   create mode 100644 include/hw/intc/riscv_clic.h
>>
>> diff --git a/default-configs/devices/riscv32-softmmu.mak b/default-configs/devices/riscv32-softmmu.mak
>> index d847bd5692..1430c30588 100644
>> --- a/default-configs/devices/riscv32-softmmu.mak
>> +++ b/default-configs/devices/riscv32-softmmu.mak
>> @@ -5,6 +5,7 @@
>>   #CONFIG_PCI_DEVICES=n
>>   CONFIG_SEMIHOSTING=y
>>   CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>> +CONFIG_RISCV_CLIC=y
>>
>>   # Boards:
>>   #
>> diff --git a/default-configs/devices/riscv64-softmmu.mak b/default-configs/devices/riscv64-softmmu.mak
>> index d5eec75f05..396800bbbd 100644
>> --- a/default-configs/devices/riscv64-softmmu.mak
>> +++ b/default-configs/devices/riscv64-softmmu.mak
>> @@ -5,6 +5,7 @@
>>   #CONFIG_PCI_DEVICES=n
>>   CONFIG_SEMIHOSTING=y
>>   CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>> +CONFIG_RISCV_CLIC=y
>>
>>   # Boards:
>>   #
>> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
>> index f4694088a4..5bf492b48f 100644
>> --- a/hw/intc/Kconfig
>> +++ b/hw/intc/Kconfig
>> @@ -68,6 +68,9 @@ config SIFIVE_CLINT
>>   config SIFIVE_PLIC
>>       bool
>>
>> +config RISCV_CLIC
>> +    bool
>> +
>>   config GOLDFISH_PIC
>>       bool
>>
>> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
>> index 1c299039f6..2aa71b6738 100644
>> --- a/hw/intc/meson.build
>> +++ b/hw/intc/meson.build
>> @@ -50,6 +50,7 @@ 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_SIFIVE_CLINT', if_true: files('sifive_clint.c'))
>>   specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.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'))
>> diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
>> new file mode 100644
>> index 0000000000..8ad534c506
>> --- /dev/null
>> +++ b/hw/intc/riscv_clic.c
>> @@ -0,0 +1,835 @@
>> +/*
>> + * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
>> + *
>> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
> Shouldn't this maintain the original copyright?

It is OK.

As it has changed a lot from the origin version, it is just another 
device called sifive_clic  in my branch, and this is the device called 
riscv_clic.

If that is the rule, I am very glad to obey it.

Thanks for pointing it out.

Zhiwei

> Alistair
>
>> + *
>> + * 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/>.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qapi/error.h"
>> +#include "qemu/log.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"
>> +
>> +/*
>> + * 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_offset)
>> +{
>> +    return (clic->clicintattr[irq_offset] >> 1) & 0x3;
>> +}
>> +
>> +static inline bool
>> +riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
>> +{
>> +    return (clic->clicintattr[irq_offset] >> 1) & 0x1;
>> +}
>> +
>> +static inline bool
>> +riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
>> +{
>> +    return (clic->clicintattr[irq_offset] & 0x1) && clic->nvbits;
>> +}
>> +
>> +static uint8_t
>> +riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
>> +{
>> +    int nlbits = clic->nlbits;
>> +
>> +    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->nlbits;
>> +    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);
>> +}
>> +
>> +/*
>> + * In a system with multiple harts, the M-mode CLIC regions for all the harts
>> + * are placed contiguously in the memory space, followed by the S-mode CLIC
>> + * regions for all harts. (Section 3.11)
>> + */
>> +static size_t
>> +riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int hartid, int irq)
>> +{
>> +    size_t mode_offset = 0;
>> +    size_t unit = clic->num_harts * clic->num_sources;
>> +
>> +    switch (mode) {
>> +    case PRV_M:
>> +        mode_offset = 0;
>> +        break;
>> +    case PRV_S:
>> +        mode_offset = unit;
>> +        break;
>> +    case PRV_U:
>> +        mode_offset = clic->prv_s ? 2 * unit : unit;
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid mode %d\n", mode);
>> +        exit(1);
>> +    }
>> +    return mode_offset + hartid * clic->num_sources + irq;
>> +}
>> +
>> +static void riscv_clic_next_interrupt(void *opaque, int hartid)
>> +{
>> +    /*
>> +     * 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
>> +     */
>> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(hartid));
>> +    CPURISCVState *env = &cpu->env;
>> +    RISCVCLICState *clic = (RISCVCLICState *)opaque;
>> +
>> +    int il[4] = {
>> +        MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
>> +            clic->mintthresh), /* PRV_U */
>> +        MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
>> +            clic->sintthresh), /* PRV_S */
>> +        0,                     /* reserverd */
>> +        MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
>> +            clic->uintthresh)  /* PRV_M */
>> +    };
>> +
>> +    /* Get sorted list of enabled interrupts for this hart */
>> +    size_t hart_offset = hartid * clic->num_sources;
>> +    CLICActiveInterrupt *active = &clic->active_list[hart_offset];
>> +    size_t active_count = clic->active_count[hartid];
>> +    uint8_t mode, level, priority;
>> +
>> +    /* Loop through the enabled interrupts sorted by mode+priority+level */
>> +    while (active_count) {
>> +        size_t irq_offset;
>> +        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;
>> +        }
>> +        irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, active->irq);
>> +        /* Check pending interrupt with high enough mode+priority+level */
>> +        if (clic->clicintip[irq_offset]) {
>> +            /* Clean vector edge-triggered pending */
>> +            if (riscv_clic_is_edge_triggered(clic, irq_offset) &&
>> +                riscv_clic_is_shv_interrupt(clic, irq_offset)) {
>> +                clic->clicintip[irq_offset] = 0;
>> +            }
>> +            /* Post pending interrupt for this hart */
>> +            clic->exccode[hartid] = active->irq | mode << 12 | level << 14;
>> +            qemu_set_irq(clic->cpu_irqs[hartid], 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 hartid, int irq)
>> +{
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
>> +    if (!clic->prv_s && !clic->prv_u) { /* M */
>> +        return mode == PRV_M;
>> +    } else if (!clic->prv_s) { /* M/U */
>> +        switch (clic->nmbits) {
>> +        case 0:
>> +            return mode == PRV_M;
>> +        case 1:
>> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M) :
>> +                                                          (mode == PRV_U);
>> +        default:
>> +            qemu_log_mask(LOG_GUEST_ERROR,
>> +                "clic: nmbits can only be 0 or 1 for M/U hart");
>> +            exit(1);
>> +        }
>> +    } else { /* M/S/U */
>> +        switch (clic->nmbits) {
>> +        case 0:
>> +            return mode == PRV_M;
>> +        case 1:
>> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M) :
>> +                                                          (mode == PRV_S);
>> +        case 2:
>> +            return mode == clic->clicintattr[irq_offset];
>> +        case 3:
>> +            qemu_log_mask(LOG_GUEST_ERROR,
>> +                "clic: nmbits can only be 0 or 1 or 2 for M/S/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 mode, int hartid, int irq)
>> +{
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
>> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
>> +}
>> +
>> +static void
>> +riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
>> +                        int irq, uint64_t value)
>> +{
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
>> +    clic->clicintip[irq_offset] = !!value;
>> +    riscv_clic_next_interrupt(clic, hartid);
>> +}
>> +
>> +/*
>> + * 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, uint64_t value)
>> +{
>> +    int mode = extract64(value, 6, 2);
>> +
>> +    if (!qtest_enabled()) {
>> +        CPURISCVState *env = current_cpu->env_ptr;
>> +        if (env->priv < mode) {
>> +            return false;
>> +        }
>> +    }
>> +    return true;
>> +}
>> +
>> +static inline int riscv_clic_encode_priority(const CLICActiveInterrupt *i)
>> +{
>> +    return ((i->intcfg & 0x3ff) << 12) | /* Highest mode+level+priority */
>> +           (i->irq & 0xfff);             /* Highest irq number */
>> +}
>> +
>> +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 hartid,
>> +                        int irq, uint64_t new_intie)
>> +{
>> +    size_t hart_offset = hartid * clic->num_sources;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
>> +    CLICActiveInterrupt *active_list = &clic->active_list[hart_offset];
>> +    size_t *active_count = &clic->active_count[hartid];
>> +
>> +    uint8_t old_intie = clic->clicintie[irq_offset];
>> +    clic->clicintie[irq_offset] = !!new_intie;
>> +
>> +    /* Add to or remove from list of active interrupts */
>> +    if (new_intie && !old_intie) {
>> +        active_list[*active_count].intcfg = (mode << 8) |
>> +                                            clic->clicintctl[irq_offset];
>> +        active_list[*active_count].irq = irq;
>> +        (*active_count)++;
>> +    } else if (!new_intie && old_intie) {
>> +        CLICActiveInterrupt key = {
>> +            (mode << 8) | clic->clicintctl[irq_offset], irq
>> +        };
>> +        CLICActiveInterrupt *result = bsearch(&key,
>> +                                              active_list, *active_count,
>> +                                              sizeof(CLICActiveInterrupt),
>> +                                              riscv_clic_active_compare);
>> +        size_t elem = (result - active_list) / sizeof(CLICActiveInterrupt);
>> +        size_t sz = (--(*active_count) - elem) * sizeof(CLICActiveInterrupt);
>> +        assert(result);
>> +        memmove(&result[0], &result[1], sz);
>> +    }
>> +
>> +    /* Sort list of active interrupts */
>> +    qsort(active_list, *active_count,
>> +          sizeof(CLICActiveInterrupt),
>> +          riscv_clic_active_compare);
>> +
>> +    riscv_clic_next_interrupt(clic, hartid);
>> +}
>> +
>> +static void
>> +riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
>> +                      uint64_t value, unsigned size,
>> +                      int mode, int hartid, int irq)
>> +{
>> +    int req = extract32(addr, 0, 2);
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
>> +
>> +    if (hartid >= clic->num_harts) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
>> +                      hartid, addr);
>> +        return;
>> +    }
>> +
>> +    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, mode, hartid, irq)) {
>> +            /*
>> +             * The actual pending bit is located at bit 0 (i.e., the
>> +             * leastsignificant 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_offset]) {
>> +                riscv_clic_update_intip(clic, mode, hartid, irq, value);
>> +            }
>> +        }
>> +        break;
>> +    case 1: /* clicintie[i] */
>> +        if (clic->clicintie[irq_offset] != value) {
>> +            riscv_clic_update_intie(clic, mode, hartid, irq, value);
>> +        }
>> +        break;
>> +    case 2: /* clicintattr[i] */
>> +        if (riscv_clic_validate_intattr(clic, value)) {
>> +            if (clic->clicintattr[irq_offset] != value) {
>> +                /* When nmbits=2, check WARL */
>> +                bool invalid = (clic->nmbits == 2) &&
>> +                               (extract64(value, 6, 2) == 0b10);
>> +                if (invalid) {
>> +                    uint8_t old_mode = extract32(clic->clicintattr[irq_offset],
>> +                                                 6, 2);
>> +                    value = deposit32(value, 6, 2, old_mode);
>> +                }
>> +                clic->clicintattr[irq_offset] = value;
>> +                riscv_clic_next_interrupt(clic, hartid);
>> +            }
>> +        }
>> +        break;
>> +    case 3: /* clicintctl[i] */
>> +        if (value != clic->clicintctl[irq_offset]) {
>> +            clic->clicintctl[irq_offset] = value;
>> +            riscv_clic_next_interrupt(clic, hartid);
>> +        }
>> +        break;
>> +    }
>> +}
>> +
>> +static uint64_t
>> +riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
>> +                     int hartid, int irq)
>> +{
>> +    int req = extract32(addr, 0, 2);
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
>> +
>> +    if (hartid >= clic->num_harts) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
>> +                      hartid, addr);
>> +        return 0;
>> +    }
>> +
>> +    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] */
>> +        return clic->clicintip[irq_offset];
>> +    case 1: /* clicintie[i] */
>> +        return clic->clicintie[irq_offset];
>> +    case 2: /* clicintattr[i] */
>> +        /*
>> +         * clicintattr register layout
>> +         * Bits Field
>> +         * 7:6 mode
>> +         * 5:3 reserved (WPRI 0)
>> +         * 2:1 trig
>> +         * 0 shv
>> +         */
>> +        return clic->clicintattr[irq_offset] & ~0x38;
>> +    case 3: /* clicintctrl */
>> +        /*
>> +         * 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_offset] |
>> +               ((1 << (8 - clic->clicintctlbits)) - 1);
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +/* Return target interrupt mode */
>> +static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
>> +{
>> +    int mode = addr / (4 * clic->num_harts * clic->num_sources);
>> +    switch (mode) {
>> +    case 0:
>> +        return PRV_M;
>> +    case 1:
>> +        assert(clic->prv_s || clic->prv_u);
>> +        return clic->prv_s ? PRV_S : PRV_U;
>> +    case 2:
>> +        assert(clic->prv_s && clic->prv_u);
>> +        return PRV_U;
>> +    default:
>> +        g_assert_not_reached();
>> +        break;
>> +    }
>> +}
>> +
>> +/* Return target hart id */
>> +static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
>> +{
>> +    int mode_unit = 4 * clic->num_harts * clic->num_sources;
>> +    int hart_unit = 4 * clic->num_sources;
>> +
>> +    return (addr % mode_unit) / hart_unit;
>> +}
>> +
>> +/* Return target interrupt number */
>> +static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
>> +{
>> +    int hart_unit = 4 * clic->num_sources;
>> +    return (addr % hart_unit) / 4;
>> +}
>> +
>> +static void
>> +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    hwaddr clic_size = clic->clic_size;
>> +    int hartid, mode, irq;
>> +
>> +    if (addr < clic_size) {
>> +        if (addr < 0x1000) {
>> +            assert(addr % 4 == 0);
>> +            int index = addr / 4;
>> +            switch (index) {
>> +            case 0: /* cliccfg */
>> +                {
>> +                    uint8_t nlbits = extract32(value, 1, 4);
>> +                    uint8_t nmbits = extract32(value, 5, 2);
>> +
>> +                    /*
>> +                     * The 4-bit cliccfg.nlbits WARL field.
>> +                     * Valid values are 0—8.
>> +                     */
>> +                    if (nlbits <= 8) {
>> +                        clic->nlbits = nlbits;
>> +                    }
>> +                    /* Valid values are given by implemented priviledges */
>> +                    if (clic->prv_s && clic->prv_u) {
>> +                        if (nmbits <= 2) {
>> +                            clic->nmbits = nmbits;
>> +                        }
>> +                    } else if (clic->prv_u) {
>> +                        if (nmbits <= 1) {
>> +                            clic->nmbits = nmbits;
>> +                        }
>> +                    } else {
>> +                        assert(!clic->prv_s);
>> +                        if (nmbits == 0) {
>> +                            clic->nmbits = 0;
>> +                        }
>> +                    }
>> +                    clic->nvbits = extract32(value, 0, 1);
>> +                    break;
>> +                }
>> +            case 1: /* clicinfo, read-only register */
>> +                qemu_log_mask(LOG_GUEST_ERROR,
>> +                              "clic: write read-only clicinfo.\n");
>> +                break;
>> +            case 0x10 ... 0x2F: /* clicinttrig */
>> +                {
>> +                    uint32_t interrupt_number = value & MAKE_64BIT_MASK(0, 13);
>> +                    if (interrupt_number <= clic->num_sources) {
>> +                        value &= ~MAKE_64BIT_MASK(13, 18);
>> +                        clic->clicinttrig[index - 0x10] = value;
>> +                    }
>> +                    break;
>> +                }
>> +            case 2: /* mintthresh */
>> +                if (!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 -= 0x1000;
>> +            hartid = riscv_clic_get_hartid(clic, addr);
>> +            mode = riscv_clic_get_mode(clic, addr);
>> +            irq = riscv_clic_get_irq(clic, addr);
>> +
>> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
>> +                riscv_clic_hart_write(clic, addr, value, size, mode,
>> +                                      hartid, irq);
>> +            }
>> +        }
>> +    } else {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid write: 0x%" HWADDR_PRIx "\n", addr);
>> +    }
>> +}
>> +
>> +static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    hwaddr clic_size = clic->clic_size;
>> +    int hartid, mode, irq;
>> +
>> +    if (addr < clic_size) {
>> +        if (addr < 0x1000) {
>> +            assert(addr % 4 == 0);
>> +            int index = addr / 4;
>> +            switch (index) {
>> +            case 0: /* cliccfg */
>> +                return clic->nvbits |
>> +                       (clic->nlbits << 1) |
>> +                       (clic->nmbits << 5);
>> +            case 1: /* clicinfo */
>> +                /*
>> +                 * clicinfo register layout
>> +                 *
>> +                 * Bits Field
>> +                 * 31 reserved (WARL 0)
>> +                 * 30:25 num_trigger
>> +                 * 24:21 CLICINTCTLBITS
>> +                 * 20:13 version (for version control)
>> +                 * 12:0 num_interrupt
>> +                 */
>> +                return clic->clicinfo & ~INT32_MAX;
>> +            case 0x10 ... 0x2F: /* clicinttrig */
>> +                /*
>> +                 * clicinttrig register layout
>> +                 *
>> +                 * Bits Field
>> +                 * 31 enable
>> +                 * 30:13 reserved (WARL 0)
>> +                 * 12:0 interrupt_number
>> +                 */
>> +                return clic->clicinttrig[index - 0x10] &
>> +                       ~MAKE_64BIT_MASK(13, 18);
>> +            case 2: /* mintthresh */
>> +                if (!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 -= 0x1000;
>> +            hartid = riscv_clic_get_hartid(clic, addr);
>> +            mode = riscv_clic_get_mode(clic, addr);
>> +            irq = riscv_clic_get_irq(clic, addr);
>> +
>> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
>> +                return riscv_clic_hart_read(clic, addr, mode, hartid, irq);
>> +            }
>> +        }
>> +    } else {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid read: 0x%" HWADDR_PRIx "\n", addr);
>> +    }
>> +    return 0;
>> +}
>> +
>> +static void riscv_clic_set_irq(void *opaque, int id, int level)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    int irq, hartid, mode;
>> +    hwaddr addr = 4 * id;
>> +    TRIG_TYPE type;
>> +
>> +    hartid = riscv_clic_get_hartid(clic, addr);
>> +    mode = riscv_clic_get_mode(clic, addr);
>> +    irq = riscv_clic_get_irq(clic, addr);
>> +    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, mode, hartid, irq, level);
>> +            break;
>> +        case NEG_LEVEL:
>> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
>> +            break;
>> +        case NEG_EDGE:
>> +            break;
>> +        }
>> +    } else {
>> +        switch (type) {
>> +        case POSITIVE_LEVEL:
>> +            riscv_clic_update_intip(clic, mode, hartid, irq, level);
>> +            break;
>> +        case POSITIVE_EDGE:
>> +            break;
>> +        case NEG_LEVEL:
>> +        case NEG_EDGE:
>> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
>> +            break;
>> +        }
>> +    }
>> +}
>> +
>> +static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level)
>> +{
>> +    CPURISCVState *env = (CPURISCVState *)opaque;
>> +    RISCVCLICState *clic = env->clic;
>> +    CPUState *cpu = env_cpu(env);
>> +
>> +    if (level) {
>> +        env->exccode = clic->exccode[cpu->cpu_index];
>> +        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 harts_x_sources = clic->num_harts * clic->num_sources;
>> +    int irqs, i;
>> +
>> +    if (clic->prv_s && clic->prv_u) {
>> +        irqs = 3 * harts_x_sources;
>> +    } else if (clic->prv_s || clic->prv_u) {
>> +        irqs = 2 * harts_x_sources;
>> +    } else {
>> +        irqs = harts_x_sources;
>> +    }
>> +
>> +    clic->clic_size = irqs * 4 + 0x1000;
>> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops, clic,
>> +                          TYPE_RISCV_CLIC, clic->clic_size);
>> +
>> +    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);
>> +    clic->active_count = g_new0(size_t, clic->num_harts);
>> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
>> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
>> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
>> +
>> +    /* Allocate irq 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_irqs, clic->num_harts);
>> +
>> +    for (i = 0; i < clic->num_harts; i++) {
>> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>> +                                         &cpu->env, 1);
>> +        qdev_connect_gpio_out(dev, i, irq);
>> +        cpu->env.clic = clic;
>> +    }
>> +}
>> +
>> +static Property riscv_clic_properties[] = {
>> +    DEFINE_PROP_BOOL("prv-s", RISCVCLICState, prv_s, false),
>> +    DEFINE_PROP_BOOL("prv-u", RISCVCLICState, prv_u, false),
>> +    DEFINE_PROP_UINT32("num-harts", RISCVCLICState, num_harts, 0),
>> +    DEFINE_PROP_UINT32("num-sources", RISCVCLICState, num_sources, 0),
>> +    DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICState, clicintctlbits, 0),
>> +    DEFINE_PROP_UINT64("mclicbase", RISCVCLICState, mclicbase, 0),
>> +    DEFINE_PROP_STRING("version", RISCVCLICState, version),
>> +    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 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 void riscv_clic_register_types(void)
>> +{
>> +    type_register_static(&riscv_clic_info);
>> +}
>> +
>> +type_init(riscv_clic_register_types)
>> +
>> +/*
>> + * riscv_clic_create:
>> + *
>> + * @addr: base address of M-Mode CLIC memory-mapped registers
>> + * @prv_s: have smode region
>> + * @prv_u: have umode region
>> + * @num_harts: number of CPU harts
>> + * @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"
>> + *
>> + * Returns: the device object
>> + */
>> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
>> +                               uint32_t num_harts, uint32_t num_sources,
>> +                               uint8_t clicintctlbits,
>> +                               const char *version)
>> +{
>> +    DeviceState *dev = qdev_new(TYPE_RISCV_CLIC);
>> +
>> +    assert(num_sources <= 4096);
>> +    assert(num_harts <= 1024);
>> +    assert(clicintctlbits <= 8);
>> +    assert(!strcmp(version, "v0.8") || !strcmp(version, "v0.9"));
>> +
>> +    qdev_prop_set_bit(dev, "prv-s", prv_s);
>> +    qdev_prop_set_bit(dev, "prv-u", prv_u);
>> +    qdev_prop_set_uint32(dev, "num-harts", num_harts);
>> +    qdev_prop_set_uint32(dev, "num-sources", num_sources);
>> +    qdev_prop_set_uint32(dev, "clicintctlbits", clicintctlbits);
>> +    qdev_prop_set_uint64(dev, "mclicbase", addr);
>> +    qdev_prop_set_string(dev, "version", version);
>> +
>> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
>> +    return dev;
>> +}
>> +
>> +void riscv_clic_get_next_interrupt(void *opaque, int hartid)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    riscv_clic_next_interrupt(clic, hartid);
>> +}
>> +
>> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int irq)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
>> +    return riscv_clic_is_shv_interrupt(clic, irq_offset);
>> +}
>> +
>> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int irq)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
>> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
>> +}
>> +
>> +void riscv_clic_clean_pending(void *opaque, int mode, int hartid, int irq)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
>> +    clic->clicintip[irq_offset] = 0;
>> +}
>> +
>> +/*
>> + * 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.
>> + */
>> +bool riscv_clic_is_clic_mode(CPURISCVState *env)
>> +{
>> +    target_ulong xtvec = (env->priv == PRV_M) ? env->mtvec : env->stvec;
>> +    return env->clic && ((xtvec & 0x3) == 3);
>> +}
>> +
>> +void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
>> +                               int *il, int *irq)
>> +{
>> +    *irq = extract32(exccode, 0, 12);
>> +    *mode = extract32(exccode, 12, 2);
>> +    *il = extract32(exccode, 14, 8);
>> +}
>> diff --git a/include/hw/intc/riscv_clic.h b/include/hw/intc/riscv_clic.h
>> new file mode 100644
>> index 0000000000..e5f89672a6
>> --- /dev/null
>> +++ b/include/hw/intc/riscv_clic.h
>> @@ -0,0 +1,103 @@
>> +/*
>> + * RISC-V CLIC(Core Local Interrupt Controller) interface.
>> + *
>> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
>> + *
>> + * 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/>.
>> + */
>> +
>> +#ifndef RISCV_CLIC_H
>> +#define RISCV_CLIC_H
>> +
>> +#include "hw/irq.h"
>> +#include "hw/sysbus.h"
>> +
>> +#define TYPE_RISCV_CLIC "riscv_clic"
>> +#define RISCV_CLIC(obj) \
>> +    OBJECT_CHECK(RISCVCLICState, (obj), TYPE_RISCV_CLIC)
>> +
>> +/*
>> + * 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;
>> +
>> +typedef struct RISCVCLICState {
>> +    /*< private >*/
>> +    SysBusDevice parent_obj;
>> +
>> +    /*< public >*/
>> +
>> +    /* Implementaion parameters */
>> +    bool prv_s;
>> +    bool prv_u;
>> +    uint32_t num_harts;
>> +    uint32_t num_sources;
>> +    uint32_t clic_size;
>> +    uint32_t clic_mmode_base;
>> +    uint32_t clicintctlbits;
>> +    uint64_t mclicbase;
>> +    char *version;
>> +
>> +    /* Global configuration */
>> +    uint8_t nmbits;
>> +    uint8_t nlbits;
>> +    uint8_t nvbits;
>> +    uint32_t clicinfo;
>> +    uint32_t clicinttrig[32];
>> +
>> +    /* Aperture configuration */
>> +    uint8_t *clicintip;
>> +    uint8_t *clicintie;
>> +    uint8_t *clicintattr;
>> +    uint8_t *clicintctl;
>> +
>> +    /* Complatible with v0.8 */
>> +    uint32_t mintthresh;
>> +    uint32_t sintthresh;
>> +    uint32_t uintthresh;
>> +
>> +    /* QEMU implementaion related fields */
>> +    uint32_t *exccode;
>> +    CLICActiveInterrupt *active_list;
>> +    size_t *active_count;
>> +    MemoryRegion mmio;
>> +    qemu_irq *cpu_irqs;
>> +} RISCVCLICState;
>> +
>> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
>> +                               uint32_t num_harts, 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 mode, int hartid, int irq);
>> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int irq);
>> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int irq);
>> +void riscv_clic_get_next_interrupt(void *opaque, int hartid);
>> +bool riscv_clic_is_clic_mode(CPURISCVState *env);
>> +#endif
>> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>> index a5eab26a69..9e389d7bbf 100644
>> --- a/target/riscv/cpu.h
>> +++ b/target/riscv/cpu.h
>> @@ -33,6 +33,7 @@
>>   #define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
>>   #define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
>>   #define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
>> +#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0
>>
>>   #define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
>>   #define TYPE_RISCV_CPU_BASE32           RISCV_CPU_TYPE_NAME("rv32")
>> @@ -247,6 +248,7 @@ struct CPURISCVState {
>>       /* Fields from here on are preserved across CPU reset. */
>>       QEMUTimer *timer; /* Internal timer */
>>       void *clic;       /* clic interrupt controller */
>> +    uint32_t exccode; /* clic irq encode */
>>   };
>>
>>   OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
>> --
>> 2.25.1
>>
>>


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 00/11] RISC-V: support clic v0.9 specification
  2021-04-19 23:30 ` [RFC PATCH 00/11] RISC-V: support clic v0.9 specification Alistair Francis
@ 2021-04-20  1:44   ` LIU Zhiwei
  2021-04-20  6:26     ` Alistair Francis
  0 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-20  1:44 UTC (permalink / raw)
  To: Alistair Francis, michaeljclark
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768


On 2021/4/20 上午7:30, Alistair Francis wrote:
> On Fri, Apr 9, 2021 at 5:56 PM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
>> 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.
>>
>> I take over the job from Michael Clark, who gave the first implementation
>> of clic-v0.7 specification. If there is any copyright question, please
>> let me know.
> You need to make sure you leave all original copyright notices and SoB in place.

OK.

Is it OK that keep the original copyright notices for new files and  
your SoB in every patch,  Michael?

>
>> Features:
>> 1. support four kinds of trigger types.
>> 2. Preserve the CSR WARL/WPRI semantics.
>> 3. Option to select different modes, such as M/S/U or M/U.
>> 4. At most 4096 interrupts.
>> 5. At most 1024 apertures.
>>
>> Todo:
>> 1. Encode the interrupt trigger information to exccode.
>> 2. Support complete CSR mclicbase when its number is fixed.
>> 3. Gave each aperture an independend address.
>>
>> It have passed my qtest case and freertos test. Welcome to have a try
>> for your hardware.
> It doesn't seem to be connected to any machine. How are you testing this?

There is a machine called SMARTL in my repository[1].  The qtest case 
is  tests/qtest/test-riscv32-clic.c. If it's better, I can upstream the 
machine together next version.

Zhiwei

[1]https://github.com/romanheros/qemu, branch: riscv-clic-upstream-rfc


>
> Alistair
>
>> Any advice is welcomed. Thanks very much.
>>
>> Zhiwei
>>
>> [1] specification website: https://github.com/riscv/riscv-fast-interrupt.
>> [2] Michael Clark origin work: https://github.com/sifive/riscv-qemu/tree/sifive-clic.
>>
>>
>> LIU Zhiwei (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 xtvt in CLIC mode
>>    target/riscv: Update CSR xnxti in CLIC mode
>>    target/riscv: Update CSR mclicbase in CLIC mode
>>    target/riscv: Update interrupt handling in CLIC mode
>>    target/riscv: Update interrupt return in CLIC mode
>>
>>   default-configs/devices/riscv32-softmmu.mak |   1 +
>>   default-configs/devices/riscv64-softmmu.mak |   1 +
>>   hw/intc/Kconfig                             |   3 +
>>   hw/intc/meson.build                         |   1 +
>>   hw/intc/riscv_clic.c                        | 836 ++++++++++++++++++++
>>   include/hw/intc/riscv_clic.h                | 103 +++
>>   target/riscv/cpu.h                          |   9 +
>>   target/riscv/cpu_bits.h                     |  32 +
>>   target/riscv/cpu_helper.c                   | 117 ++-
>>   target/riscv/csr.c                          | 247 +++++-
>>   target/riscv/op_helper.c                    |  25 +
>>   11 files changed, 1363 insertions(+), 12 deletions(-)
>>   create mode 100644 hw/intc/riscv_clic.c
>>   create mode 100644 include/hw/intc/riscv_clic.h
>>
>> --
>> 2.25.1
>>
>>


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 00/11] RISC-V: support clic v0.9 specification
  2021-04-20  1:44   ` LIU Zhiwei
@ 2021-04-20  6:26     ` Alistair Francis
  2021-04-20  7:20       ` LIU Zhiwei
  0 siblings, 1 reply; 81+ messages in thread
From: Alistair Francis @ 2021-04-20  6:26 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: open list:RISC-V, qemu-devel@nongnu.org Developers, wxy194768,
	michaeljclark, Palmer Dabbelt, Alistair Francis

On Tue, Apr 20, 2021 at 11:45 AM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
>
>
> On 2021/4/20 上午7:30, Alistair Francis wrote:
> > On Fri, Apr 9, 2021 at 5:56 PM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
> >> 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.
> >>
> >> I take over the job from Michael Clark, who gave the first implementation
> >> of clic-v0.7 specification. If there is any copyright question, please
> >> let me know.
> > You need to make sure you leave all original copyright notices and SoB in place.
>
> OK.
>
> Is it OK that keep the original copyright notices for new files and
> your SoB in every patch,  Michael?
>
> >
> >> Features:
> >> 1. support four kinds of trigger types.
> >> 2. Preserve the CSR WARL/WPRI semantics.
> >> 3. Option to select different modes, such as M/S/U or M/U.
> >> 4. At most 4096 interrupts.
> >> 5. At most 1024 apertures.
> >>
> >> Todo:
> >> 1. Encode the interrupt trigger information to exccode.
> >> 2. Support complete CSR mclicbase when its number is fixed.
> >> 3. Gave each aperture an independend address.
> >>
> >> It have passed my qtest case and freertos test. Welcome to have a try
> >> for your hardware.
> > It doesn't seem to be connected to any machine. How are you testing this?
>
> There is a machine called SMARTL in my repository[1].  The qtest case
> is  tests/qtest/test-riscv32-clic.c. If it's better, I can upstream the
> machine together next version.

I don't really want to add a new hardware device when it isn't
connected to a machine. It would be great if we could connect it to a
machine. If not SMARTL maybe we could add it as an option to the virt
machine?

What is SMARTL? Is that a publically available board?

Alistair

>
> Zhiwei
>
> [1]https://github.com/romanheros/qemu, branch: riscv-clic-upstream-rfc
>
>
> >
> > Alistair
> >
> >> Any advice is welcomed. Thanks very much.
> >>
> >> Zhiwei
> >>
> >> [1] specification website: https://github.com/riscv/riscv-fast-interrupt.
> >> [2] Michael Clark origin work: https://github.com/sifive/riscv-qemu/tree/sifive-clic.
> >>
> >>
> >> LIU Zhiwei (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 xtvt in CLIC mode
> >>    target/riscv: Update CSR xnxti in CLIC mode
> >>    target/riscv: Update CSR mclicbase in CLIC mode
> >>    target/riscv: Update interrupt handling in CLIC mode
> >>    target/riscv: Update interrupt return in CLIC mode
> >>
> >>   default-configs/devices/riscv32-softmmu.mak |   1 +
> >>   default-configs/devices/riscv64-softmmu.mak |   1 +
> >>   hw/intc/Kconfig                             |   3 +
> >>   hw/intc/meson.build                         |   1 +
> >>   hw/intc/riscv_clic.c                        | 836 ++++++++++++++++++++
> >>   include/hw/intc/riscv_clic.h                | 103 +++
> >>   target/riscv/cpu.h                          |   9 +
> >>   target/riscv/cpu_bits.h                     |  32 +
> >>   target/riscv/cpu_helper.c                   | 117 ++-
> >>   target/riscv/csr.c                          | 247 +++++-
> >>   target/riscv/op_helper.c                    |  25 +
> >>   11 files changed, 1363 insertions(+), 12 deletions(-)
> >>   create mode 100644 hw/intc/riscv_clic.c
> >>   create mode 100644 include/hw/intc/riscv_clic.h
> >>
> >> --
> >> 2.25.1
> >>
> >>


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 00/11] RISC-V: support clic v0.9 specification
  2021-04-20  6:26     ` Alistair Francis
@ 2021-04-20  7:20       ` LIU Zhiwei
  2021-04-22  0:21         ` Alistair Francis
  0 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-04-20  7:20 UTC (permalink / raw)
  To: Alistair Francis
  Cc: open list:RISC-V, qemu-devel@nongnu.org Developers, wxy194768,
	michaeljclark, Palmer Dabbelt, Alistair Francis


On 2021/4/20 下午2:26, Alistair Francis wrote:
> On Tue, Apr 20, 2021 at 11:45 AM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
>>
>> On 2021/4/20 上午7:30, Alistair Francis wrote:
>>> On Fri, Apr 9, 2021 at 5:56 PM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
>>>> 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.
>>>>
>>>> I take over the job from Michael Clark, who gave the first implementation
>>>> of clic-v0.7 specification. If there is any copyright question, please
>>>> let me know.
>>> You need to make sure you leave all original copyright notices and SoB in place.
>> OK.
>>
>> Is it OK that keep the original copyright notices for new files and
>> your SoB in every patch,  Michael?
>>
>>>> Features:
>>>> 1. support four kinds of trigger types.
>>>> 2. Preserve the CSR WARL/WPRI semantics.
>>>> 3. Option to select different modes, such as M/S/U or M/U.
>>>> 4. At most 4096 interrupts.
>>>> 5. At most 1024 apertures.
>>>>
>>>> Todo:
>>>> 1. Encode the interrupt trigger information to exccode.
>>>> 2. Support complete CSR mclicbase when its number is fixed.
>>>> 3. Gave each aperture an independend address.
>>>>
>>>> It have passed my qtest case and freertos test. Welcome to have a try
>>>> for your hardware.
>>> It doesn't seem to be connected to any machine. How are you testing this?
>> There is a machine called SMARTL in my repository[1].  The qtest case
>> is  tests/qtest/test-riscv32-clic.c. If it's better, I can upstream the
>> machine together next version.
> I don't really want to add a new hardware device when it isn't
> connected to a machine. It would be great if we could connect it to a
> machine. If not SMARTL maybe we could add it as an option to the virt
> machine?
Currently it is good to  connect CLIC to virt machine.  I can fix it in 
the next version if it is OK for you.
> What is SMARTL? Is that a publically available board?

SMARTL is a fpga evaluation board.  We usually use it  to debug programs 
for XuanTie CPU serials.
It has a 32bit CPU, 1 UART,  4 timers, and the CLIC interrupt 
controller. I will give  a detailed documentation
when I upstream it.

There are still many other  RISC-V boards, but more complex. I plan to 
upstream the XuanTie CPU
and  some widely used boards after the P extension and CLIC.

Zhiwei

>
> Alistair
>
>> Zhiwei
>>
>> [1]https://github.com/romanheros/qemu, branch: riscv-clic-upstream-rfc
>>
>>
>>> Alistair
>>>
>>>> Any advice is welcomed. Thanks very much.
>>>>
>>>> Zhiwei
>>>>
>>>> [1] specification website: https://github.com/riscv/riscv-fast-interrupt.
>>>> [2] Michael Clark origin work: https://github.com/sifive/riscv-qemu/tree/sifive-clic.
>>>>
>>>>
>>>> LIU Zhiwei (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 xtvt in CLIC mode
>>>>     target/riscv: Update CSR xnxti in CLIC mode
>>>>     target/riscv: Update CSR mclicbase in CLIC mode
>>>>     target/riscv: Update interrupt handling in CLIC mode
>>>>     target/riscv: Update interrupt return in CLIC mode
>>>>
>>>>    default-configs/devices/riscv32-softmmu.mak |   1 +
>>>>    default-configs/devices/riscv64-softmmu.mak |   1 +
>>>>    hw/intc/Kconfig                             |   3 +
>>>>    hw/intc/meson.build                         |   1 +
>>>>    hw/intc/riscv_clic.c                        | 836 ++++++++++++++++++++
>>>>    include/hw/intc/riscv_clic.h                | 103 +++
>>>>    target/riscv/cpu.h                          |   9 +
>>>>    target/riscv/cpu_bits.h                     |  32 +
>>>>    target/riscv/cpu_helper.c                   | 117 ++-
>>>>    target/riscv/csr.c                          | 247 +++++-
>>>>    target/riscv/op_helper.c                    |  25 +
>>>>    11 files changed, 1363 insertions(+), 12 deletions(-)
>>>>    create mode 100644 hw/intc/riscv_clic.c
>>>>    create mode 100644 include/hw/intc/riscv_clic.h
>>>>
>>>> --
>>>> 2.25.1
>>>>
>>>>


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-04-20  0:57     ` LIU Zhiwei
@ 2021-04-22  0:16       ` Alistair Francis
  0 siblings, 0 replies; 81+ messages in thread
From: Alistair Francis @ 2021-04-22  0:16 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

On Tue, Apr 20, 2021 at 10:57 AM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
>
>
> On 2021/4/20 上午7:25, Alistair Francis wrote:
> > On Fri, Apr 9, 2021 at 5:56 PM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
> >> 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>
> >> ---
> >>   default-configs/devices/riscv32-softmmu.mak |   1 +
> >>   default-configs/devices/riscv64-softmmu.mak |   1 +
> >>   hw/intc/Kconfig                             |   3 +
> >>   hw/intc/meson.build                         |   1 +
> >>   hw/intc/riscv_clic.c                        | 835 ++++++++++++++++++++
> >>   include/hw/intc/riscv_clic.h                | 103 +++
> >>   target/riscv/cpu.h                          |   2 +
> >>   7 files changed, 946 insertions(+)
> >>   create mode 100644 hw/intc/riscv_clic.c
> >>   create mode 100644 include/hw/intc/riscv_clic.h
> >>
> >> diff --git a/default-configs/devices/riscv32-softmmu.mak b/default-configs/devices/riscv32-softmmu.mak
> >> index d847bd5692..1430c30588 100644
> >> --- a/default-configs/devices/riscv32-softmmu.mak
> >> +++ b/default-configs/devices/riscv32-softmmu.mak
> >> @@ -5,6 +5,7 @@
> >>   #CONFIG_PCI_DEVICES=n
> >>   CONFIG_SEMIHOSTING=y
> >>   CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> >> +CONFIG_RISCV_CLIC=y
> >>
> >>   # Boards:
> >>   #
> >> diff --git a/default-configs/devices/riscv64-softmmu.mak b/default-configs/devices/riscv64-softmmu.mak
> >> index d5eec75f05..396800bbbd 100644
> >> --- a/default-configs/devices/riscv64-softmmu.mak
> >> +++ b/default-configs/devices/riscv64-softmmu.mak
> >> @@ -5,6 +5,7 @@
> >>   #CONFIG_PCI_DEVICES=n
> >>   CONFIG_SEMIHOSTING=y
> >>   CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> >> +CONFIG_RISCV_CLIC=y
> >>
> >>   # Boards:
> >>   #
> >> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
> >> index f4694088a4..5bf492b48f 100644
> >> --- a/hw/intc/Kconfig
> >> +++ b/hw/intc/Kconfig
> >> @@ -68,6 +68,9 @@ config SIFIVE_CLINT
> >>   config SIFIVE_PLIC
> >>       bool
> >>
> >> +config RISCV_CLIC
> >> +    bool
> >> +
> >>   config GOLDFISH_PIC
> >>       bool
> >>
> >> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> >> index 1c299039f6..2aa71b6738 100644
> >> --- a/hw/intc/meson.build
> >> +++ b/hw/intc/meson.build
> >> @@ -50,6 +50,7 @@ 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_SIFIVE_CLINT', if_true: files('sifive_clint.c'))
> >>   specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.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'))
> >> diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
> >> new file mode 100644
> >> index 0000000000..8ad534c506
> >> --- /dev/null
> >> +++ b/hw/intc/riscv_clic.c
> >> @@ -0,0 +1,835 @@
> >> +/*
> >> + * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
> >> + *
> >> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
> > Shouldn't this maintain the original copyright?
>
> It is OK.
>
> As it has changed a lot from the origin version, it is just another
> device called sifive_clic  in my branch, and this is the device called
> riscv_clic.

I can't comment on how much it changed. In your cover letter you say
that it's based on an implementation so I would then guess that this
should keep the copyright if you used that as a starting point.

Alistair

>
> If that is the rule, I am very glad to obey it.
>
> Thanks for pointing it out.
>
> Zhiwei
>
> > Alistair
> >
> >> + *
> >> + * 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/>.
> >> + */
> >> +
> >> +#include "qemu/osdep.h"
> >> +#include "qapi/error.h"
> >> +#include "qemu/log.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"
> >> +
> >> +/*
> >> + * 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_offset)
> >> +{
> >> +    return (clic->clicintattr[irq_offset] >> 1) & 0x3;
> >> +}
> >> +
> >> +static inline bool
> >> +riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
> >> +{
> >> +    return (clic->clicintattr[irq_offset] >> 1) & 0x1;
> >> +}
> >> +
> >> +static inline bool
> >> +riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
> >> +{
> >> +    return (clic->clicintattr[irq_offset] & 0x1) && clic->nvbits;
> >> +}
> >> +
> >> +static uint8_t
> >> +riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
> >> +{
> >> +    int nlbits = clic->nlbits;
> >> +
> >> +    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->nlbits;
> >> +    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);
> >> +}
> >> +
> >> +/*
> >> + * In a system with multiple harts, the M-mode CLIC regions for all the harts
> >> + * are placed contiguously in the memory space, followed by the S-mode CLIC
> >> + * regions for all harts. (Section 3.11)
> >> + */
> >> +static size_t
> >> +riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int hartid, int irq)
> >> +{
> >> +    size_t mode_offset = 0;
> >> +    size_t unit = clic->num_harts * clic->num_sources;
> >> +
> >> +    switch (mode) {
> >> +    case PRV_M:
> >> +        mode_offset = 0;
> >> +        break;
> >> +    case PRV_S:
> >> +        mode_offset = unit;
> >> +        break;
> >> +    case PRV_U:
> >> +        mode_offset = clic->prv_s ? 2 * unit : unit;
> >> +        break;
> >> +    default:
> >> +        qemu_log_mask(LOG_GUEST_ERROR,
> >> +                      "clic: invalid mode %d\n", mode);
> >> +        exit(1);
> >> +    }
> >> +    return mode_offset + hartid * clic->num_sources + irq;
> >> +}
> >> +
> >> +static void riscv_clic_next_interrupt(void *opaque, int hartid)
> >> +{
> >> +    /*
> >> +     * 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
> >> +     */
> >> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(hartid));
> >> +    CPURISCVState *env = &cpu->env;
> >> +    RISCVCLICState *clic = (RISCVCLICState *)opaque;
> >> +
> >> +    int il[4] = {
> >> +        MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
> >> +            clic->mintthresh), /* PRV_U */
> >> +        MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
> >> +            clic->sintthresh), /* PRV_S */
> >> +        0,                     /* reserverd */
> >> +        MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
> >> +            clic->uintthresh)  /* PRV_M */
> >> +    };
> >> +
> >> +    /* Get sorted list of enabled interrupts for this hart */
> >> +    size_t hart_offset = hartid * clic->num_sources;
> >> +    CLICActiveInterrupt *active = &clic->active_list[hart_offset];
> >> +    size_t active_count = clic->active_count[hartid];
> >> +    uint8_t mode, level, priority;
> >> +
> >> +    /* Loop through the enabled interrupts sorted by mode+priority+level */
> >> +    while (active_count) {
> >> +        size_t irq_offset;
> >> +        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;
> >> +        }
> >> +        irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, active->irq);
> >> +        /* Check pending interrupt with high enough mode+priority+level */
> >> +        if (clic->clicintip[irq_offset]) {
> >> +            /* Clean vector edge-triggered pending */
> >> +            if (riscv_clic_is_edge_triggered(clic, irq_offset) &&
> >> +                riscv_clic_is_shv_interrupt(clic, irq_offset)) {
> >> +                clic->clicintip[irq_offset] = 0;
> >> +            }
> >> +            /* Post pending interrupt for this hart */
> >> +            clic->exccode[hartid] = active->irq | mode << 12 | level << 14;
> >> +            qemu_set_irq(clic->cpu_irqs[hartid], 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 hartid, int irq)
> >> +{
> >> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> >> +    if (!clic->prv_s && !clic->prv_u) { /* M */
> >> +        return mode == PRV_M;
> >> +    } else if (!clic->prv_s) { /* M/U */
> >> +        switch (clic->nmbits) {
> >> +        case 0:
> >> +            return mode == PRV_M;
> >> +        case 1:
> >> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M) :
> >> +                                                          (mode == PRV_U);
> >> +        default:
> >> +            qemu_log_mask(LOG_GUEST_ERROR,
> >> +                "clic: nmbits can only be 0 or 1 for M/U hart");
> >> +            exit(1);
> >> +        }
> >> +    } else { /* M/S/U */
> >> +        switch (clic->nmbits) {
> >> +        case 0:
> >> +            return mode == PRV_M;
> >> +        case 1:
> >> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M) :
> >> +                                                          (mode == PRV_S);
> >> +        case 2:
> >> +            return mode == clic->clicintattr[irq_offset];
> >> +        case 3:
> >> +            qemu_log_mask(LOG_GUEST_ERROR,
> >> +                "clic: nmbits can only be 0 or 1 or 2 for M/S/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 mode, int hartid, int irq)
> >> +{
> >> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> >> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
> >> +}
> >> +
> >> +static void
> >> +riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
> >> +                        int irq, uint64_t value)
> >> +{
> >> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> >> +    clic->clicintip[irq_offset] = !!value;
> >> +    riscv_clic_next_interrupt(clic, hartid);
> >> +}
> >> +
> >> +/*
> >> + * 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, uint64_t value)
> >> +{
> >> +    int mode = extract64(value, 6, 2);
> >> +
> >> +    if (!qtest_enabled()) {
> >> +        CPURISCVState *env = current_cpu->env_ptr;
> >> +        if (env->priv < mode) {
> >> +            return false;
> >> +        }
> >> +    }
> >> +    return true;
> >> +}
> >> +
> >> +static inline int riscv_clic_encode_priority(const CLICActiveInterrupt *i)
> >> +{
> >> +    return ((i->intcfg & 0x3ff) << 12) | /* Highest mode+level+priority */
> >> +           (i->irq & 0xfff);             /* Highest irq number */
> >> +}
> >> +
> >> +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 hartid,
> >> +                        int irq, uint64_t new_intie)
> >> +{
> >> +    size_t hart_offset = hartid * clic->num_sources;
> >> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> >> +    CLICActiveInterrupt *active_list = &clic->active_list[hart_offset];
> >> +    size_t *active_count = &clic->active_count[hartid];
> >> +
> >> +    uint8_t old_intie = clic->clicintie[irq_offset];
> >> +    clic->clicintie[irq_offset] = !!new_intie;
> >> +
> >> +    /* Add to or remove from list of active interrupts */
> >> +    if (new_intie && !old_intie) {
> >> +        active_list[*active_count].intcfg = (mode << 8) |
> >> +                                            clic->clicintctl[irq_offset];
> >> +        active_list[*active_count].irq = irq;
> >> +        (*active_count)++;
> >> +    } else if (!new_intie && old_intie) {
> >> +        CLICActiveInterrupt key = {
> >> +            (mode << 8) | clic->clicintctl[irq_offset], irq
> >> +        };
> >> +        CLICActiveInterrupt *result = bsearch(&key,
> >> +                                              active_list, *active_count,
> >> +                                              sizeof(CLICActiveInterrupt),
> >> +                                              riscv_clic_active_compare);
> >> +        size_t elem = (result - active_list) / sizeof(CLICActiveInterrupt);
> >> +        size_t sz = (--(*active_count) - elem) * sizeof(CLICActiveInterrupt);
> >> +        assert(result);
> >> +        memmove(&result[0], &result[1], sz);
> >> +    }
> >> +
> >> +    /* Sort list of active interrupts */
> >> +    qsort(active_list, *active_count,
> >> +          sizeof(CLICActiveInterrupt),
> >> +          riscv_clic_active_compare);
> >> +
> >> +    riscv_clic_next_interrupt(clic, hartid);
> >> +}
> >> +
> >> +static void
> >> +riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
> >> +                      uint64_t value, unsigned size,
> >> +                      int mode, int hartid, int irq)
> >> +{
> >> +    int req = extract32(addr, 0, 2);
> >> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> >> +
> >> +    if (hartid >= clic->num_harts) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR,
> >> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
> >> +                      hartid, addr);
> >> +        return;
> >> +    }
> >> +
> >> +    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, mode, hartid, irq)) {
> >> +            /*
> >> +             * The actual pending bit is located at bit 0 (i.e., the
> >> +             * leastsignificant 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_offset]) {
> >> +                riscv_clic_update_intip(clic, mode, hartid, irq, value);
> >> +            }
> >> +        }
> >> +        break;
> >> +    case 1: /* clicintie[i] */
> >> +        if (clic->clicintie[irq_offset] != value) {
> >> +            riscv_clic_update_intie(clic, mode, hartid, irq, value);
> >> +        }
> >> +        break;
> >> +    case 2: /* clicintattr[i] */
> >> +        if (riscv_clic_validate_intattr(clic, value)) {
> >> +            if (clic->clicintattr[irq_offset] != value) {
> >> +                /* When nmbits=2, check WARL */
> >> +                bool invalid = (clic->nmbits == 2) &&
> >> +                               (extract64(value, 6, 2) == 0b10);
> >> +                if (invalid) {
> >> +                    uint8_t old_mode = extract32(clic->clicintattr[irq_offset],
> >> +                                                 6, 2);
> >> +                    value = deposit32(value, 6, 2, old_mode);
> >> +                }
> >> +                clic->clicintattr[irq_offset] = value;
> >> +                riscv_clic_next_interrupt(clic, hartid);
> >> +            }
> >> +        }
> >> +        break;
> >> +    case 3: /* clicintctl[i] */
> >> +        if (value != clic->clicintctl[irq_offset]) {
> >> +            clic->clicintctl[irq_offset] = value;
> >> +            riscv_clic_next_interrupt(clic, hartid);
> >> +        }
> >> +        break;
> >> +    }
> >> +}
> >> +
> >> +static uint64_t
> >> +riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
> >> +                     int hartid, int irq)
> >> +{
> >> +    int req = extract32(addr, 0, 2);
> >> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> >> +
> >> +    if (hartid >= clic->num_harts) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR,
> >> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
> >> +                      hartid, addr);
> >> +        return 0;
> >> +    }
> >> +
> >> +    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] */
> >> +        return clic->clicintip[irq_offset];
> >> +    case 1: /* clicintie[i] */
> >> +        return clic->clicintie[irq_offset];
> >> +    case 2: /* clicintattr[i] */
> >> +        /*
> >> +         * clicintattr register layout
> >> +         * Bits Field
> >> +         * 7:6 mode
> >> +         * 5:3 reserved (WPRI 0)
> >> +         * 2:1 trig
> >> +         * 0 shv
> >> +         */
> >> +        return clic->clicintattr[irq_offset] & ~0x38;
> >> +    case 3: /* clicintctrl */
> >> +        /*
> >> +         * 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_offset] |
> >> +               ((1 << (8 - clic->clicintctlbits)) - 1);
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +/* Return target interrupt mode */
> >> +static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
> >> +{
> >> +    int mode = addr / (4 * clic->num_harts * clic->num_sources);
> >> +    switch (mode) {
> >> +    case 0:
> >> +        return PRV_M;
> >> +    case 1:
> >> +        assert(clic->prv_s || clic->prv_u);
> >> +        return clic->prv_s ? PRV_S : PRV_U;
> >> +    case 2:
> >> +        assert(clic->prv_s && clic->prv_u);
> >> +        return PRV_U;
> >> +    default:
> >> +        g_assert_not_reached();
> >> +        break;
> >> +    }
> >> +}
> >> +
> >> +/* Return target hart id */
> >> +static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
> >> +{
> >> +    int mode_unit = 4 * clic->num_harts * clic->num_sources;
> >> +    int hart_unit = 4 * clic->num_sources;
> >> +
> >> +    return (addr % mode_unit) / hart_unit;
> >> +}
> >> +
> >> +/* Return target interrupt number */
> >> +static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
> >> +{
> >> +    int hart_unit = 4 * clic->num_sources;
> >> +    return (addr % hart_unit) / 4;
> >> +}
> >> +
> >> +static void
> >> +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
> >> +{
> >> +    RISCVCLICState *clic = opaque;
> >> +    hwaddr clic_size = clic->clic_size;
> >> +    int hartid, mode, irq;
> >> +
> >> +    if (addr < clic_size) {
> >> +        if (addr < 0x1000) {
> >> +            assert(addr % 4 == 0);
> >> +            int index = addr / 4;
> >> +            switch (index) {
> >> +            case 0: /* cliccfg */
> >> +                {
> >> +                    uint8_t nlbits = extract32(value, 1, 4);
> >> +                    uint8_t nmbits = extract32(value, 5, 2);
> >> +
> >> +                    /*
> >> +                     * The 4-bit cliccfg.nlbits WARL field.
> >> +                     * Valid values are 0—8.
> >> +                     */
> >> +                    if (nlbits <= 8) {
> >> +                        clic->nlbits = nlbits;
> >> +                    }
> >> +                    /* Valid values are given by implemented priviledges */
> >> +                    if (clic->prv_s && clic->prv_u) {
> >> +                        if (nmbits <= 2) {
> >> +                            clic->nmbits = nmbits;
> >> +                        }
> >> +                    } else if (clic->prv_u) {
> >> +                        if (nmbits <= 1) {
> >> +                            clic->nmbits = nmbits;
> >> +                        }
> >> +                    } else {
> >> +                        assert(!clic->prv_s);
> >> +                        if (nmbits == 0) {
> >> +                            clic->nmbits = 0;
> >> +                        }
> >> +                    }
> >> +                    clic->nvbits = extract32(value, 0, 1);
> >> +                    break;
> >> +                }
> >> +            case 1: /* clicinfo, read-only register */
> >> +                qemu_log_mask(LOG_GUEST_ERROR,
> >> +                              "clic: write read-only clicinfo.\n");
> >> +                break;
> >> +            case 0x10 ... 0x2F: /* clicinttrig */
> >> +                {
> >> +                    uint32_t interrupt_number = value & MAKE_64BIT_MASK(0, 13);
> >> +                    if (interrupt_number <= clic->num_sources) {
> >> +                        value &= ~MAKE_64BIT_MASK(13, 18);
> >> +                        clic->clicinttrig[index - 0x10] = value;
> >> +                    }
> >> +                    break;
> >> +                }
> >> +            case 2: /* mintthresh */
> >> +                if (!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 -= 0x1000;
> >> +            hartid = riscv_clic_get_hartid(clic, addr);
> >> +            mode = riscv_clic_get_mode(clic, addr);
> >> +            irq = riscv_clic_get_irq(clic, addr);
> >> +
> >> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
> >> +                riscv_clic_hart_write(clic, addr, value, size, mode,
> >> +                                      hartid, irq);
> >> +            }
> >> +        }
> >> +    } else {
> >> +        qemu_log_mask(LOG_GUEST_ERROR,
> >> +                      "clic: invalid write: 0x%" HWADDR_PRIx "\n", addr);
> >> +    }
> >> +}
> >> +
> >> +static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
> >> +{
> >> +    RISCVCLICState *clic = opaque;
> >> +    hwaddr clic_size = clic->clic_size;
> >> +    int hartid, mode, irq;
> >> +
> >> +    if (addr < clic_size) {
> >> +        if (addr < 0x1000) {
> >> +            assert(addr % 4 == 0);
> >> +            int index = addr / 4;
> >> +            switch (index) {
> >> +            case 0: /* cliccfg */
> >> +                return clic->nvbits |
> >> +                       (clic->nlbits << 1) |
> >> +                       (clic->nmbits << 5);
> >> +            case 1: /* clicinfo */
> >> +                /*
> >> +                 * clicinfo register layout
> >> +                 *
> >> +                 * Bits Field
> >> +                 * 31 reserved (WARL 0)
> >> +                 * 30:25 num_trigger
> >> +                 * 24:21 CLICINTCTLBITS
> >> +                 * 20:13 version (for version control)
> >> +                 * 12:0 num_interrupt
> >> +                 */
> >> +                return clic->clicinfo & ~INT32_MAX;
> >> +            case 0x10 ... 0x2F: /* clicinttrig */
> >> +                /*
> >> +                 * clicinttrig register layout
> >> +                 *
> >> +                 * Bits Field
> >> +                 * 31 enable
> >> +                 * 30:13 reserved (WARL 0)
> >> +                 * 12:0 interrupt_number
> >> +                 */
> >> +                return clic->clicinttrig[index - 0x10] &
> >> +                       ~MAKE_64BIT_MASK(13, 18);
> >> +            case 2: /* mintthresh */
> >> +                if (!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 -= 0x1000;
> >> +            hartid = riscv_clic_get_hartid(clic, addr);
> >> +            mode = riscv_clic_get_mode(clic, addr);
> >> +            irq = riscv_clic_get_irq(clic, addr);
> >> +
> >> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
> >> +                return riscv_clic_hart_read(clic, addr, mode, hartid, irq);
> >> +            }
> >> +        }
> >> +    } else {
> >> +        qemu_log_mask(LOG_GUEST_ERROR,
> >> +                      "clic: invalid read: 0x%" HWADDR_PRIx "\n", addr);
> >> +    }
> >> +    return 0;
> >> +}
> >> +
> >> +static void riscv_clic_set_irq(void *opaque, int id, int level)
> >> +{
> >> +    RISCVCLICState *clic = opaque;
> >> +    int irq, hartid, mode;
> >> +    hwaddr addr = 4 * id;
> >> +    TRIG_TYPE type;
> >> +
> >> +    hartid = riscv_clic_get_hartid(clic, addr);
> >> +    mode = riscv_clic_get_mode(clic, addr);
> >> +    irq = riscv_clic_get_irq(clic, addr);
> >> +    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, mode, hartid, irq, level);
> >> +            break;
> >> +        case NEG_LEVEL:
> >> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
> >> +            break;
> >> +        case NEG_EDGE:
> >> +            break;
> >> +        }
> >> +    } else {
> >> +        switch (type) {
> >> +        case POSITIVE_LEVEL:
> >> +            riscv_clic_update_intip(clic, mode, hartid, irq, level);
> >> +            break;
> >> +        case POSITIVE_EDGE:
> >> +            break;
> >> +        case NEG_LEVEL:
> >> +        case NEG_EDGE:
> >> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
> >> +            break;
> >> +        }
> >> +    }
> >> +}
> >> +
> >> +static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level)
> >> +{
> >> +    CPURISCVState *env = (CPURISCVState *)opaque;
> >> +    RISCVCLICState *clic = env->clic;
> >> +    CPUState *cpu = env_cpu(env);
> >> +
> >> +    if (level) {
> >> +        env->exccode = clic->exccode[cpu->cpu_index];
> >> +        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 harts_x_sources = clic->num_harts * clic->num_sources;
> >> +    int irqs, i;
> >> +
> >> +    if (clic->prv_s && clic->prv_u) {
> >> +        irqs = 3 * harts_x_sources;
> >> +    } else if (clic->prv_s || clic->prv_u) {
> >> +        irqs = 2 * harts_x_sources;
> >> +    } else {
> >> +        irqs = harts_x_sources;
> >> +    }
> >> +
> >> +    clic->clic_size = irqs * 4 + 0x1000;
> >> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops, clic,
> >> +                          TYPE_RISCV_CLIC, clic->clic_size);
> >> +
> >> +    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);
> >> +    clic->active_count = g_new0(size_t, clic->num_harts);
> >> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
> >> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
> >> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
> >> +
> >> +    /* Allocate irq 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_irqs, clic->num_harts);
> >> +
> >> +    for (i = 0; i < clic->num_harts; i++) {
> >> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
> >> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
> >> +                                         &cpu->env, 1);
> >> +        qdev_connect_gpio_out(dev, i, irq);
> >> +        cpu->env.clic = clic;
> >> +    }
> >> +}
> >> +
> >> +static Property riscv_clic_properties[] = {
> >> +    DEFINE_PROP_BOOL("prv-s", RISCVCLICState, prv_s, false),
> >> +    DEFINE_PROP_BOOL("prv-u", RISCVCLICState, prv_u, false),
> >> +    DEFINE_PROP_UINT32("num-harts", RISCVCLICState, num_harts, 0),
> >> +    DEFINE_PROP_UINT32("num-sources", RISCVCLICState, num_sources, 0),
> >> +    DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICState, clicintctlbits, 0),
> >> +    DEFINE_PROP_UINT64("mclicbase", RISCVCLICState, mclicbase, 0),
> >> +    DEFINE_PROP_STRING("version", RISCVCLICState, version),
> >> +    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 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 void riscv_clic_register_types(void)
> >> +{
> >> +    type_register_static(&riscv_clic_info);
> >> +}
> >> +
> >> +type_init(riscv_clic_register_types)
> >> +
> >> +/*
> >> + * riscv_clic_create:
> >> + *
> >> + * @addr: base address of M-Mode CLIC memory-mapped registers
> >> + * @prv_s: have smode region
> >> + * @prv_u: have umode region
> >> + * @num_harts: number of CPU harts
> >> + * @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"
> >> + *
> >> + * Returns: the device object
> >> + */
> >> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
> >> +                               uint32_t num_harts, uint32_t num_sources,
> >> +                               uint8_t clicintctlbits,
> >> +                               const char *version)
> >> +{
> >> +    DeviceState *dev = qdev_new(TYPE_RISCV_CLIC);
> >> +
> >> +    assert(num_sources <= 4096);
> >> +    assert(num_harts <= 1024);
> >> +    assert(clicintctlbits <= 8);
> >> +    assert(!strcmp(version, "v0.8") || !strcmp(version, "v0.9"));
> >> +
> >> +    qdev_prop_set_bit(dev, "prv-s", prv_s);
> >> +    qdev_prop_set_bit(dev, "prv-u", prv_u);
> >> +    qdev_prop_set_uint32(dev, "num-harts", num_harts);
> >> +    qdev_prop_set_uint32(dev, "num-sources", num_sources);
> >> +    qdev_prop_set_uint32(dev, "clicintctlbits", clicintctlbits);
> >> +    qdev_prop_set_uint64(dev, "mclicbase", addr);
> >> +    qdev_prop_set_string(dev, "version", version);
> >> +
> >> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> >> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
> >> +    return dev;
> >> +}
> >> +
> >> +void riscv_clic_get_next_interrupt(void *opaque, int hartid)
> >> +{
> >> +    RISCVCLICState *clic = opaque;
> >> +    riscv_clic_next_interrupt(clic, hartid);
> >> +}
> >> +
> >> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int irq)
> >> +{
> >> +    RISCVCLICState *clic = opaque;
> >> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> >> +    return riscv_clic_is_shv_interrupt(clic, irq_offset);
> >> +}
> >> +
> >> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int irq)
> >> +{
> >> +    RISCVCLICState *clic = opaque;
> >> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> >> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
> >> +}
> >> +
> >> +void riscv_clic_clean_pending(void *opaque, int mode, int hartid, int irq)
> >> +{
> >> +    RISCVCLICState *clic = opaque;
> >> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid, irq);
> >> +    clic->clicintip[irq_offset] = 0;
> >> +}
> >> +
> >> +/*
> >> + * 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.
> >> + */
> >> +bool riscv_clic_is_clic_mode(CPURISCVState *env)
> >> +{
> >> +    target_ulong xtvec = (env->priv == PRV_M) ? env->mtvec : env->stvec;
> >> +    return env->clic && ((xtvec & 0x3) == 3);
> >> +}
> >> +
> >> +void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
> >> +                               int *il, int *irq)
> >> +{
> >> +    *irq = extract32(exccode, 0, 12);
> >> +    *mode = extract32(exccode, 12, 2);
> >> +    *il = extract32(exccode, 14, 8);
> >> +}
> >> diff --git a/include/hw/intc/riscv_clic.h b/include/hw/intc/riscv_clic.h
> >> new file mode 100644
> >> index 0000000000..e5f89672a6
> >> --- /dev/null
> >> +++ b/include/hw/intc/riscv_clic.h
> >> @@ -0,0 +1,103 @@
> >> +/*
> >> + * RISC-V CLIC(Core Local Interrupt Controller) interface.
> >> + *
> >> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
> >> + *
> >> + * 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/>.
> >> + */
> >> +
> >> +#ifndef RISCV_CLIC_H
> >> +#define RISCV_CLIC_H
> >> +
> >> +#include "hw/irq.h"
> >> +#include "hw/sysbus.h"
> >> +
> >> +#define TYPE_RISCV_CLIC "riscv_clic"
> >> +#define RISCV_CLIC(obj) \
> >> +    OBJECT_CHECK(RISCVCLICState, (obj), TYPE_RISCV_CLIC)
> >> +
> >> +/*
> >> + * 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;
> >> +
> >> +typedef struct RISCVCLICState {
> >> +    /*< private >*/
> >> +    SysBusDevice parent_obj;
> >> +
> >> +    /*< public >*/
> >> +
> >> +    /* Implementaion parameters */
> >> +    bool prv_s;
> >> +    bool prv_u;
> >> +    uint32_t num_harts;
> >> +    uint32_t num_sources;
> >> +    uint32_t clic_size;
> >> +    uint32_t clic_mmode_base;
> >> +    uint32_t clicintctlbits;
> >> +    uint64_t mclicbase;
> >> +    char *version;
> >> +
> >> +    /* Global configuration */
> >> +    uint8_t nmbits;
> >> +    uint8_t nlbits;
> >> +    uint8_t nvbits;
> >> +    uint32_t clicinfo;
> >> +    uint32_t clicinttrig[32];
> >> +
> >> +    /* Aperture configuration */
> >> +    uint8_t *clicintip;
> >> +    uint8_t *clicintie;
> >> +    uint8_t *clicintattr;
> >> +    uint8_t *clicintctl;
> >> +
> >> +    /* Complatible with v0.8 */
> >> +    uint32_t mintthresh;
> >> +    uint32_t sintthresh;
> >> +    uint32_t uintthresh;
> >> +
> >> +    /* QEMU implementaion related fields */
> >> +    uint32_t *exccode;
> >> +    CLICActiveInterrupt *active_list;
> >> +    size_t *active_count;
> >> +    MemoryRegion mmio;
> >> +    qemu_irq *cpu_irqs;
> >> +} RISCVCLICState;
> >> +
> >> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
> >> +                               uint32_t num_harts, 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 mode, int hartid, int irq);
> >> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int irq);
> >> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int irq);
> >> +void riscv_clic_get_next_interrupt(void *opaque, int hartid);
> >> +bool riscv_clic_is_clic_mode(CPURISCVState *env);
> >> +#endif
> >> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> >> index a5eab26a69..9e389d7bbf 100644
> >> --- a/target/riscv/cpu.h
> >> +++ b/target/riscv/cpu.h
> >> @@ -33,6 +33,7 @@
> >>   #define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
> >>   #define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
> >>   #define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
> >> +#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0
> >>
> >>   #define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
> >>   #define TYPE_RISCV_CPU_BASE32           RISCV_CPU_TYPE_NAME("rv32")
> >> @@ -247,6 +248,7 @@ struct CPURISCVState {
> >>       /* Fields from here on are preserved across CPU reset. */
> >>       QEMUTimer *timer; /* Internal timer */
> >>       void *clic;       /* clic interrupt controller */
> >> +    uint32_t exccode; /* clic irq encode */
> >>   };
> >>
> >>   OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
> >> --
> >> 2.25.1
> >>
> >>


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 00/11] RISC-V: support clic v0.9 specification
  2021-04-20  7:20       ` LIU Zhiwei
@ 2021-04-22  0:21         ` Alistair Francis
  0 siblings, 0 replies; 81+ messages in thread
From: Alistair Francis @ 2021-04-22  0:21 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: open list:RISC-V, qemu-devel@nongnu.org Developers, wxy194768,
	michaeljclark, Palmer Dabbelt, Alistair Francis

On Tue, Apr 20, 2021 at 5:20 PM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
>
>
> On 2021/4/20 下午2:26, Alistair Francis wrote:
> > On Tue, Apr 20, 2021 at 11:45 AM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
> >>
> >> On 2021/4/20 上午7:30, Alistair Francis wrote:
> >>> On Fri, Apr 9, 2021 at 5:56 PM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
> >>>> 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.
> >>>>
> >>>> I take over the job from Michael Clark, who gave the first implementation
> >>>> of clic-v0.7 specification. If there is any copyright question, please
> >>>> let me know.
> >>> You need to make sure you leave all original copyright notices and SoB in place.
> >> OK.
> >>
> >> Is it OK that keep the original copyright notices for new files and
> >> your SoB in every patch,  Michael?
> >>
> >>>> Features:
> >>>> 1. support four kinds of trigger types.
> >>>> 2. Preserve the CSR WARL/WPRI semantics.
> >>>> 3. Option to select different modes, such as M/S/U or M/U.
> >>>> 4. At most 4096 interrupts.
> >>>> 5. At most 1024 apertures.
> >>>>
> >>>> Todo:
> >>>> 1. Encode the interrupt trigger information to exccode.
> >>>> 2. Support complete CSR mclicbase when its number is fixed.
> >>>> 3. Gave each aperture an independend address.
> >>>>
> >>>> It have passed my qtest case and freertos test. Welcome to have a try
> >>>> for your hardware.
> >>> It doesn't seem to be connected to any machine. How are you testing this?
> >> There is a machine called SMARTL in my repository[1].  The qtest case
> >> is  tests/qtest/test-riscv32-clic.c. If it's better, I can upstream the
> >> machine together next version.
> > I don't really want to add a new hardware device when it isn't
> > connected to a machine. It would be great if we could connect it to a
> > machine. If not SMARTL maybe we could add it as an option to the virt
> > machine?
> Currently it is good to  connect CLIC to virt machine.  I can fix it in
> the next version if it is OK for you.

The CLIC seems to be gaining traction so I think it makes sense to
start to think about connecting it up.

We don't want to break backwards compatibility though so make sure
it's an option, for example -machine virt,clic=true

> > What is SMARTL? Is that a publically available board?
>
> SMARTL is a fpga evaluation board.  We usually use it  to debug programs
> for XuanTie CPU serials.
> It has a 32bit CPU, 1 UART,  4 timers, and the CLIC interrupt
> controller. I will give  a detailed documentation
> when I upstream it.
>
> There are still many other  RISC-V boards, but more complex. I plan to
> upstream the XuanTie CPU
> and  some widely used boards after the P extension and CLIC.

I would prefer to add boards that are publicly available, so if there
are some that would be great!

Alistair

>
> Zhiwei
>
> >
> > Alistair
> >
> >> Zhiwei
> >>
> >> [1]https://github.com/romanheros/qemu, branch: riscv-clic-upstream-rfc
> >>
> >>
> >>> Alistair
> >>>
> >>>> Any advice is welcomed. Thanks very much.
> >>>>
> >>>> Zhiwei
> >>>>
> >>>> [1] specification website: https://github.com/riscv/riscv-fast-interrupt.
> >>>> [2] Michael Clark origin work: https://github.com/sifive/riscv-qemu/tree/sifive-clic.
> >>>>
> >>>>
> >>>> LIU Zhiwei (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 xtvt in CLIC mode
> >>>>     target/riscv: Update CSR xnxti in CLIC mode
> >>>>     target/riscv: Update CSR mclicbase in CLIC mode
> >>>>     target/riscv: Update interrupt handling in CLIC mode
> >>>>     target/riscv: Update interrupt return in CLIC mode
> >>>>
> >>>>    default-configs/devices/riscv32-softmmu.mak |   1 +
> >>>>    default-configs/devices/riscv64-softmmu.mak |   1 +
> >>>>    hw/intc/Kconfig                             |   3 +
> >>>>    hw/intc/meson.build                         |   1 +
> >>>>    hw/intc/riscv_clic.c                        | 836 ++++++++++++++++++++
> >>>>    include/hw/intc/riscv_clic.h                | 103 +++
> >>>>    target/riscv/cpu.h                          |   9 +
> >>>>    target/riscv/cpu_bits.h                     |  32 +
> >>>>    target/riscv/cpu_helper.c                   | 117 ++-
> >>>>    target/riscv/csr.c                          | 247 +++++-
> >>>>    target/riscv/op_helper.c                    |  25 +
> >>>>    11 files changed, 1363 insertions(+), 12 deletions(-)
> >>>>    create mode 100644 hw/intc/riscv_clic.c
> >>>>    create mode 100644 include/hw/intc/riscv_clic.h
> >>>>
> >>>> --
> >>>> 2.25.1
> >>>>
> >>>>


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 08/11] target/riscv: Update CSR xnxti in CLIC mode
  2021-04-09  7:48 ` [RFC PATCH 08/11] target/riscv: Update CSR xnxti " LIU Zhiwei
@ 2021-06-11  8:15   ` Frank Chang
  2021-06-11  8:30     ` LIU Zhiwei
  2021-06-27 10:07   ` Frank Chang
  2021-07-10 14:59   ` Frank Chang
  2 siblings, 1 reply; 81+ messages in thread
From: Frank Chang @ 2021-06-11  8:15 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 8766 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:52寫道:

> 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>
> ---
>  target/riscv/cpu_bits.h |  16 ++++++
>  target/riscv/csr.c      | 114 ++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 130 insertions(+)
>
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index 7922097776..494e41edc9 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      0x346 /* clic-spec-draft */
>  #define CSR_MINTTHRESH      0x347 /* clic-spec-draft */
>
> @@ -187,6 +188,7 @@
>  #define CSR_SCAUSE          0x142
>  #define CSR_STVAL           0x143
>  #define CSR_SIP             0x144
> +#define CSR_SNXTI           0x145 /* clic-spec-draft */
>  #define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
>  #define CSR_SINTTHRESH      0x147 /* clic-spec-draft */
>
> @@ -596,10 +598,24 @@
>  #define MINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
>  #define MINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
>
> +/* mcause */
> +#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] */
> +
>  /* sintstatus */
>  #define SINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
>  #define SINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
>
> +/* scause */
> +#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] */
> +
>  /* 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 e12222b77f..72cba080bf 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -774,6 +774,80 @@ static int rmw_mip(CPURISCVState *env, int csrno,
> target_ulong *ret_value,
>      return 0;
>  }
>
> +static bool get_xnxti_status(CPURISCVState *env)
> +{
> +    CPUState *cs = env_cpu(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_priv, cs->cpu_index,
> +                                      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;
> +    CPUState *cs = env_cpu(env);
> +    if (write_mask) {
> +        env->mstatus |= new_value & (write_mask & 0b11111);
> +    }
> +
> +    qemu_mutex_lock_iothread();
>

Hi Zhiwei,

May I ask what's the purpose to request the BQL here with
*qemu_mutex_lock_iothread()*?
Is there any critical data we need to protect in *rmw_mnxti()*?
As far I see, *rmw_mnxti()* won't call *cpu_interrupt()* which need BQL to
be held before calling.
Am I missing anything?

Regard,
Frank Chang


> +    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_priv,
> +                                                  cs->cpu_index,
> clic_irq);
> +            if (edge) {
> +                riscv_clic_clean_pending(env->clic, clic_priv,
> +                                         cs->cpu_index, 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;
> +        }
> +    }
> +    qemu_mutex_unlock_iothread();
> +    return 0;
> +}
> +
>  static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong
> *val)
>  {
>      *val = env->mintstatus;
> @@ -982,6 +1056,44 @@ static int rmw_sip(CPURISCVState *env, int csrno,
> target_ulong *ret_value,
>      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;
> +    CPUState *cs = env_cpu(env);
> +    if (write_mask) {
> +        env->mstatus |= new_value & (write_mask & 0b11111);
> +    }
> +
> +    qemu_mutex_lock_iothread();
> +    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_priv,
> +                                                  cs->cpu_index,
> clic_irq);
> +            if (edge) {
> +                riscv_clic_clean_pending(env->clic, clic_priv,
> +                                         cs->cpu_index, clic_irq);
> +            }
> +            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;
> +        }
> +    }
> +    qemu_mutex_unlock_iothread();
> +    return 0;
> +}
> +
>  static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong
> *val)
>  {
>      target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
> @@ -1755,6 +1867,7 @@ 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 },
> @@ -1766,6 +1879,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>
>      /* Supervisor Mode Core Level Interrupt Controller */
>      [CSR_STVT] = { "stvt", clic,  read_stvt, write_stvt       },
> +    [CSR_SNXTI] = { "snxti", clic,  NULL,  NULL,  rmw_snxti   },
>
>  #endif /* !CONFIG_USER_ONLY */
>  };
> --
> 2.25.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 10893 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 08/11] target/riscv: Update CSR xnxti in CLIC mode
  2021-06-11  8:15   ` Frank Chang
@ 2021-06-11  8:30     ` LIU Zhiwei
  2021-06-11  8:42       ` Frank Chang
  0 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-06-11  8:30 UTC (permalink / raw)
  To: Frank Chang
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 10459 bytes --]


On 6/11/21 4:15 PM, Frank Chang wrote:
> LIU Zhiwei <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> 於 
> 2021年4月9日 週五 下午3:52寫道:
>
>     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
>     <mailto:zhiwei_liu@c-sky.com>>
>     ---
>      target/riscv/cpu_bits.h |  16 ++++++
>      target/riscv/csr.c      | 114
>     ++++++++++++++++++++++++++++++++++++++++
>      2 files changed, 130 insertions(+)
>
>     diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
>     index 7922097776..494e41edc9 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      0x346 /* clic-spec-draft */
>      #define CSR_MINTTHRESH      0x347 /* clic-spec-draft */
>
>     @@ -187,6 +188,7 @@
>      #define CSR_SCAUSE          0x142
>      #define CSR_STVAL           0x143
>      #define CSR_SIP             0x144
>     +#define CSR_SNXTI           0x145 /* clic-spec-draft */
>      #define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
>      #define CSR_SINTTHRESH      0x147 /* clic-spec-draft */
>
>     @@ -596,10 +598,24 @@
>      #define MINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
>      #define MINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
>
>     +/* mcause */
>     +#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] */
>     +
>      /* sintstatus */
>      #define SINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
>      #define SINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
>
>     +/* scause */
>     +#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] */
>     +
>      /* 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 e12222b77f..72cba080bf 100644
>     --- a/target/riscv/csr.c
>     +++ b/target/riscv/csr.c
>     @@ -774,6 +774,80 @@ static int rmw_mip(CPURISCVState *env, int
>     csrno, target_ulong *ret_value,
>          return 0;
>      }
>
>     +static bool get_xnxti_status(CPURISCVState *env)
>     +{
>     +    CPUState *cs = env_cpu(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_priv,
>     cs->cpu_index,
>     +                                      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;
>     +    CPUState *cs = env_cpu(env);
>     +    if (write_mask) {
>     +        env->mstatus |= new_value & (write_mask & 0b11111);
>     +    }
>     +
>     +    qemu_mutex_lock_iothread();
>
>
> Hi Zhiwei,
>
> May I ask what's the purpose to request the BQL here with 
> /qemu_mutex_lock_iothread()/?
> Is there any critical data we need to protect in /rmw_mnxti()/?
> As far I see, /rmw_mnxti()/ won't call /cpu_interrupt()/ which need 
> BQL to be held before calling.
> Am I missing anything?
In my opinion, if you read or write any  MMIO register, you need to hold 
the BQL. As you can quickly see,
it calls riscv_clic_clean_pending. That's why it should hold the BQL.

Zhiwei
>
> Regard,
> Frank Chang
>
>     +    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_priv,
>     + cs->cpu_index, clic_irq);
>     +            if (edge) {
>     +                riscv_clic_clean_pending(env->clic, clic_priv,
>     +                                         cs->cpu_index, 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;
>     +        }
>     +    }
>     +    qemu_mutex_unlock_iothread();
>     +    return 0;
>     +}
>     +
>      static int read_mintstatus(CPURISCVState *env, int csrno,
>     target_ulong *val)
>      {
>          *val = env->mintstatus;
>     @@ -982,6 +1056,44 @@ static int rmw_sip(CPURISCVState *env, int
>     csrno, target_ulong *ret_value,
>          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;
>     +    CPUState *cs = env_cpu(env);
>     +    if (write_mask) {
>     +        env->mstatus |= new_value & (write_mask & 0b11111);
>     +    }
>     +
>     +    qemu_mutex_lock_iothread();
>     +    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_priv,
>     + cs->cpu_index, clic_irq);
>     +            if (edge) {
>     +                riscv_clic_clean_pending(env->clic, clic_priv,
>     +                                         cs->cpu_index, clic_irq);
>     +            }
>     +            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;
>     +        }
>     +    }
>     +    qemu_mutex_unlock_iothread();
>     +    return 0;
>     +}
>     +
>      static int read_sintstatus(CPURISCVState *env, int csrno,
>     target_ulong *val)
>      {
>          target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
>     @@ -1755,6 +1867,7 @@ 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 },
>     @@ -1766,6 +1879,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>
>          /* Supervisor Mode Core Level Interrupt Controller */
>          [CSR_STVT] = { "stvt", clic,  read_stvt, write_stvt    },
>     +    [CSR_SNXTI] = { "snxti", clic,  NULL,  NULL, rmw_snxti   },
>
>      #endif /* !CONFIG_USER_ONLY */
>      };
>     -- 
>     2.25.1
>
>

[-- Attachment #2: Type: text/html, Size: 15274 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 08/11] target/riscv: Update CSR xnxti in CLIC mode
  2021-06-11  8:30     ` LIU Zhiwei
@ 2021-06-11  8:42       ` Frank Chang
  2021-06-11  8:56         ` LIU Zhiwei
  0 siblings, 1 reply; 81+ messages in thread
From: Frank Chang @ 2021-06-11  8:42 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 9500 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月11日 週五 下午4:30寫道:

>
> On 6/11/21 4:15 PM, Frank Chang wrote:
>
> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:52寫道:
>
>> 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>
>> ---
>>  target/riscv/cpu_bits.h |  16 ++++++
>>  target/riscv/csr.c      | 114 ++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 130 insertions(+)
>>
>> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
>> index 7922097776..494e41edc9 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      0x346 /* clic-spec-draft */
>>  #define CSR_MINTTHRESH      0x347 /* clic-spec-draft */
>>
>> @@ -187,6 +188,7 @@
>>  #define CSR_SCAUSE          0x142
>>  #define CSR_STVAL           0x143
>>  #define CSR_SIP             0x144
>> +#define CSR_SNXTI           0x145 /* clic-spec-draft */
>>  #define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
>>  #define CSR_SINTTHRESH      0x147 /* clic-spec-draft */
>>
>> @@ -596,10 +598,24 @@
>>  #define MINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
>>  #define MINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
>>
>> +/* mcause */
>> +#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] */
>> +
>>  /* sintstatus */
>>  #define SINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
>>  #define SINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
>>
>> +/* scause */
>> +#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] */
>> +
>>  /* 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 e12222b77f..72cba080bf 100644
>> --- a/target/riscv/csr.c
>> +++ b/target/riscv/csr.c
>> @@ -774,6 +774,80 @@ static int rmw_mip(CPURISCVState *env, int csrno,
>> target_ulong *ret_value,
>>      return 0;
>>  }
>>
>> +static bool get_xnxti_status(CPURISCVState *env)
>> +{
>> +    CPUState *cs = env_cpu(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_priv,
>> cs->cpu_index,
>> +                                      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;
>> +    CPUState *cs = env_cpu(env);
>> +    if (write_mask) {
>> +        env->mstatus |= new_value & (write_mask & 0b11111);
>> +    }
>> +
>> +    qemu_mutex_lock_iothread();
>>
>
> Hi Zhiwei,
>
> May I ask what's the purpose to request the BQL here with
> *qemu_mutex_lock_iothread()*?
> Is there any critical data we need to protect in *rmw_mnxti()*?
> As far I see, *rmw_mnxti()* won't call *cpu_interrupt()* which need BQL
> to be held before calling.
> Am I missing anything?
>
> In my opinion, if you read or write any  MMIO register, you need to hold
> the BQL. As you can quickly see,
> it calls riscv_clic_clean_pending. That's why it should hold the BQL.
>
> Zhiwei
>

Oh, I see.
The MMIO register reads and writes should also be protected by BQL.
Thanks for the explanation.

Regards,
Frank Chang


>
> Regard,
> Frank Chang
>
>
>> +    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_priv,
>> +                                                  cs->cpu_index,
>> clic_irq);
>> +            if (edge) {
>> +                riscv_clic_clean_pending(env->clic, clic_priv,
>> +                                         cs->cpu_index, 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;
>> +        }
>> +    }
>> +    qemu_mutex_unlock_iothread();
>> +    return 0;
>> +}
>> +
>>  static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong
>> *val)
>>  {
>>      *val = env->mintstatus;
>> @@ -982,6 +1056,44 @@ static int rmw_sip(CPURISCVState *env, int csrno,
>> target_ulong *ret_value,
>>      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;
>> +    CPUState *cs = env_cpu(env);
>> +    if (write_mask) {
>> +        env->mstatus |= new_value & (write_mask & 0b11111);
>> +    }
>> +
>> +    qemu_mutex_lock_iothread();
>> +    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_priv,
>> +                                                  cs->cpu_index,
>> clic_irq);
>> +            if (edge) {
>> +                riscv_clic_clean_pending(env->clic, clic_priv,
>> +                                         cs->cpu_index, clic_irq);
>> +            }
>> +            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;
>> +        }
>> +    }
>> +    qemu_mutex_unlock_iothread();
>> +    return 0;
>> +}
>> +
>>  static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong
>> *val)
>>  {
>>      target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
>> @@ -1755,6 +1867,7 @@ 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 },
>> @@ -1766,6 +1879,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>>
>>      /* Supervisor Mode Core Level Interrupt Controller */
>>      [CSR_STVT] = { "stvt", clic,  read_stvt, write_stvt       },
>> +    [CSR_SNXTI] = { "snxti", clic,  NULL,  NULL,  rmw_snxti   },
>>
>>  #endif /* !CONFIG_USER_ONLY */
>>  };
>> --
>> 2.25.1
>>
>>
>>

[-- Attachment #2: Type: text/html, Size: 15995 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 08/11] target/riscv: Update CSR xnxti in CLIC mode
  2021-06-11  8:42       ` Frank Chang
@ 2021-06-11  8:56         ` LIU Zhiwei
  2021-06-11  9:07           ` Frank Chang
  0 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-06-11  8:56 UTC (permalink / raw)
  To: Frank Chang
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 11642 bytes --]


On 6/11/21 4:42 PM, Frank Chang wrote:
> LIU Zhiwei <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> 於 
> 2021年6月11日 週五 下午4:30寫道:
>
>
>     On 6/11/21 4:15 PM, Frank Chang wrote:
>>     LIU Zhiwei <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> 於
>>     2021年4月9日 週五 下午3:52寫道:
>>
>>         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
>>         <mailto:zhiwei_liu@c-sky.com>>
>>         ---
>>          target/riscv/cpu_bits.h |  16 ++++++
>>          target/riscv/csr.c      | 114
>>         ++++++++++++++++++++++++++++++++++++++++
>>          2 files changed, 130 insertions(+)
>>
>>         diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
>>         index 7922097776..494e41edc9 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      0x346 /* clic-spec-draft */
>>          #define CSR_MINTTHRESH      0x347 /* clic-spec-draft */
>>
>>         @@ -187,6 +188,7 @@
>>          #define CSR_SCAUSE          0x142
>>          #define CSR_STVAL           0x143
>>          #define CSR_SIP             0x144
>>         +#define CSR_SNXTI           0x145 /* clic-spec-draft */
>>          #define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
>>          #define CSR_SINTTHRESH      0x147 /* clic-spec-draft */
>>
>>         @@ -596,10 +598,24 @@
>>          #define MINTSTATUS_SIL  0x0000ff00 /* sil[7:0] */
>>          #define MINTSTATUS_UIL  0x000000ff /* uil[7:0] */
>>
>>         +/* mcause */
>>         +#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] */
>>         +
>>          /* sintstatus */
>>          #define SINTSTATUS_SIL  0x0000ff00 /* sil[7:0] */
>>          #define SINTSTATUS_UIL  0x000000ff /* uil[7:0] */
>>
>>         +/* scause */
>>         +#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] */
>>         +
>>          /* 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 e12222b77f..72cba080bf 100644
>>         --- a/target/riscv/csr.c
>>         +++ b/target/riscv/csr.c
>>         @@ -774,6 +774,80 @@ static int rmw_mip(CPURISCVState *env,
>>         int csrno, target_ulong *ret_value,
>>              return 0;
>>          }
>>
>>         +static bool get_xnxti_status(CPURISCVState *env)
>>         +{
>>         +    CPUState *cs = env_cpu(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_priv, cs->cpu_index,
>>         +                                      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;
>>         +    CPUState *cs = env_cpu(env);
>>         +    if (write_mask) {
>>         +        env->mstatus |= new_value & (write_mask & 0b11111);
>>         +    }
>>         +
>>         +    qemu_mutex_lock_iothread();
>>
>>
>>     Hi Zhiwei,
>>
>>     May I ask what's the purpose to request the BQL here with
>>     /qemu_mutex_lock_iothread()/?
>>     Is there any critical data we need to protect in /rmw_mnxti()/?
>>     As far I see, /rmw_mnxti()/ won't call /cpu_interrupt()/ which
>>     need BQL to be held before calling.
>>     Am I missing anything?
>     In my opinion, if you read or write any  MMIO register, you need
>     to hold the BQL. As you can quickly see,
>     it calls riscv_clic_clean_pending. That's why it should hold the BQL.
>
>     Zhiwei
>
>
> Oh, I see.
> The MMIO register reads and writes should also be protected by BQL.
> Thanks for the explanation.

I am glad to know that you are reviewing this patch set. As Sifive 
implements the initial v0.7 CLIC, I think you may need this patch set 
for your SOC.
If you like to, I am happy to see that you connect this patch set to 
your SOC, and resend it again. I can also provide the qtest of this 
patch set if you need.

As you may see, the v6.1 soft freeze will come in July. I am afraid I 
can't upstream a new SOC in so short time.

Zhiwei

>
> Regards,
> Frank Chang
>
>>
>>     Regard,
>>     Frank Chang
>>
>>         +    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_priv,
>>         + cs->cpu_index, clic_irq);
>>         +            if (edge) {
>>         + riscv_clic_clean_pending(env->clic, clic_priv,
>>         +  cs->cpu_index, 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;
>>         +        }
>>         +    }
>>         +    qemu_mutex_unlock_iothread();
>>         +    return 0;
>>         +}
>>         +
>>          static int read_mintstatus(CPURISCVState *env, int csrno,
>>         target_ulong *val)
>>          {
>>              *val = env->mintstatus;
>>         @@ -982,6 +1056,44 @@ static int rmw_sip(CPURISCVState *env,
>>         int csrno, target_ulong *ret_value,
>>              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;
>>         +    CPUState *cs = env_cpu(env);
>>         +    if (write_mask) {
>>         +        env->mstatus |= new_value & (write_mask & 0b11111);
>>         +    }
>>         +
>>         +    qemu_mutex_lock_iothread();
>>         +    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_priv,
>>         + cs->cpu_index, clic_irq);
>>         +            if (edge) {
>>         + riscv_clic_clean_pending(env->clic, clic_priv,
>>         +  cs->cpu_index, clic_irq);
>>         +            }
>>         +            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;
>>         +        }
>>         +    }
>>         +    qemu_mutex_unlock_iothread();
>>         +    return 0;
>>         +}
>>         +
>>          static int read_sintstatus(CPURISCVState *env, int csrno,
>>         target_ulong *val)
>>          {
>>              target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
>>         @@ -1755,6 +1867,7 @@ 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 },
>>         @@ -1766,6 +1879,7 @@ riscv_csr_operations
>>         csr_ops[CSR_TABLE_SIZE] = {
>>
>>              /* Supervisor Mode Core Level Interrupt Controller */
>>              [CSR_STVT] = { "stvt", clic,  read_stvt, write_stvt       },
>>         +    [CSR_SNXTI] = { "snxti", clic,  NULL,  NULL, rmw_snxti   },
>>
>>          #endif /* !CONFIG_USER_ONLY */
>>          };
>>         -- 
>>         2.25.1
>>
>>

[-- Attachment #2: Type: text/html, Size: 21074 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 08/11] target/riscv: Update CSR xnxti in CLIC mode
  2021-06-11  8:56         ` LIU Zhiwei
@ 2021-06-11  9:07           ` Frank Chang
  2021-06-11  9:26             ` LIU Zhiwei
  2021-06-15  7:45             ` Alistair Francis
  0 siblings, 2 replies; 81+ messages in thread
From: Frank Chang @ 2021-06-11  9:07 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 10894 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月11日 週五 下午4:56寫道:

>
> On 6/11/21 4:42 PM, Frank Chang wrote:
>
> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月11日 週五 下午4:30寫道:
>
>>
>> On 6/11/21 4:15 PM, Frank Chang wrote:
>>
>> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:52寫道:
>>
>>> 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>
>>> ---
>>>  target/riscv/cpu_bits.h |  16 ++++++
>>>  target/riscv/csr.c      | 114 ++++++++++++++++++++++++++++++++++++++++
>>>  2 files changed, 130 insertions(+)
>>>
>>> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
>>> index 7922097776..494e41edc9 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      0x346 /* clic-spec-draft */
>>>  #define CSR_MINTTHRESH      0x347 /* clic-spec-draft */
>>>
>>> @@ -187,6 +188,7 @@
>>>  #define CSR_SCAUSE          0x142
>>>  #define CSR_STVAL           0x143
>>>  #define CSR_SIP             0x144
>>> +#define CSR_SNXTI           0x145 /* clic-spec-draft */
>>>  #define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
>>>  #define CSR_SINTTHRESH      0x147 /* clic-spec-draft */
>>>
>>> @@ -596,10 +598,24 @@
>>>  #define MINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
>>>  #define MINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
>>>
>>> +/* mcause */
>>> +#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]
>>> */
>>> +
>>>  /* sintstatus */
>>>  #define SINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
>>>  #define SINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
>>>
>>> +/* scause */
>>> +#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]
>>> */
>>> +
>>>  /* 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 e12222b77f..72cba080bf 100644
>>> --- a/target/riscv/csr.c
>>> +++ b/target/riscv/csr.c
>>> @@ -774,6 +774,80 @@ static int rmw_mip(CPURISCVState *env, int csrno,
>>> target_ulong *ret_value,
>>>      return 0;
>>>  }
>>>
>>> +static bool get_xnxti_status(CPURISCVState *env)
>>> +{
>>> +    CPUState *cs = env_cpu(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_priv,
>>> cs->cpu_index,
>>> +                                      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;
>>> +    CPUState *cs = env_cpu(env);
>>> +    if (write_mask) {
>>> +        env->mstatus |= new_value & (write_mask & 0b11111);
>>> +    }
>>> +
>>> +    qemu_mutex_lock_iothread();
>>>
>>
>> Hi Zhiwei,
>>
>> May I ask what's the purpose to request the BQL here with
>> *qemu_mutex_lock_iothread()*?
>> Is there any critical data we need to protect in *rmw_mnxti()*?
>> As far I see, *rmw_mnxti()* won't call *cpu_interrupt()* which need BQL
>> to be held before calling.
>> Am I missing anything?
>>
>> In my opinion, if you read or write any  MMIO register, you need to hold
>> the BQL. As you can quickly see,
>> it calls riscv_clic_clean_pending. That's why it should hold the BQL.
>>
>> Zhiwei
>>
>
> Oh, I see.
> The MMIO register reads and writes should also be protected by BQL.
> Thanks for the explanation.
>
> I am glad to know that you are reviewing this patch set. As Sifive
> implements the initial v0.7 CLIC, I think you may need this patch set for
> your SOC.
> If you like to, I am happy to see that you connect this patch set to your
> SOC, and resend it again. I can also provide the qtest of this patch set if
> you need.
>
> As you may see, the v6.1 soft freeze will come in July. I am afraid I
> can't upstream a new SOC in so short time.
>
> Zhiwei
>
Thanks, I think we will leverage the hard works you have done into our
implementation.
However, I'm not sure I can catch up the deadline before v6.1's soft-freeze.
But I think I can help to review the patches, at least we can speed up the
review process.

Regarding qtest, I saw your head commit mentioned the repo you are using:
[1].
Is it okay to just grab the qtest from this repo?

[1]:
https://github.com/romanheros/qemu/commit/bce1845ea9b079b4c360440292dc47725d1b24ab

Thanks,
Frank Chang


>
> Regards,
> Frank Chang
>
>
>>
>> Regard,
>> Frank Chang
>>
>>
>>> +    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_priv,
>>> +                                                  cs->cpu_index,
>>> clic_irq);
>>> +            if (edge) {
>>> +                riscv_clic_clean_pending(env->clic, clic_priv,
>>> +                                         cs->cpu_index, 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;
>>> +        }
>>> +    }
>>> +    qemu_mutex_unlock_iothread();
>>> +    return 0;
>>> +}
>>> +
>>>  static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong
>>> *val)
>>>  {
>>>      *val = env->mintstatus;
>>> @@ -982,6 +1056,44 @@ static int rmw_sip(CPURISCVState *env, int csrno,
>>> target_ulong *ret_value,
>>>      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;
>>> +    CPUState *cs = env_cpu(env);
>>> +    if (write_mask) {
>>> +        env->mstatus |= new_value & (write_mask & 0b11111);
>>> +    }
>>> +
>>> +    qemu_mutex_lock_iothread();
>>> +    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_priv,
>>> +                                                  cs->cpu_index,
>>> clic_irq);
>>> +            if (edge) {
>>> +                riscv_clic_clean_pending(env->clic, clic_priv,
>>> +                                         cs->cpu_index, clic_irq);
>>> +            }
>>> +            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;
>>> +        }
>>> +    }
>>> +    qemu_mutex_unlock_iothread();
>>> +    return 0;
>>> +}
>>> +
>>>  static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong
>>> *val)
>>>  {
>>>      target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
>>> @@ -1755,6 +1867,7 @@ 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 },
>>> @@ -1766,6 +1879,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>>>
>>>      /* Supervisor Mode Core Level Interrupt Controller */
>>>      [CSR_STVT] = { "stvt", clic,  read_stvt, write_stvt       },
>>> +    [CSR_SNXTI] = { "snxti", clic,  NULL,  NULL,  rmw_snxti   },
>>>
>>>  #endif /* !CONFIG_USER_ONLY */
>>>  };
>>> --
>>> 2.25.1
>>>
>>>
>>>

[-- Attachment #2: Type: text/html, Size: 22174 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 08/11] target/riscv: Update CSR xnxti in CLIC mode
  2021-06-11  9:07           ` Frank Chang
@ 2021-06-11  9:26             ` LIU Zhiwei
  2021-06-15  7:45             ` Alistair Francis
  1 sibling, 0 replies; 81+ messages in thread
From: LIU Zhiwei @ 2021-06-11  9:26 UTC (permalink / raw)
  To: Frank Chang
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 13917 bytes --]


On 6/11/21 5:07 PM, Frank Chang wrote:
> LIU Zhiwei <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> 於 
> 2021年6月11日 週五 下午4:56寫道:
>
>
>     On 6/11/21 4:42 PM, Frank Chang wrote:
>>     LIU Zhiwei <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> 於
>>     2021年6月11日 週五 下午4:30寫道:
>>
>>
>>         On 6/11/21 4:15 PM, Frank Chang wrote:
>>>         LIU Zhiwei <zhiwei_liu@c-sky.com
>>>         <mailto:zhiwei_liu@c-sky.com>> 於 2021年4月9日 週五
>>>         下午3:52寫道:
>>>
>>>             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
>>>             <mailto:zhiwei_liu@c-sky.com>>
>>>             ---
>>>              target/riscv/cpu_bits.h |  16 ++++++
>>>              target/riscv/csr.c      | 114
>>>             ++++++++++++++++++++++++++++++++++++++++
>>>              2 files changed, 130 insertions(+)
>>>
>>>             diff --git a/target/riscv/cpu_bits.h
>>>             b/target/riscv/cpu_bits.h
>>>             index 7922097776..494e41edc9 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      0x346 /* clic-spec-draft */
>>>              #define CSR_MINTTHRESH      0x347 /* clic-spec-draft */
>>>
>>>             @@ -187,6 +188,7 @@
>>>              #define CSR_SCAUSE          0x142
>>>              #define CSR_STVAL           0x143
>>>              #define CSR_SIP             0x144
>>>             +#define CSR_SNXTI           0x145 /* clic-spec-draft */
>>>              #define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
>>>              #define CSR_SINTTHRESH      0x147 /* clic-spec-draft */
>>>
>>>             @@ -596,10 +598,24 @@
>>>              #define MINTSTATUS_SIL    0x0000ff00 /* sil[7:0] */
>>>              #define MINTSTATUS_UIL    0x000000ff /* uil[7:0] */
>>>
>>>             +/* mcause */
>>>             +#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] */
>>>             +
>>>              /* sintstatus */
>>>              #define SINTSTATUS_SIL    0x0000ff00 /* sil[7:0] */
>>>              #define SINTSTATUS_UIL    0x000000ff /* uil[7:0] */
>>>
>>>             +/* scause */
>>>             +#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] */
>>>             +
>>>              /* 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 e12222b77f..72cba080bf 100644
>>>             --- a/target/riscv/csr.c
>>>             +++ b/target/riscv/csr.c
>>>             @@ -774,6 +774,80 @@ static int rmw_mip(CPURISCVState
>>>             *env, int csrno, target_ulong *ret_value,
>>>                  return 0;
>>>              }
>>>
>>>             +static bool get_xnxti_status(CPURISCVState *env)
>>>             +{
>>>             +    CPUState *cs = env_cpu(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_priv,
>>>             cs->cpu_index,
>>>             + 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;
>>>             +    CPUState *cs = env_cpu(env);
>>>             +    if (write_mask) {
>>>             +        env->mstatus |= new_value & (write_mask & 0b11111);
>>>             +    }
>>>             +
>>>             +    qemu_mutex_lock_iothread();
>>>
>>>
>>>         Hi Zhiwei,
>>>
>>>         May I ask what's the purpose to request the BQL here with
>>>         /qemu_mutex_lock_iothread()/?
>>>         Is there any critical data we need to protect in /rmw_mnxti()/?
>>>         As far I see, /rmw_mnxti()/ won't call /cpu_interrupt()/
>>>         which need BQL to be held before calling.
>>>         Am I missing anything?
>>         In my opinion, if you read or write any  MMIO register, you
>>         need to hold the BQL. As you can quickly see,
>>         it calls riscv_clic_clean_pending. That's why it should hold
>>         the BQL.
>>
>>         Zhiwei
>>
>>
>>     Oh, I see.
>>     The MMIO register reads and writes should also be protected by BQL.
>>     Thanks for the explanation.
>
>     I am glad to know that you are reviewing this patch set. As Sifive
>     implements the initial v0.7 CLIC, I think you may need this patch
>     set for your SOC.
>     If you like to, I am happy to see that you connect this patch set
>     to your SOC, and resend it again. I can also provide the qtest of
>     this patch set if you need.
>
>     As you may see, the v6.1 soft freeze will come in July. I am
>     afraid I can't upstream a new SOC in so short time.
>
>     Zhiwei
>
> Thanks, I think we will leverage the hard works you have done into our 
> implementation.
> However, I'm not sure I can catch up the deadline before v6.1's 
> soft-freeze.
> But I think I can help to review the patches, at least we can speed up 
> the review process.
>
Nice.
> Regarding qtest, I saw your head commit mentioned the repo you are 
> using: [1].
> Is it okay to just grab the qtest from this repo?
>
> [1]: 
> https://github.com/romanheros/qemu/commit/bce1845ea9b079b4c360440292dc47725d1b24ab 
> <https://github.com/romanheros/qemu/commit/bce1845ea9b079b4c360440292dc47725d1b24ab>
>
Yes, it is. The qtest is not so trivial, as it extends current qtest 
mechanism by intercepting irq_in and irq_out for one device
(Normally, you can only intercept irq_in or irq_out for one device). If 
you have any question, just let me know.

Zhiwei

> Thanks,
> Frank Chang
>
>>
>>     Regards,
>>     Frank Chang
>>
>>>
>>>         Regard,
>>>         Frank Chang
>>>
>>>             + 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_priv,
>>>             +           cs->cpu_index, clic_irq);
>>>             +            if (edge) {
>>>             + riscv_clic_clean_pending(env->clic, clic_priv,
>>>             +  cs->cpu_index, 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;
>>>             +        }
>>>             +    }
>>>             +    qemu_mutex_unlock_iothread();
>>>             +    return 0;
>>>             +}
>>>             +
>>>              static int read_mintstatus(CPURISCVState *env, int
>>>             csrno, target_ulong *val)
>>>              {
>>>                  *val = env->mintstatus;
>>>             @@ -982,6 +1056,44 @@ static int rmw_sip(CPURISCVState
>>>             *env, int csrno, target_ulong *ret_value,
>>>                  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;
>>>             +    CPUState *cs = env_cpu(env);
>>>             +    if (write_mask) {
>>>             +        env->mstatus |= new_value & (write_mask & 0b11111);
>>>             +    }
>>>             +
>>>             +    qemu_mutex_lock_iothread();
>>>             +    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_priv,
>>>             +           cs->cpu_index, clic_irq);
>>>             +            if (edge) {
>>>             + riscv_clic_clean_pending(env->clic, clic_priv,
>>>             +  cs->cpu_index, clic_irq);
>>>             +            }
>>>             +            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;
>>>             +        }
>>>             +    }
>>>             +    qemu_mutex_unlock_iothread();
>>>             +    return 0;
>>>             +}
>>>             +
>>>              static int read_sintstatus(CPURISCVState *env, int
>>>             csrno, target_ulong *val)
>>>              {
>>>                  target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
>>>             @@ -1755,6 +1867,7 @@ 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 },
>>>             @@ -1766,6 +1879,7 @@ riscv_csr_operations
>>>             csr_ops[CSR_TABLE_SIZE] = {
>>>
>>>                  /* Supervisor Mode Core Level Interrupt Controller */
>>>                  [CSR_STVT] = { "stvt", clic, read_stvt, write_stvt 
>>>                  },
>>>             +    [CSR_SNXTI] = { "snxti", clic, NULL,  NULL, 
>>>             rmw_snxti   },
>>>
>>>              #endif /* !CONFIG_USER_ONLY */
>>>              };
>>>             -- 
>>>             2.25.1
>>>
>>>

[-- Attachment #2: Type: text/html, Size: 28665 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-04-09  7:48 ` [RFC PATCH 03/11] hw/intc: Add CLIC device LIU Zhiwei
  2021-04-19 23:25   ` Alistair Francis
@ 2021-06-13 10:10   ` Frank Chang
  2021-06-16  2:56     ` LIU Zhiwei
  2021-06-26 15:03   ` Frank Chang
                     ` (5 subsequent siblings)
  7 siblings, 1 reply; 81+ messages in thread
From: Frank Chang @ 2021-06-13 10:10 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 40107 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:57寫道:

> 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>
> ---
>  default-configs/devices/riscv32-softmmu.mak |   1 +
>  default-configs/devices/riscv64-softmmu.mak |   1 +
>  hw/intc/Kconfig                             |   3 +
>  hw/intc/meson.build                         |   1 +
>  hw/intc/riscv_clic.c                        | 835 ++++++++++++++++++++
>  include/hw/intc/riscv_clic.h                | 103 +++
>  target/riscv/cpu.h                          |   2 +
>  7 files changed, 946 insertions(+)
>  create mode 100644 hw/intc/riscv_clic.c
>  create mode 100644 include/hw/intc/riscv_clic.h
>
> diff --git a/default-configs/devices/riscv32-softmmu.mak
> b/default-configs/devices/riscv32-softmmu.mak
> index d847bd5692..1430c30588 100644
> --- a/default-configs/devices/riscv32-softmmu.mak
> +++ b/default-configs/devices/riscv32-softmmu.mak
> @@ -5,6 +5,7 @@
>  #CONFIG_PCI_DEVICES=n
>  CONFIG_SEMIHOSTING=y
>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> +CONFIG_RISCV_CLIC=y
>
>  # Boards:
>  #
> diff --git a/default-configs/devices/riscv64-softmmu.mak
> b/default-configs/devices/riscv64-softmmu.mak
> index d5eec75f05..396800bbbd 100644
> --- a/default-configs/devices/riscv64-softmmu.mak
> +++ b/default-configs/devices/riscv64-softmmu.mak
> @@ -5,6 +5,7 @@
>  #CONFIG_PCI_DEVICES=n
>  CONFIG_SEMIHOSTING=y
>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> +CONFIG_RISCV_CLIC=y
>
>  # Boards:
>  #
> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
> index f4694088a4..5bf492b48f 100644
> --- a/hw/intc/Kconfig
> +++ b/hw/intc/Kconfig
> @@ -68,6 +68,9 @@ config SIFIVE_CLINT
>  config SIFIVE_PLIC
>      bool
>
> +config RISCV_CLIC
> +    bool
> +
>  config GOLDFISH_PIC
>      bool
>
> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> index 1c299039f6..2aa71b6738 100644
> --- a/hw/intc/meson.build
> +++ b/hw/intc/meson.build
> @@ -50,6 +50,7 @@ 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_SIFIVE_CLINT', if_true:
> files('sifive_clint.c'))
>  specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true:
> files('sifive_plic.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'))
> diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
> new file mode 100644
> index 0000000000..8ad534c506
> --- /dev/null
> +++ b/hw/intc/riscv_clic.c
> @@ -0,0 +1,835 @@
> +/*
> + * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
> + *
> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
> + *
> + * 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/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/log.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"
> +
> +/*
> + * 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_offset)
> +{
> +    return (clic->clicintattr[irq_offset] >> 1) & 0x3;
> +}
> +
> +static inline bool
> +riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
> +{
> +    return (clic->clicintattr[irq_offset] >> 1) & 0x1;
> +}
> +
> +static inline bool
> +riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
> +{
> +    return (clic->clicintattr[irq_offset] & 0x1) && clic->nvbits;
> +}
> +
> +static uint8_t
> +riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
> +{
> +    int nlbits = clic->nlbits;
> +
> +    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->nlbits;
> +    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);
> +}
> +
> +/*
> + * In a system with multiple harts, the M-mode CLIC regions for all the
> harts
> + * are placed contiguously in the memory space, followed by the S-mode
> CLIC
> + * regions for all harts. (Section 3.11)
> + */
> +static size_t
> +riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int hartid, int
> irq)
> +{
> +    size_t mode_offset = 0;
> +    size_t unit = clic->num_harts * clic->num_sources;
> +
> +    switch (mode) {
> +    case PRV_M:
> +        mode_offset = 0;
> +        break;
> +    case PRV_S:
> +        mode_offset = unit;
> +        break;
> +    case PRV_U:
> +        mode_offset = clic->prv_s ? 2 * unit : unit;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid mode %d\n", mode);
> +        exit(1);
> +    }
> +    return mode_offset + hartid * clic->num_sources + irq;
> +}
> +
> +static void riscv_clic_next_interrupt(void *opaque, int hartid)
> +{
> +    /*
> +     * 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
> +     */
> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(hartid));
> +    CPURISCVState *env = &cpu->env;
> +    RISCVCLICState *clic = (RISCVCLICState *)opaque;
> +
> +    int il[4] = {
> +        MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
> +            clic->mintthresh), /* PRV_U */
> +        MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
> +            clic->sintthresh), /* PRV_S */
> +        0,                     /* reserverd */
> +        MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
> +            clic->uintthresh)  /* PRV_M */
> +    };
> +
> +    /* Get sorted list of enabled interrupts for this hart */
> +    size_t hart_offset = hartid * clic->num_sources;
> +    CLICActiveInterrupt *active = &clic->active_list[hart_offset];
> +    size_t active_count = clic->active_count[hartid];
> +    uint8_t mode, level, priority;
> +
> +    /* Loop through the enabled interrupts sorted by mode+priority+level
> */
> +    while (active_count) {
> +        size_t irq_offset;
> +        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;
> +        }
> +        irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> active->irq);
> +        /* Check pending interrupt with high enough mode+priority+level */
> +        if (clic->clicintip[irq_offset]) {
> +            /* Clean vector edge-triggered pending */
> +            if (riscv_clic_is_edge_triggered(clic, irq_offset) &&
> +                riscv_clic_is_shv_interrupt(clic, irq_offset)) {
> +                clic->clicintip[irq_offset] = 0;
> +            }
> +            /* Post pending interrupt for this hart */
> +            clic->exccode[hartid] = active->irq | mode << 12 | level <<
> 14;
> +            qemu_set_irq(clic->cpu_irqs[hartid], 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 hartid, int
> irq)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    if (!clic->prv_s && !clic->prv_u) { /* M */
> +        return mode == PRV_M;
> +    } else if (!clic->prv_s) { /* M/U */
> +        switch (clic->nmbits) {
> +        case 0:
> +            return mode == PRV_M;
> +        case 1:
> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M)
> :
> +                                                          (mode == PRV_U);
> +        default:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "clic: nmbits can only be 0 or 1 for M/U hart");
> +            exit(1);
> +        }
> +    } else { /* M/S/U */
> +        switch (clic->nmbits) {
> +        case 0:
> +            return mode == PRV_M;
> +        case 1:
> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M)
> :
> +                                                          (mode == PRV_S);
> +        case 2:
> +            return mode == clic->clicintattr[irq_offset];
> +        case 3:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "clic: nmbits can only be 0 or 1 or 2 for M/S/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 mode, int hartid, int
> irq)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
> +}
> +
> +static void
> +riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
> +                        int irq, uint64_t value)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    clic->clicintip[irq_offset] = !!value;
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +/*
> + * 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, uint64_t
> value)
> +{
> +    int mode = extract64(value, 6, 2);
> +
> +    if (!qtest_enabled()) {
> +        CPURISCVState *env = current_cpu->env_ptr;
> +        if (env->priv < mode) {
> +            return false;
> +        }
> +    }
> +    return true;
> +}
> +
> +static inline int riscv_clic_encode_priority(const CLICActiveInterrupt *i)
> +{
> +    return ((i->intcfg & 0x3ff) << 12) | /* Highest mode+level+priority */
> +           (i->irq & 0xfff);             /* Highest irq number */
> +}
> +
> +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 hartid,
> +                        int irq, uint64_t new_intie)
> +{
> +    size_t hart_offset = hartid * clic->num_sources;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    CLICActiveInterrupt *active_list = &clic->active_list[hart_offset];
> +    size_t *active_count = &clic->active_count[hartid];
> +
> +    uint8_t old_intie = clic->clicintie[irq_offset];
> +    clic->clicintie[irq_offset] = !!new_intie;
> +
> +    /* Add to or remove from list of active interrupts */
> +    if (new_intie && !old_intie) {
> +        active_list[*active_count].intcfg = (mode << 8) |
> +                                            clic->clicintctl[irq_offset];
> +        active_list[*active_count].irq = irq;
> +        (*active_count)++;
> +    } else if (!new_intie && old_intie) {
> +        CLICActiveInterrupt key = {
> +            (mode << 8) | clic->clicintctl[irq_offset], irq
> +        };
> +        CLICActiveInterrupt *result = bsearch(&key,
> +                                              active_list, *active_count,
> +                                              sizeof(CLICActiveInterrupt),
> +                                              riscv_clic_active_compare);
> +        size_t elem = (result - active_list) /
> sizeof(CLICActiveInterrupt);
> +        size_t sz = (--(*active_count) - elem) *
> sizeof(CLICActiveInterrupt);
> +        assert(result);
> +        memmove(&result[0], &result[1], sz);
> +    }
> +
> +    /* Sort list of active interrupts */
> +    qsort(active_list, *active_count,
> +          sizeof(CLICActiveInterrupt),
> +          riscv_clic_active_compare);
> +
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +static void
> +riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
> +                      uint64_t value, unsigned size,
> +                      int mode, int hartid, int irq)
> +{
> +    int req = extract32(addr, 0, 2);
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +
> +    if (hartid >= clic->num_harts) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
> +                      hartid, addr);
> +        return;
> +    }
> +
> +    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, mode, hartid, irq)) {
> +            /*
> +             * The actual pending bit is located at bit 0 (i.e., the
> +             * leastsignificant 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_offset]) {
> +                riscv_clic_update_intip(clic, mode, hartid, irq, value);
> +            }
> +        }
> +        break;
> +    case 1: /* clicintie[i] */
> +        if (clic->clicintie[irq_offset] != value) {
> +            riscv_clic_update_intie(clic, mode, hartid, irq, value);
> +        }
> +        break;
> +    case 2: /* clicintattr[i] */
> +        if (riscv_clic_validate_intattr(clic, value)) {
> +            if (clic->clicintattr[irq_offset] != value) {
> +                /* When nmbits=2, check WARL */
> +                bool invalid = (clic->nmbits == 2) &&
> +                               (extract64(value, 6, 2) == 0b10);
> +                if (invalid) {
> +                    uint8_t old_mode =
> extract32(clic->clicintattr[irq_offset],
> +                                                 6, 2);
> +                    value = deposit32(value, 6, 2, old_mode);
> +                }
> +                clic->clicintattr[irq_offset] = value;
> +                riscv_clic_next_interrupt(clic, hartid);
> +            }
> +        }
> +        break;
> +    case 3: /* clicintctl[i] */
> +        if (value != clic->clicintctl[irq_offset]) {
> +            clic->clicintctl[irq_offset] = value;
> +            riscv_clic_next_interrupt(clic, hartid);
> +        }
> +        break;
> +    }
> +}
> +
> +static uint64_t
> +riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
> +                     int hartid, int irq)
> +{
> +    int req = extract32(addr, 0, 2);
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +
> +    if (hartid >= clic->num_harts) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
> +                      hartid, addr);
> +        return 0;
> +    }
> +
> +    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] */
> +        return clic->clicintip[irq_offset];
> +    case 1: /* clicintie[i] */
> +        return clic->clicintie[irq_offset];
> +    case 2: /* clicintattr[i] */
> +        /*
> +         * clicintattr register layout
> +         * Bits Field
> +         * 7:6 mode
> +         * 5:3 reserved (WPRI 0)
> +         * 2:1 trig
> +         * 0 shv
> +         */
> +        return clic->clicintattr[irq_offset] & ~0x38;
> +    case 3: /* clicintctrl */
> +        /*
> +         * 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_offset] |
> +               ((1 << (8 - clic->clicintctlbits)) - 1);
> +    }
> +
> +    return 0;
> +}
> +
> +/* Return target interrupt mode */
> +static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int mode = addr / (4 * clic->num_harts * clic->num_sources);
> +    switch (mode) {
> +    case 0:
> +        return PRV_M;
> +    case 1:
> +        assert(clic->prv_s || clic->prv_u);
> +        return clic->prv_s ? PRV_S : PRV_U;
> +    case 2:
> +        assert(clic->prv_s && clic->prv_u);
> +        return PRV_U;
> +    default:
> +        g_assert_not_reached();
> +        break;
> +    }
> +}
> +
> +/* Return target hart id */
> +static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int mode_unit = 4 * clic->num_harts * clic->num_sources;
> +    int hart_unit = 4 * clic->num_sources;
> +
> +    return (addr % mode_unit) / hart_unit;
> +}
> +
> +/* Return target interrupt number */
> +static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int hart_unit = 4 * clic->num_sources;
> +    return (addr % hart_unit) / 4;
> +}
> +
> +static void
> +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
> +{
> +    RISCVCLICState *clic = opaque;
> +    hwaddr clic_size = clic->clic_size;
> +    int hartid, mode, irq;
> +
> +    if (addr < clic_size) {
> +        if (addr < 0x1000) {
> +            assert(addr % 4 == 0);
> +            int index = addr / 4;
> +            switch (index) {
> +            case 0: /* cliccfg */
> +                {
> +                    uint8_t nlbits = extract32(value, 1, 4);
> +                    uint8_t nmbits = extract32(value, 5, 2);
> +
> +                    /*
> +                     * The 4-bit cliccfg.nlbits WARL field.
> +                     * Valid values are 0—8.
> +                     */
> +                    if (nlbits <= 8) {
> +                        clic->nlbits = nlbits;
> +                    }
> +                    /* Valid values are given by implemented priviledges
> */
> +                    if (clic->prv_s && clic->prv_u) {
> +                        if (nmbits <= 2) {
> +                            clic->nmbits = nmbits;
> +                        }
> +                    } else if (clic->prv_u) {
> +                        if (nmbits <= 1) {
> +                            clic->nmbits = nmbits;
> +                        }
> +                    } else {
> +                        assert(!clic->prv_s);
> +                        if (nmbits == 0) {
> +                            clic->nmbits = 0;
> +                        }
> +                    }
> +                    clic->nvbits = extract32(value, 0, 1);
> +                    break;
> +                }
> +            case 1: /* clicinfo, read-only register */
> +                qemu_log_mask(LOG_GUEST_ERROR,
> +                              "clic: write read-only clicinfo.\n");
> +                break;
> +            case 0x10 ... 0x2F: /* clicinttrig */
> +                {
> +                    uint32_t interrupt_number = value &
> MAKE_64BIT_MASK(0, 13);
> +                    if (interrupt_number <= clic->num_sources) {
> +                        value &= ~MAKE_64BIT_MASK(13, 18);
> +                        clic->clicinttrig[index - 0x10] = value;
> +                    }
> +                    break;
> +                }
> +            case 2: /* mintthresh */
> +                if (!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 -= 0x1000;
> +            hartid = riscv_clic_get_hartid(clic, addr);
> +            mode = riscv_clic_get_mode(clic, addr);
> +            irq = riscv_clic_get_irq(clic, addr);
> +
> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
> +                riscv_clic_hart_write(clic, addr, value, size, mode,
> +                                      hartid, irq);
> +            }
> +        }
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid write: 0x%" HWADDR_PRIx "\n", addr);
> +    }
> +}
> +
> +static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    RISCVCLICState *clic = opaque;
> +    hwaddr clic_size = clic->clic_size;
> +    int hartid, mode, irq;
> +
> +    if (addr < clic_size) {
> +        if (addr < 0x1000) {
> +            assert(addr % 4 == 0);
> +            int index = addr / 4;
> +            switch (index) {
> +            case 0: /* cliccfg */
> +                return clic->nvbits |
> +                       (clic->nlbits << 1) |
> +                       (clic->nmbits << 5);
> +            case 1: /* clicinfo */
> +                /*
> +                 * clicinfo register layout
> +                 *
> +                 * Bits Field
> +                 * 31 reserved (WARL 0)
> +                 * 30:25 num_trigger
> +                 * 24:21 CLICINTCTLBITS
> +                 * 20:13 version (for version control)
> +                 * 12:0 num_interrupt
> +                 */
> +                return clic->clicinfo & ~INT32_MAX;
> +            case 0x10 ... 0x2F: /* clicinttrig */
> +                /*
> +                 * clicinttrig register layout
> +                 *
> +                 * Bits Field
> +                 * 31 enable
> +                 * 30:13 reserved (WARL 0)
> +                 * 12:0 interrupt_number
> +                 */
> +                return clic->clicinttrig[index - 0x10] &
> +                       ~MAKE_64BIT_MASK(13, 18);
> +            case 2: /* mintthresh */
> +                if (!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 -= 0x1000;
> +            hartid = riscv_clic_get_hartid(clic, addr);
> +            mode = riscv_clic_get_mode(clic, addr);
> +            irq = riscv_clic_get_irq(clic, addr);
> +
> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
> +                return riscv_clic_hart_read(clic, addr, mode, hartid,
> irq);
> +            }
> +        }
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid read: 0x%" HWADDR_PRIx "\n", addr);
> +    }
> +    return 0;
> +}
> +
> +static void riscv_clic_set_irq(void *opaque, int id, int level)
> +{
> +    RISCVCLICState *clic = opaque;
> +    int irq, hartid, mode;
> +    hwaddr addr = 4 * id;
> +    TRIG_TYPE type;
> +
> +    hartid = riscv_clic_get_hartid(clic, addr);
> +    mode = riscv_clic_get_mode(clic, addr);
> +    irq = riscv_clic_get_irq(clic, addr);
> +    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, mode, hartid, irq, level);
> +            break;
> +        case NEG_LEVEL:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
> +            break;
> +        case NEG_EDGE:
> +            break;
> +        }
> +    } else {
> +        switch (type) {
> +        case POSITIVE_LEVEL:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, level);
> +            break;
> +        case POSITIVE_EDGE:
> +            break;
> +        case NEG_LEVEL:
> +        case NEG_EDGE:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
> +            break;
> +        }
> +    }
> +}
> +
> +static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level)
> +{
> +    CPURISCVState *env = (CPURISCVState *)opaque;
> +    RISCVCLICState *clic = env->clic;
> +    CPUState *cpu = env_cpu(env);
> +
> +    if (level) {
> +        env->exccode = clic->exccode[cpu->cpu_index];
> +        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 harts_x_sources = clic->num_harts * clic->num_sources;
> +    int irqs, i;
> +
> +    if (clic->prv_s && clic->prv_u) {
> +        irqs = 3 * harts_x_sources;
> +    } else if (clic->prv_s || clic->prv_u) {
> +        irqs = 2 * harts_x_sources;
> +    } else {
> +        irqs = harts_x_sources;
> +    }
> +
> +    clic->clic_size = irqs * 4 + 0x1000;
> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops, clic,
> +                          TYPE_RISCV_CLIC, clic->clic_size);
> +
> +    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);
> +    clic->active_count = g_new0(size_t, clic->num_harts);
> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
> +
> +    /* Allocate irq 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_irqs, clic->num_harts);
> +
> +    for (i = 0; i < clic->num_harts; i++) {
> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>

As spec says:
  Smaller single-core systems might have only a CLIC,
  while multicore systems might have a CLIC per-core and a single shared
PLIC.
  The PLIC xeip signals are treated as hart-local interrupt sources by the
CLIC at each core.

It looks like it's possible to have one CLIC instance per core.
However if you try to bind CPU reference start from index i = 0.
It's not possible for each per-core CLIC to bind their own CPU instance in
multicore system
as they have to bind from CPU 0.

I'm not sure if we add a new "hartid-base" property just like what SiFive
PLIC is
implemented would be a good idea or not.


Regards,
Frank Chang


> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
> +                                         &cpu->env, 1);
> +        qdev_connect_gpio_out(dev, i, irq);
> +        cpu->env.clic = clic;
> +    }
> +}
> +
> +static Property riscv_clic_properties[] = {
> +    DEFINE_PROP_BOOL("prv-s", RISCVCLICState, prv_s, false),
> +    DEFINE_PROP_BOOL("prv-u", RISCVCLICState, prv_u, false),
> +    DEFINE_PROP_UINT32("num-harts", RISCVCLICState, num_harts, 0),
> +    DEFINE_PROP_UINT32("num-sources", RISCVCLICState, num_sources, 0),
> +    DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICState, clicintctlbits,
> 0),
> +    DEFINE_PROP_UINT64("mclicbase", RISCVCLICState, mclicbase, 0),
> +    DEFINE_PROP_STRING("version", RISCVCLICState, version),
> +    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 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 void riscv_clic_register_types(void)
> +{
> +    type_register_static(&riscv_clic_info);
> +}
> +
> +type_init(riscv_clic_register_types)
> +
> +/*
> + * riscv_clic_create:
> + *
> + * @addr: base address of M-Mode CLIC memory-mapped registers
> + * @prv_s: have smode region
> + * @prv_u: have umode region
> + * @num_harts: number of CPU harts
> + * @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"
> + *
> + * Returns: the device object
> + */
> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
> +                               uint32_t num_harts, uint32_t num_sources,
> +                               uint8_t clicintctlbits,
> +                               const char *version)
> +{
> +    DeviceState *dev = qdev_new(TYPE_RISCV_CLIC);
> +
> +    assert(num_sources <= 4096);
> +    assert(num_harts <= 1024);
> +    assert(clicintctlbits <= 8);
> +    assert(!strcmp(version, "v0.8") || !strcmp(version, "v0.9"));
> +
> +    qdev_prop_set_bit(dev, "prv-s", prv_s);
> +    qdev_prop_set_bit(dev, "prv-u", prv_u);
> +    qdev_prop_set_uint32(dev, "num-harts", num_harts);
> +    qdev_prop_set_uint32(dev, "num-sources", num_sources);
> +    qdev_prop_set_uint32(dev, "clicintctlbits", clicintctlbits);
> +    qdev_prop_set_uint64(dev, "mclicbase", addr);
> +    qdev_prop_set_string(dev, "version", version);
> +
> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
> +    return dev;
> +}
> +
> +void riscv_clic_get_next_interrupt(void *opaque, int hartid)
> +{
> +    RISCVCLICState *clic = opaque;
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    return riscv_clic_is_shv_interrupt(clic, irq_offset);
> +}
> +
> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int
> irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
> +}
> +
> +void riscv_clic_clean_pending(void *opaque, int mode, int hartid, int irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    clic->clicintip[irq_offset] = 0;
> +}
> +
> +/*
> + * 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.
> + */
> +bool riscv_clic_is_clic_mode(CPURISCVState *env)
> +{
> +    target_ulong xtvec = (env->priv == PRV_M) ? env->mtvec : env->stvec;
> +    return env->clic && ((xtvec & 0x3) == 3);
> +}
> +
> +void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
> +                               int *il, int *irq)
> +{
> +    *irq = extract32(exccode, 0, 12);
> +    *mode = extract32(exccode, 12, 2);
> +    *il = extract32(exccode, 14, 8);
> +}
> diff --git a/include/hw/intc/riscv_clic.h b/include/hw/intc/riscv_clic.h
> new file mode 100644
> index 0000000000..e5f89672a6
> --- /dev/null
> +++ b/include/hw/intc/riscv_clic.h
> @@ -0,0 +1,103 @@
> +/*
> + * RISC-V CLIC(Core Local Interrupt Controller) interface.
> + *
> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
> + *
> + * 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/>.
> + */
> +
> +#ifndef RISCV_CLIC_H
> +#define RISCV_CLIC_H
> +
> +#include "hw/irq.h"
> +#include "hw/sysbus.h"
> +
> +#define TYPE_RISCV_CLIC "riscv_clic"
> +#define RISCV_CLIC(obj) \
> +    OBJECT_CHECK(RISCVCLICState, (obj), TYPE_RISCV_CLIC)
> +
> +/*
> + * 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;
> +
> +typedef struct RISCVCLICState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +
> +    /* Implementaion parameters */
> +    bool prv_s;
> +    bool prv_u;
> +    uint32_t num_harts;
> +    uint32_t num_sources;
> +    uint32_t clic_size;
> +    uint32_t clic_mmode_base;
> +    uint32_t clicintctlbits;
> +    uint64_t mclicbase;
> +    char *version;
> +
> +    /* Global configuration */
> +    uint8_t nmbits;
> +    uint8_t nlbits;
> +    uint8_t nvbits;
> +    uint32_t clicinfo;
> +    uint32_t clicinttrig[32];
> +
> +    /* Aperture configuration */
> +    uint8_t *clicintip;
> +    uint8_t *clicintie;
> +    uint8_t *clicintattr;
> +    uint8_t *clicintctl;
> +
> +    /* Complatible with v0.8 */
> +    uint32_t mintthresh;
> +    uint32_t sintthresh;
> +    uint32_t uintthresh;
> +
> +    /* QEMU implementaion related fields */
> +    uint32_t *exccode;
> +    CLICActiveInterrupt *active_list;
> +    size_t *active_count;
> +    MemoryRegion mmio;
> +    qemu_irq *cpu_irqs;
> +} RISCVCLICState;
> +
> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
> +                               uint32_t num_harts, 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 mode, int hartid, int
> irq);
> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int
> irq);
> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int
> irq);
> +void riscv_clic_get_next_interrupt(void *opaque, int hartid);
> +bool riscv_clic_is_clic_mode(CPURISCVState *env);
> +#endif
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index a5eab26a69..9e389d7bbf 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -33,6 +33,7 @@
>  #define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
>  #define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
>  #define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
> +#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0
>
>  #define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
>  #define TYPE_RISCV_CPU_BASE32           RISCV_CPU_TYPE_NAME("rv32")
> @@ -247,6 +248,7 @@ struct CPURISCVState {
>      /* Fields from here on are preserved across CPU reset. */
>      QEMUTimer *timer; /* Internal timer */
>      void *clic;       /* clic interrupt controller */
> +    uint32_t exccode; /* clic irq encode */
>  };
>
>  OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
> --
> 2.25.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 47688 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 08/11] target/riscv: Update CSR xnxti in CLIC mode
  2021-06-11  9:07           ` Frank Chang
  2021-06-11  9:26             ` LIU Zhiwei
@ 2021-06-15  7:45             ` Alistair Francis
  1 sibling, 0 replies; 81+ messages in thread
From: Alistair Francis @ 2021-06-15  7:45 UTC (permalink / raw)
  To: Frank Chang
  Cc: open list:RISC-V, qemu-devel@nongnu.org Developers, wxy194768,
	Alistair Francis, Palmer Dabbelt, LIU Zhiwei

On Fri, Jun 11, 2021 at 7:08 PM Frank Chang <0xc0de0125@gmail.com> wrote:
>
> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月11日 週五 下午4:56寫道:
>>
>>
>> On 6/11/21 4:42 PM, Frank Chang wrote:
>>
>> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月11日 週五 下午4:30寫道:
>>>
>>>
>>> On 6/11/21 4:15 PM, Frank Chang wrote:
>>>
>>> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:52寫道:
>>>>
>>>> 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>
>>>> ---
>>>>  target/riscv/cpu_bits.h |  16 ++++++
>>>>  target/riscv/csr.c      | 114 ++++++++++++++++++++++++++++++++++++++++
>>>>  2 files changed, 130 insertions(+)
>>>>
>>>> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
>>>> index 7922097776..494e41edc9 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      0x346 /* clic-spec-draft */
>>>>  #define CSR_MINTTHRESH      0x347 /* clic-spec-draft */
>>>>
>>>> @@ -187,6 +188,7 @@
>>>>  #define CSR_SCAUSE          0x142
>>>>  #define CSR_STVAL           0x143
>>>>  #define CSR_SIP             0x144
>>>> +#define CSR_SNXTI           0x145 /* clic-spec-draft */
>>>>  #define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
>>>>  #define CSR_SINTTHRESH      0x147 /* clic-spec-draft */
>>>>
>>>> @@ -596,10 +598,24 @@
>>>>  #define MINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
>>>>  #define MINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
>>>>
>>>> +/* mcause */
>>>> +#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] */
>>>> +
>>>>  /* sintstatus */
>>>>  #define SINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
>>>>  #define SINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
>>>>
>>>> +/* scause */
>>>> +#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] */
>>>> +
>>>>  /* 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 e12222b77f..72cba080bf 100644
>>>> --- a/target/riscv/csr.c
>>>> +++ b/target/riscv/csr.c
>>>> @@ -774,6 +774,80 @@ static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value,
>>>>      return 0;
>>>>  }
>>>>
>>>> +static bool get_xnxti_status(CPURISCVState *env)
>>>> +{
>>>> +    CPUState *cs = env_cpu(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_priv, cs->cpu_index,
>>>> +                                      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;
>>>> +    CPUState *cs = env_cpu(env);
>>>> +    if (write_mask) {
>>>> +        env->mstatus |= new_value & (write_mask & 0b11111);
>>>> +    }
>>>> +
>>>> +    qemu_mutex_lock_iothread();
>>>
>>>
>>> Hi Zhiwei,
>>>
>>> May I ask what's the purpose to request the BQL here with qemu_mutex_lock_iothread()?
>>> Is there any critical data we need to protect in rmw_mnxti()?
>>> As far I see, rmw_mnxti() won't call cpu_interrupt() which need BQL to be held before calling.
>>> Am I missing anything?
>>>
>>> In my opinion, if you read or write any  MMIO register, you need to hold the BQL. As you can quickly see,
>>> it calls riscv_clic_clean_pending. That's why it should hold the BQL.
>>>
>>> Zhiwei
>>
>>
>> Oh, I see.
>> The MMIO register reads and writes should also be protected by BQL.
>> Thanks for the explanation.
>>
>> I am glad to know that you are reviewing this patch set. As Sifive implements the initial v0.7 CLIC, I think you may need this patch set for your SOC.
>> If you like to, I am happy to see that you connect this patch set to your SOC, and resend it again. I can also provide the qtest of this patch set if you need.
>>
>> As you may see, the v6.1 soft freeze will come in July. I am afraid I can't upstream a new SOC in so short time.
>>
>> Zhiwei
>
> Thanks, I think we will leverage the hard works you have done into our implementation.
> However, I'm not sure I can catch up the deadline before v6.1's soft-freeze.
> But I think I can help to review the patches, at least we can speed up the review process.

I just wanted to point out that in terms of *submitting* RISC-V
patches for QEMU, don't worry about the soft-freeze dates.

Feel free to send the patches during the freeze and they can be
reviewed (but not merged, unless they fix a bug). I will even apply
them to a local tree in preparation for the next merge window, after
the release.

Alistair

>
> Regarding qtest, I saw your head commit mentioned the repo you are using: [1].
> Is it okay to just grab the qtest from this repo?
>
> [1]: https://github.com/romanheros/qemu/commit/bce1845ea9b079b4c360440292dc47725d1b24ab
>
> Thanks,
> Frank Chang
>
>>
>>
>> Regards,
>> Frank Chang
>>
>>>
>>>
>>> Regard,
>>> Frank Chang
>>>
>>>>
>>>> +    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_priv,
>>>> +                                                  cs->cpu_index, clic_irq);
>>>> +            if (edge) {
>>>> +                riscv_clic_clean_pending(env->clic, clic_priv,
>>>> +                                         cs->cpu_index, 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;
>>>> +        }
>>>> +    }
>>>> +    qemu_mutex_unlock_iothread();
>>>> +    return 0;
>>>> +}
>>>> +
>>>>  static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong *val)
>>>>  {
>>>>      *val = env->mintstatus;
>>>> @@ -982,6 +1056,44 @@ static int rmw_sip(CPURISCVState *env, int csrno, target_ulong *ret_value,
>>>>      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;
>>>> +    CPUState *cs = env_cpu(env);
>>>> +    if (write_mask) {
>>>> +        env->mstatus |= new_value & (write_mask & 0b11111);
>>>> +    }
>>>> +
>>>> +    qemu_mutex_lock_iothread();
>>>> +    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_priv,
>>>> +                                                  cs->cpu_index, clic_irq);
>>>> +            if (edge) {
>>>> +                riscv_clic_clean_pending(env->clic, clic_priv,
>>>> +                                         cs->cpu_index, clic_irq);
>>>> +            }
>>>> +            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;
>>>> +        }
>>>> +    }
>>>> +    qemu_mutex_unlock_iothread();
>>>> +    return 0;
>>>> +}
>>>> +
>>>>  static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong *val)
>>>>  {
>>>>      target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
>>>> @@ -1755,6 +1867,7 @@ 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 },
>>>> @@ -1766,6 +1879,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>>>>
>>>>      /* Supervisor Mode Core Level Interrupt Controller */
>>>>      [CSR_STVT] = { "stvt", clic,  read_stvt, write_stvt       },
>>>> +    [CSR_SNXTI] = { "snxti", clic,  NULL,  NULL,  rmw_snxti   },
>>>>
>>>>  #endif /* !CONFIG_USER_ONLY */
>>>>  };
>>>> --
>>>> 2.25.1
>>>>
>>>>


^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-06-13 10:10   ` Frank Chang
@ 2021-06-16  2:56     ` LIU Zhiwei
  2021-06-26 12:56       ` Frank Chang
  0 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-06-16  2:56 UTC (permalink / raw)
  To: Frank Chang
  Cc: Alistair Francis, Palmer Dabbelt, open list:RISC-V, wxy194768,
	qemu-devel@nongnu.org Developers

[-- Attachment #1: Type: text/plain, Size: 47023 bytes --]


On 6/13/21 6:10 PM, Frank Chang wrote:
> LIU Zhiwei <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> 於 
> 2021年4月9日 週五 下午3:57寫道:
>
>     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
>     <mailto:zhiwei_liu@c-sky.com>>
>     ---
>      default-configs/devices/riscv32-softmmu.mak |   1 +
>      default-configs/devices/riscv64-softmmu.mak |   1 +
>      hw/intc/Kconfig                             |   3 +
>      hw/intc/meson.build                         |   1 +
>      hw/intc/riscv_clic.c                        | 835
>     ++++++++++++++++++++
>      include/hw/intc/riscv_clic.h                | 103 +++
>      target/riscv/cpu.h                          |   2 +
>      7 files changed, 946 insertions(+)
>      create mode 100644 hw/intc/riscv_clic.c
>      create mode 100644 include/hw/intc/riscv_clic.h
>
>     diff --git a/default-configs/devices/riscv32-softmmu.mak
>     b/default-configs/devices/riscv32-softmmu.mak
>     index d847bd5692..1430c30588 100644
>     --- a/default-configs/devices/riscv32-softmmu.mak
>     +++ b/default-configs/devices/riscv32-softmmu.mak
>     @@ -5,6 +5,7 @@
>      #CONFIG_PCI_DEVICES=n
>      CONFIG_SEMIHOSTING=y
>      CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>     +CONFIG_RISCV_CLIC=y
>
>      # Boards:
>      #
>     diff --git a/default-configs/devices/riscv64-softmmu.mak
>     b/default-configs/devices/riscv64-softmmu.mak
>     index d5eec75f05..396800bbbd 100644
>     --- a/default-configs/devices/riscv64-softmmu.mak
>     +++ b/default-configs/devices/riscv64-softmmu.mak
>     @@ -5,6 +5,7 @@
>      #CONFIG_PCI_DEVICES=n
>      CONFIG_SEMIHOSTING=y
>      CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>     +CONFIG_RISCV_CLIC=y
>
>      # Boards:
>      #
>     diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
>     index f4694088a4..5bf492b48f 100644
>     --- a/hw/intc/Kconfig
>     +++ b/hw/intc/Kconfig
>     @@ -68,6 +68,9 @@ config SIFIVE_CLINT
>      config SIFIVE_PLIC
>          bool
>
>     +config RISCV_CLIC
>     +    bool
>     +
>      config GOLDFISH_PIC
>          bool
>
>     diff --git a/hw/intc/meson.build b/hw/intc/meson.build
>     index 1c299039f6..2aa71b6738 100644
>     --- a/hw/intc/meson.build
>     +++ b/hw/intc/meson.build
>     @@ -50,6 +50,7 @@ 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_SIFIVE_CLINT', if_true:
>     files('sifive_clint.c'))
>      specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true:
>     files('sifive_plic.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'))
>     diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
>     new file mode 100644
>     index 0000000000..8ad534c506
>     --- /dev/null
>     +++ b/hw/intc/riscv_clic.c
>     @@ -0,0 +1,835 @@
>     +/*
>     + * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
>     + *
>     + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights
>     reserved.
>     + *
>     + * 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/
>     <http://www.gnu.org/licenses/>>.
>     + */
>     +
>     +#include "qemu/osdep.h"
>     +#include "qapi/error.h"
>     +#include "qemu/log.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"
>     +
>     +/*
>     + * 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_offset)
>     +{
>     +    return (clic->clicintattr[irq_offset] >> 1) & 0x3;
>     +}
>     +
>     +static inline bool
>     +riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
>     +{
>     +    return (clic->clicintattr[irq_offset] >> 1) & 0x1;
>     +}
>     +
>     +static inline bool
>     +riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
>     +{
>     +    return (clic->clicintattr[irq_offset] & 0x1) && clic->nvbits;
>     +}
>     +
>     +static uint8_t
>     +riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
>     +{
>     +    int nlbits = clic->nlbits;
>     +
>     +    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->nlbits;
>     +    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);
>     +}
>     +
>     +/*
>     + * In a system with multiple harts, the M-mode CLIC regions for
>     all the harts
>     + * are placed contiguously in the memory space, followed by the
>     S-mode CLIC
>     + * regions for all harts. (Section 3.11)
>     + */
>     +static size_t
>     +riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int
>     hartid, int irq)
>     +{
>     +    size_t mode_offset = 0;
>     +    size_t unit = clic->num_harts * clic->num_sources;
>     +
>     +    switch (mode) {
>     +    case PRV_M:
>     +        mode_offset = 0;
>     +        break;
>     +    case PRV_S:
>     +        mode_offset = unit;
>     +        break;
>     +    case PRV_U:
>     +        mode_offset = clic->prv_s ? 2 * unit : unit;
>     +        break;
>     +    default:
>     +        qemu_log_mask(LOG_GUEST_ERROR,
>     +                      "clic: invalid mode %d\n", mode);
>     +        exit(1);
>     +    }
>     +    return mode_offset + hartid * clic->num_sources + irq;
>     +}
>     +
>     +static void riscv_clic_next_interrupt(void *opaque, int hartid)
>     +{
>     +    /*
>     +     * 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
>     +     */
>     +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(hartid));
>     +    CPURISCVState *env = &cpu->env;
>     +    RISCVCLICState *clic = (RISCVCLICState *)opaque;
>     +
>     +    int il[4] = {
>     +        MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
>     +            clic->mintthresh), /* PRV_U */
>     +        MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
>     +            clic->sintthresh), /* PRV_S */
>     +        0,                     /* reserverd */
>     +        MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
>     +            clic->uintthresh)  /* PRV_M */
>     +    };
>     +
>     +    /* Get sorted list of enabled interrupts for this hart */
>     +    size_t hart_offset = hartid * clic->num_sources;
>     +    CLICActiveInterrupt *active = &clic->active_list[hart_offset];
>     +    size_t active_count = clic->active_count[hartid];
>     +    uint8_t mode, level, priority;
>     +
>     +    /* Loop through the enabled interrupts sorted by
>     mode+priority+level */
>     +    while (active_count) {
>     +        size_t irq_offset;
>     +        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;
>     +        }
>     +        irq_offset = riscv_clic_get_irq_offset(clic, mode,
>     hartid, active->irq);
>     +        /* Check pending interrupt with high enough
>     mode+priority+level */
>     +        if (clic->clicintip[irq_offset]) {
>     +            /* Clean vector edge-triggered pending */
>     +            if (riscv_clic_is_edge_triggered(clic, irq_offset) &&
>     +                riscv_clic_is_shv_interrupt(clic, irq_offset)) {
>     +                clic->clicintip[irq_offset] = 0;
>     +            }
>     +            /* Post pending interrupt for this hart */
>     +            clic->exccode[hartid] = active->irq | mode << 12 |
>     level << 14;
>     +            qemu_set_irq(clic->cpu_irqs[hartid], 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
>     hartid, int irq)
>     +{
>     +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode,
>     hartid, irq);
>     +    if (!clic->prv_s && !clic->prv_u) { /* M */
>     +        return mode == PRV_M;
>     +    } else if (!clic->prv_s) { /* M/U */
>     +        switch (clic->nmbits) {
>     +        case 0:
>     +            return mode == PRV_M;
>     +        case 1:
>     +            return clic->clicintattr[irq_offset] & 0x80 ? (mode
>     == PRV_M) :
>     + (mode == PRV_U);
>     +        default:
>     +            qemu_log_mask(LOG_GUEST_ERROR,
>     +                "clic: nmbits can only be 0 or 1 for M/U hart");
>     +            exit(1);
>     +        }
>     +    } else { /* M/S/U */
>     +        switch (clic->nmbits) {
>     +        case 0:
>     +            return mode == PRV_M;
>     +        case 1:
>     +            return clic->clicintattr[irq_offset] & 0x80 ? (mode
>     == PRV_M) :
>     + (mode == PRV_S);
>     +        case 2:
>     +            return mode == clic->clicintattr[irq_offset];
>     +        case 3:
>     +            qemu_log_mask(LOG_GUEST_ERROR,
>     +                "clic: nmbits can only be 0 or 1 or 2 for M/S/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 mode, int
>     hartid, int irq)
>     +{
>     +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode,
>     hartid, irq);
>     +    return riscv_clic_is_edge_triggered(clic, irq_offset);
>     +}
>     +
>     +static void
>     +riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
>     +                        int irq, uint64_t value)
>     +{
>     +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode,
>     hartid, irq);
>     +    clic->clicintip[irq_offset] = !!value;
>     +    riscv_clic_next_interrupt(clic, hartid);
>     +}
>     +
>     +/*
>     + * 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,
>     uint64_t value)
>     +{
>     +    int mode = extract64(value, 6, 2);
>     +
>     +    if (!qtest_enabled()) {
>     +        CPURISCVState *env = current_cpu->env_ptr;
>     +        if (env->priv < mode) {
>     +            return false;
>     +        }
>     +    }
>     +    return true;
>     +}
>     +
>     +static inline int riscv_clic_encode_priority(const
>     CLICActiveInterrupt *i)
>     +{
>     +    return ((i->intcfg & 0x3ff) << 12) | /* Highest
>     mode+level+priority */
>     +           (i->irq & 0xfff);             /* Highest irq number */
>     +}
>     +
>     +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 hartid,
>     +                        int irq, uint64_t new_intie)
>     +{
>     +    size_t hart_offset = hartid * clic->num_sources;
>     +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode,
>     hartid, irq);
>     +    CLICActiveInterrupt *active_list =
>     &clic->active_list[hart_offset];
>     +    size_t *active_count = &clic->active_count[hartid];
>     +
>     +    uint8_t old_intie = clic->clicintie[irq_offset];
>     +    clic->clicintie[irq_offset] = !!new_intie;
>     +
>     +    /* Add to or remove from list of active interrupts */
>     +    if (new_intie && !old_intie) {
>     +        active_list[*active_count].intcfg = (mode << 8) |
>     + clic->clicintctl[irq_offset];
>     +        active_list[*active_count].irq = irq;
>     +        (*active_count)++;
>     +    } else if (!new_intie && old_intie) {
>     +        CLICActiveInterrupt key = {
>     +            (mode << 8) | clic->clicintctl[irq_offset], irq
>     +        };
>     +        CLICActiveInterrupt *result = bsearch(&key,
>     +                                              active_list,
>     *active_count,
>     + sizeof(CLICActiveInterrupt),
>     + riscv_clic_active_compare);
>     +        size_t elem = (result - active_list) /
>     sizeof(CLICActiveInterrupt);
>     +        size_t sz = (--(*active_count) - elem) *
>     sizeof(CLICActiveInterrupt);
>     +        assert(result);
>     +        memmove(&result[0], &result[1], sz);
>     +    }
>     +
>     +    /* Sort list of active interrupts */
>     +    qsort(active_list, *active_count,
>     +          sizeof(CLICActiveInterrupt),
>     +          riscv_clic_active_compare);
>     +
>     +    riscv_clic_next_interrupt(clic, hartid);
>     +}
>     +
>     +static void
>     +riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
>     +                      uint64_t value, unsigned size,
>     +                      int mode, int hartid, int irq)
>     +{
>     +    int req = extract32(addr, 0, 2);
>     +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode,
>     hartid, irq);
>     +
>     +    if (hartid >= clic->num_harts) {
>     +        qemu_log_mask(LOG_GUEST_ERROR,
>     +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx
>     "\n",
>     +                      hartid, addr);
>     +        return;
>     +    }
>     +
>     +    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, mode, hartid, irq)) {
>     +            /*
>     +             * The actual pending bit is located at bit 0 (i.e., the
>     +             * leastsignificant 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_offset]) {
>     +                riscv_clic_update_intip(clic, mode, hartid, irq,
>     value);
>     +            }
>     +        }
>     +        break;
>     +    case 1: /* clicintie[i] */
>     +        if (clic->clicintie[irq_offset] != value) {
>     +            riscv_clic_update_intie(clic, mode, hartid, irq, value);
>     +        }
>     +        break;
>     +    case 2: /* clicintattr[i] */
>     +        if (riscv_clic_validate_intattr(clic, value)) {
>     +            if (clic->clicintattr[irq_offset] != value) {
>     +                /* When nmbits=2, check WARL */
>     +                bool invalid = (clic->nmbits == 2) &&
>     +                               (extract64(value, 6, 2) == 0b10);
>     +                if (invalid) {
>     +                    uint8_t old_mode =
>     extract32(clic->clicintattr[irq_offset],
>     +                                                 6, 2);
>     +                    value = deposit32(value, 6, 2, old_mode);
>     +                }
>     +                clic->clicintattr[irq_offset] = value;
>     +                riscv_clic_next_interrupt(clic, hartid);
>     +            }
>     +        }
>     +        break;
>     +    case 3: /* clicintctl[i] */
>     +        if (value != clic->clicintctl[irq_offset]) {
>     +            clic->clicintctl[irq_offset] = value;
>     +            riscv_clic_next_interrupt(clic, hartid);
>     +        }
>     +        break;
>     +    }
>     +}
>     +
>     +static uint64_t
>     +riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
>     +                     int hartid, int irq)
>     +{
>     +    int req = extract32(addr, 0, 2);
>     +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode,
>     hartid, irq);
>     +
>     +    if (hartid >= clic->num_harts) {
>     +        qemu_log_mask(LOG_GUEST_ERROR,
>     +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx
>     "\n",
>     +                      hartid, addr);
>     +        return 0;
>     +    }
>     +
>     +    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] */
>     +        return clic->clicintip[irq_offset];
>     +    case 1: /* clicintie[i] */
>     +        return clic->clicintie[irq_offset];
>     +    case 2: /* clicintattr[i] */
>     +        /*
>     +         * clicintattr register layout
>     +         * Bits Field
>     +         * 7:6 mode
>     +         * 5:3 reserved (WPRI 0)
>     +         * 2:1 trig
>     +         * 0 shv
>     +         */
>     +        return clic->clicintattr[irq_offset] & ~0x38;
>     +    case 3: /* clicintctrl */
>     +        /*
>     +         * 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_offset] |
>     +               ((1 << (8 - clic->clicintctlbits)) - 1);
>     +    }
>     +
>     +    return 0;
>     +}
>     +
>     +/* Return target interrupt mode */
>     +static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
>     +{
>     +    int mode = addr / (4 * clic->num_harts * clic->num_sources);
>     +    switch (mode) {
>     +    case 0:
>     +        return PRV_M;
>     +    case 1:
>     +        assert(clic->prv_s || clic->prv_u);
>     +        return clic->prv_s ? PRV_S : PRV_U;
>     +    case 2:
>     +        assert(clic->prv_s && clic->prv_u);
>     +        return PRV_U;
>     +    default:
>     +        g_assert_not_reached();
>     +        break;
>     +    }
>     +}
>     +
>     +/* Return target hart id */
>     +static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
>     +{
>     +    int mode_unit = 4 * clic->num_harts * clic->num_sources;
>     +    int hart_unit = 4 * clic->num_sources;
>     +
>     +    return (addr % mode_unit) / hart_unit;
>     +}
>     +
>     +/* Return target interrupt number */
>     +static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
>     +{
>     +    int hart_unit = 4 * clic->num_sources;
>     +    return (addr % hart_unit) / 4;
>     +}
>     +
>     +static void
>     +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value,
>     unsigned size)
>     +{
>     +    RISCVCLICState *clic = opaque;
>     +    hwaddr clic_size = clic->clic_size;
>     +    int hartid, mode, irq;
>     +
>     +    if (addr < clic_size) {
>     +        if (addr < 0x1000) {
>     +            assert(addr % 4 == 0);
>     +            int index = addr / 4;
>     +            switch (index) {
>     +            case 0: /* cliccfg */
>     +                {
>     +                    uint8_t nlbits = extract32(value, 1, 4);
>     +                    uint8_t nmbits = extract32(value, 5, 2);
>     +
>     +                    /*
>     +                     * The 4-bit cliccfg.nlbits WARL field.
>     +                     * Valid values are 0—8.
>     +                     */
>     +                    if (nlbits <= 8) {
>     +                        clic->nlbits = nlbits;
>     +                    }
>     +                    /* Valid values are given by implemented
>     priviledges */
>     +                    if (clic->prv_s && clic->prv_u) {
>     +                        if (nmbits <= 2) {
>     +                            clic->nmbits = nmbits;
>     +                        }
>     +                    } else if (clic->prv_u) {
>     +                        if (nmbits <= 1) {
>     +                            clic->nmbits = nmbits;
>     +                        }
>     +                    } else {
>     +                        assert(!clic->prv_s);
>     +                        if (nmbits == 0) {
>     +                            clic->nmbits = 0;
>     +                        }
>     +                    }
>     +                    clic->nvbits = extract32(value, 0, 1);
>     +                    break;
>     +                }
>     +            case 1: /* clicinfo, read-only register */
>     +                qemu_log_mask(LOG_GUEST_ERROR,
>     +                              "clic: write read-only clicinfo.\n");
>     +                break;
>     +            case 0x10 ... 0x2F: /* clicinttrig */
>     +                {
>     +                    uint32_t interrupt_number = value &
>     MAKE_64BIT_MASK(0, 13);
>     +                    if (interrupt_number <= clic->num_sources) {
>     +                        value &= ~MAKE_64BIT_MASK(13, 18);
>     +                        clic->clicinttrig[index - 0x10] = value;
>     +                    }
>     +                    break;
>     +                }
>     +            case 2: /* mintthresh */
>     +                if (!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 -= 0x1000;
>     +            hartid = riscv_clic_get_hartid(clic, addr);
>     +            mode = riscv_clic_get_mode(clic, addr);
>     +            irq = riscv_clic_get_irq(clic, addr);
>     +
>     +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
>     +                riscv_clic_hart_write(clic, addr, value, size, mode,
>     +                                      hartid, irq);
>     +            }
>     +        }
>     +    } else {
>     +        qemu_log_mask(LOG_GUEST_ERROR,
>     +                      "clic: invalid write: 0x%" HWADDR_PRIx
>     "\n", addr);
>     +    }
>     +}
>     +
>     +static uint64_t riscv_clic_read(void *opaque, hwaddr addr,
>     unsigned size)
>     +{
>     +    RISCVCLICState *clic = opaque;
>     +    hwaddr clic_size = clic->clic_size;
>     +    int hartid, mode, irq;
>     +
>     +    if (addr < clic_size) {
>     +        if (addr < 0x1000) {
>     +            assert(addr % 4 == 0);
>     +            int index = addr / 4;
>     +            switch (index) {
>     +            case 0: /* cliccfg */
>     +                return clic->nvbits |
>     +                       (clic->nlbits << 1) |
>     +                       (clic->nmbits << 5);
>     +            case 1: /* clicinfo */
>     +                /*
>     +                 * clicinfo register layout
>     +                 *
>     +                 * Bits Field
>     +                 * 31 reserved (WARL 0)
>     +                 * 30:25 num_trigger
>     +                 * 24:21 CLICINTCTLBITS
>     +                 * 20:13 version (for version control)
>     +                 * 12:0 num_interrupt
>     +                 */
>     +                return clic->clicinfo & ~INT32_MAX;
>     +            case 0x10 ... 0x2F: /* clicinttrig */
>     +                /*
>     +                 * clicinttrig register layout
>     +                 *
>     +                 * Bits Field
>     +                 * 31 enable
>     +                 * 30:13 reserved (WARL 0)
>     +                 * 12:0 interrupt_number
>     +                 */
>     +                return clic->clicinttrig[index - 0x10] &
>     +                       ~MAKE_64BIT_MASK(13, 18);
>     +            case 2: /* mintthresh */
>     +                if (!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 -= 0x1000;
>     +            hartid = riscv_clic_get_hartid(clic, addr);
>     +            mode = riscv_clic_get_mode(clic, addr);
>     +            irq = riscv_clic_get_irq(clic, addr);
>     +
>     +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
>     +                return riscv_clic_hart_read(clic, addr, mode,
>     hartid, irq);
>     +            }
>     +        }
>     +    } else {
>     +        qemu_log_mask(LOG_GUEST_ERROR,
>     +                      "clic: invalid read: 0x%" HWADDR_PRIx "\n",
>     addr);
>     +    }
>     +    return 0;
>     +}
>     +
>     +static void riscv_clic_set_irq(void *opaque, int id, int level)
>     +{
>     +    RISCVCLICState *clic = opaque;
>     +    int irq, hartid, mode;
>     +    hwaddr addr = 4 * id;
>     +    TRIG_TYPE type;
>     +
>     +    hartid = riscv_clic_get_hartid(clic, addr);
>     +    mode = riscv_clic_get_mode(clic, addr);
>     +    irq = riscv_clic_get_irq(clic, addr);
>     +    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, mode, hartid, irq, level);
>     +            break;
>     +        case NEG_LEVEL:
>     +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
>     +            break;
>     +        case NEG_EDGE:
>     +            break;
>     +        }
>     +    } else {
>     +        switch (type) {
>     +        case POSITIVE_LEVEL:
>     +            riscv_clic_update_intip(clic, mode, hartid, irq, level);
>     +            break;
>     +        case POSITIVE_EDGE:
>     +            break;
>     +        case NEG_LEVEL:
>     +        case NEG_EDGE:
>     +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
>     +            break;
>     +        }
>     +    }
>     +}
>     +
>     +static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int
>     level)
>     +{
>     +    CPURISCVState *env = (CPURISCVState *)opaque;
>     +    RISCVCLICState *clic = env->clic;
>     +    CPUState *cpu = env_cpu(env);
>     +
>     +    if (level) {
>     +        env->exccode = clic->exccode[cpu->cpu_index];
>     +        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 harts_x_sources = clic->num_harts * clic->num_sources;
>     +    int irqs, i;
>     +
>     +    if (clic->prv_s && clic->prv_u) {
>     +        irqs = 3 * harts_x_sources;
>     +    } else if (clic->prv_s || clic->prv_u) {
>     +        irqs = 2 * harts_x_sources;
>     +    } else {
>     +        irqs = harts_x_sources;
>     +    }
>     +
>     +    clic->clic_size = irqs * 4 + 0x1000;
>     +    memory_region_init_io(&clic->mmio, OBJECT(dev),
>     &riscv_clic_ops, clic,
>     +                          TYPE_RISCV_CLIC, clic->clic_size);
>     +
>     +    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);
>     +    clic->active_count = g_new0(size_t, clic->num_harts);
>     +    clic->exccode = g_new0(uint32_t, clic->num_harts);
>     +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
>     +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
>     +
>     +    /* Allocate irq 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_irqs, clic->num_harts);
>     +
>     +    for (i = 0; i < clic->num_harts; i++) {
>     +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>
>
> As spec says:
>   Smaller single-core systems might have only a CLIC,
>   while multicore systems might have a CLIC per-core and a single 
> shared PLIC.
>   The PLIC xeip signals are treated as hart-local interrupt sources by 
> the CLIC at each core.
>
> It looks like it's possible to have one CLIC instance per core.

If you want to delivery an interrupt to one hart, you should encode the 
IRQ by the interrupt number
, the hart number and the interrupt target privilege, then set the irq.

I think how to calculate the hart number is the task of PLIC and it can 
make use of "hartid-base"
to calculate it.

Thanks,
Zhiwei

> However if you try to bind CPU reference start from index i = 0.
> It's not possible for each per-core CLIC to bind their own CPU 
> instance in multicore system
> as they have to bind from CPU 0.
>
> I'm not sure if we add a new "hartid-base" property just like what 
> SiFive PLIC is
> implemented would be a good idea or not.
>
>
> Regards,
> Frank Chang
>
>     +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>     +                                         &cpu->env, 1);
>     +        qdev_connect_gpio_out(dev, i, irq);
>     +        cpu->env.clic = clic;
>     +    }
>     +}
>     +
>     +static Property riscv_clic_properties[] = {
>     +    DEFINE_PROP_BOOL("prv-s", RISCVCLICState, prv_s, false),
>     +    DEFINE_PROP_BOOL("prv-u", RISCVCLICState, prv_u, false),
>     +    DEFINE_PROP_UINT32("num-harts", RISCVCLICState, num_harts, 0),
>     +    DEFINE_PROP_UINT32("num-sources", RISCVCLICState,
>     num_sources, 0),
>     +    DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICState,
>     clicintctlbits, 0),
>     +    DEFINE_PROP_UINT64("mclicbase", RISCVCLICState, mclicbase, 0),
>     +    DEFINE_PROP_STRING("version", RISCVCLICState, version),
>     +    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 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 void riscv_clic_register_types(void)
>     +{
>     +    type_register_static(&riscv_clic_info);
>     +}
>     +
>     +type_init(riscv_clic_register_types)
>     +
>     +/*
>     + * riscv_clic_create:
>     + *
>     + * @addr: base address of M-Mode CLIC memory-mapped registers
>     + * @prv_s: have smode region
>     + * @prv_u: have umode region
>     + * @num_harts: number of CPU harts
>     + * @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"
>     + *
>     + * Returns: the device object
>     + */
>     +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
>     +                               uint32_t num_harts, uint32_t
>     num_sources,
>     +                               uint8_t clicintctlbits,
>     +                               const char *version)
>     +{
>     +    DeviceState *dev = qdev_new(TYPE_RISCV_CLIC);
>     +
>     +    assert(num_sources <= 4096);
>     +    assert(num_harts <= 1024);
>     +    assert(clicintctlbits <= 8);
>     +    assert(!strcmp(version, "v0.8") || !strcmp(version, "v0.9"));
>     +
>     +    qdev_prop_set_bit(dev, "prv-s", prv_s);
>     +    qdev_prop_set_bit(dev, "prv-u", prv_u);
>     +    qdev_prop_set_uint32(dev, "num-harts", num_harts);
>     +    qdev_prop_set_uint32(dev, "num-sources", num_sources);
>     +    qdev_prop_set_uint32(dev, "clicintctlbits", clicintctlbits);
>     +    qdev_prop_set_uint64(dev, "mclicbase", addr);
>     +    qdev_prop_set_string(dev, "version", version);
>     +
>     +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
>     +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
>     +    return dev;
>     +}
>     +
>     +void riscv_clic_get_next_interrupt(void *opaque, int hartid)
>     +{
>     +    RISCVCLICState *clic = opaque;
>     +    riscv_clic_next_interrupt(clic, hartid);
>     +}
>     +
>     +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid,
>     int irq)
>     +{
>     +    RISCVCLICState *clic = opaque;
>     +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode,
>     hartid, irq);
>     +    return riscv_clic_is_shv_interrupt(clic, irq_offset);
>     +}
>     +
>     +bool riscv_clic_edge_triggered(void *opaque, int mode, int
>     hartid, int irq)
>     +{
>     +    RISCVCLICState *clic = opaque;
>     +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode,
>     hartid, irq);
>     +    return riscv_clic_is_edge_triggered(clic, irq_offset);
>     +}
>     +
>     +void riscv_clic_clean_pending(void *opaque, int mode, int hartid,
>     int irq)
>     +{
>     +    RISCVCLICState *clic = opaque;
>     +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode,
>     hartid, irq);
>     +    clic->clicintip[irq_offset] = 0;
>     +}
>     +
>     +/*
>     + * 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.
>     + */
>     +bool riscv_clic_is_clic_mode(CPURISCVState *env)
>     +{
>     +    target_ulong xtvec = (env->priv == PRV_M) ? env->mtvec :
>     env->stvec;
>     +    return env->clic && ((xtvec & 0x3) == 3);
>     +}
>     +
>     +void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
>     +                               int *il, int *irq)
>     +{
>     +    *irq = extract32(exccode, 0, 12);
>     +    *mode = extract32(exccode, 12, 2);
>     +    *il = extract32(exccode, 14, 8);
>     +}
>     diff --git a/include/hw/intc/riscv_clic.h
>     b/include/hw/intc/riscv_clic.h
>     new file mode 100644
>     index 0000000000..e5f89672a6
>     --- /dev/null
>     +++ b/include/hw/intc/riscv_clic.h
>     @@ -0,0 +1,103 @@
>     +/*
>     + * RISC-V CLIC(Core Local Interrupt Controller) interface.
>     + *
>     + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights
>     reserved.
>     + *
>     + * 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/
>     <http://www.gnu.org/licenses/>>.
>     + */
>     +
>     +#ifndef RISCV_CLIC_H
>     +#define RISCV_CLIC_H
>     +
>     +#include "hw/irq.h"
>     +#include "hw/sysbus.h"
>     +
>     +#define TYPE_RISCV_CLIC "riscv_clic"
>     +#define RISCV_CLIC(obj) \
>     +    OBJECT_CHECK(RISCVCLICState, (obj), TYPE_RISCV_CLIC)
>     +
>     +/*
>     + * 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;
>     +
>     +typedef struct RISCVCLICState {
>     +    /*< private >*/
>     +    SysBusDevice parent_obj;
>     +
>     +    /*< public >*/
>     +
>     +    /* Implementaion parameters */
>     +    bool prv_s;
>     +    bool prv_u;
>     +    uint32_t num_harts;
>     +    uint32_t num_sources;
>     +    uint32_t clic_size;
>     +    uint32_t clic_mmode_base;
>     +    uint32_t clicintctlbits;
>     +    uint64_t mclicbase;
>     +    char *version;
>     +
>     +    /* Global configuration */
>     +    uint8_t nmbits;
>     +    uint8_t nlbits;
>     +    uint8_t nvbits;
>     +    uint32_t clicinfo;
>     +    uint32_t clicinttrig[32];
>     +
>     +    /* Aperture configuration */
>     +    uint8_t *clicintip;
>     +    uint8_t *clicintie;
>     +    uint8_t *clicintattr;
>     +    uint8_t *clicintctl;
>     +
>     +    /* Complatible with v0.8 */
>     +    uint32_t mintthresh;
>     +    uint32_t sintthresh;
>     +    uint32_t uintthresh;
>     +
>     +    /* QEMU implementaion related fields */
>     +    uint32_t *exccode;
>     +    CLICActiveInterrupt *active_list;
>     +    size_t *active_count;
>     +    MemoryRegion mmio;
>     +    qemu_irq *cpu_irqs;
>     +} RISCVCLICState;
>     +
>     +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
>     +                               uint32_t num_harts, 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 mode, int hartid,
>     int irq);
>     +bool riscv_clic_edge_triggered(void *opaque, int mode, int
>     hartid, int irq);
>     +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid,
>     int irq);
>     +void riscv_clic_get_next_interrupt(void *opaque, int hartid);
>     +bool riscv_clic_is_clic_mode(CPURISCVState *env);
>     +#endif
>     diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>     index a5eab26a69..9e389d7bbf 100644
>     --- a/target/riscv/cpu.h
>     +++ b/target/riscv/cpu.h
>     @@ -33,6 +33,7 @@
>      #define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
>      #define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
>      #define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
>     +#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0
>
>      #define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any")
>      #define TYPE_RISCV_CPU_BASE32  RISCV_CPU_TYPE_NAME("rv32")
>     @@ -247,6 +248,7 @@ struct CPURISCVState {
>          /* Fields from here on are preserved across CPU reset. */
>          QEMUTimer *timer; /* Internal timer */
>          void *clic;       /* clic interrupt controller */
>     +    uint32_t exccode; /* clic irq encode */
>      };
>
>      OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
>     -- 
>     2.25.1
>
>

[-- Attachment #2: Type: text/html, Size: 62776 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-06-16  2:56     ` LIU Zhiwei
@ 2021-06-26 12:56       ` Frank Chang
  2021-06-28  7:15         ` LIU Zhiwei
  0 siblings, 1 reply; 81+ messages in thread
From: Frank Chang @ 2021-06-26 12:56 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Alistair Francis, Palmer Dabbelt, open list:RISC-V, wxy194768,
	qemu-devel@nongnu.org Developers

[-- Attachment #1: Type: text/plain, Size: 42563 bytes --]

On Wed, Jun 16, 2021 at 10:56 AM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:

>
> On 6/13/21 6:10 PM, Frank Chang wrote:
>
> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:57寫道:
>
>> 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>
>> ---
>>  default-configs/devices/riscv32-softmmu.mak |   1 +
>>  default-configs/devices/riscv64-softmmu.mak |   1 +
>>  hw/intc/Kconfig                             |   3 +
>>  hw/intc/meson.build                         |   1 +
>>  hw/intc/riscv_clic.c                        | 835 ++++++++++++++++++++
>>  include/hw/intc/riscv_clic.h                | 103 +++
>>  target/riscv/cpu.h                          |   2 +
>>  7 files changed, 946 insertions(+)
>>  create mode 100644 hw/intc/riscv_clic.c
>>  create mode 100644 include/hw/intc/riscv_clic.h
>>
>> diff --git a/default-configs/devices/riscv32-softmmu.mak
>> b/default-configs/devices/riscv32-softmmu.mak
>> index d847bd5692..1430c30588 100644
>> --- a/default-configs/devices/riscv32-softmmu.mak
>> +++ b/default-configs/devices/riscv32-softmmu.mak
>> @@ -5,6 +5,7 @@
>>  #CONFIG_PCI_DEVICES=n
>>  CONFIG_SEMIHOSTING=y
>>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>> +CONFIG_RISCV_CLIC=y
>>
>>  # Boards:
>>  #
>> diff --git a/default-configs/devices/riscv64-softmmu.mak
>> b/default-configs/devices/riscv64-softmmu.mak
>> index d5eec75f05..396800bbbd 100644
>> --- a/default-configs/devices/riscv64-softmmu.mak
>> +++ b/default-configs/devices/riscv64-softmmu.mak
>> @@ -5,6 +5,7 @@
>>  #CONFIG_PCI_DEVICES=n
>>  CONFIG_SEMIHOSTING=y
>>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>> +CONFIG_RISCV_CLIC=y
>>
>>  # Boards:
>>  #
>> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
>> index f4694088a4..5bf492b48f 100644
>> --- a/hw/intc/Kconfig
>> +++ b/hw/intc/Kconfig
>> @@ -68,6 +68,9 @@ config SIFIVE_CLINT
>>  config SIFIVE_PLIC
>>      bool
>>
>> +config RISCV_CLIC
>> +    bool
>> +
>>  config GOLDFISH_PIC
>>      bool
>>
>> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
>> index 1c299039f6..2aa71b6738 100644
>> --- a/hw/intc/meson.build
>> +++ b/hw/intc/meson.build
>> @@ -50,6 +50,7 @@ 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_SIFIVE_CLINT', if_true:
>> files('sifive_clint.c'))
>>  specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true:
>> files('sifive_plic.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'))
>> diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
>> new file mode 100644
>> index 0000000000..8ad534c506
>> --- /dev/null
>> +++ b/hw/intc/riscv_clic.c
>> @@ -0,0 +1,835 @@
>> +/*
>> + * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
>> + *
>> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
>> + *
>> + * 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/>.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qapi/error.h"
>> +#include "qemu/log.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"
>> +
>> +/*
>> + * 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_offset)
>> +{
>> +    return (clic->clicintattr[irq_offset] >> 1) & 0x3;
>> +}
>> +
>> +static inline bool
>> +riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
>> +{
>> +    return (clic->clicintattr[irq_offset] >> 1) & 0x1;
>> +}
>> +
>> +static inline bool
>> +riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
>> +{
>> +    return (clic->clicintattr[irq_offset] & 0x1) && clic->nvbits;
>> +}
>> +
>> +static uint8_t
>> +riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
>> +{
>> +    int nlbits = clic->nlbits;
>> +
>> +    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->nlbits;
>> +    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);
>> +}
>> +
>> +/*
>> + * In a system with multiple harts, the M-mode CLIC regions for all the
>> harts
>> + * are placed contiguously in the memory space, followed by the S-mode
>> CLIC
>> + * regions for all harts. (Section 3.11)
>> + */
>> +static size_t
>> +riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int hartid,
>> int irq)
>> +{
>> +    size_t mode_offset = 0;
>> +    size_t unit = clic->num_harts * clic->num_sources;
>> +
>> +    switch (mode) {
>> +    case PRV_M:
>> +        mode_offset = 0;
>> +        break;
>> +    case PRV_S:
>> +        mode_offset = unit;
>> +        break;
>> +    case PRV_U:
>> +        mode_offset = clic->prv_s ? 2 * unit : unit;
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid mode %d\n", mode);
>> +        exit(1);
>> +    }
>> +    return mode_offset + hartid * clic->num_sources + irq;
>> +}
>> +
>> +static void riscv_clic_next_interrupt(void *opaque, int hartid)
>> +{
>> +    /*
>> +     * 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
>> +     */
>> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(hartid));
>> +    CPURISCVState *env = &cpu->env;
>> +    RISCVCLICState *clic = (RISCVCLICState *)opaque;
>> +
>> +    int il[4] = {
>> +        MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
>> +            clic->mintthresh), /* PRV_U */
>> +        MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
>> +            clic->sintthresh), /* PRV_S */
>> +        0,                     /* reserverd */
>> +        MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
>> +            clic->uintthresh)  /* PRV_M */
>> +    };
>> +
>> +    /* Get sorted list of enabled interrupts for this hart */
>> +    size_t hart_offset = hartid * clic->num_sources;
>> +    CLICActiveInterrupt *active = &clic->active_list[hart_offset];
>> +    size_t active_count = clic->active_count[hartid];
>> +    uint8_t mode, level, priority;
>> +
>> +    /* Loop through the enabled interrupts sorted by mode+priority+level
>> */
>> +    while (active_count) {
>> +        size_t irq_offset;
>> +        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;
>> +        }
>> +        irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> active->irq);
>> +        /* Check pending interrupt with high enough mode+priority+level
>> */
>> +        if (clic->clicintip[irq_offset]) {
>> +            /* Clean vector edge-triggered pending */
>> +            if (riscv_clic_is_edge_triggered(clic, irq_offset) &&
>> +                riscv_clic_is_shv_interrupt(clic, irq_offset)) {
>> +                clic->clicintip[irq_offset] = 0;
>> +            }
>> +            /* Post pending interrupt for this hart */
>> +            clic->exccode[hartid] = active->irq | mode << 12 | level <<
>> 14;
>> +            qemu_set_irq(clic->cpu_irqs[hartid], 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 hartid, int
>> irq)
>> +{
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    if (!clic->prv_s && !clic->prv_u) { /* M */
>> +        return mode == PRV_M;
>> +    } else if (!clic->prv_s) { /* M/U */
>> +        switch (clic->nmbits) {
>> +        case 0:
>> +            return mode == PRV_M;
>> +        case 1:
>> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode ==
>> PRV_M) :
>> +                                                          (mode ==
>> PRV_U);
>> +        default:
>> +            qemu_log_mask(LOG_GUEST_ERROR,
>> +                "clic: nmbits can only be 0 or 1 for M/U hart");
>> +            exit(1);
>> +        }
>> +    } else { /* M/S/U */
>> +        switch (clic->nmbits) {
>> +        case 0:
>> +            return mode == PRV_M;
>> +        case 1:
>> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode ==
>> PRV_M) :
>> +                                                          (mode ==
>> PRV_S);
>> +        case 2:
>> +            return mode == clic->clicintattr[irq_offset];
>> +        case 3:
>> +            qemu_log_mask(LOG_GUEST_ERROR,
>> +                "clic: nmbits can only be 0 or 1 or 2 for M/S/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 mode, int hartid,
>> int irq)
>> +{
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
>> +}
>> +
>> +static void
>> +riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
>> +                        int irq, uint64_t value)
>> +{
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    clic->clicintip[irq_offset] = !!value;
>> +    riscv_clic_next_interrupt(clic, hartid);
>> +}
>> +
>> +/*
>> + * 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, uint64_t
>> value)
>> +{
>> +    int mode = extract64(value, 6, 2);
>> +
>> +    if (!qtest_enabled()) {
>> +        CPURISCVState *env = current_cpu->env_ptr;
>> +        if (env->priv < mode) {
>> +            return false;
>> +        }
>> +    }
>> +    return true;
>> +}
>> +
>> +static inline int riscv_clic_encode_priority(const CLICActiveInterrupt
>> *i)
>> +{
>> +    return ((i->intcfg & 0x3ff) << 12) | /* Highest mode+level+priority
>> */
>> +           (i->irq & 0xfff);             /* Highest irq number */
>> +}
>> +
>> +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 hartid,
>> +                        int irq, uint64_t new_intie)
>> +{
>> +    size_t hart_offset = hartid * clic->num_sources;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    CLICActiveInterrupt *active_list = &clic->active_list[hart_offset];
>> +    size_t *active_count = &clic->active_count[hartid];
>> +
>> +    uint8_t old_intie = clic->clicintie[irq_offset];
>> +    clic->clicintie[irq_offset] = !!new_intie;
>> +
>> +    /* Add to or remove from list of active interrupts */
>> +    if (new_intie && !old_intie) {
>> +        active_list[*active_count].intcfg = (mode << 8) |
>> +                                            clic->clicintctl[irq_offset];
>> +        active_list[*active_count].irq = irq;
>> +        (*active_count)++;
>> +    } else if (!new_intie && old_intie) {
>> +        CLICActiveInterrupt key = {
>> +            (mode << 8) | clic->clicintctl[irq_offset], irq
>> +        };
>> +        CLICActiveInterrupt *result = bsearch(&key,
>> +                                              active_list, *active_count,
>> +
>> sizeof(CLICActiveInterrupt),
>> +                                              riscv_clic_active_compare);
>> +        size_t elem = (result - active_list) /
>> sizeof(CLICActiveInterrupt);
>> +        size_t sz = (--(*active_count) - elem) *
>> sizeof(CLICActiveInterrupt);
>> +        assert(result);
>> +        memmove(&result[0], &result[1], sz);
>> +    }
>> +
>> +    /* Sort list of active interrupts */
>> +    qsort(active_list, *active_count,
>> +          sizeof(CLICActiveInterrupt),
>> +          riscv_clic_active_compare);
>> +
>> +    riscv_clic_next_interrupt(clic, hartid);
>> +}
>> +
>> +static void
>> +riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
>> +                      uint64_t value, unsigned size,
>> +                      int mode, int hartid, int irq)
>> +{
>> +    int req = extract32(addr, 0, 2);
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +
>> +    if (hartid >= clic->num_harts) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
>> +                      hartid, addr);
>> +        return;
>> +    }
>> +
>> +    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, mode, hartid, irq)) {
>> +            /*
>> +             * The actual pending bit is located at bit 0 (i.e., the
>> +             * leastsignificant 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_offset]) {
>> +                riscv_clic_update_intip(clic, mode, hartid, irq, value);
>> +            }
>> +        }
>> +        break;
>> +    case 1: /* clicintie[i] */
>> +        if (clic->clicintie[irq_offset] != value) {
>> +            riscv_clic_update_intie(clic, mode, hartid, irq, value);
>> +        }
>> +        break;
>> +    case 2: /* clicintattr[i] */
>> +        if (riscv_clic_validate_intattr(clic, value)) {
>> +            if (clic->clicintattr[irq_offset] != value) {
>> +                /* When nmbits=2, check WARL */
>> +                bool invalid = (clic->nmbits == 2) &&
>> +                               (extract64(value, 6, 2) == 0b10);
>> +                if (invalid) {
>> +                    uint8_t old_mode =
>> extract32(clic->clicintattr[irq_offset],
>> +                                                 6, 2);
>> +                    value = deposit32(value, 6, 2, old_mode);
>> +                }
>> +                clic->clicintattr[irq_offset] = value;
>> +                riscv_clic_next_interrupt(clic, hartid);
>> +            }
>> +        }
>> +        break;
>> +    case 3: /* clicintctl[i] */
>> +        if (value != clic->clicintctl[irq_offset]) {
>> +            clic->clicintctl[irq_offset] = value;
>> +            riscv_clic_next_interrupt(clic, hartid);
>> +        }
>> +        break;
>> +    }
>> +}
>> +
>> +static uint64_t
>> +riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
>> +                     int hartid, int irq)
>> +{
>> +    int req = extract32(addr, 0, 2);
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +
>> +    if (hartid >= clic->num_harts) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
>> +                      hartid, addr);
>> +        return 0;
>> +    }
>> +
>> +    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] */
>> +        return clic->clicintip[irq_offset];
>> +    case 1: /* clicintie[i] */
>> +        return clic->clicintie[irq_offset];
>> +    case 2: /* clicintattr[i] */
>> +        /*
>> +         * clicintattr register layout
>> +         * Bits Field
>> +         * 7:6 mode
>> +         * 5:3 reserved (WPRI 0)
>> +         * 2:1 trig
>> +         * 0 shv
>> +         */
>> +        return clic->clicintattr[irq_offset] & ~0x38;
>> +    case 3: /* clicintctrl */
>> +        /*
>> +         * 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_offset] |
>> +               ((1 << (8 - clic->clicintctlbits)) - 1);
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +/* Return target interrupt mode */
>> +static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
>> +{
>> +    int mode = addr / (4 * clic->num_harts * clic->num_sources);
>> +    switch (mode) {
>> +    case 0:
>> +        return PRV_M;
>> +    case 1:
>> +        assert(clic->prv_s || clic->prv_u);
>> +        return clic->prv_s ? PRV_S : PRV_U;
>> +    case 2:
>> +        assert(clic->prv_s && clic->prv_u);
>> +        return PRV_U;
>> +    default:
>> +        g_assert_not_reached();
>> +        break;
>> +    }
>> +}
>> +
>> +/* Return target hart id */
>> +static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
>> +{
>> +    int mode_unit = 4 * clic->num_harts * clic->num_sources;
>> +    int hart_unit = 4 * clic->num_sources;
>> +
>> +    return (addr % mode_unit) / hart_unit;
>> +}
>> +
>> +/* Return target interrupt number */
>> +static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
>> +{
>> +    int hart_unit = 4 * clic->num_sources;
>> +    return (addr % hart_unit) / 4;
>> +}
>> +
>> +static void
>> +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned
>> size)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    hwaddr clic_size = clic->clic_size;
>> +    int hartid, mode, irq;
>> +
>> +    if (addr < clic_size) {
>> +        if (addr < 0x1000) {
>> +            assert(addr % 4 == 0);
>> +            int index = addr / 4;
>> +            switch (index) {
>> +            case 0: /* cliccfg */
>> +                {
>> +                    uint8_t nlbits = extract32(value, 1, 4);
>> +                    uint8_t nmbits = extract32(value, 5, 2);
>> +
>> +                    /*
>> +                     * The 4-bit cliccfg.nlbits WARL field.
>> +                     * Valid values are 0—8.
>> +                     */
>> +                    if (nlbits <= 8) {
>> +                        clic->nlbits = nlbits;
>> +                    }
>> +                    /* Valid values are given by implemented priviledges
>> */
>> +                    if (clic->prv_s && clic->prv_u) {
>> +                        if (nmbits <= 2) {
>> +                            clic->nmbits = nmbits;
>> +                        }
>> +                    } else if (clic->prv_u) {
>> +                        if (nmbits <= 1) {
>> +                            clic->nmbits = nmbits;
>> +                        }
>> +                    } else {
>> +                        assert(!clic->prv_s);
>> +                        if (nmbits == 0) {
>> +                            clic->nmbits = 0;
>> +                        }
>> +                    }
>> +                    clic->nvbits = extract32(value, 0, 1);
>> +                    break;
>> +                }
>> +            case 1: /* clicinfo, read-only register */
>> +                qemu_log_mask(LOG_GUEST_ERROR,
>> +                              "clic: write read-only clicinfo.\n");
>> +                break;
>> +            case 0x10 ... 0x2F: /* clicinttrig */
>> +                {
>> +                    uint32_t interrupt_number = value &
>> MAKE_64BIT_MASK(0, 13);
>> +                    if (interrupt_number <= clic->num_sources) {
>> +                        value &= ~MAKE_64BIT_MASK(13, 18);
>> +                        clic->clicinttrig[index - 0x10] = value;
>> +                    }
>> +                    break;
>> +                }
>> +            case 2: /* mintthresh */
>> +                if (!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 -= 0x1000;
>> +            hartid = riscv_clic_get_hartid(clic, addr);
>> +            mode = riscv_clic_get_mode(clic, addr);
>> +            irq = riscv_clic_get_irq(clic, addr);
>> +
>> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
>> +                riscv_clic_hart_write(clic, addr, value, size, mode,
>> +                                      hartid, irq);
>> +            }
>> +        }
>> +    } else {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid write: 0x%" HWADDR_PRIx "\n", addr);
>> +    }
>> +}
>> +
>> +static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    hwaddr clic_size = clic->clic_size;
>> +    int hartid, mode, irq;
>> +
>> +    if (addr < clic_size) {
>> +        if (addr < 0x1000) {
>> +            assert(addr % 4 == 0);
>> +            int index = addr / 4;
>> +            switch (index) {
>> +            case 0: /* cliccfg */
>> +                return clic->nvbits |
>> +                       (clic->nlbits << 1) |
>> +                       (clic->nmbits << 5);
>> +            case 1: /* clicinfo */
>> +                /*
>> +                 * clicinfo register layout
>> +                 *
>> +                 * Bits Field
>> +                 * 31 reserved (WARL 0)
>> +                 * 30:25 num_trigger
>> +                 * 24:21 CLICINTCTLBITS
>> +                 * 20:13 version (for version control)
>> +                 * 12:0 num_interrupt
>> +                 */
>> +                return clic->clicinfo & ~INT32_MAX;
>> +            case 0x10 ... 0x2F: /* clicinttrig */
>> +                /*
>> +                 * clicinttrig register layout
>> +                 *
>> +                 * Bits Field
>> +                 * 31 enable
>> +                 * 30:13 reserved (WARL 0)
>> +                 * 12:0 interrupt_number
>> +                 */
>> +                return clic->clicinttrig[index - 0x10] &
>> +                       ~MAKE_64BIT_MASK(13, 18);
>> +            case 2: /* mintthresh */
>> +                if (!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 -= 0x1000;
>> +            hartid = riscv_clic_get_hartid(clic, addr);
>> +            mode = riscv_clic_get_mode(clic, addr);
>> +            irq = riscv_clic_get_irq(clic, addr);
>> +
>> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
>> +                return riscv_clic_hart_read(clic, addr, mode, hartid,
>> irq);
>> +            }
>> +        }
>> +    } else {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid read: 0x%" HWADDR_PRIx "\n", addr);
>> +    }
>> +    return 0;
>> +}
>> +
>> +static void riscv_clic_set_irq(void *opaque, int id, int level)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    int irq, hartid, mode;
>> +    hwaddr addr = 4 * id;
>> +    TRIG_TYPE type;
>> +
>> +    hartid = riscv_clic_get_hartid(clic, addr);
>> +    mode = riscv_clic_get_mode(clic, addr);
>> +    irq = riscv_clic_get_irq(clic, addr);
>> +    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, mode, hartid, irq, level);
>> +            break;
>> +        case NEG_LEVEL:
>> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
>> +            break;
>> +        case NEG_EDGE:
>> +            break;
>> +        }
>> +    } else {
>> +        switch (type) {
>> +        case POSITIVE_LEVEL:
>> +            riscv_clic_update_intip(clic, mode, hartid, irq, level);
>> +            break;
>> +        case POSITIVE_EDGE:
>> +            break;
>> +        case NEG_LEVEL:
>> +        case NEG_EDGE:
>> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
>> +            break;
>> +        }
>> +    }
>> +}
>> +
>> +static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level)
>> +{
>> +    CPURISCVState *env = (CPURISCVState *)opaque;
>> +    RISCVCLICState *clic = env->clic;
>> +    CPUState *cpu = env_cpu(env);
>> +
>> +    if (level) {
>> +        env->exccode = clic->exccode[cpu->cpu_index];
>> +        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 harts_x_sources = clic->num_harts * clic->num_sources;
>> +    int irqs, i;
>> +
>> +    if (clic->prv_s && clic->prv_u) {
>> +        irqs = 3 * harts_x_sources;
>> +    } else if (clic->prv_s || clic->prv_u) {
>> +        irqs = 2 * harts_x_sources;
>> +    } else {
>> +        irqs = harts_x_sources;
>> +    }
>> +
>> +    clic->clic_size = irqs * 4 + 0x1000;
>> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops,
>> clic,
>> +                          TYPE_RISCV_CLIC, clic->clic_size);
>> +
>> +    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);
>> +    clic->active_count = g_new0(size_t, clic->num_harts);
>> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
>> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
>> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
>> +
>> +    /* Allocate irq 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_irqs, clic->num_harts);
>> +
>> +    for (i = 0; i < clic->num_harts; i++) {
>> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>>
>
> As spec says:
>   Smaller single-core systems might have only a CLIC,
>   while multicore systems might have a CLIC per-core and a single shared
> PLIC.
>   The PLIC xeip signals are treated as hart-local interrupt sources by the
> CLIC at each core.
>
> It looks like it's possible to have one CLIC instance per core.
>
> If you want to delivery an interrupt to one hart, you should encode the
> IRQ by the interrupt number
> , the hart number and the interrupt target privilege, then set the irq.
>
> I think how to calculate the hart number is the task of PLIC and it can
> make use of "hartid-base"
> to calculate it.
>
> Thanks,
> Zhiwei
>

Hi Zhiwei,

What I mean is if there are multiple CLIC instances, each per core (CLIC
spec allows that).
If you try to bind CLIC with CPU index start from 0,
it will be impossible for CLIC instance to bind CPU from index other than 0.

For example, for 4 cores system, it's possible to have 4 CLIC instances:
  * CLIC 0 binds to CPU 0
  * CLIC 1 binds to CPU 1
  * CLIC 2 binds to CPU 2
  * CLIC 3 binds to CPU 3

and that's why I said it's possible to pass an extra "hartid-base" just
like PLIC.
I know most of hardid are calculated by the requesing address, so most
hartid usages should be fine.
But I saw two places using qemu_get_cpu(),
which may cause the problem for the scenario I describe above:
i.e. riscv_clic_next_interrupt() and riscv_clic_realize() as my original
reply.

Regards,
Frank Chang


> However if you try to bind CPU reference start from index i = 0.
> It's not possible for each per-core CLIC to bind their own CPU instance in
> multicore system
> as they have to bind from CPU 0.
>
> I'm not sure if we add a new "hartid-base" property just like what SiFive
> PLIC is
> implemented would be a good idea or not.
>
>
> Regards,
> Frank Chang
>
>
>> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>> +                                         &cpu->env, 1);
>> +        qdev_connect_gpio_out(dev, i, irq);
>> +        cpu->env.clic = clic;
>> +    }
>> +}
>> +
>> +static Property riscv_clic_properties[] = {
>> +    DEFINE_PROP_BOOL("prv-s", RISCVCLICState, prv_s, false),
>> +    DEFINE_PROP_BOOL("prv-u", RISCVCLICState, prv_u, false),
>> +    DEFINE_PROP_UINT32("num-harts", RISCVCLICState, num_harts, 0),
>> +    DEFINE_PROP_UINT32("num-sources", RISCVCLICState, num_sources, 0),
>> +    DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICState, clicintctlbits,
>> 0),
>> +    DEFINE_PROP_UINT64("mclicbase", RISCVCLICState, mclicbase, 0),
>> +    DEFINE_PROP_STRING("version", RISCVCLICState, version),
>> +    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 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 void riscv_clic_register_types(void)
>> +{
>> +    type_register_static(&riscv_clic_info);
>> +}
>> +
>> +type_init(riscv_clic_register_types)
>> +
>> +/*
>> + * riscv_clic_create:
>> + *
>> + * @addr: base address of M-Mode CLIC memory-mapped registers
>> + * @prv_s: have smode region
>> + * @prv_u: have umode region
>> + * @num_harts: number of CPU harts
>> + * @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"
>> + *
>> + * Returns: the device object
>> + */
>> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
>> +                               uint32_t num_harts, uint32_t num_sources,
>> +                               uint8_t clicintctlbits,
>> +                               const char *version)
>> +{
>> +    DeviceState *dev = qdev_new(TYPE_RISCV_CLIC);
>> +
>> +    assert(num_sources <= 4096);
>> +    assert(num_harts <= 1024);
>> +    assert(clicintctlbits <= 8);
>> +    assert(!strcmp(version, "v0.8") || !strcmp(version, "v0.9"));
>> +
>> +    qdev_prop_set_bit(dev, "prv-s", prv_s);
>> +    qdev_prop_set_bit(dev, "prv-u", prv_u);
>> +    qdev_prop_set_uint32(dev, "num-harts", num_harts);
>> +    qdev_prop_set_uint32(dev, "num-sources", num_sources);
>> +    qdev_prop_set_uint32(dev, "clicintctlbits", clicintctlbits);
>> +    qdev_prop_set_uint64(dev, "mclicbase", addr);
>> +    qdev_prop_set_string(dev, "version", version);
>> +
>> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
>> +    return dev;
>> +}
>> +
>> +void riscv_clic_get_next_interrupt(void *opaque, int hartid)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    riscv_clic_next_interrupt(clic, hartid);
>> +}
>> +
>> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int
>> irq)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    return riscv_clic_is_shv_interrupt(clic, irq_offset);
>> +}
>> +
>> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int
>> irq)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
>> +}
>> +
>> +void riscv_clic_clean_pending(void *opaque, int mode, int hartid, int
>> irq)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    clic->clicintip[irq_offset] = 0;
>> +}
>> +
>> +/*
>> + * 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.
>> + */
>> +bool riscv_clic_is_clic_mode(CPURISCVState *env)
>> +{
>> +    target_ulong xtvec = (env->priv == PRV_M) ? env->mtvec : env->stvec;
>> +    return env->clic && ((xtvec & 0x3) == 3);
>> +}
>> +
>> +void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
>> +                               int *il, int *irq)
>> +{
>> +    *irq = extract32(exccode, 0, 12);
>> +    *mode = extract32(exccode, 12, 2);
>> +    *il = extract32(exccode, 14, 8);
>> +}
>> diff --git a/include/hw/intc/riscv_clic.h b/include/hw/intc/riscv_clic.h
>> new file mode 100644
>> index 0000000000..e5f89672a6
>> --- /dev/null
>> +++ b/include/hw/intc/riscv_clic.h
>> @@ -0,0 +1,103 @@
>> +/*
>> + * RISC-V CLIC(Core Local Interrupt Controller) interface.
>> + *
>> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
>> + *
>> + * 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/>.
>> + */
>> +
>> +#ifndef RISCV_CLIC_H
>> +#define RISCV_CLIC_H
>> +
>> +#include "hw/irq.h"
>> +#include "hw/sysbus.h"
>> +
>> +#define TYPE_RISCV_CLIC "riscv_clic"
>> +#define RISCV_CLIC(obj) \
>> +    OBJECT_CHECK(RISCVCLICState, (obj), TYPE_RISCV_CLIC)
>> +
>> +/*
>> + * 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;
>> +
>> +typedef struct RISCVCLICState {
>> +    /*< private >*/
>> +    SysBusDevice parent_obj;
>> +
>> +    /*< public >*/
>> +
>> +    /* Implementaion parameters */
>> +    bool prv_s;
>> +    bool prv_u;
>> +    uint32_t num_harts;
>> +    uint32_t num_sources;
>> +    uint32_t clic_size;
>> +    uint32_t clic_mmode_base;
>> +    uint32_t clicintctlbits;
>> +    uint64_t mclicbase;
>> +    char *version;
>> +
>> +    /* Global configuration */
>> +    uint8_t nmbits;
>> +    uint8_t nlbits;
>> +    uint8_t nvbits;
>> +    uint32_t clicinfo;
>> +    uint32_t clicinttrig[32];
>> +
>> +    /* Aperture configuration */
>> +    uint8_t *clicintip;
>> +    uint8_t *clicintie;
>> +    uint8_t *clicintattr;
>> +    uint8_t *clicintctl;
>> +
>> +    /* Complatible with v0.8 */
>> +    uint32_t mintthresh;
>> +    uint32_t sintthresh;
>> +    uint32_t uintthresh;
>> +
>> +    /* QEMU implementaion related fields */
>> +    uint32_t *exccode;
>> +    CLICActiveInterrupt *active_list;
>> +    size_t *active_count;
>> +    MemoryRegion mmio;
>> +    qemu_irq *cpu_irqs;
>> +} RISCVCLICState;
>> +
>> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
>> +                               uint32_t num_harts, 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 mode, int hartid, int
>> irq);
>> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int
>> irq);
>> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int
>> irq);
>> +void riscv_clic_get_next_interrupt(void *opaque, int hartid);
>> +bool riscv_clic_is_clic_mode(CPURISCVState *env);
>> +#endif
>> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>> index a5eab26a69..9e389d7bbf 100644
>> --- a/target/riscv/cpu.h
>> +++ b/target/riscv/cpu.h
>> @@ -33,6 +33,7 @@
>>  #define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
>>  #define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
>>  #define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
>> +#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0
>>
>>  #define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
>>  #define TYPE_RISCV_CPU_BASE32           RISCV_CPU_TYPE_NAME("rv32")
>> @@ -247,6 +248,7 @@ struct CPURISCVState {
>>      /* Fields from here on are preserved across CPU reset. */
>>      QEMUTimer *timer; /* Internal timer */
>>      void *clic;       /* clic interrupt controller */
>> +    uint32_t exccode; /* clic irq encode */
>>  };
>>
>>  OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
>> --
>> 2.25.1
>>
>>
>>

[-- Attachment #2: Type: text/html, Size: 65862 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-04-09  7:48 ` [RFC PATCH 03/11] hw/intc: Add CLIC device LIU Zhiwei
  2021-04-19 23:25   ` Alistair Francis
  2021-06-13 10:10   ` Frank Chang
@ 2021-06-26 15:03   ` Frank Chang
  2021-06-26 15:26     ` Frank Chang
  2021-06-29  2:43     ` LIU Zhiwei
  2021-06-26 15:20   ` Frank Chang
                     ` (4 subsequent siblings)
  7 siblings, 2 replies; 81+ messages in thread
From: Frank Chang @ 2021-06-26 15:03 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 42245 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:57寫道:

> 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>
> ---
>  default-configs/devices/riscv32-softmmu.mak |   1 +
>  default-configs/devices/riscv64-softmmu.mak |   1 +
>  hw/intc/Kconfig                             |   3 +
>  hw/intc/meson.build                         |   1 +
>  hw/intc/riscv_clic.c                        | 835 ++++++++++++++++++++
>  include/hw/intc/riscv_clic.h                | 103 +++
>  target/riscv/cpu.h                          |   2 +
>  7 files changed, 946 insertions(+)
>  create mode 100644 hw/intc/riscv_clic.c
>  create mode 100644 include/hw/intc/riscv_clic.h
>
> diff --git a/default-configs/devices/riscv32-softmmu.mak
> b/default-configs/devices/riscv32-softmmu.mak
> index d847bd5692..1430c30588 100644
> --- a/default-configs/devices/riscv32-softmmu.mak
> +++ b/default-configs/devices/riscv32-softmmu.mak
> @@ -5,6 +5,7 @@
>  #CONFIG_PCI_DEVICES=n
>  CONFIG_SEMIHOSTING=y
>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> +CONFIG_RISCV_CLIC=y
>
>  # Boards:
>  #
> diff --git a/default-configs/devices/riscv64-softmmu.mak
> b/default-configs/devices/riscv64-softmmu.mak
> index d5eec75f05..396800bbbd 100644
> --- a/default-configs/devices/riscv64-softmmu.mak
> +++ b/default-configs/devices/riscv64-softmmu.mak
> @@ -5,6 +5,7 @@
>  #CONFIG_PCI_DEVICES=n
>  CONFIG_SEMIHOSTING=y
>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> +CONFIG_RISCV_CLIC=y
>
>  # Boards:
>  #
> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
> index f4694088a4..5bf492b48f 100644
> --- a/hw/intc/Kconfig
> +++ b/hw/intc/Kconfig
> @@ -68,6 +68,9 @@ config SIFIVE_CLINT
>  config SIFIVE_PLIC
>      bool
>
> +config RISCV_CLIC
> +    bool
> +
>  config GOLDFISH_PIC
>      bool
>
> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> index 1c299039f6..2aa71b6738 100644
> --- a/hw/intc/meson.build
> +++ b/hw/intc/meson.build
> @@ -50,6 +50,7 @@ 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_SIFIVE_CLINT', if_true:
> files('sifive_clint.c'))
>  specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true:
> files('sifive_plic.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'))
> diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
> new file mode 100644
> index 0000000000..8ad534c506
> --- /dev/null
> +++ b/hw/intc/riscv_clic.c
> @@ -0,0 +1,835 @@
> +/*
> + * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
> + *
> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
> + *
> + * 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/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/log.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"
> +
> +/*
> + * 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_offset)
> +{
> +    return (clic->clicintattr[irq_offset] >> 1) & 0x3;
> +}
> +
> +static inline bool
> +riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
> +{
> +    return (clic->clicintattr[irq_offset] >> 1) & 0x1;
> +}
> +
> +static inline bool
> +riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
> +{
> +    return (clic->clicintattr[irq_offset] & 0x1) && clic->nvbits;
> +}
> +
> +static uint8_t
> +riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
> +{
> +    int nlbits = clic->nlbits;
> +
> +    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;
> +}
>

According to spec:
  if the nlbits > CLICINTCTLBITS, then the lower bits of the 8-bit
  interrupt level are assumed to be all 1s.

That is, the valid nlbits should be: min(clic->nlbits, CLICINTCTLBITS);
The cliccfg example in spec also shows that:

CLICINTCTLBITS  nlbits  clicintctl[i]  interrupt levels
      0                       2         ........         255
      1                       2         l.......         127,255
      2                       2         ll......         63,127,191,255
      3                       3         lll.....
 31,63,95,127,159,191,223,255
      4                       1         lppp....      127,255


> +
> +static uint8_t
> +riscv_clic_get_interrupt_priority(RISCVCLICState *clic, uint8_t intctl)
> +{
> +    int npbits = clic->clicintctlbits - clic->nlbits;
> +    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);
> +}
> +
> +/*
> + * In a system with multiple harts, the M-mode CLIC regions for all the
> harts
> + * are placed contiguously in the memory space, followed by the S-mode
> CLIC
> + * regions for all harts. (Section 3.11)
> + */
>

The above description is not true any more in the latest spec:
  The CLIC specification does not dictate how CLIC memory-mapped registers
are
  split between M/S/U regions as well as the layout of multiple harts as
this is generally
  a platform issue and each platform needs to define a discovery mechanism
to determine
  the memory map locations.

But I think we can just keep the current design for now anyway, as it's
also one of legal memory layout.
Otherwise, the design would be more complicated I think.

+static size_t
> +riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int hartid, int
> irq)
> +{
> +    size_t mode_offset = 0;
> +    size_t unit = clic->num_harts * clic->num_sources;
> +
> +    switch (mode) {
> +    case PRV_M:
> +        mode_offset = 0;
> +        break;
> +    case PRV_S:
> +        mode_offset = unit;
> +        break;
> +    case PRV_U:
> +        mode_offset = clic->prv_s ? 2 * unit : unit;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid mode %d\n", mode);
> +        exit(1);
> +    }
> +    return mode_offset + hartid * clic->num_sources + irq;
> +}
> +
> +static void riscv_clic_next_interrupt(void *opaque, int hartid)
> +{
> +    /*
> +     * 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
> +     */
> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(hartid));
> +    CPURISCVState *env = &cpu->env;
> +    RISCVCLICState *clic = (RISCVCLICState *)opaque;
> +
> +    int il[4] = {
> +        MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
> +            clic->mintthresh), /* PRV_U */
> +        MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
> +            clic->sintthresh), /* PRV_S */
> +        0,                     /* reserverd */
> +        MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
> +            clic->uintthresh)  /* PRV_M */
> +    };
> +
> +    /* Get sorted list of enabled interrupts for this hart */
> +    size_t hart_offset = hartid * clic->num_sources;
> +    CLICActiveInterrupt *active = &clic->active_list[hart_offset];
> +    size_t active_count = clic->active_count[hartid];
> +    uint8_t mode, level, priority;
> +
> +    /* Loop through the enabled interrupts sorted by mode+priority+level
> */
> +    while (active_count) {
> +        size_t irq_offset;
> +        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;
> +        }
> +        irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> active->irq);
> +        /* Check pending interrupt with high enough mode+priority+level */
> +        if (clic->clicintip[irq_offset]) {
> +            /* Clean vector edge-triggered pending */
> +            if (riscv_clic_is_edge_triggered(clic, irq_offset) &&
> +                riscv_clic_is_shv_interrupt(clic, irq_offset)) {
> +                clic->clicintip[irq_offset] = 0;
> +            }
> +            /* Post pending interrupt for this hart */
> +            clic->exccode[hartid] = active->irq | mode << 12 | level <<
> 14;
> +            qemu_set_irq(clic->cpu_irqs[hartid], 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 hartid, int
> irq)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    if (!clic->prv_s && !clic->prv_u) { /* M */
> +        return mode == PRV_M;
> +    } else if (!clic->prv_s) { /* M/U */
> +        switch (clic->nmbits) {
> +        case 0:
> +            return mode == PRV_M;
> +        case 1:
> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M)
> :
> +                                                          (mode == PRV_U);
> +        default:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "clic: nmbits can only be 0 or 1 for M/U hart");
> +            exit(1);
> +        }
> +    } else { /* M/S/U */
> +        switch (clic->nmbits) {
> +        case 0:
> +            return mode == PRV_M;
> +        case 1:
> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M)
> :
> +                                                          (mode == PRV_S);
> +        case 2:
> +            return mode == clic->clicintattr[irq_offset];
> +        case 3:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "clic: nmbits can only be 0 or 1 or 2 for M/S/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 mode, int hartid, int
> irq)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
> +}
> +
> +static void
> +riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
> +                        int irq, uint64_t value)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    clic->clicintip[irq_offset] = !!value;
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +/*
> + * 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, uint64_t
> value)
> +{
> +    int mode = extract64(value, 6, 2);
> +
> +    if (!qtest_enabled()) {
> +        CPURISCVState *env = current_cpu->env_ptr;
> +        if (env->priv < mode) {
> +            return false;
> +        }
> +    }
> +    return true;
> +}
> +
> +static inline int riscv_clic_encode_priority(const CLICActiveInterrupt *i)
> +{
> +    return ((i->intcfg & 0x3ff) << 12) | /* Highest mode+level+priority */
> +           (i->irq & 0xfff);             /* Highest irq number */
> +}
> +
> +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 hartid,
> +                        int irq, uint64_t new_intie)
> +{
> +    size_t hart_offset = hartid * clic->num_sources;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    CLICActiveInterrupt *active_list = &clic->active_list[hart_offset];
> +    size_t *active_count = &clic->active_count[hartid];
> +
> +    uint8_t old_intie = clic->clicintie[irq_offset];
> +    clic->clicintie[irq_offset] = !!new_intie;
> +
> +    /* Add to or remove from list of active interrupts */
> +    if (new_intie && !old_intie) {
> +        active_list[*active_count].intcfg = (mode << 8) |
> +                                            clic->clicintctl[irq_offset];
> +        active_list[*active_count].irq = irq;
> +        (*active_count)++;
> +    } else if (!new_intie && old_intie) {
> +        CLICActiveInterrupt key = {
> +            (mode << 8) | clic->clicintctl[irq_offset], irq
> +        };
> +        CLICActiveInterrupt *result = bsearch(&key,
> +                                              active_list, *active_count,
> +                                              sizeof(CLICActiveInterrupt),
> +                                              riscv_clic_active_compare);
> +        size_t elem = (result - active_list) /
> sizeof(CLICActiveInterrupt);
>

I think what you are trying to do here is to get the number of elements
right after the active interrupt to be deleted in order to calculate the
size of
active interrupts to be memmoved.

However, according to C spec:
  When two pointers are subtracted, both shall point to elements of the
same array object,
  or one past the last element of the array object; the result is the
difference of the
  subscripts of the two array elements.

So I think: (result - active_list) is already the number of elements you
want.
You don't have to divide it with sizeof(CLICActiveInterrupt) again.


> +        size_t sz = (--(*active_count) - elem) *
> sizeof(CLICActiveInterrupt);
> +        assert(result);
>

Nit: assert(result) can be moved above size_t elem statement.


> +        memmove(&result[0], &result[1], sz);
> +    }
> +
> +    /* Sort list of active interrupts */
> +    qsort(active_list, *active_count,
> +          sizeof(CLICActiveInterrupt),
> +          riscv_clic_active_compare);
> +
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +static void
> +riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
> +                      uint64_t value, unsigned size,
> +                      int mode, int hartid, int irq)
> +{
> +    int req = extract32(addr, 0, 2);
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +
> +    if (hartid >= clic->num_harts) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
> +                      hartid, addr);
> +        return;
> +    }
> +
> +    if (irq >= clic->num_sources) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid irq %u: 0x%" HWADDR_PRIx "\n", irq,
> addr);
> +        return;
> +    }
> +
> +    switch (req) {
>

Spec. says that it's legal to write 32-bit value to set
{clicintctl[i], clicintattr[i], clicintie[i] ,clicintip[i]} at the same
time:
  A 32-bit write to {clicintctl,clicintattr,clicintie,clicintip} is legal.
  However, there is no specified order in which the effects of
  the individual byte updates take effect.


> +    case 0: /* clicintip[i] */
> +        if (riscv_clic_validate_intip(clic, mode, hartid, irq)) {
> +            /*
> +             * The actual pending bit is located at bit 0 (i.e., the
> +             * leastsignificant bit). In case future extensions expand
> the bit
>

Typo: leastsignificant => least significant


> +             * 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_offset]) {
> +                riscv_clic_update_intip(clic, mode, hartid, irq, value);
> +            }
> +        }
> +        break;
> +    case 1: /* clicintie[i] */
> +        if (clic->clicintie[irq_offset] != value) {
> +            riscv_clic_update_intie(clic, mode, hartid, irq, value);
> +        }
> +        break;
> +    case 2: /* clicintattr[i] */
> +        if (riscv_clic_validate_intattr(clic, value)) {
> +            if (clic->clicintattr[irq_offset] != value) {
> +                /* When nmbits=2, check WARL */
> +                bool invalid = (clic->nmbits == 2) &&
> +                               (extract64(value, 6, 2) == 0b10);
> +                if (invalid) {
> +                    uint8_t old_mode =
> extract32(clic->clicintattr[irq_offset],
> +                                                 6, 2);
> +                    value = deposit32(value, 6, 2, old_mode);
> +                }
> +                clic->clicintattr[irq_offset] = value;
> +                riscv_clic_next_interrupt(clic, hartid);
> +            }
> +        }
> +        break;
> +    case 3: /* clicintctl[i] */
> +        if (value != clic->clicintctl[irq_offset]) {
> +            clic->clicintctl[irq_offset] = value;
>

If irq i is already in the active_list, when will its intcfg been synced?


> +            riscv_clic_next_interrupt(clic, hartid);
> +        }
> +        break;
> +    }
> +}
> +
> +static uint64_t
> +riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
> +                     int hartid, int irq)
> +{
> +    int req = extract32(addr, 0, 2);
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +
> +    if (hartid >= clic->num_harts) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
> +                      hartid, addr);
> +        return 0;
> +    }
> +
> +    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] */
> +        return clic->clicintip[irq_offset];
> +    case 1: /* clicintie[i] */
> +        return clic->clicintie[irq_offset];
> +    case 2: /* clicintattr[i] */
> +        /*
> +         * clicintattr register layout
> +         * Bits Field
> +         * 7:6 mode
> +         * 5:3 reserved (WPRI 0)
> +         * 2:1 trig
> +         * 0 shv
> +         */
> +        return clic->clicintattr[irq_offset] & ~0x38;
> +    case 3: /* clicintctrl */
>

Typo: clicintctl


> +        /*
> +         * 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_offset] |
> +               ((1 << (8 - clic->clicintctlbits)) - 1);
> +    }
> +
> +    return 0;
> +}
> +
> +/* Return target interrupt mode */
> +static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int mode = addr / (4 * clic->num_harts * clic->num_sources);
> +    switch (mode) {
> +    case 0:
> +        return PRV_M;
> +    case 1:
> +        assert(clic->prv_s || clic->prv_u);
> +        return clic->prv_s ? PRV_S : PRV_U;
> +    case 2:
> +        assert(clic->prv_s && clic->prv_u);
> +        return PRV_U;
> +    default:
> +        g_assert_not_reached();
> +        break;
> +    }
> +}
> +
> +/* Return target hart id */
> +static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int mode_unit = 4 * clic->num_harts * clic->num_sources;
> +    int hart_unit = 4 * clic->num_sources;
> +
> +    return (addr % mode_unit) / hart_unit;
> +}
> +
> +/* Return target interrupt number */
> +static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int hart_unit = 4 * clic->num_sources;
> +    return (addr % hart_unit) / 4;
> +}
> +
> +static void
> +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
> +{
> +    RISCVCLICState *clic = opaque;
> +    hwaddr clic_size = clic->clic_size;
> +    int hartid, mode, irq;
> +
> +    if (addr < clic_size) {
>

Is this necessary?
I think memory region size already limits the request address to be within
the range of clic_size.


> +        if (addr < 0x1000) {
> +            assert(addr % 4 == 0);
> +            int index = addr / 4;
> +            switch (index) {
> +            case 0: /* cliccfg */
> +                {
> +                    uint8_t nlbits = extract32(value, 1, 4);
> +                    uint8_t nmbits = extract32(value, 5, 2);
> +
> +                    /*
> +                     * The 4-bit cliccfg.nlbits WARL field.
> +                     * Valid values are 0—8.
> +                     */
> +                    if (nlbits <= 8) {
> +                        clic->nlbits = nlbits;
> +                    }
> +                    /* Valid values are given by implemented priviledges
> */
> +                    if (clic->prv_s && clic->prv_u) {
> +                        if (nmbits <= 2) {
> +                            clic->nmbits = nmbits;
> +                        }
> +                    } else if (clic->prv_u) {
> +                        if (nmbits <= 1) {
> +                            clic->nmbits = nmbits;
> +                        }
> +                    } else {
> +                        assert(!clic->prv_s);
> +                        if (nmbits == 0) {
> +                            clic->nmbits = 0;
> +                        }
> +                    }
> +                    clic->nvbits = extract32(value, 0, 1);
> +                    break;
> +                }
> +            case 1: /* clicinfo, read-only register */
> +                qemu_log_mask(LOG_GUEST_ERROR,
> +                              "clic: write read-only clicinfo.\n");
> +                break;
> +            case 0x10 ... 0x2F: /* clicinttrig */
> +                {
> +                    uint32_t interrupt_number = value &
> MAKE_64BIT_MASK(0, 13);
> +                    if (interrupt_number <= clic->num_sources) {
> +                        value &= ~MAKE_64BIT_MASK(13, 18);
> +                        clic->clicinttrig[index - 0x10] = value;
> +                    }
> +                    break;
> +                }
> +            case 2: /* mintthresh */
> +                if (!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 -= 0x1000;
> +            hartid = riscv_clic_get_hartid(clic, addr);
> +            mode = riscv_clic_get_mode(clic, addr);
> +            irq = riscv_clic_get_irq(clic, addr);
> +
> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
> +                riscv_clic_hart_write(clic, addr, value, size, mode,
> +                                      hartid, irq);
> +            }
> +        }
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid write: 0x%" HWADDR_PRIx "\n", addr);
> +    }
> +}
> +
> +static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    RISCVCLICState *clic = opaque;
> +    hwaddr clic_size = clic->clic_size;
> +    int hartid, mode, irq;
> +
> +    if (addr < clic_size) {
>

Same to riscv_clic_write().

Thanks,
Frank Chang


> +        if (addr < 0x1000) {
> +            assert(addr % 4 == 0);
> +            int index = addr / 4;
> +            switch (index) {
> +            case 0: /* cliccfg */
> +                return clic->nvbits |
> +                       (clic->nlbits << 1) |
> +                       (clic->nmbits << 5);
> +            case 1: /* clicinfo */
> +                /*
> +                 * clicinfo register layout
> +                 *
> +                 * Bits Field
> +                 * 31 reserved (WARL 0)
> +                 * 30:25 num_trigger
> +                 * 24:21 CLICINTCTLBITS
> +                 * 20:13 version (for version control)
> +                 * 12:0 num_interrupt
> +                 */
> +                return clic->clicinfo & ~INT32_MAX;
>

clic->clicinfo should represent the CLIC setting information.
I think it's better to add clic reset function or in riscv_clic_realize()
to initialize clic->clicinfo.


> +            case 0x10 ... 0x2F: /* clicinttrig */
> +                /*
> +                 * clicinttrig register layout
> +                 *
> +                 * Bits Field
> +                 * 31 enable
> +                 * 30:13 reserved (WARL 0)
> +                 * 12:0 interrupt_number
> +                 */
> +                return clic->clicinttrig[index - 0x10] &
> +                       ~MAKE_64BIT_MASK(13, 18);
> +            case 2: /* mintthresh */
> +                if (!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 -= 0x1000;
> +            hartid = riscv_clic_get_hartid(clic, addr);
> +            mode = riscv_clic_get_mode(clic, addr);
> +            irq = riscv_clic_get_irq(clic, addr);
> +
> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
> +                return riscv_clic_hart_read(clic, addr, mode, hartid,
> irq);
> +            }
> +        }
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid read: 0x%" HWADDR_PRIx "\n", addr);
> +    }
> +    return 0;
> +}
> +
> +static void riscv_clic_set_irq(void *opaque, int id, int level)
> +{
> +    RISCVCLICState *clic = opaque;
> +    int irq, hartid, mode;
> +    hwaddr addr = 4 * id;
> +    TRIG_TYPE type;
> +
> +    hartid = riscv_clic_get_hartid(clic, addr);
> +    mode = riscv_clic_get_mode(clic, addr);
> +    irq = riscv_clic_get_irq(clic, addr);
> +    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, mode, hartid, irq, level);
> +            break;
> +        case NEG_LEVEL:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
> +            break;
> +        case NEG_EDGE:
> +            break;
> +        }
> +    } else {
> +        switch (type) {
> +        case POSITIVE_LEVEL:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, level);
> +            break;
> +        case POSITIVE_EDGE:
> +            break;
> +        case NEG_LEVEL:
> +        case NEG_EDGE:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
> +            break;
> +        }
> +    }
> +}
> +
> +static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level)
> +{
> +    CPURISCVState *env = (CPURISCVState *)opaque;
> +    RISCVCLICState *clic = env->clic;
> +    CPUState *cpu = env_cpu(env);
> +
> +    if (level) {
> +        env->exccode = clic->exccode[cpu->cpu_index];
> +        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 harts_x_sources = clic->num_harts * clic->num_sources;
> +    int irqs, i;
> +
> +    if (clic->prv_s && clic->prv_u) {
> +        irqs = 3 * harts_x_sources;
> +    } else if (clic->prv_s || clic->prv_u) {
> +        irqs = 2 * harts_x_sources;
> +    } else {
> +        irqs = harts_x_sources;
> +    }
> +
> +    clic->clic_size = irqs * 4 + 0x1000;
> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops, clic,
> +                          TYPE_RISCV_CLIC, clic->clic_size);
> +
> +    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);
>

Should the size of clic->active_list be: harts_x_sources?


> +    clic->active_count = g_new0(size_t, clic->num_harts);
> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
> +
> +    /* Allocate irq 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_irqs, clic->num_harts);
> +
> +    for (i = 0; i < clic->num_harts; i++) {
> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
> +                                         &cpu->env, 1);
> +        qdev_connect_gpio_out(dev, i, irq);
> +        cpu->env.clic = clic;
> +    }
> +}
> +
> +static Property riscv_clic_properties[] = {
> +    DEFINE_PROP_BOOL("prv-s", RISCVCLICState, prv_s, false),
> +    DEFINE_PROP_BOOL("prv-u", RISCVCLICState, prv_u, false),
> +    DEFINE_PROP_UINT32("num-harts", RISCVCLICState, num_harts, 0),
> +    DEFINE_PROP_UINT32("num-sources", RISCVCLICState, num_sources, 0),
> +    DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICState, clicintctlbits,
> 0),
> +    DEFINE_PROP_UINT64("mclicbase", RISCVCLICState, mclicbase, 0),
> +    DEFINE_PROP_STRING("version", RISCVCLICState, version),
> +    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 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 void riscv_clic_register_types(void)
> +{
> +    type_register_static(&riscv_clic_info);
> +}
> +
> +type_init(riscv_clic_register_types)
> +
> +/*
> + * riscv_clic_create:
> + *
> + * @addr: base address of M-Mode CLIC memory-mapped registers
> + * @prv_s: have smode region
> + * @prv_u: have umode region
> + * @num_harts: number of CPU harts
> + * @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"
> + *
> + * Returns: the device object
> + */
> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
> +                               uint32_t num_harts, uint32_t num_sources,
> +                               uint8_t clicintctlbits,
> +                               const char *version)
> +{
> +    DeviceState *dev = qdev_new(TYPE_RISCV_CLIC);
> +
> +    assert(num_sources <= 4096);
> +    assert(num_harts <= 1024);
> +    assert(clicintctlbits <= 8);
> +    assert(!strcmp(version, "v0.8") || !strcmp(version, "v0.9"));
> +
> +    qdev_prop_set_bit(dev, "prv-s", prv_s);
> +    qdev_prop_set_bit(dev, "prv-u", prv_u);
> +    qdev_prop_set_uint32(dev, "num-harts", num_harts);
> +    qdev_prop_set_uint32(dev, "num-sources", num_sources);
> +    qdev_prop_set_uint32(dev, "clicintctlbits", clicintctlbits);
> +    qdev_prop_set_uint64(dev, "mclicbase", addr);
> +    qdev_prop_set_string(dev, "version", version);
> +
> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
> +    return dev;
> +}
> +
> +void riscv_clic_get_next_interrupt(void *opaque, int hartid)
> +{
> +    RISCVCLICState *clic = opaque;
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    return riscv_clic_is_shv_interrupt(clic, irq_offset);
> +}
> +
> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int
> irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
> +}
> +
> +void riscv_clic_clean_pending(void *opaque, int mode, int hartid, int irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    clic->clicintip[irq_offset] = 0;
> +}
> +
> +/*
> + * 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.
> + */
> +bool riscv_clic_is_clic_mode(CPURISCVState *env)
> +{
> +    target_ulong xtvec = (env->priv == PRV_M) ? env->mtvec : env->stvec;
> +    return env->clic && ((xtvec & 0x3) == 3);
> +}
> +
> +void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
> +                               int *il, int *irq)
> +{
> +    *irq = extract32(exccode, 0, 12);
> +    *mode = extract32(exccode, 12, 2);
> +    *il = extract32(exccode, 14, 8);
> +}
> diff --git a/include/hw/intc/riscv_clic.h b/include/hw/intc/riscv_clic.h
> new file mode 100644
> index 0000000000..e5f89672a6
> --- /dev/null
> +++ b/include/hw/intc/riscv_clic.h
> @@ -0,0 +1,103 @@
> +/*
> + * RISC-V CLIC(Core Local Interrupt Controller) interface.
> + *
> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
> + *
> + * 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/>.
> + */
> +
> +#ifndef RISCV_CLIC_H
> +#define RISCV_CLIC_H
> +
> +#include "hw/irq.h"
> +#include "hw/sysbus.h"
> +
> +#define TYPE_RISCV_CLIC "riscv_clic"
> +#define RISCV_CLIC(obj) \
> +    OBJECT_CHECK(RISCVCLICState, (obj), TYPE_RISCV_CLIC)
> +
> +/*
> + * 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;
> +
> +typedef struct RISCVCLICState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +
> +    /* Implementaion parameters */
> +    bool prv_s;
> +    bool prv_u;
> +    uint32_t num_harts;
> +    uint32_t num_sources;
> +    uint32_t clic_size;
> +    uint32_t clic_mmode_base;
> +    uint32_t clicintctlbits;
> +    uint64_t mclicbase;
> +    char *version;
> +
> +    /* Global configuration */
> +    uint8_t nmbits;
> +    uint8_t nlbits;
> +    uint8_t nvbits;
> +    uint32_t clicinfo;
> +    uint32_t clicinttrig[32];
> +
> +    /* Aperture configuration */
> +    uint8_t *clicintip;
> +    uint8_t *clicintie;
> +    uint8_t *clicintattr;
> +    uint8_t *clicintctl;
> +
> +    /* Complatible with v0.8 */
> +    uint32_t mintthresh;
> +    uint32_t sintthresh;
> +    uint32_t uintthresh;
> +
> +    /* QEMU implementaion related fields */
> +    uint32_t *exccode;
> +    CLICActiveInterrupt *active_list;
> +    size_t *active_count;
> +    MemoryRegion mmio;
> +    qemu_irq *cpu_irqs;
> +} RISCVCLICState;
> +
> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
> +                               uint32_t num_harts, 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 mode, int hartid, int
> irq);
> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int
> irq);
> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int
> irq);
> +void riscv_clic_get_next_interrupt(void *opaque, int hartid);
> +bool riscv_clic_is_clic_mode(CPURISCVState *env);
> +#endif
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index a5eab26a69..9e389d7bbf 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -33,6 +33,7 @@
>  #define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
>  #define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
>  #define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
> +#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0
>
>  #define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
>  #define TYPE_RISCV_CPU_BASE32           RISCV_CPU_TYPE_NAME("rv32")
> @@ -247,6 +248,7 @@ struct CPURISCVState {
>      /* Fields from here on are preserved across CPU reset. */
>      QEMUTimer *timer; /* Internal timer */
>      void *clic;       /* clic interrupt controller */
> +    uint32_t exccode; /* clic irq encode */
>  };
>
>  OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
> --
> 2.25.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 52021 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-04-09  7:48 ` [RFC PATCH 03/11] hw/intc: Add CLIC device LIU Zhiwei
                     ` (2 preceding siblings ...)
  2021-06-26 15:03   ` Frank Chang
@ 2021-06-26 15:20   ` Frank Chang
  2021-06-29  2:50     ` LIU Zhiwei
  2021-06-26 17:15   ` Frank Chang
                     ` (3 subsequent siblings)
  7 siblings, 1 reply; 81+ messages in thread
From: Frank Chang @ 2021-06-26 15:20 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 39792 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:57寫道:

> 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>
> ---
>  default-configs/devices/riscv32-softmmu.mak |   1 +
>  default-configs/devices/riscv64-softmmu.mak |   1 +
>  hw/intc/Kconfig                             |   3 +
>  hw/intc/meson.build                         |   1 +
>  hw/intc/riscv_clic.c                        | 835 ++++++++++++++++++++
>  include/hw/intc/riscv_clic.h                | 103 +++
>  target/riscv/cpu.h                          |   2 +
>  7 files changed, 946 insertions(+)
>  create mode 100644 hw/intc/riscv_clic.c
>  create mode 100644 include/hw/intc/riscv_clic.h
>
> diff --git a/default-configs/devices/riscv32-softmmu.mak
> b/default-configs/devices/riscv32-softmmu.mak
> index d847bd5692..1430c30588 100644
> --- a/default-configs/devices/riscv32-softmmu.mak
> +++ b/default-configs/devices/riscv32-softmmu.mak
> @@ -5,6 +5,7 @@
>  #CONFIG_PCI_DEVICES=n
>  CONFIG_SEMIHOSTING=y
>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> +CONFIG_RISCV_CLIC=y
>
>  # Boards:
>  #
> diff --git a/default-configs/devices/riscv64-softmmu.mak
> b/default-configs/devices/riscv64-softmmu.mak
> index d5eec75f05..396800bbbd 100644
> --- a/default-configs/devices/riscv64-softmmu.mak
> +++ b/default-configs/devices/riscv64-softmmu.mak
> @@ -5,6 +5,7 @@
>  #CONFIG_PCI_DEVICES=n
>  CONFIG_SEMIHOSTING=y
>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> +CONFIG_RISCV_CLIC=y
>
>  # Boards:
>  #
> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
> index f4694088a4..5bf492b48f 100644
> --- a/hw/intc/Kconfig
> +++ b/hw/intc/Kconfig
> @@ -68,6 +68,9 @@ config SIFIVE_CLINT
>  config SIFIVE_PLIC
>      bool
>
> +config RISCV_CLIC
> +    bool
> +
>  config GOLDFISH_PIC
>      bool
>
> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> index 1c299039f6..2aa71b6738 100644
> --- a/hw/intc/meson.build
> +++ b/hw/intc/meson.build
> @@ -50,6 +50,7 @@ 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_SIFIVE_CLINT', if_true:
> files('sifive_clint.c'))
>  specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true:
> files('sifive_plic.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'))
> diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
> new file mode 100644
> index 0000000000..8ad534c506
> --- /dev/null
> +++ b/hw/intc/riscv_clic.c
> @@ -0,0 +1,835 @@
> +/*
> + * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
> + *
> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
> + *
> + * 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/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/log.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"
> +
> +/*
> + * 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_offset)
> +{
> +    return (clic->clicintattr[irq_offset] >> 1) & 0x3;
> +}
> +
> +static inline bool
> +riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
> +{
> +    return (clic->clicintattr[irq_offset] >> 1) & 0x1;
> +}
> +
> +static inline bool
> +riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
> +{
> +    return (clic->clicintattr[irq_offset] & 0x1) && clic->nvbits;
> +}
> +
> +static uint8_t
> +riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
> +{
> +    int nlbits = clic->nlbits;
> +
> +    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->nlbits;
> +    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);
> +}
> +
> +/*
> + * In a system with multiple harts, the M-mode CLIC regions for all the
> harts
> + * are placed contiguously in the memory space, followed by the S-mode
> CLIC
> + * regions for all harts. (Section 3.11)
> + */
> +static size_t
> +riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int hartid, int
> irq)
> +{
> +    size_t mode_offset = 0;
> +    size_t unit = clic->num_harts * clic->num_sources;
> +
> +    switch (mode) {
> +    case PRV_M:
> +        mode_offset = 0;
> +        break;
> +    case PRV_S:
> +        mode_offset = unit;
> +        break;
> +    case PRV_U:
> +        mode_offset = clic->prv_s ? 2 * unit : unit;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid mode %d\n", mode);
> +        exit(1);
> +    }
> +    return mode_offset + hartid * clic->num_sources + irq;
> +}
> +
> +static void riscv_clic_next_interrupt(void *opaque, int hartid)
> +{
> +    /*
> +     * 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
> +     */
> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(hartid));
> +    CPURISCVState *env = &cpu->env;
> +    RISCVCLICState *clic = (RISCVCLICState *)opaque;
> +
> +    int il[4] = {
> +        MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
> +            clic->mintthresh), /* PRV_U */
> +        MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
> +            clic->sintthresh), /* PRV_S */
> +        0,                     /* reserverd */
> +        MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
> +            clic->uintthresh)  /* PRV_M */
> +    };
> +
> +    /* Get sorted list of enabled interrupts for this hart */
> +    size_t hart_offset = hartid * clic->num_sources;
> +    CLICActiveInterrupt *active = &clic->active_list[hart_offset];
> +    size_t active_count = clic->active_count[hartid];
> +    uint8_t mode, level, priority;
> +
> +    /* Loop through the enabled interrupts sorted by mode+priority+level
> */
> +    while (active_count) {
> +        size_t irq_offset;
> +        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;
> +        }
> +        irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> active->irq);
> +        /* Check pending interrupt with high enough mode+priority+level */
> +        if (clic->clicintip[irq_offset]) {
> +            /* Clean vector edge-triggered pending */
> +            if (riscv_clic_is_edge_triggered(clic, irq_offset) &&
> +                riscv_clic_is_shv_interrupt(clic, irq_offset)) {
> +                clic->clicintip[irq_offset] = 0;
> +            }
> +            /* Post pending interrupt for this hart */
> +            clic->exccode[hartid] = active->irq | mode << 12 | level <<
> 14;
> +            qemu_set_irq(clic->cpu_irqs[hartid], 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 hartid, int
> irq)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    if (!clic->prv_s && !clic->prv_u) { /* M */
> +        return mode == PRV_M;
> +    } else if (!clic->prv_s) { /* M/U */
> +        switch (clic->nmbits) {
> +        case 0:
> +            return mode == PRV_M;
> +        case 1:
> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M)
> :
> +                                                          (mode == PRV_U);
> +        default:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "clic: nmbits can only be 0 or 1 for M/U hart");
> +            exit(1);
> +        }
> +    } else { /* M/S/U */
> +        switch (clic->nmbits) {
> +        case 0:
> +            return mode == PRV_M;
> +        case 1:
> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M)
> :
> +                                                          (mode == PRV_S);
> +        case 2:
> +            return mode == clic->clicintattr[irq_offset];
> +        case 3:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "clic: nmbits can only be 0 or 1 or 2 for M/S/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 mode, int hartid, int
> irq)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
> +}
> +
> +static void
> +riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
> +                        int irq, uint64_t value)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    clic->clicintip[irq_offset] = !!value;
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +/*
> + * 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, uint64_t
> value)
> +{
> +    int mode = extract64(value, 6, 2);
> +
> +    if (!qtest_enabled()) {
> +        CPURISCVState *env = current_cpu->env_ptr;
> +        if (env->priv < mode) {
> +            return false;
> +        }
> +    }
> +    return true;
> +}
> +
> +static inline int riscv_clic_encode_priority(const CLICActiveInterrupt *i)
> +{
> +    return ((i->intcfg & 0x3ff) << 12) | /* Highest mode+level+priority */
> +           (i->irq & 0xfff);             /* Highest irq number */
> +}
> +
> +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 hartid,
> +                        int irq, uint64_t new_intie)
> +{
> +    size_t hart_offset = hartid * clic->num_sources;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    CLICActiveInterrupt *active_list = &clic->active_list[hart_offset];
> +    size_t *active_count = &clic->active_count[hartid];
> +
> +    uint8_t old_intie = clic->clicintie[irq_offset];
> +    clic->clicintie[irq_offset] = !!new_intie;
> +
> +    /* Add to or remove from list of active interrupts */
> +    if (new_intie && !old_intie) {
> +        active_list[*active_count].intcfg = (mode << 8) |
> +                                            clic->clicintctl[irq_offset];
> +        active_list[*active_count].irq = irq;
> +        (*active_count)++;
> +    } else if (!new_intie && old_intie) {
> +        CLICActiveInterrupt key = {
> +            (mode << 8) | clic->clicintctl[irq_offset], irq
> +        };
> +        CLICActiveInterrupt *result = bsearch(&key,
> +                                              active_list, *active_count,
> +                                              sizeof(CLICActiveInterrupt),
> +                                              riscv_clic_active_compare);
> +        size_t elem = (result - active_list) /
> sizeof(CLICActiveInterrupt);
> +        size_t sz = (--(*active_count) - elem) *
> sizeof(CLICActiveInterrupt);
> +        assert(result);
> +        memmove(&result[0], &result[1], sz);
> +    }
> +
> +    /* Sort list of active interrupts */
> +    qsort(active_list, *active_count,
> +          sizeof(CLICActiveInterrupt),
> +          riscv_clic_active_compare);
> +
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +static void
> +riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
> +                      uint64_t value, unsigned size,
> +                      int mode, int hartid, int irq)
> +{
> +    int req = extract32(addr, 0, 2);
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +
> +    if (hartid >= clic->num_harts) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
> +                      hartid, addr);
> +        return;
> +    }
> +
> +    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, mode, hartid, irq)) {
> +            /*
> +             * The actual pending bit is located at bit 0 (i.e., the
> +             * leastsignificant 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_offset]) {
> +                riscv_clic_update_intip(clic, mode, hartid, irq, value);
> +            }
> +        }
> +        break;
> +    case 1: /* clicintie[i] */
> +        if (clic->clicintie[irq_offset] != value) {
> +            riscv_clic_update_intie(clic, mode, hartid, irq, value);
> +        }
> +        break;
> +    case 2: /* clicintattr[i] */
> +        if (riscv_clic_validate_intattr(clic, value)) {
> +            if (clic->clicintattr[irq_offset] != value) {
> +                /* When nmbits=2, check WARL */
> +                bool invalid = (clic->nmbits == 2) &&
> +                               (extract64(value, 6, 2) == 0b10);
> +                if (invalid) {
> +                    uint8_t old_mode =
> extract32(clic->clicintattr[irq_offset],
> +                                                 6, 2);
> +                    value = deposit32(value, 6, 2, old_mode);
> +                }
> +                clic->clicintattr[irq_offset] = value;
> +                riscv_clic_next_interrupt(clic, hartid);
> +            }
> +        }
> +        break;
> +    case 3: /* clicintctl[i] */
> +        if (value != clic->clicintctl[irq_offset]) {
> +            clic->clicintctl[irq_offset] = value;
> +            riscv_clic_next_interrupt(clic, hartid);
> +        }
> +        break;
> +    }
> +}
> +
> +static uint64_t
> +riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
> +                     int hartid, int irq)
> +{
> +    int req = extract32(addr, 0, 2);
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +
> +    if (hartid >= clic->num_harts) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
> +                      hartid, addr);
> +        return 0;
> +    }
> +
> +    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] */
> +        return clic->clicintip[irq_offset];
> +    case 1: /* clicintie[i] */
> +        return clic->clicintie[irq_offset];
> +    case 2: /* clicintattr[i] */
> +        /*
> +         * clicintattr register layout
> +         * Bits Field
> +         * 7:6 mode
> +         * 5:3 reserved (WPRI 0)
> +         * 2:1 trig
> +         * 0 shv
> +         */
> +        return clic->clicintattr[irq_offset] & ~0x38;
> +    case 3: /* clicintctrl */
> +        /*
> +         * 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_offset] |
> +               ((1 << (8 - clic->clicintctlbits)) - 1);
> +    }
> +
> +    return 0;
> +}
> +
> +/* Return target interrupt mode */
> +static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int mode = addr / (4 * clic->num_harts * clic->num_sources);
> +    switch (mode) {
> +    case 0:
> +        return PRV_M;
> +    case 1:
> +        assert(clic->prv_s || clic->prv_u);
> +        return clic->prv_s ? PRV_S : PRV_U;
> +    case 2:
> +        assert(clic->prv_s && clic->prv_u);
> +        return PRV_U;
> +    default:
> +        g_assert_not_reached();
> +        break;
> +    }
> +}
> +
> +/* Return target hart id */
> +static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int mode_unit = 4 * clic->num_harts * clic->num_sources;
> +    int hart_unit = 4 * clic->num_sources;
> +
> +    return (addr % mode_unit) / hart_unit;
> +}
> +
> +/* Return target interrupt number */
> +static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int hart_unit = 4 * clic->num_sources;
> +    return (addr % hart_unit) / 4;
> +}
> +
> +static void
> +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
> +{
> +    RISCVCLICState *clic = opaque;
> +    hwaddr clic_size = clic->clic_size;
> +    int hartid, mode, irq;
> +
> +    if (addr < clic_size) {
> +        if (addr < 0x1000) {
> +            assert(addr % 4 == 0);
> +            int index = addr / 4;
> +            switch (index) {
> +            case 0: /* cliccfg */
> +                {
> +                    uint8_t nlbits = extract32(value, 1, 4);
> +                    uint8_t nmbits = extract32(value, 5, 2);
> +
> +                    /*
> +                     * The 4-bit cliccfg.nlbits WARL field.
> +                     * Valid values are 0—8.
> +                     */
> +                    if (nlbits <= 8) {
> +                        clic->nlbits = nlbits;
> +                    }
> +                    /* Valid values are given by implemented priviledges
> */
> +                    if (clic->prv_s && clic->prv_u) {
> +                        if (nmbits <= 2) {
> +                            clic->nmbits = nmbits;
> +                        }
> +                    } else if (clic->prv_u) {
> +                        if (nmbits <= 1) {
> +                            clic->nmbits = nmbits;
> +                        }
> +                    } else {
> +                        assert(!clic->prv_s);
> +                        if (nmbits == 0) {
> +                            clic->nmbits = 0;
> +                        }
> +                    }
> +                    clic->nvbits = extract32(value, 0, 1);
> +                    break;
> +                }
> +            case 1: /* clicinfo, read-only register */
> +                qemu_log_mask(LOG_GUEST_ERROR,
> +                              "clic: write read-only clicinfo.\n");
> +                break;
> +            case 0x10 ... 0x2F: /* clicinttrig */
> +                {
> +                    uint32_t interrupt_number = value &
> MAKE_64BIT_MASK(0, 13);
> +                    if (interrupt_number <= clic->num_sources) {
> +                        value &= ~MAKE_64BIT_MASK(13, 18);
> +                        clic->clicinttrig[index - 0x10] = value;
> +                    }
> +                    break;
> +                }
> +            case 2: /* mintthresh */
> +                if (!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 -= 0x1000;
> +            hartid = riscv_clic_get_hartid(clic, addr);
> +            mode = riscv_clic_get_mode(clic, addr);
> +            irq = riscv_clic_get_irq(clic, addr);
> +
> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
> +                riscv_clic_hart_write(clic, addr, value, size, mode,
> +                                      hartid, irq);
> +            }
> +        }
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid write: 0x%" HWADDR_PRIx "\n", addr);
> +    }
> +}
> +
> +static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    RISCVCLICState *clic = opaque;
> +    hwaddr clic_size = clic->clic_size;
> +    int hartid, mode, irq;
> +
> +    if (addr < clic_size) {
> +        if (addr < 0x1000) {
> +            assert(addr % 4 == 0);
> +            int index = addr / 4;
> +            switch (index) {
> +            case 0: /* cliccfg */
> +                return clic->nvbits |
> +                       (clic->nlbits << 1) |
> +                       (clic->nmbits << 5);
> +            case 1: /* clicinfo */
> +                /*
> +                 * clicinfo register layout
> +                 *
> +                 * Bits Field
> +                 * 31 reserved (WARL 0)
> +                 * 30:25 num_trigger
> +                 * 24:21 CLICINTCTLBITS
> +                 * 20:13 version (for version control)
> +                 * 12:0 num_interrupt
> +                 */
> +                return clic->clicinfo & ~INT32_MAX;
> +            case 0x10 ... 0x2F: /* clicinttrig */
> +                /*
> +                 * clicinttrig register layout
> +                 *
> +                 * Bits Field
> +                 * 31 enable
> +                 * 30:13 reserved (WARL 0)
> +                 * 12:0 interrupt_number
> +                 */
> +                return clic->clicinttrig[index - 0x10] &
> +                       ~MAKE_64BIT_MASK(13, 18);
> +            case 2: /* mintthresh */
> +                if (!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 -= 0x1000;
> +            hartid = riscv_clic_get_hartid(clic, addr);
> +            mode = riscv_clic_get_mode(clic, addr);
> +            irq = riscv_clic_get_irq(clic, addr);
> +
> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
> +                return riscv_clic_hart_read(clic, addr, mode, hartid,
> irq);
> +            }
> +        }
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid read: 0x%" HWADDR_PRIx "\n", addr);
> +    }
> +    return 0;
> +}
> +
> +static void riscv_clic_set_irq(void *opaque, int id, int level)
> +{
> +    RISCVCLICState *clic = opaque;
> +    int irq, hartid, mode;
> +    hwaddr addr = 4 * id;
> +    TRIG_TYPE type;
> +
> +    hartid = riscv_clic_get_hartid(clic, addr);
> +    mode = riscv_clic_get_mode(clic, addr);
> +    irq = riscv_clic_get_irq(clic, addr);
> +    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, mode, hartid, irq, level);
> +            break;
> +        case NEG_LEVEL:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
> +            break;
> +        case NEG_EDGE:
> +            break;
> +        }
> +    } else {
> +        switch (type) {
> +        case POSITIVE_LEVEL:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, level);
> +            break;
> +        case POSITIVE_EDGE:
> +            break;
> +        case NEG_LEVEL:
> +        case NEG_EDGE:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
> +            break;
> +        }
> +    }
> +}
> +
> +static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level)
> +{
> +    CPURISCVState *env = (CPURISCVState *)opaque;
> +    RISCVCLICState *clic = env->clic;
> +    CPUState *cpu = env_cpu(env);
> +
> +    if (level) {
> +        env->exccode = clic->exccode[cpu->cpu_index];
> +        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 harts_x_sources = clic->num_harts * clic->num_sources;
> +    int irqs, i;
> +
> +    if (clic->prv_s && clic->prv_u) {
> +        irqs = 3 * harts_x_sources;
> +    } else if (clic->prv_s || clic->prv_u) {
> +        irqs = 2 * harts_x_sources;
> +    } else {
> +        irqs = harts_x_sources;
> +    }
> +
> +    clic->clic_size = irqs * 4 + 0x1000;
> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops, clic,
> +                          TYPE_RISCV_CLIC, clic->clic_size);
> +
> +    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);
> +    clic->active_count = g_new0(size_t, clic->num_harts);
> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
> +
> +    /* Allocate irq 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_irqs, clic->num_harts);
> +
> +    for (i = 0; i < clic->num_harts; i++) {
> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
> +                                         &cpu->env, 1);
> +        qdev_connect_gpio_out(dev, i, irq);
> +        cpu->env.clic = clic;
> +    }
> +}
> +
> +static Property riscv_clic_properties[] = {
> +    DEFINE_PROP_BOOL("prv-s", RISCVCLICState, prv_s, false),
> +    DEFINE_PROP_BOOL("prv-u", RISCVCLICState, prv_u, false),
> +    DEFINE_PROP_UINT32("num-harts", RISCVCLICState, num_harts, 0),
> +    DEFINE_PROP_UINT32("num-sources", RISCVCLICState, num_sources, 0),
> +    DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICState, clicintctlbits,
> 0),
> +    DEFINE_PROP_UINT64("mclicbase", RISCVCLICState, mclicbase, 0),
> +    DEFINE_PROP_STRING("version", RISCVCLICState, version),
> +    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 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 void riscv_clic_register_types(void)
> +{
> +    type_register_static(&riscv_clic_info);
> +}
> +
> +type_init(riscv_clic_register_types)
> +
> +/*
> + * riscv_clic_create:
> + *
> + * @addr: base address of M-Mode CLIC memory-mapped registers
> + * @prv_s: have smode region
> + * @prv_u: have umode region
> + * @num_harts: number of CPU harts
> + * @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"
> + *
> + * Returns: the device object
> + */
> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
> +                               uint32_t num_harts, uint32_t num_sources,
> +                               uint8_t clicintctlbits,
> +                               const char *version)
> +{
> +    DeviceState *dev = qdev_new(TYPE_RISCV_CLIC);
> +
> +    assert(num_sources <= 4096);
> +    assert(num_harts <= 1024);
> +    assert(clicintctlbits <= 8);
> +    assert(!strcmp(version, "v0.8") || !strcmp(version, "v0.9"));
> +
> +    qdev_prop_set_bit(dev, "prv-s", prv_s);
> +    qdev_prop_set_bit(dev, "prv-u", prv_u);
> +    qdev_prop_set_uint32(dev, "num-harts", num_harts);
> +    qdev_prop_set_uint32(dev, "num-sources", num_sources);
> +    qdev_prop_set_uint32(dev, "clicintctlbits", clicintctlbits);
> +    qdev_prop_set_uint64(dev, "mclicbase", addr);
>

According to spec:
  Since the CLIC memory map must be aligned at a 4KiB boundary,
  the mclicbase CSR has its 12 least-significant bits hardwired to zero.
  It is used to inform software about the location of CLIC memory mappped
registers.

I think it's better to add another addr check to ensure it's 4KiB aligned.

Thanks,
Frank Chang


> +    qdev_prop_set_string(dev, "version", version);
> +
> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
> +    return dev;
> +}
> +
> +void riscv_clic_get_next_interrupt(void *opaque, int hartid)
> +{
> +    RISCVCLICState *clic = opaque;
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    return riscv_clic_is_shv_interrupt(clic, irq_offset);
> +}
> +
> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int
> irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
> +}
> +
> +void riscv_clic_clean_pending(void *opaque, int mode, int hartid, int irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    clic->clicintip[irq_offset] = 0;
> +}
> +
> +/*
> + * 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.
> + */
> +bool riscv_clic_is_clic_mode(CPURISCVState *env)
> +{
> +    target_ulong xtvec = (env->priv == PRV_M) ? env->mtvec : env->stvec;
> +    return env->clic && ((xtvec & 0x3) == 3);
> +}
> +
> +void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
> +                               int *il, int *irq)
> +{
> +    *irq = extract32(exccode, 0, 12);
> +    *mode = extract32(exccode, 12, 2);
> +    *il = extract32(exccode, 14, 8);
> +}
> diff --git a/include/hw/intc/riscv_clic.h b/include/hw/intc/riscv_clic.h
> new file mode 100644
> index 0000000000..e5f89672a6
> --- /dev/null
> +++ b/include/hw/intc/riscv_clic.h
> @@ -0,0 +1,103 @@
> +/*
> + * RISC-V CLIC(Core Local Interrupt Controller) interface.
> + *
> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
> + *
> + * 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/>.
> + */
> +
> +#ifndef RISCV_CLIC_H
> +#define RISCV_CLIC_H
> +
> +#include "hw/irq.h"
> +#include "hw/sysbus.h"
> +
> +#define TYPE_RISCV_CLIC "riscv_clic"
> +#define RISCV_CLIC(obj) \
> +    OBJECT_CHECK(RISCVCLICState, (obj), TYPE_RISCV_CLIC)
> +
> +/*
> + * 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;
> +
> +typedef struct RISCVCLICState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +
> +    /* Implementaion parameters */
> +    bool prv_s;
> +    bool prv_u;
> +    uint32_t num_harts;
> +    uint32_t num_sources;
> +    uint32_t clic_size;
> +    uint32_t clic_mmode_base;
> +    uint32_t clicintctlbits;
> +    uint64_t mclicbase;
> +    char *version;
> +
> +    /* Global configuration */
> +    uint8_t nmbits;
> +    uint8_t nlbits;
> +    uint8_t nvbits;
> +    uint32_t clicinfo;
> +    uint32_t clicinttrig[32];
> +
> +    /* Aperture configuration */
> +    uint8_t *clicintip;
> +    uint8_t *clicintie;
> +    uint8_t *clicintattr;
> +    uint8_t *clicintctl;
> +
> +    /* Complatible with v0.8 */
> +    uint32_t mintthresh;
> +    uint32_t sintthresh;
> +    uint32_t uintthresh;
> +
> +    /* QEMU implementaion related fields */
> +    uint32_t *exccode;
> +    CLICActiveInterrupt *active_list;
> +    size_t *active_count;
> +    MemoryRegion mmio;
> +    qemu_irq *cpu_irqs;
> +} RISCVCLICState;
> +
> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
> +                               uint32_t num_harts, 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 mode, int hartid, int
> irq);
> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int
> irq);
> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int
> irq);
> +void riscv_clic_get_next_interrupt(void *opaque, int hartid);
> +bool riscv_clic_is_clic_mode(CPURISCVState *env);
> +#endif
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index a5eab26a69..9e389d7bbf 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -33,6 +33,7 @@
>  #define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
>  #define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
>  #define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
> +#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0
>
>  #define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
>  #define TYPE_RISCV_CPU_BASE32           RISCV_CPU_TYPE_NAME("rv32")
> @@ -247,6 +248,7 @@ struct CPURISCVState {
>      /* Fields from here on are preserved across CPU reset. */
>      QEMUTimer *timer; /* Internal timer */
>      void *clic;       /* clic interrupt controller */
> +    uint32_t exccode; /* clic irq encode */
>  };
>
>  OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
> --
> 2.25.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 47295 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-06-26 15:03   ` Frank Chang
@ 2021-06-26 15:26     ` Frank Chang
  2021-06-29  2:52       ` LIU Zhiwei
  2021-06-29  2:43     ` LIU Zhiwei
  1 sibling, 1 reply; 81+ messages in thread
From: Frank Chang @ 2021-06-26 15:26 UTC (permalink / raw)
  To: Frank Chang
  Cc: open list:RISC-V, qemu-devel@nongnu.org Developers, wxy194768,
	Palmer Dabbelt, Alistair Francis, LIU Zhiwei

[-- Attachment #1: Type: text/plain, Size: 43999 bytes --]

Frank Chang <frank.chang@sifive.com> 於 2021年6月26日 週六 下午11:03寫道:

> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:57寫道:
>
>> 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>
>> ---
>>  default-configs/devices/riscv32-softmmu.mak |   1 +
>>  default-configs/devices/riscv64-softmmu.mak |   1 +
>>  hw/intc/Kconfig                             |   3 +
>>  hw/intc/meson.build                         |   1 +
>>  hw/intc/riscv_clic.c                        | 835 ++++++++++++++++++++
>>  include/hw/intc/riscv_clic.h                | 103 +++
>>  target/riscv/cpu.h                          |   2 +
>>  7 files changed, 946 insertions(+)
>>  create mode 100644 hw/intc/riscv_clic.c
>>  create mode 100644 include/hw/intc/riscv_clic.h
>>
>> diff --git a/default-configs/devices/riscv32-softmmu.mak
>> b/default-configs/devices/riscv32-softmmu.mak
>> index d847bd5692..1430c30588 100644
>> --- a/default-configs/devices/riscv32-softmmu.mak
>> +++ b/default-configs/devices/riscv32-softmmu.mak
>> @@ -5,6 +5,7 @@
>>  #CONFIG_PCI_DEVICES=n
>>  CONFIG_SEMIHOSTING=y
>>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>> +CONFIG_RISCV_CLIC=y
>>
>>  # Boards:
>>  #
>> diff --git a/default-configs/devices/riscv64-softmmu.mak
>> b/default-configs/devices/riscv64-softmmu.mak
>> index d5eec75f05..396800bbbd 100644
>> --- a/default-configs/devices/riscv64-softmmu.mak
>> +++ b/default-configs/devices/riscv64-softmmu.mak
>> @@ -5,6 +5,7 @@
>>  #CONFIG_PCI_DEVICES=n
>>  CONFIG_SEMIHOSTING=y
>>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>> +CONFIG_RISCV_CLIC=y
>>
>>  # Boards:
>>  #
>> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
>> index f4694088a4..5bf492b48f 100644
>> --- a/hw/intc/Kconfig
>> +++ b/hw/intc/Kconfig
>> @@ -68,6 +68,9 @@ config SIFIVE_CLINT
>>  config SIFIVE_PLIC
>>      bool
>>
>> +config RISCV_CLIC
>> +    bool
>> +
>>  config GOLDFISH_PIC
>>      bool
>>
>> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
>> index 1c299039f6..2aa71b6738 100644
>> --- a/hw/intc/meson.build
>> +++ b/hw/intc/meson.build
>> @@ -50,6 +50,7 @@ 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_SIFIVE_CLINT', if_true:
>> files('sifive_clint.c'))
>>  specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true:
>> files('sifive_plic.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'))
>> diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
>> new file mode 100644
>> index 0000000000..8ad534c506
>> --- /dev/null
>> +++ b/hw/intc/riscv_clic.c
>> @@ -0,0 +1,835 @@
>> +/*
>> + * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
>> + *
>> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
>> + *
>> + * 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/>.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qapi/error.h"
>> +#include "qemu/log.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"
>> +
>> +/*
>> + * 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_offset)
>> +{
>> +    return (clic->clicintattr[irq_offset] >> 1) & 0x3;
>> +}
>> +
>> +static inline bool
>> +riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
>> +{
>> +    return (clic->clicintattr[irq_offset] >> 1) & 0x1;
>> +}
>> +
>> +static inline bool
>> +riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
>> +{
>> +    return (clic->clicintattr[irq_offset] & 0x1) && clic->nvbits;
>> +}
>> +
>> +static uint8_t
>> +riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
>> +{
>> +    int nlbits = clic->nlbits;
>> +
>> +    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;
>> +}
>>
>
> According to spec:
>   if the nlbits > CLICINTCTLBITS, then the lower bits of the 8-bit
>   interrupt level are assumed to be all 1s.
>
> That is, the valid nlbits should be: min(clic->nlbits, CLICINTCTLBITS);
> The cliccfg example in spec also shows that:
>
> CLICINTCTLBITS  nlbits  clicintctl[i]  interrupt levels
>       0                       2         ........         255
>       1                       2         l.......         127,255
>       2                       2         ll......         63,127,191,255
>       3                       3         lll.....
>  31,63,95,127,159,191,223,255
>       4                       1         lppp....      127,255
>
>
>> +
>> +static uint8_t
>> +riscv_clic_get_interrupt_priority(RISCVCLICState *clic, uint8_t intctl)
>> +{
>> +    int npbits = clic->clicintctlbits - clic->nlbits;
>> +    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);
>> +}
>> +
>> +/*
>> + * In a system with multiple harts, the M-mode CLIC regions for all the
>> harts
>> + * are placed contiguously in the memory space, followed by the S-mode
>> CLIC
>> + * regions for all harts. (Section 3.11)
>> + */
>>
>
> The above description is not true any more in the latest spec:
>   The CLIC specification does not dictate how CLIC memory-mapped registers
> are
>   split between M/S/U regions as well as the layout of multiple harts as
> this is generally
>   a platform issue and each platform needs to define a discovery mechanism
> to determine
>   the memory map locations.
>

Comparing to 20210217 spec, the original description you wrote is correct.
But it's sad that the latest change is still in v0.9-draft, no version is
bumped.

Minor:
I think most of the description in comments may need to be updated
if you want to update to the latest v0.9-draft spec.
Do you have any plans to follow the latest changes?

Regards,
Frank Chang


>
> But I think we can just keep the current design for now anyway, as it's
> also one of legal memory layout.
> Otherwise, the design would be more complicated I think.
>

> +static size_t
>> +riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int hartid,
>> int irq)
>> +{
>> +    size_t mode_offset = 0;
>> +    size_t unit = clic->num_harts * clic->num_sources;
>> +
>> +    switch (mode) {
>> +    case PRV_M:
>> +        mode_offset = 0;
>> +        break;
>> +    case PRV_S:
>> +        mode_offset = unit;
>> +        break;
>> +    case PRV_U:
>> +        mode_offset = clic->prv_s ? 2 * unit : unit;
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid mode %d\n", mode);
>> +        exit(1);
>> +    }
>> +    return mode_offset + hartid * clic->num_sources + irq;
>> +}
>> +
>> +static void riscv_clic_next_interrupt(void *opaque, int hartid)
>> +{
>> +    /*
>> +     * 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
>> +     */
>> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(hartid));
>> +    CPURISCVState *env = &cpu->env;
>> +    RISCVCLICState *clic = (RISCVCLICState *)opaque;
>> +
>> +    int il[4] = {
>> +        MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
>> +            clic->mintthresh), /* PRV_U */
>> +        MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
>> +            clic->sintthresh), /* PRV_S */
>> +        0,                     /* reserverd */
>> +        MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
>> +            clic->uintthresh)  /* PRV_M */
>> +    };
>> +
>> +    /* Get sorted list of enabled interrupts for this hart */
>> +    size_t hart_offset = hartid * clic->num_sources;
>> +    CLICActiveInterrupt *active = &clic->active_list[hart_offset];
>> +    size_t active_count = clic->active_count[hartid];
>> +    uint8_t mode, level, priority;
>> +
>> +    /* Loop through the enabled interrupts sorted by mode+priority+level
>> */
>> +    while (active_count) {
>> +        size_t irq_offset;
>> +        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;
>> +        }
>> +        irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> active->irq);
>> +        /* Check pending interrupt with high enough mode+priority+level
>> */
>> +        if (clic->clicintip[irq_offset]) {
>> +            /* Clean vector edge-triggered pending */
>> +            if (riscv_clic_is_edge_triggered(clic, irq_offset) &&
>> +                riscv_clic_is_shv_interrupt(clic, irq_offset)) {
>> +                clic->clicintip[irq_offset] = 0;
>> +            }
>> +            /* Post pending interrupt for this hart */
>> +            clic->exccode[hartid] = active->irq | mode << 12 | level <<
>> 14;
>> +            qemu_set_irq(clic->cpu_irqs[hartid], 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 hartid, int
>> irq)
>> +{
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    if (!clic->prv_s && !clic->prv_u) { /* M */
>> +        return mode == PRV_M;
>> +    } else if (!clic->prv_s) { /* M/U */
>> +        switch (clic->nmbits) {
>> +        case 0:
>> +            return mode == PRV_M;
>> +        case 1:
>> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode ==
>> PRV_M) :
>> +                                                          (mode ==
>> PRV_U);
>> +        default:
>> +            qemu_log_mask(LOG_GUEST_ERROR,
>> +                "clic: nmbits can only be 0 or 1 for M/U hart");
>> +            exit(1);
>> +        }
>> +    } else { /* M/S/U */
>> +        switch (clic->nmbits) {
>> +        case 0:
>> +            return mode == PRV_M;
>> +        case 1:
>> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode ==
>> PRV_M) :
>> +                                                          (mode ==
>> PRV_S);
>> +        case 2:
>> +            return mode == clic->clicintattr[irq_offset];
>> +        case 3:
>> +            qemu_log_mask(LOG_GUEST_ERROR,
>> +                "clic: nmbits can only be 0 or 1 or 2 for M/S/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 mode, int hartid,
>> int irq)
>> +{
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
>> +}
>> +
>> +static void
>> +riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
>> +                        int irq, uint64_t value)
>> +{
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    clic->clicintip[irq_offset] = !!value;
>> +    riscv_clic_next_interrupt(clic, hartid);
>> +}
>> +
>> +/*
>> + * 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, uint64_t
>> value)
>> +{
>> +    int mode = extract64(value, 6, 2);
>> +
>> +    if (!qtest_enabled()) {
>> +        CPURISCVState *env = current_cpu->env_ptr;
>> +        if (env->priv < mode) {
>> +            return false;
>> +        }
>> +    }
>> +    return true;
>> +}
>> +
>> +static inline int riscv_clic_encode_priority(const CLICActiveInterrupt
>> *i)
>> +{
>> +    return ((i->intcfg & 0x3ff) << 12) | /* Highest mode+level+priority
>> */
>> +           (i->irq & 0xfff);             /* Highest irq number */
>> +}
>> +
>> +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 hartid,
>> +                        int irq, uint64_t new_intie)
>> +{
>> +    size_t hart_offset = hartid * clic->num_sources;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    CLICActiveInterrupt *active_list = &clic->active_list[hart_offset];
>> +    size_t *active_count = &clic->active_count[hartid];
>> +
>> +    uint8_t old_intie = clic->clicintie[irq_offset];
>> +    clic->clicintie[irq_offset] = !!new_intie;
>> +
>> +    /* Add to or remove from list of active interrupts */
>> +    if (new_intie && !old_intie) {
>> +        active_list[*active_count].intcfg = (mode << 8) |
>> +                                            clic->clicintctl[irq_offset];
>> +        active_list[*active_count].irq = irq;
>> +        (*active_count)++;
>> +    } else if (!new_intie && old_intie) {
>> +        CLICActiveInterrupt key = {
>> +            (mode << 8) | clic->clicintctl[irq_offset], irq
>> +        };
>> +        CLICActiveInterrupt *result = bsearch(&key,
>> +                                              active_list, *active_count,
>> +
>> sizeof(CLICActiveInterrupt),
>> +                                              riscv_clic_active_compare);
>> +        size_t elem = (result - active_list) /
>> sizeof(CLICActiveInterrupt);
>>
>
> I think what you are trying to do here is to get the number of elements
> right after the active interrupt to be deleted in order to calculate the
> size of
> active interrupts to be memmoved.
>
> However, according to C spec:
>   When two pointers are subtracted, both shall point to elements of the
> same array object,
>   or one past the last element of the array object; the result is the
> difference of the
>   subscripts of the two array elements.
>
> So I think: (result - active_list) is already the number of elements you
> want.
> You don't have to divide it with sizeof(CLICActiveInterrupt) again.
>
>
>> +        size_t sz = (--(*active_count) - elem) *
>> sizeof(CLICActiveInterrupt);
>> +        assert(result);
>>
>
> Nit: assert(result) can be moved above size_t elem statement.
>
>
>> +        memmove(&result[0], &result[1], sz);
>> +    }
>> +
>> +    /* Sort list of active interrupts */
>> +    qsort(active_list, *active_count,
>> +          sizeof(CLICActiveInterrupt),
>> +          riscv_clic_active_compare);
>> +
>> +    riscv_clic_next_interrupt(clic, hartid);
>> +}
>> +
>> +static void
>> +riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
>> +                      uint64_t value, unsigned size,
>> +                      int mode, int hartid, int irq)
>> +{
>> +    int req = extract32(addr, 0, 2);
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +
>> +    if (hartid >= clic->num_harts) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
>> +                      hartid, addr);
>> +        return;
>> +    }
>> +
>> +    if (irq >= clic->num_sources) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid irq %u: 0x%" HWADDR_PRIx "\n", irq,
>> addr);
>> +        return;
>> +    }
>> +
>> +    switch (req) {
>>
>
> Spec. says that it's legal to write 32-bit value to set
> {clicintctl[i], clicintattr[i], clicintie[i] ,clicintip[i]} at the same
> time:
>   A 32-bit write to {clicintctl,clicintattr,clicintie,clicintip} is legal.
>   However, there is no specified order in which the effects of
>   the individual byte updates take effect.
>
>
>> +    case 0: /* clicintip[i] */
>> +        if (riscv_clic_validate_intip(clic, mode, hartid, irq)) {
>> +            /*
>> +             * The actual pending bit is located at bit 0 (i.e., the
>> +             * leastsignificant bit). In case future extensions expand
>> the bit
>>
>
> Typo: leastsignificant => least significant
>
>
>> +             * 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_offset]) {
>> +                riscv_clic_update_intip(clic, mode, hartid, irq, value);
>> +            }
>> +        }
>> +        break;
>> +    case 1: /* clicintie[i] */
>> +        if (clic->clicintie[irq_offset] != value) {
>> +            riscv_clic_update_intie(clic, mode, hartid, irq, value);
>> +        }
>> +        break;
>> +    case 2: /* clicintattr[i] */
>> +        if (riscv_clic_validate_intattr(clic, value)) {
>> +            if (clic->clicintattr[irq_offset] != value) {
>> +                /* When nmbits=2, check WARL */
>> +                bool invalid = (clic->nmbits == 2) &&
>> +                               (extract64(value, 6, 2) == 0b10);
>> +                if (invalid) {
>> +                    uint8_t old_mode =
>> extract32(clic->clicintattr[irq_offset],
>> +                                                 6, 2);
>> +                    value = deposit32(value, 6, 2, old_mode);
>> +                }
>> +                clic->clicintattr[irq_offset] = value;
>> +                riscv_clic_next_interrupt(clic, hartid);
>> +            }
>> +        }
>> +        break;
>> +    case 3: /* clicintctl[i] */
>> +        if (value != clic->clicintctl[irq_offset]) {
>> +            clic->clicintctl[irq_offset] = value;
>>
>
> If irq i is already in the active_list, when will its intcfg been synced?
>
>
>> +            riscv_clic_next_interrupt(clic, hartid);
>> +        }
>> +        break;
>> +    }
>> +}
>> +
>> +static uint64_t
>> +riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
>> +                     int hartid, int irq)
>> +{
>> +    int req = extract32(addr, 0, 2);
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +
>> +    if (hartid >= clic->num_harts) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
>> +                      hartid, addr);
>> +        return 0;
>> +    }
>> +
>> +    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] */
>> +        return clic->clicintip[irq_offset];
>> +    case 1: /* clicintie[i] */
>> +        return clic->clicintie[irq_offset];
>> +    case 2: /* clicintattr[i] */
>> +        /*
>> +         * clicintattr register layout
>> +         * Bits Field
>> +         * 7:6 mode
>> +         * 5:3 reserved (WPRI 0)
>> +         * 2:1 trig
>> +         * 0 shv
>> +         */
>> +        return clic->clicintattr[irq_offset] & ~0x38;
>> +    case 3: /* clicintctrl */
>>
>
> Typo: clicintctl
>
>
>> +        /*
>> +         * 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_offset] |
>> +               ((1 << (8 - clic->clicintctlbits)) - 1);
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +/* Return target interrupt mode */
>> +static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
>> +{
>> +    int mode = addr / (4 * clic->num_harts * clic->num_sources);
>> +    switch (mode) {
>> +    case 0:
>> +        return PRV_M;
>> +    case 1:
>> +        assert(clic->prv_s || clic->prv_u);
>> +        return clic->prv_s ? PRV_S : PRV_U;
>> +    case 2:
>> +        assert(clic->prv_s && clic->prv_u);
>> +        return PRV_U;
>> +    default:
>> +        g_assert_not_reached();
>> +        break;
>> +    }
>> +}
>> +
>> +/* Return target hart id */
>> +static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
>> +{
>> +    int mode_unit = 4 * clic->num_harts * clic->num_sources;
>> +    int hart_unit = 4 * clic->num_sources;
>> +
>> +    return (addr % mode_unit) / hart_unit;
>> +}
>> +
>> +/* Return target interrupt number */
>> +static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
>> +{
>> +    int hart_unit = 4 * clic->num_sources;
>> +    return (addr % hart_unit) / 4;
>> +}
>> +
>> +static void
>> +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned
>> size)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    hwaddr clic_size = clic->clic_size;
>> +    int hartid, mode, irq;
>> +
>> +    if (addr < clic_size) {
>>
>
> Is this necessary?
> I think memory region size already limits the request address to be within
> the range of clic_size.
>
>
>> +        if (addr < 0x1000) {
>> +            assert(addr % 4 == 0);
>> +            int index = addr / 4;
>> +            switch (index) {
>> +            case 0: /* cliccfg */
>> +                {
>> +                    uint8_t nlbits = extract32(value, 1, 4);
>> +                    uint8_t nmbits = extract32(value, 5, 2);
>> +
>> +                    /*
>> +                     * The 4-bit cliccfg.nlbits WARL field.
>> +                     * Valid values are 0—8.
>> +                     */
>> +                    if (nlbits <= 8) {
>> +                        clic->nlbits = nlbits;
>> +                    }
>> +                    /* Valid values are given by implemented priviledges
>> */
>> +                    if (clic->prv_s && clic->prv_u) {
>> +                        if (nmbits <= 2) {
>> +                            clic->nmbits = nmbits;
>> +                        }
>> +                    } else if (clic->prv_u) {
>> +                        if (nmbits <= 1) {
>> +                            clic->nmbits = nmbits;
>> +                        }
>> +                    } else {
>> +                        assert(!clic->prv_s);
>> +                        if (nmbits == 0) {
>> +                            clic->nmbits = 0;
>> +                        }
>> +                    }
>> +                    clic->nvbits = extract32(value, 0, 1);
>> +                    break;
>> +                }
>> +            case 1: /* clicinfo, read-only register */
>> +                qemu_log_mask(LOG_GUEST_ERROR,
>> +                              "clic: write read-only clicinfo.\n");
>> +                break;
>> +            case 0x10 ... 0x2F: /* clicinttrig */
>> +                {
>> +                    uint32_t interrupt_number = value &
>> MAKE_64BIT_MASK(0, 13);
>> +                    if (interrupt_number <= clic->num_sources) {
>> +                        value &= ~MAKE_64BIT_MASK(13, 18);
>> +                        clic->clicinttrig[index - 0x10] = value;
>> +                    }
>> +                    break;
>> +                }
>> +            case 2: /* mintthresh */
>> +                if (!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 -= 0x1000;
>> +            hartid = riscv_clic_get_hartid(clic, addr);
>> +            mode = riscv_clic_get_mode(clic, addr);
>> +            irq = riscv_clic_get_irq(clic, addr);
>> +
>> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
>> +                riscv_clic_hart_write(clic, addr, value, size, mode,
>> +                                      hartid, irq);
>> +            }
>> +        }
>> +    } else {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid write: 0x%" HWADDR_PRIx "\n", addr);
>> +    }
>> +}
>> +
>> +static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    hwaddr clic_size = clic->clic_size;
>> +    int hartid, mode, irq;
>> +
>> +    if (addr < clic_size) {
>>
>
> Same to riscv_clic_write().
>
> Thanks,
> Frank Chang
>
>
>> +        if (addr < 0x1000) {
>> +            assert(addr % 4 == 0);
>> +            int index = addr / 4;
>> +            switch (index) {
>> +            case 0: /* cliccfg */
>> +                return clic->nvbits |
>> +                       (clic->nlbits << 1) |
>> +                       (clic->nmbits << 5);
>> +            case 1: /* clicinfo */
>> +                /*
>> +                 * clicinfo register layout
>> +                 *
>> +                 * Bits Field
>> +                 * 31 reserved (WARL 0)
>> +                 * 30:25 num_trigger
>> +                 * 24:21 CLICINTCTLBITS
>> +                 * 20:13 version (for version control)
>> +                 * 12:0 num_interrupt
>> +                 */
>> +                return clic->clicinfo & ~INT32_MAX;
>>
>
> clic->clicinfo should represent the CLIC setting information.
> I think it's better to add clic reset function or in riscv_clic_realize()
> to initialize clic->clicinfo.
>
>
>> +            case 0x10 ... 0x2F: /* clicinttrig */
>> +                /*
>> +                 * clicinttrig register layout
>> +                 *
>> +                 * Bits Field
>> +                 * 31 enable
>> +                 * 30:13 reserved (WARL 0)
>> +                 * 12:0 interrupt_number
>> +                 */
>> +                return clic->clicinttrig[index - 0x10] &
>> +                       ~MAKE_64BIT_MASK(13, 18);
>> +            case 2: /* mintthresh */
>> +                if (!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 -= 0x1000;
>> +            hartid = riscv_clic_get_hartid(clic, addr);
>> +            mode = riscv_clic_get_mode(clic, addr);
>> +            irq = riscv_clic_get_irq(clic, addr);
>> +
>> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
>> +                return riscv_clic_hart_read(clic, addr, mode, hartid,
>> irq);
>> +            }
>> +        }
>> +    } else {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid read: 0x%" HWADDR_PRIx "\n", addr);
>> +    }
>> +    return 0;
>> +}
>> +
>> +static void riscv_clic_set_irq(void *opaque, int id, int level)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    int irq, hartid, mode;
>> +    hwaddr addr = 4 * id;
>> +    TRIG_TYPE type;
>> +
>> +    hartid = riscv_clic_get_hartid(clic, addr);
>> +    mode = riscv_clic_get_mode(clic, addr);
>> +    irq = riscv_clic_get_irq(clic, addr);
>> +    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, mode, hartid, irq, level);
>> +            break;
>> +        case NEG_LEVEL:
>> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
>> +            break;
>> +        case NEG_EDGE:
>> +            break;
>> +        }
>> +    } else {
>> +        switch (type) {
>> +        case POSITIVE_LEVEL:
>> +            riscv_clic_update_intip(clic, mode, hartid, irq, level);
>> +            break;
>> +        case POSITIVE_EDGE:
>> +            break;
>> +        case NEG_LEVEL:
>> +        case NEG_EDGE:
>> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
>> +            break;
>> +        }
>> +    }
>> +}
>> +
>> +static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level)
>> +{
>> +    CPURISCVState *env = (CPURISCVState *)opaque;
>> +    RISCVCLICState *clic = env->clic;
>> +    CPUState *cpu = env_cpu(env);
>> +
>> +    if (level) {
>> +        env->exccode = clic->exccode[cpu->cpu_index];
>> +        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 harts_x_sources = clic->num_harts * clic->num_sources;
>> +    int irqs, i;
>> +
>> +    if (clic->prv_s && clic->prv_u) {
>> +        irqs = 3 * harts_x_sources;
>> +    } else if (clic->prv_s || clic->prv_u) {
>> +        irqs = 2 * harts_x_sources;
>> +    } else {
>> +        irqs = harts_x_sources;
>> +    }
>> +
>> +    clic->clic_size = irqs * 4 + 0x1000;
>> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops,
>> clic,
>> +                          TYPE_RISCV_CLIC, clic->clic_size);
>> +
>> +    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);
>>
>
> Should the size of clic->active_list be: harts_x_sources?
>
>
>> +    clic->active_count = g_new0(size_t, clic->num_harts);
>> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
>> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
>> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
>> +
>> +    /* Allocate irq 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_irqs, clic->num_harts);
>> +
>> +    for (i = 0; i < clic->num_harts; i++) {
>> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>> +                                         &cpu->env, 1);
>> +        qdev_connect_gpio_out(dev, i, irq);
>> +        cpu->env.clic = clic;
>> +    }
>> +}
>> +
>> +static Property riscv_clic_properties[] = {
>> +    DEFINE_PROP_BOOL("prv-s", RISCVCLICState, prv_s, false),
>> +    DEFINE_PROP_BOOL("prv-u", RISCVCLICState, prv_u, false),
>> +    DEFINE_PROP_UINT32("num-harts", RISCVCLICState, num_harts, 0),
>> +    DEFINE_PROP_UINT32("num-sources", RISCVCLICState, num_sources, 0),
>> +    DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICState, clicintctlbits,
>> 0),
>> +    DEFINE_PROP_UINT64("mclicbase", RISCVCLICState, mclicbase, 0),
>> +    DEFINE_PROP_STRING("version", RISCVCLICState, version),
>> +    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 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 void riscv_clic_register_types(void)
>> +{
>> +    type_register_static(&riscv_clic_info);
>> +}
>> +
>> +type_init(riscv_clic_register_types)
>> +
>> +/*
>> + * riscv_clic_create:
>> + *
>> + * @addr: base address of M-Mode CLIC memory-mapped registers
>> + * @prv_s: have smode region
>> + * @prv_u: have umode region
>> + * @num_harts: number of CPU harts
>> + * @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"
>> + *
>> + * Returns: the device object
>> + */
>> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
>> +                               uint32_t num_harts, uint32_t num_sources,
>> +                               uint8_t clicintctlbits,
>> +                               const char *version)
>> +{
>> +    DeviceState *dev = qdev_new(TYPE_RISCV_CLIC);
>> +
>> +    assert(num_sources <= 4096);
>> +    assert(num_harts <= 1024);
>> +    assert(clicintctlbits <= 8);
>> +    assert(!strcmp(version, "v0.8") || !strcmp(version, "v0.9"));
>> +
>> +    qdev_prop_set_bit(dev, "prv-s", prv_s);
>> +    qdev_prop_set_bit(dev, "prv-u", prv_u);
>> +    qdev_prop_set_uint32(dev, "num-harts", num_harts);
>> +    qdev_prop_set_uint32(dev, "num-sources", num_sources);
>> +    qdev_prop_set_uint32(dev, "clicintctlbits", clicintctlbits);
>> +    qdev_prop_set_uint64(dev, "mclicbase", addr);
>> +    qdev_prop_set_string(dev, "version", version);
>> +
>> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
>> +    return dev;
>> +}
>> +
>> +void riscv_clic_get_next_interrupt(void *opaque, int hartid)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    riscv_clic_next_interrupt(clic, hartid);
>> +}
>> +
>> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int
>> irq)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    return riscv_clic_is_shv_interrupt(clic, irq_offset);
>> +}
>> +
>> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int
>> irq)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
>> +}
>> +
>> +void riscv_clic_clean_pending(void *opaque, int mode, int hartid, int
>> irq)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    clic->clicintip[irq_offset] = 0;
>> +}
>> +
>> +/*
>> + * 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.
>> + */
>> +bool riscv_clic_is_clic_mode(CPURISCVState *env)
>> +{
>> +    target_ulong xtvec = (env->priv == PRV_M) ? env->mtvec : env->stvec;
>> +    return env->clic && ((xtvec & 0x3) == 3);
>> +}
>> +
>> +void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
>> +                               int *il, int *irq)
>> +{
>> +    *irq = extract32(exccode, 0, 12);
>> +    *mode = extract32(exccode, 12, 2);
>> +    *il = extract32(exccode, 14, 8);
>> +}
>> diff --git a/include/hw/intc/riscv_clic.h b/include/hw/intc/riscv_clic.h
>> new file mode 100644
>> index 0000000000..e5f89672a6
>> --- /dev/null
>> +++ b/include/hw/intc/riscv_clic.h
>> @@ -0,0 +1,103 @@
>> +/*
>> + * RISC-V CLIC(Core Local Interrupt Controller) interface.
>> + *
>> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
>> + *
>> + * 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/>.
>> + */
>> +
>> +#ifndef RISCV_CLIC_H
>> +#define RISCV_CLIC_H
>> +
>> +#include "hw/irq.h"
>> +#include "hw/sysbus.h"
>> +
>> +#define TYPE_RISCV_CLIC "riscv_clic"
>> +#define RISCV_CLIC(obj) \
>> +    OBJECT_CHECK(RISCVCLICState, (obj), TYPE_RISCV_CLIC)
>> +
>> +/*
>> + * 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;
>> +
>> +typedef struct RISCVCLICState {
>> +    /*< private >*/
>> +    SysBusDevice parent_obj;
>> +
>> +    /*< public >*/
>> +
>> +    /* Implementaion parameters */
>> +    bool prv_s;
>> +    bool prv_u;
>> +    uint32_t num_harts;
>> +    uint32_t num_sources;
>> +    uint32_t clic_size;
>> +    uint32_t clic_mmode_base;
>> +    uint32_t clicintctlbits;
>> +    uint64_t mclicbase;
>> +    char *version;
>> +
>> +    /* Global configuration */
>> +    uint8_t nmbits;
>> +    uint8_t nlbits;
>> +    uint8_t nvbits;
>> +    uint32_t clicinfo;
>> +    uint32_t clicinttrig[32];
>> +
>> +    /* Aperture configuration */
>> +    uint8_t *clicintip;
>> +    uint8_t *clicintie;
>> +    uint8_t *clicintattr;
>> +    uint8_t *clicintctl;
>> +
>> +    /* Complatible with v0.8 */
>> +    uint32_t mintthresh;
>> +    uint32_t sintthresh;
>> +    uint32_t uintthresh;
>> +
>> +    /* QEMU implementaion related fields */
>> +    uint32_t *exccode;
>> +    CLICActiveInterrupt *active_list;
>> +    size_t *active_count;
>> +    MemoryRegion mmio;
>> +    qemu_irq *cpu_irqs;
>> +} RISCVCLICState;
>> +
>> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
>> +                               uint32_t num_harts, 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 mode, int hartid, int
>> irq);
>> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int
>> irq);
>> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int
>> irq);
>> +void riscv_clic_get_next_interrupt(void *opaque, int hartid);
>> +bool riscv_clic_is_clic_mode(CPURISCVState *env);
>> +#endif
>> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>> index a5eab26a69..9e389d7bbf 100644
>> --- a/target/riscv/cpu.h
>> +++ b/target/riscv/cpu.h
>> @@ -33,6 +33,7 @@
>>  #define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
>>  #define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
>>  #define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
>> +#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0
>>
>>  #define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
>>  #define TYPE_RISCV_CPU_BASE32           RISCV_CPU_TYPE_NAME("rv32")
>> @@ -247,6 +248,7 @@ struct CPURISCVState {
>>      /* Fields from here on are preserved across CPU reset. */
>>      QEMUTimer *timer; /* Internal timer */
>>      void *clic;       /* clic interrupt controller */
>> +    uint32_t exccode; /* clic irq encode */
>>  };
>>
>>  OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
>> --
>> 2.25.1
>>
>>
>>

[-- Attachment #2: Type: text/html, Size: 53272 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 09/11] target/riscv: Update CSR mclicbase in CLIC mode
  2021-04-09  7:48 ` [RFC PATCH 09/11] target/riscv: Update CSR mclicbase " LIU Zhiwei
@ 2021-06-26 15:31   ` Frank Chang
  2021-06-29  2:54     ` LIU Zhiwei
  0 siblings, 1 reply; 81+ messages in thread
From: Frank Chang @ 2021-06-26 15:31 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 1469 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:52寫道:

> The machine mode mclicbase CSR is an XLEN-bit read-only register providing
> the base address of CLIC memory mapped registers.
>
> Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
> ---
>  hw/intc/riscv_clic.c | 1 +
>  target/riscv/cpu.h   | 1 +
>  2 files changed, 2 insertions(+)
>
> diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
> index 8ad534c506..e902dd4062 100644
> --- a/hw/intc/riscv_clic.c
> +++ b/hw/intc/riscv_clic.c
> @@ -715,6 +715,7 @@ static void riscv_clic_realize(DeviceState *dev, Error
> **errp)
>                                           &cpu->env, 1);
>          qdev_connect_gpio_out(dev, i, irq);
>          cpu->env.clic = clic;
> +        cpu->env.mclicbase = clic->mclicbase;
>      }
>  }
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index b5fd796f98..b0b8565649 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -162,6 +162,7 @@ struct CPURISCVState {
>      uint32_t miclaim;
>      uint32_t mintstatus; /* clic-spec */
>      target_ulong mintthresh; /* clic-spec */
> +    target_ulong mclicbase; /* clic-spec */
>
>      target_ulong mie;
>      target_ulong mideleg;
> --
> 2.25.1
>
>
>
Is it because current spec doesn't specify the number of mclicbase CSR
(0x3??)
so you are not adding it into csr.c?

If so,

Reviewed-by: Frank Chang <frank.chang@sifive.com>

[-- Attachment #2: Type: text/html, Size: 2070 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-04-09  7:48 ` [RFC PATCH 03/11] hw/intc: Add CLIC device LIU Zhiwei
                     ` (3 preceding siblings ...)
  2021-06-26 15:20   ` Frank Chang
@ 2021-06-26 17:15   ` Frank Chang
  2021-06-26 17:19     ` Frank Chang
  2021-06-28 10:16   ` Frank Chang
                     ` (2 subsequent siblings)
  7 siblings, 1 reply; 81+ messages in thread
From: Frank Chang @ 2021-06-26 17:15 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 40683 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:57寫道:

> 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>
> ---
>  default-configs/devices/riscv32-softmmu.mak |   1 +
>  default-configs/devices/riscv64-softmmu.mak |   1 +
>  hw/intc/Kconfig                             |   3 +
>  hw/intc/meson.build                         |   1 +
>  hw/intc/riscv_clic.c                        | 835 ++++++++++++++++++++
>  include/hw/intc/riscv_clic.h                | 103 +++
>  target/riscv/cpu.h                          |   2 +
>  7 files changed, 946 insertions(+)
>  create mode 100644 hw/intc/riscv_clic.c
>  create mode 100644 include/hw/intc/riscv_clic.h
>
> diff --git a/default-configs/devices/riscv32-softmmu.mak
> b/default-configs/devices/riscv32-softmmu.mak
> index d847bd5692..1430c30588 100644
> --- a/default-configs/devices/riscv32-softmmu.mak
> +++ b/default-configs/devices/riscv32-softmmu.mak
> @@ -5,6 +5,7 @@
>  #CONFIG_PCI_DEVICES=n
>  CONFIG_SEMIHOSTING=y
>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> +CONFIG_RISCV_CLIC=y
>
>  # Boards:
>  #
> diff --git a/default-configs/devices/riscv64-softmmu.mak
> b/default-configs/devices/riscv64-softmmu.mak
> index d5eec75f05..396800bbbd 100644
> --- a/default-configs/devices/riscv64-softmmu.mak
> +++ b/default-configs/devices/riscv64-softmmu.mak
> @@ -5,6 +5,7 @@
>  #CONFIG_PCI_DEVICES=n
>  CONFIG_SEMIHOSTING=y
>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> +CONFIG_RISCV_CLIC=y
>
>  # Boards:
>  #
> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
> index f4694088a4..5bf492b48f 100644
> --- a/hw/intc/Kconfig
> +++ b/hw/intc/Kconfig
> @@ -68,6 +68,9 @@ config SIFIVE_CLINT
>  config SIFIVE_PLIC
>      bool
>
> +config RISCV_CLIC
> +    bool
> +
>  config GOLDFISH_PIC
>      bool
>
> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> index 1c299039f6..2aa71b6738 100644
> --- a/hw/intc/meson.build
> +++ b/hw/intc/meson.build
> @@ -50,6 +50,7 @@ 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_SIFIVE_CLINT', if_true:
> files('sifive_clint.c'))
>  specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true:
> files('sifive_plic.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'))
> diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
> new file mode 100644
> index 0000000000..8ad534c506
> --- /dev/null
> +++ b/hw/intc/riscv_clic.c
> @@ -0,0 +1,835 @@
> +/*
> + * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
> + *
> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
> + *
> + * 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/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/log.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"
> +
> +/*
> + * 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_offset)
> +{
> +    return (clic->clicintattr[irq_offset] >> 1) & 0x3;
> +}
> +
> +static inline bool
> +riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
> +{
> +    return (clic->clicintattr[irq_offset] >> 1) & 0x1;
> +}
> +
> +static inline bool
> +riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
> +{
> +    return (clic->clicintattr[irq_offset] & 0x1) && clic->nvbits;
> +}
> +
> +static uint8_t
> +riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
> +{
> +    int nlbits = clic->nlbits;
> +
> +    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->nlbits;
> +    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);
> +}
> +
> +/*
> + * In a system with multiple harts, the M-mode CLIC regions for all the
> harts
> + * are placed contiguously in the memory space, followed by the S-mode
> CLIC
> + * regions for all harts. (Section 3.11)
> + */
> +static size_t
> +riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int hartid, int
> irq)
> +{
> +    size_t mode_offset = 0;
> +    size_t unit = clic->num_harts * clic->num_sources;
> +
> +    switch (mode) {
> +    case PRV_M:
> +        mode_offset = 0;
> +        break;
> +    case PRV_S:
> +        mode_offset = unit;
> +        break;
> +    case PRV_U:
> +        mode_offset = clic->prv_s ? 2 * unit : unit;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid mode %d\n", mode);
> +        exit(1);
> +    }
> +    return mode_offset + hartid * clic->num_sources + irq;
> +}
> +
> +static void riscv_clic_next_interrupt(void *opaque, int hartid)
> +{
> +    /*
> +     * 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
> +     */
> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(hartid));
> +    CPURISCVState *env = &cpu->env;
> +    RISCVCLICState *clic = (RISCVCLICState *)opaque;
> +
> +    int il[4] = {
> +        MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
> +            clic->mintthresh), /* PRV_U */
> +        MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
> +            clic->sintthresh), /* PRV_S */
> +        0,                     /* reserverd */
> +        MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
> +            clic->uintthresh)  /* PRV_M */
> +    };
>

The mintthresh register layout is not cleared in v0.8 spec[1].
I think mintthresh holds the thresholds for four different privileges (mth,
hth, sth, uth)?
Each of them occupies 8-bits?

If you want to support v0.8 CLIC as well, then I think you have to
extract threshold values from mintthresh for v0.8 CLIC,
or assign clic->sintthresh and clic->uintthresh when user writes to
mintthresh memory-mapped register.
Otherwise, sintthresh and uintthresh should be valid in v0.9 CLIC only.

v0.8 spec is also vague on whether mintthresh is a CLIC memory-mapped
register or a CSR:
It does have the corresponding memory-mapped address,
but spec. also describes it as a CSR register:
  The interrupt-level threshold (mintthresh) is a new read-write CSR,
  which holds an 8-bit field for each privilege mode (i.e., mth, hth, sth,
uth).

But as long as there's no CSR number for mintthresh,
so I guess it should be a memory-mapped register.

Also, for v0.9 CLIC, I think it's better to AND xintthresh with 8-bit mask
because xintthresh is defined as target_ulong CSRs in cpu.h.
User might write something larger than 8 bits.

[1] https://github.com/riscv/riscv-fast-interrupt/blob/74f86c3858/clic.adoc

Regards,
Frank Chang

+
> +    /* Get sorted list of enabled interrupts for this hart */
> +    size_t hart_offset = hartid * clic->num_sources;
> +    CLICActiveInterrupt *active = &clic->active_list[hart_offset];
> +    size_t active_count = clic->active_count[hartid];
> +    uint8_t mode, level, priority;
> +
> +    /* Loop through the enabled interrupts sorted by mode+priority+level
> */
> +    while (active_count) {
> +        size_t irq_offset;
> +        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;
> +        }
> +        irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> active->irq);
> +        /* Check pending interrupt with high enough mode+priority+level */
> +        if (clic->clicintip[irq_offset]) {
> +            /* Clean vector edge-triggered pending */
> +            if (riscv_clic_is_edge_triggered(clic, irq_offset) &&
> +                riscv_clic_is_shv_interrupt(clic, irq_offset)) {
> +                clic->clicintip[irq_offset] = 0;
> +            }
> +            /* Post pending interrupt for this hart */
> +            clic->exccode[hartid] = active->irq | mode << 12 | level <<
> 14;
> +            qemu_set_irq(clic->cpu_irqs[hartid], 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 hartid, int
> irq)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    if (!clic->prv_s && !clic->prv_u) { /* M */
> +        return mode == PRV_M;
> +    } else if (!clic->prv_s) { /* M/U */
> +        switch (clic->nmbits) {
> +        case 0:
> +            return mode == PRV_M;
> +        case 1:
> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M)
> :
> +                                                          (mode == PRV_U);
> +        default:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "clic: nmbits can only be 0 or 1 for M/U hart");
> +            exit(1);
> +        }
> +    } else { /* M/S/U */
> +        switch (clic->nmbits) {
> +        case 0:
> +            return mode == PRV_M;
> +        case 1:
> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M)
> :
> +                                                          (mode == PRV_S);
> +        case 2:
> +            return mode == clic->clicintattr[irq_offset];
> +        case 3:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "clic: nmbits can only be 0 or 1 or 2 for M/S/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 mode, int hartid, int
> irq)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
> +}
> +
> +static void
> +riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
> +                        int irq, uint64_t value)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    clic->clicintip[irq_offset] = !!value;
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +/*
> + * 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, uint64_t
> value)
> +{
> +    int mode = extract64(value, 6, 2);
> +
> +    if (!qtest_enabled()) {
> +        CPURISCVState *env = current_cpu->env_ptr;
> +        if (env->priv < mode) {
> +            return false;
> +        }
> +    }
> +    return true;
> +}
> +
> +static inline int riscv_clic_encode_priority(const CLICActiveInterrupt *i)
> +{
> +    return ((i->intcfg & 0x3ff) << 12) | /* Highest mode+level+priority */
> +           (i->irq & 0xfff);             /* Highest irq number */
> +}
> +
> +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 hartid,
> +                        int irq, uint64_t new_intie)
> +{
> +    size_t hart_offset = hartid * clic->num_sources;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    CLICActiveInterrupt *active_list = &clic->active_list[hart_offset];
> +    size_t *active_count = &clic->active_count[hartid];
> +
> +    uint8_t old_intie = clic->clicintie[irq_offset];
> +    clic->clicintie[irq_offset] = !!new_intie;
> +
> +    /* Add to or remove from list of active interrupts */
> +    if (new_intie && !old_intie) {
> +        active_list[*active_count].intcfg = (mode << 8) |
> +                                            clic->clicintctl[irq_offset];
> +        active_list[*active_count].irq = irq;
> +        (*active_count)++;
> +    } else if (!new_intie && old_intie) {
> +        CLICActiveInterrupt key = {
> +            (mode << 8) | clic->clicintctl[irq_offset], irq
> +        };
> +        CLICActiveInterrupt *result = bsearch(&key,
> +                                              active_list, *active_count,
> +                                              sizeof(CLICActiveInterrupt),
> +                                              riscv_clic_active_compare);
> +        size_t elem = (result - active_list) /
> sizeof(CLICActiveInterrupt);
> +        size_t sz = (--(*active_count) - elem) *
> sizeof(CLICActiveInterrupt);
> +        assert(result);
> +        memmove(&result[0], &result[1], sz);
> +    }
> +
> +    /* Sort list of active interrupts */
> +    qsort(active_list, *active_count,
> +          sizeof(CLICActiveInterrupt),
> +          riscv_clic_active_compare);
> +
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +static void
> +riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
> +                      uint64_t value, unsigned size,
> +                      int mode, int hartid, int irq)
> +{
> +    int req = extract32(addr, 0, 2);
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +
> +    if (hartid >= clic->num_harts) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
> +                      hartid, addr);
> +        return;
> +    }
> +
> +    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, mode, hartid, irq)) {
> +            /*
> +             * The actual pending bit is located at bit 0 (i.e., the
> +             * leastsignificant 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_offset]) {
> +                riscv_clic_update_intip(clic, mode, hartid, irq, value);
> +            }
> +        }
> +        break;
> +    case 1: /* clicintie[i] */
> +        if (clic->clicintie[irq_offset] != value) {
> +            riscv_clic_update_intie(clic, mode, hartid, irq, value);
> +        }
> +        break;
> +    case 2: /* clicintattr[i] */
> +        if (riscv_clic_validate_intattr(clic, value)) {
> +            if (clic->clicintattr[irq_offset] != value) {
> +                /* When nmbits=2, check WARL */
> +                bool invalid = (clic->nmbits == 2) &&
> +                               (extract64(value, 6, 2) == 0b10);
> +                if (invalid) {
> +                    uint8_t old_mode =
> extract32(clic->clicintattr[irq_offset],
> +                                                 6, 2);
> +                    value = deposit32(value, 6, 2, old_mode);
> +                }
> +                clic->clicintattr[irq_offset] = value;
> +                riscv_clic_next_interrupt(clic, hartid);
> +            }
> +        }
> +        break;
> +    case 3: /* clicintctl[i] */
> +        if (value != clic->clicintctl[irq_offset]) {
> +            clic->clicintctl[irq_offset] = value;
> +            riscv_clic_next_interrupt(clic, hartid);
> +        }
> +        break;
> +    }
> +}
> +
> +static uint64_t
> +riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
> +                     int hartid, int irq)
> +{
> +    int req = extract32(addr, 0, 2);
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +
> +    if (hartid >= clic->num_harts) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
> +                      hartid, addr);
> +        return 0;
> +    }
> +
> +    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] */
> +        return clic->clicintip[irq_offset];
> +    case 1: /* clicintie[i] */
> +        return clic->clicintie[irq_offset];
> +    case 2: /* clicintattr[i] */
> +        /*
> +         * clicintattr register layout
> +         * Bits Field
> +         * 7:6 mode
> +         * 5:3 reserved (WPRI 0)
> +         * 2:1 trig
> +         * 0 shv
> +         */
> +        return clic->clicintattr[irq_offset] & ~0x38;
> +    case 3: /* clicintctrl */
> +        /*
> +         * 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_offset] |
> +               ((1 << (8 - clic->clicintctlbits)) - 1);
> +    }
> +
> +    return 0;
> +}
> +
> +/* Return target interrupt mode */
> +static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int mode = addr / (4 * clic->num_harts * clic->num_sources);
> +    switch (mode) {
> +    case 0:
> +        return PRV_M;
> +    case 1:
> +        assert(clic->prv_s || clic->prv_u);
> +        return clic->prv_s ? PRV_S : PRV_U;
> +    case 2:
> +        assert(clic->prv_s && clic->prv_u);
> +        return PRV_U;
> +    default:
> +        g_assert_not_reached();
> +        break;
> +    }
> +}
> +
> +/* Return target hart id */
> +static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int mode_unit = 4 * clic->num_harts * clic->num_sources;
> +    int hart_unit = 4 * clic->num_sources;
> +
> +    return (addr % mode_unit) / hart_unit;
> +}
> +
> +/* Return target interrupt number */
> +static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int hart_unit = 4 * clic->num_sources;
> +    return (addr % hart_unit) / 4;
> +}
> +
> +static void
> +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
> +{
> +    RISCVCLICState *clic = opaque;
> +    hwaddr clic_size = clic->clic_size;
> +    int hartid, mode, irq;
> +
> +    if (addr < clic_size) {
> +        if (addr < 0x1000) {
> +            assert(addr % 4 == 0);
> +            int index = addr / 4;
> +            switch (index) {
> +            case 0: /* cliccfg */
> +                {
> +                    uint8_t nlbits = extract32(value, 1, 4);
> +                    uint8_t nmbits = extract32(value, 5, 2);
> +
> +                    /*
> +                     * The 4-bit cliccfg.nlbits WARL field.
> +                     * Valid values are 0—8.
> +                     */
> +                    if (nlbits <= 8) {
> +                        clic->nlbits = nlbits;
> +                    }
> +                    /* Valid values are given by implemented priviledges
> */
> +                    if (clic->prv_s && clic->prv_u) {
> +                        if (nmbits <= 2) {
> +                            clic->nmbits = nmbits;
> +                        }
> +                    } else if (clic->prv_u) {
> +                        if (nmbits <= 1) {
> +                            clic->nmbits = nmbits;
> +                        }
> +                    } else {
> +                        assert(!clic->prv_s);
> +                        if (nmbits == 0) {
> +                            clic->nmbits = 0;
> +                        }
> +                    }
> +                    clic->nvbits = extract32(value, 0, 1);
> +                    break;
> +                }
> +            case 1: /* clicinfo, read-only register */
> +                qemu_log_mask(LOG_GUEST_ERROR,
> +                              "clic: write read-only clicinfo.\n");
> +                break;
> +            case 0x10 ... 0x2F: /* clicinttrig */
> +                {
> +                    uint32_t interrupt_number = value &
> MAKE_64BIT_MASK(0, 13);
> +                    if (interrupt_number <= clic->num_sources) {
> +                        value &= ~MAKE_64BIT_MASK(13, 18);
> +                        clic->clicinttrig[index - 0x10] = value;
> +                    }
> +                    break;
> +                }
> +            case 2: /* mintthresh */
> +                if (!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 -= 0x1000;
> +            hartid = riscv_clic_get_hartid(clic, addr);
> +            mode = riscv_clic_get_mode(clic, addr);
> +            irq = riscv_clic_get_irq(clic, addr);
> +
> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
> +                riscv_clic_hart_write(clic, addr, value, size, mode,
> +                                      hartid, irq);
> +            }
> +        }
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid write: 0x%" HWADDR_PRIx "\n", addr);
> +    }
> +}
> +
> +static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    RISCVCLICState *clic = opaque;
> +    hwaddr clic_size = clic->clic_size;
> +    int hartid, mode, irq;
> +
> +    if (addr < clic_size) {
> +        if (addr < 0x1000) {
> +            assert(addr % 4 == 0);
> +            int index = addr / 4;
> +            switch (index) {
> +            case 0: /* cliccfg */
> +                return clic->nvbits |
> +                       (clic->nlbits << 1) |
> +                       (clic->nmbits << 5);
> +            case 1: /* clicinfo */
> +                /*
> +                 * clicinfo register layout
> +                 *
> +                 * Bits Field
> +                 * 31 reserved (WARL 0)
> +                 * 30:25 num_trigger
> +                 * 24:21 CLICINTCTLBITS
> +                 * 20:13 version (for version control)
> +                 * 12:0 num_interrupt
> +                 */
> +                return clic->clicinfo & ~INT32_MAX;
> +            case 0x10 ... 0x2F: /* clicinttrig */
> +                /*
> +                 * clicinttrig register layout
> +                 *
> +                 * Bits Field
> +                 * 31 enable
> +                 * 30:13 reserved (WARL 0)
> +                 * 12:0 interrupt_number
> +                 */
> +                return clic->clicinttrig[index - 0x10] &
> +                       ~MAKE_64BIT_MASK(13, 18);
> +            case 2: /* mintthresh */
> +                if (!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 -= 0x1000;
> +            hartid = riscv_clic_get_hartid(clic, addr);
> +            mode = riscv_clic_get_mode(clic, addr);
> +            irq = riscv_clic_get_irq(clic, addr);
> +
> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
> +                return riscv_clic_hart_read(clic, addr, mode, hartid,
> irq);
> +            }
> +        }
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid read: 0x%" HWADDR_PRIx "\n", addr);
> +    }
> +    return 0;
> +}
> +
> +static void riscv_clic_set_irq(void *opaque, int id, int level)
> +{
> +    RISCVCLICState *clic = opaque;
> +    int irq, hartid, mode;
> +    hwaddr addr = 4 * id;
> +    TRIG_TYPE type;
> +
> +    hartid = riscv_clic_get_hartid(clic, addr);
> +    mode = riscv_clic_get_mode(clic, addr);
> +    irq = riscv_clic_get_irq(clic, addr);
> +    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, mode, hartid, irq, level);
> +            break;
> +        case NEG_LEVEL:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
> +            break;
> +        case NEG_EDGE:
> +            break;
> +        }
> +    } else {
> +        switch (type) {
> +        case POSITIVE_LEVEL:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, level);
> +            break;
> +        case POSITIVE_EDGE:
> +            break;
> +        case NEG_LEVEL:
> +        case NEG_EDGE:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
> +            break;
> +        }
> +    }
> +}
> +
> +static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level)
> +{
> +    CPURISCVState *env = (CPURISCVState *)opaque;
> +    RISCVCLICState *clic = env->clic;
> +    CPUState *cpu = env_cpu(env);
> +
> +    if (level) {
> +        env->exccode = clic->exccode[cpu->cpu_index];
> +        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 harts_x_sources = clic->num_harts * clic->num_sources;
> +    int irqs, i;
> +
> +    if (clic->prv_s && clic->prv_u) {
> +        irqs = 3 * harts_x_sources;
> +    } else if (clic->prv_s || clic->prv_u) {
> +        irqs = 2 * harts_x_sources;
> +    } else {
> +        irqs = harts_x_sources;
> +    }
> +
> +    clic->clic_size = irqs * 4 + 0x1000;
> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops, clic,
> +                          TYPE_RISCV_CLIC, clic->clic_size);
> +
> +    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);
> +    clic->active_count = g_new0(size_t, clic->num_harts);
> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
> +
> +    /* Allocate irq 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_irqs, clic->num_harts);
> +
> +    for (i = 0; i < clic->num_harts; i++) {
> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
> +                                         &cpu->env, 1);
> +        qdev_connect_gpio_out(dev, i, irq);
> +        cpu->env.clic = clic;
> +    }
> +}
> +
> +static Property riscv_clic_properties[] = {
> +    DEFINE_PROP_BOOL("prv-s", RISCVCLICState, prv_s, false),
> +    DEFINE_PROP_BOOL("prv-u", RISCVCLICState, prv_u, false),
> +    DEFINE_PROP_UINT32("num-harts", RISCVCLICState, num_harts, 0),
> +    DEFINE_PROP_UINT32("num-sources", RISCVCLICState, num_sources, 0),
> +    DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICState, clicintctlbits,
> 0),
> +    DEFINE_PROP_UINT64("mclicbase", RISCVCLICState, mclicbase, 0),
> +    DEFINE_PROP_STRING("version", RISCVCLICState, version),
> +    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 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 void riscv_clic_register_types(void)
> +{
> +    type_register_static(&riscv_clic_info);
> +}
> +
> +type_init(riscv_clic_register_types)
> +
> +/*
> + * riscv_clic_create:
> + *
> + * @addr: base address of M-Mode CLIC memory-mapped registers
> + * @prv_s: have smode region
> + * @prv_u: have umode region
> + * @num_harts: number of CPU harts
> + * @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"
> + *
> + * Returns: the device object
> + */
> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
> +                               uint32_t num_harts, uint32_t num_sources,
> +                               uint8_t clicintctlbits,
> +                               const char *version)
> +{
> +    DeviceState *dev = qdev_new(TYPE_RISCV_CLIC);
> +
> +    assert(num_sources <= 4096);
> +    assert(num_harts <= 1024);
> +    assert(clicintctlbits <= 8);
> +    assert(!strcmp(version, "v0.8") || !strcmp(version, "v0.9"));
> +
> +    qdev_prop_set_bit(dev, "prv-s", prv_s);
> +    qdev_prop_set_bit(dev, "prv-u", prv_u);
> +    qdev_prop_set_uint32(dev, "num-harts", num_harts);
> +    qdev_prop_set_uint32(dev, "num-sources", num_sources);
> +    qdev_prop_set_uint32(dev, "clicintctlbits", clicintctlbits);
> +    qdev_prop_set_uint64(dev, "mclicbase", addr);
> +    qdev_prop_set_string(dev, "version", version);
> +
> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
> +    return dev;
> +}
> +
> +void riscv_clic_get_next_interrupt(void *opaque, int hartid)
> +{
> +    RISCVCLICState *clic = opaque;
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    return riscv_clic_is_shv_interrupt(clic, irq_offset);
> +}
> +
> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int
> irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
> +}
> +
> +void riscv_clic_clean_pending(void *opaque, int mode, int hartid, int irq)
> +{
> +    RISCVCLICState *clic = opaque;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    clic->clicintip[irq_offset] = 0;
> +}
> +
> +/*
> + * 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.
> + */
> +bool riscv_clic_is_clic_mode(CPURISCVState *env)
> +{
> +    target_ulong xtvec = (env->priv == PRV_M) ? env->mtvec : env->stvec;
> +    return env->clic && ((xtvec & 0x3) == 3);
> +}
> +
> +void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
> +                               int *il, int *irq)
> +{
> +    *irq = extract32(exccode, 0, 12);
> +    *mode = extract32(exccode, 12, 2);
> +    *il = extract32(exccode, 14, 8);
> +}
> diff --git a/include/hw/intc/riscv_clic.h b/include/hw/intc/riscv_clic.h
> new file mode 100644
> index 0000000000..e5f89672a6
> --- /dev/null
> +++ b/include/hw/intc/riscv_clic.h
> @@ -0,0 +1,103 @@
> +/*
> + * RISC-V CLIC(Core Local Interrupt Controller) interface.
> + *
> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
> + *
> + * 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/>.
> + */
> +
> +#ifndef RISCV_CLIC_H
> +#define RISCV_CLIC_H
> +
> +#include "hw/irq.h"
> +#include "hw/sysbus.h"
> +
> +#define TYPE_RISCV_CLIC "riscv_clic"
> +#define RISCV_CLIC(obj) \
> +    OBJECT_CHECK(RISCVCLICState, (obj), TYPE_RISCV_CLIC)
> +
> +/*
> + * 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;
> +
> +typedef struct RISCVCLICState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +
> +    /* Implementaion parameters */
> +    bool prv_s;
> +    bool prv_u;
> +    uint32_t num_harts;
> +    uint32_t num_sources;
> +    uint32_t clic_size;
> +    uint32_t clic_mmode_base;
> +    uint32_t clicintctlbits;
> +    uint64_t mclicbase;
> +    char *version;
> +
> +    /* Global configuration */
> +    uint8_t nmbits;
> +    uint8_t nlbits;
> +    uint8_t nvbits;
> +    uint32_t clicinfo;
> +    uint32_t clicinttrig[32];
> +
> +    /* Aperture configuration */
> +    uint8_t *clicintip;
> +    uint8_t *clicintie;
> +    uint8_t *clicintattr;
> +    uint8_t *clicintctl;
> +
> +    /* Complatible with v0.8 */
> +    uint32_t mintthresh;
> +    uint32_t sintthresh;
> +    uint32_t uintthresh;
> +
> +    /* QEMU implementaion related fields */
> +    uint32_t *exccode;
> +    CLICActiveInterrupt *active_list;
> +    size_t *active_count;
> +    MemoryRegion mmio;
> +    qemu_irq *cpu_irqs;
> +} RISCVCLICState;
> +
> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
> +                               uint32_t num_harts, 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 mode, int hartid, int
> irq);
> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int
> irq);
> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int
> irq);
> +void riscv_clic_get_next_interrupt(void *opaque, int hartid);
> +bool riscv_clic_is_clic_mode(CPURISCVState *env);
> +#endif
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index a5eab26a69..9e389d7bbf 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -33,6 +33,7 @@
>  #define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
>  #define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
>  #define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
> +#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0
>
>  #define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
>  #define TYPE_RISCV_CPU_BASE32           RISCV_CPU_TYPE_NAME("rv32")
> @@ -247,6 +248,7 @@ struct CPURISCVState {
>      /* Fields from here on are preserved across CPU reset. */
>      QEMUTimer *timer; /* Internal timer */
>      void *clic;       /* clic interrupt controller */
> +    uint32_t exccode; /* clic irq encode */
>  };
>
>  OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
> --
> 2.25.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 48446 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-06-26 17:15   ` Frank Chang
@ 2021-06-26 17:19     ` Frank Chang
  0 siblings, 0 replies; 81+ messages in thread
From: Frank Chang @ 2021-06-26 17:19 UTC (permalink / raw)
  To: Frank Chang
  Cc: open list:RISC-V, qemu-devel@nongnu.org Developers, wxy194768,
	Palmer Dabbelt, Alistair Francis, LIU Zhiwei

[-- Attachment #1: Type: text/plain, Size: 42165 bytes --]

Frank Chang <frank.chang@sifive.com> 於 2021年6月27日 週日 上午1:15寫道:

> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:57寫道:
>
>> 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>
>> ---
>>  default-configs/devices/riscv32-softmmu.mak |   1 +
>>  default-configs/devices/riscv64-softmmu.mak |   1 +
>>  hw/intc/Kconfig                             |   3 +
>>  hw/intc/meson.build                         |   1 +
>>  hw/intc/riscv_clic.c                        | 835 ++++++++++++++++++++
>>  include/hw/intc/riscv_clic.h                | 103 +++
>>  target/riscv/cpu.h                          |   2 +
>>  7 files changed, 946 insertions(+)
>>  create mode 100644 hw/intc/riscv_clic.c
>>  create mode 100644 include/hw/intc/riscv_clic.h
>>
>> diff --git a/default-configs/devices/riscv32-softmmu.mak
>> b/default-configs/devices/riscv32-softmmu.mak
>> index d847bd5692..1430c30588 100644
>> --- a/default-configs/devices/riscv32-softmmu.mak
>> +++ b/default-configs/devices/riscv32-softmmu.mak
>> @@ -5,6 +5,7 @@
>>  #CONFIG_PCI_DEVICES=n
>>  CONFIG_SEMIHOSTING=y
>>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>> +CONFIG_RISCV_CLIC=y
>>
>>  # Boards:
>>  #
>> diff --git a/default-configs/devices/riscv64-softmmu.mak
>> b/default-configs/devices/riscv64-softmmu.mak
>> index d5eec75f05..396800bbbd 100644
>> --- a/default-configs/devices/riscv64-softmmu.mak
>> +++ b/default-configs/devices/riscv64-softmmu.mak
>> @@ -5,6 +5,7 @@
>>  #CONFIG_PCI_DEVICES=n
>>  CONFIG_SEMIHOSTING=y
>>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>> +CONFIG_RISCV_CLIC=y
>>
>>  # Boards:
>>  #
>> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
>> index f4694088a4..5bf492b48f 100644
>> --- a/hw/intc/Kconfig
>> +++ b/hw/intc/Kconfig
>> @@ -68,6 +68,9 @@ config SIFIVE_CLINT
>>  config SIFIVE_PLIC
>>      bool
>>
>> +config RISCV_CLIC
>> +    bool
>> +
>>  config GOLDFISH_PIC
>>      bool
>>
>> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
>> index 1c299039f6..2aa71b6738 100644
>> --- a/hw/intc/meson.build
>> +++ b/hw/intc/meson.build
>> @@ -50,6 +50,7 @@ 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_SIFIVE_CLINT', if_true:
>> files('sifive_clint.c'))
>>  specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true:
>> files('sifive_plic.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'))
>> diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
>> new file mode 100644
>> index 0000000000..8ad534c506
>> --- /dev/null
>> +++ b/hw/intc/riscv_clic.c
>> @@ -0,0 +1,835 @@
>> +/*
>> + * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
>> + *
>> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
>> + *
>> + * 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/>.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qapi/error.h"
>> +#include "qemu/log.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"
>> +
>> +/*
>> + * 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_offset)
>> +{
>> +    return (clic->clicintattr[irq_offset] >> 1) & 0x3;
>> +}
>> +
>> +static inline bool
>> +riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
>> +{
>> +    return (clic->clicintattr[irq_offset] >> 1) & 0x1;
>> +}
>> +
>> +static inline bool
>> +riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
>> +{
>> +    return (clic->clicintattr[irq_offset] & 0x1) && clic->nvbits;
>> +}
>> +
>> +static uint8_t
>> +riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
>> +{
>> +    int nlbits = clic->nlbits;
>> +
>> +    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->nlbits;
>> +    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);
>> +}
>> +
>> +/*
>> + * In a system with multiple harts, the M-mode CLIC regions for all the
>> harts
>> + * are placed contiguously in the memory space, followed by the S-mode
>> CLIC
>> + * regions for all harts. (Section 3.11)
>> + */
>> +static size_t
>> +riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int hartid,
>> int irq)
>> +{
>> +    size_t mode_offset = 0;
>> +    size_t unit = clic->num_harts * clic->num_sources;
>> +
>> +    switch (mode) {
>> +    case PRV_M:
>> +        mode_offset = 0;
>> +        break;
>> +    case PRV_S:
>> +        mode_offset = unit;
>> +        break;
>> +    case PRV_U:
>> +        mode_offset = clic->prv_s ? 2 * unit : unit;
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid mode %d\n", mode);
>> +        exit(1);
>> +    }
>> +    return mode_offset + hartid * clic->num_sources + irq;
>> +}
>> +
>> +static void riscv_clic_next_interrupt(void *opaque, int hartid)
>> +{
>> +    /*
>> +     * 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
>> +     */
>> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(hartid));
>> +    CPURISCVState *env = &cpu->env;
>> +    RISCVCLICState *clic = (RISCVCLICState *)opaque;
>> +
>> +    int il[4] = {
>> +        MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
>> +            clic->mintthresh), /* PRV_U */
>> +        MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
>> +            clic->sintthresh), /* PRV_S */
>> +        0,                     /* reserverd */
>> +        MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
>> +            clic->uintthresh)  /* PRV_M */
>> +    };
>>
>
> The mintthresh register layout is not cleared in v0.8 spec[1].
> I think mintthresh holds the thresholds for four different privileges
> (mth, hth, sth, uth)?
> Each of them occupies 8-bits?
>
> If you want to support v0.8 CLIC as well, then I think you have to
> extract threshold values from mintthresh for v0.8 CLIC,
> or assign clic->sintthresh and clic->uintthresh when user writes to
> mintthresh memory-mapped register.
> Otherwise, sintthresh and uintthresh should be valid in v0.9 CLIC only.
>
> v0.8 spec is also vague on whether mintthresh is a CLIC memory-mapped
> register or a CSR:
> It does have the corresponding memory-mapped address,
> but spec. also describes it as a CSR register:
>   The interrupt-level threshold (mintthresh) is a new read-write CSR,
>   which holds an 8-bit field for each privilege mode (i.e., mth, hth, sth,
> uth).
>
> But as long as there's no CSR number for mintthresh,
> so I guess it should be a memory-mapped register.
>
> Also, for v0.9 CLIC, I think it's better to AND xintthresh with 8-bit mask
> because xintthresh is defined as target_ulong CSRs in cpu.h.
> User might write something larger than 8 bits.
>

Sorry, I forgot to mention.
Shouldn't xintthresh values retrieved from env->xintthresh for v0.9 CLIC?
(xintthresh are CSRs, instead of CLIC memory-mapped register in v0.9 CLIC.)

Regards,
Frank Chang


>
> [1]
> https://github.com/riscv/riscv-fast-interrupt/blob/74f86c3858/clic.adoc
>
> Regards,
> Frank Chang
>
> +
>> +    /* Get sorted list of enabled interrupts for this hart */
>> +    size_t hart_offset = hartid * clic->num_sources;
>> +    CLICActiveInterrupt *active = &clic->active_list[hart_offset];
>> +    size_t active_count = clic->active_count[hartid];
>> +    uint8_t mode, level, priority;
>> +
>> +    /* Loop through the enabled interrupts sorted by mode+priority+level
>> */
>> +    while (active_count) {
>> +        size_t irq_offset;
>> +        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;
>> +        }
>> +        irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> active->irq);
>> +        /* Check pending interrupt with high enough mode+priority+level
>> */
>> +        if (clic->clicintip[irq_offset]) {
>> +            /* Clean vector edge-triggered pending */
>> +            if (riscv_clic_is_edge_triggered(clic, irq_offset) &&
>> +                riscv_clic_is_shv_interrupt(clic, irq_offset)) {
>> +                clic->clicintip[irq_offset] = 0;
>> +            }
>> +            /* Post pending interrupt for this hart */
>> +            clic->exccode[hartid] = active->irq | mode << 12 | level <<
>> 14;
>> +            qemu_set_irq(clic->cpu_irqs[hartid], 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 hartid, int
>> irq)
>> +{
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    if (!clic->prv_s && !clic->prv_u) { /* M */
>> +        return mode == PRV_M;
>> +    } else if (!clic->prv_s) { /* M/U */
>> +        switch (clic->nmbits) {
>> +        case 0:
>> +            return mode == PRV_M;
>> +        case 1:
>> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode ==
>> PRV_M) :
>> +                                                          (mode ==
>> PRV_U);
>> +        default:
>> +            qemu_log_mask(LOG_GUEST_ERROR,
>> +                "clic: nmbits can only be 0 or 1 for M/U hart");
>> +            exit(1);
>> +        }
>> +    } else { /* M/S/U */
>> +        switch (clic->nmbits) {
>> +        case 0:
>> +            return mode == PRV_M;
>> +        case 1:
>> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode ==
>> PRV_M) :
>> +                                                          (mode ==
>> PRV_S);
>> +        case 2:
>> +            return mode == clic->clicintattr[irq_offset];
>> +        case 3:
>> +            qemu_log_mask(LOG_GUEST_ERROR,
>> +                "clic: nmbits can only be 0 or 1 or 2 for M/S/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 mode, int hartid,
>> int irq)
>> +{
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
>> +}
>> +
>> +static void
>> +riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
>> +                        int irq, uint64_t value)
>> +{
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    clic->clicintip[irq_offset] = !!value;
>> +    riscv_clic_next_interrupt(clic, hartid);
>> +}
>> +
>> +/*
>> + * 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, uint64_t
>> value)
>> +{
>> +    int mode = extract64(value, 6, 2);
>> +
>> +    if (!qtest_enabled()) {
>> +        CPURISCVState *env = current_cpu->env_ptr;
>> +        if (env->priv < mode) {
>> +            return false;
>> +        }
>> +    }
>> +    return true;
>> +}
>> +
>> +static inline int riscv_clic_encode_priority(const CLICActiveInterrupt
>> *i)
>> +{
>> +    return ((i->intcfg & 0x3ff) << 12) | /* Highest mode+level+priority
>> */
>> +           (i->irq & 0xfff);             /* Highest irq number */
>> +}
>> +
>> +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 hartid,
>> +                        int irq, uint64_t new_intie)
>> +{
>> +    size_t hart_offset = hartid * clic->num_sources;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    CLICActiveInterrupt *active_list = &clic->active_list[hart_offset];
>> +    size_t *active_count = &clic->active_count[hartid];
>> +
>> +    uint8_t old_intie = clic->clicintie[irq_offset];
>> +    clic->clicintie[irq_offset] = !!new_intie;
>> +
>> +    /* Add to or remove from list of active interrupts */
>> +    if (new_intie && !old_intie) {
>> +        active_list[*active_count].intcfg = (mode << 8) |
>> +                                            clic->clicintctl[irq_offset];
>> +        active_list[*active_count].irq = irq;
>> +        (*active_count)++;
>> +    } else if (!new_intie && old_intie) {
>> +        CLICActiveInterrupt key = {
>> +            (mode << 8) | clic->clicintctl[irq_offset], irq
>> +        };
>> +        CLICActiveInterrupt *result = bsearch(&key,
>> +                                              active_list, *active_count,
>> +
>> sizeof(CLICActiveInterrupt),
>> +                                              riscv_clic_active_compare);
>> +        size_t elem = (result - active_list) /
>> sizeof(CLICActiveInterrupt);
>> +        size_t sz = (--(*active_count) - elem) *
>> sizeof(CLICActiveInterrupt);
>> +        assert(result);
>> +        memmove(&result[0], &result[1], sz);
>> +    }
>> +
>> +    /* Sort list of active interrupts */
>> +    qsort(active_list, *active_count,
>> +          sizeof(CLICActiveInterrupt),
>> +          riscv_clic_active_compare);
>> +
>> +    riscv_clic_next_interrupt(clic, hartid);
>> +}
>> +
>> +static void
>> +riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
>> +                      uint64_t value, unsigned size,
>> +                      int mode, int hartid, int irq)
>> +{
>> +    int req = extract32(addr, 0, 2);
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +
>> +    if (hartid >= clic->num_harts) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
>> +                      hartid, addr);
>> +        return;
>> +    }
>> +
>> +    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, mode, hartid, irq)) {
>> +            /*
>> +             * The actual pending bit is located at bit 0 (i.e., the
>> +             * leastsignificant 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_offset]) {
>> +                riscv_clic_update_intip(clic, mode, hartid, irq, value);
>> +            }
>> +        }
>> +        break;
>> +    case 1: /* clicintie[i] */
>> +        if (clic->clicintie[irq_offset] != value) {
>> +            riscv_clic_update_intie(clic, mode, hartid, irq, value);
>> +        }
>> +        break;
>> +    case 2: /* clicintattr[i] */
>> +        if (riscv_clic_validate_intattr(clic, value)) {
>> +            if (clic->clicintattr[irq_offset] != value) {
>> +                /* When nmbits=2, check WARL */
>> +                bool invalid = (clic->nmbits == 2) &&
>> +                               (extract64(value, 6, 2) == 0b10);
>> +                if (invalid) {
>> +                    uint8_t old_mode =
>> extract32(clic->clicintattr[irq_offset],
>> +                                                 6, 2);
>> +                    value = deposit32(value, 6, 2, old_mode);
>> +                }
>> +                clic->clicintattr[irq_offset] = value;
>> +                riscv_clic_next_interrupt(clic, hartid);
>> +            }
>> +        }
>> +        break;
>> +    case 3: /* clicintctl[i] */
>> +        if (value != clic->clicintctl[irq_offset]) {
>> +            clic->clicintctl[irq_offset] = value;
>> +            riscv_clic_next_interrupt(clic, hartid);
>> +        }
>> +        break;
>> +    }
>> +}
>> +
>> +static uint64_t
>> +riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
>> +                     int hartid, int irq)
>> +{
>> +    int req = extract32(addr, 0, 2);
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +
>> +    if (hartid >= clic->num_harts) {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
>> +                      hartid, addr);
>> +        return 0;
>> +    }
>> +
>> +    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] */
>> +        return clic->clicintip[irq_offset];
>> +    case 1: /* clicintie[i] */
>> +        return clic->clicintie[irq_offset];
>> +    case 2: /* clicintattr[i] */
>> +        /*
>> +         * clicintattr register layout
>> +         * Bits Field
>> +         * 7:6 mode
>> +         * 5:3 reserved (WPRI 0)
>> +         * 2:1 trig
>> +         * 0 shv
>> +         */
>> +        return clic->clicintattr[irq_offset] & ~0x38;
>> +    case 3: /* clicintctrl */
>> +        /*
>> +         * 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_offset] |
>> +               ((1 << (8 - clic->clicintctlbits)) - 1);
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +/* Return target interrupt mode */
>> +static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
>> +{
>> +    int mode = addr / (4 * clic->num_harts * clic->num_sources);
>> +    switch (mode) {
>> +    case 0:
>> +        return PRV_M;
>> +    case 1:
>> +        assert(clic->prv_s || clic->prv_u);
>> +        return clic->prv_s ? PRV_S : PRV_U;
>> +    case 2:
>> +        assert(clic->prv_s && clic->prv_u);
>> +        return PRV_U;
>> +    default:
>> +        g_assert_not_reached();
>> +        break;
>> +    }
>> +}
>> +
>> +/* Return target hart id */
>> +static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
>> +{
>> +    int mode_unit = 4 * clic->num_harts * clic->num_sources;
>> +    int hart_unit = 4 * clic->num_sources;
>> +
>> +    return (addr % mode_unit) / hart_unit;
>> +}
>> +
>> +/* Return target interrupt number */
>> +static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
>> +{
>> +    int hart_unit = 4 * clic->num_sources;
>> +    return (addr % hart_unit) / 4;
>> +}
>> +
>> +static void
>> +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned
>> size)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    hwaddr clic_size = clic->clic_size;
>> +    int hartid, mode, irq;
>> +
>> +    if (addr < clic_size) {
>> +        if (addr < 0x1000) {
>> +            assert(addr % 4 == 0);
>> +            int index = addr / 4;
>> +            switch (index) {
>> +            case 0: /* cliccfg */
>> +                {
>> +                    uint8_t nlbits = extract32(value, 1, 4);
>> +                    uint8_t nmbits = extract32(value, 5, 2);
>> +
>> +                    /*
>> +                     * The 4-bit cliccfg.nlbits WARL field.
>> +                     * Valid values are 0—8.
>> +                     */
>> +                    if (nlbits <= 8) {
>> +                        clic->nlbits = nlbits;
>> +                    }
>> +                    /* Valid values are given by implemented priviledges
>> */
>> +                    if (clic->prv_s && clic->prv_u) {
>> +                        if (nmbits <= 2) {
>> +                            clic->nmbits = nmbits;
>> +                        }
>> +                    } else if (clic->prv_u) {
>> +                        if (nmbits <= 1) {
>> +                            clic->nmbits = nmbits;
>> +                        }
>> +                    } else {
>> +                        assert(!clic->prv_s);
>> +                        if (nmbits == 0) {
>> +                            clic->nmbits = 0;
>> +                        }
>> +                    }
>> +                    clic->nvbits = extract32(value, 0, 1);
>> +                    break;
>> +                }
>> +            case 1: /* clicinfo, read-only register */
>> +                qemu_log_mask(LOG_GUEST_ERROR,
>> +                              "clic: write read-only clicinfo.\n");
>> +                break;
>> +            case 0x10 ... 0x2F: /* clicinttrig */
>> +                {
>> +                    uint32_t interrupt_number = value &
>> MAKE_64BIT_MASK(0, 13);
>> +                    if (interrupt_number <= clic->num_sources) {
>> +                        value &= ~MAKE_64BIT_MASK(13, 18);
>> +                        clic->clicinttrig[index - 0x10] = value;
>> +                    }
>> +                    break;
>> +                }
>> +            case 2: /* mintthresh */
>> +                if (!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 -= 0x1000;
>> +            hartid = riscv_clic_get_hartid(clic, addr);
>> +            mode = riscv_clic_get_mode(clic, addr);
>> +            irq = riscv_clic_get_irq(clic, addr);
>> +
>> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
>> +                riscv_clic_hart_write(clic, addr, value, size, mode,
>> +                                      hartid, irq);
>> +            }
>> +        }
>> +    } else {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid write: 0x%" HWADDR_PRIx "\n", addr);
>> +    }
>> +}
>> +
>> +static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    hwaddr clic_size = clic->clic_size;
>> +    int hartid, mode, irq;
>> +
>> +    if (addr < clic_size) {
>> +        if (addr < 0x1000) {
>> +            assert(addr % 4 == 0);
>> +            int index = addr / 4;
>> +            switch (index) {
>> +            case 0: /* cliccfg */
>> +                return clic->nvbits |
>> +                       (clic->nlbits << 1) |
>> +                       (clic->nmbits << 5);
>> +            case 1: /* clicinfo */
>> +                /*
>> +                 * clicinfo register layout
>> +                 *
>> +                 * Bits Field
>> +                 * 31 reserved (WARL 0)
>> +                 * 30:25 num_trigger
>> +                 * 24:21 CLICINTCTLBITS
>> +                 * 20:13 version (for version control)
>> +                 * 12:0 num_interrupt
>> +                 */
>> +                return clic->clicinfo & ~INT32_MAX;
>> +            case 0x10 ... 0x2F: /* clicinttrig */
>> +                /*
>> +                 * clicinttrig register layout
>> +                 *
>> +                 * Bits Field
>> +                 * 31 enable
>> +                 * 30:13 reserved (WARL 0)
>> +                 * 12:0 interrupt_number
>> +                 */
>> +                return clic->clicinttrig[index - 0x10] &
>> +                       ~MAKE_64BIT_MASK(13, 18);
>> +            case 2: /* mintthresh */
>> +                if (!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 -= 0x1000;
>> +            hartid = riscv_clic_get_hartid(clic, addr);
>> +            mode = riscv_clic_get_mode(clic, addr);
>> +            irq = riscv_clic_get_irq(clic, addr);
>> +
>> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
>> +                return riscv_clic_hart_read(clic, addr, mode, hartid,
>> irq);
>> +            }
>> +        }
>> +    } else {
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "clic: invalid read: 0x%" HWADDR_PRIx "\n", addr);
>> +    }
>> +    return 0;
>> +}
>> +
>> +static void riscv_clic_set_irq(void *opaque, int id, int level)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    int irq, hartid, mode;
>> +    hwaddr addr = 4 * id;
>> +    TRIG_TYPE type;
>> +
>> +    hartid = riscv_clic_get_hartid(clic, addr);
>> +    mode = riscv_clic_get_mode(clic, addr);
>> +    irq = riscv_clic_get_irq(clic, addr);
>> +    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, mode, hartid, irq, level);
>> +            break;
>> +        case NEG_LEVEL:
>> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
>> +            break;
>> +        case NEG_EDGE:
>> +            break;
>> +        }
>> +    } else {
>> +        switch (type) {
>> +        case POSITIVE_LEVEL:
>> +            riscv_clic_update_intip(clic, mode, hartid, irq, level);
>> +            break;
>> +        case POSITIVE_EDGE:
>> +            break;
>> +        case NEG_LEVEL:
>> +        case NEG_EDGE:
>> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
>> +            break;
>> +        }
>> +    }
>> +}
>> +
>> +static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level)
>> +{
>> +    CPURISCVState *env = (CPURISCVState *)opaque;
>> +    RISCVCLICState *clic = env->clic;
>> +    CPUState *cpu = env_cpu(env);
>> +
>> +    if (level) {
>> +        env->exccode = clic->exccode[cpu->cpu_index];
>> +        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 harts_x_sources = clic->num_harts * clic->num_sources;
>> +    int irqs, i;
>> +
>> +    if (clic->prv_s && clic->prv_u) {
>> +        irqs = 3 * harts_x_sources;
>> +    } else if (clic->prv_s || clic->prv_u) {
>> +        irqs = 2 * harts_x_sources;
>> +    } else {
>> +        irqs = harts_x_sources;
>> +    }
>> +
>> +    clic->clic_size = irqs * 4 + 0x1000;
>> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops,
>> clic,
>> +                          TYPE_RISCV_CLIC, clic->clic_size);
>> +
>> +    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);
>> +    clic->active_count = g_new0(size_t, clic->num_harts);
>> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
>> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
>> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
>> +
>> +    /* Allocate irq 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_irqs, clic->num_harts);
>> +
>> +    for (i = 0; i < clic->num_harts; i++) {
>> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>> +                                         &cpu->env, 1);
>> +        qdev_connect_gpio_out(dev, i, irq);
>> +        cpu->env.clic = clic;
>> +    }
>> +}
>> +
>> +static Property riscv_clic_properties[] = {
>> +    DEFINE_PROP_BOOL("prv-s", RISCVCLICState, prv_s, false),
>> +    DEFINE_PROP_BOOL("prv-u", RISCVCLICState, prv_u, false),
>> +    DEFINE_PROP_UINT32("num-harts", RISCVCLICState, num_harts, 0),
>> +    DEFINE_PROP_UINT32("num-sources", RISCVCLICState, num_sources, 0),
>> +    DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICState, clicintctlbits,
>> 0),
>> +    DEFINE_PROP_UINT64("mclicbase", RISCVCLICState, mclicbase, 0),
>> +    DEFINE_PROP_STRING("version", RISCVCLICState, version),
>> +    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 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 void riscv_clic_register_types(void)
>> +{
>> +    type_register_static(&riscv_clic_info);
>> +}
>> +
>> +type_init(riscv_clic_register_types)
>> +
>> +/*
>> + * riscv_clic_create:
>> + *
>> + * @addr: base address of M-Mode CLIC memory-mapped registers
>> + * @prv_s: have smode region
>> + * @prv_u: have umode region
>> + * @num_harts: number of CPU harts
>> + * @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"
>> + *
>> + * Returns: the device object
>> + */
>> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
>> +                               uint32_t num_harts, uint32_t num_sources,
>> +                               uint8_t clicintctlbits,
>> +                               const char *version)
>> +{
>> +    DeviceState *dev = qdev_new(TYPE_RISCV_CLIC);
>> +
>> +    assert(num_sources <= 4096);
>> +    assert(num_harts <= 1024);
>> +    assert(clicintctlbits <= 8);
>> +    assert(!strcmp(version, "v0.8") || !strcmp(version, "v0.9"));
>> +
>> +    qdev_prop_set_bit(dev, "prv-s", prv_s);
>> +    qdev_prop_set_bit(dev, "prv-u", prv_u);
>> +    qdev_prop_set_uint32(dev, "num-harts", num_harts);
>> +    qdev_prop_set_uint32(dev, "num-sources", num_sources);
>> +    qdev_prop_set_uint32(dev, "clicintctlbits", clicintctlbits);
>> +    qdev_prop_set_uint64(dev, "mclicbase", addr);
>> +    qdev_prop_set_string(dev, "version", version);
>> +
>> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
>> +    return dev;
>> +}
>> +
>> +void riscv_clic_get_next_interrupt(void *opaque, int hartid)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    riscv_clic_next_interrupt(clic, hartid);
>> +}
>> +
>> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int
>> irq)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    return riscv_clic_is_shv_interrupt(clic, irq_offset);
>> +}
>> +
>> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int
>> irq)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
>> +}
>> +
>> +void riscv_clic_clean_pending(void *opaque, int mode, int hartid, int
>> irq)
>> +{
>> +    RISCVCLICState *clic = opaque;
>> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
>> irq);
>> +    clic->clicintip[irq_offset] = 0;
>> +}
>> +
>> +/*
>> + * 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.
>> + */
>> +bool riscv_clic_is_clic_mode(CPURISCVState *env)
>> +{
>> +    target_ulong xtvec = (env->priv == PRV_M) ? env->mtvec : env->stvec;
>> +    return env->clic && ((xtvec & 0x3) == 3);
>> +}
>> +
>> +void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
>> +                               int *il, int *irq)
>> +{
>> +    *irq = extract32(exccode, 0, 12);
>> +    *mode = extract32(exccode, 12, 2);
>> +    *il = extract32(exccode, 14, 8);
>> +}
>> diff --git a/include/hw/intc/riscv_clic.h b/include/hw/intc/riscv_clic.h
>> new file mode 100644
>> index 0000000000..e5f89672a6
>> --- /dev/null
>> +++ b/include/hw/intc/riscv_clic.h
>> @@ -0,0 +1,103 @@
>> +/*
>> + * RISC-V CLIC(Core Local Interrupt Controller) interface.
>> + *
>> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
>> + *
>> + * 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/>.
>> + */
>> +
>> +#ifndef RISCV_CLIC_H
>> +#define RISCV_CLIC_H
>> +
>> +#include "hw/irq.h"
>> +#include "hw/sysbus.h"
>> +
>> +#define TYPE_RISCV_CLIC "riscv_clic"
>> +#define RISCV_CLIC(obj) \
>> +    OBJECT_CHECK(RISCVCLICState, (obj), TYPE_RISCV_CLIC)
>> +
>> +/*
>> + * 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;
>> +
>> +typedef struct RISCVCLICState {
>> +    /*< private >*/
>> +    SysBusDevice parent_obj;
>> +
>> +    /*< public >*/
>> +
>> +    /* Implementaion parameters */
>> +    bool prv_s;
>> +    bool prv_u;
>> +    uint32_t num_harts;
>> +    uint32_t num_sources;
>> +    uint32_t clic_size;
>> +    uint32_t clic_mmode_base;
>> +    uint32_t clicintctlbits;
>> +    uint64_t mclicbase;
>> +    char *version;
>> +
>> +    /* Global configuration */
>> +    uint8_t nmbits;
>> +    uint8_t nlbits;
>> +    uint8_t nvbits;
>> +    uint32_t clicinfo;
>> +    uint32_t clicinttrig[32];
>> +
>> +    /* Aperture configuration */
>> +    uint8_t *clicintip;
>> +    uint8_t *clicintie;
>> +    uint8_t *clicintattr;
>> +    uint8_t *clicintctl;
>> +
>> +    /* Complatible with v0.8 */
>> +    uint32_t mintthresh;
>> +    uint32_t sintthresh;
>> +    uint32_t uintthresh;
>> +
>> +    /* QEMU implementaion related fields */
>> +    uint32_t *exccode;
>> +    CLICActiveInterrupt *active_list;
>> +    size_t *active_count;
>> +    MemoryRegion mmio;
>> +    qemu_irq *cpu_irqs;
>> +} RISCVCLICState;
>> +
>> +DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
>> +                               uint32_t num_harts, 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 mode, int hartid, int
>> irq);
>> +bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int
>> irq);
>> +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int
>> irq);
>> +void riscv_clic_get_next_interrupt(void *opaque, int hartid);
>> +bool riscv_clic_is_clic_mode(CPURISCVState *env);
>> +#endif
>> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>> index a5eab26a69..9e389d7bbf 100644
>> --- a/target/riscv/cpu.h
>> +++ b/target/riscv/cpu.h
>> @@ -33,6 +33,7 @@
>>  #define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
>>  #define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
>>  #define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
>> +#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0
>>
>>  #define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
>>  #define TYPE_RISCV_CPU_BASE32           RISCV_CPU_TYPE_NAME("rv32")
>> @@ -247,6 +248,7 @@ struct CPURISCVState {
>>      /* Fields from here on are preserved across CPU reset. */
>>      QEMUTimer *timer; /* Internal timer */
>>      void *clic;       /* clic interrupt controller */
>> +    uint32_t exccode; /* clic irq encode */
>>  };
>>
>>  OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
>> --
>> 2.25.1
>>
>>
>>

[-- Attachment #2: Type: text/html, Size: 49310 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 02/11] target/riscv: Update CSR xintthresh in CLIC mode
  2021-04-09  7:48 ` [RFC PATCH 02/11] target/riscv: Update CSR xintthresh in CLIC mode LIU Zhiwei
@ 2021-06-26 17:23   ` Frank Chang
  2021-06-27  8:23     ` Frank Chang
  0 siblings, 1 reply; 81+ messages in thread
From: Frank Chang @ 2021-06-26 17:23 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 3959 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:52寫道:

> 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: LIU Zhiwei <zhiwei_liu@c-sky.com>
>

Reviewed-by: Frank Chang <frank.chang@sifive.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 1a44ca62c7..a5eab26a69 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -160,6 +160,7 @@ struct CPURISCVState {
>
>      uint32_t miclaim;
>      uint32_t mintstatus; /* clic-spec */
> +    target_ulong mintthresh; /* clic-spec */
>
>      target_ulong mie;
>      target_ulong mideleg;
> @@ -173,6 +174,7 @@ struct CPURISCVState {
>      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 c4ce6ec3d9..9447801d22 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      0x346 /* clic-spec-draft */
> +#define CSR_MINTTHRESH      0x347 /* clic-spec-draft */
>
>  /* Legacy Machine Trap Handling (priv v1.9.1) */
>  #define CSR_MBADADDR        0x343
> @@ -185,6 +186,7 @@
>  #define CSR_STVAL           0x143
>  #define CSR_SIP             0x144
>  #define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
> +#define CSR_SINTTHRESH      0x147 /* clic-spec-draft */
>
>  /* Legacy Supervisor Trap Handling (priv v1.9.1) */
>  #define CSR_SBADADDR        0x143
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index 320b18ab60..4c31364967 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -746,6 +746,18 @@ static int read_mintstatus(CPURISCVState *env, int
> csrno, target_ulong *val)
>      return 0;
>  }
>
> +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 int read_sstatus(CPURISCVState *env, int csrno, target_ulong *val)
>  {
> @@ -912,6 +924,18 @@ static int read_sintstatus(CPURISCVState *env, int
> csrno, target_ulong *val)
>      return 0;
>  }
>
> +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 int read_satp(CPURISCVState *env, int csrno, target_ulong *val)
>  {
> @@ -1666,9 +1690,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 },
>
>  #endif /* !CONFIG_USER_ONLY */
>  };
> --
> 2.25.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 4942 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 05/11] target/riscv: Update CSR xip in CLIC mode
  2021-04-09  7:48 ` [RFC PATCH 05/11] target/riscv: Update CSR xip " LIU Zhiwei
@ 2021-06-27  6:45   ` Frank Chang
  0 siblings, 0 replies; 81+ messages in thread
From: Frank Chang @ 2021-06-27  6:45 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 1864 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:52寫道:

> 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>

---
>  target/riscv/csr.c | 10 ++++++++++
>  1 file changed, 10 insertions(+)
>
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index 74bc7a08aa..f6c84b9fe4 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -735,6 +735,11 @@ static int rmw_mip(CPURISCVState *env, int csrno,
> target_ulong *ret_value,
>      target_ulong mask = write_mask & delegable_ints & ~env->miclaim;
>      uint32_t old_mip;
>
> +     /* The xip CSR appears hardwired to zero in CLIC mode. (Section 4.3)
> */
> +    if (riscv_clic_is_clic_mode(env)) {
> +        *ret_value = 0;
> +        return 0;
>

Should return RISCV_EXCP_NONE when you rebase to the latest master branch.
Same to rmw_sip().

Reviewed-by: Frank Chang <frank.chang@sifive.com>


> +    }
>      if (mask) {
>          old_mip = riscv_cpu_update_mip(cpu, mask, (new_value & mask));
>      } else {
> @@ -922,6 +927,11 @@ static int rmw_sip(CPURISCVState *env, int csrno,
> target_ulong *ret_value,
>      if (riscv_cpu_virt_enabled(env)) {
>          ret = rmw_vsip(env, CSR_VSIP, ret_value, new_value, write_mask);
>      } else {
> +        /* The xip CSR appears hardwired to zero in CLIC mode. (Section
> 4.3) */
> +        if (riscv_clic_is_clic_mode(env)) {
> +            *ret_value = 0;
> +            return 0;
> +        }
>          ret = rmw_mip(env, CSR_MSTATUS, ret_value, new_value,
>                        write_mask & env->mideleg & sip_writable_mask);
>      }
> --
> 2.25.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 2763 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 04/11] target/riscv: Update CSR xie in CLIC mode
  2021-04-09  7:48 ` [RFC PATCH 04/11] target/riscv: Update CSR xie in CLIC mode LIU Zhiwei
@ 2021-06-27  6:50   ` Frank Chang
  0 siblings, 0 replies; 81+ messages in thread
From: Frank Chang @ 2021-06-27  6:50 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 2733 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:51寫道:

> 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>
> ---
>  target/riscv/csr.c | 19 ++++++++++++++++---
>  1 file changed, 16 insertions(+), 3 deletions(-)
>
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index 4c31364967..74bc7a08aa 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -23,6 +23,10 @@
>  #include "qemu/main-loop.h"
>  #include "exec/exec-all.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)
>  {
> @@ -611,13 +615,17 @@ static int write_mideleg(CPURISCVState *env, int
> csrno, target_ulong val)
>
>  static int read_mie(CPURISCVState *env, int csrno, target_ulong *val)
>  {
> -    *val = env->mie;
> +    /* The xie CSR appears hardwired to zero in CLIC mode, (Section 4.3)
> */
> +    *val = riscv_clic_is_clic_mode(env) ? 0 : env->mie;
>      return 0;
>  }
>
>  static int write_mie(CPURISCVState *env, int csrno, target_ulong val)
>  {
> -    env->mie = (env->mie & ~all_ints) | (val & all_ints);
> +    /* Writes to xie will be ignored and will not trap. (Section 4.3) */
> +    if (!riscv_clic_is_clic_mode(env)) {
> +        env->mie = (env->mie & ~all_ints) | (val & all_ints);
> +    }
>      return 0;
>  }
>
> @@ -785,7 +793,8 @@ static int read_sie(CPURISCVState *env, int csrno,
> target_ulong *val)
>      if (riscv_cpu_virt_enabled(env)) {
>          read_vsie(env, CSR_VSIE, val);
>      } else {
> -        *val = env->mie & env->mideleg;
> +        /* The xie CSR appears hardwired to zero in CLIC mode. (Section
> 4.3) */
> +        *val = riscv_clic_is_clic_mode(env) ? 0 : env->mie & env->mideleg;
>      }
>      return 0;
>  }
> @@ -805,6 +814,10 @@ static int write_sie(CPURISCVState *env, int csrno,
> target_ulong val)
>      } else {
>          target_ulong newval = (env->mie & ~S_MODE_INTERRUPTS) |
>                                (val & S_MODE_INTERRUPTS);
> +        /* Writes to xie will be ignored and will not trap. (Section 4.3)
> */
> +        if (riscv_clic_is_clic_mode(env)) {
> +            return 0;
>
+        }
>

Minor:
This one can be omitted as write_sie() will eventually call write_mie().

Anyway,
Reviewed-by: Frank Chang <frank.chang@sifive.com>


>          write_mie(env, CSR_MIE, newval);
>      }
>
> --
> 2.25.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 3776 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 02/11] target/riscv: Update CSR xintthresh in CLIC mode
  2021-06-26 17:23   ` Frank Chang
@ 2021-06-27  8:23     ` Frank Chang
  0 siblings, 0 replies; 81+ messages in thread
From: Frank Chang @ 2021-06-27  8:23 UTC (permalink / raw)
  To: Frank Chang
  Cc: open list:RISC-V, qemu-devel@nongnu.org Developers, wxy194768,
	Palmer Dabbelt, Alistair Francis, LIU Zhiwei

[-- Attachment #1: Type: text/plain, Size: 4525 bytes --]

Frank Chang <frank.chang@sifive.com> 於 2021年6月27日 週日 上午1:23寫道:

> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:52寫道:
>
>> 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: LIU Zhiwei <zhiwei_liu@c-sky.com>
>>
>
> Reviewed-by: Frank Chang <frank.chang@sifive.com>
>

Sorry, recall that mintthresh description is vague in v0.8 CLIC spec[1].
If mintthresh is a CLIC memory-mapped register in v0.8 CLIC.
Then I think you should restrict the CSR accesses to mintthresh and
sintthresh when CLIC is v0.8.

[1] https://github.com/riscv/riscv-fast-interrupt/blob/74f86c3858/clic.adoc

Regards,
Frank Chang


>
>
>> ---
>>  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 1a44ca62c7..a5eab26a69 100644
>> --- a/target/riscv/cpu.h
>> +++ b/target/riscv/cpu.h
>> @@ -160,6 +160,7 @@ struct CPURISCVState {
>>
>>      uint32_t miclaim;
>>      uint32_t mintstatus; /* clic-spec */
>> +    target_ulong mintthresh; /* clic-spec */
>>
>>      target_ulong mie;
>>      target_ulong mideleg;
>> @@ -173,6 +174,7 @@ struct CPURISCVState {
>>      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 c4ce6ec3d9..9447801d22 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      0x346 /* clic-spec-draft */
>> +#define CSR_MINTTHRESH      0x347 /* clic-spec-draft */
>>
>>  /* Legacy Machine Trap Handling (priv v1.9.1) */
>>  #define CSR_MBADADDR        0x343
>> @@ -185,6 +186,7 @@
>>  #define CSR_STVAL           0x143
>>  #define CSR_SIP             0x144
>>  #define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
>> +#define CSR_SINTTHRESH      0x147 /* clic-spec-draft */
>>
>>  /* Legacy Supervisor Trap Handling (priv v1.9.1) */
>>  #define CSR_SBADADDR        0x143
>> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
>> index 320b18ab60..4c31364967 100644
>> --- a/target/riscv/csr.c
>> +++ b/target/riscv/csr.c
>> @@ -746,6 +746,18 @@ static int read_mintstatus(CPURISCVState *env, int
>> csrno, target_ulong *val)
>>      return 0;
>>  }
>>
>> +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 int read_sstatus(CPURISCVState *env, int csrno, target_ulong *val)
>>  {
>> @@ -912,6 +924,18 @@ static int read_sintstatus(CPURISCVState *env, int
>> csrno, target_ulong *val)
>>      return 0;
>>  }
>>
>> +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 int read_satp(CPURISCVState *env, int csrno, target_ulong *val)
>>  {
>> @@ -1666,9 +1690,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 },
>>
>>  #endif /* !CONFIG_USER_ONLY */
>>  };
>> --
>> 2.25.1
>>
>>
>>

[-- Attachment #2: Type: text/html, Size: 6093 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 07/11] target/riscv: Update CSR xtvt in CLIC mode
  2021-04-09  7:48 ` [RFC PATCH 07/11] target/riscv: Update CSR xtvt " LIU Zhiwei
@ 2021-06-27  8:33   ` Frank Chang
  0 siblings, 0 replies; 81+ messages in thread
From: Frank Chang @ 2021-06-27  8:33 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 4226 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:52寫道:

> The xtvt WARL XLEN-bit CSR holds the base address of the trap vector table,
> aligned on a 64-byte or greater power-of-two boundary.
>
> 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 9e389d7bbf..b5fd796f98 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -173,11 +173,13 @@ struct CPURISCVState {
>      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 9447801d22..7922097776 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -149,6 +149,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
> @@ -178,6 +179,7 @@
>  #define CSR_SIE             0x104
>  #define CSR_STVEC           0x105
>  #define CSR_SCOUNTEREN      0x106
> +#define CSR_STVT            0x107 /* clic-spec-draft */
>
>  /* Supervisor Trap Handling */
>  #define CSR_SSCRATCH        0x140
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index 39ff72041a..e12222b77f 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -667,6 +667,18 @@ static int write_mcounteren(CPURISCVState *env, int
> csrno, target_ulong val)
>      return 0;
>  }
>
> +static int read_mtvt(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> +    *val = env->mtvt;
> +    return 0;
> +}
> +
> +static int write_mtvt(CPURISCVState *env, int csrno, target_ulong val)
> +{
> +    env->mtvt = val & ~((1ULL << 6) - 1);
>

mtvt CSR has additional minimum alignment restriction in v0.8 CLIC spec[1]:
  2^ceiling(log2(N)) x 4 bytes, where N is the maximum number of interrupt
sources.


> +    return 0;
> +}
> +
>  /* This regiser is replaced with CSR_MCOUNTINHIBIT in 1.11.0 */
>  static int read_mscounteren(CPURISCVState *env, int csrno, target_ulong
> *val)
>  {
> @@ -876,6 +888,18 @@ static int write_scounteren(CPURISCVState *env, int
> csrno, target_ulong val)
>      return 0;
>  }
>
> +static int read_stvt(CPURISCVState *env, int csrno, target_ulong *val)


stvt CSR seems not to exist in v0.8 CLIC spec[1].

[1] https://github.com/riscv/riscv-fast-interrupt/blob/74f86c3858/clic.adoc

Regards,
Frank Chang


>

+{
> +    *val = env->stvt;
> +    return 0;
> +}
> +
> +static int write_stvt(CPURISCVState *env, int csrno, target_ulong val)
> +{
> +    env->stvt = val & ~((1ULL << 6) - 1);
> +    return 0;
> +}
> +
>  /* Supervisor Trap Handling */
>  static int read_sscratch(CPURISCVState *env, int csrno, target_ulong *val)
>  {
> @@ -1730,6 +1754,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>      [CSR_MHPMCOUNTER31H] = { "mhpmcounter31h", any32,  read_zero },
>
>      /* 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 },
> @@ -1739,5 +1764,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>      [CSR_SINTTHRESH] = { "sintthresh", clic,  read_sintthresh,
>                           write_sintthresh },
>
> +    /* Supervisor Mode Core Level Interrupt Controller */
> +    [CSR_STVT] = { "stvt", clic,  read_stvt, write_stvt       },
> +
>  #endif /* !CONFIG_USER_ONLY */
>  };
> --
> 2.25.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 5685 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 06/11] target/riscv: Update CSR xtvec in CLIC mode
  2021-04-09  7:48 ` [RFC PATCH 06/11] target/riscv: Update CSR xtvec " LIU Zhiwei
@ 2021-06-27  8:59   ` Frank Chang
  2021-07-10 15:04   ` Frank Chang
  1 sibling, 0 replies; 81+ messages in thread
From: Frank Chang @ 2021-06-27  8:59 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 2708 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:51寫道:

> 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>
> ---
>  target/riscv/csr.c | 22 ++++++++++++++++++++--
>  1 file changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index f6c84b9fe4..39ff72041a 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -637,9 +637,18 @@ static int read_mtvec(CPURISCVState *env, int csrno,
> target_ulong *val)
>
>  static int write_mtvec(CPURISCVState *env, int csrno, target_ulong val)
>  {
> -    /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
> +    /*
> +     * bits [1:0] encode mode; 0 = direct, 1 = vectored, 3 = CLIC,
> +     * others reserved
> +     */
>      if ((val & 3) < 2) {
>          env->mtvec = val;
> +    } else if ((val & 1) && 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.
> +         */
> +        env->mtvec = ((val & ~0x3f) << 6) | (0b000011);
>

Why do we need to left-shift the value 6 bits here?


>      } else {
>          qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: reserved mode not
> supported\n");
>      }
> @@ -837,9 +846,18 @@ static int read_stvec(CPURISCVState *env, int csrno,
> target_ulong *val)
>
>  static int write_stvec(CPURISCVState *env, int csrno, target_ulong val)
>  {
> -    /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
> +    /*
> +     * bits [1:0] encode mode; 0 = direct, 1 = vectored, 3 = CLIC,
> +     * others reserved
> +     */
>      if ((val & 3) < 2) {
>          env->stvec = val;
> +    } else if ((val & 1) && 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.
> +         */
> +        env->stvec = ((val & ~0x3f) << 6) | (0b000011);
>

Same here, why do we need to left-shift the value 6 bits here?

Also, CLIC v0.8 spec[1] doesn't include the change for stvec.
I'm not sure if it's the same as v0.9 to check stvec
when the interrupt is delegated to S-mode in CLIC-mode.

[1] https://github.com/riscv/riscv-fast-interrupt/blob/74f86c3858/clic.adoc

Regards,
Frank Chang

     } else {
>          qemu_log_mask(LOG_UNIMP, "CSR_STVEC: reserved mode not
> supported\n");
>      }
> --
> 2.25.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 3867 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 08/11] target/riscv: Update CSR xnxti in CLIC mode
  2021-04-09  7:48 ` [RFC PATCH 08/11] target/riscv: Update CSR xnxti " LIU Zhiwei
  2021-06-11  8:15   ` Frank Chang
@ 2021-06-27 10:07   ` Frank Chang
  2021-07-10 14:59   ` Frank Chang
  2 siblings, 0 replies; 81+ messages in thread
From: Frank Chang @ 2021-06-27 10:07 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 8736 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:52寫道:

> 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>
> ---
>  target/riscv/cpu_bits.h |  16 ++++++
>  target/riscv/csr.c      | 114 ++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 130 insertions(+)
>
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index 7922097776..494e41edc9 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      0x346 /* clic-spec-draft */
>  #define CSR_MINTTHRESH      0x347 /* clic-spec-draft */
>
> @@ -187,6 +188,7 @@
>  #define CSR_SCAUSE          0x142
>  #define CSR_STVAL           0x143
>  #define CSR_SIP             0x144
> +#define CSR_SNXTI           0x145 /* clic-spec-draft */
>  #define CSR_SINTSTATUS      0x146 /* clic-spec-draft */
>  #define CSR_SINTTHRESH      0x147 /* clic-spec-draft */
>
> @@ -596,10 +598,24 @@
>  #define MINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
>  #define MINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
>
> +/* mcause */
> +#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] */
> +
>  /* sintstatus */
>  #define SINTSTATUS_SIL                     0x0000ff00 /* sil[7:0] */
>  #define SINTSTATUS_UIL                     0x000000ff /* uil[7:0] */
>
> +/* scause */
> +#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] */
> +
>  /* 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 e12222b77f..72cba080bf 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -774,6 +774,80 @@ static int rmw_mip(CPURISCVState *env, int csrno,
> target_ulong *ret_value,
>      return 0;
>  }
>
> +static bool get_xnxti_status(CPURISCVState *env)
> +{
> +    CPUState *cs = env_cpu(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);
>

Same here, for v0.8 CLIC[1],
both mintthresh and sintthresh valuse should be retrieved from
CLIC mintthresh memory-mapped register.

+        } 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_priv, cs->cpu_index,
> +                                      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;
> +    CPUState *cs = env_cpu(env);
> +    if (write_mask) {
> +        env->mstatus |= new_value & (write_mask & 0b11111);
> +    }
> +
> +    qemu_mutex_lock_iothread();
> +    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_priv,
> +                                                  cs->cpu_index,
> clic_irq);
> +            if (edge) {
> +                riscv_clic_clean_pending(env->clic, clic_priv,
> +                                         cs->cpu_index, 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;
> +        }
> +    }
> +    qemu_mutex_unlock_iothread();
> +    return 0;
> +}
> +
>  static int read_mintstatus(CPURISCVState *env, int csrno, target_ulong
> *val)
>  {
>      *val = env->mintstatus;
> @@ -982,6 +1056,44 @@ static int rmw_sip(CPURISCVState *env, int csrno,
> target_ulong *ret_value,
>      return ret;
>  }
>
> +static int rmw_snxti(CPURISCVState *env, int csrno, target_ulong
> *ret_value,
> +                     target_ulong new_value, target_ulong write_mask)
>

snxti does not exist in v0.8 CLIC spec[1].

[1] https://github.com/riscv/riscv-fast-interrupt/blob/74f86c3858/clic.adoc

Regards,
Frank Chang


> +{
> +    int clic_priv, clic_il, clic_irq;
> +    bool ready;
> +    CPUState *cs = env_cpu(env);
> +    if (write_mask) {
> +        env->mstatus |= new_value & (write_mask & 0b11111);
> +    }
> +
> +    qemu_mutex_lock_iothread();
> +    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_priv,
> +                                                  cs->cpu_index,
> clic_irq);
> +            if (edge) {
> +                riscv_clic_clean_pending(env->clic, clic_priv,
> +                                         cs->cpu_index, clic_irq);
> +            }
> +            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;
> +        }
> +    }
> +    qemu_mutex_unlock_iothread();
> +    return 0;
> +}
> +
>  static int read_sintstatus(CPURISCVState *env, int csrno, target_ulong
> *val)
>  {
>      target_ulong mask = SINTSTATUS_SIL | SINTSTATUS_UIL;
> @@ -1755,6 +1867,7 @@ 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 },
> @@ -1766,6 +1879,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>
>      /* Supervisor Mode Core Level Interrupt Controller */
>      [CSR_STVT] = { "stvt", clic,  read_stvt, write_stvt       },
> +    [CSR_SNXTI] = { "snxti", clic,  NULL,  NULL,  rmw_snxti   },
>
>  #endif /* !CONFIG_USER_ONLY */
>  };
> --
> 2.25.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 11272 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 11/11] target/riscv: Update interrupt return in CLIC mode
  2021-04-09  7:48 ` [RFC PATCH 11/11] target/riscv: Update interrupt return " LIU Zhiwei
@ 2021-06-27 12:08   ` Frank Chang
  2021-07-13  7:15   ` Frank Chang
  1 sibling, 0 replies; 81+ messages in thread
From: Frank Chang @ 2021-06-27 12:08 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 3056 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:55寫道:

> 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 priviledge interrupt pending forever.
>
> Therefore when interrupts return, pull a pending interrupt to service.
>
> Signed-off-by: LIU Zhiwei <zhiwei_liu@c-sky.com>
> ---
>  target/riscv/op_helper.c | 25 +++++++++++++++++++++++++
>  1 file changed, 25 insertions(+)
>
> diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> index 1eddcb94de..42563b22ba 100644
> --- a/target/riscv/op_helper.c
> +++ b/target/riscv/op_helper.c
> @@ -24,6 +24,10 @@
>  #include "exec/exec-all.h"
>  #include "exec/helper-proto.h"
>
> +#if !defined(CONFIG_USER_ONLY)
> +#include "hw/intc/riscv_clic.h"
> +#endif
> +
>  /* Exceptions processing helpers */
>  void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env,
>                                            uint32_t exception, uintptr_t
> pc)
> @@ -130,6 +134,17 @@ target_ulong helper_sret(CPURISCVState *env,
> target_ulong cpu_pc_deb)
>          mstatus = set_field(mstatus, MSTATUS_SPIE, 1);
>          mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U);
>          env->mstatus = mstatus;
> +
> +        if (riscv_clic_is_clic_mode(env)) {
> +            CPUState *cs = env_cpu(env);
> +            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, 0);
>

Should scause.spie set to 1?


> +            env->scause = set_field(env->scause, SCAUSE_SPP, PRV_U);
> +            qemu_mutex_lock_iothread();
> +            riscv_clic_get_next_interrupt(env->clic, cs->cpu_index);
> +            qemu_mutex_unlock_iothread();
> +        }
>      }
>
>      riscv_cpu_set_mode(env, prev_priv);
> @@ -172,6 +187,16 @@ target_ulong helper_mret(CPURISCVState *env,
> target_ulong cpu_pc_deb)
>          riscv_cpu_set_virt_enabled(env, prev_virt);
>      }
>
> +    if (riscv_clic_is_clic_mode(env)) {
> +        CPUState *cs = env_cpu(env);
> +        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, 0);
>

Should mcause.mpie set to 1?
  The xcause.xpp and xcause.xpie fields are modified following the behavior
  previously defined for xstatus.xpp and xstatus.xpie respectively.

RISC-V Privilege spec:
  When executing an xRET instruction, xPIE is set to 1.

Regards,
Frank Chang


> +        env->mcause = set_field(env->mcause, MCAUSE_MPP, PRV_U);
> +        qemu_mutex_lock_iothread();
> +        riscv_clic_get_next_interrupt(env->clic, cs->cpu_index);
> +        qemu_mutex_unlock_iothread();
> +    }
>      return retpc;
>  }
>
> --
> 2.25.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 4162 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 10/11] target/riscv: Update interrupt handling in CLIC mode
  2021-04-09  7:48 ` [RFC PATCH 10/11] target/riscv: Update interrupt handling " LIU Zhiwei
@ 2021-06-27 15:39   ` Frank Chang
  0 siblings, 0 replies; 81+ messages in thread
From: Frank Chang @ 2021-06-27 15:39 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 9816 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:54寫道:

> Decode CLIC interrupt information from exccode, includes interrupt
> priviledge 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>
> ---
>  target/riscv/cpu_bits.h   |   1 +
>  target/riscv/cpu_helper.c | 117 +++++++++++++++++++++++++++++++++++---
>  2 files changed, 111 insertions(+), 7 deletions(-)
>
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index 494e41edc9..d8378d2384 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -557,6 +557,7 @@
>  #define RISCV_EXCP_VIRT_INSTRUCTION_FAULT        0x16
>  #define RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT  0x17
>
> +#define RISCV_EXCP_INT_CLIC                0x40000000
>  #define RISCV_EXCP_INT_FLAG                0x80000000
>  #define RISCV_EXCP_INT_MASK                0x7fffffff
>
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 21c54ef561..998d1a2742 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -26,6 +26,10 @@
>  #include "trace.h"
>  #include "semihosting/common-semi.h"
>
> +#if !defined(CONFIG_USER_ONLY)
> +#include "hw/intc/riscv_clic.h"
> +#endif
> +
>  int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
>  {
>  #ifdef CONFIG_USER_ONLY
> @@ -36,6 +40,20 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
>  }
>
>  #ifndef CONFIG_USER_ONLY
> +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)
>  {
>      target_ulong irqs;
> @@ -90,6 +108,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 = (env->exccode >> 12) & 0b11;
> +        int enabled = riscv_cpu_local_irq_mode_enabled(env, mode);
> +        if (enabled) {
> +            cs->exception_index = RISCV_EXCP_INT_CLIC | env->exccode;
> +            cs->interrupt_request = cs->interrupt_request &
> ~CPU_INTERRUPT_CLIC;
> +            riscv_cpu_do_interrupt(cs);
> +            return true;
> +        }
> +    }
>  #endif
>      return false;
>  }
> @@ -884,6 +914,55 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address,
> int size,
>  #endif
>  }
>
> +
> +#if !defined(CONFIG_USER_ONLY)
> +static target_ulong riscv_intr_pc(CPURISCVState *env, target_ulong tvec,
> +                                  target_ulong tvt, bool async, bool clic,
> +                                  int cause, int mode)
> +{
> +    int mode1 = tvec & 0b11, mode2 = tvec & 0b111111;
> +    CPUState *cs = env_cpu(env);
> +
> +    if (!(async || clic)) {
> +        return tvec & ~0b11;
>

In CLIC mode, synchronous exception traps always jump to NBASE.


> +    }
> +    /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
> +    switch (mode1) {
> +    case 0b00:
> +        return tvec & ~0b11;
> +    case 0b01:
> +        return (tvec & ~0b11) + cause * 4;
> +    default:
> +        if (env->clic && (mode2 == 0b000011)) {
> +            /* Non-vectored, clicintattr[i].shv = 0 || cliccfg.nvbits = 0
> */
> +            if (!riscv_clic_shv_interrupt(env->clic, mode, cs->cpu_index,
> +                                          cause)) {
> +                /* NBASE = mtvec[XLEN-1:6]<<6 */
> +                return tvec & ~0b111111;
> +            } else {
> +                /*
> +                 * pc := M[TBASE + XLEN/8 * exccode)] & ~1,
> +                 * TBASE = mtvt[XLEN-1:6]<<6
> +                 */
> +                int size = TARGET_LONG_BITS / 8;
> +                target_ulong tbase = (tvt & ~0b111111) + size * cause;
> +                void *host = tlb_vaddr_to_host(env, tbase, MMU_DATA_LOAD,
> mode);
>

According to spec:
  For permissions-checking purposes, the memory access to retrieve the
  function pointer for vectoring is treated as a load with the privilege
mode
  and interrupt level of the interrupt handler. If there is an access
exception
  on the table load, xepc holds the faulting address. If this was a page
fault,
  the table load can be resumed by returning with xepc pointing to the
  table entry and the trap handler mode bit set.

So retreiving vector function pointer may raise an exception,
but tlb_vaddr_to_host() cannot reflect that.
We also need to include hardware vectoring bit: xinhv
to allow resumable traps on fetches to the trap vector table.

However, I think it's not easy to raise an exception inside of interrupt
handling routine.
It will immediately siglongjmp back to cpu_exec() and we will lose current
context :(


> +                if (host != NULL) {
> +                    target_ulong new_pc = ldn_p(host, size);
> +                    if (tlb_vaddr_to_host(env, new_pc, MMU_INST_FETCH,
> mode)) {
> +                        return new_pc;
> +                    }
> +                }
> +                qemu_log_mask(LOG_GUEST_ERROR,
> +                              "CLIC: load trap handler error!\n");
> +                exit(1);
> +            }
> +        }
> +        g_assert_not_reached();
> +    }
> +}
> +#endif
> +
>  /*
>   * Handle Traps
>   *
> @@ -898,11 +977,13 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>      CPURISCVState *env = &cpu->env;
>      bool force_hs_execp = riscv_cpu_force_hs_excep_enabled(env);
>      uint64_t s;
> +    int mode, level;
>
>      /* 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_INT_CLIC);
>      target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK;
>      target_ulong deleg = async ? env->mideleg : env->medeleg;
>      bool write_tval = false;
> @@ -958,6 +1039,28 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>          }
>      }
>
> +    if (clic) {
> +        mode = (cause >> 12) & 3;
> +        level = (cause >> 14) & 0xff;
> +        cause &= 0xfff;
> +        cause |= get_field(env->mstatus, MSTATUS_MPP) << 28;
> +        switch (mode) {
> +        case PRV_M:
> +            cause |= get_field(env->mintstatus, MINTSTATUS_MIL) << 16;
> +            cause |= get_field(env->mstatus, MSTATUS_MIE) << 27;
> +            env->mintstatus = set_field(env->mintstatus, MINTSTATUS_MIL,
> level);
> +            break;
> +        case PRV_S:
> +            cause |= get_field(env->mintstatus, MINTSTATUS_SIL) << 16;
> +            cause |= get_field(env->mstatus, MSTATUS_SPIE) << 27;
> +            env->mintstatus = set_field(env->mintstatus, MINTSTATUS_SIL,
> level);
> +            break;
> +        }
> +    } else {
> +        mode = env->priv <= PRV_S &&
> +            cause < TARGET_LONG_BITS && ((deleg >> cause) & 1) ? PRV_S :
> PRV_M;
> +    }
> +
>      trace_riscv_trap(env->mhartid, async, cause, env->pc, tval,
>                       riscv_cpu_get_trap_name(cause, async));
>
> @@ -967,8 +1070,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 < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
> +    if (mode == PRV_S) {
>          /* handle the trap in S-mode */
>          if (riscv_has_ext(env, RVH)) {
>              target_ulong hdeleg = async ? env->hideleg : env->hedeleg;
> @@ -1021,12 +1123,13 @@ 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));
> +        env->scause = cause | ((target_ulong)(async | clic) <<
> +                               (TARGET_LONG_BITS - 1));
>          env->sepc = env->pc;
>          env->sbadaddr = tval;
>          env->htval = htval;
> -        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,
> +                                clic & 0xfff, cause, PRV_S);
>

Should AND 0xfff on cause instead of clic variable.

Regards,
Frank Chang


>          riscv_cpu_set_mode(env, PRV_S);
>      } else {
>          /* handle the trap in M-mode */
> @@ -1056,8 +1159,8 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>          env->mepc = env->pc;
>          env->mbadaddr = tval;
>          env->mtval2 = mtval2;
> -        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,
> +                                clic, cause & 0xfff, PRV_M);
>          riscv_cpu_set_mode(env, PRV_M);
>      }
>
> --
> 2.25.1
>
>
>

[-- Attachment #2: Type: text/html, Size: 12550 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 00/11] RISC-V: support clic v0.9 specification
  2021-04-09  7:48 [RFC PATCH 00/11] RISC-V: support clic v0.9 specification LIU Zhiwei
                   ` (11 preceding siblings ...)
  2021-04-19 23:30 ` [RFC PATCH 00/11] RISC-V: support clic v0.9 specification Alistair Francis
@ 2021-06-27 15:55 ` Frank Chang
  12 siblings, 0 replies; 81+ messages in thread
From: Frank Chang @ 2021-06-27 15:55 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 3118 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:58寫道:

> 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.
>
> I take over the job from Michael Clark, who gave the first implementation
> of clic-v0.7 specification. If there is any copyright question, please
> let me know.
>
> Features:
> 1. support four kinds of trigger types.
> 2. Preserve the CSR WARL/WPRI semantics.
> 3. Option to select different modes, such as M/S/U or M/U.
> 4. At most 4096 interrupts.
> 5. At most 1024 apertures.
>
> Todo:
> 1. Encode the interrupt trigger information to exccode.
> 2. Support complete CSR mclicbase when its number is fixed.
> 3. Gave each aperture an independend address.
>
> It have passed my qtest case and freertos test. Welcome to have a try
> for your hardware.
>
> Any advice is welcomed. Thanks very much.
>
> Zhiwei
>
> [1] specification website: https://github.com/riscv/riscv-fast-interrupt.
> [2] Michael Clark origin work:
> https://github.com/sifive/riscv-qemu/tree/sifive-clic.
>
>
> LIU Zhiwei (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 xtvt in CLIC mode
>   target/riscv: Update CSR xnxti in CLIC mode
>   target/riscv: Update CSR mclicbase in CLIC mode
>   target/riscv: Update interrupt handling in CLIC mode
>   target/riscv: Update interrupt return in CLIC mode
>
>  default-configs/devices/riscv32-softmmu.mak |   1 +
>  default-configs/devices/riscv64-softmmu.mak |   1 +
>  hw/intc/Kconfig                             |   3 +
>  hw/intc/meson.build                         |   1 +
>  hw/intc/riscv_clic.c                        | 836 ++++++++++++++++++++
>  include/hw/intc/riscv_clic.h                | 103 +++
>  target/riscv/cpu.h                          |   9 +
>  target/riscv/cpu_bits.h                     |  32 +
>  target/riscv/cpu_helper.c                   | 117 ++-
>  target/riscv/csr.c                          | 247 +++++-
>  target/riscv/op_helper.c                    |  25 +
>  11 files changed, 1363 insertions(+), 12 deletions(-)
>  create mode 100644 hw/intc/riscv_clic.c
>  create mode 100644 include/hw/intc/riscv_clic.h
>
> --
> 2.25.1
>
>
>
After reviewing this patchset.
I found that CLIC v0.8 spec is quite incomplete.
It lacks all S-mode related CSRs.

If you think that it's just the v0.8 spec issue for not covering
all the required S-mode related CSRs -- and we should include them
in CLIC v0.8 implementation even it's not documented explicitly.
You can just ignore my comments in regard to S-mode CSRs for v0.8 CLIC.
(Besides mintthresh, v0.8 spec does say that it holds the 8-bit field
interrupt thresholds for each privilege mode.)

Regards,
Frank Chang

[-- Attachment #2: Type: text/html, Size: 4008 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-06-26 12:56       ` Frank Chang
@ 2021-06-28  7:15         ` LIU Zhiwei
  2021-06-28  7:23           ` Frank Chang
  0 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-06-28  7:15 UTC (permalink / raw)
  To: Frank Chang
  Cc: Alistair Francis, Palmer Dabbelt, open list:RISC-V, wxy194768,
	qemu-devel@nongnu.org Developers

[-- Attachment #1: Type: text/plain, Size: 4571 bytes --]


On 2021/6/26 下午8:56, Frank Chang wrote:
> On Wed, Jun 16, 2021 at 10:56 AM LIU Zhiwei <zhiwei_liu@c-sky.com 
> <mailto:zhiwei_liu@c-sky.com>> wrote:
>
>
>     On 6/13/21 6:10 PM, Frank Chang wrote:
>>     LIU Zhiwei <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> 於
>>     2021年4月9日 週五 下午3:57寫道:
>>
>>         +static void riscv_clic_realize(DeviceState *dev, Error **errp)
>>         +{
>>         +    RISCVCLICState *clic = RISCV_CLIC(dev);
>>         +    size_t harts_x_sources = clic->num_harts *
>>         clic->num_sources;
>>         +    int irqs, i;
>>         +
>>         +    if (clic->prv_s && clic->prv_u) {
>>         +        irqs = 3 * harts_x_sources;
>>         +    } else if (clic->prv_s || clic->prv_u) {
>>         +        irqs = 2 * harts_x_sources;
>>         +    } else {
>>         +        irqs = harts_x_sources;
>>         +    }
>>         +
>>         +    clic->clic_size = irqs * 4 + 0x1000;
>>         +    memory_region_init_io(&clic->mmio, OBJECT(dev),
>>         &riscv_clic_ops, clic,
>>         +                          TYPE_RISCV_CLIC, clic->clic_size);
>>         +
>>         +    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);
>>         +    clic->active_count = g_new0(size_t, clic->num_harts);
>>         +    clic->exccode = g_new0(uint32_t, clic->num_harts);
>>         +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
>>         +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
>>         +
>>         +    /* Allocate irq 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_irqs, clic->num_harts);
>>         +
>>         +    for (i = 0; i < clic->num_harts; i++) {
>>         +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>>
>>
>>     As spec says:
>>       Smaller single-core systems might have only a CLIC,
>>       while multicore systems might have a CLIC per-core and a single
>>     shared PLIC.
>>       The PLIC xeip signals are treated as hart-local interrupt
>>     sources by the CLIC at each core.
>>
>>     It looks like it's possible to have one CLIC instance per core.
>
>     If you want to delivery an interrupt to one hart, you should
>     encode the IRQ by the interrupt number
>     , the hart number and the interrupt target privilege, then set the
>     irq.
>
>     I think how to calculate the hart number is the task of PLIC and
>     it can make use of "hartid-base"
>     to calculate it.
>
>     Thanks,
>     Zhiwei
>
>
> Hi Zhiwei,
>
> What I mean is if there are multiple CLIC instances, each per core 
> (CLIC spec allows that).
> If you try to bind CLIC with CPU index start from 0,
> it will be impossible for CLIC instance to bind CPU from index other 
> than 0.
>
> For example, for 4 cores system, it's possible to have 4 CLIC instances:
>   * CLIC 0 binds to CPU 0
>   * CLIC 1 binds to CPU 1
>   * CLIC 2 binds to CPU 2
>   * CLIC 3 binds to CPU 3
>
> and that's why I said it's possible to pass an extra "hartid-base" 
> just like PLIC.
> I know most of hardid are calculated by the requesing address, so most 
> hartid usages should be fine.
> But I saw two places using qemu_get_cpu(),
> which may cause the problem for the scenario I describe above:
> i.e. riscv_clic_next_interrupt() and riscv_clic_realize() as my 
> original reply.

So what's the problem here?

Currently all cores share the same CLIC instance. Do you want to give 
each core  a CLIC instance?

Thanks,
Zhiwei

>
> Regards,
> Frank Chang
>
>>     However if you try to bind CPU reference start from index i = 0.
>>     It's not possible for each per-core CLIC to bind their own CPU
>>     instance in multicore system
>>     as they have to bind from CPU 0.
>>
>>     I'm not sure if we add a new "hartid-base" property just like
>>     what SiFive PLIC is
>>     implemented would be a good idea or not.
>>
>>
>>     Regards,
>>     Frank Chang
>>
>>         + qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>>         +  &cpu->env, 1);
>>         +        qdev_connect_gpio_out(dev, i, irq);
>>         +        cpu->env.clic = clic;
>>         +    }
>>         +}
>>         +
>>
>>

[-- Attachment #2: Type: text/html, Size: 9777 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-06-28  7:15         ` LIU Zhiwei
@ 2021-06-28  7:23           ` Frank Chang
  2021-06-28  7:39             ` LIU Zhiwei
  0 siblings, 1 reply; 81+ messages in thread
From: Frank Chang @ 2021-06-28  7:23 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: open list:RISC-V, Frank Chang, qemu-devel@nongnu.org Developers,
	wxy194768, Palmer Dabbelt, Alistair Francis

[-- Attachment #1: Type: text/plain, Size: 4790 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午3:17寫道:

>
> On 2021/6/26 下午8:56, Frank Chang wrote:
>
> On Wed, Jun 16, 2021 at 10:56 AM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
>
>>
>> On 6/13/21 6:10 PM, Frank Chang wrote:
>>
>> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:57寫道:
>>
>> +static void riscv_clic_realize(DeviceState *dev, Error **errp)
>>> +{
>>> +    RISCVCLICState *clic = RISCV_CLIC(dev);
>>> +    size_t harts_x_sources = clic->num_harts * clic->num_sources;
>>> +    int irqs, i;
>>> +
>>> +    if (clic->prv_s && clic->prv_u) {
>>> +        irqs = 3 * harts_x_sources;
>>> +    } else if (clic->prv_s || clic->prv_u) {
>>> +        irqs = 2 * harts_x_sources;
>>> +    } else {
>>> +        irqs = harts_x_sources;
>>> +    }
>>> +
>>> +    clic->clic_size = irqs * 4 + 0x1000;
>>> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops,
>>> clic,
>>> +                          TYPE_RISCV_CLIC, clic->clic_size);
>>> +
>>> +    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);
>>> +    clic->active_count = g_new0(size_t, clic->num_harts);
>>> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
>>> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
>>> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
>>> +
>>> +    /* Allocate irq 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_irqs, clic->num_harts);
>>> +
>>> +    for (i = 0; i < clic->num_harts; i++) {
>>> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>>>
>>
>> As spec says:
>>   Smaller single-core systems might have only a CLIC,
>>   while multicore systems might have a CLIC per-core and a single shared
>> PLIC.
>>   The PLIC xeip signals are treated as hart-local interrupt sources by
>> the CLIC at each core.
>>
>> It looks like it's possible to have one CLIC instance per core.
>>
>> If you want to delivery an interrupt to one hart, you should encode the
>> IRQ by the interrupt number
>> , the hart number and the interrupt target privilege, then set the irq.
>>
>> I think how to calculate the hart number is the task of PLIC and it can
>> make use of "hartid-base"
>> to calculate it.
>>
>> Thanks,
>> Zhiwei
>>
>
> Hi Zhiwei,
>
> What I mean is if there are multiple CLIC instances, each per core (CLIC
> spec allows that).
> If you try to bind CLIC with CPU index start from 0,
> it will be impossible for CLIC instance to bind CPU from index other than
> 0.
>
> For example, for 4 cores system, it's possible to have 4 CLIC instances:
>   * CLIC 0 binds to CPU 0
>   * CLIC 1 binds to CPU 1
>   * CLIC 2 binds to CPU 2
>   * CLIC 3 binds to CPU 3
>
> and that's why I said it's possible to pass an extra "hartid-base" just
> like PLIC.
> I know most of hardid are calculated by the requesing address, so most
> hartid usages should be fine.
> But I saw two places using qemu_get_cpu(),
>
> which may cause the problem for the scenario I describe above:
> i.e. riscv_clic_next_interrupt() and riscv_clic_realize() as my original
> reply.
>
> So what's the problem here?
>
> Currently all cores share the same CLIC instance. Do you want to give each
> core  a CLIC instance?
>
Yes, that's what I mean, which should be supported as what spec says[1]:
  The CLIC complements the PLIC. Smaller single-core systems might have
only a CLIC,
  while multicore systems might have *a CLIC per-core* and a single shared
PLIC.
  The PLIC xeip signals are treated as hart-local interrupt sources by the
CLIC at each core.

[1]
https://github.com/riscv/riscv-fast-interrupt/blob/646310a5e4ae055964b4680f12c1c04a7cc0dd56/clic.adoc#12-clic-versus-plic

Thanks,
Frank Chang


>
> Thanks,
> Zhiwei
>
Regards,
> Frank Chang
>
>
>> However if you try to bind CPU reference start from index i = 0.
>> It's not possible for each per-core CLIC to bind their own CPU instance
>> in multicore system
>> as they have to bind from CPU 0.
>>
>> I'm not sure if we add a new "hartid-base" property just like what SiFive
>> PLIC is
>> implemented would be a good idea or not.
>>
>>
>> Regards,
>> Frank Chang
>>
>>
>>> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>>> +                                         &cpu->env, 1);
>>> +        qdev_connect_gpio_out(dev, i, irq);
>>> +        cpu->env.clic = clic;
>>> +    }
>>> +}
>>> +
>>>
>>>
>>>

[-- Attachment #2: Type: text/html, Size: 10720 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-06-28  7:23           ` Frank Chang
@ 2021-06-28  7:39             ` LIU Zhiwei
  2021-06-28  7:49               ` Frank Chang
  0 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-06-28  7:39 UTC (permalink / raw)
  To: Frank Chang
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V, wxy194768,
	qemu-devel@nongnu.org Developers

[-- Attachment #1: Type: text/plain, Size: 6123 bytes --]


On 2021/6/28 下午3:23, Frank Chang wrote:
> LIU Zhiwei <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> 於 
> 2021年6月28日 週一 下午3:17寫道:
>
>
>     On 2021/6/26 下午8:56, Frank Chang wrote:
>>     On Wed, Jun 16, 2021 at 10:56 AM LIU Zhiwei <zhiwei_liu@c-sky.com
>>     <mailto:zhiwei_liu@c-sky.com>> wrote:
>>
>>
>>         On 6/13/21 6:10 PM, Frank Chang wrote:
>>>         LIU Zhiwei <zhiwei_liu@c-sky.com
>>>         <mailto:zhiwei_liu@c-sky.com>> 於 2021年4月9日 週五
>>>         下午3:57寫道:
>>>
>>>             +static void riscv_clic_realize(DeviceState *dev, Error
>>>             **errp)
>>>             +{
>>>             +    RISCVCLICState *clic = RISCV_CLIC(dev);
>>>             +    size_t harts_x_sources = clic->num_harts *
>>>             clic->num_sources;
>>>             +    int irqs, i;
>>>             +
>>>             +    if (clic->prv_s && clic->prv_u) {
>>>             +        irqs = 3 * harts_x_sources;
>>>             +    } else if (clic->prv_s || clic->prv_u) {
>>>             +        irqs = 2 * harts_x_sources;
>>>             +    } else {
>>>             +        irqs = harts_x_sources;
>>>             +    }
>>>             +
>>>             +    clic->clic_size = irqs * 4 + 0x1000;
>>>             + memory_region_init_io(&clic->mmio, OBJECT(dev),
>>>             &riscv_clic_ops, clic,
>>>             + TYPE_RISCV_CLIC, clic->clic_size);
>>>             +
>>>             +    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);
>>>             +    clic->active_count = g_new0(size_t, clic->num_harts);
>>>             +    clic->exccode = g_new0(uint32_t, clic->num_harts);
>>>             +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
>>>             + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
>>>             +
>>>             +    /* Allocate irq 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_irqs,
>>>             clic->num_harts);
>>>             +
>>>             +    for (i = 0; i < clic->num_harts; i++) {
>>>             +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>>>
>>>
>>>         As spec says:
>>>           Smaller single-core systems might have only a CLIC,
>>>           while multicore systems might have a CLIC per-core and a
>>>         single shared PLIC.
>>>           The PLIC xeip signals are treated as hart-local interrupt
>>>         sources by the CLIC at each core.
>>>
>>>         It looks like it's possible to have one CLIC instance per core.
>>
>>         If you want to delivery an interrupt to one hart, you should
>>         encode the IRQ by the interrupt number
>>         , the hart number and the interrupt target privilege, then
>>         set the irq.
>>
>>         I think how to calculate the hart number is the task of PLIC
>>         and it can make use of "hartid-base"
>>         to calculate it.
>>
>>         Thanks,
>>         Zhiwei
>>
>>
>>     Hi Zhiwei,
>>
>>     What I mean is if there are multiple CLIC instances, each per
>>     core (CLIC spec allows that).
>>     If you try to bind CLIC with CPU index start from 0,
>>     it will be impossible for CLIC instance to bind CPU from index
>>     other than 0.
>>
>>     For example, for 4 cores system, it's possible to have 4 CLIC
>>     instances:
>>       * CLIC 0 binds to CPU 0
>>       * CLIC 1 binds to CPU 1
>>       * CLIC 2 binds to CPU 2
>>       * CLIC 3 binds to CPU 3
>>
>>     and that's why I said it's possible to pass an extra
>>     "hartid-base" just like PLIC.
>>     I know most of hardid are calculated by the requesing address, so
>>     most hartid usages should be fine.
>>     But I saw two places using qemu_get_cpu(),
>>     which may cause the problem for the scenario I describe above:
>>     i.e. riscv_clic_next_interrupt() and riscv_clic_realize() as my
>>     original reply.
>
>     So what's the problem here?
>
>     Currently all cores share the same CLIC instance. Do you want to
>     give each core  a CLIC instance?
>
> Yes, that's what I mean, which should be supported as what spec says[1]:
>   The CLIC complements the PLIC. Smaller single-core systems might 
> have only a CLIC,
>   while multicore systems might have *a CLIC per-core* and a single 
> shared PLIC.
>   The PLIC xeip signals are treated as hart-local interrupt sources by 
> the CLIC at each core.
>
> [1] 
> https://github.com/riscv/riscv-fast-interrupt/blob/646310a5e4ae055964b4680f12c1c04a7cc0dd56/clic.adoc#12-clic-versus-plic 
> <https://github.com/riscv/riscv-fast-interrupt/blob/646310a5e4ae055964b4680f12c1c04a7cc0dd56/clic.adoc#12-clic-versus-plic>
>
> Thanks,
> Frank Chang

If we give each core a CLIC instance, it is not convenient to access the 
shared memory, such as 0x0-0x1000.
Which CLIC instance should contain this memory region?

Thanks,
Zhiwei

>
>     Thanks,
>     Zhiwei
>
>>     Regards,
>>     Frank Chang
>>
>>>         However if you try to bind CPU reference start from index i = 0.
>>>         It's not possible for each per-core CLIC to bind their own
>>>         CPU instance in multicore system
>>>         as they have to bind from CPU 0.
>>>
>>>         I'm not sure if we add a new "hartid-base" property just
>>>         like what SiFive PLIC is
>>>         implemented would be a good idea or not.
>>>
>>>
>>>         Regards,
>>>         Frank Chang
>>>
>>>             +     qemu_irq irq =
>>>             qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>>>             +  &cpu->env, 1);
>>>             +        qdev_connect_gpio_out(dev, i, irq);
>>>             +        cpu->env.clic = clic;
>>>             +    }
>>>             +}
>>>             +
>>>
>>>

[-- Attachment #2: Type: text/html, Size: 14971 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-06-28  7:39             ` LIU Zhiwei
@ 2021-06-28  7:49               ` Frank Chang
  2021-06-28  8:01                 ` LIU Zhiwei
  0 siblings, 1 reply; 81+ messages in thread
From: Frank Chang @ 2021-06-28  7:49 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: open list:RISC-V, Frank Chang, qemu-devel@nongnu.org Developers,
	wxy194768, Alistair Francis, Palmer Dabbelt

[-- Attachment #1: Type: text/plain, Size: 5498 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午3:40寫道:

>
> On 2021/6/28 下午3:23, Frank Chang wrote:
>
> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午3:17寫道:
>
>>
>> On 2021/6/26 下午8:56, Frank Chang wrote:
>>
>> On Wed, Jun 16, 2021 at 10:56 AM LIU Zhiwei <zhiwei_liu@c-sky.com> wrote:
>>
>>>
>>> On 6/13/21 6:10 PM, Frank Chang wrote:
>>>
>>> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:57寫道:
>>>
>>> +static void riscv_clic_realize(DeviceState *dev, Error **errp)
>>>> +{
>>>> +    RISCVCLICState *clic = RISCV_CLIC(dev);
>>>> +    size_t harts_x_sources = clic->num_harts * clic->num_sources;
>>>> +    int irqs, i;
>>>> +
>>>> +    if (clic->prv_s && clic->prv_u) {
>>>> +        irqs = 3 * harts_x_sources;
>>>> +    } else if (clic->prv_s || clic->prv_u) {
>>>> +        irqs = 2 * harts_x_sources;
>>>> +    } else {
>>>> +        irqs = harts_x_sources;
>>>> +    }
>>>> +
>>>> +    clic->clic_size = irqs * 4 + 0x1000;
>>>> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops,
>>>> clic,
>>>> +                          TYPE_RISCV_CLIC, clic->clic_size);
>>>> +
>>>> +    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);
>>>> +    clic->active_count = g_new0(size_t, clic->num_harts);
>>>> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
>>>> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
>>>> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
>>>> +
>>>> +    /* Allocate irq 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_irqs, clic->num_harts);
>>>> +
>>>> +    for (i = 0; i < clic->num_harts; i++) {
>>>> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>>>>
>>>
>>> As spec says:
>>>   Smaller single-core systems might have only a CLIC,
>>>   while multicore systems might have a CLIC per-core and a single shared
>>> PLIC.
>>>   The PLIC xeip signals are treated as hart-local interrupt sources by
>>> the CLIC at each core.
>>>
>>> It looks like it's possible to have one CLIC instance per core.
>>>
>>> If you want to delivery an interrupt to one hart, you should encode the
>>> IRQ by the interrupt number
>>> , the hart number and the interrupt target privilege, then set the irq.
>>>
>>> I think how to calculate the hart number is the task of PLIC and it can
>>> make use of "hartid-base"
>>> to calculate it.
>>>
>>> Thanks,
>>> Zhiwei
>>>
>>
>> Hi Zhiwei,
>>
>> What I mean is if there are multiple CLIC instances, each per core (CLIC
>> spec allows that).
>> If you try to bind CLIC with CPU index start from 0,
>> it will be impossible for CLIC instance to bind CPU from index other than
>> 0.
>>
>> For example, for 4 cores system, it's possible to have 4 CLIC instances:
>>   * CLIC 0 binds to CPU 0
>>   * CLIC 1 binds to CPU 1
>>   * CLIC 2 binds to CPU 2
>>   * CLIC 3 binds to CPU 3
>>
>> and that's why I said it's possible to pass an extra "hartid-base" just
>> like PLIC.
>> I know most of hardid are calculated by the requesing address, so most
>> hartid usages should be fine.
>> But I saw two places using qemu_get_cpu(),
>>
>> which may cause the problem for the scenario I describe above:
>> i.e. riscv_clic_next_interrupt() and riscv_clic_realize() as my original
>> reply.
>>
>> So what's the problem here?
>>
>> Currently all cores share the same CLIC instance. Do you want to give
>> each core  a CLIC instance?
>>
> Yes, that's what I mean, which should be supported as what spec says[1]:
>   The CLIC complements the PLIC. Smaller single-core systems might have
> only a CLIC,
>   while multicore systems might have *a CLIC per-core* and a single
> shared PLIC.
>   The PLIC xeip signals are treated as hart-local interrupt sources by the
> CLIC at each core.
>
> [1]
> https://github.com/riscv/riscv-fast-interrupt/blob/646310a5e4ae055964b4680f12c1c04a7cc0dd56/clic.adoc#12-clic-versus-plic
>
> Thanks,
> Frank Chang
>
>
> If we give each core a CLIC instance, it is not convenient to access the
> shared memory, such as 0x0-0x1000.
> Which CLIC instance should contain this memory region?
>
What do you mean by: "access the shared memory" here?
I thought the memory region is defined during CLIC's creation?
So it should depend on the platform that creates CLIC instances.

Thanks,
Frank Chang


> Thanks,
> Zhiwei
>
>
>> Thanks,
>> Zhiwei
>>
> Regards,
>> Frank Chang
>>
>>
>>> However if you try to bind CPU reference start from index i = 0.
>>> It's not possible for each per-core CLIC to bind their own CPU instance
>>> in multicore system
>>> as they have to bind from CPU 0.
>>>
>>> I'm not sure if we add a new "hartid-base" property just like what
>>> SiFive PLIC is
>>> implemented would be a good idea or not.
>>>
>>>
>>> Regards,
>>> Frank Chang
>>>
>>>
>>>> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>>>> +                                         &cpu->env, 1);
>>>> +        qdev_connect_gpio_out(dev, i, irq);
>>>> +        cpu->env.clic = clic;
>>>> +    }
>>>> +}
>>>> +
>>>>
>>>>
>>>>

[-- Attachment #2: Type: text/html, Size: 15233 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-06-28  7:49               ` Frank Chang
@ 2021-06-28  8:01                 ` LIU Zhiwei
  2021-06-28  8:07                   ` Frank Chang
  0 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-06-28  8:01 UTC (permalink / raw)
  To: Frank Chang
  Cc: Alistair Francis, Palmer Dabbelt, open list:RISC-V, wxy194768,
	qemu-devel@nongnu.org Developers

[-- Attachment #1: Type: text/plain, Size: 7379 bytes --]


On 2021/6/28 下午3:49, Frank Chang wrote:
> LIU Zhiwei <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> 於 
> 2021年6月28日 週一 下午3:40寫道:
>
>
>     On 2021/6/28 下午3:23, Frank Chang wrote:
>>     LIU Zhiwei <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> 於
>>     2021年6月28日 週一 下午3:17寫道:
>>
>>
>>         On 2021/6/26 下午8:56, Frank Chang wrote:
>>>         On Wed, Jun 16, 2021 at 10:56 AM LIU Zhiwei
>>>         <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> wrote:
>>>
>>>
>>>             On 6/13/21 6:10 PM, Frank Chang wrote:
>>>>             LIU Zhiwei <zhiwei_liu@c-sky.com
>>>>             <mailto:zhiwei_liu@c-sky.com>> 於 2021年4月9日 週五
>>>>             下午3:57寫道:
>>>>
>>>>                 +static void riscv_clic_realize(DeviceState *dev,
>>>>                 Error **errp)
>>>>                 +{
>>>>                 +    RISCVCLICState *clic = RISCV_CLIC(dev);
>>>>                 +    size_t harts_x_sources = clic->num_harts *
>>>>                 clic->num_sources;
>>>>                 +    int irqs, i;
>>>>                 +
>>>>                 +    if (clic->prv_s && clic->prv_u) {
>>>>                 +        irqs = 3 * harts_x_sources;
>>>>                 +    } else if (clic->prv_s || clic->prv_u) {
>>>>                 +        irqs = 2 * harts_x_sources;
>>>>                 +    } else {
>>>>                 +        irqs = harts_x_sources;
>>>>                 +    }
>>>>                 +
>>>>                 +    clic->clic_size = irqs * 4 + 0x1000;
>>>>                 + memory_region_init_io(&clic->mmio, OBJECT(dev),
>>>>                 &riscv_clic_ops, clic,
>>>>                 + TYPE_RISCV_CLIC, clic->clic_size);
>>>>                 +
>>>>                 +    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);
>>>>                 +    clic->active_count = g_new0(size_t,
>>>>                 clic->num_harts);
>>>>                 +    clic->exccode = g_new0(uint32_t, clic->num_harts);
>>>>                 +    clic->cpu_irqs = g_new0(qemu_irq,
>>>>                 clic->num_harts);
>>>>                 + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
>>>>                 +
>>>>                 +    /* Allocate irq 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_irqs,
>>>>                 clic->num_harts);
>>>>                 +
>>>>                 +    for (i = 0; i < clic->num_harts; i++) {
>>>>                 +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>>>>
>>>>
>>>>             As spec says:
>>>>               Smaller single-core systems might have only a CLIC,
>>>>               while multicore systems might have a CLIC per-core
>>>>             and a single shared PLIC.
>>>>               The PLIC xeip signals are treated as hart-local
>>>>             interrupt sources by the CLIC at each core.
>>>>
>>>>             It looks like it's possible to have one CLIC instance
>>>>             per core.
>>>
>>>             If you want to delivery an interrupt to one hart, you
>>>             should encode the IRQ by the interrupt number
>>>             , the hart number and the interrupt target privilege,
>>>             then set the irq.
>>>
>>>             I think how to calculate the hart number is the task of
>>>             PLIC and it can make use of "hartid-base"
>>>             to calculate it.
>>>
>>>             Thanks,
>>>             Zhiwei
>>>
>>>
>>>         Hi Zhiwei,
>>>
>>>         What I mean is if there are multiple CLIC instances, each
>>>         per core (CLIC spec allows that).
>>>         If you try to bind CLIC with CPU index start from 0,
>>>         it will be impossible for CLIC instance to bind CPU from
>>>         index other than 0.
>>>
>>>         For example, for 4 cores system, it's possible to have 4
>>>         CLIC instances:
>>>           * CLIC 0 binds to CPU 0
>>>           * CLIC 1 binds to CPU 1
>>>           * CLIC 2 binds to CPU 2
>>>           * CLIC 3 binds to CPU 3
>>>
>>>         and that's why I said it's possible to pass an extra
>>>         "hartid-base" just like PLIC.
>>>         I know most of hardid are calculated by the requesing
>>>         address, so most hartid usages should be fine.
>>>         But I saw two places using qemu_get_cpu(),
>>>         which may cause the problem for the scenario I describe above:
>>>         i.e. riscv_clic_next_interrupt() and riscv_clic_realize() as
>>>         my original reply.
>>
>>         So what's the problem here?
>>
>>         Currently all cores share the same CLIC instance. Do you want
>>         to give each core  a CLIC instance?
>>
>>     Yes, that's what I mean, which should be supported as what spec
>>     says[1]:
>>       The CLIC complements the PLIC. Smaller single-core systems
>>     might have only a CLIC,
>>       while multicore systems might have *a CLIC per-core* and a
>>     single shared PLIC.
>>       The PLIC xeip signals are treated as hart-local interrupt
>>     sources by the CLIC at each core.
>>
>>     [1]
>>     https://github.com/riscv/riscv-fast-interrupt/blob/646310a5e4ae055964b4680f12c1c04a7cc0dd56/clic.adoc#12-clic-versus-plic
>>     <https://github.com/riscv/riscv-fast-interrupt/blob/646310a5e4ae055964b4680f12c1c04a7cc0dd56/clic.adoc#12-clic-versus-plic>
>>
>>     Thanks,
>>     Frank Chang
>
>     If we give each core a CLIC instance, it is not convenient to
>     access the shared memory, such as 0x0-0x1000.
>     Which CLIC instance should contain this memory region?
>
> What do you mean by: "access the shared memory" here?

It means the cliccfg or clicinfo which  should be shared by all CLIC 
instances.

Thanks,
Zhiwei

> I thought the memory region is defined during CLIC's creation?
> So it should depend on the platform that creates CLIC instances.
>
> Thanks,
> Frank Chang
>
>     Thanks,
>     Zhiwei
>
>>
>>         Thanks,
>>         Zhiwei
>>
>>>         Regards,
>>>         Frank Chang
>>>
>>>>             However if you try to bind CPU reference start from
>>>>             index i = 0.
>>>>             It's not possible for each per-core CLIC to bind their
>>>>             own CPU instance in multicore system
>>>>             as they have to bind from CPU 0.
>>>>
>>>>             I'm not sure if we add a new "hartid-base" property
>>>>             just like what SiFive PLIC is
>>>>             implemented would be a good idea or not.
>>>>
>>>>
>>>>             Regards,
>>>>             Frank Chang
>>>>
>>>>                 +        qemu_irq irq =
>>>>                 qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>>>>                 +            &cpu->env, 1);
>>>>                 + qdev_connect_gpio_out(dev, i, irq);
>>>>                 +        cpu->env.clic = clic;
>>>>                 +    }
>>>>                 +}
>>>>                 +
>>>>
>>>>

[-- Attachment #2: Type: text/html, Size: 20813 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-06-28  8:01                 ` LIU Zhiwei
@ 2021-06-28  8:07                   ` Frank Chang
  2021-06-28  8:11                     ` LIU Zhiwei
  0 siblings, 1 reply; 81+ messages in thread
From: Frank Chang @ 2021-06-28  8:07 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: open list:RISC-V, Frank Chang, qemu-devel@nongnu.org Developers,
	wxy194768, Palmer Dabbelt, Alistair Francis

[-- Attachment #1: Type: text/plain, Size: 6208 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午4:03寫道:

>
> On 2021/6/28 下午3:49, Frank Chang wrote:
>
> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午3:40寫道:
>
>>
>> On 2021/6/28 下午3:23, Frank Chang wrote:
>>
>> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午3:17寫道:
>>
>>>
>>> On 2021/6/26 下午8:56, Frank Chang wrote:
>>>
>>> On Wed, Jun 16, 2021 at 10:56 AM LIU Zhiwei <zhiwei_liu@c-sky.com>
>>> wrote:
>>>
>>>>
>>>> On 6/13/21 6:10 PM, Frank Chang wrote:
>>>>
>>>> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:57寫道:
>>>>
>>>> +static void riscv_clic_realize(DeviceState *dev, Error **errp)
>>>>> +{
>>>>> +    RISCVCLICState *clic = RISCV_CLIC(dev);
>>>>> +    size_t harts_x_sources = clic->num_harts * clic->num_sources;
>>>>> +    int irqs, i;
>>>>> +
>>>>> +    if (clic->prv_s && clic->prv_u) {
>>>>> +        irqs = 3 * harts_x_sources;
>>>>> +    } else if (clic->prv_s || clic->prv_u) {
>>>>> +        irqs = 2 * harts_x_sources;
>>>>> +    } else {
>>>>> +        irqs = harts_x_sources;
>>>>> +    }
>>>>> +
>>>>> +    clic->clic_size = irqs * 4 + 0x1000;
>>>>> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops,
>>>>> clic,
>>>>> +                          TYPE_RISCV_CLIC, clic->clic_size);
>>>>> +
>>>>> +    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);
>>>>> +    clic->active_count = g_new0(size_t, clic->num_harts);
>>>>> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
>>>>> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
>>>>> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
>>>>> +
>>>>> +    /* Allocate irq 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_irqs, clic->num_harts);
>>>>> +
>>>>> +    for (i = 0; i < clic->num_harts; i++) {
>>>>> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>>>>>
>>>>
>>>> As spec says:
>>>>   Smaller single-core systems might have only a CLIC,
>>>>   while multicore systems might have a CLIC per-core and a single
>>>> shared PLIC.
>>>>   The PLIC xeip signals are treated as hart-local interrupt sources by
>>>> the CLIC at each core.
>>>>
>>>> It looks like it's possible to have one CLIC instance per core.
>>>>
>>>> If you want to delivery an interrupt to one hart, you should encode the
>>>> IRQ by the interrupt number
>>>> , the hart number and the interrupt target privilege, then set the irq.
>>>>
>>>> I think how to calculate the hart number is the task of PLIC and it can
>>>> make use of "hartid-base"
>>>> to calculate it.
>>>>
>>>> Thanks,
>>>> Zhiwei
>>>>
>>>
>>> Hi Zhiwei,
>>>
>>> What I mean is if there are multiple CLIC instances, each per core (CLIC
>>> spec allows that).
>>> If you try to bind CLIC with CPU index start from 0,
>>> it will be impossible for CLIC instance to bind CPU from index other
>>> than 0.
>>>
>>> For example, for 4 cores system, it's possible to have 4 CLIC instances:
>>>   * CLIC 0 binds to CPU 0
>>>   * CLIC 1 binds to CPU 1
>>>   * CLIC 2 binds to CPU 2
>>>   * CLIC 3 binds to CPU 3
>>>
>>> and that's why I said it's possible to pass an extra "hartid-base" just
>>> like PLIC.
>>> I know most of hardid are calculated by the requesing address, so most
>>> hartid usages should be fine.
>>> But I saw two places using qemu_get_cpu(),
>>>
>>> which may cause the problem for the scenario I describe above:
>>> i.e. riscv_clic_next_interrupt() and riscv_clic_realize() as my original
>>> reply.
>>>
>>> So what's the problem here?
>>>
>>> Currently all cores share the same CLIC instance. Do you want to give
>>> each core  a CLIC instance?
>>>
>> Yes, that's what I mean, which should be supported as what spec says[1]:
>>   The CLIC complements the PLIC. Smaller single-core systems might have
>> only a CLIC,
>>   while multicore systems might have *a CLIC per-core* and a single
>> shared PLIC.
>>   The PLIC xeip signals are treated as hart-local interrupt sources by
>> the CLIC at each core.
>>
>> [1]
>> https://github.com/riscv/riscv-fast-interrupt/blob/646310a5e4ae055964b4680f12c1c04a7cc0dd56/clic.adoc#12-clic-versus-plic
>>
>> Thanks,
>> Frank Chang
>>
>>
>> If we give each core a CLIC instance, it is not convenient to access the
>> shared memory, such as 0x0-0x1000.
>> Which CLIC instance should contain this memory region?
>>
> What do you mean by: "access the shared memory" here?
>
> It means the cliccfg or clicinfo which  should be shared by all CLIC
> instances.
>
If there are multiple CLIC instances, shouldn't they have their own base
addresses?
So I do not understand how cliccfg and clicinfo would be shared by all CLIC
instances. (Or they should?)
Each CLIC instance will manage its own cliccfg and clicinfo.

Thanks,
Frank Chang

Thanks,
> Zhiwei
>
> I thought the memory region is defined during CLIC's creation?
> So it should depend on the platform that creates CLIC instances.
>
> Thanks,
> Frank Chang
>
>
>> Thanks,
>> Zhiwei
>>
>>
>>> Thanks,
>>> Zhiwei
>>>
>> Regards,
>>> Frank Chang
>>>
>>>
>>>> However if you try to bind CPU reference start from index i = 0.
>>>> It's not possible for each per-core CLIC to bind their own CPU instance
>>>> in multicore system
>>>> as they have to bind from CPU 0.
>>>>
>>>> I'm not sure if we add a new "hartid-base" property just like what
>>>> SiFive PLIC is
>>>> implemented would be a good idea or not.
>>>>
>>>>
>>>> Regards,
>>>> Frank Chang
>>>>
>>>>
>>>>> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>>>>> +                                         &cpu->env, 1);
>>>>> +        qdev_connect_gpio_out(dev, i, irq);
>>>>> +        cpu->env.clic = clic;
>>>>> +    }
>>>>> +}
>>>>> +
>>>>>
>>>>>
>>>>>

[-- Attachment #2: Type: text/html, Size: 20749 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-06-28  8:07                   ` Frank Chang
@ 2021-06-28  8:11                     ` LIU Zhiwei
  2021-06-28  8:19                       ` Frank Chang
  0 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-06-28  8:11 UTC (permalink / raw)
  To: Frank Chang
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V, wxy194768,
	qemu-devel@nongnu.org Developers

[-- Attachment #1: Type: text/plain, Size: 9054 bytes --]


On 2021/6/28 下午4:07, Frank Chang wrote:
> LIU Zhiwei <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> 於 
> 2021年6月28日 週一 下午4:03寫道:
>
>
>     On 2021/6/28 下午3:49, Frank Chang wrote:
>>     LIU Zhiwei <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> 於
>>     2021年6月28日 週一 下午3:40寫道:
>>
>>
>>         On 2021/6/28 下午3:23, Frank Chang wrote:
>>>         LIU Zhiwei <zhiwei_liu@c-sky.com
>>>         <mailto:zhiwei_liu@c-sky.com>> 於 2021年6月28日 週一
>>>         下午3:17寫道:
>>>
>>>
>>>             On 2021/6/26 下午8:56, Frank Chang wrote:
>>>>             On Wed, Jun 16, 2021 at 10:56 AM LIU Zhiwei
>>>>             <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> wrote:
>>>>
>>>>
>>>>                 On 6/13/21 6:10 PM, Frank Chang wrote:
>>>>>                 LIU Zhiwei <zhiwei_liu@c-sky.com
>>>>>                 <mailto:zhiwei_liu@c-sky.com>> 於 2021年4月9日 週五
>>>>>                 下午3:57寫道:
>>>>>
>>>>>                     +static void riscv_clic_realize(DeviceState
>>>>>                     *dev, Error **errp)
>>>>>                     +{
>>>>>                     +    RISCVCLICState *clic = RISCV_CLIC(dev);
>>>>>                     +    size_t harts_x_sources = clic->num_harts
>>>>>                     * clic->num_sources;
>>>>>                     +    int irqs, i;
>>>>>                     +
>>>>>                     +    if (clic->prv_s && clic->prv_u) {
>>>>>                     +        irqs = 3 * harts_x_sources;
>>>>>                     +    } else if (clic->prv_s || clic->prv_u) {
>>>>>                     +        irqs = 2 * harts_x_sources;
>>>>>                     +    } else {
>>>>>                     +        irqs = harts_x_sources;
>>>>>                     +    }
>>>>>                     +
>>>>>                     + clic->clic_size = irqs * 4 + 0x1000;
>>>>>                     + memory_region_init_io(&clic->mmio,
>>>>>                     OBJECT(dev), &riscv_clic_ops, clic,
>>>>>                     + TYPE_RISCV_CLIC, clic->clic_size);
>>>>>                     +
>>>>>                     + 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);
>>>>>                     + clic->active_count = g_new0(size_t,
>>>>>                     clic->num_harts);
>>>>>                     + clic->exccode = g_new0(uint32_t,
>>>>>                     clic->num_harts);
>>>>>                     + clic->cpu_irqs = g_new0(qemu_irq,
>>>>>                     clic->num_harts);
>>>>>                     + sysbus_init_mmio(SYS_BUS_DEVICE(dev),
>>>>>                     &clic->mmio);
>>>>>                     +
>>>>>                     +    /* Allocate irq 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_irqs,
>>>>>                     clic->num_harts);
>>>>>                     +
>>>>>                     +    for (i = 0; i < clic->num_harts; i++) {
>>>>>                     +        RISCVCPU *cpu =
>>>>>                     RISCV_CPU(qemu_get_cpu(i));
>>>>>
>>>>>
>>>>>                 As spec says:
>>>>>                   Smaller single-core systems might have only a CLIC,
>>>>>                   while multicore systems might have a CLIC
>>>>>                 per-core and a single shared PLIC.
>>>>>                   The PLIC xeip signals are treated as hart-local
>>>>>                 interrupt sources by the CLIC at each core.
>>>>>
>>>>>                 It looks like it's possible to have one CLIC
>>>>>                 instance per core.
>>>>
>>>>                 If you want to delivery an interrupt to one hart,
>>>>                 you should encode the IRQ by the interrupt number
>>>>                 , the hart number and the interrupt target
>>>>                 privilege, then set the irq.
>>>>
>>>>                 I think how to calculate the hart number is the
>>>>                 task of PLIC and it can make use of "hartid-base"
>>>>                 to calculate it.
>>>>
>>>>                 Thanks,
>>>>                 Zhiwei
>>>>
>>>>
>>>>             Hi Zhiwei,
>>>>
>>>>             What I mean is if there are multiple CLIC instances,
>>>>             each per core (CLIC spec allows that).
>>>>             If you try to bind CLIC with CPU index start from 0,
>>>>             it will be impossible for CLIC instance to bind CPU
>>>>             from index other than 0.
>>>>
>>>>             For example, for 4 cores system, it's possible to have
>>>>             4 CLIC instances:
>>>>               * CLIC 0 binds to CPU 0
>>>>               * CLIC 1 binds to CPU 1
>>>>               * CLIC 2 binds to CPU 2
>>>>               * CLIC 3 binds to CPU 3
>>>>
>>>>             and that's why I said it's possible to pass an extra
>>>>             "hartid-base" just like PLIC.
>>>>             I know most of hardid are calculated by the requesing
>>>>             address, so most hartid usages should be fine.
>>>>             But I saw two places using qemu_get_cpu(),
>>>>             which may cause the problem for the scenario I describe
>>>>             above:
>>>>             i.e. riscv_clic_next_interrupt() and
>>>>             riscv_clic_realize() as my original reply.
>>>
>>>             So what's the problem here?
>>>
>>>             Currently all cores share the same CLIC instance. Do you
>>>             want to give each core  a CLIC instance?
>>>
>>>         Yes, that's what I mean, which should be supported as what
>>>         spec says[1]:
>>>           The CLIC complements the PLIC. Smaller single-core systems
>>>         might have only a CLIC,
>>>           while multicore systems might have *a CLIC per-core* and a
>>>         single shared PLIC.
>>>           The PLIC xeip signals are treated as hart-local interrupt
>>>         sources by the CLIC at each core.
>>>
>>>         [1]
>>>         https://github.com/riscv/riscv-fast-interrupt/blob/646310a5e4ae055964b4680f12c1c04a7cc0dd56/clic.adoc#12-clic-versus-plic
>>>         <https://github.com/riscv/riscv-fast-interrupt/blob/646310a5e4ae055964b4680f12c1c04a7cc0dd56/clic.adoc#12-clic-versus-plic>
>>>
>>>         Thanks,
>>>         Frank Chang
>>
>>         If we give each core a CLIC instance, it is not convenient to
>>         access the shared memory, such as 0x0-0x1000.
>>         Which CLIC instance should contain this memory region?
>>
>>     What do you mean by: "access the shared memory" here?
>
>     It means the cliccfg or clicinfo which  should be shared by all
>     CLIC instances.
>
> If there are multiple CLIC instances, shouldn't they have their own 
> base addresses?
> So I do not understand how cliccfg and clicinfo would be shared by all 
> CLIC instances. (Or they should?)

Once we have a talk on tech-fast-interrupt. The chair of fast interrupt 
reply is:

/"The first part (address 0x0000-0x0FFF) which contains 
cliccfg/clicinfo/clicinttrig should be global since only one copy of the 
configuration setting is enough.//
//On the other hand, the latter part (0x1000-0x4FFF) which contains 
control bits for individual interrupt should be one copy per hart"/

Thanks,
Zhiwei

> Each CLIC instance will manage its own cliccfg and clicinfo.
>
> Thanks,
> Frank Chang
>
>     Thanks,
>     Zhiwei
>
>>     I thought the memory region is defined during CLIC's creation?
>>     So it should depend on the platform that creates CLIC instances.
>>
>>     Thanks,
>>     Frank Chang
>>
>>         Thanks,
>>         Zhiwei
>>
>>>
>>>             Thanks,
>>>             Zhiwei
>>>
>>>>             Regards,
>>>>             Frank Chang
>>>>
>>>>>                 However if you try to bind CPU reference start
>>>>>                 from index i = 0.
>>>>>                 It's not possible for each per-core CLIC to bind
>>>>>                 their own CPU instance in multicore system
>>>>>                 as they have to bind from CPU 0.
>>>>>
>>>>>                 I'm not sure if we add a new "hartid-base"
>>>>>                 property just like what SiFive PLIC is
>>>>>                 implemented would be a good idea or not.
>>>>>
>>>>>
>>>>>                 Regards,
>>>>>                 Frank Chang
>>>>>
>>>>>                     +        qemu_irq irq =
>>>>>                     qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>>>>>                     +  &cpu->env, 1);
>>>>>                     + qdev_connect_gpio_out(dev, i, irq);
>>>>>                     + cpu->env.clic = clic;
>>>>>                     +    }
>>>>>                     +}
>>>>>                     +
>>>>>
>>>>>

[-- Attachment #2: Type: text/html, Size: 29763 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-06-28  8:11                     ` LIU Zhiwei
@ 2021-06-28  8:19                       ` Frank Chang
  2021-06-28  8:43                         ` LIU Zhiwei
  0 siblings, 1 reply; 81+ messages in thread
From: Frank Chang @ 2021-06-28  8:19 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: open list:RISC-V, Frank Chang, qemu-devel@nongnu.org Developers,
	wxy194768, Alistair Francis, Palmer Dabbelt

[-- Attachment #1: Type: text/plain, Size: 7458 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午4:12寫道:

>
> On 2021/6/28 下午4:07, Frank Chang wrote:
>
> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午4:03寫道:
>
>>
>> On 2021/6/28 下午3:49, Frank Chang wrote:
>>
>> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午3:40寫道:
>>
>>>
>>> On 2021/6/28 下午3:23, Frank Chang wrote:
>>>
>>> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午3:17寫道:
>>>
>>>>
>>>> On 2021/6/26 下午8:56, Frank Chang wrote:
>>>>
>>>> On Wed, Jun 16, 2021 at 10:56 AM LIU Zhiwei <zhiwei_liu@c-sky.com>
>>>> wrote:
>>>>
>>>>>
>>>>> On 6/13/21 6:10 PM, Frank Chang wrote:
>>>>>
>>>>> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:57寫道:
>>>>>
>>>>> +static void riscv_clic_realize(DeviceState *dev, Error **errp)
>>>>>> +{
>>>>>> +    RISCVCLICState *clic = RISCV_CLIC(dev);
>>>>>> +    size_t harts_x_sources = clic->num_harts * clic->num_sources;
>>>>>> +    int irqs, i;
>>>>>> +
>>>>>> +    if (clic->prv_s && clic->prv_u) {
>>>>>> +        irqs = 3 * harts_x_sources;
>>>>>> +    } else if (clic->prv_s || clic->prv_u) {
>>>>>> +        irqs = 2 * harts_x_sources;
>>>>>> +    } else {
>>>>>> +        irqs = harts_x_sources;
>>>>>> +    }
>>>>>> +
>>>>>> +    clic->clic_size = irqs * 4 + 0x1000;
>>>>>> +    memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops,
>>>>>> clic,
>>>>>> +                          TYPE_RISCV_CLIC, clic->clic_size);
>>>>>> +
>>>>>> +    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);
>>>>>> +    clic->active_count = g_new0(size_t, clic->num_harts);
>>>>>> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
>>>>>> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
>>>>>> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
>>>>>> +
>>>>>> +    /* Allocate irq 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_irqs, clic->num_harts);
>>>>>> +
>>>>>> +    for (i = 0; i < clic->num_harts; i++) {
>>>>>> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>>>>>>
>>>>>
>>>>> As spec says:
>>>>>   Smaller single-core systems might have only a CLIC,
>>>>>   while multicore systems might have a CLIC per-core and a single
>>>>> shared PLIC.
>>>>>   The PLIC xeip signals are treated as hart-local interrupt sources by
>>>>> the CLIC at each core.
>>>>>
>>>>> It looks like it's possible to have one CLIC instance per core.
>>>>>
>>>>> If you want to delivery an interrupt to one hart, you should encode
>>>>> the IRQ by the interrupt number
>>>>> , the hart number and the interrupt target privilege, then set the irq.
>>>>>
>>>>> I think how to calculate the hart number is the task of PLIC and it
>>>>> can make use of "hartid-base"
>>>>> to calculate it.
>>>>>
>>>>> Thanks,
>>>>> Zhiwei
>>>>>
>>>>
>>>> Hi Zhiwei,
>>>>
>>>> What I mean is if there are multiple CLIC instances, each per core
>>>> (CLIC spec allows that).
>>>> If you try to bind CLIC with CPU index start from 0,
>>>> it will be impossible for CLIC instance to bind CPU from index other
>>>> than 0.
>>>>
>>>> For example, for 4 cores system, it's possible to have 4 CLIC instances:
>>>>   * CLIC 0 binds to CPU 0
>>>>   * CLIC 1 binds to CPU 1
>>>>   * CLIC 2 binds to CPU 2
>>>>   * CLIC 3 binds to CPU 3
>>>>
>>>> and that's why I said it's possible to pass an extra "hartid-base" just
>>>> like PLIC.
>>>> I know most of hardid are calculated by the requesing address, so most
>>>> hartid usages should be fine.
>>>> But I saw two places using qemu_get_cpu(),
>>>>
>>>> which may cause the problem for the scenario I describe above:
>>>> i.e. riscv_clic_next_interrupt() and riscv_clic_realize() as my
>>>> original reply.
>>>>
>>>> So what's the problem here?
>>>>
>>>> Currently all cores share the same CLIC instance. Do you want to give
>>>> each core  a CLIC instance?
>>>>
>>> Yes, that's what I mean, which should be supported as what spec says[1]:
>>>   The CLIC complements the PLIC. Smaller single-core systems might have
>>> only a CLIC,
>>>   while multicore systems might have *a CLIC per-core* and a single
>>> shared PLIC.
>>>   The PLIC xeip signals are treated as hart-local interrupt sources by
>>> the CLIC at each core.
>>>
>>> [1]
>>> https://github.com/riscv/riscv-fast-interrupt/blob/646310a5e4ae055964b4680f12c1c04a7cc0dd56/clic.adoc#12-clic-versus-plic
>>>
>>> Thanks,
>>> Frank Chang
>>>
>>>
>>> If we give each core a CLIC instance, it is not convenient to access the
>>> shared memory, such as 0x0-0x1000.
>>> Which CLIC instance should contain this memory region?
>>>
>> What do you mean by: "access the shared memory" here?
>>
>> It means the cliccfg or clicinfo which  should be shared by all CLIC
>> instances.
>>
> If there are multiple CLIC instances, shouldn't they have their own base
> addresses?
> So I do not understand how cliccfg and clicinfo would be shared by all
> CLIC instances. (Or they should?)
>
> Once we have a talk on tech-fast-interrupt. The chair of fast interrupt
> reply is:
>
> *"The first part (address 0x0000-0x0FFF) which contains
> cliccfg/clicinfo/clicinttrig should be global since only one copy of the
> configuration setting is enough.*
> *On the other hand, the latter part (0x1000-0x4FFF) which contains control
> bits for individual interrupt should be one copy per hart"*
>
Hmm... interesting, that's probably something I have missed.
and they didn't document this statement in the spec :(

But I think this statement has a contradiction against the system with
multi-CLIC instances described in spec.
Does it imply that either:
  * I can only have one CLIC in the system, or
  * All CLIC instances must have the same configuration in the system.

Do you have the link to this statement? I would like to take a look.

Thanks,
Frank Chang


> Thanks,
> Zhiwei
>
> Each CLIC instance will manage its own cliccfg and clicinfo.
>
> Thanks,
> Frank Chang
>
> Thanks,
>> Zhiwei
>>
>> I thought the memory region is defined during CLIC's creation?
>> So it should depend on the platform that creates CLIC instances.
>>
>> Thanks,
>> Frank Chang
>>
>>
>>> Thanks,
>>> Zhiwei
>>>
>>>
>>>> Thanks,
>>>> Zhiwei
>>>>
>>> Regards,
>>>> Frank Chang
>>>>
>>>>
>>>>> However if you try to bind CPU reference start from index i = 0.
>>>>> It's not possible for each per-core CLIC to bind their own CPU
>>>>> instance in multicore system
>>>>> as they have to bind from CPU 0.
>>>>>
>>>>> I'm not sure if we add a new "hartid-base" property just like what
>>>>> SiFive PLIC is
>>>>> implemented would be a good idea or not.
>>>>>
>>>>>
>>>>> Regards,
>>>>> Frank Chang
>>>>>
>>>>>
>>>>>> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>>>>>> +                                         &cpu->env, 1);
>>>>>> +        qdev_connect_gpio_out(dev, i, irq);
>>>>>> +        cpu->env.clic = clic;
>>>>>> +    }
>>>>>> +}
>>>>>> +
>>>>>>
>>>>>>
>>>>>>

[-- Attachment #2: Type: text/html, Size: 29278 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-06-28  8:19                       ` Frank Chang
@ 2021-06-28  8:43                         ` LIU Zhiwei
  2021-06-28  9:11                           ` Frank Chang
  0 siblings, 1 reply; 81+ messages in thread
From: LIU Zhiwei @ 2021-06-28  8:43 UTC (permalink / raw)
  To: Frank Chang
  Cc: Alistair Francis, Palmer Dabbelt, open list:RISC-V, wxy194768,
	qemu-devel@nongnu.org Developers

[-- Attachment #1: Type: text/plain, Size: 11003 bytes --]


On 2021/6/28 下午4:19, Frank Chang wrote:
> LIU Zhiwei <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> 於 
> 2021年6月28日 週一 下午4:12寫道:
>
>
>     On 2021/6/28 下午4:07, Frank Chang wrote:
>>     LIU Zhiwei <zhiwei_liu@c-sky.com <mailto:zhiwei_liu@c-sky.com>> 於
>>     2021年6月28日 週一 下午4:03寫道:
>>
>>
>>         On 2021/6/28 下午3:49, Frank Chang wrote:
>>>         LIU Zhiwei <zhiwei_liu@c-sky.com
>>>         <mailto:zhiwei_liu@c-sky.com>> 於 2021年6月28日 週一
>>>         下午3:40寫道:
>>>
>>>
>>>             On 2021/6/28 下午3:23, Frank Chang wrote:
>>>>             LIU Zhiwei <zhiwei_liu@c-sky.com
>>>>             <mailto:zhiwei_liu@c-sky.com>> 於 2021年6月28日 週一
>>>>             下午3:17寫道:
>>>>
>>>>
>>>>                 On 2021/6/26 下午8:56, Frank Chang wrote:
>>>>>                 On Wed, Jun 16, 2021 at 10:56 AM LIU Zhiwei
>>>>>                 <zhiwei_liu@c-sky.com
>>>>>                 <mailto:zhiwei_liu@c-sky.com>> wrote:
>>>>>
>>>>>
>>>>>                     On 6/13/21 6:10 PM, Frank Chang wrote:
>>>>>>                     LIU Zhiwei <zhiwei_liu@c-sky.com
>>>>>>                     <mailto:zhiwei_liu@c-sky.com>> 於 2021年4月9日
>>>>>>                     週五 下午3:57寫道:
>>>>>>
>>>>>>                         +static void
>>>>>>                         riscv_clic_realize(DeviceState *dev,
>>>>>>                         Error **errp)
>>>>>>                         +{
>>>>>>                         + RISCVCLICState *clic = RISCV_CLIC(dev);
>>>>>>                         +    size_t harts_x_sources =
>>>>>>                         clic->num_harts * clic->num_sources;
>>>>>>                         +    int irqs, i;
>>>>>>                         +
>>>>>>                         +    if (clic->prv_s && clic->prv_u) {
>>>>>>                         +        irqs = 3 * harts_x_sources;
>>>>>>                         +    } else if (clic->prv_s || clic->prv_u) {
>>>>>>                         +        irqs = 2 * harts_x_sources;
>>>>>>                         +    } else {
>>>>>>                         +        irqs = harts_x_sources;
>>>>>>                         +    }
>>>>>>                         +
>>>>>>                         + clic->clic_size = irqs * 4 + 0x1000;
>>>>>>                         + memory_region_init_io(&clic->mmio,
>>>>>>                         OBJECT(dev), &riscv_clic_ops, clic,
>>>>>>                         + TYPE_RISCV_CLIC, clic->clic_size);
>>>>>>                         +
>>>>>>                         + 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);
>>>>>>                         + clic->active_count = g_new0(size_t,
>>>>>>                         clic->num_harts);
>>>>>>                         + clic->exccode = g_new0(uint32_t,
>>>>>>                         clic->num_harts);
>>>>>>                         + clic->cpu_irqs = g_new0(qemu_irq,
>>>>>>                         clic->num_harts);
>>>>>>                         + sysbus_init_mmio(SYS_BUS_DEVICE(dev),
>>>>>>                         &clic->mmio);
>>>>>>                         +
>>>>>>                         +    /* Allocate irq 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_irqs,
>>>>>>                         clic->num_harts);
>>>>>>                         +
>>>>>>                         +    for (i = 0; i < clic->num_harts; i++) {
>>>>>>                         + RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>>>>>>
>>>>>>
>>>>>>                     As spec says:
>>>>>>                       Smaller single-core systems might have only
>>>>>>                     a CLIC,
>>>>>>                       while multicore systems might have a CLIC
>>>>>>                     per-core and a single shared PLIC.
>>>>>>                       The PLIC xeip signals are treated as
>>>>>>                     hart-local interrupt sources by the CLIC at
>>>>>>                     each core.
>>>>>>
>>>>>>                     It looks like it's possible to have one CLIC
>>>>>>                     instance per core.
>>>>>
>>>>>                     If you want to delivery an interrupt to one
>>>>>                     hart, you should encode the IRQ by the
>>>>>                     interrupt number
>>>>>                     , the hart number and the interrupt target
>>>>>                     privilege, then set the irq.
>>>>>
>>>>>                     I think how to calculate the hart number is
>>>>>                     the task of PLIC and it can make use of
>>>>>                     "hartid-base"
>>>>>                     to calculate it.
>>>>>
>>>>>                     Thanks,
>>>>>                     Zhiwei
>>>>>
>>>>>
>>>>>                 Hi Zhiwei,
>>>>>
>>>>>                 What I mean is if there are multiple CLIC
>>>>>                 instances, each per core (CLIC spec allows that).
>>>>>                 If you try to bind CLIC with CPU index start from 0,
>>>>>                 it will be impossible for CLIC instance to bind
>>>>>                 CPU from index other than 0.
>>>>>
>>>>>                 For example, for 4 cores system, it's possible to
>>>>>                 have 4 CLIC instances:
>>>>>                   * CLIC 0 binds to CPU 0
>>>>>                   * CLIC 1 binds to CPU 1
>>>>>                   * CLIC 2 binds to CPU 2
>>>>>                   * CLIC 3 binds to CPU 3
>>>>>
>>>>>                 and that's why I said it's possible to pass an
>>>>>                 extra "hartid-base" just like PLIC.
>>>>>                 I know most of hardid are calculated by the
>>>>>                 requesing address, so most hartid usages should be
>>>>>                 fine.
>>>>>                 But I saw two places using qemu_get_cpu(),
>>>>>                 which may cause the problem for the scenario I
>>>>>                 describe above:
>>>>>                 i.e. riscv_clic_next_interrupt() and
>>>>>                 riscv_clic_realize() as my original reply.
>>>>
>>>>                 So what's the problem here?
>>>>
>>>>                 Currently all cores share the same CLIC instance.
>>>>                 Do you want to give each core  a CLIC instance?
>>>>
>>>>             Yes, that's what I mean, which should be supported as
>>>>             what spec says[1]:
>>>>               The CLIC complements the PLIC. Smaller single-core
>>>>             systems might have only a CLIC,
>>>>               while multicore systems might have *a CLIC per-core*
>>>>             and a single shared PLIC.
>>>>               The PLIC xeip signals are treated as hart-local
>>>>             interrupt sources by the CLIC at each core.
>>>>
>>>>             [1]
>>>>             https://github.com/riscv/riscv-fast-interrupt/blob/646310a5e4ae055964b4680f12c1c04a7cc0dd56/clic.adoc#12-clic-versus-plic
>>>>             <https://github.com/riscv/riscv-fast-interrupt/blob/646310a5e4ae055964b4680f12c1c04a7cc0dd56/clic.adoc#12-clic-versus-plic>
>>>>
>>>>             Thanks,
>>>>             Frank Chang
>>>
>>>             If we give each core a CLIC instance, it is not
>>>             convenient to access the shared memory, such as 0x0-0x1000.
>>>             Which CLIC instance should contain this memory region?
>>>
>>>         What do you mean by: "access the shared memory" here?
>>
>>         It means the cliccfg or clicinfo which should be shared by
>>         all CLIC instances.
>>
>>     If there are multiple CLIC instances, shouldn't they have their
>>     own base addresses?
>>     So I do not understand how cliccfg and clicinfo would be shared
>>     by all CLIC instances. (Or they should?)
>
>     Once we have a talk on tech-fast-interrupt. The chair of fast
>     interrupt reply is:
>
>     /"The first part (address 0x0000-0x0FFF) which contains
>     cliccfg/clicinfo/clicinttrig should be global since only one copy
>     of the configuration setting is enough.//
>     //On the other hand, the latter part (0x1000-0x4FFF) which
>     contains control bits for individual interrupt should be one copy
>     per hart"/
>
> Hmm... interesting, that's probably something I have missed.
> and they didn't document this statement in the spec :(
>
> But I think this statement has a contradiction against the system with 
> multi-CLIC instances described in spec.
> Does it imply that either:
>   * I can only have one CLIC in the system, or
>   * All CLIC instances must have the same configuration in the system.

The second one.

I think the CLIC instance here is just on the concept of logic, like the 
current implementation. Furthermore, we can give
every logic CLIC instance a configurable memory region from the machine 
board in the near future.

Thanks,
Zhiwei

> Do you have the link to this statement? I would like to take a look.
>
> Thanks,
> Frank Chang
>
>
>     Thanks,
>     Zhiwei
>
>>     Each CLIC instance will manage its own cliccfg and clicinfo.
>>
>>     Thanks,
>>     Frank Chang
>>
>>         Thanks,
>>         Zhiwei
>>
>>>         I thought the memory region is defined during CLIC's creation?
>>>         So it should depend on the platform that creates CLIC instances.
>>>
>>>         Thanks,
>>>         Frank Chang
>>>
>>>             Thanks,
>>>             Zhiwei
>>>
>>>>
>>>>                 Thanks,
>>>>                 Zhiwei
>>>>
>>>>>                 Regards,
>>>>>                 Frank Chang
>>>>>
>>>>>>                     However if you try to bind CPU reference
>>>>>>                     start from index i = 0.
>>>>>>                     It's not possible for each per-core CLIC to
>>>>>>                     bind their own CPU instance in multicore system
>>>>>>                     as they have to bind from CPU 0.
>>>>>>
>>>>>>                     I'm not sure if we add a new "hartid-base"
>>>>>>                     property just like what SiFive PLIC is
>>>>>>                     implemented would be a good idea or not.
>>>>>>
>>>>>>
>>>>>>                     Regards,
>>>>>>                     Frank Chang
>>>>>>
>>>>>>                         + qemu_irq irq =
>>>>>>                         qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>>>>>>                         +  &cpu->env, 1);
>>>>>>                         + qdev_connect_gpio_out(dev, i, irq);
>>>>>>                         + cpu->env.clic = clic;
>>>>>>                         +    }
>>>>>>                         +}
>>>>>>                         +
>>>>>>
>>>>>>

[-- Attachment #2: Type: text/html, Size: 39851 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-06-28  8:43                         ` LIU Zhiwei
@ 2021-06-28  9:11                           ` Frank Chang
  0 siblings, 0 replies; 81+ messages in thread
From: Frank Chang @ 2021-06-28  9:11 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: open list:RISC-V, Frank Chang, qemu-devel@nongnu.org Developers,
	wxy194768, Palmer Dabbelt, Alistair Francis

[-- Attachment #1: Type: text/plain, Size: 8569 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午4:47寫道:

>
> On 2021/6/28 下午4:19, Frank Chang wrote:
>
> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午4:12寫道:
>
>>
>> On 2021/6/28 下午4:07, Frank Chang wrote:
>>
>> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午4:03寫道:
>>
>>>
>>> On 2021/6/28 下午3:49, Frank Chang wrote:
>>>
>>> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午3:40寫道:
>>>
>>>>
>>>> On 2021/6/28 下午3:23, Frank Chang wrote:
>>>>
>>>> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年6月28日 週一 下午3:17寫道:
>>>>
>>>>>
>>>>> On 2021/6/26 下午8:56, Frank Chang wrote:
>>>>>
>>>>> On Wed, Jun 16, 2021 at 10:56 AM LIU Zhiwei <zhiwei_liu@c-sky.com>
>>>>> wrote:
>>>>>
>>>>>>
>>>>>> On 6/13/21 6:10 PM, Frank Chang wrote:
>>>>>>
>>>>>> LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:57寫道:
>>>>>>
>>>>>> +static void riscv_clic_realize(DeviceState *dev, Error **errp)
>>>>>>> +{
>>>>>>> +    RISCVCLICState *clic = RISCV_CLIC(dev);
>>>>>>> +    size_t harts_x_sources = clic->num_harts * clic->num_sources;
>>>>>>> +    int irqs, i;
>>>>>>> +
>>>>>>> +    if (clic->prv_s && clic->prv_u) {
>>>>>>> +        irqs = 3 * harts_x_sources;
>>>>>>> +    } else if (clic->prv_s || clic->prv_u) {
>>>>>>> +        irqs = 2 * harts_x_sources;
>>>>>>> +    } else {
>>>>>>> +        irqs = harts_x_sources;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    clic->clic_size = irqs * 4 + 0x1000;
>>>>>>> +    memory_region_init_io(&clic->mmio, OBJECT(dev),
>>>>>>> &riscv_clic_ops, clic,
>>>>>>> +                          TYPE_RISCV_CLIC, clic->clic_size);
>>>>>>> +
>>>>>>> +    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);
>>>>>>> +    clic->active_count = g_new0(size_t, clic->num_harts);
>>>>>>> +    clic->exccode = g_new0(uint32_t, clic->num_harts);
>>>>>>> +    clic->cpu_irqs = g_new0(qemu_irq, clic->num_harts);
>>>>>>> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio);
>>>>>>> +
>>>>>>> +    /* Allocate irq 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_irqs, clic->num_harts);
>>>>>>> +
>>>>>>> +    for (i = 0; i < clic->num_harts; i++) {
>>>>>>> +        RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
>>>>>>>
>>>>>>
>>>>>> As spec says:
>>>>>>   Smaller single-core systems might have only a CLIC,
>>>>>>   while multicore systems might have a CLIC per-core and a single
>>>>>> shared PLIC.
>>>>>>   The PLIC xeip signals are treated as hart-local interrupt sources
>>>>>> by the CLIC at each core.
>>>>>>
>>>>>> It looks like it's possible to have one CLIC instance per core.
>>>>>>
>>>>>> If you want to delivery an interrupt to one hart, you should encode
>>>>>> the IRQ by the interrupt number
>>>>>> , the hart number and the interrupt target privilege, then set the
>>>>>> irq.
>>>>>>
>>>>>> I think how to calculate the hart number is the task of PLIC and it
>>>>>> can make use of "hartid-base"
>>>>>> to calculate it.
>>>>>>
>>>>>> Thanks,
>>>>>> Zhiwei
>>>>>>
>>>>>
>>>>> Hi Zhiwei,
>>>>>
>>>>> What I mean is if there are multiple CLIC instances, each per core
>>>>> (CLIC spec allows that).
>>>>> If you try to bind CLIC with CPU index start from 0,
>>>>> it will be impossible for CLIC instance to bind CPU from index other
>>>>> than 0.
>>>>>
>>>>> For example, for 4 cores system, it's possible to have 4 CLIC
>>>>> instances:
>>>>>   * CLIC 0 binds to CPU 0
>>>>>   * CLIC 1 binds to CPU 1
>>>>>   * CLIC 2 binds to CPU 2
>>>>>   * CLIC 3 binds to CPU 3
>>>>>
>>>>> and that's why I said it's possible to pass an extra "hartid-base"
>>>>> just like PLIC.
>>>>> I know most of hardid are calculated by the requesing address, so most
>>>>> hartid usages should be fine.
>>>>> But I saw two places using qemu_get_cpu(),
>>>>>
>>>>> which may cause the problem for the scenario I describe above:
>>>>> i.e. riscv_clic_next_interrupt() and riscv_clic_realize() as my
>>>>> original reply.
>>>>>
>>>>> So what's the problem here?
>>>>>
>>>>> Currently all cores share the same CLIC instance. Do you want to give
>>>>> each core  a CLIC instance?
>>>>>
>>>> Yes, that's what I mean, which should be supported as what spec says[1]:
>>>>   The CLIC complements the PLIC. Smaller single-core systems might have
>>>> only a CLIC,
>>>>   while multicore systems might have *a CLIC per-core* and a single
>>>> shared PLIC.
>>>>   The PLIC xeip signals are treated as hart-local interrupt sources by
>>>> the CLIC at each core.
>>>>
>>>> [1]
>>>> https://github.com/riscv/riscv-fast-interrupt/blob/646310a5e4ae055964b4680f12c1c04a7cc0dd56/clic.adoc#12-clic-versus-plic
>>>>
>>>> Thanks,
>>>> Frank Chang
>>>>
>>>>
>>>> If we give each core a CLIC instance, it is not convenient to access
>>>> the shared memory, such as 0x0-0x1000.
>>>> Which CLIC instance should contain this memory region?
>>>>
>>> What do you mean by: "access the shared memory" here?
>>>
>>> It means the cliccfg or clicinfo which  should be shared by all CLIC
>>> instances.
>>>
>> If there are multiple CLIC instances, shouldn't they have their own base
>> addresses?
>> So I do not understand how cliccfg and clicinfo would be shared by all
>> CLIC instances. (Or they should?)
>>
>> Once we have a talk on tech-fast-interrupt. The chair of fast interrupt
>> reply is:
>>
>> *"The first part (address 0x0000-0x0FFF) which contains
>> cliccfg/clicinfo/clicinttrig should be global since only one copy of the
>> configuration setting is enough.*
>> *On the other hand, the latter part (0x1000-0x4FFF) which contains
>> control bits for individual interrupt should be one copy per hart"*
>>
> Hmm... interesting, that's probably something I have missed.
> and they didn't document this statement in the spec :(
>
> But I think this statement has a contradiction against the system with
> multi-CLIC instances described in spec.
> Does it imply that either:
>   * I can only have one CLIC in the system, or
>   * All CLIC instances must have the same configuration in the system.
>
> The second one.
>
> I think the CLIC instance here is just on the concept of logic,  like the
> current implementation. Furthermore, we can give
> every logic CLIC instance a configurable memory region from the machine
> board in the near future.
>
That's possible, but if the CLIC instance is just the concept of logic.
It also implies that we can only instantiate one CLIC device in the system
at a time.
Which this CLIC instance represents the multi-CLIC instances logically.

Maybe just let us put this aside and leave what the current implementation
is for now.
We can update the implementation in the future when the spec is clearer on
this topic :(

Thanks,
Frank Chang


> Thanks,
> Zhiwei
>
> Do you have the link to this statement? I would like to take a look.
>
> Thanks,
> Frank Chang
>
>
>> Thanks,
>> Zhiwei
>>
>> Each CLIC instance will manage its own cliccfg and clicinfo.
>>
>> Thanks,
>> Frank Chang
>>
>> Thanks,
>>> Zhiwei
>>>
>>> I thought the memory region is defined during CLIC's creation?
>>> So it should depend on the platform that creates CLIC instances.
>>>
>>> Thanks,
>>> Frank Chang
>>>
>>>
>>>> Thanks,
>>>> Zhiwei
>>>>
>>>>
>>>>> Thanks,
>>>>> Zhiwei
>>>>>
>>>> Regards,
>>>>> Frank Chang
>>>>>
>>>>>
>>>>>> However if you try to bind CPU reference start from index i = 0.
>>>>>> It's not possible for each per-core CLIC to bind their own CPU
>>>>>> instance in multicore system
>>>>>> as they have to bind from CPU 0.
>>>>>>
>>>>>> I'm not sure if we add a new "hartid-base" property just like what
>>>>>> SiFive PLIC is
>>>>>> implemented would be a good idea or not.
>>>>>>
>>>>>>
>>>>>> Regards,
>>>>>> Frank Chang
>>>>>>
>>>>>>
>>>>>>> +        qemu_irq irq = qemu_allocate_irq(riscv_clic_cpu_irq_handler,
>>>>>>> +                                         &cpu->env, 1);
>>>>>>> +        qdev_connect_gpio_out(dev, i, irq);
>>>>>>> +        cpu->env.clic = clic;
>>>>>>> +    }
>>>>>>> +}
>>>>>>> +
>>>>>>>
>>>>>>>
>>>>>>>

[-- Attachment #2: Type: text/html, Size: 38743 bytes --]

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [RFC PATCH 03/11] hw/intc: Add CLIC device
  2021-04-09  7:48 ` [RFC PATCH 03/11] hw/intc: Add CLIC device LIU Zhiwei
                     ` (4 preceding siblings ...)
  2021-06-26 17:15   ` Frank Chang
@ 2021-06-28 10:16   ` Frank Chang
  2021-06-28 12:56     ` LIU Zhiwei
  2021-06-28 10:24   ` Frank Chang
  2021-07-13  6:53   ` Frank Chang
  7 siblings, 1 reply; 81+ messages in thread
From: Frank Chang @ 2021-06-28 10:16 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, open list:RISC-V,
	qemu-devel@nongnu.org Developers, wxy194768

[-- Attachment #1: Type: text/plain, Size: 39587 bytes --]

LIU Zhiwei <zhiwei_liu@c-sky.com> 於 2021年4月9日 週五 下午3:57寫道:

> 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>
> ---
>  default-configs/devices/riscv32-softmmu.mak |   1 +
>  default-configs/devices/riscv64-softmmu.mak |   1 +
>  hw/intc/Kconfig                             |   3 +
>  hw/intc/meson.build                         |   1 +
>  hw/intc/riscv_clic.c                        | 835 ++++++++++++++++++++
>  include/hw/intc/riscv_clic.h                | 103 +++
>  target/riscv/cpu.h                          |   2 +
>  7 files changed, 946 insertions(+)
>  create mode 100644 hw/intc/riscv_clic.c
>  create mode 100644 include/hw/intc/riscv_clic.h
>
> diff --git a/default-configs/devices/riscv32-softmmu.mak
> b/default-configs/devices/riscv32-softmmu.mak
> index d847bd5692..1430c30588 100644
> --- a/default-configs/devices/riscv32-softmmu.mak
> +++ b/default-configs/devices/riscv32-softmmu.mak
> @@ -5,6 +5,7 @@
>  #CONFIG_PCI_DEVICES=n
>  CONFIG_SEMIHOSTING=y
>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> +CONFIG_RISCV_CLIC=y
>
>  # Boards:
>  #
> diff --git a/default-configs/devices/riscv64-softmmu.mak
> b/default-configs/devices/riscv64-softmmu.mak
> index d5eec75f05..396800bbbd 100644
> --- a/default-configs/devices/riscv64-softmmu.mak
> +++ b/default-configs/devices/riscv64-softmmu.mak
> @@ -5,6 +5,7 @@
>  #CONFIG_PCI_DEVICES=n
>  CONFIG_SEMIHOSTING=y
>  CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> +CONFIG_RISCV_CLIC=y
>
>  # Boards:
>  #
> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
> index f4694088a4..5bf492b48f 100644
> --- a/hw/intc/Kconfig
> +++ b/hw/intc/Kconfig
> @@ -68,6 +68,9 @@ config SIFIVE_CLINT
>  config SIFIVE_PLIC
>      bool
>
> +config RISCV_CLIC
> +    bool
> +
>  config GOLDFISH_PIC
>      bool
>
> diff --git a/hw/intc/meson.build b/hw/intc/meson.build
> index 1c299039f6..2aa71b6738 100644
> --- a/hw/intc/meson.build
> +++ b/hw/intc/meson.build
> @@ -50,6 +50,7 @@ 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_SIFIVE_CLINT', if_true:
> files('sifive_clint.c'))
>  specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true:
> files('sifive_plic.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'))
> diff --git a/hw/intc/riscv_clic.c b/hw/intc/riscv_clic.c
> new file mode 100644
> index 0000000000..8ad534c506
> --- /dev/null
> +++ b/hw/intc/riscv_clic.c
> @@ -0,0 +1,835 @@
> +/*
> + * RISC-V CLIC(Core Local Interrupt Controller) for QEMU.
> + *
> + * Copyright (c) 2021 T-Head Semiconductor Co., Ltd. All rights reserved.
> + *
> + * 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/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/log.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"
> +
> +/*
> + * 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_offset)
> +{
> +    return (clic->clicintattr[irq_offset] >> 1) & 0x3;
> +}
> +
> +static inline bool
> +riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
> +{
> +    return (clic->clicintattr[irq_offset] >> 1) & 0x1;
> +}
> +
> +static inline bool
> +riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
> +{
> +    return (clic->clicintattr[irq_offset] & 0x1) && clic->nvbits;
> +}
> +
> +static uint8_t
> +riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
> +{
> +    int nlbits = clic->nlbits;
> +
> +    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->nlbits;
> +    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);
> +}
> +
> +/*
> + * In a system with multiple harts, the M-mode CLIC regions for all the
> harts
> + * are placed contiguously in the memory space, followed by the S-mode
> CLIC
> + * regions for all harts. (Section 3.11)
> + */
> +static size_t
> +riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int hartid, int
> irq)
> +{
> +    size_t mode_offset = 0;
> +    size_t unit = clic->num_harts * clic->num_sources;
> +
> +    switch (mode) {
> +    case PRV_M:
> +        mode_offset = 0;
> +        break;
> +    case PRV_S:
> +        mode_offset = unit;
> +        break;
> +    case PRV_U:
> +        mode_offset = clic->prv_s ? 2 * unit : unit;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid mode %d\n", mode);
> +        exit(1);
> +    }
> +    return mode_offset + hartid * clic->num_sources + irq;
> +}
> +
> +static void riscv_clic_next_interrupt(void *opaque, int hartid)
> +{
> +    /*
> +     * 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
> +     */
> +    RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(hartid));
> +    CPURISCVState *env = &cpu->env;
> +    RISCVCLICState *clic = (RISCVCLICState *)opaque;
> +
> +    int il[4] = {
> +        MAX(get_field(env->mintstatus, MINTSTATUS_UIL),
> +            clic->mintthresh), /* PRV_U */
> +        MAX(get_field(env->mintstatus, MINTSTATUS_SIL),
> +            clic->sintthresh), /* PRV_S */
> +        0,                     /* reserverd */
> +        MAX(get_field(env->mintstatus, MINTSTATUS_MIL),
> +            clic->uintthresh)  /* PRV_M */
> +    };
> +
> +    /* Get sorted list of enabled interrupts for this hart */
> +    size_t hart_offset = hartid * clic->num_sources;
> +    CLICActiveInterrupt *active = &clic->active_list[hart_offset];
> +    size_t active_count = clic->active_count[hartid];
> +    uint8_t mode, level, priority;
> +
> +    /* Loop through the enabled interrupts sorted by mode+priority+level
> */
> +    while (active_count) {
> +        size_t irq_offset;
> +        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;
> +        }
> +        irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> active->irq);
> +        /* Check pending interrupt with high enough mode+priority+level */
> +        if (clic->clicintip[irq_offset]) {
> +            /* Clean vector edge-triggered pending */
> +            if (riscv_clic_is_edge_triggered(clic, irq_offset) &&
> +                riscv_clic_is_shv_interrupt(clic, irq_offset)) {
> +                clic->clicintip[irq_offset] = 0;
> +            }
> +            /* Post pending interrupt for this hart */
> +            clic->exccode[hartid] = active->irq | mode << 12 | level <<
> 14;
> +            qemu_set_irq(clic->cpu_irqs[hartid], 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 hartid, int
> irq)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    if (!clic->prv_s && !clic->prv_u) { /* M */
> +        return mode == PRV_M;
>

Does the mode here to be checked should be the current hart's privilege mode
at the time register is been accessed?

Regards,
Frank Chang


> +    } else if (!clic->prv_s) { /* M/U */
> +        switch (clic->nmbits) {
> +        case 0:
> +            return mode == PRV_M;
> +        case 1:
> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M)
> :
> +                                                          (mode == PRV_U);
> +        default:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "clic: nmbits can only be 0 or 1 for M/U hart");
> +            exit(1);
> +        }
> +    } else { /* M/S/U */
> +        switch (clic->nmbits) {
> +        case 0:
> +            return mode == PRV_M;
> +        case 1:
> +            return clic->clicintattr[irq_offset] & 0x80 ? (mode == PRV_M)
> :
> +                                                          (mode == PRV_S);
> +        case 2:
> +            return mode == clic->clicintattr[irq_offset];
> +        case 3:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "clic: nmbits can only be 0 or 1 or 2 for M/S/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 mode, int hartid, int
> irq)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    return riscv_clic_is_edge_triggered(clic, irq_offset);
> +}
> +
> +static void
> +riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
> +                        int irq, uint64_t value)
> +{
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    clic->clicintip[irq_offset] = !!value;
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +/*
> + * 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, uint64_t
> value)
> +{
> +    int mode = extract64(value, 6, 2);
> +
> +    if (!qtest_enabled()) {
> +        CPURISCVState *env = current_cpu->env_ptr;
> +        if (env->priv < mode) {
> +            return false;
> +        }
> +    }
> +    return true;
> +}
> +
> +static inline int riscv_clic_encode_priority(const CLICActiveInterrupt *i)
> +{
> +    return ((i->intcfg & 0x3ff) << 12) | /* Highest mode+level+priority */
> +           (i->irq & 0xfff);             /* Highest irq number */
> +}
> +
> +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 hartid,
> +                        int irq, uint64_t new_intie)
> +{
> +    size_t hart_offset = hartid * clic->num_sources;
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +    CLICActiveInterrupt *active_list = &clic->active_list[hart_offset];
> +    size_t *active_count = &clic->active_count[hartid];
> +
> +    uint8_t old_intie = clic->clicintie[irq_offset];
> +    clic->clicintie[irq_offset] = !!new_intie;
> +
> +    /* Add to or remove from list of active interrupts */
> +    if (new_intie && !old_intie) {
> +        active_list[*active_count].intcfg = (mode << 8) |
> +                                            clic->clicintctl[irq_offset];
> +        active_list[*active_count].irq = irq;
> +        (*active_count)++;
> +    } else if (!new_intie && old_intie) {
> +        CLICActiveInterrupt key = {
> +            (mode << 8) | clic->clicintctl[irq_offset], irq
> +        };
> +        CLICActiveInterrupt *result = bsearch(&key,
> +                                              active_list, *active_count,
> +                                              sizeof(CLICActiveInterrupt),
> +                                              riscv_clic_active_compare);
> +        size_t elem = (result - active_list) /
> sizeof(CLICActiveInterrupt);
> +        size_t sz = (--(*active_count) - elem) *
> sizeof(CLICActiveInterrupt);
> +        assert(result);
> +        memmove(&result[0], &result[1], sz);
> +    }
> +
> +    /* Sort list of active interrupts */
> +    qsort(active_list, *active_count,
> +          sizeof(CLICActiveInterrupt),
> +          riscv_clic_active_compare);
> +
> +    riscv_clic_next_interrupt(clic, hartid);
> +}
> +
> +static void
> +riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
> +                      uint64_t value, unsigned size,
> +                      int mode, int hartid, int irq)
> +{
> +    int req = extract32(addr, 0, 2);
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +
> +    if (hartid >= clic->num_harts) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
> +                      hartid, addr);
> +        return;
> +    }
> +
> +    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, mode, hartid, irq)) {
> +            /*
> +             * The actual pending bit is located at bit 0 (i.e., the
> +             * leastsignificant 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_offset]) {
> +                riscv_clic_update_intip(clic, mode, hartid, irq, value);
> +            }
> +        }
> +        break;
> +    case 1: /* clicintie[i] */
> +        if (clic->clicintie[irq_offset] != value) {
> +            riscv_clic_update_intie(clic, mode, hartid, irq, value);
> +        }
> +        break;
> +    case 2: /* clicintattr[i] */
> +        if (riscv_clic_validate_intattr(clic, value)) {
> +            if (clic->clicintattr[irq_offset] != value) {
> +                /* When nmbits=2, check WARL */
> +                bool invalid = (clic->nmbits == 2) &&
> +                               (extract64(value, 6, 2) == 0b10);
> +                if (invalid) {
> +                    uint8_t old_mode =
> extract32(clic->clicintattr[irq_offset],
> +                                                 6, 2);
> +                    value = deposit32(value, 6, 2, old_mode);
> +                }
> +                clic->clicintattr[irq_offset] = value;
> +                riscv_clic_next_interrupt(clic, hartid);
> +            }
> +        }
> +        break;
> +    case 3: /* clicintctl[i] */
> +        if (value != clic->clicintctl[irq_offset]) {
> +            clic->clicintctl[irq_offset] = value;
> +            riscv_clic_next_interrupt(clic, hartid);
> +        }
> +        break;
> +    }
> +}
> +
> +static uint64_t
> +riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
> +                     int hartid, int irq)
> +{
> +    int req = extract32(addr, 0, 2);
> +    size_t irq_offset = riscv_clic_get_irq_offset(clic, mode, hartid,
> irq);
> +
> +    if (hartid >= clic->num_harts) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n",
> +                      hartid, addr);
> +        return 0;
> +    }
> +
> +    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] */
> +        return clic->clicintip[irq_offset];
> +    case 1: /* clicintie[i] */
> +        return clic->clicintie[irq_offset];
> +    case 2: /* clicintattr[i] */
> +        /*
> +         * clicintattr register layout
> +         * Bits Field
> +         * 7:6 mode
> +         * 5:3 reserved (WPRI 0)
> +         * 2:1 trig
> +         * 0 shv
> +         */
> +        return clic->clicintattr[irq_offset] & ~0x38;
> +    case 3: /* clicintctrl */
> +        /*
> +         * 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_offset] |
> +               ((1 << (8 - clic->clicintctlbits)) - 1);
> +    }
> +
> +    return 0;
> +}
> +
> +/* Return target interrupt mode */
> +static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int mode = addr / (4 * clic->num_harts * clic->num_sources);
> +    switch (mode) {
> +    case 0:
> +        return PRV_M;
> +    case 1:
> +        assert(clic->prv_s || clic->prv_u);
> +        return clic->prv_s ? PRV_S : PRV_U;
> +    case 2:
> +        assert(clic->prv_s && clic->prv_u);
> +        return PRV_U;
> +    default:
> +        g_assert_not_reached();
> +        break;
> +    }
> +}
> +
> +/* Return target hart id */
> +static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int mode_unit = 4 * clic->num_harts * clic->num_sources;
> +    int hart_unit = 4 * clic->num_sources;
> +
> +    return (addr % mode_unit) / hart_unit;
> +}
> +
> +/* Return target interrupt number */
> +static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
> +{
> +    int hart_unit = 4 * clic->num_sources;
> +    return (addr % hart_unit) / 4;
> +}
> +
> +static void
> +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
> +{
> +    RISCVCLICState *clic = opaque;
> +    hwaddr clic_size = clic->clic_size;
> +    int hartid, mode, irq;
> +
> +    if (addr < clic_size) {
> +        if (addr < 0x1000) {
> +            assert(addr % 4 == 0);
> +            int index = addr / 4;
> +            switch (index) {
> +            case 0: /* cliccfg */
> +                {
> +                    uint8_t nlbits = extract32(value, 1, 4);
> +                    uint8_t nmbits = extract32(value, 5, 2);
> +
> +                    /*
> +                     * The 4-bit cliccfg.nlbits WARL field.
> +                     * Valid values are 0—8.
> +                     */
> +                    if (nlbits <= 8) {
> +                        clic->nlbits = nlbits;
> +                    }
> +                    /* Valid values are given by implemented priviledges
> */
> +                    if (clic->prv_s && clic->prv_u) {
> +                        if (nmbits <= 2) {
> +                            clic->nmbits = nmbits;
> +                        }
> +                    } else if (clic->prv_u) {
> +                        if (nmbits <= 1) {
> +                            clic->nmbits = nmbits;
> +                        }
> +                    } else {
> +                        assert(!clic->prv_s);
> +                        if (nmbits == 0) {
> +                            clic->nmbits = 0;
> +                        }
> +                    }
> +                    clic->nvbits = extract32(value, 0, 1);
> +                    break;
> +                }
> +            case 1: /* clicinfo, read-only register */
> +                qemu_log_mask(LOG_GUEST_ERROR,
> +                              "clic: write read-only clicinfo.\n");
> +                break;
> +            case 0x10 ... 0x2F: /* clicinttrig */
> +                {
> +                    uint32_t interrupt_number = value &
> MAKE_64BIT_MASK(0, 13);
> +                    if (interrupt_number <= clic->num_sources) {
> +                        value &= ~MAKE_64BIT_MASK(13, 18);
> +                        clic->clicinttrig[index - 0x10] = value;
> +                    }
> +                    break;
> +                }
> +            case 2: /* mintthresh */
> +                if (!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 -= 0x1000;
> +            hartid = riscv_clic_get_hartid(clic, addr);
> +            mode = riscv_clic_get_mode(clic, addr);
> +            irq = riscv_clic_get_irq(clic, addr);
> +
> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
> +                riscv_clic_hart_write(clic, addr, value, size, mode,
> +                                      hartid, irq);
> +            }
> +        }
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid write: 0x%" HWADDR_PRIx "\n", addr);
> +    }
> +}
> +
> +static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    RISCVCLICState *clic = opaque;
> +    hwaddr clic_size = clic->clic_size;
> +    int hartid, mode, irq;
> +
> +    if (addr < clic_size) {
> +        if (addr < 0x1000) {
> +            assert(addr % 4 == 0);
> +            int index = addr / 4;
> +            switch (index) {
> +            case 0: /* cliccfg */
> +                return clic->nvbits |
> +                       (clic->nlbits << 1) |
> +                       (clic->nmbits << 5);
> +            case 1: /* clicinfo */
> +                /*
> +                 * clicinfo register layout
> +                 *
> +                 * Bits Field
> +                 * 31 reserved (WARL 0)
> +                 * 30:25 num_trigger
> +                 * 24:21 CLICINTCTLBITS
> +                 * 20:13 version (for version control)
> +                 * 12:0 num_interrupt
> +                 */
> +                return clic->clicinfo & ~INT32_MAX;
> +            case 0x10 ... 0x2F: /* clicinttrig */
> +                /*
> +                 * clicinttrig register layout
> +                 *
> +                 * Bits Field
> +                 * 31 enable
> +                 * 30:13 reserved (WARL 0)
> +                 * 12:0 interrupt_number
> +                 */
> +                return clic->clicinttrig[index - 0x10] &
> +                       ~MAKE_64BIT_MASK(13, 18);
> +            case 2: /* mintthresh */
> +                if (!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 -= 0x1000;
> +            hartid = riscv_clic_get_hartid(clic, addr);
> +            mode = riscv_clic_get_mode(clic, addr);
> +            irq = riscv_clic_get_irq(clic, addr);
> +
> +            if (riscv_clic_check_visible(clic, mode, hartid, irq)) {
> +                return riscv_clic_hart_read(clic, addr, mode, hartid,
> irq);
> +            }
> +        }
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "clic: invalid read: 0x%" HWADDR_PRIx "\n", addr);
> +    }
> +    return 0;
> +}
> +
> +static void riscv_clic_set_irq(void *opaque, int id, int level)
> +{
> +    RISCVCLICState *clic = opaque;
> +    int irq, hartid, mode;
> +    hwaddr addr = 4 * id;
> +    TRIG_TYPE type;
> +
> +    hartid = riscv_clic_get_hartid(clic, addr);
> +    mode = riscv_clic_get_mode(clic, addr);
> +    irq = riscv_clic_get_irq(clic, addr);
> +    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, mode, hartid, irq, level);
> +            break;
> +        case NEG_LEVEL:
> +            riscv_clic_update_intip(clic, mode, hartid, irq, !level);
> +            break;
> +        case NEG_EDGE:
> +            break;
> +        }
> +    } else {
> +        switch (type) {
> +        case POSITIVE_LEVEL:
> +            r