[PATCH v16 09/12] hw/riscv: Add support for RISCV CPS

Djordje Todorovic posted 12 patches 1 month ago
Maintainers: Palmer Dabbelt <palmer@dabbelt.com>, Alistair Francis <alistair.francis@wdc.com>, Weiwei Li <liwei1518@gmail.com>, Daniel Henrique Barboza <dbarboza@ventanamicro.com>, Liu Zhiwei <zhiwei_liu@linux.alibaba.com>, Paolo Bonzini <pbonzini@redhat.com>
[PATCH v16 09/12] hw/riscv: Add support for RISCV CPS
Posted by Djordje Todorovic 1 month ago
Add support for the Coherent Processing System for RISC-V.
This enables SMP support for RISC-V boards that require
cache-coherent multiprocessor systems.

Signed-off-by: Chao-ying Fu <cfu@mips.com>
Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com>
Acked-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
---
 hw/misc/Kconfig        |   4 +
 hw/riscv/cps.c         | 196 +++++++++++++++++++++++++++++++++++++++++
 hw/riscv/meson.build   |   2 +
 include/hw/riscv/cps.h |  66 ++++++++++++++
 4 files changed, 268 insertions(+)
 create mode 100644 hw/riscv/cps.c
 create mode 100644 include/hw/riscv/cps.h

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 38be72b141..4a22d68233 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -127,12 +127,16 @@ config RISCV_MIPS_CMGCR
 config RISCV_MIPS_CPC
     bool
 
+config RISCV_MIPS_CPS
+    bool
+
 config MIPS_BOSTON_AIA
     bool
     default y
     depends on RISCV64
     select RISCV_MIPS_CMGCR
     select RISCV_MIPS_CPC
+    select RISCV_MIPS_CPS
 
 config MPS2_FPGAIO
     bool
diff --git a/hw/riscv/cps.c b/hw/riscv/cps.c
new file mode 100644
index 0000000000..86172be5b3
--- /dev/null
+++ b/hw/riscv/cps.c
@@ -0,0 +1,196 @@
+/*
+ * Coherent Processing System emulation.
+ *
+ * Copyright (c) 2016 Imagination Technologies
+ *
+ * Copyright (c) 2025 MIPS
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "hw/riscv/cps.h"
+#include "hw/core/qdev-properties.h"
+#include "system/reset.h"
+#include "hw/intc/riscv_aclint.h"
+#include "hw/intc/riscv_aplic.h"
+#include "hw/intc/riscv_imsic.h"
+#include "hw/pci/msi.h"
+
+static void riscv_cps_init(Object *obj)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    RISCVCPSState *s = RISCV_CPS(obj);
+
+    /*
+     * Cover entire address space as there do not seem to be any
+     * constraints for the base address of CPC .
+     */
+    memory_region_init(&s->container, obj, "mips-cps-container", UINT64_MAX);
+    sysbus_init_mmio(sbd, &s->container);
+}
+
+static void main_cpu_reset(void *opaque)
+{
+    CPUState *cs = opaque;
+
+    cpu_reset(cs);
+}
+
+static void riscv_cps_realize(DeviceState *dev, Error **errp)
+{
+    RISCVCPSState *s = RISCV_CPS(dev);
+    RISCVCPU *cpu;
+    int i;
+
+    /* Validate num_vp */
+    if (s->num_vp == 0) {
+        error_setg(errp, "num-vp must be at least 1");
+        return;
+    }
+    if (s->num_vp > MAX_HARTS) {
+        error_setg(errp, "num-vp cannot exceed %d", MAX_HARTS);
+        return;
+    }
+
+    /* Allocate CPU array */
+    s->cpus = g_new0(CPUState *, s->num_vp);
+
+    /* Set up cpu_index and mhartid for avaiable CPUs. */
+    int harts_in_cluster = s->num_hart * s->num_core;
+    int num_of_clusters = s->num_vp / harts_in_cluster;
+    for (i = 0; i < s->num_vp; i++) {
+        cpu = RISCV_CPU(object_new(s->cpu_type));
+
+        /* All VPs are halted on reset. Leave powering up to CPC. */
+        object_property_set_bool(OBJECT(cpu), "start-powered-off", true,
+                                 &error_abort);
+
+        if (!qdev_realize_and_unref(DEVICE(cpu), NULL, errp)) {
+            return;
+        }
+
+        /* Store CPU in array */
+        s->cpus[i] = CPU(cpu);
+
+        /* Set up mhartid */
+        int cluster_id = i / harts_in_cluster;
+        int hart_id = (i % harts_in_cluster) % s->num_hart;
+        int core_id = (i % harts_in_cluster) / s->num_hart;
+        int mhartid = (cluster_id << MHARTID_CLUSTER_SHIFT) +
+                      (core_id << MHARTID_CORE_SHIFT) +
+                      (hart_id << MHARTID_HART_SHIFT);
+        cpu->env.mhartid = mhartid;
+        qemu_register_reset(main_cpu_reset, s->cpus[i]);
+    }
+
+    /* Cluster Power Controller */
+    object_initialize_child(OBJECT(dev), "cpc", &s->cpc, TYPE_RISCV_CPC);
+    object_property_set_uint(OBJECT(&s->cpc), "cluster-id", 0,
+                            &error_abort);
+    object_property_set_uint(OBJECT(&s->cpc), "num-vp", s->num_vp,
+                            &error_abort);
+    object_property_set_uint(OBJECT(&s->cpc), "num-hart", s->num_hart,
+                            &error_abort);
+    object_property_set_uint(OBJECT(&s->cpc), "num-core", s->num_core,
+                            &error_abort);
+
+    /* Pass CPUs to CPC using link properties */
+    for (i = 0; i < s->num_vp; i++) {
+        char *propname = g_strdup_printf("cpu[%d]", i);
+        object_property_set_link(OBJECT(&s->cpc), propname,
+                                OBJECT(s->cpus[i]), &error_abort);
+        g_free(propname);
+    }
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->cpc), errp)) {
+        return;
+    }
+
+    memory_region_add_subregion(&s->container, 0,
+                            sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpc), 0));
+
+    /* Global Configuration Registers */
+    object_initialize_child(OBJECT(dev), "gcr", &s->gcr, TYPE_RISCV_GCR);
+    object_property_set_uint(OBJECT(&s->gcr), "cluster-id", 0,
+                            &error_abort);
+    object_property_set_uint(OBJECT(&s->gcr), "num-vp", s->num_vp,
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->gcr), "gcr-rev", 0xa00,
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->gcr), "gcr-base", s->gcr_base,
+                            &error_abort);
+    object_property_set_link(OBJECT(&s->gcr), "cpc", OBJECT(&s->cpc.mr),
+                             &error_abort);
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->gcr), errp)) {
+        return;
+    }
+
+    memory_region_add_subregion(&s->container, s->gcr_base,
+                            sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gcr), 0));
+
+    for (i = 0; i < num_of_clusters; i++) {
+        uint64_t cm_base = GLOBAL_CM_BASE + (CM_SIZE * i);
+        uint32_t hartid_base = i << MHARTID_CLUSTER_SHIFT;
+        s->aplic = riscv_aplic_create(cm_base + AIA_PLIC_M_OFFSET,
+                                      AIA_PLIC_M_SIZE,
+                                      hartid_base, /* hartid_base */
+                                      MAX_HARTS, /* num_harts */
+                                      APLIC_NUM_SOURCES,
+                                      APLIC_NUM_PRIO_BITS,
+                                      false, true, NULL);
+        riscv_aplic_create(cm_base + AIA_PLIC_S_OFFSET,
+                           AIA_PLIC_S_SIZE,
+                           hartid_base, /* hartid_base */
+                           MAX_HARTS, /* num_harts */
+                           APLIC_NUM_SOURCES,
+                           APLIC_NUM_PRIO_BITS,
+                           false, false, s->aplic);
+        /* PLIC changes msi_nonbroken to ture. We revert the change. */
+        msi_nonbroken = false;
+        riscv_aclint_swi_create(cm_base + AIA_CLINT_OFFSET,
+                                hartid_base, MAX_HARTS, false);
+        riscv_aclint_mtimer_create(cm_base + AIA_CLINT_OFFSET +
+                                   RISCV_ACLINT_SWI_SIZE,
+                                   RISCV_ACLINT_DEFAULT_MTIMER_SIZE,
+                                   hartid_base,
+                                   MAX_HARTS,
+                                   RISCV_ACLINT_DEFAULT_MTIMECMP,
+                                   RISCV_ACLINT_DEFAULT_MTIME,
+                                   RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false);
+    }
+}
+
+static const Property riscv_cps_properties[] = {
+    DEFINE_PROP_UINT32("num-vp", RISCVCPSState, num_vp, 1),
+    DEFINE_PROP_UINT32("num-hart", RISCVCPSState, num_hart, 1),
+    DEFINE_PROP_UINT32("num-core", RISCVCPSState, num_core, 1),
+    DEFINE_PROP_UINT64("gcr-base", RISCVCPSState, gcr_base, GCR_BASE_ADDR),
+    DEFINE_PROP_STRING("cpu-type", RISCVCPSState, cpu_type),
+};
+
+static void riscv_cps_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = riscv_cps_realize;
+    device_class_set_props(dc, riscv_cps_properties);
+}
+
+static const TypeInfo riscv_cps_info = {
+    .name = TYPE_RISCV_CPS,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(RISCVCPSState),
+    .instance_init = riscv_cps_init,
+    .class_init = riscv_cps_class_init,
+};
+
+static void riscv_cps_register_types(void)
+{
+    type_register_static(&riscv_cps_info);
+}
+
+type_init(riscv_cps_register_types)
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index 2a8d5b136c..9023b80087 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -15,4 +15,6 @@ riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files(
 riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-generic.c'))
 riscv_ss.add(when: 'CONFIG_XIANGSHAN_KUNMINGHU', if_true: files('xiangshan_kmh.c'))
 
+riscv_ss.add(when: 'CONFIG_RISCV_MIPS_CPS', if_true: files('cps.c'))
+
 hw_arch += {'riscv': riscv_ss}
diff --git a/include/hw/riscv/cps.h b/include/hw/riscv/cps.h
new file mode 100644
index 0000000000..f33fd7ac86
--- /dev/null
+++ b/include/hw/riscv/cps.h
@@ -0,0 +1,66 @@
+/*
+ * Coherent Processing System emulation.
+ *
+ * Copyright (c) 2016 Imagination Technologies
+ *
+ * Copyright (c) 2025 MIPS
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef RISCV_CPS_H
+#define RISCV_CPS_H
+
+#include "hw/core/sysbus.h"
+#include "hw/misc/riscv_cmgcr.h"
+#include "hw/misc/riscv_cpc.h"
+#include "target/riscv/cpu.h"
+#include "qom/object.h"
+
+#define TYPE_RISCV_CPS "riscv-cps"
+OBJECT_DECLARE_SIMPLE_TYPE(RISCVCPSState, RISCV_CPS)
+
+/* The model supports up to 64 harts. */
+#define MAX_HARTS 64
+
+/* The global CM base for the boston-aia model. */
+#define GLOBAL_CM_BASE 0x16100000
+/* The CM block is 512 KiB. */
+#define CM_SIZE (1 << 19)
+
+/*
+ * The mhartid bits has cluster at bit 16, core at bit 4, and hart at
+ * bit 0.
+ */
+
+#define MHARTID_CLUSTER_SHIFT 16
+#define MHARTID_CORE_SHIFT 4
+#define MHARTID_HART_SHIFT 0
+
+#define APLIC_NUM_SOURCES 0x35 /* Arbitray maximum number of interrupts. */
+#define APLIC_NUM_PRIO_BITS 3
+#define AIA_PLIC_M_OFFSET 0x40000
+#define AIA_PLIC_M_SIZE 0x8000
+#define AIA_PLIC_S_OFFSET 0x60000
+#define AIA_PLIC_S_SIZE 0x8000
+#define AIA_CLINT_OFFSET 0x50000
+
+typedef struct RISCVCPSState {
+    SysBusDevice parent_obj;
+
+    uint32_t num_vp;
+    uint32_t num_hart;
+    uint32_t num_core;
+    uint64_t gcr_base;
+    char *cpu_type;
+
+    MemoryRegion container;
+    RISCVGCRState gcr;
+    RISCVCPCState cpc;
+
+    DeviceState *aplic;
+    CPUState **cpus;
+} RISCVCPSState;
+
+#endif
-- 
2.34.1
Re: [PATCH v16 09/12] hw/riscv: Add support for RISCV CPS
Posted by Alistair Francis 4 weeks, 1 day ago
On Thu, Jan 8, 2026 at 11:41 PM Djordje Todorovic
<Djordje.Todorovic@htecgroup.com> wrote:
>
> Add support for the Coherent Processing System for RISC-V.
> This enables SMP support for RISC-V boards that require
> cache-coherent multiprocessor systems.
>
> Signed-off-by: Chao-ying Fu <cfu@mips.com>
> Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com>
> Acked-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
> ---
>  hw/misc/Kconfig        |   4 +
>  hw/riscv/cps.c         | 196 +++++++++++++++++++++++++++++++++++++++++
>  hw/riscv/meson.build   |   2 +
>  include/hw/riscv/cps.h |  66 ++++++++++++++
>  4 files changed, 268 insertions(+)
>  create mode 100644 hw/riscv/cps.c
>  create mode 100644 include/hw/riscv/cps.h
>
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index 38be72b141..4a22d68233 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -127,12 +127,16 @@ config RISCV_MIPS_CMGCR
>  config RISCV_MIPS_CPC
>      bool
>
> +config RISCV_MIPS_CPS
> +    bool
> +
>  config MIPS_BOSTON_AIA
>      bool
>      default y
>      depends on RISCV64
>      select RISCV_MIPS_CMGCR
>      select RISCV_MIPS_CPC
> +    select RISCV_MIPS_CPS
>
>  config MPS2_FPGAIO
>      bool
> diff --git a/hw/riscv/cps.c b/hw/riscv/cps.c
> new file mode 100644
> index 0000000000..86172be5b3
> --- /dev/null
> +++ b/hw/riscv/cps.c
> @@ -0,0 +1,196 @@
> +/*
> + * Coherent Processing System emulation.
> + *
> + * Copyright (c) 2016 Imagination Technologies
> + *
> + * Copyright (c) 2025 MIPS
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/module.h"
> +#include "hw/riscv/cps.h"
> +#include "hw/core/qdev-properties.h"
> +#include "system/reset.h"
> +#include "hw/intc/riscv_aclint.h"
> +#include "hw/intc/riscv_aplic.h"
> +#include "hw/intc/riscv_imsic.h"
> +#include "hw/pci/msi.h"
> +
> +static void riscv_cps_init(Object *obj)
> +{
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> +    RISCVCPSState *s = RISCV_CPS(obj);
> +
> +    /*
> +     * Cover entire address space as there do not seem to be any
> +     * constraints for the base address of CPC .
> +     */
> +    memory_region_init(&s->container, obj, "mips-cps-container", UINT64_MAX);
> +    sysbus_init_mmio(sbd, &s->container);
> +}
> +
> +static void main_cpu_reset(void *opaque)
> +{
> +    CPUState *cs = opaque;
> +
> +    cpu_reset(cs);
> +}
> +
> +static void riscv_cps_realize(DeviceState *dev, Error **errp)
> +{
> +    RISCVCPSState *s = RISCV_CPS(dev);
> +    RISCVCPU *cpu;
> +    int i;
> +
> +    /* Validate num_vp */
> +    if (s->num_vp == 0) {
> +        error_setg(errp, "num-vp must be at least 1");
> +        return;
> +    }
> +    if (s->num_vp > MAX_HARTS) {
> +        error_setg(errp, "num-vp cannot exceed %d", MAX_HARTS);
> +        return;
> +    }
> +
> +    /* Allocate CPU array */
> +    s->cpus = g_new0(CPUState *, s->num_vp);
> +
> +    /* Set up cpu_index and mhartid for avaiable CPUs. */
> +    int harts_in_cluster = s->num_hart * s->num_core;
> +    int num_of_clusters = s->num_vp / harts_in_cluster;
> +    for (i = 0; i < s->num_vp; i++) {
> +        cpu = RISCV_CPU(object_new(s->cpu_type));
> +
> +        /* All VPs are halted on reset. Leave powering up to CPC. */
> +        object_property_set_bool(OBJECT(cpu), "start-powered-off", true,
> +                                 &error_abort);
> +
> +        if (!qdev_realize_and_unref(DEVICE(cpu), NULL, errp)) {
> +            return;
> +        }
> +
> +        /* Store CPU in array */
> +        s->cpus[i] = CPU(cpu);
> +
> +        /* Set up mhartid */
> +        int cluster_id = i / harts_in_cluster;
> +        int hart_id = (i % harts_in_cluster) % s->num_hart;
> +        int core_id = (i % harts_in_cluster) / s->num_hart;
> +        int mhartid = (cluster_id << MHARTID_CLUSTER_SHIFT) +
> +                      (core_id << MHARTID_CORE_SHIFT) +
> +                      (hart_id << MHARTID_HART_SHIFT);
> +        cpu->env.mhartid = mhartid;
> +        qemu_register_reset(main_cpu_reset, s->cpus[i]);
> +    }
> +
> +    /* Cluster Power Controller */
> +    object_initialize_child(OBJECT(dev), "cpc", &s->cpc, TYPE_RISCV_CPC);
> +    object_property_set_uint(OBJECT(&s->cpc), "cluster-id", 0,
> +                            &error_abort);
> +    object_property_set_uint(OBJECT(&s->cpc), "num-vp", s->num_vp,
> +                            &error_abort);
> +    object_property_set_uint(OBJECT(&s->cpc), "num-hart", s->num_hart,
> +                            &error_abort);
> +    object_property_set_uint(OBJECT(&s->cpc), "num-core", s->num_core,
> +                            &error_abort);
> +
> +    /* Pass CPUs to CPC using link properties */
> +    for (i = 0; i < s->num_vp; i++) {
> +        char *propname = g_strdup_printf("cpu[%d]", i);
> +        object_property_set_link(OBJECT(&s->cpc), propname,
> +                                OBJECT(s->cpus[i]), &error_abort);
> +        g_free(propname);
> +    }
> +
> +    if (!sysbus_realize(SYS_BUS_DEVICE(&s->cpc), errp)) {
> +        return;
> +    }
> +
> +    memory_region_add_subregion(&s->container, 0,
> +                            sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpc), 0));
> +
> +    /* Global Configuration Registers */
> +    object_initialize_child(OBJECT(dev), "gcr", &s->gcr, TYPE_RISCV_GCR);
> +    object_property_set_uint(OBJECT(&s->gcr), "cluster-id", 0,
> +                            &error_abort);
> +    object_property_set_uint(OBJECT(&s->gcr), "num-vp", s->num_vp,
> +                            &error_abort);
> +    object_property_set_int(OBJECT(&s->gcr), "gcr-rev", 0xa00,
> +                            &error_abort);
> +    object_property_set_int(OBJECT(&s->gcr), "gcr-base", s->gcr_base,
> +                            &error_abort);
> +    object_property_set_link(OBJECT(&s->gcr), "cpc", OBJECT(&s->cpc.mr),
> +                             &error_abort);
> +    if (!sysbus_realize(SYS_BUS_DEVICE(&s->gcr), errp)) {
> +        return;
> +    }
> +
> +    memory_region_add_subregion(&s->container, s->gcr_base,
> +                            sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gcr), 0));
> +
> +    for (i = 0; i < num_of_clusters; i++) {
> +        uint64_t cm_base = GLOBAL_CM_BASE + (CM_SIZE * i);

This causes a Coverity issue

*** CID 1644076:         Integer handling issues  (OVERFLOW_BEFORE_WIDEN)
/builds/qemu-project/qemu/hw/riscv/cps.c: 136             in riscv_cps_realize()
130         }
131
132         memory_region_add_subregion(&s->container, s->gcr_base,
133
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gcr), 0));
134
135         for (i = 0; i < num_of_clusters; i++) {
>>>     CID 1644076:         Integer handling issues  (OVERFLOW_BEFORE_WIDEN)
>>>     Potentially overflowing expression "524288 * i" with type "int" (32 bits, signed) is evaluated using 32-bit arithmetic, and then used in a context that expects an expression of type "uint64_t" (64 bits, unsigned).
136             uint64_t cm_base = GLOBAL_CM_BASE + (CM_SIZE * i);
137             uint32_t hartid_base = i << MHARTID_CLUSTER_SHIFT;
138             s->aplic = riscv_aplic_create(cm_base + AIA_PLIC_M_OFFSET,
139                                           AIA_PLIC_M_SIZE,
140                                           hartid_base, /* hartid_base */
141                                           MAX_HARTS, /* num_harts */


Can you send a patch to fix this issue and link to CID 1644076?

Alistair


> +        uint32_t hartid_base = i << MHARTID_CLUSTER_SHIFT;
> +        s->aplic = riscv_aplic_create(cm_base + AIA_PLIC_M_OFFSET,
> +                                      AIA_PLIC_M_SIZE,
> +                                      hartid_base, /* hartid_base */
> +                                      MAX_HARTS, /* num_harts */
> +                                      APLIC_NUM_SOURCES,
> +                                      APLIC_NUM_PRIO_BITS,
> +                                      false, true, NULL);
> +        riscv_aplic_create(cm_base + AIA_PLIC_S_OFFSET,
> +                           AIA_PLIC_S_SIZE,
> +                           hartid_base, /* hartid_base */
> +                           MAX_HARTS, /* num_harts */
> +                           APLIC_NUM_SOURCES,
> +                           APLIC_NUM_PRIO_BITS,
> +                           false, false, s->aplic);
> +        /* PLIC changes msi_nonbroken to ture. We revert the change. */
> +        msi_nonbroken = false;
> +        riscv_aclint_swi_create(cm_base + AIA_CLINT_OFFSET,
> +                                hartid_base, MAX_HARTS, false);
> +        riscv_aclint_mtimer_create(cm_base + AIA_CLINT_OFFSET +
> +                                   RISCV_ACLINT_SWI_SIZE,
> +                                   RISCV_ACLINT_DEFAULT_MTIMER_SIZE,
> +                                   hartid_base,
> +                                   MAX_HARTS,
> +                                   RISCV_ACLINT_DEFAULT_MTIMECMP,
> +                                   RISCV_ACLINT_DEFAULT_MTIME,
> +                                   RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false);
> +    }
> +}
> +
> +static const Property riscv_cps_properties[] = {
> +    DEFINE_PROP_UINT32("num-vp", RISCVCPSState, num_vp, 1),
> +    DEFINE_PROP_UINT32("num-hart", RISCVCPSState, num_hart, 1),
> +    DEFINE_PROP_UINT32("num-core", RISCVCPSState, num_core, 1),
> +    DEFINE_PROP_UINT64("gcr-base", RISCVCPSState, gcr_base, GCR_BASE_ADDR),
> +    DEFINE_PROP_STRING("cpu-type", RISCVCPSState, cpu_type),
> +};
> +
> +static void riscv_cps_class_init(ObjectClass *klass, const void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->realize = riscv_cps_realize;
> +    device_class_set_props(dc, riscv_cps_properties);
> +}
> +
> +static const TypeInfo riscv_cps_info = {
> +    .name = TYPE_RISCV_CPS,
> +    .parent = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(RISCVCPSState),
> +    .instance_init = riscv_cps_init,
> +    .class_init = riscv_cps_class_init,
> +};
> +
> +static void riscv_cps_register_types(void)
> +{
> +    type_register_static(&riscv_cps_info);
> +}
> +
> +type_init(riscv_cps_register_types)
> diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
> index 2a8d5b136c..9023b80087 100644
> --- a/hw/riscv/meson.build
> +++ b/hw/riscv/meson.build
> @@ -15,4 +15,6 @@ riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files(
>  riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-generic.c'))
>  riscv_ss.add(when: 'CONFIG_XIANGSHAN_KUNMINGHU', if_true: files('xiangshan_kmh.c'))
>
> +riscv_ss.add(when: 'CONFIG_RISCV_MIPS_CPS', if_true: files('cps.c'))
> +
>  hw_arch += {'riscv': riscv_ss}
> diff --git a/include/hw/riscv/cps.h b/include/hw/riscv/cps.h
> new file mode 100644
> index 0000000000..f33fd7ac86
> --- /dev/null
> +++ b/include/hw/riscv/cps.h
> @@ -0,0 +1,66 @@
> +/*
> + * Coherent Processing System emulation.
> + *
> + * Copyright (c) 2016 Imagination Technologies
> + *
> + * Copyright (c) 2025 MIPS
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + */
> +
> +#ifndef RISCV_CPS_H
> +#define RISCV_CPS_H
> +
> +#include "hw/core/sysbus.h"
> +#include "hw/misc/riscv_cmgcr.h"
> +#include "hw/misc/riscv_cpc.h"
> +#include "target/riscv/cpu.h"
> +#include "qom/object.h"
> +
> +#define TYPE_RISCV_CPS "riscv-cps"
> +OBJECT_DECLARE_SIMPLE_TYPE(RISCVCPSState, RISCV_CPS)
> +
> +/* The model supports up to 64 harts. */
> +#define MAX_HARTS 64
> +
> +/* The global CM base for the boston-aia model. */
> +#define GLOBAL_CM_BASE 0x16100000
> +/* The CM block is 512 KiB. */
> +#define CM_SIZE (1 << 19)
> +
> +/*
> + * The mhartid bits has cluster at bit 16, core at bit 4, and hart at
> + * bit 0.
> + */
> +
> +#define MHARTID_CLUSTER_SHIFT 16
> +#define MHARTID_CORE_SHIFT 4
> +#define MHARTID_HART_SHIFT 0
> +
> +#define APLIC_NUM_SOURCES 0x35 /* Arbitray maximum number of interrupts. */
> +#define APLIC_NUM_PRIO_BITS 3
> +#define AIA_PLIC_M_OFFSET 0x40000
> +#define AIA_PLIC_M_SIZE 0x8000
> +#define AIA_PLIC_S_OFFSET 0x60000
> +#define AIA_PLIC_S_SIZE 0x8000
> +#define AIA_CLINT_OFFSET 0x50000
> +
> +typedef struct RISCVCPSState {
> +    SysBusDevice parent_obj;
> +
> +    uint32_t num_vp;
> +    uint32_t num_hart;
> +    uint32_t num_core;
> +    uint64_t gcr_base;
> +    char *cpu_type;
> +
> +    MemoryRegion container;
> +    RISCVGCRState gcr;
> +    RISCVCPCState cpc;
> +
> +    DeviceState *aplic;
> +    CPUState **cpus;
> +} RISCVCPSState;
> +
> +#endif
> --
> 2.34.1
Re: [PATCH v16 09/12] hw/riscv: Add support for RISCV CPS
Posted by Djordje Todorovic 4 weeks ago
On 12. 1. 26. 06:42, Alistair Francis wrote:
> CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe.
>
>
> On Thu, Jan 8, 2026 at 11:41 PM Djordje Todorovic
> <Djordje.Todorovic@htecgroup.com> wrote:
>> Add support for the Coherent Processing System for RISC-V.
>> This enables SMP support for RISC-V boards that require
>> cache-coherent multiprocessor systems.
>>
>> Signed-off-by: Chao-ying Fu <cfu@mips.com>
>> Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com>
>> Acked-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
>> ---
>>   hw/misc/Kconfig        |   4 +
>>   hw/riscv/cps.c         | 196 +++++++++++++++++++++++++++++++++++++++++
>>   hw/riscv/meson.build   |   2 +
>>   include/hw/riscv/cps.h |  66 ++++++++++++++
>>   4 files changed, 268 insertions(+)
>>   create mode 100644 hw/riscv/cps.c
>>   create mode 100644 include/hw/riscv/cps.h
>>
>> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
>> index 38be72b141..4a22d68233 100644
>> --- a/hw/misc/Kconfig
>> +++ b/hw/misc/Kconfig
>> @@ -127,12 +127,16 @@ config RISCV_MIPS_CMGCR
>>   config RISCV_MIPS_CPC
>>       bool
>>
>> +config RISCV_MIPS_CPS
>> +    bool
>> +
>>   config MIPS_BOSTON_AIA
>>       bool
>>       default y
>>       depends on RISCV64
>>       select RISCV_MIPS_CMGCR
>>       select RISCV_MIPS_CPC
>> +    select RISCV_MIPS_CPS
>>
>>   config MPS2_FPGAIO
>>       bool
>> diff --git a/hw/riscv/cps.c b/hw/riscv/cps.c
>> new file mode 100644
>> index 0000000000..86172be5b3
>> --- /dev/null
>> +++ b/hw/riscv/cps.c
>> @@ -0,0 +1,196 @@
>> +/*
>> + * Coherent Processing System emulation.
>> + *
>> + * Copyright (c) 2016 Imagination Technologies
>> + *
>> + * Copyright (c) 2025 MIPS
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + *
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qapi/error.h"
>> +#include "qemu/module.h"
>> +#include "hw/riscv/cps.h"
>> +#include "hw/core/qdev-properties.h"
>> +#include "system/reset.h"
>> +#include "hw/intc/riscv_aclint.h"
>> +#include "hw/intc/riscv_aplic.h"
>> +#include "hw/intc/riscv_imsic.h"
>> +#include "hw/pci/msi.h"
>> +
>> +static void riscv_cps_init(Object *obj)
>> +{
>> +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
>> +    RISCVCPSState *s = RISCV_CPS(obj);
>> +
>> +    /*
>> +     * Cover entire address space as there do not seem to be any
>> +     * constraints for the base address of CPC .
>> +     */
>> +    memory_region_init(&s->container, obj, "mips-cps-container", UINT64_MAX);
>> +    sysbus_init_mmio(sbd, &s->container);
>> +}
>> +
>> +static void main_cpu_reset(void *opaque)
>> +{
>> +    CPUState *cs = opaque;
>> +
>> +    cpu_reset(cs);
>> +}
>> +
>> +static void riscv_cps_realize(DeviceState *dev, Error **errp)
>> +{
>> +    RISCVCPSState *s = RISCV_CPS(dev);
>> +    RISCVCPU *cpu;
>> +    int i;
>> +
>> +    /* Validate num_vp */
>> +    if (s->num_vp == 0) {
>> +        error_setg(errp, "num-vp must be at least 1");
>> +        return;
>> +    }
>> +    if (s->num_vp > MAX_HARTS) {
>> +        error_setg(errp, "num-vp cannot exceed %d", MAX_HARTS);
>> +        return;
>> +    }
>> +
>> +    /* Allocate CPU array */
>> +    s->cpus = g_new0(CPUState *, s->num_vp);
>> +
>> +    /* Set up cpu_index and mhartid for avaiable CPUs. */
>> +    int harts_in_cluster = s->num_hart * s->num_core;
>> +    int num_of_clusters = s->num_vp / harts_in_cluster;
>> +    for (i = 0; i < s->num_vp; i++) {
>> +        cpu = RISCV_CPU(object_new(s->cpu_type));
>> +
>> +        /* All VPs are halted on reset. Leave powering up to CPC. */
>> +        object_property_set_bool(OBJECT(cpu), "start-powered-off", true,
>> +                                 &error_abort);
>> +
>> +        if (!qdev_realize_and_unref(DEVICE(cpu), NULL, errp)) {
>> +            return;
>> +        }
>> +
>> +        /* Store CPU in array */
>> +        s->cpus[i] = CPU(cpu);
>> +
>> +        /* Set up mhartid */
>> +        int cluster_id = i / harts_in_cluster;
>> +        int hart_id = (i % harts_in_cluster) % s->num_hart;
>> +        int core_id = (i % harts_in_cluster) / s->num_hart;
>> +        int mhartid = (cluster_id << MHARTID_CLUSTER_SHIFT) +
>> +                      (core_id << MHARTID_CORE_SHIFT) +
>> +                      (hart_id << MHARTID_HART_SHIFT);
>> +        cpu->env.mhartid = mhartid;
>> +        qemu_register_reset(main_cpu_reset, s->cpus[i]);
>> +    }
>> +
>> +    /* Cluster Power Controller */
>> +    object_initialize_child(OBJECT(dev), "cpc", &s->cpc, TYPE_RISCV_CPC);
>> +    object_property_set_uint(OBJECT(&s->cpc), "cluster-id", 0,
>> +                            &error_abort);
>> +    object_property_set_uint(OBJECT(&s->cpc), "num-vp", s->num_vp,
>> +                            &error_abort);
>> +    object_property_set_uint(OBJECT(&s->cpc), "num-hart", s->num_hart,
>> +                            &error_abort);
>> +    object_property_set_uint(OBJECT(&s->cpc), "num-core", s->num_core,
>> +                            &error_abort);
>> +
>> +    /* Pass CPUs to CPC using link properties */
>> +    for (i = 0; i < s->num_vp; i++) {
>> +        char *propname = g_strdup_printf("cpu[%d]", i);
>> +        object_property_set_link(OBJECT(&s->cpc), propname,
>> +                                OBJECT(s->cpus[i]), &error_abort);
>> +        g_free(propname);
>> +    }
>> +
>> +    if (!sysbus_realize(SYS_BUS_DEVICE(&s->cpc), errp)) {
>> +        return;
>> +    }
>> +
>> +    memory_region_add_subregion(&s->container, 0,
>> +                            sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpc), 0));
>> +
>> +    /* Global Configuration Registers */
>> +    object_initialize_child(OBJECT(dev), "gcr", &s->gcr, TYPE_RISCV_GCR);
>> +    object_property_set_uint(OBJECT(&s->gcr), "cluster-id", 0,
>> +                            &error_abort);
>> +    object_property_set_uint(OBJECT(&s->gcr), "num-vp", s->num_vp,
>> +                            &error_abort);
>> +    object_property_set_int(OBJECT(&s->gcr), "gcr-rev", 0xa00,
>> +                            &error_abort);
>> +    object_property_set_int(OBJECT(&s->gcr), "gcr-base", s->gcr_base,
>> +                            &error_abort);
>> +    object_property_set_link(OBJECT(&s->gcr), "cpc", OBJECT(&s->cpc.mr),
>> +                             &error_abort);
>> +    if (!sysbus_realize(SYS_BUS_DEVICE(&s->gcr), errp)) {
>> +        return;
>> +    }
>> +
>> +    memory_region_add_subregion(&s->container, s->gcr_base,
>> +                            sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gcr), 0));
>> +
>> +    for (i = 0; i < num_of_clusters; i++) {
>> +        uint64_t cm_base = GLOBAL_CM_BASE + (CM_SIZE * i);
> This causes a Coverity issue
>
> *** CID 1644076:         Integer handling issues  (OVERFLOW_BEFORE_WIDEN)
> /builds/qemu-project/qemu/hw/riscv/cps.c: 136             in riscv_cps_realize()
> 130         }
> 131
> 132         memory_region_add_subregion(&s->container, s->gcr_base,
> 133
> sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gcr), 0));
> 134
> 135         for (i = 0; i < num_of_clusters; i++) {
>>>>      CID 1644076:         Integer handling issues  (OVERFLOW_BEFORE_WIDEN)
>>>>      Potentially overflowing expression "524288 * i" with type "int" (32 bits, signed) is evaluated using 32-bit arithmetic, and then used in a context that expects an expression of type "uint64_t" (64 bits, unsigned).
> 136             uint64_t cm_base = GLOBAL_CM_BASE + (CM_SIZE * i);
> 137             uint32_t hartid_base = i << MHARTID_CLUSTER_SHIFT;
> 138             s->aplic = riscv_aplic_create(cm_base + AIA_PLIC_M_OFFSET,
> 139                                           AIA_PLIC_M_SIZE,
> 140                                           hartid_base, /* hartid_base */
> 141                                           MAX_HARTS, /* num_harts */
>
>
> Can you send a patch to fix this issue and link to CID 1644076?
>
> Alistair
>
Hi Alistair,

Thanks for merging it.

Yes, I have just sent the two patches that fix those problems.

Best,
Djordje


>> +        uint32_t hartid_base = i << MHARTID_CLUSTER_SHIFT;
>> +        s->aplic = riscv_aplic_create(cm_base + AIA_PLIC_M_OFFSET,
>> +                                      AIA_PLIC_M_SIZE,
>> +                                      hartid_base, /* hartid_base */
>> +                                      MAX_HARTS, /* num_harts */
>> +                                      APLIC_NUM_SOURCES,
>> +                                      APLIC_NUM_PRIO_BITS,
>> +                                      false, true, NULL);
>> +        riscv_aplic_create(cm_base + AIA_PLIC_S_OFFSET,
>> +                           AIA_PLIC_S_SIZE,
>> +                           hartid_base, /* hartid_base */
>> +                           MAX_HARTS, /* num_harts */
>> +                           APLIC_NUM_SOURCES,
>> +                           APLIC_NUM_PRIO_BITS,
>> +                           false, false, s->aplic);
>> +        /* PLIC changes msi_nonbroken to ture. We revert the change. */
>> +        msi_nonbroken = false;
>> +        riscv_aclint_swi_create(cm_base + AIA_CLINT_OFFSET,
>> +                                hartid_base, MAX_HARTS, false);
>> +        riscv_aclint_mtimer_create(cm_base + AIA_CLINT_OFFSET +
>> +                                   RISCV_ACLINT_SWI_SIZE,
>> +                                   RISCV_ACLINT_DEFAULT_MTIMER_SIZE,
>> +                                   hartid_base,
>> +                                   MAX_HARTS,
>> +                                   RISCV_ACLINT_DEFAULT_MTIMECMP,
>> +                                   RISCV_ACLINT_DEFAULT_MTIME,
>> +                                   RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false);
>> +    }
>> +}
>> +
>> +static const Property riscv_cps_properties[] = {
>> +    DEFINE_PROP_UINT32("num-vp", RISCVCPSState, num_vp, 1),
>> +    DEFINE_PROP_UINT32("num-hart", RISCVCPSState, num_hart, 1),
>> +    DEFINE_PROP_UINT32("num-core", RISCVCPSState, num_core, 1),
>> +    DEFINE_PROP_UINT64("gcr-base", RISCVCPSState, gcr_base, GCR_BASE_ADDR),
>> +    DEFINE_PROP_STRING("cpu-type", RISCVCPSState, cpu_type),
>> +};
>> +
>> +static void riscv_cps_class_init(ObjectClass *klass, const void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    dc->realize = riscv_cps_realize;
>> +    device_class_set_props(dc, riscv_cps_properties);
>> +}
>> +
>> +static const TypeInfo riscv_cps_info = {
>> +    .name = TYPE_RISCV_CPS,
>> +    .parent = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size = sizeof(RISCVCPSState),
>> +    .instance_init = riscv_cps_init,
>> +    .class_init = riscv_cps_class_init,
>> +};
>> +
>> +static void riscv_cps_register_types(void)
>> +{
>> +    type_register_static(&riscv_cps_info);
>> +}
>> +
>> +type_init(riscv_cps_register_types)
>> diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
>> index 2a8d5b136c..9023b80087 100644
>> --- a/hw/riscv/meson.build
>> +++ b/hw/riscv/meson.build
>> @@ -15,4 +15,6 @@ riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files(
>>   riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-generic.c'))
>>   riscv_ss.add(when: 'CONFIG_XIANGSHAN_KUNMINGHU', if_true: files('xiangshan_kmh.c'))
>>
>> +riscv_ss.add(when: 'CONFIG_RISCV_MIPS_CPS', if_true: files('cps.c'))
>> +
>>   hw_arch += {'riscv': riscv_ss}
>> diff --git a/include/hw/riscv/cps.h b/include/hw/riscv/cps.h
>> new file mode 100644
>> index 0000000000..f33fd7ac86
>> --- /dev/null
>> +++ b/include/hw/riscv/cps.h
>> @@ -0,0 +1,66 @@
>> +/*
>> + * Coherent Processing System emulation.
>> + *
>> + * Copyright (c) 2016 Imagination Technologies
>> + *
>> + * Copyright (c) 2025 MIPS
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + *
>> + */
>> +
>> +#ifndef RISCV_CPS_H
>> +#define RISCV_CPS_H
>> +
>> +#include "hw/core/sysbus.h"
>> +#include "hw/misc/riscv_cmgcr.h"
>> +#include "hw/misc/riscv_cpc.h"
>> +#include "target/riscv/cpu.h"
>> +#include "qom/object.h"
>> +
>> +#define TYPE_RISCV_CPS "riscv-cps"
>> +OBJECT_DECLARE_SIMPLE_TYPE(RISCVCPSState, RISCV_CPS)
>> +
>> +/* The model supports up to 64 harts. */
>> +#define MAX_HARTS 64
>> +
>> +/* The global CM base for the boston-aia model. */
>> +#define GLOBAL_CM_BASE 0x16100000
>> +/* The CM block is 512 KiB. */
>> +#define CM_SIZE (1 << 19)
>> +
>> +/*
>> + * The mhartid bits has cluster at bit 16, core at bit 4, and hart at
>> + * bit 0.
>> + */
>> +
>> +#define MHARTID_CLUSTER_SHIFT 16
>> +#define MHARTID_CORE_SHIFT 4
>> +#define MHARTID_HART_SHIFT 0
>> +
>> +#define APLIC_NUM_SOURCES 0x35 /* Arbitray maximum number of interrupts. */
>> +#define APLIC_NUM_PRIO_BITS 3
>> +#define AIA_PLIC_M_OFFSET 0x40000
>> +#define AIA_PLIC_M_SIZE 0x8000
>> +#define AIA_PLIC_S_OFFSET 0x60000
>> +#define AIA_PLIC_S_SIZE 0x8000
>> +#define AIA_CLINT_OFFSET 0x50000
>> +
>> +typedef struct RISCVCPSState {
>> +    SysBusDevice parent_obj;
>> +
>> +    uint32_t num_vp;
>> +    uint32_t num_hart;
>> +    uint32_t num_core;
>> +    uint64_t gcr_base;
>> +    char *cpu_type;
>> +
>> +    MemoryRegion container;
>> +    RISCVGCRState gcr;
>> +    RISCVCPCState cpc;
>> +
>> +    DeviceState *aplic;
>> +    CPUState **cpus;
>> +} RISCVCPSState;
>> +
>> +#endif
>> --
>> 2.34.1