[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] hw/riscv: add CLIC into virt machine
From: |
Ian Brockbank |
Subject: |
[PATCH] hw/riscv: add CLIC into virt machine |
Date: |
Tue, 13 Aug 2024 15:54:11 +0100 |
Signed-off-by: Ian Brockbank<ian.brockbank@cirrus.com>
---
hw/riscv/virt.c | 235 +++++++++++++++++++++++++++++++++++++++-
include/hw/riscv/virt.h | 35 ++++++
2 files changed, 267 insertions(+), 3 deletions(-)
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 9981e0f6c9..3429733c7c 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -1,4 +1,4 @@
-/*
+ /*
* QEMU RISC-V VirtIO Board
*
* Copyright (c) 2017 SiFive, Inc.
@@ -39,6 +39,7 @@
#include "hw/firmware/smbios.h"
#include "hw/intc/riscv_aclint.h"
#include "hw/intc/riscv_aplic.h"
+#include "hw/intc/riscv_clic.h"
#include "hw/intc/sifive_plic.h"
#include "hw/misc/sifive_test.h"
#include "hw/platform-bus.h"
@@ -72,6 +73,7 @@ static const MemMapEntry virt_memmap[] = {
[VIRT_MROM] = { 0x1000, 0xf000 },
[VIRT_TEST] = { 0x100000, 0x1000 },
[VIRT_RTC] = { 0x101000, 0x1000 },
+ [VIRT_CLIC] = { 0x2000000, VIRT_CLIC_MAX_SIZE(VIRT_CPUS_MAX)},
[VIRT_CLINT] = { 0x2000000, 0x10000 },
[VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 },
[VIRT_PCIE_PIO] = { 0x3000000, 0x10000 },
@@ -424,6 +426,37 @@ static void create_fdt_socket_aclint(RISCVVirtState *s,
}
}
+static void create_fdt_socket_clic(RISCVVirtState *s,
+ const MemMapEntry *memmap, int socket)
+{
+ g_autofree char *clic_name = NULL;
+ g_autofree uint32_t *clic_cells = NULL;
+ unsigned long mclicbase;
+ MachineState *ms = MACHINE(s);
+ static const char * const clic_compat[1] = {
+ "riscv,clic-0.9"
+ };
+
+ /*
+ * The spec doesn't define a memory layout, other than to say that each
+ * CLIC should be on a 4KiB boundary if memory-mapped.
+ * This implementation makes all the CLICs contiguous, in the order M, S,
U,
+ * and assumes the worst-case size.
+ * TODO: create entries for each CLIC on the system.
+ */
+ mclicbase = memmap[VIRT_CLIC].base;
+ clic_name = g_strdup_printf("/soc/clic@%lx", mclicbase);
+ qemu_fdt_add_subnode(ms->fdt, clic_name);
+ qemu_fdt_setprop_string_array(ms->fdt, clic_name, "compatible",
+ (char **)&clic_compat,
+ ARRAY_SIZE(clic_compat));
+ qemu_fdt_setprop_cells(ms->fdt, clic_name, "regs",
+ 0x0, mclicbase, 0x0, memmap[VIRT_CLIC].size);
+ qemu_fdt_setprop_cell(ms->fdt, clic_name, "riscv,num-sources",
+ VIRT_IRQCHIP_NUM_SOURCES);
+ riscv_socket_fdt_write_id(ms, clic_name, socket);
+}
+
static void create_fdt_socket_plic(RISCVVirtState *s,
const MemMapEntry *memmap, int socket,
uint32_t *phandle, uint32_t *intc_phandles,
@@ -760,7 +793,10 @@ static void create_fdt_sockets(RISCVVirtState *s, const
MemMapEntry *memmap,
create_fdt_socket_memory(s, memmap, socket);
- if (virt_aclint_allowed() && s->have_aclint) {
+
+ if (s->have_clic) {
+ create_fdt_socket_clic(s, memmap, socket);
+ } else if (virt_aclint_allowed() && s->have_aclint) {
create_fdt_socket_aclint(s, memmap, socket,
&intc_phandles[phandle_pos]);
} else if (tcg_enabled()) {
@@ -1207,6 +1243,37 @@ static DeviceState *virt_create_plic(const MemMapEntry
*memmap, int socket,
return ret;
}
+static DeviceState *virt_create_clic(RISCVVirtState *s, uint64_t clic_base,
+ int hartid)
+{
+ DeviceState *ret;
+ uint32_t block_size = VIRT_CLIC_HART_SIZE(s->clic_prv_s, s->clic_prv_u);
+ uint64_t mclicbase = clic_base + hartid * block_size;
+ uint64_t sclicbase = 0;
+ uint64_t uclicbase = 0;
+
+ /*
+ * The spec doesn't define a memory layout, other than to say that each
+ * CLIC should be on a 4KiB boundary if memory-mapped.
+ * This implementation makes all the CLICs contiguous, in the order M, S,
U.
+ */
+ if (s->clic_prv_s) {
+ sclicbase = mclicbase + VIRT_CLIC_BLOCK_SIZE;
+ }
+ if (s->clic_prv_u) {
+ uclicbase = mclicbase + VIRT_CLIC_BLOCK_SIZE;
+ if (s->clic_prv_s) {
+ uclicbase += VIRT_CLIC_BLOCK_SIZE;
+ }
+ }
+ ret = riscv_clic_create(mclicbase, sclicbase, uclicbase,
+ hartid, VIRT_IRQCHIP_NUM_SOURCES,
+ s->clic_intctlbits,
+ s->clic_version);
+
+ return ret;
+}
+
static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests,
const MemMapEntry *memmap, int socket,
int base_hartid, int hart_count)
@@ -1506,7 +1573,7 @@ static void virt_machine_init(MachineState *machine)
i * memmap[VIRT_ACLINT_SSWI].size,
base_hartid, hart_count, true);
}
- } else if (tcg_enabled()) {
+ } else if (tcg_enabled() && !s->have_clic) {
/* Per-socket SiFive CLINT */
riscv_aclint_swi_create(
memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size,
@@ -1528,6 +1595,12 @@ static void virt_machine_init(MachineState *machine)
hart_count);
}
+ /* CLIC if present - needs to come after PLIC */
+ if (s->have_clic) {
+ s->irqchip[i] = virt_create_clic(s, memmap[VIRT_CLIC].base,
+ base_hartid);
+ }
+
/* Try to use different IRQCHIP instance based device type */
if (i == 0) {
mmio_irqchip = s->irqchip[i];
@@ -1711,6 +1784,134 @@ static void virt_set_aclint(Object *obj, bool value,
Error **errp)
s->have_aclint = value;
}
+static bool virt_get_clic(Object *obj, Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+
+ return s->have_clic;
+}
+
+static void virt_set_clic(Object *obj, bool value, Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+
+ s->have_clic = value;
+}
+
+static char *virt_get_clic_mode(Object *obj, Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+ const char *val;
+
+ if (s->have_clic) {
+ if (s->clic_prv_s && s->clic_prv_u) {
+ val = "msu";
+ } else if (s->clic_prv_s) {
+ val = "ms";
+ } else if (s->clic_prv_u) {
+ val = "mu";
+ } else {
+ val = "m";
+ }
+ } else {
+ val = "none";
+ }
+
+ return g_strdup(val);
+}
+
+static void virt_set_clic_mode(Object *obj, const char *val, Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+ const char *c = val;
+
+ s->have_clic = true;
+
+ s->clic_prv_s = false;
+ s->clic_prv_u = false;
+
+ /* The mode is encoded with m = machine, s = PRV_S, u = PRV_U */
+ while (*c && !*errp) {
+ switch (*c) {
+ case 'm':
+ case 'M':
+ /* Machine mode is implicit and always enabled */
+ break;
+ case 's':
+ case 'S':
+ s->clic_prv_s = true;
+ break;
+ case 'u':
+ case 'U':
+ s->clic_prv_u = true;
+ break;
+ default:
+ error_setg(errp, "Invalid CLIC interrupt mode %c in %s", *c, val);
+ error_append_hint(errp, "Valid values are m, s and u.\n");
+ break;
+ }
+ ++c;
+ }
+}
+
+static char *virt_get_clic_version(Object *obj, Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+ return g_strdup(s->clic_version);
+}
+
+static void virt_set_clic_version(Object *obj, const char *val, Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+ g_autofree char **tokens = g_strsplit(val, "-", 2);
+
+ if (0 != strcmp(tokens[0], "v0.9")) {
+ error_setg(errp, "Invalid CLIC version %s", tokens[0]);
+ error_append_hint(errp,
+ "Only v0.9 is supported. The 'v' is required.\n");
+ return;
+ }
+
+ if (tokens[1]) {
+ if (0 != strcmp(tokens[1], "jmp")) {
+ error_setg(errp, "Invalid CLIC version suffix -%s", tokens[1]);
+ error_append_hint(errp, "Only -jmp is supported.\n");
+ return;
+ }
+ }
+
+ s->clic_version = g_strdup(val);
+ s->have_clic = true;
+}
+
+static void virt_get_clicintctlbits(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+ uint8_t value = s->clic_intctlbits;
+
+ visit_type_uint8(v, name, &value, errp);
+}
+
+static void virt_set_clicintctlbits(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+ uint8_t value;
+
+ if (!visit_type_uint8(v, name, &value, errp)) {
+ return;
+ }
+ if (value > MAX_CLIC_INTCTLBITS) {
+ error_setg(errp, "CLIC intctlbits must be <= %d; was %d",
+ MAX_CLIC_INTCTLBITS, value);
+ }
+
+ s->clic_intctlbits = value;
+}
+
bool virt_is_acpi_enabled(RISCVVirtState *s)
{
return s->acpi != ON_OFF_AUTO_OFF;
@@ -1768,6 +1969,7 @@ static void virt_machine_class_init(ObjectClass *oc, void
*data)
{
MachineClass *mc = MACHINE_CLASS(oc);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
+ ObjectProperty *op = NULL;
mc->desc = "RISC-V VirtIO board";
mc->init = virt_machine_init;
@@ -1800,6 +2002,33 @@ static void virt_machine_class_init(ObjectClass *oc,
void *data)
"enable/disable emulating "
"ACLINT devices");
+ object_class_property_add_bool(oc, "clic", virt_get_clic,
+ virt_set_clic);
+ object_class_property_set_description(oc, "clic",
+ "Set on/off to enable/disable "
+ "emulating CLIC devices");
+ object_class_property_add_str(oc, "clic-mode", virt_get_clic_mode,
+ virt_set_clic_mode);
+ object_class_property_set_description(oc, "clic-mode",
+ "Specify supported CLIC modes: "
+ "m = machine (always enabled), "
+ "s = PRV_S, u = PRV_U");
+ op = object_class_property_add_str(oc, "clic-version",
+ virt_get_clic_version,
+ virt_set_clic_version);
+ object_property_set_default_str(op, VIRT_CLIC_VERSION);
+ object_class_property_set_description(oc, "clic-version",
+ "Set the CLIC version to use. "
+ "Valid values are v0.9 and
v0.9-jmp");
+ op = object_class_property_add(oc, "clic-intctlbits", "uint8",
+ virt_get_clicintctlbits,
+ virt_set_clicintctlbits,
+ NULL, NULL);
+ object_property_set_default_uint(op, 8);
+ object_class_property_set_description(oc, "clic-intctlbits",
+ "The number of intctl bits used in "
+ "this CLIC implementation");
+
object_class_property_add_str(oc, "aia", virt_get_aia,
virt_set_aia);
object_class_property_set_description(oc, "aia",
diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
index c0dc41ff9a..d0a245608c 100644
--- a/include/hw/riscv/virt.h
+++ b/include/hw/riscv/virt.h
@@ -55,6 +55,11 @@ struct RISCVVirtState {
int fdt_size;
bool have_aclint;
+ bool have_clic;
+ bool clic_prv_s;
+ bool clic_prv_u;
+ uint8_t clic_intctlbits;
+ char *clic_version;
RISCVVirtAIAType aia_type;
int aia_guests;
char *oem_id;
@@ -71,6 +76,7 @@ enum {
VIRT_RTC,
VIRT_CLINT,
VIRT_ACLINT_SSWI,
+ VIRT_CLIC,
VIRT_PLIC,
VIRT_APLIC_M,
VIRT_APLIC_S,
@@ -113,6 +119,35 @@ enum {
#define VIRT_PLIC_SIZE(__num_context) \
(VIRT_PLIC_CONTEXT_BASE + (__num_context) * VIRT_PLIC_CONTEXT_STRIDE)
+#define VIRT_CLIC_INTCLTBITS 3
+#define VIRT_CLIC_VERSION "v0.9"
+#define VIRT_CLIC_MAX_IRQS 0x1000
+#define VIRT_CLIC_CONTEXT_BASE 0x1000
+#define VIRT_CLIC_CONTEXT_COUNT(_prv_s, _prv_u) \
+ (1 + ((_prv_s) ? 1 : 0) + ((_prv_u) ? 1 : 0))
+#define VIRT_CLIC_FULL_CONTEXT_COUNT VIRT_CLIC_CONTEXT_COUNT(1, 1)
+#define VIRT_CLIC_ALIGN_BITS 12
+#define VIRT_CLIC_ALIGN_MASK ((1U << VIRT_CLIC_ALIGN_BITS) - 1)
+/* Round up to next 4KiB alignment boundary */
+#define VIRT_CLIC_ALIGN(_base_addr) \
+ (((_base_addr) + VIRT_CLIC_ALIGN_MASK) & VIRT_CLIC_ALIGN_MASK)
+#define VIRT_CLIC_INT_SIZE(_irq_count) ((_irq_count) * 4)
+/*
+ * The spec doesn't define a memory layout, other than to say that each
+ * CLIC should be on a 4KiB boundary if memory-mapped.
+ * This implementation makes all the CLICs contiguous, in the order M, S, U,
+ * and assumes the worst-case size.
+ */
+#define VIRT_CLIC_BLOCK_SIZE \
+ (VIRT_CLIC_CONTEXT_BASE + VIRT_CLIC_INT_SIZE(VIRT_CLIC_MAX_IRQS))
+#define VIRT_CLIC_HART_SIZE(_prv_s, _prv_u) \
+ (VIRT_CLIC_CONTEXT_COUNT(_prv_s, _prv_u) * VIRT_CLIC_BLOCK_SIZE)
+#define VIRT_CLIC_SIZE(_hart_count, _prv_s, _prv_u) \
+ ((_hart_count) * VIRT_CLIC_HART_SIZE(_prv_s, _prv_u))
+#define VIRT_CLIC_MAX_HART_SIZE VIRT_CLIC_HART_SIZE(1, 1)
+#define VIRT_CLIC_MAX_SIZE(_hart_count) \
+ ((_hart_count) * VIRT_CLIC_MAX_HART_SIZE)
+
#define FDT_PCI_ADDR_CELLS 3
#define FDT_PCI_INT_CELLS 1
#define FDT_PLIC_ADDR_CELLS 0
--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential
information that is intended solely for the person(s) to whom it is addressed.
If you are not an intended recipient you must not: read; copy; distribute;
discuss; take any action in or make any reliance upon the contents of this
message; nor open or read any attachment. If you have received this message in
error, please notify us as soon as possible on the following telephone number
and destroy this message including any attachments. Thank you. Cirrus Logic
International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are
companies registered in Scotland, with registered numbers SC089839 and SC495735
respectively. Our registered office is at 7B Nightingale Way, Quartermile,
Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
- [PATCH 00/11] RISC-V: support CLIC v0.9 specification, Ian Brockbank, 2024/08/13
- [PATCH] target/riscv: Update CSR xip in CLIC mode, Ian Brockbank, 2024/08/13
- [PATCH] tests: add riscv clic qtest case and a function in qtest, Ian Brockbank, 2024/08/13
- [PATCH] target/riscv: Update interrupt return in CLIC mode, Ian Brockbank, 2024/08/13
- [PATCH] hw/riscv: add CLIC into virt machine,
Ian Brockbank <=
- [PATCH] target/riscv: Update CSR xie in CLIC mode, Ian Brockbank, 2024/08/13
- [PATCH] hw/intc: Add CLIC device, Ian Brockbank, 2024/08/13
- [PATCH] target/riscv: Update interrupt handling in CLIC mode, Ian Brockbank, 2024/08/13
- [PATCH] target/riscv: Update CSR xnxti in CLIC mode, Ian Brockbank, 2024/08/13
- [PATCH] target/riscv: Update CSR xintthresh in CLIC mode, Ian Brockbank, 2024/08/13
- [PATCH] target/riscv: Update CSR xtvec in CLIC mode, Ian Brockbank, 2024/08/13
- [PATCH] target/riscv: Add CLIC CSR mintstatus, Ian Brockbank, 2024/08/13