From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,HTML_MESSAGE,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id DFA7CC07E95 for ; Tue, 13 Jul 2021 06:55:56 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 0529C611C0 for ; Tue, 13 Jul 2021 06:55:55 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 0529C611C0 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=sifive.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:46390 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1m3CKg-0003b2-Sb for qemu-devel@archiver.kernel.org; Tue, 13 Jul 2021 02:55:54 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:52258) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1m3CIx-0001tv-Dk for qemu-devel@nongnu.org; Tue, 13 Jul 2021 02:54:07 -0400 Received: from mail-pf1-x434.google.com ([2607:f8b0:4864:20::434]:39813) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1m3CIq-0005tl-KF for qemu-devel@nongnu.org; Tue, 13 Jul 2021 02:54:06 -0400 Received: by mail-pf1-x434.google.com with SMTP id b12so18652767pfv.6 for ; Mon, 12 Jul 2021 23:53:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sifive.com; s=google; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=4i5R1Fn3YChKePIjEES2GmtvuFSZL3TYgdCZ+Buf4Bg=; b=nOxYTr0pDqPZtfQ1RaF1P4KnEViLFD0She9QhCY0zdWwymnuPRAj34lS8mUObBxnIP xW3RNWtkiZEvpblDVqpDBH5wkgCTx2K4AzO5uEUfY0WZY8Ub51dkcNNIzL4VR/yhf261 O9M0RRoi2yuycXmvBNEbuPschpzvmEEA+dXdAjszwJTVhMgmsM6GDLra08o6h56JOEa1 xGmwB3RQ+VMmFOKKJTofjnXpGTWktjz4XmgIYjSdvfm6dqlLVNMiDxQMnzTkVW6bkYl3 +VqvdvTvSrc9OpXXJxjK8s9FQ1ECisjuDhgQwvZdFt8jHDMWR3bxPijBbDolvh5GCwES ZgAw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=4i5R1Fn3YChKePIjEES2GmtvuFSZL3TYgdCZ+Buf4Bg=; b=tUe7K6PRGK0JasV9STL74+erRTMLc/gY6e7H5nKkSiAaP9/yS+0bEiZkrCqn9U4TY+ 7154Zsuz74StnzjkNcUHfhpmwTzXjBbdk04bzhqQ//41gEectmOYVAauptu2K7AKdRDe zFLzyiNcCKq1nlm2BWJC9WOWvI8ONDdA6qy/R/pvZioaCgakl8cx96tv42GyWvBTu1uQ IoARyw2PtNvKqTDkWRRO3sHioT2+lZ5sEFtkAyHm1KNCId7fWerPz/BVSTz4iXiKVchi qO5bfjr3QKD9JIUoD3zgzo8fK2TTbF8EEkvzOg3iqCmM5N+HAx+RWsjzgCNbe++oGJ1L Upcg== X-Gm-Message-State: AOAM53366+faYzwu9U8aTIjC/Bb1UUl1nFtSdnHTuMEQ5WOgrsbc38u6 tRgKUHuoAhmRwDtV82PBZY9weSTY6XVhjRKv X-Google-Smtp-Source: ABdhPJwMH2hBF9twtTHCEIyncesdtp1b4E7xqrd02VQwyDk+70d6JwXMUZ8x5JarNqR10YYa1JZziw== X-Received: by 2002:a63:1d42:: with SMTP id d2mr2932078pgm.21.1626159238106; Mon, 12 Jul 2021 23:53:58 -0700 (PDT) Received: from mail-pg1-f176.google.com (mail-pg1-f176.google.com. [209.85.215.176]) by smtp.gmail.com with ESMTPSA id g141sm18307719pfb.210.2021.07.12.23.53.56 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 12 Jul 2021 23:53:56 -0700 (PDT) Received: by mail-pg1-f176.google.com with SMTP id t9so20784244pgn.4; Mon, 12 Jul 2021 23:53:56 -0700 (PDT) X-Received: by 2002:a63:655:: with SMTP id 82mr2921421pgg.133.1626159236209; Mon, 12 Jul 2021 23:53:56 -0700 (PDT) MIME-Version: 1.0 References: <20210409074857.166082-1-zhiwei_liu@c-sky.com> <20210409074857.166082-4-zhiwei_liu@c-sky.com> In-Reply-To: <20210409074857.166082-4-zhiwei_liu@c-sky.com> From: Frank Chang Date: Tue, 13 Jul 2021 14:53:44 +0800 X-Gmail-Original-Message-ID: Message-ID: Subject: Re: [RFC PATCH 03/11] hw/intc: Add CLIC device To: LIU Zhiwei Content-Type: multipart/alternative; boundary="0000000000009f629105c6fbb3f6" Received-SPF: pass client-ip=2607:f8b0:4864:20::434; envelope-from=frank.chang@sifive.com; helo=mail-pf1-x434.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Palmer Dabbelt , Alistair Francis , "open list:RISC-V" , "qemu-devel@nongnu.org Developers" , wxy194768@alibaba-inc.com Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" --0000000000009f629105c6fbb3f6 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable LIU Zhiwei =E6=96=BC 2021=E5=B9=B44=E6=9C=889=E6=97= =A5 =E9=80=B1=E4=BA=94 =E4=B8=8B=E5=8D=883:57=E5=AF=AB=E9=81=93=EF=BC=9A > 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 > --- > 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=3Dn > CONFIG_SEMIHOSTING=3Dy > CONFIG_ARM_COMPATIBLE_SEMIHOSTING=3Dy > +CONFIG_RISCV_CLIC=3Dy > > # 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=3Dn > CONFIG_SEMIHOSTING=3Dy > CONFIG_ARM_COMPATIBLE_SEMIHOSTING=3Dy > +CONFIG_RISCV_CLIC=3Dy > > # 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 WITHOU= T > + * 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 . > + */ > + > +#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 =3D clic->nlbits; > + > + uint8_t mask_il =3D ((1 << nlbits) - 1) << (8 - nlbits); > + uint8_t mask_padding =3D (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 =3D clic->clicintctlbits - clic->nlbits; > + uint8_t mask_priority =3D ((1 << npbits) - 1) << (8 - npbits); > + uint8_t mask_padding =3D (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 =3D intcfg >> 8; > + *level =3D riscv_clic_get_interrupt_level(clic, intcfg & 0xff); > + *priority =3D 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, in= t > irq) > +{ > + size_t mode_offset =3D 0; > + size_t unit =3D clic->num_harts * clic->num_sources; > + > + switch (mode) { > + case PRV_M: > + mode_offset =3D 0; > + break; > + case PRV_S: > + mode_offset =3D unit; > + break; > + case PRV_U: > + mode_offset =3D 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 =3D RISCV_CPU(qemu_get_cpu(hartid)); > + CPURISCVState *env =3D &cpu->env; > + RISCVCLICState *clic =3D (RISCVCLICState *)opaque; > + > + int il[4] =3D { > + 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 =3D hartid * clic->num_sources; > + CLICActiveInterrupt *active =3D &clic->active_list[hart_offset]; > + size_t active_count =3D 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 =3D=3D env->priv && level <=3D il[= mode])) > { > + /* > + * No pending interrupts with high enough mode+priority+leve= l > + * break and clear pending interrupt for this hart > + */ > + break; > + } > + irq_offset =3D 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] =3D 0; > + } > + /* Post pending interrupt for this hart */ > + clic->exccode[hartid] =3D active->irq | mode << 12 | level <= < > 14; > + qemu_set_irq(clic->cpu_irqs[hartid], 1); > + return; > + } > + /* Check next enabled interrupt */ > + active_count--; > + active++; > + } > +} > Hi Zhiwei, The global interrupt may be disabled (xstatus.xie =3D 0) at the time active interrupt i is both enabled and pending (i.e. clicintie[i] =3D 1 && clicintip[i] =3D 1) Therefore, interrupt i will not be taken right away until xstatus.xie is set to 1. However, during global interrupt is disabled, SW/HW may either disable or change pending interrupt i to non-pending. Interrupt i should not be active anymore. But current implementation can't reflect that because CPU_INTERRUPT_CLIC bit won't be unset. To solve this issue, in my own implementation, I tried to: In riscv_clic_update_intip(), if interrupt i turns to non-pending, check if interrupt i is an active pending interrupt. (by decoding IRQ number from env->exccode and check if cpu->interrupt_request flag also has CPU_INTERRUPT_CLIC bit set.) If so, interrupt i is current active pending interrupt to be disabled, we have to unset CPU_INTERRUPT_CLIC bit from interrupt request flag for the hart to prevent interrupt i been taken later as it's not pending anymore. Then call riscv_clic_next_interrupt() as usual to pick next interrupt. Same behavior is also applied in riscv_clic_update_intie() when interrupt i is disabled and interrupt i is also the active pending interrupt. Also, I think the effective interrupt level check should be moved from riscv_clic_update_intip() to riscv_cpu_local_irq_mode_enabled() in cpu_helper.c. Consider the following scenario: a. Active pending interrupt i is not been handled due to global interrupt is disabled. b. Software accesses xnxti CSR with write side effect (xstatus.xie is still set to 0). xintstatus.xil will be updated to the interrupt level of interrupt i. c. Interrupt i turns to non-pending/disabled. d. Interrupt j is enabled and pending. e. Interrupt j won't be selected in riscv_clic_next_interrupt() if its interrupt level is lower than interrupt i (xinitstatus.xil still holds the interrupt level of interrupt i), which is incorrect because interrupt i is not pending and active anymore. By moving effective interrupt level check from riscv_clic_update_intip() to riscv_cpu_local_irq_mode_enabled() can eliminate the issue of lower-priority interrupt been masked out by higher-interrupt-level non active interrupt. Let me know if I miss anything. Regards, Frank Chang > + > +/* > + * 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 =3D riscv_clic_get_irq_offset(clic, mode, hartid, > irq); > + if (!clic->prv_s && !clic->prv_u) { /* M */ > + return mode =3D=3D PRV_M; > + } else if (!clic->prv_s) { /* M/U */ > + switch (clic->nmbits) { > + case 0: > + return mode =3D=3D PRV_M; > + case 1: > + return clic->clicintattr[irq_offset] & 0x80 ? (mode =3D=3D P= RV_M) > : > + (mode =3D=3D P= RV_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 =3D=3D PRV_M; > + case 1: > + return clic->clicintattr[irq_offset] & 0x80 ? (mode =3D=3D P= RV_M) > : > + (mode =3D=3D P= RV_S); > + case 2: > + return mode =3D=3D 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, in= t > irq) > +{ > + size_t irq_offset =3D 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 =3D riscv_clic_get_irq_offset(clic, mode, hartid, > irq); > + clic->clicintip[irq_offset] =3D !!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 =3D extract64(value, 6, 2); > + > + if (!qtest_enabled()) { > + CPURISCVState *env =3D 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 =3D hartid * clic->num_sources; > + size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, hartid, > irq); > + CLICActiveInterrupt *active_list =3D &clic->active_list[hart_offset]= ; > + size_t *active_count =3D &clic->active_count[hartid]; > + > + uint8_t old_intie =3D clic->clicintie[irq_offset]; > + clic->clicintie[irq_offset] =3D !!new_intie; > + > + /* Add to or remove from list of active interrupts */ > + if (new_intie && !old_intie) { > + active_list[*active_count].intcfg =3D (mode << 8) | > + clic->clicintctl[irq_offset]= ; > + active_list[*active_count].irq =3D irq; > + (*active_count)++; > + } else if (!new_intie && old_intie) { > + CLICActiveInterrupt key =3D { > + (mode << 8) | clic->clicintctl[irq_offset], irq > + }; > + CLICActiveInterrupt *result =3D bsearch(&key, > + active_list, *active_count= , > + sizeof(CLICActiveInterrupt= ), > + riscv_clic_active_compare)= ; > + size_t elem =3D (result - active_list) / > sizeof(CLICActiveInterrupt); > + size_t sz =3D (--(*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 =3D extract32(addr, 0, 2); > + size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, hartid, > irq); > + > + if (hartid >=3D clic->num_harts) { > + qemu_log_mask(LOG_GUEST_ERROR, > + "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n", > + hartid, addr); > + return; > + } > + > + if (irq >=3D 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]=3Dzero means no > interrupt > + * pending, and clicintip[i]!=3D0 (not just 1) indicates an > + * interrupt is pending. (Section 3.4) > + */ > + if (value !=3D clic->clicintip[irq_offset]) { > + riscv_clic_update_intip(clic, mode, hartid, irq, value); > + } > + } > + break; > + case 1: /* clicintie[i] */ > + if (clic->clicintie[irq_offset] !=3D 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] !=3D value) { > + /* When nmbits=3D2, check WARL */ > + bool invalid =3D (clic->nmbits =3D=3D 2) && > + (extract64(value, 6, 2) =3D=3D 0b10); > + if (invalid) { > + uint8_t old_mode =3D > extract32(clic->clicintattr[irq_offset], > + 6, 2); > + value =3D deposit32(value, 6, 2, old_mode); > + } > + clic->clicintattr[irq_offset] =3D value; > + riscv_clic_next_interrupt(clic, hartid); > + } > + } > + break; > + case 3: /* clicintctl[i] */ > + if (value !=3D clic->clicintctl[irq_offset]) { > + clic->clicintctl[irq_offset] =3D 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 =3D extract32(addr, 0, 2); > + size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, hartid, > irq); > + > + if (hartid >=3D clic->num_harts) { > + qemu_log_mask(LOG_GUEST_ERROR, > + "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n", > + hartid, addr); > + return 0; > + } > + > + if (irq >=3D 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 =3D 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 =3D 4 * clic->num_harts * clic->num_sources; > + int hart_unit =3D 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 =3D 4 * clic->num_sources; > + return (addr % hart_unit) / 4; > +} > + > +static void > +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned siz= e) > +{ > + RISCVCLICState *clic =3D opaque; > + hwaddr clic_size =3D clic->clic_size; > + int hartid, mode, irq; > + > + if (addr < clic_size) { > + if (addr < 0x1000) { > + assert(addr % 4 =3D=3D 0); > + int index =3D addr / 4; > + switch (index) { > + case 0: /* cliccfg */ > + { > + uint8_t nlbits =3D extract32(value, 1, 4); > + uint8_t nmbits =3D extract32(value, 5, 2); > + > + /* > + * The 4-bit cliccfg.nlbits WARL field. > + * Valid values are 0=E2=80=948. > + */ > + if (nlbits <=3D 8) { > + clic->nlbits =3D nlbits; > + } > + /* Valid values are given by implemented priviledges > */ > + if (clic->prv_s && clic->prv_u) { > + if (nmbits <=3D 2) { > + clic->nmbits =3D nmbits; > + } > + } else if (clic->prv_u) { > + if (nmbits <=3D 1) { > + clic->nmbits =3D nmbits; > + } > + } else { > + assert(!clic->prv_s); > + if (nmbits =3D=3D 0) { > + clic->nmbits =3D 0; > + } > + } > + clic->nvbits =3D 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 =3D value & > MAKE_64BIT_MASK(0, 13); > + if (interrupt_number <=3D clic->num_sources) { > + value &=3D ~MAKE_64BIT_MASK(13, 18); > + clic->clicinttrig[index - 0x10] =3D value; > + } > + break; > + } > + case 2: /* mintthresh */ > + if (!strcmp(clic->version, "v0.8")) { > + clic->mintthresh =3D value; > + break; > + } > + qemu_log_mask(LOG_GUEST_ERROR, > + "clic: invalid write addr: 0x%" HWADDR_PRI= x > "\n", > + addr); > + break; > + default: > + qemu_log_mask(LOG_GUEST_ERROR, > + "clic: invalid write addr: 0x%" HWADDR_PRI= x > "\n", > + addr); > + return; > + } > + } else { > + addr -=3D 0x1000; > + hartid =3D riscv_clic_get_hartid(clic, addr); > + mode =3D riscv_clic_get_mode(clic, addr); > + irq =3D 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 =3D opaque; > + hwaddr clic_size =3D clic->clic_size; > + int hartid, mode, irq; > + > + if (addr < clic_size) { > + if (addr < 0x1000) { > + assert(addr % 4 =3D=3D 0); > + int index =3D 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 -=3D 0x1000; > + hartid =3D riscv_clic_get_hartid(clic, addr); > + mode =3D riscv_clic_get_mode(clic, addr); > + irq =3D 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 =3D opaque; > + int irq, hartid, mode; > + hwaddr addr =3D 4 * id; > + TRIG_TYPE type; > + > + hartid =3D riscv_clic_get_hartid(clic, addr); > + mode =3D riscv_clic_get_mode(clic, addr); > + irq =3D riscv_clic_get_irq(clic, addr); > + type =3D 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 pendin= g > 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 =3D (CPURISCVState *)opaque; > + RISCVCLICState *clic =3D env->clic; > + CPUState *cpu =3D env_cpu(env); > + > + if (level) { > + env->exccode =3D clic->exccode[cpu->cpu_index]; > + cpu_interrupt(env_cpu(env), CPU_INTERRUPT_CLIC); > + } > +} > + > +static const MemoryRegionOps riscv_clic_ops =3D { > + .read =3D riscv_clic_read, > + .write =3D riscv_clic_write, > + .endianness =3D DEVICE_LITTLE_ENDIAN, > + .valid =3D { > + .min_access_size =3D 1, > + .max_access_size =3D 8 > + } > +}; > + > +static void riscv_clic_realize(DeviceState *dev, Error **errp) > +{ > + RISCVCLICState *clic =3D RISCV_CLIC(dev); > + size_t harts_x_sources =3D clic->num_harts * clic->num_sources; > + int irqs, i; > + > + if (clic->prv_s && clic->prv_u) { > + irqs =3D 3 * harts_x_sources; > + } else if (clic->prv_s || clic->prv_u) { > + irqs =3D 2 * harts_x_sources; > + } else { > + irqs =3D harts_x_sources; > + } > + > + clic->clic_size =3D irqs * 4 + 0x1000; > + memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops, cli= c, > + TYPE_RISCV_CLIC, clic->clic_size); > + > + clic->clicintip =3D g_new0(uint8_t, irqs); > + clic->clicintie =3D g_new0(uint8_t, irqs); > + clic->clicintattr =3D g_new0(uint8_t, irqs); > + clic->clicintctl =3D g_new0(uint8_t, irqs); > + clic->active_list =3D g_new0(CLICActiveInterrupt, irqs); > + clic->active_count =3D g_new0(size_t, clic->num_harts); > + clic->exccode =3D g_new0(uint32_t, clic->num_harts); > + clic->cpu_irqs =3D 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 =3D 0; i < clic->num_harts; i++) { > + RISCVCPU *cpu =3D RISCV_CPU(qemu_get_cpu(i)); > + qemu_irq irq =3D qemu_allocate_irq(riscv_clic_cpu_irq_handler, > + &cpu->env, 1); > + qdev_connect_gpio_out(dev, i, irq); > + cpu->env.clic =3D clic; > + } > +} > + > +static Property riscv_clic_properties[] =3D { > + 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 =3D DEVICE_CLASS(klass); > + > + dc->realize =3D riscv_clic_realize; > + device_class_set_props(dc, riscv_clic_properties); > +} > + > +static const TypeInfo riscv_clic_info =3D { > + .name =3D TYPE_RISCV_CLIC, > + .parent =3D TYPE_SYS_BUS_DEVICE, > + .instance_size =3D sizeof(RISCVCLICState), > + .class_init =3D 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 =3D qdev_new(TYPE_RISCV_CLIC); > + > + assert(num_sources <=3D 4096); > + assert(num_harts <=3D 1024); > + assert(clicintctlbits <=3D 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 =3D opaque; > + riscv_clic_next_interrupt(clic, hartid); > +} > + > +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int ir= q) > +{ > + RISCVCLICState *clic =3D opaque; > + size_t irq_offset =3D 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 =3D opaque; > + size_t irq_offset =3D 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 ir= q) > +{ > + RISCVCLICState *clic =3D opaque; > + size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, hartid, > irq); > + clic->clicintip[irq_offset] =3D 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 =3D (env->priv =3D=3D PRV_M) ? env->mtvec : env->= stvec; > + return env->clic && ((xtvec & 0x3) =3D=3D 3); > +} > + > +void riscv_clic_decode_exccode(uint32_t exccode, int *mode, > + int *il, int *irq) > +{ > + *irq =3D extract32(exccode, 0, 12); > + *mode =3D extract32(exccode, 12, 2); > + *il =3D 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 WITHOU= T > + * 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 . > + */ > + > +#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 > > > --0000000000009f629105c6fbb3f6 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
LIU Zhiwei <zhiwei_liu@c-sky.com> =E6=96=BC 2021=E5=B9=B44=E6=9C= =889=E6=97=A5 =E9=80=B1=E4=BA=94 =E4=B8=8B=E5=8D=883:57=E5=AF=AB=E9=81=93= =EF=BC=9A
The Core-Local Interrupt Controller (CLIC) provides lo= w-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>
---
=C2=A0default-configs/devices/riscv32-softmmu.mak |=C2=A0 =C2=A01 +
=C2=A0default-configs/devices/riscv64-softmmu.mak |=C2=A0 =C2=A01 +
=C2=A0hw/intc/Kconfig=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 =C2=A03 +
=C2=A0hw/intc/meson.build=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 =C2=A01 +
=C2=A0hw/intc/riscv_clic.c=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | 835 ++++++++++++++++++++
=C2=A0include/hw/intc/riscv_clic.h=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 | 103 +++
=C2=A0target/riscv/cpu.h=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A02 +
=C2=A07 files changed, 946 insertions(+)
=C2=A0create mode 100644 hw/intc/riscv_clic.c
=C2=A0create 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 @@
=C2=A0#CONFIG_PCI_DEVICES=3Dn
=C2=A0CONFIG_SEMIHOSTING=3Dy
=C2=A0CONFIG_ARM_COMPATIBLE_SEMIHOSTING=3Dy
+CONFIG_RISCV_CLIC=3Dy

=C2=A0# Boards:
=C2=A0#
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 @@
=C2=A0#CONFIG_PCI_DEVICES=3Dn
=C2=A0CONFIG_SEMIHOSTING=3Dy
=C2=A0CONFIG_ARM_COMPATIBLE_SEMIHOSTING=3Dy
+CONFIG_RISCV_CLIC=3Dy

=C2=A0# Boards:
=C2=A0#
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
=C2=A0config SIFIVE_PLIC
=C2=A0 =C2=A0 =C2=A0bool

+config RISCV_CLIC
+=C2=A0 =C2=A0 bool
+
=C2=A0config GOLDFISH_PIC
=C2=A0 =C2=A0 =C2=A0bool

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'))
=C2=A0specific_ss.add(when: 'CONFIG_SH_INTC', if_true: files('s= h_intc.c'))
=C2=A0specific_ss.add(when: 'CONFIG_SIFIVE_CLINT', if_true: files(&= #39;sifive_clint.c'))
=C2=A0specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files(&#= 39;sifive_plic.c'))
+specific_ss.add(when: 'CONFIG_RISCV_CLIC', if_true: files('ris= cv_clic.c'))
=C2=A0specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics= .c'))
=C2=A0specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'],<= br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 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.<= br> + *
+ * 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<= br> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.=C2=A0 See the GNU General Public Lice= nse for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along= with
+ * this program.=C2=A0 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 e= ach
+ * interrupt input. Bit 1, trig[0], is defined as "edge-triggered&quo= t;
+ * (0: level-triggered, 1: edge-triggered); while bit 2, trig[1], is defin= ed as
+ * "negative-edge" (0: positive-edge, 1: negative-edge). (Sectio= n 3.6)
+ */
+
+static inline TRIG_TYPE
+riscv_clic_get_trigger_type(RISCVCLICState *clic, size_t irq_offset)
+{
+=C2=A0 =C2=A0 return (clic->clicintattr[irq_offset] >> 1) & 0= x3;
+}
+
+static inline bool
+riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
+{
+=C2=A0 =C2=A0 return (clic->clicintattr[irq_offset] >> 1) & 0= x1;
+}
+
+static inline bool
+riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
+{
+=C2=A0 =C2=A0 return (clic->clicintattr[irq_offset] & 0x1) &&am= p; clic->nvbits;
+}
+
+static uint8_t
+riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
+{
+=C2=A0 =C2=A0 int nlbits =3D clic->nlbits;
+
+=C2=A0 =C2=A0 uint8_t mask_il =3D ((1 << nlbits) - 1) << (8 - = nlbits);
+=C2=A0 =C2=A0 uint8_t mask_padding =3D (1 << (8 - nlbits)) - 1;
+=C2=A0 =C2=A0 /* unused level bits are set to 1 */
+=C2=A0 =C2=A0 return (intctl & mask_il) | mask_padding;
+}
+
+static uint8_t
+riscv_clic_get_interrupt_priority(RISCVCLICState *clic, uint8_t intctl) +{
+=C2=A0 =C2=A0 int npbits =3D clic->clicintctlbits - clic->nlbits; +=C2=A0 =C2=A0 uint8_t mask_priority =3D ((1 << npbits) - 1) <<= (8 - npbits);
+=C2=A0 =C2=A0 uint8_t mask_padding =3D (1 << (8 - npbits)) - 1;
+
+=C2=A0 =C2=A0 if (npbits < 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return UINT8_MAX;
+=C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 /* unused priority bits are set to 1 */
+=C2=A0 =C2=A0 return (intctl & mask_priority) | mask_padding;
+}
+
+static void
+riscv_clic_intcfg_decode(RISCVCLICState *clic, uint16_t intcfg,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0uint8_t *mode, uint8_t *level,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0uint8_t *priority)
+{
+=C2=A0 =C2=A0 *mode =3D intcfg >> 8;
+=C2=A0 =C2=A0 *level =3D riscv_clic_get_interrupt_level(clic, intcfg &= 0xff);
+=C2=A0 =C2=A0 *priority =3D riscv_clic_get_interrupt_priority(clic, intcfg= & 0xff);
+}
+
+/*
+ * In a system with multiple harts, the M-mode CLIC regions for all the ha= rts
+ * are placed contiguously in the memory space, followed by the S-mode CLI= C
+ * regions for all harts. (Section 3.11)
+ */
+static size_t
+riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int hartid, int = irq)
+{
+=C2=A0 =C2=A0 size_t mode_offset =3D 0;
+=C2=A0 =C2=A0 size_t unit =3D clic->num_harts * clic->num_sources; +
+=C2=A0 =C2=A0 switch (mode) {
+=C2=A0 =C2=A0 case PRV_M:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 mode_offset =3D 0;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 case PRV_S:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 mode_offset =3D unit;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 case PRV_U:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 mode_offset =3D clic->prv_s ? 2 * unit : un= it;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "clic: invalid mode %d\n", mode);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 exit(1);
+=C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 return mode_offset + hartid * clic->num_sources + irq; +}
+
+static void riscv_clic_next_interrupt(void *opaque, int hartid)
+{
+=C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0* Scan active list for highest priority pending interr= upts
+=C2=A0 =C2=A0 =C2=A0* comparing against this harts mintstatus register and= interrupt
+=C2=A0 =C2=A0 =C2=A0* the core if we have a higher priority interrupt to d= eliver
+=C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 RISCVCPU *cpu =3D RISCV_CPU(qemu_get_cpu(hartid));
+=C2=A0 =C2=A0 CPURISCVState *env =3D &cpu->env;
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D (RISCVCLICState *)opaque;
+
+=C2=A0 =C2=A0 int il[4] =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 MAX(get_field(env->mintstatus, MINTSTATUS_U= IL),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic->mintthresh), /* PRV_U *= /
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 MAX(get_field(env->mintstatus, MINTSTATUS_S= IL),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic->sintthresh), /* PRV_S *= /
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 0,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* reserverd */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 MAX(get_field(env->mintstatus, MINTSTATUS_M= IL),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic->uintthresh)=C2=A0 /* PR= V_M */
+=C2=A0 =C2=A0 };
+
+=C2=A0 =C2=A0 /* Get sorted list of enabled interrupts for this hart */ +=C2=A0 =C2=A0 size_t hart_offset =3D hartid * clic->num_sources;
+=C2=A0 =C2=A0 CLICActiveInterrupt *active =3D &clic->active_list[ha= rt_offset];
+=C2=A0 =C2=A0 size_t active_count =3D clic->active_count[hartid];
+=C2=A0 =C2=A0 uint8_t mode, level, priority;
+
+=C2=A0 =C2=A0 /* Loop through the enabled interrupts sorted by mode+priori= ty+level */
+=C2=A0 =C2=A0 while (active_count) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 size_t irq_offset;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_intcfg_decode(clic, active->intc= fg, &mode, &level,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0&priority);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (mode < env->priv || (mode =3D=3D env= ->priv && level <=3D il[mode])) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* No pending interrupts wi= th high enough mode+priority+level
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* break and clear pending = interrupt for this hart
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 irq_offset =3D riscv_clic_get_irq_offset(clic,= mode, hartid, active->irq);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Check pending interrupt with high enough mo= de+priority+level */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (clic->clicintip[irq_offset]) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Clean vector edge-triggered p= ending */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (riscv_clic_is_edge_triggered= (clic, irq_offset) &&
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_is_shv_= interrupt(clic, irq_offset)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic->clicintip= [irq_offset] =3D 0;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Post pending interrupt for th= is hart */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic->exccode[hartid] =3D act= ive->irq | mode << 12 | level << 14;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_set_irq(clic->cpu_irqs[h= artid], 1);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Check next enabled interrupt */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 active_count--;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 active++;
+=C2=A0 =C2=A0 }
+}

Hi Zhiwei,

= The global interrupt may be disabled (xstatus.xie =3D 0) at the time active= interrupt i
is both enabled and pending (i.e. clicintie[i] = =3D 1 && clicintip[i] =3D 1)
Therefore, interrupt i will = not be taken right away until xstatus.xie is set to 1.

=
However, during global interrupt is disabled,
SW/HW may eith= er disable or change pending interrupt i to non-pending.
Interrup= t i should not be active anymore.
But current implementation can&= #39;t reflect that because
CPU_INTERRUPT_CLIC bit won't be un= set.

To solve this issue, in my own implementation= , I tried to:

In riscv_clic_update_intip(), if int= errupt i turns to non-pending,
check if interrupt i is an act= ive pending interrupt.
(by decoding IRQ number from env->excco= de and check if
cpu->interrupt_request flag also has CPU_INTER= RUPT_CLIC bit set.)

If so, interrupt i is current = active pending interrupt to be disabled, we have to
unset CPU_INT= ERRUPT_CLIC bit from interrupt request flag for the hart
to p= revent interrupt i been taken later as it's not pending anymore.
Then call riscv_clic_next_interrupt() as usual to pick next interru= pt.

Same behavior is also applied in risc= v_clic_update_intie() when
interrupt i is disabled and interrupt = i is also the active pending interrupt.

Also= , I think the effective interrupt level check should be moved
fro= m riscv_clic_update_intip() to=C2=A0riscv_cpu_local_irq_mode_enabled() in c= pu_helper.c.

Consider the following scenario:
<= /div>
=C2=A0 =C2=A0 a. Active pending interrupt i is not been handled d= ue to global interrupt is disabled.
=C2=A0 =C2=A0 b. Softwar= e accesses xnxti CSR with write side effect (xstatus.xie is still set to 0)= .
=C2=A0 =C2=A0 =C2=A0 =C2=A0 xintstatus.xil will be updated to t= he interrupt level of interrupt i.
=C2=A0 =C2=A0 c. Interru= pt i turns to non-pending/disabled.
=C2=A0 =C2=A0 d. Interrupt j = is enabled and pending.
=C2=A0 =C2=A0 e. Interrupt j won't be= selected in riscv_clic_next_interrupt() if its interrupt level
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 is lower than interrupt i (xinitstatus.xil stil= l holds the interrupt level of interrupt i),
=C2=A0 =C2=A0 =C2=A0= =C2=A0 which is incorrect because interrupt i is not pending and active an= ymore.

By moving effective interrupt level check f= rom=C2=A0
riscv_clic_update_intip() to=C2=A0riscv_cpu_local_irq_m= ode_enabled()
can eliminate the issue of lower-priority interrupt= been masked out
by higher-interrupt-level non active interrupt.<= /div>

Let me know if I miss anything.

Regards,
Frank Chang
=C2=A0
+
+/*
+ * 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 i= rq)
+{
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+=C2=A0 =C2=A0 if (!clic->prv_s && !clic->prv_u) { /* M */ +=C2=A0 =C2=A0 =C2=A0 =C2=A0 return mode =3D=3D PRV_M;
+=C2=A0 =C2=A0 } else if (!clic->prv_s) { /* M/U */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (clic->nmbits) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case 0:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return mode =3D=3D PRV_M;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case 1:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->clicintattr[irq_= offset] & 0x80 ? (mode =3D=3D PRV_M) :
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (mode =3D=3D PRV_U)= ;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR, +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clic: nmbits= can only be 0 or 1 for M/U hart");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 exit(1);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 } else { /* M/S/U */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (clic->nmbits) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case 0:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return mode =3D=3D PRV_M;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case 1:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->clicintattr[irq_= offset] & 0x80 ? (mode =3D=3D PRV_M) :
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (mode =3D=3D PRV_S)= ;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case 2:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return mode =3D=3D clic->clic= intattr[irq_offset];
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case 3:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR, +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clic: nmbits= can only be 0 or 1 or 2 for M/S/U hart");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 exit(1);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 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)
+{
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+=C2=A0 =C2=A0 return riscv_clic_is_edge_triggered(clic, irq_offset);
+}
+
+static void
+riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 int irq, uint64_t value)
+{
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+=C2=A0 =C2=A0 clic->clicintip[irq_offset] =3D !!value;
+=C2=A0 =C2=A0 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 val= ue)
+{
+=C2=A0 =C2=A0 int mode =3D extract64(value, 6, 2);
+
+=C2=A0 =C2=A0 if (!qtest_enabled()) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 CPURISCVState *env =3D current_cpu->env_ptr= ;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (env->priv < mode) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return false;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 return true;
+}
+
+static inline int riscv_clic_encode_priority(const CLICActiveInterrupt *i)=
+{
+=C2=A0 =C2=A0 return ((i->intcfg & 0x3ff) << 12) | /* Highest= mode+level+priority */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(i->irq & 0xfff);=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* Highest irq number */
+}
+
+static int riscv_clic_active_compare(const void *a, const void *b)
+{
+=C2=A0 =C2=A0 return riscv_clic_encode_priority(b) - riscv_clic_encode_pri= ority(a);
+}
+
+static void
+riscv_clic_update_intie(RISCVCLICState *clic, int mode, int hartid,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 int irq, uint64_t new_intie)
+{
+=C2=A0 =C2=A0 size_t hart_offset =3D hartid * clic->num_sources;
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+=C2=A0 =C2=A0 CLICActiveInterrupt *active_list =3D &clic->active_li= st[hart_offset];
+=C2=A0 =C2=A0 size_t *active_count =3D &clic->active_count[hartid];=
+
+=C2=A0 =C2=A0 uint8_t old_intie =3D clic->clicintie[irq_offset];
+=C2=A0 =C2=A0 clic->clicintie[irq_offset] =3D !!new_intie;
+
+=C2=A0 =C2=A0 /* Add to or remove from list of active interrupts */
+=C2=A0 =C2=A0 if (new_intie && !old_intie) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 active_list[*active_count].intcfg =3D (mode &l= t;< 8) |
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 clic->clicintctl[irq_offset];
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 active_list[*active_count].irq =3D irq;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 (*active_count)++;
+=C2=A0 =C2=A0 } else if (!new_intie && old_intie) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 CLICActiveInterrupt key =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (mode << 8) | clic->cli= cintctl[irq_offset], irq
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 };
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 CLICActiveInterrupt *result =3D bsearch(&k= ey,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 active_list, *active_count,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 sizeof(CLICActiveInterrupt),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 riscv_clic_active_compare);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 size_t elem =3D (result - active_list) / sizeo= f(CLICActiveInterrupt);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 size_t sz =3D (--(*active_count) - elem) * siz= eof(CLICActiveInterrupt);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 assert(result);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 memmove(&result[0], &result[1], sz); +=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 /* Sort list of active interrupts */
+=C2=A0 =C2=A0 qsort(active_list, *active_count,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sizeof(CLICActiveInterrupt),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_active_compare);
+
+=C2=A0 =C2=A0 riscv_clic_next_interrupt(clic, hartid);
+}
+
+static void
+riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 uint64_t value, unsigned size,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 int mode, int hartid, int irq)
+{
+=C2=A0 =C2=A0 int req =3D extract32(addr, 0, 2);
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+
+=C2=A0 =C2=A0 if (hartid >=3D clic->num_harts) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n", +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 hartid, addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (irq >=3D clic->num_sources) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "clic: invalid irq %u: 0x%" HWADDR_PRIx "\n", irq, = addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 switch (req) {
+=C2=A0 =C2=A0 case 0: /* clicintip[i] */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (riscv_clic_validate_intip(clic, mode, hart= id, irq)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* The actual pending bit i= s located at bit 0 (i.e., the
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* leastsignificant bit). I= n case future extensions expand the bit
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* field, from FW perspecti= ve clicintip[i]=3Dzero means no interrupt
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* pending, and clicintip[i= ]!=3D0 (not just 1) indicates an
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* interrupt is pending. (S= ection 3.4)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (value !=3D clic->clicinti= p[irq_offset]) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_update_= intip(clic, mode, hartid, irq, value);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 case 1: /* clicintie[i] */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (clic->clicintie[irq_offset] !=3D value)= {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_update_intie(clic, mo= de, hartid, irq, value);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 case 2: /* clicintattr[i] */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (riscv_clic_validate_intattr(clic, value)) = {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (clic->clicintattr[irq_off= set] !=3D value) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* When nmbits=3D2= , check WARL */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 bool invalid =3D (= clic->nmbits =3D=3D 2) &&
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(extract64(value, 6, 2) =3D=3D 0b10);=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (invalid) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 uint= 8_t old_mode =3D extract32(clic->clicintattr[irq_offset],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A06, 2);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 valu= e =3D deposit32(value, 6, 2, old_mode);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic->clicintat= tr[irq_offset] =3D value;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_next_in= terrupt(clic, hartid);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 case 3: /* clicintctl[i] */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (value !=3D clic->clicintctl[irq_offset]= ) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic->clicintctl[irq_offset] = =3D value;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_next_interrupt(clic, = hartid);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 }
+}
+
+static uint64_t
+riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0int hartid, int irq)
+{
+=C2=A0 =C2=A0 int req =3D extract32(addr, 0, 2);
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+
+=C2=A0 =C2=A0 if (hartid >=3D clic->num_harts) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n", +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 hartid, addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return 0;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (irq >=3D clic->num_sources) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "clic: invalid irq %u: 0x%" HWADDR_PRIx "\n", irq, = addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return 0;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 switch (req) {
+=C2=A0 =C2=A0 case 0: /* clicintip[i] */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->clicintip[irq_offset];
+=C2=A0 =C2=A0 case 1: /* clicintie[i] */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->clicintie[irq_offset];
+=C2=A0 =C2=A0 case 2: /* clicintattr[i] */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* clicintattr register layout
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* Bits Field
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 7:6 mode
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 5:3 reserved (WPRI 0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 2:1 trig
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 0 shv
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->clicintattr[irq_offset] & = ~0x38;
+=C2=A0 =C2=A0 case 3: /* clicintctrl */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* The implemented bits are kept left-jus= tified in the most-significant
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* bits of each 8-bit clicintctl[i] regis= ter, with the lower
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* unimplemented bits treated as hardwire= d to 1.(Section 3.7)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->clicintctl[irq_offset] |
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0((1 << (8 - c= lic->clicintctlbits)) - 1);
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 return 0;
+}
+
+/* Return target interrupt mode */
+static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
+{
+=C2=A0 =C2=A0 int mode =3D addr / (4 * clic->num_harts * clic->num_s= ources);
+=C2=A0 =C2=A0 switch (mode) {
+=C2=A0 =C2=A0 case 0:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return PRV_M;
+=C2=A0 =C2=A0 case 1:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 assert(clic->prv_s || clic->prv_u);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->prv_s ? PRV_S : PRV_U;
+=C2=A0 =C2=A0 case 2:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 assert(clic->prv_s && clic->prv_= u);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return PRV_U;
+=C2=A0 =C2=A0 default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_assert_not_reached();
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 }
+}
+
+/* Return target hart id */
+static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
+{
+=C2=A0 =C2=A0 int mode_unit =3D 4 * clic->num_harts * clic->num_sour= ces;
+=C2=A0 =C2=A0 int hart_unit =3D 4 * clic->num_sources;
+
+=C2=A0 =C2=A0 return (addr % mode_unit) / hart_unit;
+}
+
+/* Return target interrupt number */
+static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
+{
+=C2=A0 =C2=A0 int hart_unit =3D 4 * clic->num_sources;
+=C2=A0 =C2=A0 return (addr % hart_unit) / 4;
+}
+
+static void
+riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)=
+{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D opaque;
+=C2=A0 =C2=A0 hwaddr clic_size =3D clic->clic_size;
+=C2=A0 =C2=A0 int hartid, mode, irq;
+
+=C2=A0 =C2=A0 if (addr < clic_size) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (addr < 0x1000) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 assert(addr % 4 =3D=3D 0);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 int index =3D addr / 4;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (index) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 0: /* cliccfg */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 uint= 8_t nlbits =3D extract32(value, 1, 4);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 uint= 8_t nmbits =3D extract32(value, 5, 2);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0* The 4-bit cliccfg.nlbits WARL field.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0* Valid values are 0=E2=80=948.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (= nlbits <=3D 8) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 clic->nlbits =3D nlbits;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* V= alid values are given by implemented priviledges */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (= clic->prv_s && clic->prv_u) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 if (nmbits <=3D 2) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 clic->nmbits =3D nmbits;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } el= se if (clic->prv_u) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 if (nmbits <=3D 1) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 clic->nmbits =3D nmbits;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } el= se {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 assert(!clic->prv_s);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 if (nmbits =3D=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 clic->nmbits =3D 0;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic= ->nvbits =3D extract32(value, 0, 1);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 brea= k;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 1: /* clicinfo, read-only r= egister */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_= GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clic: write read-only clicinfo.\n&quo= t;);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 0x10 ... 0x2F: /* clicinttr= ig */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 uint= 32_t interrupt_number =3D value & MAKE_64BIT_MASK(0, 13);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (= interrupt_number <=3D clic->num_sources) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 value &=3D ~MAKE_64BIT_MASK(13, 18);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 clic->clicinttrig[index - 0x10] =3D value;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 brea= k;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 2: /* mintthresh */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (!strcmp(clic-&= gt;version, "v0.8")) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic= ->mintthresh =3D value;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 brea= k;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_= GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clic: invalid write addr: 0x%" H= WADDR_PRIx "\n",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_= GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clic: invalid write addr: 0x%" H= WADDR_PRIx "\n",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 addr -=3D 0x1000;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 hartid =3D riscv_clic_get_hartid= (clic, addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mode =3D riscv_clic_get_mode(cli= c, addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 irq =3D riscv_clic_get_irq(clic,= addr);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (riscv_clic_check_visible(cli= c, mode, hartid, irq)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_hart_wr= ite(clic, addr, value, size, mode,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 hartid, irq); +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "clic: invalid write: 0x%" HWADDR_PRIx "\n", addr);=
+=C2=A0 =C2=A0 }
+}
+
+static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)<= br> +{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D opaque;
+=C2=A0 =C2=A0 hwaddr clic_size =3D clic->clic_size;
+=C2=A0 =C2=A0 int hartid, mode, irq;
+
+=C2=A0 =C2=A0 if (addr < clic_size) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (addr < 0x1000) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 assert(addr % 4 =3D=3D 0);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 int index =3D addr / 4;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (index) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 0: /* cliccfg */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->nv= bits |
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0(clic->nlbits << 1) |
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0(clic->nmbits << 5);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 1: /* clicinfo */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* clicinfo r= egister layout
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* Bits Field=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 31 reserve= d (WARL 0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 30:25 num_= trigger
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 24:21 CLIC= INTCTLBITS
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 20:13 vers= ion (for version control)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 12:0 num_i= nterrupt
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->cl= icinfo & ~INT32_MAX;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 0x10 ... 0x2F: /* clicinttr= ig */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* clicinttri= g register layout
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* Bits Field=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 31 enable<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 30:13 rese= rved (WARL 0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 12:0 inter= rupt_number
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->cl= icinttrig[index - 0x10] &
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0~MAKE_64BIT_MASK(13, 18);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 2: /* mintthresh */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (!strcmp(clic-&= gt;version, "v0.8")) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 retu= rn clic->mintthresh;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 brea= k;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_= GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clic: invalid read : 0x%" HWADDR= _PRIx "\n",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_= GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clic: invalid read : 0x%" HWADDR= _PRIx "\n",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 addr -=3D 0x1000;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 hartid =3D riscv_clic_get_hartid= (clic, addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mode =3D riscv_clic_get_mode(cli= c, addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 irq =3D riscv_clic_get_irq(clic,= addr);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (riscv_clic_check_visible(cli= c, mode, hartid, irq)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return riscv_clic_= hart_read(clic, addr, mode, hartid, irq);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "clic: invalid read: 0x%" HWADDR_PRIx "\n", addr);<= br> +=C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 return 0;
+}
+
+static void riscv_clic_set_irq(void *opaque, int id, int level)
+{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D opaque;
+=C2=A0 =C2=A0 int irq, hartid, mode;
+=C2=A0 =C2=A0 hwaddr addr =3D 4 * id;
+=C2=A0 =C2=A0 TRIG_TYPE type;
+
+=C2=A0 =C2=A0 hartid =3D riscv_clic_get_hartid(clic, addr);
+=C2=A0 =C2=A0 mode =3D riscv_clic_get_mode(clic, addr);
+=C2=A0 =C2=A0 irq =3D riscv_clic_get_irq(clic, addr);
+=C2=A0 =C2=A0 type =3D riscv_clic_get_trigger_type(clic, id);
+
+=C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0* In general, the edge-triggered interrupt state shoul= d be kept in pending
+=C2=A0 =C2=A0 =C2=A0* bit, while the level-triggered interrupt should be k= ept in the level
+=C2=A0 =C2=A0 =C2=A0* state of the incoming wire.
+=C2=A0 =C2=A0 =C2=A0*
+=C2=A0 =C2=A0 =C2=A0* For CLIC, model the level-triggered interrupt by rea= d-only pending bit.
+=C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 if (level) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (type) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case POSITIVE_LEVEL:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case POSITIVE_EDGE:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_update_intip(clic, mo= de, hartid, irq, level);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case NEG_LEVEL:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_update_intip(clic, mo= de, hartid, irq, !level);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case NEG_EDGE:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (type) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case POSITIVE_LEVEL:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_update_intip(clic, mo= de, hartid, irq, level);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case POSITIVE_EDGE:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case NEG_LEVEL:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case NEG_EDGE:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_update_intip(clic, mo= de, hartid, irq, !level);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+}
+
+static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level) +{
+=C2=A0 =C2=A0 CPURISCVState *env =3D (CPURISCVState *)opaque;
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D env->clic;
+=C2=A0 =C2=A0 CPUState *cpu =3D env_cpu(env);
+
+=C2=A0 =C2=A0 if (level) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 env->exccode =3D clic->exccode[cpu->c= pu_index];
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 cpu_interrupt(env_cpu(env), CPU_INTERRUPT_CLIC= );
+=C2=A0 =C2=A0 }
+}
+
+static const MemoryRegionOps riscv_clic_ops =3D {
+=C2=A0 =C2=A0 .read =3D riscv_clic_read,
+=C2=A0 =C2=A0 .write =3D riscv_clic_write,
+=C2=A0 =C2=A0 .endianness =3D DEVICE_LITTLE_ENDIAN,
+=C2=A0 =C2=A0 .valid =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 .min_access_size =3D 1,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 .max_access_size =3D 8
+=C2=A0 =C2=A0 }
+};
+
+static void riscv_clic_realize(DeviceState *dev, Error **errp)
+{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D RISCV_CLIC(dev);
+=C2=A0 =C2=A0 size_t harts_x_sources =3D clic->num_harts * clic->num= _sources;
+=C2=A0 =C2=A0 int irqs, i;
+
+=C2=A0 =C2=A0 if (clic->prv_s && clic->prv_u) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 irqs =3D 3 * harts_x_sources;
+=C2=A0 =C2=A0 } else if (clic->prv_s || clic->prv_u) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 irqs =3D 2 * harts_x_sources;
+=C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 irqs =3D harts_x_sources;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 clic->clic_size =3D irqs * 4 + 0x1000;
+=C2=A0 =C2=A0 memory_region_init_io(&clic->mmio, OBJECT(dev), &= riscv_clic_ops, clic,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 TYPE_RISCV_CLIC, clic->clic_size);
+
+=C2=A0 =C2=A0 clic->clicintip =3D g_new0(uint8_t, irqs);
+=C2=A0 =C2=A0 clic->clicintie =3D g_new0(uint8_t, irqs);
+=C2=A0 =C2=A0 clic->clicintattr =3D g_new0(uint8_t, irqs);
+=C2=A0 =C2=A0 clic->clicintctl =3D g_new0(uint8_t, irqs);
+=C2=A0 =C2=A0 clic->active_list =3D g_new0(CLICActiveInterrupt, irqs);<= br> +=C2=A0 =C2=A0 clic->active_count =3D g_new0(size_t, clic->num_harts)= ;
+=C2=A0 =C2=A0 clic->exccode =3D g_new0(uint32_t, clic->num_harts); +=C2=A0 =C2=A0 clic->cpu_irqs =3D g_new0(qemu_irq, clic->num_harts);<= br> +=C2=A0 =C2=A0 sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio); +
+=C2=A0 =C2=A0 /* Allocate irq through gpio, so that we can use qtest */ +=C2=A0 =C2=A0 qdev_init_gpio_in(dev, riscv_clic_set_irq, irqs);
+=C2=A0 =C2=A0 qdev_init_gpio_out(dev, clic->cpu_irqs, clic->num_hart= s);
+
+=C2=A0 =C2=A0 for (i =3D 0; i < clic->num_harts; i++) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 RISCVCPU *cpu =3D RISCV_CPU(qemu_get_cpu(i));<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_irq irq =3D qemu_allocate_irq(riscv_clic_= cpu_irq_handler,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0&a= mp;cpu->env, 1);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qdev_connect_gpio_out(dev, i, irq);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 cpu->env.clic =3D clic;
+=C2=A0 =C2=A0 }
+}
+
+static Property riscv_clic_properties[] =3D {
+=C2=A0 =C2=A0 DEFINE_PROP_BOOL("prv-s", RISCVCLICState, prv_s, f= alse),
+=C2=A0 =C2=A0 DEFINE_PROP_BOOL("prv-u", RISCVCLICState, prv_u, f= alse),
+=C2=A0 =C2=A0 DEFINE_PROP_UINT32("num-harts", RISCVCLICState, nu= m_harts, 0),
+=C2=A0 =C2=A0 DEFINE_PROP_UINT32("num-sources", RISCVCLICState, = num_sources, 0),
+=C2=A0 =C2=A0 DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICStat= e, clicintctlbits, 0),
+=C2=A0 =C2=A0 DEFINE_PROP_UINT64("mclicbase", RISCVCLICState, mc= licbase, 0),
+=C2=A0 =C2=A0 DEFINE_PROP_STRING("version", RISCVCLICState, vers= ion),
+=C2=A0 =C2=A0 DEFINE_PROP_END_OF_LIST(),
+};
+
+static void riscv_clic_class_init(ObjectClass *klass, void *data)
+{
+=C2=A0 =C2=A0 DeviceClass *dc =3D DEVICE_CLASS(klass);
+
+=C2=A0 =C2=A0 dc->realize =3D riscv_clic_realize;
+=C2=A0 =C2=A0 device_class_set_props(dc, riscv_clic_properties);
+}
+
+static const TypeInfo riscv_clic_info =3D {
+=C2=A0 =C2=A0 .name=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D TYPE_RISCV_CLIC,=
+=C2=A0 =C2=A0 .parent=C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D TYPE_SYS_BUS_DEVICE,<= br> +=C2=A0 =C2=A0 .instance_size =3D sizeof(RISCVCLICState),
+=C2=A0 =C2=A0 .class_init=C2=A0 =C2=A0 =3D riscv_clic_class_init,
+};
+
+static void riscv_clic_register_types(void)
+{
+=C2=A0 =C2=A0 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 regist= ers
+ * @version: clic version, such as "v0.9"
+ *
+ * Returns: the device object
+ */
+DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t num_harts, uint32_t num_sour= ces,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint8_t clicintctlbits,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0const char *version)
+{
+=C2=A0 =C2=A0 DeviceState *dev =3D qdev_new(TYPE_RISCV_CLIC);
+
+=C2=A0 =C2=A0 assert(num_sources <=3D 4096);
+=C2=A0 =C2=A0 assert(num_harts <=3D 1024);
+=C2=A0 =C2=A0 assert(clicintctlbits <=3D 8);
+=C2=A0 =C2=A0 assert(!strcmp(version, "v0.8") || !strcmp(version= , "v0.9"));
+
+=C2=A0 =C2=A0 qdev_prop_set_bit(dev, "prv-s", prv_s);
+=C2=A0 =C2=A0 qdev_prop_set_bit(dev, "prv-u", prv_u);
+=C2=A0 =C2=A0 qdev_prop_set_uint32(dev, "num-harts", num_harts);=
+=C2=A0 =C2=A0 qdev_prop_set_uint32(dev, "num-sources", num_sourc= es);
+=C2=A0 =C2=A0 qdev_prop_set_uint32(dev, "clicintctlbits", clicin= tctlbits);
+=C2=A0 =C2=A0 qdev_prop_set_uint64(dev, "mclicbase", addr);
+=C2=A0 =C2=A0 qdev_prop_set_string(dev, "version", version);
+
+=C2=A0 =C2=A0 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fat= al);
+=C2=A0 =C2=A0 sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+=C2=A0 =C2=A0 return dev;
+}
+
+void riscv_clic_get_next_interrupt(void *opaque, int hartid)
+{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D opaque;
+=C2=A0 =C2=A0 riscv_clic_next_interrupt(clic, hartid);
+}
+
+bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int irq)=
+{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D opaque;
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+=C2=A0 =C2=A0 return riscv_clic_is_shv_interrupt(clic, irq_offset);
+}
+
+bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int irq= )
+{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D opaque;
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+=C2=A0 =C2=A0 return riscv_clic_is_edge_triggered(clic, irq_offset);
+}
+
+void riscv_clic_clean_pending(void *opaque, int mode, int hartid, int irq)=
+{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D opaque;
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+=C2=A0 =C2=A0 clic->clicintip[irq_offset] =3D 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=C2=A0 are 1= 1.
+ */
+bool riscv_clic_is_clic_mode(CPURISCVState *env)
+{
+=C2=A0 =C2=A0 target_ulong xtvec =3D (env->priv =3D=3D PRV_M) ? env->= ;mtvec : env->stvec;
+=C2=A0 =C2=A0 return env->clic && ((xtvec & 0x3) =3D=3D 3);=
+}
+
+void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int *il, int *irq)
+{
+=C2=A0 =C2=A0 *irq =3D extract32(exccode, 0, 12);
+=C2=A0 =C2=A0 *mode =3D extract32(exccode, 12, 2);
+=C2=A0 =C2=A0 *il =3D 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.<= br> + *
+ * 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<= br> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.=C2=A0 See the GNU General Public Lice= nse for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along= with
+ * this program.=C2=A0 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) \
+=C2=A0 =C2=A0 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 {
+=C2=A0 =C2=A0 uint16_t intcfg;
+=C2=A0 =C2=A0 uint16_t irq;
+} CLICActiveInterrupt;
+
+typedef enum TRIG_TYPE {
+=C2=A0 =C2=A0 POSITIVE_LEVEL,
+=C2=A0 =C2=A0 POSITIVE_EDGE,
+=C2=A0 =C2=A0 NEG_LEVEL,
+=C2=A0 =C2=A0 NEG_EDGE,
+} TRIG_TYPE;
+
+typedef struct RISCVCLICState {
+=C2=A0 =C2=A0 /*< private >*/
+=C2=A0 =C2=A0 SysBusDevice parent_obj;
+
+=C2=A0 =C2=A0 /*< public >*/
+
+=C2=A0 =C2=A0 /* Implementaion parameters */
+=C2=A0 =C2=A0 bool prv_s;
+=C2=A0 =C2=A0 bool prv_u;
+=C2=A0 =C2=A0 uint32_t num_harts;
+=C2=A0 =C2=A0 uint32_t num_sources;
+=C2=A0 =C2=A0 uint32_t clic_size;
+=C2=A0 =C2=A0 uint32_t clic_mmode_base;
+=C2=A0 =C2=A0 uint32_t clicintctlbits;
+=C2=A0 =C2=A0 uint64_t mclicbase;
+=C2=A0 =C2=A0 char *version;
+
+=C2=A0 =C2=A0 /* Global configuration */
+=C2=A0 =C2=A0 uint8_t nmbits;
+=C2=A0 =C2=A0 uint8_t nlbits;
+=C2=A0 =C2=A0 uint8_t nvbits;
+=C2=A0 =C2=A0 uint32_t clicinfo;
+=C2=A0 =C2=A0 uint32_t clicinttrig[32];
+
+=C2=A0 =C2=A0 /* Aperture configuration */
+=C2=A0 =C2=A0 uint8_t *clicintip;
+=C2=A0 =C2=A0 uint8_t *clicintie;
+=C2=A0 =C2=A0 uint8_t *clicintattr;
+=C2=A0 =C2=A0 uint8_t *clicintctl;
+
+=C2=A0 =C2=A0 /* Complatible with v0.8 */
+=C2=A0 =C2=A0 uint32_t mintthresh;
+=C2=A0 =C2=A0 uint32_t sintthresh;
+=C2=A0 =C2=A0 uint32_t uintthresh;
+
+=C2=A0 =C2=A0 /* QEMU implementaion related fields */
+=C2=A0 =C2=A0 uint32_t *exccode;
+=C2=A0 =C2=A0 CLICActiveInterrupt *active_list;
+=C2=A0 =C2=A0 size_t *active_count;
+=C2=A0 =C2=A0 MemoryRegion mmio;
+=C2=A0 =C2=A0 qemu_irq *cpu_irqs;
+} RISCVCLICState;
+
+DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t num_harts, uint32_t num_sour= ces,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint8_t clicintctlbits,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0const 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 @@
=C2=A0#define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
=C2=A0#define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
=C2=A0#define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
+#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0

=C2=A0#define TYPE_RISCV_CPU_ANY=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 RISCV_CPU_TYPE_NAME("any")
=C2=A0#define TYPE_RISCV_CPU_BASE32=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0RISCV_CPU_TYPE_NAME("rv32")
@@ -247,6 +248,7 @@ struct CPURISCVState {
=C2=A0 =C2=A0 =C2=A0/* Fields from here on are preserved across CPU reset. = */
=C2=A0 =C2=A0 =C2=A0QEMUTimer *timer; /* Internal timer */
=C2=A0 =C2=A0 =C2=A0void *clic;=C2=A0 =C2=A0 =C2=A0 =C2=A0/* clic interrupt= controller */
+=C2=A0 =C2=A0 uint32_t exccode; /* clic irq encode */
=C2=A0};

=C2=A0OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
--
2.25.1


--0000000000009f629105c6fbb3f6-- From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from list by lists.gnu.org with archive (Exim 4.90_1) id 1m3CIz-0001ut-Qu for mharc-qemu-riscv@gnu.org; Tue, 13 Jul 2021 02:54:09 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:52260) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1m3CIx-0001u3-T2 for qemu-riscv@nongnu.org; Tue, 13 Jul 2021 02:54:07 -0400 Received: from mail-pg1-x52c.google.com ([2607:f8b0:4864:20::52c]:46766) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1m3CIq-0005tj-Jl for qemu-riscv@nongnu.org; Tue, 13 Jul 2021 02:54:07 -0400 Received: by mail-pg1-x52c.google.com with SMTP id w15so20758828pgk.13 for ; Mon, 12 Jul 2021 23:53:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sifive.com; s=google; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=4i5R1Fn3YChKePIjEES2GmtvuFSZL3TYgdCZ+Buf4Bg=; b=nOxYTr0pDqPZtfQ1RaF1P4KnEViLFD0She9QhCY0zdWwymnuPRAj34lS8mUObBxnIP xW3RNWtkiZEvpblDVqpDBH5wkgCTx2K4AzO5uEUfY0WZY8Ub51dkcNNIzL4VR/yhf261 O9M0RRoi2yuycXmvBNEbuPschpzvmEEA+dXdAjszwJTVhMgmsM6GDLra08o6h56JOEa1 xGmwB3RQ+VMmFOKKJTofjnXpGTWktjz4XmgIYjSdvfm6dqlLVNMiDxQMnzTkVW6bkYl3 +VqvdvTvSrc9OpXXJxjK8s9FQ1ECisjuDhgQwvZdFt8jHDMWR3bxPijBbDolvh5GCwES ZgAw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=4i5R1Fn3YChKePIjEES2GmtvuFSZL3TYgdCZ+Buf4Bg=; b=Yk/ZgDLhYYAZ15/Z427BOch7kUlKbqdgBjayXp4UMNiycl0WpYFI97q85HwO0fy1Hd C8fpbOZ908zYu7aWl41uHMS8M1DEh4DQv0mQJcfTZTniJq+haTH33Yr48BHrwSQyqgJR jKC2QmMML9AYra8fzWdlqI7pehBlAaff0OebFimAXabd6RMofSm2oMZvKvTk96Q+aRlh 5G01+GSE9gfMYu/F2K1v38uE8OTmg0Zm+4UW+b6tTrvakxg2RIsdeCct3d8X6HdY3PDt n8XMW8Uylr/p4AzPVWvAYirDgu5U+94Aa/IJQUiGmq2dscSgvBcJs05zLLFoYBSQtaiw KwCQ== X-Gm-Message-State: AOAM530wfKx39hUbwskztkUUn6rncG4U35VS0p51f8g/nBm8LFB1gc+H /rVMuUCZYtrVeS6jm8cUT5+zZQ== X-Google-Smtp-Source: ABdhPJwMH2hBF9twtTHCEIyncesdtp1b4E7xqrd02VQwyDk+70d6JwXMUZ8x5JarNqR10YYa1JZziw== X-Received: by 2002:a63:1d42:: with SMTP id d2mr2932078pgm.21.1626159238106; Mon, 12 Jul 2021 23:53:58 -0700 (PDT) Received: from mail-pg1-f176.google.com (mail-pg1-f176.google.com. [209.85.215.176]) by smtp.gmail.com with ESMTPSA id g141sm18307719pfb.210.2021.07.12.23.53.56 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 12 Jul 2021 23:53:56 -0700 (PDT) Received: by mail-pg1-f176.google.com with SMTP id t9so20784244pgn.4; Mon, 12 Jul 2021 23:53:56 -0700 (PDT) X-Received: by 2002:a63:655:: with SMTP id 82mr2921421pgg.133.1626159236209; Mon, 12 Jul 2021 23:53:56 -0700 (PDT) MIME-Version: 1.0 References: <20210409074857.166082-1-zhiwei_liu@c-sky.com> <20210409074857.166082-4-zhiwei_liu@c-sky.com> In-Reply-To: <20210409074857.166082-4-zhiwei_liu@c-sky.com> From: Frank Chang Date: Tue, 13 Jul 2021 14:53:44 +0800 X-Gmail-Original-Message-ID: Message-ID: Subject: Re: [RFC PATCH 03/11] hw/intc: Add CLIC device To: LIU Zhiwei Cc: "qemu-devel@nongnu.org Developers" , "open list:RISC-V" , Palmer Dabbelt , Alistair Francis , wxy194768@alibaba-inc.com Content-Type: multipart/alternative; boundary="0000000000009f629105c6fbb3f6" Received-SPF: pass client-ip=2607:f8b0:4864:20::52c; envelope-from=frank.chang@sifive.com; helo=mail-pg1-x52c.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-riscv@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 13 Jul 2021 06:54:08 -0000 --0000000000009f629105c6fbb3f6 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable LIU Zhiwei =E6=96=BC 2021=E5=B9=B44=E6=9C=889=E6=97= =A5 =E9=80=B1=E4=BA=94 =E4=B8=8B=E5=8D=883:57=E5=AF=AB=E9=81=93=EF=BC=9A > 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 > --- > 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=3Dn > CONFIG_SEMIHOSTING=3Dy > CONFIG_ARM_COMPATIBLE_SEMIHOSTING=3Dy > +CONFIG_RISCV_CLIC=3Dy > > # 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=3Dn > CONFIG_SEMIHOSTING=3Dy > CONFIG_ARM_COMPATIBLE_SEMIHOSTING=3Dy > +CONFIG_RISCV_CLIC=3Dy > > # 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 WITHOU= T > + * 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 . > + */ > + > +#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 =3D clic->nlbits; > + > + uint8_t mask_il =3D ((1 << nlbits) - 1) << (8 - nlbits); > + uint8_t mask_padding =3D (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 =3D clic->clicintctlbits - clic->nlbits; > + uint8_t mask_priority =3D ((1 << npbits) - 1) << (8 - npbits); > + uint8_t mask_padding =3D (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 =3D intcfg >> 8; > + *level =3D riscv_clic_get_interrupt_level(clic, intcfg & 0xff); > + *priority =3D 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, in= t > irq) > +{ > + size_t mode_offset =3D 0; > + size_t unit =3D clic->num_harts * clic->num_sources; > + > + switch (mode) { > + case PRV_M: > + mode_offset =3D 0; > + break; > + case PRV_S: > + mode_offset =3D unit; > + break; > + case PRV_U: > + mode_offset =3D 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 =3D RISCV_CPU(qemu_get_cpu(hartid)); > + CPURISCVState *env =3D &cpu->env; > + RISCVCLICState *clic =3D (RISCVCLICState *)opaque; > + > + int il[4] =3D { > + 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 =3D hartid * clic->num_sources; > + CLICActiveInterrupt *active =3D &clic->active_list[hart_offset]; > + size_t active_count =3D 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 =3D=3D env->priv && level <=3D il[= mode])) > { > + /* > + * No pending interrupts with high enough mode+priority+leve= l > + * break and clear pending interrupt for this hart > + */ > + break; > + } > + irq_offset =3D 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] =3D 0; > + } > + /* Post pending interrupt for this hart */ > + clic->exccode[hartid] =3D active->irq | mode << 12 | level <= < > 14; > + qemu_set_irq(clic->cpu_irqs[hartid], 1); > + return; > + } > + /* Check next enabled interrupt */ > + active_count--; > + active++; > + } > +} > Hi Zhiwei, The global interrupt may be disabled (xstatus.xie =3D 0) at the time active interrupt i is both enabled and pending (i.e. clicintie[i] =3D 1 && clicintip[i] =3D 1) Therefore, interrupt i will not be taken right away until xstatus.xie is set to 1. However, during global interrupt is disabled, SW/HW may either disable or change pending interrupt i to non-pending. Interrupt i should not be active anymore. But current implementation can't reflect that because CPU_INTERRUPT_CLIC bit won't be unset. To solve this issue, in my own implementation, I tried to: In riscv_clic_update_intip(), if interrupt i turns to non-pending, check if interrupt i is an active pending interrupt. (by decoding IRQ number from env->exccode and check if cpu->interrupt_request flag also has CPU_INTERRUPT_CLIC bit set.) If so, interrupt i is current active pending interrupt to be disabled, we have to unset CPU_INTERRUPT_CLIC bit from interrupt request flag for the hart to prevent interrupt i been taken later as it's not pending anymore. Then call riscv_clic_next_interrupt() as usual to pick next interrupt. Same behavior is also applied in riscv_clic_update_intie() when interrupt i is disabled and interrupt i is also the active pending interrupt. Also, I think the effective interrupt level check should be moved from riscv_clic_update_intip() to riscv_cpu_local_irq_mode_enabled() in cpu_helper.c. Consider the following scenario: a. Active pending interrupt i is not been handled due to global interrupt is disabled. b. Software accesses xnxti CSR with write side effect (xstatus.xie is still set to 0). xintstatus.xil will be updated to the interrupt level of interrupt i. c. Interrupt i turns to non-pending/disabled. d. Interrupt j is enabled and pending. e. Interrupt j won't be selected in riscv_clic_next_interrupt() if its interrupt level is lower than interrupt i (xinitstatus.xil still holds the interrupt level of interrupt i), which is incorrect because interrupt i is not pending and active anymore. By moving effective interrupt level check from riscv_clic_update_intip() to riscv_cpu_local_irq_mode_enabled() can eliminate the issue of lower-priority interrupt been masked out by higher-interrupt-level non active interrupt. Let me know if I miss anything. Regards, Frank Chang > + > +/* > + * 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 =3D riscv_clic_get_irq_offset(clic, mode, hartid, > irq); > + if (!clic->prv_s && !clic->prv_u) { /* M */ > + return mode =3D=3D PRV_M; > + } else if (!clic->prv_s) { /* M/U */ > + switch (clic->nmbits) { > + case 0: > + return mode =3D=3D PRV_M; > + case 1: > + return clic->clicintattr[irq_offset] & 0x80 ? (mode =3D=3D P= RV_M) > : > + (mode =3D=3D P= RV_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 =3D=3D PRV_M; > + case 1: > + return clic->clicintattr[irq_offset] & 0x80 ? (mode =3D=3D P= RV_M) > : > + (mode =3D=3D P= RV_S); > + case 2: > + return mode =3D=3D 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, in= t > irq) > +{ > + size_t irq_offset =3D 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 =3D riscv_clic_get_irq_offset(clic, mode, hartid, > irq); > + clic->clicintip[irq_offset] =3D !!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 =3D extract64(value, 6, 2); > + > + if (!qtest_enabled()) { > + CPURISCVState *env =3D 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 =3D hartid * clic->num_sources; > + size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, hartid, > irq); > + CLICActiveInterrupt *active_list =3D &clic->active_list[hart_offset]= ; > + size_t *active_count =3D &clic->active_count[hartid]; > + > + uint8_t old_intie =3D clic->clicintie[irq_offset]; > + clic->clicintie[irq_offset] =3D !!new_intie; > + > + /* Add to or remove from list of active interrupts */ > + if (new_intie && !old_intie) { > + active_list[*active_count].intcfg =3D (mode << 8) | > + clic->clicintctl[irq_offset]= ; > + active_list[*active_count].irq =3D irq; > + (*active_count)++; > + } else if (!new_intie && old_intie) { > + CLICActiveInterrupt key =3D { > + (mode << 8) | clic->clicintctl[irq_offset], irq > + }; > + CLICActiveInterrupt *result =3D bsearch(&key, > + active_list, *active_count= , > + sizeof(CLICActiveInterrupt= ), > + riscv_clic_active_compare)= ; > + size_t elem =3D (result - active_list) / > sizeof(CLICActiveInterrupt); > + size_t sz =3D (--(*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 =3D extract32(addr, 0, 2); > + size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, hartid, > irq); > + > + if (hartid >=3D clic->num_harts) { > + qemu_log_mask(LOG_GUEST_ERROR, > + "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n", > + hartid, addr); > + return; > + } > + > + if (irq >=3D 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]=3Dzero means no > interrupt > + * pending, and clicintip[i]!=3D0 (not just 1) indicates an > + * interrupt is pending. (Section 3.4) > + */ > + if (value !=3D clic->clicintip[irq_offset]) { > + riscv_clic_update_intip(clic, mode, hartid, irq, value); > + } > + } > + break; > + case 1: /* clicintie[i] */ > + if (clic->clicintie[irq_offset] !=3D 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] !=3D value) { > + /* When nmbits=3D2, check WARL */ > + bool invalid =3D (clic->nmbits =3D=3D 2) && > + (extract64(value, 6, 2) =3D=3D 0b10); > + if (invalid) { > + uint8_t old_mode =3D > extract32(clic->clicintattr[irq_offset], > + 6, 2); > + value =3D deposit32(value, 6, 2, old_mode); > + } > + clic->clicintattr[irq_offset] =3D value; > + riscv_clic_next_interrupt(clic, hartid); > + } > + } > + break; > + case 3: /* clicintctl[i] */ > + if (value !=3D clic->clicintctl[irq_offset]) { > + clic->clicintctl[irq_offset] =3D 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 =3D extract32(addr, 0, 2); > + size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, hartid, > irq); > + > + if (hartid >=3D clic->num_harts) { > + qemu_log_mask(LOG_GUEST_ERROR, > + "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n", > + hartid, addr); > + return 0; > + } > + > + if (irq >=3D 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 =3D 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 =3D 4 * clic->num_harts * clic->num_sources; > + int hart_unit =3D 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 =3D 4 * clic->num_sources; > + return (addr % hart_unit) / 4; > +} > + > +static void > +riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned siz= e) > +{ > + RISCVCLICState *clic =3D opaque; > + hwaddr clic_size =3D clic->clic_size; > + int hartid, mode, irq; > + > + if (addr < clic_size) { > + if (addr < 0x1000) { > + assert(addr % 4 =3D=3D 0); > + int index =3D addr / 4; > + switch (index) { > + case 0: /* cliccfg */ > + { > + uint8_t nlbits =3D extract32(value, 1, 4); > + uint8_t nmbits =3D extract32(value, 5, 2); > + > + /* > + * The 4-bit cliccfg.nlbits WARL field. > + * Valid values are 0=E2=80=948. > + */ > + if (nlbits <=3D 8) { > + clic->nlbits =3D nlbits; > + } > + /* Valid values are given by implemented priviledges > */ > + if (clic->prv_s && clic->prv_u) { > + if (nmbits <=3D 2) { > + clic->nmbits =3D nmbits; > + } > + } else if (clic->prv_u) { > + if (nmbits <=3D 1) { > + clic->nmbits =3D nmbits; > + } > + } else { > + assert(!clic->prv_s); > + if (nmbits =3D=3D 0) { > + clic->nmbits =3D 0; > + } > + } > + clic->nvbits =3D 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 =3D value & > MAKE_64BIT_MASK(0, 13); > + if (interrupt_number <=3D clic->num_sources) { > + value &=3D ~MAKE_64BIT_MASK(13, 18); > + clic->clicinttrig[index - 0x10] =3D value; > + } > + break; > + } > + case 2: /* mintthresh */ > + if (!strcmp(clic->version, "v0.8")) { > + clic->mintthresh =3D value; > + break; > + } > + qemu_log_mask(LOG_GUEST_ERROR, > + "clic: invalid write addr: 0x%" HWADDR_PRI= x > "\n", > + addr); > + break; > + default: > + qemu_log_mask(LOG_GUEST_ERROR, > + "clic: invalid write addr: 0x%" HWADDR_PRI= x > "\n", > + addr); > + return; > + } > + } else { > + addr -=3D 0x1000; > + hartid =3D riscv_clic_get_hartid(clic, addr); > + mode =3D riscv_clic_get_mode(clic, addr); > + irq =3D 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 =3D opaque; > + hwaddr clic_size =3D clic->clic_size; > + int hartid, mode, irq; > + > + if (addr < clic_size) { > + if (addr < 0x1000) { > + assert(addr % 4 =3D=3D 0); > + int index =3D 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 -=3D 0x1000; > + hartid =3D riscv_clic_get_hartid(clic, addr); > + mode =3D riscv_clic_get_mode(clic, addr); > + irq =3D 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 =3D opaque; > + int irq, hartid, mode; > + hwaddr addr =3D 4 * id; > + TRIG_TYPE type; > + > + hartid =3D riscv_clic_get_hartid(clic, addr); > + mode =3D riscv_clic_get_mode(clic, addr); > + irq =3D riscv_clic_get_irq(clic, addr); > + type =3D 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 pendin= g > 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 =3D (CPURISCVState *)opaque; > + RISCVCLICState *clic =3D env->clic; > + CPUState *cpu =3D env_cpu(env); > + > + if (level) { > + env->exccode =3D clic->exccode[cpu->cpu_index]; > + cpu_interrupt(env_cpu(env), CPU_INTERRUPT_CLIC); > + } > +} > + > +static const MemoryRegionOps riscv_clic_ops =3D { > + .read =3D riscv_clic_read, > + .write =3D riscv_clic_write, > + .endianness =3D DEVICE_LITTLE_ENDIAN, > + .valid =3D { > + .min_access_size =3D 1, > + .max_access_size =3D 8 > + } > +}; > + > +static void riscv_clic_realize(DeviceState *dev, Error **errp) > +{ > + RISCVCLICState *clic =3D RISCV_CLIC(dev); > + size_t harts_x_sources =3D clic->num_harts * clic->num_sources; > + int irqs, i; > + > + if (clic->prv_s && clic->prv_u) { > + irqs =3D 3 * harts_x_sources; > + } else if (clic->prv_s || clic->prv_u) { > + irqs =3D 2 * harts_x_sources; > + } else { > + irqs =3D harts_x_sources; > + } > + > + clic->clic_size =3D irqs * 4 + 0x1000; > + memory_region_init_io(&clic->mmio, OBJECT(dev), &riscv_clic_ops, cli= c, > + TYPE_RISCV_CLIC, clic->clic_size); > + > + clic->clicintip =3D g_new0(uint8_t, irqs); > + clic->clicintie =3D g_new0(uint8_t, irqs); > + clic->clicintattr =3D g_new0(uint8_t, irqs); > + clic->clicintctl =3D g_new0(uint8_t, irqs); > + clic->active_list =3D g_new0(CLICActiveInterrupt, irqs); > + clic->active_count =3D g_new0(size_t, clic->num_harts); > + clic->exccode =3D g_new0(uint32_t, clic->num_harts); > + clic->cpu_irqs =3D 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 =3D 0; i < clic->num_harts; i++) { > + RISCVCPU *cpu =3D RISCV_CPU(qemu_get_cpu(i)); > + qemu_irq irq =3D qemu_allocate_irq(riscv_clic_cpu_irq_handler, > + &cpu->env, 1); > + qdev_connect_gpio_out(dev, i, irq); > + cpu->env.clic =3D clic; > + } > +} > + > +static Property riscv_clic_properties[] =3D { > + 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 =3D DEVICE_CLASS(klass); > + > + dc->realize =3D riscv_clic_realize; > + device_class_set_props(dc, riscv_clic_properties); > +} > + > +static const TypeInfo riscv_clic_info =3D { > + .name =3D TYPE_RISCV_CLIC, > + .parent =3D TYPE_SYS_BUS_DEVICE, > + .instance_size =3D sizeof(RISCVCLICState), > + .class_init =3D 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 =3D qdev_new(TYPE_RISCV_CLIC); > + > + assert(num_sources <=3D 4096); > + assert(num_harts <=3D 1024); > + assert(clicintctlbits <=3D 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 =3D opaque; > + riscv_clic_next_interrupt(clic, hartid); > +} > + > +bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int ir= q) > +{ > + RISCVCLICState *clic =3D opaque; > + size_t irq_offset =3D 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 =3D opaque; > + size_t irq_offset =3D 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 ir= q) > +{ > + RISCVCLICState *clic =3D opaque; > + size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, hartid, > irq); > + clic->clicintip[irq_offset] =3D 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 =3D (env->priv =3D=3D PRV_M) ? env->mtvec : env->= stvec; > + return env->clic && ((xtvec & 0x3) =3D=3D 3); > +} > + > +void riscv_clic_decode_exccode(uint32_t exccode, int *mode, > + int *il, int *irq) > +{ > + *irq =3D extract32(exccode, 0, 12); > + *mode =3D extract32(exccode, 12, 2); > + *il =3D 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 WITHOU= T > + * 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 . > + */ > + > +#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 > > > --0000000000009f629105c6fbb3f6 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
LIU Zhiwei <zhiwei_liu@c-sky.com> =E6=96=BC 2021=E5=B9=B44=E6=9C= =889=E6=97=A5 =E9=80=B1=E4=BA=94 =E4=B8=8B=E5=8D=883:57=E5=AF=AB=E9=81=93= =EF=BC=9A
The Core-Local Interrupt Controller (CLIC) provides lo= w-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>
---
=C2=A0default-configs/devices/riscv32-softmmu.mak |=C2=A0 =C2=A01 +
=C2=A0default-configs/devices/riscv64-softmmu.mak |=C2=A0 =C2=A01 +
=C2=A0hw/intc/Kconfig=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 =C2=A03 +
=C2=A0hw/intc/meson.build=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 =C2=A01 +
=C2=A0hw/intc/riscv_clic.c=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | 835 ++++++++++++++++++++
=C2=A0include/hw/intc/riscv_clic.h=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 | 103 +++
=C2=A0target/riscv/cpu.h=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A02 +
=C2=A07 files changed, 946 insertions(+)
=C2=A0create mode 100644 hw/intc/riscv_clic.c
=C2=A0create 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 @@
=C2=A0#CONFIG_PCI_DEVICES=3Dn
=C2=A0CONFIG_SEMIHOSTING=3Dy
=C2=A0CONFIG_ARM_COMPATIBLE_SEMIHOSTING=3Dy
+CONFIG_RISCV_CLIC=3Dy

=C2=A0# Boards:
=C2=A0#
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 @@
=C2=A0#CONFIG_PCI_DEVICES=3Dn
=C2=A0CONFIG_SEMIHOSTING=3Dy
=C2=A0CONFIG_ARM_COMPATIBLE_SEMIHOSTING=3Dy
+CONFIG_RISCV_CLIC=3Dy

=C2=A0# Boards:
=C2=A0#
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
=C2=A0config SIFIVE_PLIC
=C2=A0 =C2=A0 =C2=A0bool

+config RISCV_CLIC
+=C2=A0 =C2=A0 bool
+
=C2=A0config GOLDFISH_PIC
=C2=A0 =C2=A0 =C2=A0bool

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'))
=C2=A0specific_ss.add(when: 'CONFIG_SH_INTC', if_true: files('s= h_intc.c'))
=C2=A0specific_ss.add(when: 'CONFIG_SIFIVE_CLINT', if_true: files(&= #39;sifive_clint.c'))
=C2=A0specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files(&#= 39;sifive_plic.c'))
+specific_ss.add(when: 'CONFIG_RISCV_CLIC', if_true: files('ris= cv_clic.c'))
=C2=A0specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics= .c'))
=C2=A0specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'],<= br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 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.<= br> + *
+ * 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<= br> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.=C2=A0 See the GNU General Public Lice= nse for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along= with
+ * this program.=C2=A0 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 e= ach
+ * interrupt input. Bit 1, trig[0], is defined as "edge-triggered&quo= t;
+ * (0: level-triggered, 1: edge-triggered); while bit 2, trig[1], is defin= ed as
+ * "negative-edge" (0: positive-edge, 1: negative-edge). (Sectio= n 3.6)
+ */
+
+static inline TRIG_TYPE
+riscv_clic_get_trigger_type(RISCVCLICState *clic, size_t irq_offset)
+{
+=C2=A0 =C2=A0 return (clic->clicintattr[irq_offset] >> 1) & 0= x3;
+}
+
+static inline bool
+riscv_clic_is_edge_triggered(RISCVCLICState *clic, size_t irq_offset)
+{
+=C2=A0 =C2=A0 return (clic->clicintattr[irq_offset] >> 1) & 0= x1;
+}
+
+static inline bool
+riscv_clic_is_shv_interrupt(RISCVCLICState *clic, size_t irq_offset)
+{
+=C2=A0 =C2=A0 return (clic->clicintattr[irq_offset] & 0x1) &&am= p; clic->nvbits;
+}
+
+static uint8_t
+riscv_clic_get_interrupt_level(RISCVCLICState *clic, uint8_t intctl)
+{
+=C2=A0 =C2=A0 int nlbits =3D clic->nlbits;
+
+=C2=A0 =C2=A0 uint8_t mask_il =3D ((1 << nlbits) - 1) << (8 - = nlbits);
+=C2=A0 =C2=A0 uint8_t mask_padding =3D (1 << (8 - nlbits)) - 1;
+=C2=A0 =C2=A0 /* unused level bits are set to 1 */
+=C2=A0 =C2=A0 return (intctl & mask_il) | mask_padding;
+}
+
+static uint8_t
+riscv_clic_get_interrupt_priority(RISCVCLICState *clic, uint8_t intctl) +{
+=C2=A0 =C2=A0 int npbits =3D clic->clicintctlbits - clic->nlbits; +=C2=A0 =C2=A0 uint8_t mask_priority =3D ((1 << npbits) - 1) <<= (8 - npbits);
+=C2=A0 =C2=A0 uint8_t mask_padding =3D (1 << (8 - npbits)) - 1;
+
+=C2=A0 =C2=A0 if (npbits < 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return UINT8_MAX;
+=C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 /* unused priority bits are set to 1 */
+=C2=A0 =C2=A0 return (intctl & mask_priority) | mask_padding;
+}
+
+static void
+riscv_clic_intcfg_decode(RISCVCLICState *clic, uint16_t intcfg,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0uint8_t *mode, uint8_t *level,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0uint8_t *priority)
+{
+=C2=A0 =C2=A0 *mode =3D intcfg >> 8;
+=C2=A0 =C2=A0 *level =3D riscv_clic_get_interrupt_level(clic, intcfg &= 0xff);
+=C2=A0 =C2=A0 *priority =3D riscv_clic_get_interrupt_priority(clic, intcfg= & 0xff);
+}
+
+/*
+ * In a system with multiple harts, the M-mode CLIC regions for all the ha= rts
+ * are placed contiguously in the memory space, followed by the S-mode CLI= C
+ * regions for all harts. (Section 3.11)
+ */
+static size_t
+riscv_clic_get_irq_offset(RISCVCLICState *clic, int mode, int hartid, int = irq)
+{
+=C2=A0 =C2=A0 size_t mode_offset =3D 0;
+=C2=A0 =C2=A0 size_t unit =3D clic->num_harts * clic->num_sources; +
+=C2=A0 =C2=A0 switch (mode) {
+=C2=A0 =C2=A0 case PRV_M:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 mode_offset =3D 0;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 case PRV_S:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 mode_offset =3D unit;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 case PRV_U:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 mode_offset =3D clic->prv_s ? 2 * unit : un= it;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "clic: invalid mode %d\n", mode);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 exit(1);
+=C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 return mode_offset + hartid * clic->num_sources + irq; +}
+
+static void riscv_clic_next_interrupt(void *opaque, int hartid)
+{
+=C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0* Scan active list for highest priority pending interr= upts
+=C2=A0 =C2=A0 =C2=A0* comparing against this harts mintstatus register and= interrupt
+=C2=A0 =C2=A0 =C2=A0* the core if we have a higher priority interrupt to d= eliver
+=C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 RISCVCPU *cpu =3D RISCV_CPU(qemu_get_cpu(hartid));
+=C2=A0 =C2=A0 CPURISCVState *env =3D &cpu->env;
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D (RISCVCLICState *)opaque;
+
+=C2=A0 =C2=A0 int il[4] =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 MAX(get_field(env->mintstatus, MINTSTATUS_U= IL),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic->mintthresh), /* PRV_U *= /
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 MAX(get_field(env->mintstatus, MINTSTATUS_S= IL),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic->sintthresh), /* PRV_S *= /
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 0,=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* reserverd */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 MAX(get_field(env->mintstatus, MINTSTATUS_M= IL),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic->uintthresh)=C2=A0 /* PR= V_M */
+=C2=A0 =C2=A0 };
+
+=C2=A0 =C2=A0 /* Get sorted list of enabled interrupts for this hart */ +=C2=A0 =C2=A0 size_t hart_offset =3D hartid * clic->num_sources;
+=C2=A0 =C2=A0 CLICActiveInterrupt *active =3D &clic->active_list[ha= rt_offset];
+=C2=A0 =C2=A0 size_t active_count =3D clic->active_count[hartid];
+=C2=A0 =C2=A0 uint8_t mode, level, priority;
+
+=C2=A0 =C2=A0 /* Loop through the enabled interrupts sorted by mode+priori= ty+level */
+=C2=A0 =C2=A0 while (active_count) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 size_t irq_offset;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_intcfg_decode(clic, active->intc= fg, &mode, &level,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0&priority);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (mode < env->priv || (mode =3D=3D env= ->priv && level <=3D il[mode])) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* No pending interrupts wi= th high enough mode+priority+level
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* break and clear pending = interrupt for this hart
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 irq_offset =3D riscv_clic_get_irq_offset(clic,= mode, hartid, active->irq);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Check pending interrupt with high enough mo= de+priority+level */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (clic->clicintip[irq_offset]) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Clean vector edge-triggered p= ending */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (riscv_clic_is_edge_triggered= (clic, irq_offset) &&
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_is_shv_= interrupt(clic, irq_offset)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic->clicintip= [irq_offset] =3D 0;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Post pending interrupt for th= is hart */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic->exccode[hartid] =3D act= ive->irq | mode << 12 | level << 14;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_set_irq(clic->cpu_irqs[h= artid], 1);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Check next enabled interrupt */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 active_count--;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 active++;
+=C2=A0 =C2=A0 }
+}

Hi Zhiwei,

= The global interrupt may be disabled (xstatus.xie =3D 0) at the time active= interrupt i
is both enabled and pending (i.e. clicintie[i] = =3D 1 && clicintip[i] =3D 1)
Therefore, interrupt i will = not be taken right away until xstatus.xie is set to 1.

=
However, during global interrupt is disabled,
SW/HW may eith= er disable or change pending interrupt i to non-pending.
Interrup= t i should not be active anymore.
But current implementation can&= #39;t reflect that because
CPU_INTERRUPT_CLIC bit won't be un= set.

To solve this issue, in my own implementation= , I tried to:

In riscv_clic_update_intip(), if int= errupt i turns to non-pending,
check if interrupt i is an act= ive pending interrupt.
(by decoding IRQ number from env->excco= de and check if
cpu->interrupt_request flag also has CPU_INTER= RUPT_CLIC bit set.)

If so, interrupt i is current = active pending interrupt to be disabled, we have to
unset CPU_INT= ERRUPT_CLIC bit from interrupt request flag for the hart
to p= revent interrupt i been taken later as it's not pending anymore.
Then call riscv_clic_next_interrupt() as usual to pick next interru= pt.

Same behavior is also applied in risc= v_clic_update_intie() when
interrupt i is disabled and interrupt = i is also the active pending interrupt.

Also= , I think the effective interrupt level check should be moved
fro= m riscv_clic_update_intip() to=C2=A0riscv_cpu_local_irq_mode_enabled() in c= pu_helper.c.

Consider the following scenario:
<= /div>
=C2=A0 =C2=A0 a. Active pending interrupt i is not been handled d= ue to global interrupt is disabled.
=C2=A0 =C2=A0 b. Softwar= e accesses xnxti CSR with write side effect (xstatus.xie is still set to 0)= .
=C2=A0 =C2=A0 =C2=A0 =C2=A0 xintstatus.xil will be updated to t= he interrupt level of interrupt i.
=C2=A0 =C2=A0 c. Interru= pt i turns to non-pending/disabled.
=C2=A0 =C2=A0 d. Interrupt j = is enabled and pending.
=C2=A0 =C2=A0 e. Interrupt j won't be= selected in riscv_clic_next_interrupt() if its interrupt level
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 is lower than interrupt i (xinitstatus.xil stil= l holds the interrupt level of interrupt i),
=C2=A0 =C2=A0 =C2=A0= =C2=A0 which is incorrect because interrupt i is not pending and active an= ymore.

By moving effective interrupt level check f= rom=C2=A0
riscv_clic_update_intip() to=C2=A0riscv_cpu_local_irq_m= ode_enabled()
can eliminate the issue of lower-priority interrupt= been masked out
by higher-interrupt-level non active interrupt.<= /div>

Let me know if I miss anything.

Regards,
Frank Chang
=C2=A0
+
+/*
+ * 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 i= rq)
+{
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+=C2=A0 =C2=A0 if (!clic->prv_s && !clic->prv_u) { /* M */ +=C2=A0 =C2=A0 =C2=A0 =C2=A0 return mode =3D=3D PRV_M;
+=C2=A0 =C2=A0 } else if (!clic->prv_s) { /* M/U */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (clic->nmbits) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case 0:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return mode =3D=3D PRV_M;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case 1:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->clicintattr[irq_= offset] & 0x80 ? (mode =3D=3D PRV_M) :
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (mode =3D=3D PRV_U)= ;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR, +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clic: nmbits= can only be 0 or 1 for M/U hart");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 exit(1);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 } else { /* M/S/U */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (clic->nmbits) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case 0:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return mode =3D=3D PRV_M;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case 1:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->clicintattr[irq_= offset] & 0x80 ? (mode =3D=3D PRV_M) :
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (mode =3D=3D PRV_S)= ;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case 2:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return mode =3D=3D clic->clic= intattr[irq_offset];
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case 3:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR, +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clic: nmbits= can only be 0 or 1 or 2 for M/S/U hart");
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 exit(1);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 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)
+{
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+=C2=A0 =C2=A0 return riscv_clic_is_edge_triggered(clic, irq_offset);
+}
+
+static void
+riscv_clic_update_intip(RISCVCLICState *clic, int mode, int hartid,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 int irq, uint64_t value)
+{
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+=C2=A0 =C2=A0 clic->clicintip[irq_offset] =3D !!value;
+=C2=A0 =C2=A0 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 val= ue)
+{
+=C2=A0 =C2=A0 int mode =3D extract64(value, 6, 2);
+
+=C2=A0 =C2=A0 if (!qtest_enabled()) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 CPURISCVState *env =3D current_cpu->env_ptr= ;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (env->priv < mode) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return false;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 return true;
+}
+
+static inline int riscv_clic_encode_priority(const CLICActiveInterrupt *i)=
+{
+=C2=A0 =C2=A0 return ((i->intcfg & 0x3ff) << 12) | /* Highest= mode+level+priority */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(i->irq & 0xfff);=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* Highest irq number */
+}
+
+static int riscv_clic_active_compare(const void *a, const void *b)
+{
+=C2=A0 =C2=A0 return riscv_clic_encode_priority(b) - riscv_clic_encode_pri= ority(a);
+}
+
+static void
+riscv_clic_update_intie(RISCVCLICState *clic, int mode, int hartid,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 int irq, uint64_t new_intie)
+{
+=C2=A0 =C2=A0 size_t hart_offset =3D hartid * clic->num_sources;
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+=C2=A0 =C2=A0 CLICActiveInterrupt *active_list =3D &clic->active_li= st[hart_offset];
+=C2=A0 =C2=A0 size_t *active_count =3D &clic->active_count[hartid];=
+
+=C2=A0 =C2=A0 uint8_t old_intie =3D clic->clicintie[irq_offset];
+=C2=A0 =C2=A0 clic->clicintie[irq_offset] =3D !!new_intie;
+
+=C2=A0 =C2=A0 /* Add to or remove from list of active interrupts */
+=C2=A0 =C2=A0 if (new_intie && !old_intie) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 active_list[*active_count].intcfg =3D (mode &l= t;< 8) |
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 clic->clicintctl[irq_offset];
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 active_list[*active_count].irq =3D irq;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 (*active_count)++;
+=C2=A0 =C2=A0 } else if (!new_intie && old_intie) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 CLICActiveInterrupt key =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (mode << 8) | clic->cli= cintctl[irq_offset], irq
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 };
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 CLICActiveInterrupt *result =3D bsearch(&k= ey,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 active_list, *active_count,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 sizeof(CLICActiveInterrupt),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 riscv_clic_active_compare);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 size_t elem =3D (result - active_list) / sizeo= f(CLICActiveInterrupt);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 size_t sz =3D (--(*active_count) - elem) * siz= eof(CLICActiveInterrupt);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 assert(result);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 memmove(&result[0], &result[1], sz); +=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 /* Sort list of active interrupts */
+=C2=A0 =C2=A0 qsort(active_list, *active_count,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sizeof(CLICActiveInterrupt),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_active_compare);
+
+=C2=A0 =C2=A0 riscv_clic_next_interrupt(clic, hartid);
+}
+
+static void
+riscv_clic_hart_write(RISCVCLICState *clic, hwaddr addr,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 uint64_t value, unsigned size,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 int mode, int hartid, int irq)
+{
+=C2=A0 =C2=A0 int req =3D extract32(addr, 0, 2);
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+
+=C2=A0 =C2=A0 if (hartid >=3D clic->num_harts) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n", +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 hartid, addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (irq >=3D clic->num_sources) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "clic: invalid irq %u: 0x%" HWADDR_PRIx "\n", irq, = addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 switch (req) {
+=C2=A0 =C2=A0 case 0: /* clicintip[i] */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (riscv_clic_validate_intip(clic, mode, hart= id, irq)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* The actual pending bit i= s located at bit 0 (i.e., the
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* leastsignificant bit). I= n case future extensions expand the bit
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* field, from FW perspecti= ve clicintip[i]=3Dzero means no interrupt
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* pending, and clicintip[i= ]!=3D0 (not just 1) indicates an
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* interrupt is pending. (S= ection 3.4)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (value !=3D clic->clicinti= p[irq_offset]) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_update_= intip(clic, mode, hartid, irq, value);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 case 1: /* clicintie[i] */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (clic->clicintie[irq_offset] !=3D value)= {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_update_intie(clic, mo= de, hartid, irq, value);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 case 2: /* clicintattr[i] */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (riscv_clic_validate_intattr(clic, value)) = {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (clic->clicintattr[irq_off= set] !=3D value) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* When nmbits=3D2= , check WARL */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 bool invalid =3D (= clic->nmbits =3D=3D 2) &&
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(extract64(value, 6, 2) =3D=3D 0b10);=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (invalid) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 uint= 8_t old_mode =3D extract32(clic->clicintattr[irq_offset],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A06, 2);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 valu= e =3D deposit32(value, 6, 2, old_mode);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic->clicintat= tr[irq_offset] =3D value;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_next_in= terrupt(clic, hartid);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 case 3: /* clicintctl[i] */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (value !=3D clic->clicintctl[irq_offset]= ) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic->clicintctl[irq_offset] = =3D value;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_next_interrupt(clic, = hartid);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 }
+}
+
+static uint64_t
+riscv_clic_hart_read(RISCVCLICState *clic, hwaddr addr, int mode,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0int hartid, int irq)
+{
+=C2=A0 =C2=A0 int req =3D extract32(addr, 0, 2);
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+
+=C2=A0 =C2=A0 if (hartid >=3D clic->num_harts) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "clic: invalid hartid %u: 0x%" HWADDR_PRIx "\n", +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 hartid, addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return 0;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (irq >=3D clic->num_sources) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "clic: invalid irq %u: 0x%" HWADDR_PRIx "\n", irq, = addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return 0;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 switch (req) {
+=C2=A0 =C2=A0 case 0: /* clicintip[i] */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->clicintip[irq_offset];
+=C2=A0 =C2=A0 case 1: /* clicintie[i] */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->clicintie[irq_offset];
+=C2=A0 =C2=A0 case 2: /* clicintattr[i] */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* clicintattr register layout
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* Bits Field
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 7:6 mode
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 5:3 reserved (WPRI 0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 2:1 trig
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 0 shv
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->clicintattr[irq_offset] & = ~0x38;
+=C2=A0 =C2=A0 case 3: /* clicintctrl */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* The implemented bits are kept left-jus= tified in the most-significant
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* bits of each 8-bit clicintctl[i] regis= ter, with the lower
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* unimplemented bits treated as hardwire= d to 1.(Section 3.7)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->clicintctl[irq_offset] |
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0((1 << (8 - c= lic->clicintctlbits)) - 1);
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 return 0;
+}
+
+/* Return target interrupt mode */
+static int riscv_clic_get_mode(RISCVCLICState *clic, hwaddr addr)
+{
+=C2=A0 =C2=A0 int mode =3D addr / (4 * clic->num_harts * clic->num_s= ources);
+=C2=A0 =C2=A0 switch (mode) {
+=C2=A0 =C2=A0 case 0:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return PRV_M;
+=C2=A0 =C2=A0 case 1:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 assert(clic->prv_s || clic->prv_u);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->prv_s ? PRV_S : PRV_U;
+=C2=A0 =C2=A0 case 2:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 assert(clic->prv_s && clic->prv_= u);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return PRV_U;
+=C2=A0 =C2=A0 default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_assert_not_reached();
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 }
+}
+
+/* Return target hart id */
+static int riscv_clic_get_hartid(RISCVCLICState *clic, hwaddr addr)
+{
+=C2=A0 =C2=A0 int mode_unit =3D 4 * clic->num_harts * clic->num_sour= ces;
+=C2=A0 =C2=A0 int hart_unit =3D 4 * clic->num_sources;
+
+=C2=A0 =C2=A0 return (addr % mode_unit) / hart_unit;
+}
+
+/* Return target interrupt number */
+static int riscv_clic_get_irq(RISCVCLICState *clic, hwaddr addr)
+{
+=C2=A0 =C2=A0 int hart_unit =3D 4 * clic->num_sources;
+=C2=A0 =C2=A0 return (addr % hart_unit) / 4;
+}
+
+static void
+riscv_clic_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)=
+{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D opaque;
+=C2=A0 =C2=A0 hwaddr clic_size =3D clic->clic_size;
+=C2=A0 =C2=A0 int hartid, mode, irq;
+
+=C2=A0 =C2=A0 if (addr < clic_size) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (addr < 0x1000) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 assert(addr % 4 =3D=3D 0);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 int index =3D addr / 4;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (index) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 0: /* cliccfg */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 uint= 8_t nlbits =3D extract32(value, 1, 4);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 uint= 8_t nmbits =3D extract32(value, 5, 2);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0* The 4-bit cliccfg.nlbits WARL field.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0* Valid values are 0=E2=80=948.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (= nlbits <=3D 8) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 clic->nlbits =3D nlbits;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* V= alid values are given by implemented priviledges */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (= clic->prv_s && clic->prv_u) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 if (nmbits <=3D 2) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 clic->nmbits =3D nmbits;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } el= se if (clic->prv_u) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 if (nmbits <=3D 1) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 clic->nmbits =3D nmbits;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } el= se {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 assert(!clic->prv_s);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 if (nmbits =3D=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 clic->nmbits =3D 0;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic= ->nvbits =3D extract32(value, 0, 1);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 brea= k;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 1: /* clicinfo, read-only r= egister */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_= GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clic: write read-only clicinfo.\n&quo= t;);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 0x10 ... 0x2F: /* clicinttr= ig */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 uint= 32_t interrupt_number =3D value & MAKE_64BIT_MASK(0, 13);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (= interrupt_number <=3D clic->num_sources) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 value &=3D ~MAKE_64BIT_MASK(13, 18);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 clic->clicinttrig[index - 0x10] =3D value;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 brea= k;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 2: /* mintthresh */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (!strcmp(clic-&= gt;version, "v0.8")) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 clic= ->mintthresh =3D value;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 brea= k;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_= GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clic: invalid write addr: 0x%" H= WADDR_PRIx "\n",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_= GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clic: invalid write addr: 0x%" H= WADDR_PRIx "\n",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 addr -=3D 0x1000;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 hartid =3D riscv_clic_get_hartid= (clic, addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mode =3D riscv_clic_get_mode(cli= c, addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 irq =3D riscv_clic_get_irq(clic,= addr);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (riscv_clic_check_visible(cli= c, mode, hartid, irq)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_hart_wr= ite(clic, addr, value, size, mode,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 hartid, irq); +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "clic: invalid write: 0x%" HWADDR_PRIx "\n", addr);=
+=C2=A0 =C2=A0 }
+}
+
+static uint64_t riscv_clic_read(void *opaque, hwaddr addr, unsigned size)<= br> +{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D opaque;
+=C2=A0 =C2=A0 hwaddr clic_size =3D clic->clic_size;
+=C2=A0 =C2=A0 int hartid, mode, irq;
+
+=C2=A0 =C2=A0 if (addr < clic_size) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (addr < 0x1000) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 assert(addr % 4 =3D=3D 0);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 int index =3D addr / 4;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (index) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 0: /* cliccfg */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->nv= bits |
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0(clic->nlbits << 1) |
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0(clic->nmbits << 5);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 1: /* clicinfo */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* clicinfo r= egister layout
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* Bits Field=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 31 reserve= d (WARL 0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 30:25 num_= trigger
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 24:21 CLIC= INTCTLBITS
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 20:13 vers= ion (for version control)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 12:0 num_i= nterrupt
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->cl= icinfo & ~INT32_MAX;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 0x10 ... 0x2F: /* clicinttr= ig */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* clicinttri= g register layout
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* Bits Field=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 31 enable<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 30:13 rese= rved (WARL 0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* 12:0 inter= rupt_number
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return clic->cl= icinttrig[index - 0x10] &
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0~MAKE_64BIT_MASK(13, 18);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 case 2: /* mintthresh */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (!strcmp(clic-&= gt;version, "v0.8")) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 retu= rn clic->mintthresh;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 brea= k;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_= GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clic: invalid read : 0x%" HWADDR= _PRIx "\n",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_= GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "clic: invalid read : 0x%" HWADDR= _PRIx "\n",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 addr -=3D 0x1000;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 hartid =3D riscv_clic_get_hartid= (clic, addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mode =3D riscv_clic_get_mode(cli= c, addr);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 irq =3D riscv_clic_get_irq(clic,= addr);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (riscv_clic_check_visible(cli= c, mode, hartid, irq)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return riscv_clic_= hart_read(clic, addr, mode, hartid, irq);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_log_mask(LOG_GUEST_ERROR,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 "clic: invalid read: 0x%" HWADDR_PRIx "\n", addr);<= br> +=C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 return 0;
+}
+
+static void riscv_clic_set_irq(void *opaque, int id, int level)
+{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D opaque;
+=C2=A0 =C2=A0 int irq, hartid, mode;
+=C2=A0 =C2=A0 hwaddr addr =3D 4 * id;
+=C2=A0 =C2=A0 TRIG_TYPE type;
+
+=C2=A0 =C2=A0 hartid =3D riscv_clic_get_hartid(clic, addr);
+=C2=A0 =C2=A0 mode =3D riscv_clic_get_mode(clic, addr);
+=C2=A0 =C2=A0 irq =3D riscv_clic_get_irq(clic, addr);
+=C2=A0 =C2=A0 type =3D riscv_clic_get_trigger_type(clic, id);
+
+=C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0* In general, the edge-triggered interrupt state shoul= d be kept in pending
+=C2=A0 =C2=A0 =C2=A0* bit, while the level-triggered interrupt should be k= ept in the level
+=C2=A0 =C2=A0 =C2=A0* state of the incoming wire.
+=C2=A0 =C2=A0 =C2=A0*
+=C2=A0 =C2=A0 =C2=A0* For CLIC, model the level-triggered interrupt by rea= d-only pending bit.
+=C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 if (level) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (type) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case POSITIVE_LEVEL:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case POSITIVE_EDGE:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_update_intip(clic, mo= de, hartid, irq, level);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case NEG_LEVEL:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_update_intip(clic, mo= de, hartid, irq, !level);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case NEG_EDGE:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 switch (type) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case POSITIVE_LEVEL:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_update_intip(clic, mo= de, hartid, irq, level);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case POSITIVE_EDGE:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case NEG_LEVEL:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 case NEG_EDGE:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 riscv_clic_update_intip(clic, mo= de, hartid, irq, !level);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+}
+
+static void riscv_clic_cpu_irq_handler(void *opaque, int irq, int level) +{
+=C2=A0 =C2=A0 CPURISCVState *env =3D (CPURISCVState *)opaque;
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D env->clic;
+=C2=A0 =C2=A0 CPUState *cpu =3D env_cpu(env);
+
+=C2=A0 =C2=A0 if (level) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 env->exccode =3D clic->exccode[cpu->c= pu_index];
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 cpu_interrupt(env_cpu(env), CPU_INTERRUPT_CLIC= );
+=C2=A0 =C2=A0 }
+}
+
+static const MemoryRegionOps riscv_clic_ops =3D {
+=C2=A0 =C2=A0 .read =3D riscv_clic_read,
+=C2=A0 =C2=A0 .write =3D riscv_clic_write,
+=C2=A0 =C2=A0 .endianness =3D DEVICE_LITTLE_ENDIAN,
+=C2=A0 =C2=A0 .valid =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 .min_access_size =3D 1,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 .max_access_size =3D 8
+=C2=A0 =C2=A0 }
+};
+
+static void riscv_clic_realize(DeviceState *dev, Error **errp)
+{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D RISCV_CLIC(dev);
+=C2=A0 =C2=A0 size_t harts_x_sources =3D clic->num_harts * clic->num= _sources;
+=C2=A0 =C2=A0 int irqs, i;
+
+=C2=A0 =C2=A0 if (clic->prv_s && clic->prv_u) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 irqs =3D 3 * harts_x_sources;
+=C2=A0 =C2=A0 } else if (clic->prv_s || clic->prv_u) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 irqs =3D 2 * harts_x_sources;
+=C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 irqs =3D harts_x_sources;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 clic->clic_size =3D irqs * 4 + 0x1000;
+=C2=A0 =C2=A0 memory_region_init_io(&clic->mmio, OBJECT(dev), &= riscv_clic_ops, clic,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 TYPE_RISCV_CLIC, clic->clic_size);
+
+=C2=A0 =C2=A0 clic->clicintip =3D g_new0(uint8_t, irqs);
+=C2=A0 =C2=A0 clic->clicintie =3D g_new0(uint8_t, irqs);
+=C2=A0 =C2=A0 clic->clicintattr =3D g_new0(uint8_t, irqs);
+=C2=A0 =C2=A0 clic->clicintctl =3D g_new0(uint8_t, irqs);
+=C2=A0 =C2=A0 clic->active_list =3D g_new0(CLICActiveInterrupt, irqs);<= br> +=C2=A0 =C2=A0 clic->active_count =3D g_new0(size_t, clic->num_harts)= ;
+=C2=A0 =C2=A0 clic->exccode =3D g_new0(uint32_t, clic->num_harts); +=C2=A0 =C2=A0 clic->cpu_irqs =3D g_new0(qemu_irq, clic->num_harts);<= br> +=C2=A0 =C2=A0 sysbus_init_mmio(SYS_BUS_DEVICE(dev), &clic->mmio); +
+=C2=A0 =C2=A0 /* Allocate irq through gpio, so that we can use qtest */ +=C2=A0 =C2=A0 qdev_init_gpio_in(dev, riscv_clic_set_irq, irqs);
+=C2=A0 =C2=A0 qdev_init_gpio_out(dev, clic->cpu_irqs, clic->num_hart= s);
+
+=C2=A0 =C2=A0 for (i =3D 0; i < clic->num_harts; i++) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 RISCVCPU *cpu =3D RISCV_CPU(qemu_get_cpu(i));<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_irq irq =3D qemu_allocate_irq(riscv_clic_= cpu_irq_handler,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0&a= mp;cpu->env, 1);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qdev_connect_gpio_out(dev, i, irq);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 cpu->env.clic =3D clic;
+=C2=A0 =C2=A0 }
+}
+
+static Property riscv_clic_properties[] =3D {
+=C2=A0 =C2=A0 DEFINE_PROP_BOOL("prv-s", RISCVCLICState, prv_s, f= alse),
+=C2=A0 =C2=A0 DEFINE_PROP_BOOL("prv-u", RISCVCLICState, prv_u, f= alse),
+=C2=A0 =C2=A0 DEFINE_PROP_UINT32("num-harts", RISCVCLICState, nu= m_harts, 0),
+=C2=A0 =C2=A0 DEFINE_PROP_UINT32("num-sources", RISCVCLICState, = num_sources, 0),
+=C2=A0 =C2=A0 DEFINE_PROP_UINT32("clicintctlbits", RISCVCLICStat= e, clicintctlbits, 0),
+=C2=A0 =C2=A0 DEFINE_PROP_UINT64("mclicbase", RISCVCLICState, mc= licbase, 0),
+=C2=A0 =C2=A0 DEFINE_PROP_STRING("version", RISCVCLICState, vers= ion),
+=C2=A0 =C2=A0 DEFINE_PROP_END_OF_LIST(),
+};
+
+static void riscv_clic_class_init(ObjectClass *klass, void *data)
+{
+=C2=A0 =C2=A0 DeviceClass *dc =3D DEVICE_CLASS(klass);
+
+=C2=A0 =C2=A0 dc->realize =3D riscv_clic_realize;
+=C2=A0 =C2=A0 device_class_set_props(dc, riscv_clic_properties);
+}
+
+static const TypeInfo riscv_clic_info =3D {
+=C2=A0 =C2=A0 .name=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D TYPE_RISCV_CLIC,=
+=C2=A0 =C2=A0 .parent=C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D TYPE_SYS_BUS_DEVICE,<= br> +=C2=A0 =C2=A0 .instance_size =3D sizeof(RISCVCLICState),
+=C2=A0 =C2=A0 .class_init=C2=A0 =C2=A0 =3D riscv_clic_class_init,
+};
+
+static void riscv_clic_register_types(void)
+{
+=C2=A0 =C2=A0 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 regist= ers
+ * @version: clic version, such as "v0.9"
+ *
+ * Returns: the device object
+ */
+DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t num_harts, uint32_t num_sour= ces,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint8_t clicintctlbits,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0const char *version)
+{
+=C2=A0 =C2=A0 DeviceState *dev =3D qdev_new(TYPE_RISCV_CLIC);
+
+=C2=A0 =C2=A0 assert(num_sources <=3D 4096);
+=C2=A0 =C2=A0 assert(num_harts <=3D 1024);
+=C2=A0 =C2=A0 assert(clicintctlbits <=3D 8);
+=C2=A0 =C2=A0 assert(!strcmp(version, "v0.8") || !strcmp(version= , "v0.9"));
+
+=C2=A0 =C2=A0 qdev_prop_set_bit(dev, "prv-s", prv_s);
+=C2=A0 =C2=A0 qdev_prop_set_bit(dev, "prv-u", prv_u);
+=C2=A0 =C2=A0 qdev_prop_set_uint32(dev, "num-harts", num_harts);=
+=C2=A0 =C2=A0 qdev_prop_set_uint32(dev, "num-sources", num_sourc= es);
+=C2=A0 =C2=A0 qdev_prop_set_uint32(dev, "clicintctlbits", clicin= tctlbits);
+=C2=A0 =C2=A0 qdev_prop_set_uint64(dev, "mclicbase", addr);
+=C2=A0 =C2=A0 qdev_prop_set_string(dev, "version", version);
+
+=C2=A0 =C2=A0 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fat= al);
+=C2=A0 =C2=A0 sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+=C2=A0 =C2=A0 return dev;
+}
+
+void riscv_clic_get_next_interrupt(void *opaque, int hartid)
+{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D opaque;
+=C2=A0 =C2=A0 riscv_clic_next_interrupt(clic, hartid);
+}
+
+bool riscv_clic_shv_interrupt(void *opaque, int mode, int hartid, int irq)=
+{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D opaque;
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+=C2=A0 =C2=A0 return riscv_clic_is_shv_interrupt(clic, irq_offset);
+}
+
+bool riscv_clic_edge_triggered(void *opaque, int mode, int hartid, int irq= )
+{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D opaque;
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+=C2=A0 =C2=A0 return riscv_clic_is_edge_triggered(clic, irq_offset);
+}
+
+void riscv_clic_clean_pending(void *opaque, int mode, int hartid, int irq)=
+{
+=C2=A0 =C2=A0 RISCVCLICState *clic =3D opaque;
+=C2=A0 =C2=A0 size_t irq_offset =3D riscv_clic_get_irq_offset(clic, mode, = hartid, irq);
+=C2=A0 =C2=A0 clic->clicintip[irq_offset] =3D 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=C2=A0 are 1= 1.
+ */
+bool riscv_clic_is_clic_mode(CPURISCVState *env)
+{
+=C2=A0 =C2=A0 target_ulong xtvec =3D (env->priv =3D=3D PRV_M) ? env->= ;mtvec : env->stvec;
+=C2=A0 =C2=A0 return env->clic && ((xtvec & 0x3) =3D=3D 3);=
+}
+
+void riscv_clic_decode_exccode(uint32_t exccode, int *mode,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int *il, int *irq)
+{
+=C2=A0 =C2=A0 *irq =3D extract32(exccode, 0, 12);
+=C2=A0 =C2=A0 *mode =3D extract32(exccode, 12, 2);
+=C2=A0 =C2=A0 *il =3D 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.<= br> + *
+ * 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<= br> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.=C2=A0 See the GNU General Public Lice= nse for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along= with
+ * this program.=C2=A0 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) \
+=C2=A0 =C2=A0 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 {
+=C2=A0 =C2=A0 uint16_t intcfg;
+=C2=A0 =C2=A0 uint16_t irq;
+} CLICActiveInterrupt;
+
+typedef enum TRIG_TYPE {
+=C2=A0 =C2=A0 POSITIVE_LEVEL,
+=C2=A0 =C2=A0 POSITIVE_EDGE,
+=C2=A0 =C2=A0 NEG_LEVEL,
+=C2=A0 =C2=A0 NEG_EDGE,
+} TRIG_TYPE;
+
+typedef struct RISCVCLICState {
+=C2=A0 =C2=A0 /*< private >*/
+=C2=A0 =C2=A0 SysBusDevice parent_obj;
+
+=C2=A0 =C2=A0 /*< public >*/
+
+=C2=A0 =C2=A0 /* Implementaion parameters */
+=C2=A0 =C2=A0 bool prv_s;
+=C2=A0 =C2=A0 bool prv_u;
+=C2=A0 =C2=A0 uint32_t num_harts;
+=C2=A0 =C2=A0 uint32_t num_sources;
+=C2=A0 =C2=A0 uint32_t clic_size;
+=C2=A0 =C2=A0 uint32_t clic_mmode_base;
+=C2=A0 =C2=A0 uint32_t clicintctlbits;
+=C2=A0 =C2=A0 uint64_t mclicbase;
+=C2=A0 =C2=A0 char *version;
+
+=C2=A0 =C2=A0 /* Global configuration */
+=C2=A0 =C2=A0 uint8_t nmbits;
+=C2=A0 =C2=A0 uint8_t nlbits;
+=C2=A0 =C2=A0 uint8_t nvbits;
+=C2=A0 =C2=A0 uint32_t clicinfo;
+=C2=A0 =C2=A0 uint32_t clicinttrig[32];
+
+=C2=A0 =C2=A0 /* Aperture configuration */
+=C2=A0 =C2=A0 uint8_t *clicintip;
+=C2=A0 =C2=A0 uint8_t *clicintie;
+=C2=A0 =C2=A0 uint8_t *clicintattr;
+=C2=A0 =C2=A0 uint8_t *clicintctl;
+
+=C2=A0 =C2=A0 /* Complatible with v0.8 */
+=C2=A0 =C2=A0 uint32_t mintthresh;
+=C2=A0 =C2=A0 uint32_t sintthresh;
+=C2=A0 =C2=A0 uint32_t uintthresh;
+
+=C2=A0 =C2=A0 /* QEMU implementaion related fields */
+=C2=A0 =C2=A0 uint32_t *exccode;
+=C2=A0 =C2=A0 CLICActiveInterrupt *active_list;
+=C2=A0 =C2=A0 size_t *active_count;
+=C2=A0 =C2=A0 MemoryRegion mmio;
+=C2=A0 =C2=A0 qemu_irq *cpu_irqs;
+} RISCVCLICState;
+
+DeviceState *riscv_clic_create(hwaddr addr, bool prv_s, bool prv_u,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint32_t num_harts, uint32_t num_sour= ces,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0uint8_t clicintctlbits,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0const 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 @@
=C2=A0#define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
=C2=A0#define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
=C2=A0#define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
+#define CPU_INTERRUPT_CLIC CPU_INTERRUPT_TGT_EXT_0

=C2=A0#define TYPE_RISCV_CPU_ANY=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 RISCV_CPU_TYPE_NAME("any")
=C2=A0#define TYPE_RISCV_CPU_BASE32=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0RISCV_CPU_TYPE_NAME("rv32")
@@ -247,6 +248,7 @@ struct CPURISCVState {
=C2=A0 =C2=A0 =C2=A0/* Fields from here on are preserved across CPU reset. = */
=C2=A0 =C2=A0 =C2=A0QEMUTimer *timer; /* Internal timer */
=C2=A0 =C2=A0 =C2=A0void *clic;=C2=A0 =C2=A0 =C2=A0 =C2=A0/* clic interrupt= controller */
+=C2=A0 =C2=A0 uint32_t exccode; /* clic irq encode */
=C2=A0};

=C2=A0OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
--
2.25.1


--0000000000009f629105c6fbb3f6--