This device determines the target IOPMP device for forwarding information based on: * Address: For parallel IOPMP devices * Stage: For cascading IOPMP devices
Signed-off-by: Ethan Chen <etha...@andestech.com> --- hw/misc/meson.build | 1 + hw/misc/riscv_iopmp_dispatcher.c | 136 +++++++++++++++++++++++ include/hw/misc/riscv_iopmp_dispatcher.h | 61 ++++++++++ 3 files changed, 198 insertions(+) create mode 100644 hw/misc/riscv_iopmp_dispatcher.c create mode 100644 include/hw/misc/riscv_iopmp_dispatcher.h diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 88f2bb6b88..497f83637f 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -35,6 +35,7 @@ system_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true: files('sifive_e_aon.c')) system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c')) system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c')) specific_ss.add(when: 'CONFIG_RISCV_IOPMP', if_true: files('riscv_iopmp.c')) +specific_ss.add(when: 'CONFIG_RISCV_IOPMP', if_true: files('riscv_iopmp_dispatcher.c')) subdir('macio') diff --git a/hw/misc/riscv_iopmp_dispatcher.c b/hw/misc/riscv_iopmp_dispatcher.c new file mode 100644 index 0000000000..ba6eaeb396 --- /dev/null +++ b/hw/misc/riscv_iopmp_dispatcher.c @@ -0,0 +1,136 @@ +/* + * QEMU RISC-V IOPMP dispatcher + * + * Receives transaction information from the requestor and forwards it to the + * corresponding IOPMP device. + * + * Copyright (c) 2023-2025 Andes Tech. Corp. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "trace.h" +#include "exec/exec-all.h" +#include "exec/address-spaces.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "hw/misc/riscv_iopmp_dispatcher.h" +#include "memory.h" +#include "hw/irq.h" +#include "hw/misc/riscv_iopmp_txn_info.h" + +static void riscv_iopmp_dispatcher_realize(DeviceState *dev, Error **errp) +{ + int i; + RISCVIOPMPDispState *s = RISCV_IOPMP_DISP(dev); + + s->SinkMemMap = g_new(SinkMemMapEntry *, s->stage_num); + for (i = 0; i < s->stage_num; i++) { + s->SinkMemMap[i] = g_new(SinkMemMapEntry, s->target_num); + } + + object_initialize_child(OBJECT(s), "iopmp_dispatcher_txn_info", + &s->txn_info_sink, + TYPE_RISCV_IOPMP_DISP_SS); +} + +static Property iopmp_dispatcher_properties[] = { + DEFINE_PROP_UINT32("stage-num", RISCVIOPMPDispState, stage_num, 2), + DEFINE_PROP_UINT32("target-num", RISCVIOPMPDispState, target_num, 10), +}; + +static void riscv_iopmp_dispatcher_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + device_class_set_props(dc, iopmp_dispatcher_properties); + dc->realize = riscv_iopmp_dispatcher_realize; +} + +static const TypeInfo riscv_iopmp_dispatcher_info = { + .name = TYPE_RISCV_IOPMP_DISP, + .parent = TYPE_DEVICE, + .instance_size = sizeof(RISCVIOPMPDispState), + .class_init = riscv_iopmp_dispatcher_class_init, +}; + +static size_t dispatcher_txn_info_push(StreamSink *txn_info_sink, + unsigned char *buf, + size_t len, bool eop) +{ + uint64_t addr; + uint32_t stage; + int i, j; + riscv_iopmp_disp_ss *ss = + RISCV_IOPMP_DISP_SS(txn_info_sink); + RISCVIOPMPDispState *s = RISCV_IOPMP_DISP(container_of(ss, + RISCVIOPMPDispState, txn_info_sink)); + riscv_iopmp_txn_info signal; + memcpy(&signal, buf, len); + addr = signal.start_addr; + stage = signal.stage; + for (i = stage; i < s->stage_num; i++) { + for (j = 0; j < s->target_num; j++) { + if (s->SinkMemMap[i][j].map.base <= addr && + addr < s->SinkMemMap[i][j].map.base + + s->SinkMemMap[i][j].map.size) { + return stream_push(s->SinkMemMap[i][j].sink, buf, len, eop); + } + } + } + /* Always pass if target is not protected by IOPMP*/ + return 1; +} + +static void riscv_iopmp_disp_ss_class_init( + ObjectClass *klass, void *data) +{ + StreamSinkClass *ssc = STREAM_SINK_CLASS(klass); + ssc->push = dispatcher_txn_info_push; +} + +static const TypeInfo riscv_iopmp_disp_ss_info = { + .name = TYPE_RISCV_IOPMP_DISP_SS, + .parent = TYPE_OBJECT, + .instance_size = sizeof(riscv_iopmp_disp_ss), + .class_init = riscv_iopmp_disp_ss_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_STREAM_SINK }, + { } + }, +}; + +void iopmp_dispatcher_add_target(DeviceState *dev, StreamSink *sink, + uint64_t base, uint64_t size, uint32_t stage, uint32_t id) +{ + RISCVIOPMPDispState *s = RISCV_IOPMP_DISP(dev); + if (stage < s->stage_num && id < s->target_num) { + s->SinkMemMap[stage][id].map.base = base; + s->SinkMemMap[stage][id].map.size = size; + s->SinkMemMap[stage][id].sink = sink; + } +} + +static void +iopmp_dispatcher_register_types(void) +{ + type_register_static(&riscv_iopmp_dispatcher_info); + type_register_static(&riscv_iopmp_disp_ss_info); +} + +type_init(iopmp_dispatcher_register_types); + diff --git a/include/hw/misc/riscv_iopmp_dispatcher.h b/include/hw/misc/riscv_iopmp_dispatcher.h new file mode 100644 index 0000000000..bbaa76507b --- /dev/null +++ b/include/hw/misc/riscv_iopmp_dispatcher.h @@ -0,0 +1,61 @@ +/* + * QEMU RISC-V IOPMP dispatcher + * + * Receives transaction information from the requestor and forwards it to the + * corresponding IOPMP device. + * + * Copyright (c) 2023-2024 Andes Tech. Corp. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef RISCV_IOPMP_DISPATCHER_H +#define RISCV_IOPMP_DISPATCHER_H + +#include "hw/sysbus.h" +#include "qemu/typedefs.h" +#include "memory.h" +#include "hw/stream.h" +#include "hw/misc/riscv_iopmp_txn_info.h" +#include "exec/hwaddr.h" + +#define TYPE_RISCV_IOPMP_DISP "riscv-iopmp-dispatcher" +OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOPMPDispState, RISCV_IOPMP_DISP) + +#define TYPE_RISCV_IOPMP_DISP_SS "riscv-iopmp-dispatcher-streamsink" +OBJECT_DECLARE_SIMPLE_TYPE(riscv_iopmp_disp_ss, RISCV_IOPMP_DISP_SS) + +typedef struct riscv_iopmp_disp_ss { + Object parent; +} riscv_iopmp_disp_ss; + +typedef struct SinkMemMapEntry { + StreamSink *sink; + MemMapEntry map; +} SinkMemMapEntry; + +typedef struct RISCVIOPMPDispState { + SysBusDevice parent_obj; + riscv_iopmp_disp_ss txn_info_sink; + SinkMemMapEntry **SinkMemMap; + /* The maximum number of cascading stages of IOPMP */ + uint32_t stage_num; + /* The maximum number of parallel IOPMP devices within a single stage. */ + uint32_t target_num; +} RISCVIOPMPDispState; + +void iopmp_dispatcher_add_target(DeviceState *dev, StreamSink *sink, + uint64_t base, uint64_t size, uint32_t stage, uint32_t id); +#endif -- 2.34.1