* [PATCH v3 0/4] Support RISC-V IOPMP @ 2023-11-14 9:47 Ethan Chen via 2023-11-14 9:47 ` [PATCH v3 1/4] hw/core: Add config stream Ethan Chen via ` (3 more replies) 0 siblings, 4 replies; 14+ messages in thread From: Ethan Chen via @ 2023-11-14 9:47 UTC (permalink / raw) To: qemu-devel Cc: peter.maydell, edgar.iglesias, richard.henderson, pbonzini, palmer, alistair.francis, in.meng, liweiwei, dbarboza, hiwei_liu, qemu-riscv, peterx, david, Ethan Chen This series implements IOPMP specification v1.0.0-draft4 rapid-k model. When IOPMP is enabled, a DMA device ATCDMAC300 is added to RISC-V virt platform. This DMA devce is connected to the IOPMP and has the functionalities required by IOPMP, including: - Support specify source-id (SID) - Support asynchronous I/O to handle stall transcations IOPMP takes a transaction which partially match an entry as a partially hit error. The transaction size is depending on source device, destination device and bus. Source device can send transaction information to IOPMP StreamSink. IOPMP will check partially hit with transaction information. If source device does not send transaction information, IOPMP will skip partially hit check. Changes for v3: - Remove iopmp_start_addr and iopmp_end_addr in MemTxAttrs - IOPMP: Get transaction_info from StreamSink instead of MemTxAttrs - ATCDMAC300: Convert ATCDMAC burst to AXI burst - ATCDMAC300: Send transaction_info to IOPMP StreamSink Ethan Chen (4): hw/core: Add config stream Add RISC-V IOPMP support hw/dma: Add Andes ATCDMAC300 support hw/riscv/virt: Add IOPMP support hw/core/Kconfig | 3 + hw/core/meson.build | 1 + hw/dma/Kconfig | 4 + hw/dma/atcdmac300.c | 566 ++++++++++ hw/dma/meson.build | 1 + hw/misc/Kconfig | 4 + hw/misc/meson.build | 1 + hw/misc/riscv_iopmp.c | 967 ++++++++++++++++++ hw/riscv/Kconfig | 2 + hw/riscv/virt.c | 72 +- include/hw/dma/atcdmac300.h | 180 ++++ include/hw/misc/riscv_iopmp.h | 342 +++++++ .../hw/misc/riscv_iopmp_transaction_info.h | 28 + include/hw/riscv/virt.h | 10 +- 14 files changed, 2178 insertions(+), 3 deletions(-) create mode 100644 hw/dma/atcdmac300.c create mode 100644 hw/misc/riscv_iopmp.c create mode 100644 include/hw/dma/atcdmac300.h create mode 100644 include/hw/misc/riscv_iopmp.h create mode 100644 include/hw/misc/riscv_iopmp_transaction_info.h -- 2.34.1 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v3 1/4] hw/core: Add config stream 2023-11-14 9:47 [PATCH v3 0/4] Support RISC-V IOPMP Ethan Chen via @ 2023-11-14 9:47 ` Ethan Chen via 2023-11-21 5:24 ` Alistair Francis 2023-11-14 9:47 ` [PATCH v3 2/4] Add RISC-V IOPMP support Ethan Chen via ` (2 subsequent siblings) 3 siblings, 1 reply; 14+ messages in thread From: Ethan Chen via @ 2023-11-14 9:47 UTC (permalink / raw) To: qemu-devel Cc: peter.maydell, edgar.iglesias, richard.henderson, pbonzini, palmer, alistair.francis, in.meng, liweiwei, dbarboza, hiwei_liu, qemu-riscv, peterx, david, Ethan Chen Make other device can use /hw/core/stream.c by select this config. Signed-off-by: Ethan Chen <ethan84@andestech.com> --- hw/core/Kconfig | 3 +++ hw/core/meson.build | 1 + 2 files changed, 4 insertions(+) diff --git a/hw/core/Kconfig b/hw/core/Kconfig index 9397503656..628dc3d883 100644 --- a/hw/core/Kconfig +++ b/hw/core/Kconfig @@ -27,3 +27,6 @@ config REGISTER config SPLIT_IRQ bool + +config STREAM + bool \ No newline at end of file diff --git a/hw/core/meson.build b/hw/core/meson.build index 67dad04de5..d6ce14d5ce 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -34,6 +34,7 @@ system_ss.add(when: 'CONFIG_REGISTER', if_true: files('register.c')) system_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c')) system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) +system_ss.add(when: 'CONFIG_STREAM', if_true: files('stream.c')) system_ss.add(files( 'cpu-sysemu.c', -- 2.34.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v3 1/4] hw/core: Add config stream 2023-11-14 9:47 ` [PATCH v3 1/4] hw/core: Add config stream Ethan Chen via @ 2023-11-21 5:24 ` Alistair Francis 2023-11-21 5:28 ` Alistair Francis 0 siblings, 1 reply; 14+ messages in thread From: Alistair Francis @ 2023-11-21 5:24 UTC (permalink / raw) To: Ethan Chen Cc: qemu-devel, peter.maydell, edgar.iglesias, richard.henderson, pbonzini, palmer, alistair.francis, in.meng, liweiwei, dbarboza, hiwei_liu, qemu-riscv, peterx, david On Tue, Nov 14, 2023 at 7:49 PM Ethan Chen via <qemu-devel@nongnu.org> wrote: > > Make other device can use /hw/core/stream.c by select this config. > > Signed-off-by: Ethan Chen <ethan84@andestech.com> > --- > hw/core/Kconfig | 3 +++ > hw/core/meson.build | 1 + > 2 files changed, 4 insertions(+) > > diff --git a/hw/core/Kconfig b/hw/core/Kconfig > index 9397503656..628dc3d883 100644 > --- a/hw/core/Kconfig > +++ b/hw/core/Kconfig > @@ -27,3 +27,6 @@ config REGISTER > > config SPLIT_IRQ > bool > + > +config STREAM > + bool > \ No newline at end of file You are missing a newline here. I think checkpatch should catch this, make sure you run it on all of your patches > diff --git a/hw/core/meson.build b/hw/core/meson.build > index 67dad04de5..d6ce14d5ce 100644 > --- a/hw/core/meson.build > +++ b/hw/core/meson.build > @@ -34,6 +34,7 @@ system_ss.add(when: 'CONFIG_REGISTER', if_true: files('register.c')) > system_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c')) > system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) > system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) > +system_ss.add(when: 'CONFIG_STREAM', if_true: files('stream.c')) You have added the build but not the file. This will fail to compile. Each patch must compile and run when applied individually in order. That way we maintain git bisectability. Can you please make sure that the build is not broken as your patches are applied Alistair > > system_ss.add(files( > 'cpu-sysemu.c', > -- > 2.34.1 > > ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3 1/4] hw/core: Add config stream 2023-11-21 5:24 ` Alistair Francis @ 2023-11-21 5:28 ` Alistair Francis 2023-11-21 10:09 ` Ethan Chen via 0 siblings, 1 reply; 14+ messages in thread From: Alistair Francis @ 2023-11-21 5:28 UTC (permalink / raw) To: Ethan Chen Cc: qemu-devel, peter.maydell, edgar.iglesias, richard.henderson, pbonzini, palmer, alistair.francis, in.meng, liweiwei, dbarboza, hiwei_liu, qemu-riscv, peterx, david On Tue, Nov 21, 2023 at 3:24 PM Alistair Francis <alistair23@gmail.com> wrote: > > On Tue, Nov 14, 2023 at 7:49 PM Ethan Chen via <qemu-devel@nongnu.org> wrote: > > > > Make other device can use /hw/core/stream.c by select this config. > > > > Signed-off-by: Ethan Chen <ethan84@andestech.com> > > --- > > hw/core/Kconfig | 3 +++ > > hw/core/meson.build | 1 + > > 2 files changed, 4 insertions(+) > > > > diff --git a/hw/core/Kconfig b/hw/core/Kconfig > > index 9397503656..628dc3d883 100644 > > --- a/hw/core/Kconfig > > +++ b/hw/core/Kconfig > > @@ -27,3 +27,6 @@ config REGISTER > > > > config SPLIT_IRQ > > bool > > + > > +config STREAM > > + bool > > \ No newline at end of file > > You are missing a newline here. I think checkpatch should catch this, > make sure you run it on all of your patches > > > diff --git a/hw/core/meson.build b/hw/core/meson.build > > index 67dad04de5..d6ce14d5ce 100644 > > --- a/hw/core/meson.build > > +++ b/hw/core/meson.build > > @@ -34,6 +34,7 @@ system_ss.add(when: 'CONFIG_REGISTER', if_true: files('register.c')) > > system_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c')) > > system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) > > system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) > > +system_ss.add(when: 'CONFIG_STREAM', if_true: files('stream.c')) > > You have added the build but not the file. This will fail to compile. > > Each patch must compile and run when applied individually in order. > That way we maintain git bisectability. Can you please make sure that > the build is not broken as your patches are applied Whoops! The file already exists. We should only include the file stream.c once. So we should change the CONFIG_XILINX_AXI to select CONFIG_STREAM in this patch Alistair ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3 1/4] hw/core: Add config stream 2023-11-21 5:28 ` Alistair Francis @ 2023-11-21 10:09 ` Ethan Chen via 0 siblings, 0 replies; 14+ messages in thread From: Ethan Chen via @ 2023-11-21 10:09 UTC (permalink / raw) To: Alistair Francis Cc: qemu-devel, peter.maydell, edgar.iglesias, richard.henderson, pbonzini, palmer, alistair.francis, in.meng, liweiwei, dbarboza, hiwei_liu, qemu-riscv, peterx, david On Tue, Nov 21, 2023 at 03:28:13PM +1000, Alistair Francis wrote: > On Tue, Nov 21, 2023 at 3:24 PM Alistair Francis <alistair23@gmail.com> wrote: > > > > On Tue, Nov 14, 2023 at 7:49 PM Ethan Chen via <qemu-devel@nongnu.org> wrote: > > > > > > Make other device can use /hw/core/stream.c by select this config. > > > > > > Signed-off-by: Ethan Chen <ethan84@andestech.com> > > > --- > > > hw/core/Kconfig | 3 +++ > > > hw/core/meson.build | 1 + > > > 2 files changed, 4 insertions(+) > > > > > > diff --git a/hw/core/Kconfig b/hw/core/Kconfig > > > index 9397503656..628dc3d883 100644 > > > --- a/hw/core/Kconfig > > > +++ b/hw/core/Kconfig > > > @@ -27,3 +27,6 @@ config REGISTER > > > > > > config SPLIT_IRQ > > > bool > > > + > > > +config STREAM > > > + bool > > > \ No newline at end of file > > > > You are missing a newline here. I think checkpatch should catch this, > > make sure you run it on all of your patches Sorry for that. It is wired that this was not catched by checkpatch. > > > > > diff --git a/hw/core/meson.build b/hw/core/meson.build > > > index 67dad04de5..d6ce14d5ce 100644 > > > --- a/hw/core/meson.build > > > +++ b/hw/core/meson.build > > > @@ -34,6 +34,7 @@ system_ss.add(when: 'CONFIG_REGISTER', if_true: files('register.c')) > > > system_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c')) > > > system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) > > > system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) > > > +system_ss.add(when: 'CONFIG_STREAM', if_true: files('stream.c')) > > > > You have added the build but not the file. This will fail to compile. > > > > Each patch must compile and run when applied individually in order. > > That way we maintain git bisectability. Can you please make sure that > > the build is not broken as your patches are applied > > Whoops! The file already exists. > > We should only include the file stream.c once. So we should change the > CONFIG_XILINX_AXI to select CONFIG_STREAM in this patch > I will fix that in next revision. Thanks, Ethan Chen ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v3 2/4] Add RISC-V IOPMP support 2023-11-14 9:47 [PATCH v3 0/4] Support RISC-V IOPMP Ethan Chen via 2023-11-14 9:47 ` [PATCH v3 1/4] hw/core: Add config stream Ethan Chen via @ 2023-11-14 9:47 ` Ethan Chen via 2023-11-21 5:38 ` Alistair Francis 2023-11-14 9:47 ` [PATCH v3 3/4] hw/dma: Add Andes ATCDMAC300 support Ethan Chen via 2023-11-14 9:47 ` [PATCH v3 4/4] hw/riscv/virt: Add IOPMP support Ethan Chen via 3 siblings, 1 reply; 14+ messages in thread From: Ethan Chen via @ 2023-11-14 9:47 UTC (permalink / raw) To: qemu-devel Cc: peter.maydell, edgar.iglesias, richard.henderson, pbonzini, palmer, alistair.francis, in.meng, liweiwei, dbarboza, hiwei_liu, qemu-riscv, peterx, david, Ethan Chen Support specification Version 1.0.0-draft4 rapid-k model. Signed-off-by: Ethan Chen <ethan84@andestech.com> --- hw/misc/Kconfig | 4 + hw/misc/meson.build | 1 + hw/misc/riscv_iopmp.c | 967 ++++++++++++++++++ include/hw/misc/riscv_iopmp.h | 342 +++++++ .../hw/misc/riscv_iopmp_transaction_info.h | 28 + 5 files changed, 1342 insertions(+) create mode 100644 hw/misc/riscv_iopmp.c create mode 100644 include/hw/misc/riscv_iopmp.h create mode 100644 include/hw/misc/riscv_iopmp_transaction_info.h diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index cc8a8c1418..953569e682 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -200,4 +200,8 @@ config IOSB config XLNX_VERSAL_TRNG bool +config RISCV_IOPMP + bool + select STREAM + source macio/Kconfig diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 36c20d5637..86b81e1690 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -35,6 +35,7 @@ system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c')) 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')) subdir('macio') diff --git a/hw/misc/riscv_iopmp.c b/hw/misc/riscv_iopmp.c new file mode 100644 index 0000000000..99d0a554dd --- /dev/null +++ b/hw/misc/riscv_iopmp.c @@ -0,0 +1,967 @@ +/* + * QEMU RISC-V IOPMP (Input Output Physical Memory Protection) + * + * Copyright (c) 2023 Andes Tech. Corp. + * + * 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.h" +#include "memory.h" +#include "hw/irq.h" + +#define TYPE_IOPMP_IOMMU_MEMORY_REGION "iopmp-iommu-memory-region" +#define TYPE_IOPMP_TRASACTION_INFO_SINK "iopmp_transaction_info_sink" + +DECLARE_INSTANCE_CHECKER(Iopmp_StreamSink, IOPMP_TRASACTION_INFO_SINK, + TYPE_IOPMP_TRASACTION_INFO_SINK) +#define LOGGE(x...) qemu_log_mask(LOG_GUEST_ERROR, x) +#define xLOG(x...) +#define yLOG(x...) qemu_log(x) +#ifdef DEBUG_RISCV_IOPMP + #define LOG(x...) yLOG(x) +#else + #define LOG(x...) xLOG(x) +#endif + +#define MEMTX_IOPMP_STALL (1 << 3) + + +static void iopmp_decode_napot(target_ulong a, target_ulong *sa, + target_ulong *ea) +{ + /* + * aaaa...aaa0 8-byte NAPOT range + * aaaa...aa01 16-byte NAPOT range + * aaaa...a011 32-byte NAPOT range + * ... + * aa01...1111 2^XLEN-byte NAPOT range + * a011...1111 2^(XLEN+1)-byte NAPOT range + * 0111...1111 2^(XLEN+2)-byte NAPOT range + * 1111...1111 Reserved + */ + + a = (a << 2) | 0x3; + *sa = a & (a + 1); + *ea = a | (a + 1); +} + +static void iopmp_update_rule(IopmpState *s, uint32_t entry_index) +{ + uint8_t this_cfg = s->regs.entry[entry_index].cfg_reg; + target_ulong this_addr = s->regs.entry[entry_index].addr_reg; + target_ulong prev_addr = 0u; + target_ulong sa = 0u; + target_ulong ea = 0u; + + if (entry_index >= 1u) { + prev_addr = s->regs.entry[entry_index - 1].addr_reg; + } + + switch (iopmp_get_field(this_cfg, ENTRY_CFG_A)) { + case IOPMP_AMATCH_OFF: + sa = 0u; + ea = -1; + break; + + case IOPMP_AMATCH_TOR: + sa = (prev_addr) << 2; /* shift up from [xx:0] to [xx+2:2] */ + ea = ((this_addr) << 2) - 1u; + if (sa > ea) { + sa = ea = 0u; + } + break; + + case IOPMP_AMATCH_NA4: + sa = this_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ + ea = (sa + 4u) - 1u; + break; + + case IOPMP_AMATCH_NAPOT: + iopmp_decode_napot(this_addr, &sa, &ea); + break; + + default: + sa = 0u; + ea = 0u; + break; + } + + s->entry_addr[entry_index].sa = sa; + s->entry_addr[entry_index].ea = ea; +} + +static uint64_t iopmp_read(void *opaque, hwaddr addr, unsigned size) +{ + IopmpState *s = IOPMP(opaque); + uint32_t rz = 0; + uint32_t is_stall = 0; + uint32_t sid; + switch (addr) { + case IOPMP_VERSION ... IOPMP_USER_CFG0 + 16 * (IOPMP_MAX_ENTRY_NUM - 1): + switch (addr) { + case IOPMP_VERSION: + rz = VENDER_ANDES << VERSION_VENDOR | + SPECVER_1_0_0_DRAFT4 << VERSION_SPECVER; + break; + case IOPMP_IMP: + rz = IMPID_1_0_0_DRAFT4_0; + break; + case IOPMP_HWCFG0: /* RO */ + rz = s->sid_num << HWCFG0_SID_NUM | + s->entry_num << HWCFG0_ENTRY_NUM; + break; + case IOPMP_HWCFG1: + rz = s->model << HWCFG1_MODEL | TOR_EN << HWCFG1_TOR_EN | + s->sps_en << HWCFG1_SPS_EN | + USER_CFG_EN << HWCFG1_USER_CFG_EN | + s->prient_prog << HWCFG1_PRIENT_PROG | + s->sid_transl_en << HWCFG1_SID_TRANSL_EN | + s->sid_transl_prog << HWCFG1_SID_TRANSL_PROG | + s->md_num << HWCFG1_MD_NUM | + s->enable << HWCFG1_ENABLE; + break; + case IOPMP_HWCFG2: + rz = s->prio_entry << HWCFG2_PRIO_ENTRY | + s->sid_transl << HWCFG2_SID_TRANSL; + break; + case IOPMP_ENTRYOFFSET: + rz = IOPMP_ENTRY_ADDR0; + break; + case IOPMP_ERRREACT: + rz = s->regs.errreact; + break; + case IOPMP_MDSTALL: + if (s->md_stall_stat) { + is_stall = 1; + } + rz = iopmp_get_field(s->regs.mdstall, MDSTALL_MD) | is_stall; + break; + case IOPMP_MDSTALLH: + rz = s->regs.mdstall >> 32; + break; + case IOPMP_SIDSCP: + sid = iopmp_get_field(s->regs.sidscp, SIDSCP_SID); + if (sid < s->sid_num) { + rz = sid | (s->sidscp_op[sid]) << SIDSCP_STAT; + } else { + rz = sid | 3 << SIDSCP_STAT; + } + break; + case IOPMP_MDLCK: + rz = s->regs.mdlck & UINT32_MAX; + break; + case IOPMP_MDLCKH: + rz = s->regs.mdlck >> 32; + break; + case IOPMP_MDCFGLCK: + rz = s->regs.mdcfglck; + break; + case IOPMP_ENTRYLCK: + rz = s->regs.entrylck; + break; + case IOPMP_ERR_REQADDR: + rz = s->regs.err_reqaddr & UINT32_MAX; + break; + case IOPMP_ERR_REQADDRH: + rz = s->regs.err_reqaddr >> 32; + break; + case IOPMP_ERR_REQSID: + rz = s->regs.err_reqsid; + break; + case IOPMP_ERR_REQINFO: + rz = s->regs.err_reqinfo; + break; + + default: + if (addr >= IOPMP_MDCFG0 && + addr < IOPMP_MDCFG0 + 4 * (s->md_num - 1)) { + int offset = addr - IOPMP_MDCFG0; + int idx = offset >> 2; + if (idx == 0) { + if (offset == 0) { + rz = s->regs.mdcfg[idx]; + } else { + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); + } + } else { + /* Only MDCFG0 is implemented */ + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); + } + } else if (addr >= IOPMP_SRCMD_EN0 && + addr < IOPMP_SRCMD_WH0 + 32 * (s->sid_num - 1)) { + int offset = addr - IOPMP_SRCMD_EN0; + int idx = offset >> 5; + offset &= 0x1f; + if (offset == 0) { + rz = s->regs.srcmd_en[idx] & UINT32_MAX; + } else if (offset == 4) { + rz = s->regs.srcmd_en[idx] >> 32; + } else if (offset == 8) { + rz = s->regs.srcmd_r[idx] & UINT32_MAX; + } else if (offset == 12) { + rz = s->regs.srcmd_r[idx] >> 32; + } else if (offset == 16) { + rz = s->regs.srcmd_w[idx] & UINT32_MAX; + } else if (offset == 24) { + rz = s->regs.srcmd_w[idx] >> 32; + } else { + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); + } + } else if (addr >= IOPMP_ENTRY_ADDR0 && + addr < IOPMP_USER_CFG0 + 16 * (s->entry_num - 1)) { + int offset = addr - IOPMP_ENTRY_ADDR0; + int idx = offset >> 4; + offset &= 0xf; + if (offset == 0) { + rz = s->regs.entry[idx].addr_reg & UINT32_MAX; + } else if (offset == 4) { + rz = s->regs.entry[idx].addr_reg >> 32; + } else if (offset == 8) { + rz = s->regs.entry[idx].cfg_reg; + } else if (offset == 12) { + /* Not supported user customized permission*/ + rz = 0; + } else { + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); + } + } else { + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); + } + break; + } + LOG("\e[95m%s: addr %08x, value %08x\e[0m\n", __func__, (int)addr, + (int)rz); + break; + default: + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); + } + + return rz; +} + +static void +iopmp_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) +{ + IopmpState *s = IOPMP(opaque); + int value_f; + int reg_f; + uint32_t sid, op; + + switch (addr) { + case IOPMP_VERSION ... IOPMP_USER_CFG0 + 16 * (IOPMP_MAX_ENTRY_NUM - 1): + switch (addr) { + case IOPMP_VERSION: /* RO */ + break; + case IOPMP_IMP: /* RO */ + break; + case IOPMP_HWCFG0: /* RO */ + break; + case IOPMP_HWCFG1: + if (iopmp_get_field(value, HWCFG1_PRIENT_PROG)) { + /* W1C */ + s->prient_prog = 0; + } + if (iopmp_get_field(value, HWCFG1_SID_TRANSL_PROG)) { + /* W1C */ + s->sid_transl_prog = 0; + } + if (iopmp_get_field(value, HWCFG1_ENABLE)) { + /* W1S */ + s->enable = 1; + } + break; + case IOPMP_HWCFG2: + if (s->prient_prog) { + s->prio_entry = iopmp_get_field(value, HWCFG2_PRIO_ENTRY); + } + if (s->sid_transl_en && s->sid_transl_prog) { + s->sid_transl = iopmp_get_field(value, HWCFG2_SID_TRANSL); + } + break; + case IOPMP_ERRREACT: + if (!iopmp_get_field(s->regs.errreact, ERRREACT_L)) { + iopmp_set_field32(&s->regs.errreact, ERRREACT_L, + iopmp_get_field(value, ERRREACT_L)); + if (iopmp_get_field(value, ERRREACT_IP)) { + iopmp_set_field32(&s->regs.errreact, ERRREACT_IP, 0); + } + iopmp_set_field32(&s->regs.errreact, ERRREACT_IE, + iopmp_get_field(value, ERRREACT_IE)); + iopmp_set_field32(&s->regs.errreact, ERRREACT_IRE, + iopmp_get_field(value, ERRREACT_IRE)); + iopmp_set_field32(&s->regs.errreact, ERRREACT_RRE, + iopmp_get_field(value, ERRREACT_RRE)); + iopmp_set_field32(&s->regs.errreact, ERRREACT_IWE, + iopmp_get_field(value, ERRREACT_IWE)); + iopmp_set_field32(&s->regs.errreact, ERRREACT_RWE, + iopmp_get_field(value, ERRREACT_RWE)); + iopmp_set_field32(&s->regs.errreact, ERRREACT_PEE, + iopmp_get_field(value, ERRREACT_PEE)); + iopmp_set_field32(&s->regs.errreact, ERRREACT_RPE, + iopmp_get_field(value, ERRREACT_RPE)); + } else { + if (iopmp_get_field(value, ERRREACT_IP)) { + iopmp_set_field32(&s->regs.errreact, ERRREACT_IP, 0); + } + } + break; + case IOPMP_MDSTALL: + iopmp_set_field64(&s->regs.mdstall, MDSTALL, value); + break; + case IOPMP_MDSTALLH: + iopmp_set_field64(&s->regs.mdstall, MDSTALLH, value); + break; + case IOPMP_SIDSCP: + sid = iopmp_get_field(value, SIDSCP_SID); + op = iopmp_get_field(value, SIDSCP_OP); + if (sid < s->sid_num) { + if (op != SIDSCP_OP_QUERY) { + s->sidscp_op[sid] = op; + s->regs.sidscp = value; + } + } else { + s->regs.sidscp = sid | (0x3 << SIDSCP_OP); + } + break; + case IOPMP_MDLCK: + if (!(s->regs.mdlck & (1 << MDLCK_L))) { + s->regs.mdlck = value | + (s->regs.mdstall & ~(uint64_t)UINT32_MAX); + } + break; + case IOPMP_MDLCKH: + if (!(s->regs.mdlck & (1 << MDLCK_L))) { + s->regs.mdlck = (uint64_t)value << 32 | + (s->regs.mdstall & UINT32_MAX); + } + break; + case IOPMP_MDCFGLCK: + if (!iopmp_get_field(s->regs.mdcfglck, MDCFGLCK_L)) { + value_f = iopmp_get_field(value, MDCFGLCK_F); + reg_f = iopmp_get_field(s->regs.mdcfglck, MDCFGLCK_F); + if (value_f > reg_f) { + iopmp_set_field32(&s->regs.mdcfglck, MDCFGLCK_F, value_f); + } + iopmp_set_field32(&s->regs.mdcfglck, MDCFGLCK_L, + iopmp_get_field(value, MDCFGLCK_L)); + } + break; + case IOPMP_ENTRYLCK: + if (!(iopmp_get_field(s->regs.entrylck, ENTRYLCK_L))) { + value_f = iopmp_get_field(value, ENTRYLCK_F); + reg_f = iopmp_get_field(s->regs.entrylck, ENTRYLCK_F); + if (value_f > reg_f) { + iopmp_set_field32(&s->regs.entrylck, ENTRYLCK_F, value_f); + } + iopmp_set_field32(&s->regs.entrylck, ENTRYLCK_F, + iopmp_get_field(value, ENTRYLCK_F)); + } + case IOPMP_ERR_REQADDR: /* RO */ + break; + case IOPMP_ERR_REQADDRH: /* RO */ + break; + case IOPMP_ERR_REQSID: /* RO */ + break; + case IOPMP_ERR_REQINFO: /* RO */ + break; + + default: + if (addr >= IOPMP_MDCFG0 && + addr < IOPMP_MDCFG0 + 4 * (s->md_num - 1)) { + int offset = addr - IOPMP_MDCFG0; + int idx = offset >> 2; + /* RO in rapid-k model */ + if (idx > 0) { + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); + } + } else if (addr >= IOPMP_SRCMD_EN0 && + addr < IOPMP_SRCMD_WH0 + 32 * (s->sid_num - 1)) { + int offset = addr - IOPMP_SRCMD_EN0; + int idx = offset >> 5; + offset &= 0x1f; + if (offset % 4) { + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); + } else if (iopmp_get_field(s->regs.srcmd_en[idx], + SRCMD_EN_L) == 0) { + if (offset == 0) { + iopmp_set_field64(&s->regs.srcmd_en[idx], SRCMD_EN_MD, + iopmp_get_field(value, SRCMD_EN_MD)); + iopmp_set_field64(&s->regs.srcmd_en[idx], SRCMD_EN_L, + iopmp_get_field(value, SRCMD_EN_L)); + } else if (offset == 4) { + iopmp_set_field64(&s->regs.srcmd_en[idx], SRCMD_ENH_MDH, + value); + } else if (offset == 8 && s->sps_en) { + iopmp_set_field64(&s->regs.srcmd_r[idx], SRCMD_R_MD, + iopmp_get_field(value, SRCMD_R_MD)); + } else if (offset == 12 && s->sps_en) { + iopmp_set_field64(&s->regs.srcmd_r[idx], SRCMD_RH_MDH, + value); + } else if (offset == 16 && s->sps_en) { + iopmp_set_field64(&s->regs.srcmd_w[idx], SRCMD_W_MD, + iopmp_get_field(value, SRCMD_W_MD)); + } else if (offset == 24 && s->sps_en) { + iopmp_set_field64(&s->regs.srcmd_w[idx], SRCMD_WH_MDH, + value); + } + } + } else if (addr >= IOPMP_ENTRY_ADDR0 && + addr < IOPMP_USER_CFG0 + 16 * (s->entry_num - 1)) { + int offset = addr - IOPMP_ENTRY_ADDR0; + int idx = offset >> 4; + offset &= 0xf; + if (offset == 0) { + iopmp_set_field64(&s->regs.entry[idx].addr_reg, + ENTRY_ADDR_ADDR, value); + } else if (offset == 4) { + iopmp_set_field64(&s->regs.entry[idx].addr_reg, + ENTRY_ADDRH_ADDRH, value); + } else if (offset == 8) { + s->regs.entry[idx].cfg_reg = value; + } else if (offset == 12) { + /* Not supported user customized permission*/ + ; + } else { + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); + } + iopmp_update_rule(s, idx); + } else { + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); + } + /* If IOPMP permission of any addr has been changed, */ + /* flush TLB pages. */ + tlb_flush_all_cpus_synced(current_cpu); + break; + } + LOG("\e[95m%s: addr %08x, value %08x\e[0m\n", __func__, (int)addr, + (int)rz); + break; + default: + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); + } +} + +/* Match entry in memory domain */ +static int match_entry_md(IopmpState *s, int md_idx, hwaddr s_addr, + hwaddr e_addr, int *entry_idx) +{ + int entry_idx_s, entry_idx_e; + int result = ENTRY_NO_HIT; + int i = 0; + entry_idx_s = md_idx * s->regs.mdcfg[0]; + entry_idx_e = (md_idx + 1) * s->regs.mdcfg[0]; + if (entry_idx_s >= s->entry_num) { + return result; + } + if (entry_idx_e > s->entry_num) { + entry_idx_e = s->entry_num; + } + i = entry_idx_s; + while (i < entry_idx_e) { + if (s_addr >= s->entry_addr[i].sa && s_addr <= s->entry_addr[i].ea) { + /* check end address */ + if (e_addr >= s->entry_addr[i].sa && + e_addr <= s->entry_addr[i].ea) { + *entry_idx = i; + return ENTRY_HIT; + } else if (i >= s->prio_entry) { + /* record result and continue for non-prio_entry */ + result = ENTRY_PAR_HIT; + continue; + } else { + return ENTRY_PAR_HIT; + } + } + i++; + } + return result; +} + +static int match_entry(IopmpState *s, int sid, hwaddr s_addr, hwaddr e_addr, + int *match_md_idx, int *match_entry_idx) +{ + int cur_result = ENTRY_NO_HIT; + int result = ENTRY_NO_HIT; + uint64_t srcmd_en = s->regs.srcmd_en[sid] >> 1; + for (int md_idx = 0; md_idx < s->md_num; md_idx++) { + if (srcmd_en & (1ULL << md_idx)) { + cur_result = match_entry_md(s, md_idx, s_addr, e_addr, + match_entry_idx); + if (cur_result == ENTRY_HIT) { + *match_md_idx = md_idx; + return cur_result; + } + if (cur_result > result) { + result = cur_result; + } + } + } + return result; +} + +static bool check_md_stall(IopmpState *s, int md_idx) +{ + uint64_t md_selected = iopmp_get_field(s->regs.mdstall, MDSTALL_MD) & + (1 << md_idx); + if (iopmp_get_field(s->regs.mdstall, MDSTALL_EXEMPT)) { + return !md_selected; + } else { + return md_selected; + } +} + +static inline bool check_sidscp_stall(IopmpState *s, int sid) +{ + return s->sidscp_op[sid] == SIDSCP_OP_STALL; +} + +static void iopmp_error_reaction(IopmpState *s, uint32_t id, hwaddr start, + hwaddr end, uint32_t info) +{ + if (start == s->prev_error_info[id].start_addr && + end == s->prev_error_info[id].end_addr && + info == s->prev_error_info[id].reqinfo) { + /* skip following error */ + ; + } else { + s->prev_error_info[id].start_addr = start; + s->prev_error_info[id].end_addr = end; + s->prev_error_info[id].reqinfo = info; + if (!iopmp_get_field(s->regs.errreact, ERRREACT_IP)) { + iopmp_set_field32(&s->regs.errreact, ERRREACT_IP, 1); + s->regs.err_reqsid = id; + s->regs.err_reqaddr = start; + s->regs.err_reqinfo = info; + + if (iopmp_get_field(info, ERR_REQINFO_TYPE) == ERR_REQINFO_TYPE_READ + && iopmp_get_field(s->regs.errreact, ERRREACT_IE) && + iopmp_get_field(s->regs.errreact, ERRREACT_IRE)) { + qemu_set_irq(s->irq, 1); + } + if (iopmp_get_field(info, ERR_REQINFO_TYPE) == + ERR_REQINFO_TYPE_WRITE && + iopmp_get_field(s->regs.errreact, ERRREACT_IE) && + iopmp_get_field(s->regs.errreact, ERRREACT_IWE)) { + qemu_set_irq(s->irq, 1); + } + } + } +} + +static IOMMUTLBEntry iopmp_translate(IOMMUMemoryRegion *iommu, hwaddr addr, + IOMMUAccessFlags flags, int iommu_idx) +{ + bool is_stalled = false; + int sid = iommu_idx; + IopmpState *s = IOPMP(container_of(iommu, IopmpState, iommu)); + hwaddr start_addr, end_addr; + if (s->transaction_state[sid].supported) { + /* get transaction_state if device supported */ + start_addr = s->transaction_state[sid].start_addr; + end_addr = s->transaction_state[sid].end_addr; + if (addr > end_addr || addr < start_addr || + !s->transaction_state[sid].running) { + error_report("transaction_state error."); + exit(1); + } + } else { + start_addr = addr; + end_addr = addr; + } + IOMMUTLBEntry entry = { + .target_as = &s->downstream_as, + .iova = addr, + .translated_addr = addr, + .addr_mask = (~(hwaddr)0), + .perm = IOMMU_NONE, + }; + int entry_idx = -1; + int md_idx = -1; + int result = match_entry(s, sid, start_addr, end_addr, &md_idx, &entry_idx); + int srcmd_rw; + if (result == ENTRY_HIT) { + is_stalled = check_md_stall(s, md_idx) || check_sidscp_stall(s, sid); + if (is_stalled) { + s->md_stall_stat |= (1 << md_idx); + entry.target_as = &s->stall_io_as; + entry.perm = IOMMU_RW; + return entry; + } else { + s->md_stall_stat &= ~(1 << md_idx); + } + entry.perm = s->regs.entry[entry_idx].cfg_reg & 0x7; + if (s->sps_en) { + /* do not affect x permission */ + srcmd_rw = 0x4 | ((s->regs.srcmd_r[sid] >> + (md_idx + SRCMD_R_MD)) & 0x1); + srcmd_rw |= ((s->regs.srcmd_w[sid] >> + (md_idx + SRCMD_W_MD)) & 0x1) << 1; + entry.perm &= srcmd_rw; + } + if ((entry.perm & flags) == 0) { + /* permission denied */ + iopmp_error_reaction(s, sid, start_addr, end_addr, + (entry_idx << ERR_REQINFO_EID) | + ((flags - 1) << ERR_REQINFO_TYPE)); + entry.target_as = &s->blocked_io_as; + entry.perm = IOMMU_RW; + } else { + entry.addr_mask = s->entry_addr[entry_idx].ea - + s->entry_addr[entry_idx].sa; + /* clear error info */ + s->prev_error_info[sid].reqinfo = 0; + if (s->sid_transl_en) { + /* next iopmp */ + if (s->next_iommu) { + int new_sid = s->sid_transl; + IopmpState *next_s = IOPMP(container_of(s->next_iommu, + IopmpState, iommu)); + next_s->transaction_state[new_sid].supported = true; + while (next_s->transaction_state[new_sid].running) { + ; + } + qemu_mutex_lock(&next_s->iopmp_transaction_mutex); + next_s->transaction_state[new_sid].running = 1; + qemu_mutex_unlock(&next_s->iopmp_transaction_mutex); + next_s->transaction_state[new_sid].start_addr = start_addr; + next_s->transaction_state[new_sid].end_addr = end_addr; + entry = iopmp_translate(s->next_iommu, addr, flags, + s->sid_transl); + qemu_mutex_lock(&next_s->iopmp_transaction_mutex); + next_s->transaction_state[new_sid].running = 0; + qemu_mutex_unlock(&next_s->iopmp_transaction_mutex); + return entry; + } else { + error_report("Next iopmp is not found."); + exit(1); + } + } + } + } else { + if (result == ENTRY_PAR_HIT) { + iopmp_error_reaction(s, sid, start_addr, end_addr, + (1 << ERR_REQINFO_PAR_HIT) | + ((flags - 1) << ERR_REQINFO_TYPE)); + } else { + iopmp_error_reaction(s, sid, start_addr, end_addr, + (1 << ERR_REQINFO_NO_HIT) | + ((flags - 1) << ERR_REQINFO_TYPE)); + } + entry.target_as = &s->blocked_io_as; + entry.perm = IOMMU_RW; + } + return entry; +} + +static const MemoryRegionOps iopmp_ops = { + .read = iopmp_read, + .write = iopmp_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = {.min_access_size = 4, .max_access_size = 4} +}; + +static MemTxResult iopmp_block_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size, MemTxAttrs attrs) +{ + IopmpState *s = IOPMP(opaque); + + switch (iopmp_get_field(s->regs.errreact, ERRREACT_RWE)) { + case RWE_BUS_ERROR: + return MEMTX_ERROR; + break; + case RWE_DECODE_ERROR: + return MEMTX_DECODE_ERROR; + break; + case RWE_SUCCESS: + return MEMTX_OK; + break; + default: + break; + } + return MEMTX_OK; +} + +static MemTxResult iopmp_block_read(void *opaque, hwaddr addr, uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + IopmpState *s = IOPMP(opaque); + + switch (iopmp_get_field(s->regs.errreact, ERRREACT_RRE)) { + case RRE_BUS_ERROR: + return MEMTX_ERROR; + break; + case RRE_DECODE_ERROR: + return MEMTX_DECODE_ERROR; + break; + case RRE_SUCCESS_ZEROS: + *pdata = 0; + return MEMTX_OK; + break; + case RRE_SUCCESS_ONES: + *pdata = UINT64_MAX; + return MEMTX_OK; + break; + default: + break; + } + return MEMTX_OK; +} + +static const MemoryRegionOps iopmp_block_io_ops = { + .read_with_attrs = iopmp_block_read, + .write_with_attrs = iopmp_block_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = {.min_access_size = 1, .max_access_size = 8}, +}; + +static MemTxResult iopmp_handle_stall(IopmpState *s, hwaddr addr, + MemTxAttrs attrs) +{ + return MEMTX_IOPMP_STALL; +} + +static MemTxResult iopmp_stall_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size, MemTxAttrs attrs) +{ + IopmpState *s = IOPMP(opaque); + + return iopmp_handle_stall(s, addr, attrs); +} + +static MemTxResult iopmp_stall_read(void *opaque, hwaddr addr, uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + IopmpState *s = IOPMP(opaque); + + *pdata = 0; + return iopmp_handle_stall(s, addr, attrs); +} + +static const MemoryRegionOps iopmp_stall_io_ops = { + .read_with_attrs = iopmp_stall_read, + .write_with_attrs = iopmp_stall_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = {.min_access_size = 1, .max_access_size = 8}, +}; + +static void iopmp_realize(DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + IopmpState *s = IOPMP(dev); + s->downstream = get_system_memory(); + uint64_t size = memory_region_size(s->downstream); + + qemu_mutex_init(&s->iopmp_transaction_mutex); + s->prient_prog = 1; + s->sid_num = MIN(s->sid_num, IOPMP_MAX_SID_NUM); + s->md_num = MIN(s->md_num, IOPMP_MAX_MD_NUM); + s->entry_num = MIN(s->entry_num, IOPMP_MAX_ENTRY_NUM); + if (s->sid_transl_en) { + s->sid_transl_prog = 1; + } + if (!s->model_str || strcmp(s->model_str, "rapidk") == 0) { + /* apply default model */ + s->model = IOPMP_MODEL_RAPIDK; + iopmp_set_field32(&s->regs.mdcfglck, MDCFGLCK_F, s->md_num); + iopmp_set_field32(&s->regs.mdcfglck, MDCFGLCK_L, 1); + s->regs.mdcfg[0] = s->k; + } else { + error_report("IOPMP model %s is not supported. " + "Vailid values are full, rapidk, dynamick," + "isolation and compactk.", s->model_str); + exit(1); + } + memory_region_init_iommu(&s->iommu, sizeof(s->iommu), + TYPE_IOPMP_IOMMU_MEMORY_REGION, + obj, "iopmp-iommu", UINT64_MAX); + address_space_init(&s->iopmp_as, MEMORY_REGION(&s->iommu), "iommu"); + memory_region_init_io(&s->mmio, obj, &iopmp_ops, + s, "iopmp-regs", 0x100000); + sysbus_init_mmio(sbd, &s->mmio); + memory_region_init_io(&s->blocked_io, obj, &iopmp_block_io_ops, + s, "iopmp-blocked-io", size); + address_space_init(&s->downstream_as, s->downstream, + "iopmp-downstream-as"); + address_space_init(&s->blocked_io_as, &s->blocked_io, + "iopmp-blocked-io-as"); + + memory_region_init_io(&s->stall_io, obj, &iopmp_stall_io_ops, + s, "iopmp-stall-io", size); + address_space_init(&s->stall_io_as, &s->stall_io, + "iopmp-stall-io-as"); + + object_initialize_child(OBJECT(s), "iopmp_transaction_info", + &s->transaction_info_sink, + TYPE_IOPMP_TRASACTION_INFO_SINK); +} + +static void iopmp_reset(DeviceState *dev) +{ + IopmpState *s = IOPMP(dev); + qemu_set_irq(s->irq, 0); + memset(&s->regs, 0, sizeof(iopmp_regs)); + memset(&s->entry_addr, 0, IOPMP_MAX_ENTRY_NUM * sizeof(iopmp_addr_t)); + if (s->model == IOPMP_MODEL_RAPIDK) { + iopmp_set_field32(&s->regs.mdcfglck, MDCFGLCK_F, s->md_num); + iopmp_set_field32(&s->regs.mdcfglck, MDCFGLCK_L, 1); + s->regs.mdcfg[0] = s->k; + } + s->regs.errreact = 0; + + s->prient_prog = 1; + if (s->sid_transl_en) { + s->sid_transl_prog = 1; + } +} + +static int iopmp_attrs_to_index(IOMMUMemoryRegion *iommu, MemTxAttrs attrs) +{ + unsigned int sid = attrs.requester_id; + return sid; +} + +static void iopmp_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = iopmp_translate; + imrc->attrs_to_index = iopmp_attrs_to_index; +} + +static Property iopmp_property[] = { + DEFINE_PROP_STRING("model", IopmpState, model_str), + DEFINE_PROP_BOOL("sps_en", IopmpState, sps_en, false), + DEFINE_PROP_BOOL("sid_transl_en", IopmpState, sid_transl_en, false), + DEFINE_PROP_UINT32("k", IopmpState, k, IOPMP_MODEL_K), + DEFINE_PROP_UINT32("prio_entry", IopmpState, prio_entry, PRIO_ENTRY), + DEFINE_PROP_UINT32("sid_num", IopmpState, sid_num, IOPMP_MAX_SID_NUM), + DEFINE_PROP_UINT32("md_num", IopmpState, md_num, IOPMP_MAX_MD_NUM), + DEFINE_PROP_UINT32("entry_num", IopmpState, entry_num, IOPMP_MAX_ENTRY_NUM), + DEFINE_PROP_END_OF_LIST(), +}; + +static void iopmp_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + device_class_set_props(dc, iopmp_property); + dc->realize = iopmp_realize; + dc->reset = iopmp_reset; +} + +static void iopmp_init(Object *obj) +{ + IopmpState *s = IOPMP(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + sysbus_init_irq(sbd, &s->irq); +} + +static const TypeInfo iopmp_info = { + .name = TYPE_IOPMP, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IopmpState), + .instance_init = iopmp_init, + .class_init = iopmp_class_init, +}; + +static const TypeInfo +iopmp_iommu_memory_region_info = { + .name = TYPE_IOPMP_IOMMU_MEMORY_REGION, + .parent = TYPE_IOMMU_MEMORY_REGION, + .class_init = iopmp_iommu_memory_region_class_init, +}; + +DeviceState *iopmp_create(hwaddr addr, qemu_irq irq) +{ + LOG("%s:\n", __func__); + DeviceState *iopmp_device = sysbus_create_varargs(TYPE_IOPMP, addr, irq, + NULL); + return iopmp_device; +} + +void +cascade_iopmp(DeviceState *cur_dev, DeviceState *next_dev) +{ + IopmpState *s = IOPMP(cur_dev); + s->sid_transl_en = true; + IopmpState *next_s = IOPMP(next_dev); + s->next_iommu = &next_s->iommu; +} + +static size_t +transaction_info_push(StreamSink *transaction_info_sink, unsigned char *buf, + size_t len, bool eop) +{ + Iopmp_StreamSink *ss = IOPMP_TRASACTION_INFO_SINK(transaction_info_sink); + IopmpState *s = IOPMP(container_of(ss, IopmpState, + transaction_info_sink)); + iopmp_transaction_info signal; + memcpy(&signal, buf, len); + uint32_t sid = signal.sid; + if (s->transaction_state[sid].running) { + if (eop) { + qemu_mutex_lock(&s->iopmp_transaction_mutex); + s->transaction_state[sid].running = 0; + qemu_mutex_unlock(&s->iopmp_transaction_mutex); + return 1; + } else { + return 0; + } + } else if (len == sizeof(iopmp_transaction_info)) { + s->transaction_state[sid].supported = 1; + qemu_mutex_lock(&s->iopmp_transaction_mutex); + s->transaction_state[sid].running = 1; + qemu_mutex_unlock(&s->iopmp_transaction_mutex); + s->transaction_state[sid].start_addr = signal.start_addr; + s->transaction_state[sid].end_addr = signal.end_addr; + return 1; + } + return 0; +} + +static void iopmp_transaction_info_sink_class_init(ObjectClass *klass, + void *data) +{ + StreamSinkClass *ssc = STREAM_SINK_CLASS(klass); + ssc->push = transaction_info_push; +} + +static const TypeInfo transaction_info_sink = { + .name = TYPE_IOPMP_TRASACTION_INFO_SINK, + .parent = TYPE_OBJECT, + .instance_size = sizeof(Iopmp_StreamSink), + .class_init = iopmp_transaction_info_sink_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_STREAM_SINK }, + { } + }, +}; + +static void +iopmp_register_types(void) +{ + type_register_static(&iopmp_info); + type_register_static(&iopmp_iommu_memory_region_info); + type_register_static(&transaction_info_sink); +} + +type_init(iopmp_register_types); diff --git a/include/hw/misc/riscv_iopmp.h b/include/hw/misc/riscv_iopmp.h new file mode 100644 index 0000000000..7ff078c903 --- /dev/null +++ b/include/hw/misc/riscv_iopmp.h @@ -0,0 +1,342 @@ +/* + * QEMU RISC-V IOPMP (Input Output Physical Memory Protection) + * + * Copyright (c) 2023 Andes Tech. Corp. + * + * 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_H +#define RISCV_IOPMP_H + +#include "hw/sysbus.h" +#include "qemu/typedefs.h" +#include "memory.h" +#include "hw/stream.h" +#include "hw/misc/riscv_iopmp_transaction_info.h" + +#define TYPE_IOPMP "iopmp" +#define IOPMP(obj) OBJECT_CHECK(IopmpState, (obj), TYPE_IOPMP) + +#define iopmp_get_field(reg, name) (((reg) & (name ## _FIELD)) >> (name)) +#define iopmp_set_field32(reg, name, newval) do { \ + uint32_t val = *reg; \ + val &= ~name##_FIELD; \ + val |= ((newval) << name) & name##_FIELD; \ + *reg = val; \ + } while (0) +#define iopmp_set_field64(reg, name, newval) do { \ + uint64_t val = *reg; \ + val &= ~name##_FIELD; \ + val |= ((newval) << name) & name##_FIELD; \ + *reg = val; \ + } while (0) + + +#define IOPMP_MAX_MD_NUM 63 +#define IOPMP_MAX_SID_NUM 256 +#define IOPMP_MAX_ENTRY_NUM 512 + +#define IOPMP_VERSION 0x0 +#define IOPMP_IMP 0x4 +#define IOPMP_HWCFG0 0x8 +#define IOPMP_HWCFG1 0xC +#define IOPMP_HWCFG2 0x10 +#define IOPMP_ENTRYOFFSET 0x20 +#define IOPMP_ERRREACT 0x28 +#define IOPMP_MDSTALL 0x30 +#define IOPMP_MDSTALLH 0x34 +#define IOPMP_SIDSCP 0x38 +#define IOPMP_MDLCK 0x40 +#define IOPMP_MDLCKH 0x44 +#define IOPMP_MDCFGLCK 0x48 +#define IOPMP_ENTRYLCK 0x4C + +#define IOPMP_ERR_REQADDR 0x60 +#define IOPMP_ERR_REQADDRH 0x64 +#define IOPMP_ERR_REQSID 0x68 +#define IOPMP_ERR_REQINFO 0x6C + +#define IOPMP_MDCFG0 0x800 +#define IOPMP_SRCMD_EN0 0x1000 +#define IOPMP_SRCMD_ENH0 0x1004 +#define IOPMP_SRCMD_R0 0x1008 +#define IOPMP_SRCMD_RH0 0x100C +#define IOPMP_SRCMD_W0 0x1010 +#define IOPMP_SRCMD_WH0 0x1014 + +#define IOPMP_ENTRY_ADDR0 0x4000 +#define IOPMP_ENTRY_ADDRH0 0x4004 +#define IOPMP_ENTRY_CFG0 0x4008 +#define IOPMP_USER_CFG0 0x400C + +#define VERSION_VENDOR 0 +#define VERSION_SPECVER 24 +#define VENDER_ANDES 6533 +#define SPECVER_1_0_0_DRAFT4 4 + +#define IMPID_1_0_0_DRAFT4_0 10040 + +#define HWCFG0_SID_NUM 0 +#define HWCFG0_ENTRY_NUM 16 + +#define HWCFG1_MODEL 0 +#define HWCFG1_TOR_EN 4 +#define HWCFG1_SPS_EN 5 +#define HWCFG1_USER_CFG_EN 6 +#define HWCFG1_PRIENT_PROG 7 +#define HWCFG1_SID_TRANSL_EN 8 +#define HWCFG1_SID_TRANSL_PROG 9 +#define HWCFG1_MD_NUM 24 +#define HWCFG1_ENABLE 31 + +#define HWCFG1_SPS_EN_FIELD (1 << HWCFG1_SPS_EN) +#define HWCFG1_PRIENT_PROG_FIELD (1 << HWCFG1_PRIENT_PROG) +#define HWCFG1_SID_TRANSL_PROG_FIELD (1 << HWCFG1_SID_TRANSL_PROG) +#define HWCFG1_ENABLE_FIELD (1 << HWCFG1_ENABLE) + +#define HWCFG2_PRIO_ENTRY 0 +#define HWCFG2_SID_TRANSL 16 + +#define HWCFG2_PRIO_ENTRY_FIELD (0xFFFF << HWCFG2_PRIO_ENTRY) +#define HWCFG2_SID_TRANSL_FIELD (0xFFFF << HWCFG2_SID_TRANSL) + +#define ERRREACT_L 0 +#define ERRREACT_IE 1 +#define ERRREACT_IP 2 +#define ERRREACT_IRE 4 +#define ERRREACT_RRE 5 +#define ERRREACT_IWE 8 +#define ERRREACT_RWE 9 +#define ERRREACT_PEE 28 +#define ERRREACT_RPE 29 + +#define ERRREACT_L_FIELD (0x1 << ERRREACT_L) +#define ERRREACT_IE_FIELD (0x1 << ERRREACT_IE) +#define ERRREACT_IP_FIELD (0x1 << ERRREACT_IP) +#define ERRREACT_IRE_FIELD (0x1 << ERRREACT_IRE) +#define ERRREACT_RRE_FIELD (0x7 << ERRREACT_RRE) +#define ERRREACT_IWE_FIELD (0x1 << ERRREACT_IWE) +#define ERRREACT_RWE_FIELD (0x7 << ERRREACT_RWE) +#define ERRREACT_PEE_FIELD (0x1 << ERRREACT_PEE) +#define ERRREACT_RPE_FIELD (0x7 << ERRREACT_RPE) + +#define RRE_BUS_ERROR 0 +#define RRE_DECODE_ERROR 1 +#define RRE_SUCCESS_ZEROS 2 +#define RRE_SUCCESS_ONES 3 + +#define RWE_BUS_ERROR 0 +#define RWE_DECODE_ERROR 1 +#define RWE_SUCCESS 2 + +#define MDSTALL 0 +#define MDSTALLH 32 +#define MDSTALL_FIELD UINT32_MAX +#define MDSTALLH_FIELD (UINT64_MAX << MDSTALLH) +#define MDSTALL_EXEMPT 0 +#define MDSTALL_EXEMPT_FIELD (1 << MDSTALL_EXEMPT) +#define MDSTALL_ISSTALLED 0 +#define MDSTALL_MD 1 +#define MDSTALL_MD_FIELD (0x7FFFFFFFFFFFFFFF << MDSTALL_MD) + +#define SIDSCP_SID 0 +#define SIDSCP_STAT 30 +#define SIDSCP_OP 30 +#define SIDSCP_SID_FIELD (0xFFFF << SIDSCP_SID) +#define SIDSCP_STAT_FIELD (0x3 << SIDSCP_STAT) +#define SIDSCP_OP_FIELD (0x3 << SIDSCP_OP) +#define SIDSCP_OP_QUERY 0 +#define SIDSCP_OP_STALL 1 +#define SIDSCP_OP_NOTSTALL 2 + +#define MDLCK_L 0 +#define MDLCK_MD 1 + +#define MDCFGLCK_L 0 +#define MDCFGLCK_L_FIELD (0x1 << MDCFGLCK_L) +#define MDCFGLCK_F 1 +#define MDCFGLCK_F_FIELD (0x7F << MDCFGLCK_F) + +#define ENTRYLCK_L 0 +#define ENTRYLCK_L_FIELD (0x1 << MDCFGLCK_L) +#define ENTRYLCK_F 1 +#define ENTRYLCK_F_FIELD (0xFFFF << ENTRYLCK_F) + +#define ERR_REQINFO_NO_HIT 0 +#define ERR_REQINFO_PAR_HIT 1 +#define ERR_REQINFO_TYPE 8 +#define ERR_REQINFO_EID 16 + +#define ERR_REQINFO_NO_HIT_FIELD (0x1 << ERR_REQINFO_NO_HIT) +#define ERR_REQINFO_PAR_HIT_FIELD (0x1 << ERR_REQINFO_PAR_HIT) +#define ERR_REQINFO_TYPE_FIELD (0x3 << ERR_REQINFO_TYPE) +#define ERR_REQINFO_EID_FIELD (0xFFFF << ERR_REQINFO_EID) + +#define ERR_REQINFO_TYPE_READ 0 +#define ERR_REQINFO_TYPE_WRITE 1 +#define ERR_REQINFO_TYPE_USER 3 + +#define SRCMD_EN_L 0 +#define SRCMD_EN_MD 1 +#define SRCMD_EN_L_FIELD (0x1 << SRCMD_EN_L) +#define SRCMD_EN_MD_FIELD (0x7FFFFFFF << SRCMD_EN_MD) +#define SRCMD_ENH_MDH 32 +#define SRCMD_ENH_MDH_FIELD (0xFFFFFFFFUL << SRCMD_ENH_MDH) + +#define SRCMD_R_MD 1 +#define SRCMD_R_MD_FIELD (0x7FFFFFFF << SRCMD_R_MD) +#define SRCMD_RH_MDH 32 +#define SRCMD_RH_MDH_FIELD (0xFFFFFFFFUL << SRCMD_RH_MDH) +#define SRCMD_W_MD 1 +#define SRCMD_W_MD_FIELD (0x7FFFFFFF << SRCMD_W_MD) +#define SRCMD_WH_MDH 32 +#define SRCMD_WH_MDH_FIELD (0xFFFFFFFFUL << SRCMD_WH_MDH) + +#define ENTRY_ADDR_ADDR 0 +#define ENTRY_ADDR_ADDR_FIELD 0xFFFFFFFF +#define ENTRY_ADDRH_ADDRH 32 +#define ENTRY_ADDRH_ADDRH_FIELD (0xFFFFFFFFUL << ENTRY_ADDRH_ADDRH) + +#define ENTRY_CFG_R 0 +#define ENTRY_CFG_W 1 +#define ENTRY_CFG_X 2 +#define ENTRY_CFG_A 3 +#define ENTRY_CFG_A_FIELD (0x3 << ENTRY_CFG_A) + +#define IOPMP_MODEL_FULL 0 +#define IOPMP_MODEL_RAPIDK 0x1 +#define IOPMP_MODEL_DYNAMICK 0x2 +#define IOPMP_MODEL_ISOLATION 0x3 +#define IOPMP_MODEL_COMPACTK 0x4 +#define IOPMP_MODEL_K 8 + +#define TOR_EN 1 +#define SPS_EN 0 +#define USER_CFG_EN 0 +#define PROG_PRIENT 1 +#define PRIO_ENTRY IOPMP_MAX_ENTRY_NUM +#define SID_TRANSL_EN 0 +#define SID_TRANSL 0 + +#define ENTRY_NO_HIT 0 +#define ENTRY_PAR_HIT 1 +#define ENTRY_HIT 2 + +#define AXI_BURST_TYPE_FIX 0 +#define AXI_BURST_TYPE_INC 1 + +typedef enum { + IOPMP_READ = 1 << 0, + IOPMP_WRITE = 1 << 1, + IOPMP_EXEC = 1 << 2, + IOPMP_ADDRMODE = 1 << 3, +} iopmp_priv_t; + +typedef enum { + IOPMP_AMATCH_OFF, /* Null (off) */ + IOPMP_AMATCH_TOR, /* Top of Range */ + IOPMP_AMATCH_NA4, /* Naturally aligned four-byte region */ + IOPMP_AMATCH_NAPOT /* Naturally aligned power-of-two region */ +} iopmp_am_t; + +typedef struct { + uint64_t addr_reg; + uint32_t cfg_reg; +} iopmp_entry_t; + +typedef struct { + target_ulong sa; + target_ulong ea; +} iopmp_addr_t; + +typedef struct { + uint64_t srcmd_en[IOPMP_MAX_SID_NUM]; + uint64_t srcmd_r[IOPMP_MAX_SID_NUM]; + uint64_t srcmd_w[IOPMP_MAX_SID_NUM]; + uint32_t mdcfg[IOPMP_MAX_MD_NUM]; + iopmp_entry_t entry[IOPMP_MAX_ENTRY_NUM]; + uint64_t mdmsk; + uint64_t mdlck; + uint32_t entrylck; + uint32_t mdcfglck; + uint32_t arrlck; + uint64_t mdstall; + uint32_t sidscp; + uint32_t errreact; + uint64_t err_reqaddr; + uint32_t err_reqsid; + uint32_t err_reqinfo; +} iopmp_regs; + +/* To verfiy the same transcation */ +typedef struct iopmp_transaction_state { + bool supported; + bool running; + hwaddr start_addr; + hwaddr end_addr; +} iopmp_transaction_state; + +typedef struct iopmp_error_info { + uint32_t reqinfo; + hwaddr start_addr; + hwaddr end_addr; +} iopmp_error_info; + +typedef struct Iopmp_StreamSink { + Object parent; +} Iopmp_StreamSink; + +typedef struct IopmpState { + SysBusDevice parent_obj; + iopmp_addr_t entry_addr[IOPMP_MAX_ENTRY_NUM]; + iopmp_transaction_state transaction_state[IOPMP_MAX_SID_NUM]; + QemuMutex iopmp_transaction_mutex; + iopmp_error_info prev_error_info[IOPMP_MAX_SID_NUM]; + MemoryRegion mmio; + IOMMUMemoryRegion iommu; + IOMMUMemoryRegion *next_iommu; + iopmp_regs regs; + MemoryRegion *downstream; + MemoryRegion blocked_io; + MemoryRegion stall_io; + char *model_str; + uint32_t model; + uint32_t k; + bool sps_en; + bool sid_transl_prog; + bool prient_prog; + bool sid_transl_en; + uint32_t sid_transl; + bool on_axi_bus; + Iopmp_StreamSink transaction_info_sink; + + AddressSpace iopmp_as; + AddressSpace downstream_as; + AddressSpace blocked_io_as; + AddressSpace stall_io_as; + qemu_irq irq; + bool enable; + uint32_t sidscp_op[IOPMP_MAX_SID_NUM]; + uint64_t md_stall_stat; + uint32_t prio_entry; + + uint32_t sid_num; + uint32_t md_num; + uint32_t entry_num; +} IopmpState; + +DeviceState *iopmp_create(hwaddr addr, qemu_irq irq); +void cascade_iopmp(DeviceState *cur_dev, DeviceState *next_dev); + +#endif diff --git a/include/hw/misc/riscv_iopmp_transaction_info.h b/include/hw/misc/riscv_iopmp_transaction_info.h new file mode 100644 index 0000000000..fd12fd214c --- /dev/null +++ b/include/hw/misc/riscv_iopmp_transaction_info.h @@ -0,0 +1,28 @@ +/* + * QEMU RISC-V IOPMP (Input Output Physical Memory Protection) + * + * Copyright (c) 2023 Andes Tech. Corp. + * + * 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_TRANSACTION_INFO_H +#define RISCV_IOPMP_TRANSACTION_INFO_H + +typedef struct { + uint32_t sid:16; + uint64_t start_addr; + uint64_t end_addr; +} iopmp_transaction_info; + +#endif -- 2.34.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v3 2/4] Add RISC-V IOPMP support 2023-11-14 9:47 ` [PATCH v3 2/4] Add RISC-V IOPMP support Ethan Chen via @ 2023-11-21 5:38 ` Alistair Francis 0 siblings, 0 replies; 14+ messages in thread From: Alistair Francis @ 2023-11-21 5:38 UTC (permalink / raw) To: Ethan Chen Cc: qemu-devel, peter.maydell, edgar.iglesias, richard.henderson, pbonzini, palmer, alistair.francis, in.meng, liweiwei, dbarboza, hiwei_liu, qemu-riscv, peterx, david On Tue, Nov 14, 2023 at 7:48 PM Ethan Chen via <qemu-devel@nongnu.org> wrote: > > Support specification Version 1.0.0-draft4 rapid-k model. Thanks for the patch. Can you add some more information here. It would be great if you can link to the spec and explain the implementation a little bit > > Signed-off-by: Ethan Chen <ethan84@andestech.com> > --- > hw/misc/Kconfig | 4 + > hw/misc/meson.build | 1 + > hw/misc/riscv_iopmp.c | 967 ++++++++++++++++++ > include/hw/misc/riscv_iopmp.h | 342 +++++++ > .../hw/misc/riscv_iopmp_transaction_info.h | 28 + > 5 files changed, 1342 insertions(+) > create mode 100644 hw/misc/riscv_iopmp.c > create mode 100644 include/hw/misc/riscv_iopmp.h > create mode 100644 include/hw/misc/riscv_iopmp_transaction_info.h > > diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig > index cc8a8c1418..953569e682 100644 > --- a/hw/misc/Kconfig > +++ b/hw/misc/Kconfig > @@ -200,4 +200,8 @@ config IOSB > config XLNX_VERSAL_TRNG > bool > > +config RISCV_IOPMP > + bool > + select STREAM > + > source macio/Kconfig > diff --git a/hw/misc/meson.build b/hw/misc/meson.build > index 36c20d5637..86b81e1690 100644 > --- a/hw/misc/meson.build > +++ b/hw/misc/meson.build > @@ -35,6 +35,7 @@ system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c')) > 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')) > > subdir('macio') > > diff --git a/hw/misc/riscv_iopmp.c b/hw/misc/riscv_iopmp.c > new file mode 100644 > index 0000000000..99d0a554dd > --- /dev/null > +++ b/hw/misc/riscv_iopmp.c > @@ -0,0 +1,967 @@ > +/* > + * QEMU RISC-V IOPMP (Input Output Physical Memory Protection) > + * > + * Copyright (c) 2023 Andes Tech. Corp. > + * > + * 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.h" > +#include "memory.h" > +#include "hw/irq.h" > + > +#define TYPE_IOPMP_IOMMU_MEMORY_REGION "iopmp-iommu-memory-region" > +#define TYPE_IOPMP_TRASACTION_INFO_SINK "iopmp_transaction_info_sink" > + > +DECLARE_INSTANCE_CHECKER(Iopmp_StreamSink, IOPMP_TRASACTION_INFO_SINK, > + TYPE_IOPMP_TRASACTION_INFO_SINK) > +#define LOGGE(x...) qemu_log_mask(LOG_GUEST_ERROR, x) > +#define xLOG(x...) > +#define yLOG(x...) qemu_log(x) > +#ifdef DEBUG_RISCV_IOPMP > + #define LOG(x...) yLOG(x) > +#else > + #define LOG(x...) xLOG(x) > +#endif We should use QEMU's trace features instead of debug macros. You can see more details in the QEMU docs: https://www.qemu.org/docs/master/devel/tracing.html > + > +#define MEMTX_IOPMP_STALL (1 << 3) > + > + > +static void iopmp_decode_napot(target_ulong a, target_ulong *sa, > + target_ulong *ea) > +{ > + /* > + * aaaa...aaa0 8-byte NAPOT range > + * aaaa...aa01 16-byte NAPOT range > + * aaaa...a011 32-byte NAPOT range > + * ... > + * aa01...1111 2^XLEN-byte NAPOT range > + * a011...1111 2^(XLEN+1)-byte NAPOT range > + * 0111...1111 2^(XLEN+2)-byte NAPOT range > + * 1111...1111 Reserved > + */ > + > + a = (a << 2) | 0x3; > + *sa = a & (a + 1); > + *ea = a | (a + 1); > +} > + > +static void iopmp_update_rule(IopmpState *s, uint32_t entry_index) > +{ > + uint8_t this_cfg = s->regs.entry[entry_index].cfg_reg; > + target_ulong this_addr = s->regs.entry[entry_index].addr_reg; > + target_ulong prev_addr = 0u; > + target_ulong sa = 0u; > + target_ulong ea = 0u; > + > + if (entry_index >= 1u) { > + prev_addr = s->regs.entry[entry_index - 1].addr_reg; > + } > + > + switch (iopmp_get_field(this_cfg, ENTRY_CFG_A)) { > + case IOPMP_AMATCH_OFF: > + sa = 0u; > + ea = -1; > + break; > + > + case IOPMP_AMATCH_TOR: > + sa = (prev_addr) << 2; /* shift up from [xx:0] to [xx+2:2] */ > + ea = ((this_addr) << 2) - 1u; > + if (sa > ea) { > + sa = ea = 0u; > + } > + break; > + > + case IOPMP_AMATCH_NA4: > + sa = this_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ > + ea = (sa + 4u) - 1u; > + break; > + > + case IOPMP_AMATCH_NAPOT: > + iopmp_decode_napot(this_addr, &sa, &ea); > + break; > + > + default: > + sa = 0u; > + ea = 0u; > + break; > + } > + > + s->entry_addr[entry_index].sa = sa; > + s->entry_addr[entry_index].ea = ea; > +} > + > +static uint64_t iopmp_read(void *opaque, hwaddr addr, unsigned size) > +{ > + IopmpState *s = IOPMP(opaque); > + uint32_t rz = 0; > + uint32_t is_stall = 0; > + uint32_t sid; > + switch (addr) { > + case IOPMP_VERSION ... IOPMP_USER_CFG0 + 16 * (IOPMP_MAX_ENTRY_NUM - 1): > + switch (addr) { > + case IOPMP_VERSION: > + rz = VENDER_ANDES << VERSION_VENDOR | > + SPECVER_1_0_0_DRAFT4 << VERSION_SPECVER; > + break; > + case IOPMP_IMP: > + rz = IMPID_1_0_0_DRAFT4_0; > + break; > + case IOPMP_HWCFG0: /* RO */ > + rz = s->sid_num << HWCFG0_SID_NUM | > + s->entry_num << HWCFG0_ENTRY_NUM; > + break; > + case IOPMP_HWCFG1: > + rz = s->model << HWCFG1_MODEL | TOR_EN << HWCFG1_TOR_EN | > + s->sps_en << HWCFG1_SPS_EN | > + USER_CFG_EN << HWCFG1_USER_CFG_EN | > + s->prient_prog << HWCFG1_PRIENT_PROG | > + s->sid_transl_en << HWCFG1_SID_TRANSL_EN | > + s->sid_transl_prog << HWCFG1_SID_TRANSL_PROG | > + s->md_num << HWCFG1_MD_NUM | > + s->enable << HWCFG1_ENABLE; > + break; > + case IOPMP_HWCFG2: > + rz = s->prio_entry << HWCFG2_PRIO_ENTRY | > + s->sid_transl << HWCFG2_SID_TRANSL; > + break; > + case IOPMP_ENTRYOFFSET: > + rz = IOPMP_ENTRY_ADDR0; > + break; > + case IOPMP_ERRREACT: > + rz = s->regs.errreact; > + break; > + case IOPMP_MDSTALL: > + if (s->md_stall_stat) { > + is_stall = 1; > + } > + rz = iopmp_get_field(s->regs.mdstall, MDSTALL_MD) | is_stall; > + break; > + case IOPMP_MDSTALLH: > + rz = s->regs.mdstall >> 32; > + break; > + case IOPMP_SIDSCP: > + sid = iopmp_get_field(s->regs.sidscp, SIDSCP_SID); > + if (sid < s->sid_num) { > + rz = sid | (s->sidscp_op[sid]) << SIDSCP_STAT; > + } else { > + rz = sid | 3 << SIDSCP_STAT; > + } > + break; > + case IOPMP_MDLCK: > + rz = s->regs.mdlck & UINT32_MAX; > + break; > + case IOPMP_MDLCKH: > + rz = s->regs.mdlck >> 32; > + break; > + case IOPMP_MDCFGLCK: > + rz = s->regs.mdcfglck; > + break; > + case IOPMP_ENTRYLCK: > + rz = s->regs.entrylck; > + break; > + case IOPMP_ERR_REQADDR: > + rz = s->regs.err_reqaddr & UINT32_MAX; > + break; > + case IOPMP_ERR_REQADDRH: > + rz = s->regs.err_reqaddr >> 32; > + break; > + case IOPMP_ERR_REQSID: > + rz = s->regs.err_reqsid; > + break; > + case IOPMP_ERR_REQINFO: > + rz = s->regs.err_reqinfo; > + break; > + > + default: > + if (addr >= IOPMP_MDCFG0 && > + addr < IOPMP_MDCFG0 + 4 * (s->md_num - 1)) { > + int offset = addr - IOPMP_MDCFG0; > + int idx = offset >> 2; > + if (idx == 0) { > + if (offset == 0) { > + rz = s->regs.mdcfg[idx]; > + } else { > + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); > + } > + } else { > + /* Only MDCFG0 is implemented */ > + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); > + } > + } else if (addr >= IOPMP_SRCMD_EN0 && > + addr < IOPMP_SRCMD_WH0 + 32 * (s->sid_num - 1)) { > + int offset = addr - IOPMP_SRCMD_EN0; > + int idx = offset >> 5; > + offset &= 0x1f; > + if (offset == 0) { > + rz = s->regs.srcmd_en[idx] & UINT32_MAX; > + } else if (offset == 4) { > + rz = s->regs.srcmd_en[idx] >> 32; > + } else if (offset == 8) { > + rz = s->regs.srcmd_r[idx] & UINT32_MAX; > + } else if (offset == 12) { > + rz = s->regs.srcmd_r[idx] >> 32; > + } else if (offset == 16) { > + rz = s->regs.srcmd_w[idx] & UINT32_MAX; > + } else if (offset == 24) { > + rz = s->regs.srcmd_w[idx] >> 32; > + } else { > + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); > + } > + } else if (addr >= IOPMP_ENTRY_ADDR0 && > + addr < IOPMP_USER_CFG0 + 16 * (s->entry_num - 1)) { > + int offset = addr - IOPMP_ENTRY_ADDR0; > + int idx = offset >> 4; > + offset &= 0xf; > + if (offset == 0) { > + rz = s->regs.entry[idx].addr_reg & UINT32_MAX; > + } else if (offset == 4) { > + rz = s->regs.entry[idx].addr_reg >> 32; > + } else if (offset == 8) { > + rz = s->regs.entry[idx].cfg_reg; > + } else if (offset == 12) { > + /* Not supported user customized permission*/ > + rz = 0; > + } else { > + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); > + } > + } else { > + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); > + } > + break; > + } > + LOG("\e[95m%s: addr %08x, value %08x\e[0m\n", __func__, (int)addr, > + (int)rz); > + break; > + default: > + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); > + } > + > + return rz; > +} > + > +static void > +iopmp_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) > +{ > + IopmpState *s = IOPMP(opaque); > + int value_f; > + int reg_f; > + uint32_t sid, op; > + > + switch (addr) { > + case IOPMP_VERSION ... IOPMP_USER_CFG0 + 16 * (IOPMP_MAX_ENTRY_NUM - 1): > + switch (addr) { > + case IOPMP_VERSION: /* RO */ > + break; > + case IOPMP_IMP: /* RO */ > + break; > + case IOPMP_HWCFG0: /* RO */ > + break; > + case IOPMP_HWCFG1: > + if (iopmp_get_field(value, HWCFG1_PRIENT_PROG)) { > + /* W1C */ > + s->prient_prog = 0; > + } > + if (iopmp_get_field(value, HWCFG1_SID_TRANSL_PROG)) { > + /* W1C */ > + s->sid_transl_prog = 0; > + } > + if (iopmp_get_field(value, HWCFG1_ENABLE)) { > + /* W1S */ > + s->enable = 1; > + } > + break; > + case IOPMP_HWCFG2: > + if (s->prient_prog) { > + s->prio_entry = iopmp_get_field(value, HWCFG2_PRIO_ENTRY); > + } > + if (s->sid_transl_en && s->sid_transl_prog) { > + s->sid_transl = iopmp_get_field(value, HWCFG2_SID_TRANSL); > + } > + break; > + case IOPMP_ERRREACT: > + if (!iopmp_get_field(s->regs.errreact, ERRREACT_L)) { > + iopmp_set_field32(&s->regs.errreact, ERRREACT_L, > + iopmp_get_field(value, ERRREACT_L)); > + if (iopmp_get_field(value, ERRREACT_IP)) { > + iopmp_set_field32(&s->regs.errreact, ERRREACT_IP, 0); > + } > + iopmp_set_field32(&s->regs.errreact, ERRREACT_IE, > + iopmp_get_field(value, ERRREACT_IE)); > + iopmp_set_field32(&s->regs.errreact, ERRREACT_IRE, > + iopmp_get_field(value, ERRREACT_IRE)); > + iopmp_set_field32(&s->regs.errreact, ERRREACT_RRE, > + iopmp_get_field(value, ERRREACT_RRE)); > + iopmp_set_field32(&s->regs.errreact, ERRREACT_IWE, > + iopmp_get_field(value, ERRREACT_IWE)); > + iopmp_set_field32(&s->regs.errreact, ERRREACT_RWE, > + iopmp_get_field(value, ERRREACT_RWE)); > + iopmp_set_field32(&s->regs.errreact, ERRREACT_PEE, > + iopmp_get_field(value, ERRREACT_PEE)); > + iopmp_set_field32(&s->regs.errreact, ERRREACT_RPE, > + iopmp_get_field(value, ERRREACT_RPE)); > + } else { > + if (iopmp_get_field(value, ERRREACT_IP)) { > + iopmp_set_field32(&s->regs.errreact, ERRREACT_IP, 0); > + } > + } > + break; > + case IOPMP_MDSTALL: > + iopmp_set_field64(&s->regs.mdstall, MDSTALL, value); > + break; > + case IOPMP_MDSTALLH: > + iopmp_set_field64(&s->regs.mdstall, MDSTALLH, value); > + break; > + case IOPMP_SIDSCP: > + sid = iopmp_get_field(value, SIDSCP_SID); > + op = iopmp_get_field(value, SIDSCP_OP); > + if (sid < s->sid_num) { > + if (op != SIDSCP_OP_QUERY) { > + s->sidscp_op[sid] = op; > + s->regs.sidscp = value; > + } > + } else { > + s->regs.sidscp = sid | (0x3 << SIDSCP_OP); > + } > + break; > + case IOPMP_MDLCK: > + if (!(s->regs.mdlck & (1 << MDLCK_L))) { > + s->regs.mdlck = value | > + (s->regs.mdstall & ~(uint64_t)UINT32_MAX); > + } > + break; > + case IOPMP_MDLCKH: > + if (!(s->regs.mdlck & (1 << MDLCK_L))) { > + s->regs.mdlck = (uint64_t)value << 32 | > + (s->regs.mdstall & UINT32_MAX); > + } > + break; > + case IOPMP_MDCFGLCK: > + if (!iopmp_get_field(s->regs.mdcfglck, MDCFGLCK_L)) { > + value_f = iopmp_get_field(value, MDCFGLCK_F); > + reg_f = iopmp_get_field(s->regs.mdcfglck, MDCFGLCK_F); > + if (value_f > reg_f) { > + iopmp_set_field32(&s->regs.mdcfglck, MDCFGLCK_F, value_f); > + } > + iopmp_set_field32(&s->regs.mdcfglck, MDCFGLCK_L, > + iopmp_get_field(value, MDCFGLCK_L)); > + } > + break; > + case IOPMP_ENTRYLCK: > + if (!(iopmp_get_field(s->regs.entrylck, ENTRYLCK_L))) { > + value_f = iopmp_get_field(value, ENTRYLCK_F); > + reg_f = iopmp_get_field(s->regs.entrylck, ENTRYLCK_F); > + if (value_f > reg_f) { > + iopmp_set_field32(&s->regs.entrylck, ENTRYLCK_F, value_f); > + } > + iopmp_set_field32(&s->regs.entrylck, ENTRYLCK_F, > + iopmp_get_field(value, ENTRYLCK_F)); > + } > + case IOPMP_ERR_REQADDR: /* RO */ > + break; > + case IOPMP_ERR_REQADDRH: /* RO */ > + break; > + case IOPMP_ERR_REQSID: /* RO */ > + break; > + case IOPMP_ERR_REQINFO: /* RO */ > + break; > + > + default: > + if (addr >= IOPMP_MDCFG0 && > + addr < IOPMP_MDCFG0 + 4 * (s->md_num - 1)) { > + int offset = addr - IOPMP_MDCFG0; > + int idx = offset >> 2; > + /* RO in rapid-k model */ > + if (idx > 0) { > + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); > + } > + } else if (addr >= IOPMP_SRCMD_EN0 && > + addr < IOPMP_SRCMD_WH0 + 32 * (s->sid_num - 1)) { > + int offset = addr - IOPMP_SRCMD_EN0; > + int idx = offset >> 5; > + offset &= 0x1f; > + if (offset % 4) { > + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); > + } else if (iopmp_get_field(s->regs.srcmd_en[idx], > + SRCMD_EN_L) == 0) { > + if (offset == 0) { > + iopmp_set_field64(&s->regs.srcmd_en[idx], SRCMD_EN_MD, > + iopmp_get_field(value, SRCMD_EN_MD)); > + iopmp_set_field64(&s->regs.srcmd_en[idx], SRCMD_EN_L, > + iopmp_get_field(value, SRCMD_EN_L)); > + } else if (offset == 4) { > + iopmp_set_field64(&s->regs.srcmd_en[idx], SRCMD_ENH_MDH, > + value); > + } else if (offset == 8 && s->sps_en) { > + iopmp_set_field64(&s->regs.srcmd_r[idx], SRCMD_R_MD, > + iopmp_get_field(value, SRCMD_R_MD)); > + } else if (offset == 12 && s->sps_en) { > + iopmp_set_field64(&s->regs.srcmd_r[idx], SRCMD_RH_MDH, > + value); > + } else if (offset == 16 && s->sps_en) { > + iopmp_set_field64(&s->regs.srcmd_w[idx], SRCMD_W_MD, > + iopmp_get_field(value, SRCMD_W_MD)); > + } else if (offset == 24 && s->sps_en) { > + iopmp_set_field64(&s->regs.srcmd_w[idx], SRCMD_WH_MDH, > + value); > + } > + } > + } else if (addr >= IOPMP_ENTRY_ADDR0 && > + addr < IOPMP_USER_CFG0 + 16 * (s->entry_num - 1)) { > + int offset = addr - IOPMP_ENTRY_ADDR0; > + int idx = offset >> 4; > + offset &= 0xf; > + if (offset == 0) { > + iopmp_set_field64(&s->regs.entry[idx].addr_reg, > + ENTRY_ADDR_ADDR, value); > + } else if (offset == 4) { > + iopmp_set_field64(&s->regs.entry[idx].addr_reg, > + ENTRY_ADDRH_ADDRH, value); > + } else if (offset == 8) { > + s->regs.entry[idx].cfg_reg = value; > + } else if (offset == 12) { > + /* Not supported user customized permission*/ > + ; > + } else { > + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); > + } > + iopmp_update_rule(s, idx); > + } else { > + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); > + } > + /* If IOPMP permission of any addr has been changed, */ > + /* flush TLB pages. */ > + tlb_flush_all_cpus_synced(current_cpu); > + break; > + } > + LOG("\e[95m%s: addr %08x, value %08x\e[0m\n", __func__, (int)addr, > + (int)rz); > + break; > + default: > + LOGGE("%s: Bad addr %x\n", __func__, (int)addr); > + } > +} > + > +/* Match entry in memory domain */ > +static int match_entry_md(IopmpState *s, int md_idx, hwaddr s_addr, > + hwaddr e_addr, int *entry_idx) > +{ > + int entry_idx_s, entry_idx_e; > + int result = ENTRY_NO_HIT; > + int i = 0; > + entry_idx_s = md_idx * s->regs.mdcfg[0]; > + entry_idx_e = (md_idx + 1) * s->regs.mdcfg[0]; > + if (entry_idx_s >= s->entry_num) { > + return result; > + } > + if (entry_idx_e > s->entry_num) { > + entry_idx_e = s->entry_num; > + } > + i = entry_idx_s; > + while (i < entry_idx_e) { > + if (s_addr >= s->entry_addr[i].sa && s_addr <= s->entry_addr[i].ea) { > + /* check end address */ > + if (e_addr >= s->entry_addr[i].sa && > + e_addr <= s->entry_addr[i].ea) { > + *entry_idx = i; > + return ENTRY_HIT; > + } else if (i >= s->prio_entry) { > + /* record result and continue for non-prio_entry */ > + result = ENTRY_PAR_HIT; > + continue; > + } else { > + return ENTRY_PAR_HIT; > + } > + } > + i++; > + } > + return result; > +} > + > +static int match_entry(IopmpState *s, int sid, hwaddr s_addr, hwaddr e_addr, > + int *match_md_idx, int *match_entry_idx) > +{ > + int cur_result = ENTRY_NO_HIT; > + int result = ENTRY_NO_HIT; > + uint64_t srcmd_en = s->regs.srcmd_en[sid] >> 1; > + for (int md_idx = 0; md_idx < s->md_num; md_idx++) { > + if (srcmd_en & (1ULL << md_idx)) { > + cur_result = match_entry_md(s, md_idx, s_addr, e_addr, > + match_entry_idx); > + if (cur_result == ENTRY_HIT) { > + *match_md_idx = md_idx; > + return cur_result; > + } > + if (cur_result > result) { > + result = cur_result; > + } > + } > + } > + return result; > +} > + > +static bool check_md_stall(IopmpState *s, int md_idx) > +{ > + uint64_t md_selected = iopmp_get_field(s->regs.mdstall, MDSTALL_MD) & > + (1 << md_idx); > + if (iopmp_get_field(s->regs.mdstall, MDSTALL_EXEMPT)) { > + return !md_selected; > + } else { > + return md_selected; > + } > +} > + > +static inline bool check_sidscp_stall(IopmpState *s, int sid) > +{ > + return s->sidscp_op[sid] == SIDSCP_OP_STALL; > +} > + > +static void iopmp_error_reaction(IopmpState *s, uint32_t id, hwaddr start, > + hwaddr end, uint32_t info) > +{ > + if (start == s->prev_error_info[id].start_addr && > + end == s->prev_error_info[id].end_addr && > + info == s->prev_error_info[id].reqinfo) { > + /* skip following error */ > + ; > + } else { > + s->prev_error_info[id].start_addr = start; > + s->prev_error_info[id].end_addr = end; > + s->prev_error_info[id].reqinfo = info; > + if (!iopmp_get_field(s->regs.errreact, ERRREACT_IP)) { > + iopmp_set_field32(&s->regs.errreact, ERRREACT_IP, 1); > + s->regs.err_reqsid = id; > + s->regs.err_reqaddr = start; > + s->regs.err_reqinfo = info; > + > + if (iopmp_get_field(info, ERR_REQINFO_TYPE) == ERR_REQINFO_TYPE_READ > + && iopmp_get_field(s->regs.errreact, ERRREACT_IE) && > + iopmp_get_field(s->regs.errreact, ERRREACT_IRE)) { > + qemu_set_irq(s->irq, 1); > + } > + if (iopmp_get_field(info, ERR_REQINFO_TYPE) == > + ERR_REQINFO_TYPE_WRITE && > + iopmp_get_field(s->regs.errreact, ERRREACT_IE) && > + iopmp_get_field(s->regs.errreact, ERRREACT_IWE)) { > + qemu_set_irq(s->irq, 1); > + } > + } > + } > +} > + > +static IOMMUTLBEntry iopmp_translate(IOMMUMemoryRegion *iommu, hwaddr addr, > + IOMMUAccessFlags flags, int iommu_idx) > +{ > + bool is_stalled = false; > + int sid = iommu_idx; > + IopmpState *s = IOPMP(container_of(iommu, IopmpState, iommu)); > + hwaddr start_addr, end_addr; > + if (s->transaction_state[sid].supported) { > + /* get transaction_state if device supported */ > + start_addr = s->transaction_state[sid].start_addr; > + end_addr = s->transaction_state[sid].end_addr; > + if (addr > end_addr || addr < start_addr || > + !s->transaction_state[sid].running) { > + error_report("transaction_state error."); > + exit(1); > + } > + } else { > + start_addr = addr; > + end_addr = addr; > + } > + IOMMUTLBEntry entry = { > + .target_as = &s->downstream_as, > + .iova = addr, > + .translated_addr = addr, > + .addr_mask = (~(hwaddr)0), > + .perm = IOMMU_NONE, > + }; > + int entry_idx = -1; > + int md_idx = -1; > + int result = match_entry(s, sid, start_addr, end_addr, &md_idx, &entry_idx); > + int srcmd_rw; > + if (result == ENTRY_HIT) { > + is_stalled = check_md_stall(s, md_idx) || check_sidscp_stall(s, sid); > + if (is_stalled) { > + s->md_stall_stat |= (1 << md_idx); > + entry.target_as = &s->stall_io_as; > + entry.perm = IOMMU_RW; > + return entry; > + } else { > + s->md_stall_stat &= ~(1 << md_idx); > + } > + entry.perm = s->regs.entry[entry_idx].cfg_reg & 0x7; > + if (s->sps_en) { > + /* do not affect x permission */ > + srcmd_rw = 0x4 | ((s->regs.srcmd_r[sid] >> > + (md_idx + SRCMD_R_MD)) & 0x1); > + srcmd_rw |= ((s->regs.srcmd_w[sid] >> > + (md_idx + SRCMD_W_MD)) & 0x1) << 1; > + entry.perm &= srcmd_rw; > + } > + if ((entry.perm & flags) == 0) { > + /* permission denied */ > + iopmp_error_reaction(s, sid, start_addr, end_addr, > + (entry_idx << ERR_REQINFO_EID) | > + ((flags - 1) << ERR_REQINFO_TYPE)); > + entry.target_as = &s->blocked_io_as; > + entry.perm = IOMMU_RW; > + } else { > + entry.addr_mask = s->entry_addr[entry_idx].ea - > + s->entry_addr[entry_idx].sa; > + /* clear error info */ > + s->prev_error_info[sid].reqinfo = 0; > + if (s->sid_transl_en) { > + /* next iopmp */ > + if (s->next_iommu) { > + int new_sid = s->sid_transl; > + IopmpState *next_s = IOPMP(container_of(s->next_iommu, > + IopmpState, iommu)); > + next_s->transaction_state[new_sid].supported = true; > + while (next_s->transaction_state[new_sid].running) { > + ; > + } > + qemu_mutex_lock(&next_s->iopmp_transaction_mutex); > + next_s->transaction_state[new_sid].running = 1; > + qemu_mutex_unlock(&next_s->iopmp_transaction_mutex); > + next_s->transaction_state[new_sid].start_addr = start_addr; > + next_s->transaction_state[new_sid].end_addr = end_addr; > + entry = iopmp_translate(s->next_iommu, addr, flags, > + s->sid_transl); > + qemu_mutex_lock(&next_s->iopmp_transaction_mutex); > + next_s->transaction_state[new_sid].running = 0; > + qemu_mutex_unlock(&next_s->iopmp_transaction_mutex); > + return entry; > + } else { > + error_report("Next iopmp is not found."); > + exit(1); This is code that the guest calls, we shouldn't allow the guest to kill QEMU like this. > + } > + } > + } > + } else { > + if (result == ENTRY_PAR_HIT) { > + iopmp_error_reaction(s, sid, start_addr, end_addr, > + (1 << ERR_REQINFO_PAR_HIT) | > + ((flags - 1) << ERR_REQINFO_TYPE)); > + } else { > + iopmp_error_reaction(s, sid, start_addr, end_addr, > + (1 << ERR_REQINFO_NO_HIT) | > + ((flags - 1) << ERR_REQINFO_TYPE)); > + } > + entry.target_as = &s->blocked_io_as; > + entry.perm = IOMMU_RW; > + } > + return entry; > +} > + > +static const MemoryRegionOps iopmp_ops = { > + .read = iopmp_read, > + .write = iopmp_write, > + .endianness = DEVICE_NATIVE_ENDIAN, > + .valid = {.min_access_size = 4, .max_access_size = 4} > +}; > + > +static MemTxResult iopmp_block_write(void *opaque, hwaddr addr, uint64_t value, > + unsigned size, MemTxAttrs attrs) > +{ > + IopmpState *s = IOPMP(opaque); > + > + switch (iopmp_get_field(s->regs.errreact, ERRREACT_RWE)) { > + case RWE_BUS_ERROR: > + return MEMTX_ERROR; > + break; > + case RWE_DECODE_ERROR: > + return MEMTX_DECODE_ERROR; > + break; > + case RWE_SUCCESS: > + return MEMTX_OK; > + break; > + default: > + break; > + } > + return MEMTX_OK; > +} > + > +static MemTxResult iopmp_block_read(void *opaque, hwaddr addr, uint64_t *pdata, > + unsigned size, MemTxAttrs attrs) > +{ > + IopmpState *s = IOPMP(opaque); > + > + switch (iopmp_get_field(s->regs.errreact, ERRREACT_RRE)) { > + case RRE_BUS_ERROR: > + return MEMTX_ERROR; > + break; > + case RRE_DECODE_ERROR: > + return MEMTX_DECODE_ERROR; > + break; > + case RRE_SUCCESS_ZEROS: > + *pdata = 0; > + return MEMTX_OK; > + break; > + case RRE_SUCCESS_ONES: > + *pdata = UINT64_MAX; > + return MEMTX_OK; > + break; > + default: > + break; > + } > + return MEMTX_OK; > +} > + > +static const MemoryRegionOps iopmp_block_io_ops = { > + .read_with_attrs = iopmp_block_read, > + .write_with_attrs = iopmp_block_write, > + .endianness = DEVICE_NATIVE_ENDIAN, > + .valid = {.min_access_size = 1, .max_access_size = 8}, > +}; > + > +static MemTxResult iopmp_handle_stall(IopmpState *s, hwaddr addr, > + MemTxAttrs attrs) > +{ > + return MEMTX_IOPMP_STALL; > +} > + > +static MemTxResult iopmp_stall_write(void *opaque, hwaddr addr, uint64_t value, > + unsigned size, MemTxAttrs attrs) > +{ > + IopmpState *s = IOPMP(opaque); > + > + return iopmp_handle_stall(s, addr, attrs); > +} > + > +static MemTxResult iopmp_stall_read(void *opaque, hwaddr addr, uint64_t *pdata, > + unsigned size, MemTxAttrs attrs) > +{ > + IopmpState *s = IOPMP(opaque); > + > + *pdata = 0; > + return iopmp_handle_stall(s, addr, attrs); > +} > + > +static const MemoryRegionOps iopmp_stall_io_ops = { > + .read_with_attrs = iopmp_stall_read, > + .write_with_attrs = iopmp_stall_write, > + .endianness = DEVICE_NATIVE_ENDIAN, > + .valid = {.min_access_size = 1, .max_access_size = 8}, > +}; > + > +static void iopmp_realize(DeviceState *dev, Error **errp) > +{ > + Object *obj = OBJECT(dev); > + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); > + IopmpState *s = IOPMP(dev); > + s->downstream = get_system_memory(); > + uint64_t size = memory_region_size(s->downstream); > + > + qemu_mutex_init(&s->iopmp_transaction_mutex); > + s->prient_prog = 1; > + s->sid_num = MIN(s->sid_num, IOPMP_MAX_SID_NUM); > + s->md_num = MIN(s->md_num, IOPMP_MAX_MD_NUM); > + s->entry_num = MIN(s->entry_num, IOPMP_MAX_ENTRY_NUM); > + if (s->sid_transl_en) { > + s->sid_transl_prog = 1; > + } > + if (!s->model_str || strcmp(s->model_str, "rapidk") == 0) { > + /* apply default model */ > + s->model = IOPMP_MODEL_RAPIDK; > + iopmp_set_field32(&s->regs.mdcfglck, MDCFGLCK_F, s->md_num); > + iopmp_set_field32(&s->regs.mdcfglck, MDCFGLCK_L, 1); > + s->regs.mdcfg[0] = s->k; > + } else { > + error_report("IOPMP model %s is not supported. " > + "Vailid values are full, rapidk, dynamick," > + "isolation and compactk.", s->model_str); > + exit(1); > + } > + memory_region_init_iommu(&s->iommu, sizeof(s->iommu), > + TYPE_IOPMP_IOMMU_MEMORY_REGION, > + obj, "iopmp-iommu", UINT64_MAX); > + address_space_init(&s->iopmp_as, MEMORY_REGION(&s->iommu), "iommu"); > + memory_region_init_io(&s->mmio, obj, &iopmp_ops, > + s, "iopmp-regs", 0x100000); > + sysbus_init_mmio(sbd, &s->mmio); > + memory_region_init_io(&s->blocked_io, obj, &iopmp_block_io_ops, > + s, "iopmp-blocked-io", size); > + address_space_init(&s->downstream_as, s->downstream, > + "iopmp-downstream-as"); > + address_space_init(&s->blocked_io_as, &s->blocked_io, > + "iopmp-blocked-io-as"); > + > + memory_region_init_io(&s->stall_io, obj, &iopmp_stall_io_ops, > + s, "iopmp-stall-io", size); > + address_space_init(&s->stall_io_as, &s->stall_io, > + "iopmp-stall-io-as"); > + > + object_initialize_child(OBJECT(s), "iopmp_transaction_info", > + &s->transaction_info_sink, > + TYPE_IOPMP_TRASACTION_INFO_SINK); > +} > + > +static void iopmp_reset(DeviceState *dev) > +{ > + IopmpState *s = IOPMP(dev); > + qemu_set_irq(s->irq, 0); > + memset(&s->regs, 0, sizeof(iopmp_regs)); > + memset(&s->entry_addr, 0, IOPMP_MAX_ENTRY_NUM * sizeof(iopmp_addr_t)); > + if (s->model == IOPMP_MODEL_RAPIDK) { > + iopmp_set_field32(&s->regs.mdcfglck, MDCFGLCK_F, s->md_num); > + iopmp_set_field32(&s->regs.mdcfglck, MDCFGLCK_L, 1); > + s->regs.mdcfg[0] = s->k; > + } > + s->regs.errreact = 0; > + > + s->prient_prog = 1; > + if (s->sid_transl_en) { > + s->sid_transl_prog = 1; > + } > +} > + > +static int iopmp_attrs_to_index(IOMMUMemoryRegion *iommu, MemTxAttrs attrs) > +{ > + unsigned int sid = attrs.requester_id; > + return sid; > +} > + > +static void iopmp_iommu_memory_region_class_init(ObjectClass *klass, void *data) > +{ > + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); > + > + imrc->translate = iopmp_translate; > + imrc->attrs_to_index = iopmp_attrs_to_index; > +} > + > +static Property iopmp_property[] = { > + DEFINE_PROP_STRING("model", IopmpState, model_str), > + DEFINE_PROP_BOOL("sps_en", IopmpState, sps_en, false), > + DEFINE_PROP_BOOL("sid_transl_en", IopmpState, sid_transl_en, false), > + DEFINE_PROP_UINT32("k", IopmpState, k, IOPMP_MODEL_K), > + DEFINE_PROP_UINT32("prio_entry", IopmpState, prio_entry, PRIO_ENTRY), > + DEFINE_PROP_UINT32("sid_num", IopmpState, sid_num, IOPMP_MAX_SID_NUM), > + DEFINE_PROP_UINT32("md_num", IopmpState, md_num, IOPMP_MAX_MD_NUM), > + DEFINE_PROP_UINT32("entry_num", IopmpState, entry_num, IOPMP_MAX_ENTRY_NUM), > + DEFINE_PROP_END_OF_LIST(), > +}; > + > +static void iopmp_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + device_class_set_props(dc, iopmp_property); > + dc->realize = iopmp_realize; > + dc->reset = iopmp_reset; > +} > + > +static void iopmp_init(Object *obj) > +{ > + IopmpState *s = IOPMP(obj); > + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); > + sysbus_init_irq(sbd, &s->irq); > +} > + > +static const TypeInfo iopmp_info = { > + .name = TYPE_IOPMP, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(IopmpState), > + .instance_init = iopmp_init, > + .class_init = iopmp_class_init, > +}; > + > +static const TypeInfo > +iopmp_iommu_memory_region_info = { > + .name = TYPE_IOPMP_IOMMU_MEMORY_REGION, > + .parent = TYPE_IOMMU_MEMORY_REGION, > + .class_init = iopmp_iommu_memory_region_class_init, > +}; > + > +DeviceState *iopmp_create(hwaddr addr, qemu_irq irq) > +{ > + LOG("%s:\n", __func__); > + DeviceState *iopmp_device = sysbus_create_varargs(TYPE_IOPMP, addr, irq, > + NULL); > + return iopmp_device; > +} Let's drop this function. These helper functions end up being a pain to maintain > + > +void > +cascade_iopmp(DeviceState *cur_dev, DeviceState *next_dev) > +{ > + IopmpState *s = IOPMP(cur_dev); > + s->sid_transl_en = true; > + IopmpState *next_s = IOPMP(next_dev); > + s->next_iommu = &next_s->iommu; > +} > + > +static size_t > +transaction_info_push(StreamSink *transaction_info_sink, unsigned char *buf, > + size_t len, bool eop) > +{ > + Iopmp_StreamSink *ss = IOPMP_TRASACTION_INFO_SINK(transaction_info_sink); > + IopmpState *s = IOPMP(container_of(ss, IopmpState, > + transaction_info_sink)); > + iopmp_transaction_info signal; > + memcpy(&signal, buf, len); > + uint32_t sid = signal.sid; > + if (s->transaction_state[sid].running) { > + if (eop) { > + qemu_mutex_lock(&s->iopmp_transaction_mutex); > + s->transaction_state[sid].running = 0; > + qemu_mutex_unlock(&s->iopmp_transaction_mutex); > + return 1; > + } else { > + return 0; > + } > + } else if (len == sizeof(iopmp_transaction_info)) { > + s->transaction_state[sid].supported = 1; > + qemu_mutex_lock(&s->iopmp_transaction_mutex); > + s->transaction_state[sid].running = 1; > + qemu_mutex_unlock(&s->iopmp_transaction_mutex); > + s->transaction_state[sid].start_addr = signal.start_addr; > + s->transaction_state[sid].end_addr = signal.end_addr; > + return 1; > + } > + return 0; I think a few more newlines would make this a little easier to read :) > +} > + > +static void iopmp_transaction_info_sink_class_init(ObjectClass *klass, > + void *data) > +{ > + StreamSinkClass *ssc = STREAM_SINK_CLASS(klass); > + ssc->push = transaction_info_push; > +} > + > +static const TypeInfo transaction_info_sink = { > + .name = TYPE_IOPMP_TRASACTION_INFO_SINK, > + .parent = TYPE_OBJECT, > + .instance_size = sizeof(Iopmp_StreamSink), > + .class_init = iopmp_transaction_info_sink_class_init, > + .interfaces = (InterfaceInfo[]) { > + { TYPE_STREAM_SINK }, > + { } > + }, > +}; > + > +static void > +iopmp_register_types(void) > +{ > + type_register_static(&iopmp_info); > + type_register_static(&iopmp_iommu_memory_region_info); > + type_register_static(&transaction_info_sink); > +} > + > +type_init(iopmp_register_types); > diff --git a/include/hw/misc/riscv_iopmp.h b/include/hw/misc/riscv_iopmp.h > new file mode 100644 > index 0000000000..7ff078c903 > --- /dev/null > +++ b/include/hw/misc/riscv_iopmp.h > @@ -0,0 +1,342 @@ > +/* > + * QEMU RISC-V IOPMP (Input Output Physical Memory Protection) > + * > + * Copyright (c) 2023 Andes Tech. Corp. > + * > + * 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_H > +#define RISCV_IOPMP_H > + > +#include "hw/sysbus.h" > +#include "qemu/typedefs.h" > +#include "memory.h" > +#include "hw/stream.h" > +#include "hw/misc/riscv_iopmp_transaction_info.h" > + > +#define TYPE_IOPMP "iopmp" > +#define IOPMP(obj) OBJECT_CHECK(IopmpState, (obj), TYPE_IOPMP) > + > +#define iopmp_get_field(reg, name) (((reg) & (name ## _FIELD)) >> (name)) > +#define iopmp_set_field32(reg, name, newval) do { \ > + uint32_t val = *reg; \ > + val &= ~name##_FIELD; \ > + val |= ((newval) << name) & name##_FIELD; \ > + *reg = val; \ > + } while (0) > +#define iopmp_set_field64(reg, name, newval) do { \ > + uint64_t val = *reg; \ > + val &= ~name##_FIELD; \ > + val |= ((newval) << name) & name##_FIELD; \ > + *reg = val; \ > + } while (0) I think it would be a lot simpler and easier to read if you use the existing register macros. Have a look at hw/ssi/ibex_spi_host.c for an example of using REG32() and FIELD() to define the registers and then FIELD_DP32() and FIELD_EX32() to access the registers. The registers should probably also be in the C file, as we don't need to export them. Alistair > + > + > +#define IOPMP_MAX_MD_NUM 63 > +#define IOPMP_MAX_SID_NUM 256 > +#define IOPMP_MAX_ENTRY_NUM 512 > + > +#define IOPMP_VERSION 0x0 > +#define IOPMP_IMP 0x4 > +#define IOPMP_HWCFG0 0x8 > +#define IOPMP_HWCFG1 0xC > +#define IOPMP_HWCFG2 0x10 > +#define IOPMP_ENTRYOFFSET 0x20 > +#define IOPMP_ERRREACT 0x28 > +#define IOPMP_MDSTALL 0x30 > +#define IOPMP_MDSTALLH 0x34 > +#define IOPMP_SIDSCP 0x38 > +#define IOPMP_MDLCK 0x40 > +#define IOPMP_MDLCKH 0x44 > +#define IOPMP_MDCFGLCK 0x48 > +#define IOPMP_ENTRYLCK 0x4C > + > +#define IOPMP_ERR_REQADDR 0x60 > +#define IOPMP_ERR_REQADDRH 0x64 > +#define IOPMP_ERR_REQSID 0x68 > +#define IOPMP_ERR_REQINFO 0x6C > + > +#define IOPMP_MDCFG0 0x800 > +#define IOPMP_SRCMD_EN0 0x1000 > +#define IOPMP_SRCMD_ENH0 0x1004 > +#define IOPMP_SRCMD_R0 0x1008 > +#define IOPMP_SRCMD_RH0 0x100C > +#define IOPMP_SRCMD_W0 0x1010 > +#define IOPMP_SRCMD_WH0 0x1014 > + > +#define IOPMP_ENTRY_ADDR0 0x4000 > +#define IOPMP_ENTRY_ADDRH0 0x4004 > +#define IOPMP_ENTRY_CFG0 0x4008 > +#define IOPMP_USER_CFG0 0x400C > + > +#define VERSION_VENDOR 0 > +#define VERSION_SPECVER 24 > +#define VENDER_ANDES 6533 > +#define SPECVER_1_0_0_DRAFT4 4 > + > +#define IMPID_1_0_0_DRAFT4_0 10040 > + > +#define HWCFG0_SID_NUM 0 > +#define HWCFG0_ENTRY_NUM 16 > + > +#define HWCFG1_MODEL 0 > +#define HWCFG1_TOR_EN 4 > +#define HWCFG1_SPS_EN 5 > +#define HWCFG1_USER_CFG_EN 6 > +#define HWCFG1_PRIENT_PROG 7 > +#define HWCFG1_SID_TRANSL_EN 8 > +#define HWCFG1_SID_TRANSL_PROG 9 > +#define HWCFG1_MD_NUM 24 > +#define HWCFG1_ENABLE 31 > + > +#define HWCFG1_SPS_EN_FIELD (1 << HWCFG1_SPS_EN) > +#define HWCFG1_PRIENT_PROG_FIELD (1 << HWCFG1_PRIENT_PROG) > +#define HWCFG1_SID_TRANSL_PROG_FIELD (1 << HWCFG1_SID_TRANSL_PROG) > +#define HWCFG1_ENABLE_FIELD (1 << HWCFG1_ENABLE) > + > +#define HWCFG2_PRIO_ENTRY 0 > +#define HWCFG2_SID_TRANSL 16 > + > +#define HWCFG2_PRIO_ENTRY_FIELD (0xFFFF << HWCFG2_PRIO_ENTRY) > +#define HWCFG2_SID_TRANSL_FIELD (0xFFFF << HWCFG2_SID_TRANSL) > + > +#define ERRREACT_L 0 > +#define ERRREACT_IE 1 > +#define ERRREACT_IP 2 > +#define ERRREACT_IRE 4 > +#define ERRREACT_RRE 5 > +#define ERRREACT_IWE 8 > +#define ERRREACT_RWE 9 > +#define ERRREACT_PEE 28 > +#define ERRREACT_RPE 29 > + > +#define ERRREACT_L_FIELD (0x1 << ERRREACT_L) > +#define ERRREACT_IE_FIELD (0x1 << ERRREACT_IE) > +#define ERRREACT_IP_FIELD (0x1 << ERRREACT_IP) > +#define ERRREACT_IRE_FIELD (0x1 << ERRREACT_IRE) > +#define ERRREACT_RRE_FIELD (0x7 << ERRREACT_RRE) > +#define ERRREACT_IWE_FIELD (0x1 << ERRREACT_IWE) > +#define ERRREACT_RWE_FIELD (0x7 << ERRREACT_RWE) > +#define ERRREACT_PEE_FIELD (0x1 << ERRREACT_PEE) > +#define ERRREACT_RPE_FIELD (0x7 << ERRREACT_RPE) > + > +#define RRE_BUS_ERROR 0 > +#define RRE_DECODE_ERROR 1 > +#define RRE_SUCCESS_ZEROS 2 > +#define RRE_SUCCESS_ONES 3 > + > +#define RWE_BUS_ERROR 0 > +#define RWE_DECODE_ERROR 1 > +#define RWE_SUCCESS 2 > + > +#define MDSTALL 0 > +#define MDSTALLH 32 > +#define MDSTALL_FIELD UINT32_MAX > +#define MDSTALLH_FIELD (UINT64_MAX << MDSTALLH) > +#define MDSTALL_EXEMPT 0 > +#define MDSTALL_EXEMPT_FIELD (1 << MDSTALL_EXEMPT) > +#define MDSTALL_ISSTALLED 0 > +#define MDSTALL_MD 1 > +#define MDSTALL_MD_FIELD (0x7FFFFFFFFFFFFFFF << MDSTALL_MD) > + > +#define SIDSCP_SID 0 > +#define SIDSCP_STAT 30 > +#define SIDSCP_OP 30 > +#define SIDSCP_SID_FIELD (0xFFFF << SIDSCP_SID) > +#define SIDSCP_STAT_FIELD (0x3 << SIDSCP_STAT) > +#define SIDSCP_OP_FIELD (0x3 << SIDSCP_OP) > +#define SIDSCP_OP_QUERY 0 > +#define SIDSCP_OP_STALL 1 > +#define SIDSCP_OP_NOTSTALL 2 > + > +#define MDLCK_L 0 > +#define MDLCK_MD 1 > + > +#define MDCFGLCK_L 0 > +#define MDCFGLCK_L_FIELD (0x1 << MDCFGLCK_L) > +#define MDCFGLCK_F 1 > +#define MDCFGLCK_F_FIELD (0x7F << MDCFGLCK_F) > + > +#define ENTRYLCK_L 0 > +#define ENTRYLCK_L_FIELD (0x1 << MDCFGLCK_L) > +#define ENTRYLCK_F 1 > +#define ENTRYLCK_F_FIELD (0xFFFF << ENTRYLCK_F) > + > +#define ERR_REQINFO_NO_HIT 0 > +#define ERR_REQINFO_PAR_HIT 1 > +#define ERR_REQINFO_TYPE 8 > +#define ERR_REQINFO_EID 16 > + > +#define ERR_REQINFO_NO_HIT_FIELD (0x1 << ERR_REQINFO_NO_HIT) > +#define ERR_REQINFO_PAR_HIT_FIELD (0x1 << ERR_REQINFO_PAR_HIT) > +#define ERR_REQINFO_TYPE_FIELD (0x3 << ERR_REQINFO_TYPE) > +#define ERR_REQINFO_EID_FIELD (0xFFFF << ERR_REQINFO_EID) > + > +#define ERR_REQINFO_TYPE_READ 0 > +#define ERR_REQINFO_TYPE_WRITE 1 > +#define ERR_REQINFO_TYPE_USER 3 > + > +#define SRCMD_EN_L 0 > +#define SRCMD_EN_MD 1 > +#define SRCMD_EN_L_FIELD (0x1 << SRCMD_EN_L) > +#define SRCMD_EN_MD_FIELD (0x7FFFFFFF << SRCMD_EN_MD) > +#define SRCMD_ENH_MDH 32 > +#define SRCMD_ENH_MDH_FIELD (0xFFFFFFFFUL << SRCMD_ENH_MDH) > + > +#define SRCMD_R_MD 1 > +#define SRCMD_R_MD_FIELD (0x7FFFFFFF << SRCMD_R_MD) > +#define SRCMD_RH_MDH 32 > +#define SRCMD_RH_MDH_FIELD (0xFFFFFFFFUL << SRCMD_RH_MDH) > +#define SRCMD_W_MD 1 > +#define SRCMD_W_MD_FIELD (0x7FFFFFFF << SRCMD_W_MD) > +#define SRCMD_WH_MDH 32 > +#define SRCMD_WH_MDH_FIELD (0xFFFFFFFFUL << SRCMD_WH_MDH) > + > +#define ENTRY_ADDR_ADDR 0 > +#define ENTRY_ADDR_ADDR_FIELD 0xFFFFFFFF > +#define ENTRY_ADDRH_ADDRH 32 > +#define ENTRY_ADDRH_ADDRH_FIELD (0xFFFFFFFFUL << ENTRY_ADDRH_ADDRH) > + > +#define ENTRY_CFG_R 0 > +#define ENTRY_CFG_W 1 > +#define ENTRY_CFG_X 2 > +#define ENTRY_CFG_A 3 > +#define ENTRY_CFG_A_FIELD (0x3 << ENTRY_CFG_A) > + > +#define IOPMP_MODEL_FULL 0 > +#define IOPMP_MODEL_RAPIDK 0x1 > +#define IOPMP_MODEL_DYNAMICK 0x2 > +#define IOPMP_MODEL_ISOLATION 0x3 > +#define IOPMP_MODEL_COMPACTK 0x4 > +#define IOPMP_MODEL_K 8 > + > +#define TOR_EN 1 > +#define SPS_EN 0 > +#define USER_CFG_EN 0 > +#define PROG_PRIENT 1 > +#define PRIO_ENTRY IOPMP_MAX_ENTRY_NUM > +#define SID_TRANSL_EN 0 > +#define SID_TRANSL 0 > + > +#define ENTRY_NO_HIT 0 > +#define ENTRY_PAR_HIT 1 > +#define ENTRY_HIT 2 > + > +#define AXI_BURST_TYPE_FIX 0 > +#define AXI_BURST_TYPE_INC 1 > + > +typedef enum { > + IOPMP_READ = 1 << 0, > + IOPMP_WRITE = 1 << 1, > + IOPMP_EXEC = 1 << 2, > + IOPMP_ADDRMODE = 1 << 3, > +} iopmp_priv_t; > + > +typedef enum { > + IOPMP_AMATCH_OFF, /* Null (off) */ > + IOPMP_AMATCH_TOR, /* Top of Range */ > + IOPMP_AMATCH_NA4, /* Naturally aligned four-byte region */ > + IOPMP_AMATCH_NAPOT /* Naturally aligned power-of-two region */ > +} iopmp_am_t; > + > +typedef struct { > + uint64_t addr_reg; > + uint32_t cfg_reg; > +} iopmp_entry_t; > + > +typedef struct { > + target_ulong sa; > + target_ulong ea; > +} iopmp_addr_t; > + > +typedef struct { > + uint64_t srcmd_en[IOPMP_MAX_SID_NUM]; > + uint64_t srcmd_r[IOPMP_MAX_SID_NUM]; > + uint64_t srcmd_w[IOPMP_MAX_SID_NUM]; > + uint32_t mdcfg[IOPMP_MAX_MD_NUM]; > + iopmp_entry_t entry[IOPMP_MAX_ENTRY_NUM]; > + uint64_t mdmsk; > + uint64_t mdlck; > + uint32_t entrylck; > + uint32_t mdcfglck; > + uint32_t arrlck; > + uint64_t mdstall; > + uint32_t sidscp; > + uint32_t errreact; > + uint64_t err_reqaddr; > + uint32_t err_reqsid; > + uint32_t err_reqinfo; > +} iopmp_regs; > + > +/* To verfiy the same transcation */ > +typedef struct iopmp_transaction_state { > + bool supported; > + bool running; > + hwaddr start_addr; > + hwaddr end_addr; > +} iopmp_transaction_state; > + > +typedef struct iopmp_error_info { > + uint32_t reqinfo; > + hwaddr start_addr; > + hwaddr end_addr; > +} iopmp_error_info; > + > +typedef struct Iopmp_StreamSink { > + Object parent; > +} Iopmp_StreamSink; > + > +typedef struct IopmpState { > + SysBusDevice parent_obj; > + iopmp_addr_t entry_addr[IOPMP_MAX_ENTRY_NUM]; > + iopmp_transaction_state transaction_state[IOPMP_MAX_SID_NUM]; > + QemuMutex iopmp_transaction_mutex; > + iopmp_error_info prev_error_info[IOPMP_MAX_SID_NUM]; > + MemoryRegion mmio; > + IOMMUMemoryRegion iommu; > + IOMMUMemoryRegion *next_iommu; > + iopmp_regs regs; > + MemoryRegion *downstream; > + MemoryRegion blocked_io; > + MemoryRegion stall_io; > + char *model_str; > + uint32_t model; > + uint32_t k; > + bool sps_en; > + bool sid_transl_prog; > + bool prient_prog; > + bool sid_transl_en; > + uint32_t sid_transl; > + bool on_axi_bus; > + Iopmp_StreamSink transaction_info_sink; > + > + AddressSpace iopmp_as; > + AddressSpace downstream_as; > + AddressSpace blocked_io_as; > + AddressSpace stall_io_as; > + qemu_irq irq; > + bool enable; > + uint32_t sidscp_op[IOPMP_MAX_SID_NUM]; > + uint64_t md_stall_stat; > + uint32_t prio_entry; > + > + uint32_t sid_num; > + uint32_t md_num; > + uint32_t entry_num; > +} IopmpState; > + > +DeviceState *iopmp_create(hwaddr addr, qemu_irq irq); > +void cascade_iopmp(DeviceState *cur_dev, DeviceState *next_dev); > + > +#endif > diff --git a/include/hw/misc/riscv_iopmp_transaction_info.h b/include/hw/misc/riscv_iopmp_transaction_info.h > new file mode 100644 > index 0000000000..fd12fd214c > --- /dev/null > +++ b/include/hw/misc/riscv_iopmp_transaction_info.h > @@ -0,0 +1,28 @@ > +/* > + * QEMU RISC-V IOPMP (Input Output Physical Memory Protection) > + * > + * Copyright (c) 2023 Andes Tech. Corp. > + * > + * 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_TRANSACTION_INFO_H > +#define RISCV_IOPMP_TRANSACTION_INFO_H > + > +typedef struct { > + uint32_t sid:16; > + uint64_t start_addr; > + uint64_t end_addr; > +} iopmp_transaction_info; > + > +#endif > -- > 2.34.1 > > ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v3 3/4] hw/dma: Add Andes ATCDMAC300 support 2023-11-14 9:47 [PATCH v3 0/4] Support RISC-V IOPMP Ethan Chen via 2023-11-14 9:47 ` [PATCH v3 1/4] hw/core: Add config stream Ethan Chen via 2023-11-14 9:47 ` [PATCH v3 2/4] Add RISC-V IOPMP support Ethan Chen via @ 2023-11-14 9:47 ` Ethan Chen via 2023-11-21 5:39 ` Alistair Francis 2023-11-14 9:47 ` [PATCH v3 4/4] hw/riscv/virt: Add IOPMP support Ethan Chen via 3 siblings, 1 reply; 14+ messages in thread From: Ethan Chen via @ 2023-11-14 9:47 UTC (permalink / raw) To: qemu-devel Cc: peter.maydell, edgar.iglesias, richard.henderson, pbonzini, palmer, alistair.francis, in.meng, liweiwei, dbarboza, hiwei_liu, qemu-riscv, peterx, david, Ethan Chen Signed-off-by: Ethan Chen <ethan84@andestech.com> --- hw/dma/Kconfig | 4 + hw/dma/atcdmac300.c | 566 ++++++++++++++++++++++++++++++++++++ hw/dma/meson.build | 1 + include/hw/dma/atcdmac300.h | 180 ++++++++++++ 4 files changed, 751 insertions(+) create mode 100644 hw/dma/atcdmac300.c create mode 100644 include/hw/dma/atcdmac300.h diff --git a/hw/dma/Kconfig b/hw/dma/Kconfig index 98fbb1bb04..a1d335b52f 100644 --- a/hw/dma/Kconfig +++ b/hw/dma/Kconfig @@ -30,3 +30,7 @@ config SIFIVE_PDMA config XLNX_CSU_DMA bool select REGISTER + +config ATCDMAC300 + bool + select STREAM diff --git a/hw/dma/atcdmac300.c b/hw/dma/atcdmac300.c new file mode 100644 index 0000000000..bc6012f44e --- /dev/null +++ b/hw/dma/atcdmac300.c @@ -0,0 +1,566 @@ +/* + * Andes ATCDMAC300 (Andes Technology DMA Controller) + * + * Copyright (c) 2022 Andes Tech. Corp. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/dma/atcdmac300.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "exec/memattrs.h" +#include "exec/address-spaces.h" +#include "hw/stream.h" +#include "hw/misc/riscv_iopmp_transaction_info.h" + +/* #define DEBUG_ANDES_ATCDMAC300 */ +#define LOGGE(x...) qemu_log_mask(LOG_GUEST_ERROR, x) +#define xLOG(x...) +#define yLOG(x...) qemu_log(x) +#ifdef DEBUG_ANDES_ATCDMAC300 + #define LOG(x...) yLOG(x) +#else + #define LOG(x...) xLOG(x) +#endif + +#define MEMTX_IOPMP_STALL (1 << 3) + +static void atcdmac300_dma_int_stat_update(ATCDMAC300State *s, int status, + int ch) +{ + s->IntStatus |= (1 << (status + ch)); +} + +static void atcdmac300_dma_reset_chan(ATCDMAC300State *s, int ch) +{ + if (s) { + s->chan[ch].ChnCtrl &= ~(1 << CHAN_CTL_ENABLE); + s->ChEN &= ~(1 << ch); + } +} + +static void atcdmac300_dma_reset(ATCDMAC300State *s) +{ + int ch; + for (ch = 0; ch < ATCDMAC300_MAX_CHAN; ch++) { + atcdmac300_dma_reset_chan(s, ch); + } +} + +static uint64_t atcdmac300_read(void *opaque, hwaddr offset, unsigned size) +{ + ATCDMAC300State *s = opaque; + int ch = 0; + uint64_t result = 0; + + if (offset >= 0x40) { + ch = ATCDMAC300_GET_CHAN(offset); + offset = ATCDMAC300_GET_OFF(offset, ch); + } + + switch (offset) { + case ATCDMAC300_DMA_CFG: + result = s->DMACfg; + break; + case ATCDMAC300_DMAC_CTRL: + break; + case ATCDMAC300_CHN_ABT: + break; + case ATCDMAC300_INT_STATUS: + result = s->IntStatus; + break; + case ATCDMAC300_CHAN_ENABLE: + result = s->ChEN; + break; + case ATCDMAC300_CHAN_CTL: + result = s->chan[ch].ChnCtrl; + break; + default: + LOGGE("%s: Bad offset 0x%" HWADDR_PRIX "\n", + __func__, offset); + break; + } + + LOG("### atcdmac300_read()=0x%lx, val=0x%lx\n", offset, result); + return result; +} + +static void transaction_info_push(StreamSink *sink, uint8_t *buf, + bool eop) +{ + if (sink == NULL) { + /* Do nothing if address stream is not support*/ + return; + } + if (eop) { + while (stream_push(sink, buf, sizeof(iopmp_transaction_info), true) + == 0) { + ; + } + } else { + while (stream_push(sink, buf, sizeof(iopmp_transaction_info), false) + == 0) { + ; + } + } +} + +static MemTxResult dma_iopmp_read(ATCDMAC300State *s, hwaddr addr, void *buf, + hwaddr len, + iopmp_transaction_info *transaction) +{ + MemTxResult result; + if (s->iopmp_as) { + if (s->iopmp_address_sink) { + transaction_info_push(s->iopmp_address_sink, + (uint8_t *)transaction, false); + } + MemTxAttrs dma_attrs = {.requester_id = s->sid}; + result = address_space_rw(s->iopmp_as, addr, dma_attrs, + buf, len, false); + if (s->iopmp_address_sink) { + transaction_info_push(s->iopmp_address_sink, + (uint8_t *)transaction, true); + return result; + } + } + cpu_physical_memory_read(addr, buf, len); + return MEMTX_OK; +} + +static MemTxResult dma_iopmp_write(ATCDMAC300State *s, hwaddr addr, void *buf, + hwaddr len, + iopmp_transaction_info *transaction) +{ + MemTxResult result = 0; + if (s->iopmp_as) { + if (s->iopmp_address_sink) { + transaction_info_push(s->iopmp_address_sink, + (uint8_t *)transaction, false); + } + MemTxAttrs dma_attrs = {.requester_id = s->sid}; + result = address_space_rw(s->iopmp_as, addr, dma_attrs, + buf, len, true); + if (s->iopmp_address_sink) { + transaction_info_push(s->iopmp_address_sink, + (uint8_t *)transaction, true); + return result; + } + } + cpu_physical_memory_write(addr, buf, len); + return MEMTX_OK; +} + +static void atcdmac300_co_run_channel(void *opaque, int ch) +{ + ATCDMAC300State *s = opaque; + int result; + uint64_t src_addr, dst_addr; + /* End address for AXI_BOUNDARY check */ + uint64_t src_end_addr, dst_end_addr; + /* DMA register bit field */ + uint32_t src_addr_ctl, dst_addr_ctl, int_tc_mask, int_err_mask, + int_abort_mask, burst_size, src_width, dst_width; + /* Internal computation */ + uint32_t remain_size_byte, dst_remain_byte, burst_size_transfer, + src_burst_remain, src_width_byte, dst_width_byte, + burst_size_byte, dma_remain_transfer_size, buf_index; + uint32_t axi_src_len, axi_dst_len; + uint8_t buf[ATCDMAC300_MAX_BURST_SIZE * 32]; + iopmp_transaction_info src_transaction, dst_transaction; + src_transaction.sid = s->sid; + dst_transaction.sid = s->sid; + if (((s->chan[ch].ChnCtrl >> CHAN_CTL_ENABLE) & 0x1) != 0x1) { + return; + } + src_width = (s->chan[ch].ChnCtrl >> CHAN_CTL_SRC_WIDTH) & + CHAN_CTL_SRC_WIDTH_MASK; + dst_width = (s->chan[ch].ChnCtrl >> CHAN_CTL_DST_WIDTH) & + CHAN_CTL_DST_WIDTH_MASK; + burst_size = (s->chan[ch].ChnCtrl >> CHAN_CTL_SRC_BURST_SZ) & + CHAN_CTL_SRC_BURST_SZ_MASK; + src_addr = (s->chan[ch].ChnSrcAddrH << 32) | + s->chan[ch].ChnSrcAddr; + dst_addr = (s->chan[ch].ChnDstAddrH << 32) | + s->chan[ch].ChnDstAddr; + src_addr_ctl = (s->chan[ch].ChnCtrl >> CHAN_CTL_SRC_ADDR_CTL) & + CHAN_CTL_SRC_ADDR_CTL_MASK; + dst_addr_ctl = (s->chan[ch].ChnCtrl >> CHAN_CTL_DST_ADDR_CTL) & + CHAN_CTL_DST_ADDR_CTL_MASK; + + src_width_byte = 1 << src_width; + dst_width_byte = 1 << dst_width; + dma_remain_transfer_size = s->chan[ch].ChnTranSize; + remain_size_byte = dma_remain_transfer_size * src_width_byte; + int_tc_mask = (s->chan[ch].ChnCtrl >> CHAN_CTL_INT_TC_MASK_POS) + & 0x1; + int_err_mask = (s->chan[ch].ChnCtrl >> + CHAN_CTL_INT_ERR_MASK_POS) & 0x1; + int_abort_mask = (s->chan[ch].ChnCtrl >> + CHAN_CTL_INT_ABT_MASK_POS) & 0x1; + burst_size_transfer = (1 << burst_size); + burst_size_byte = burst_size_transfer * src_width_byte; + if (remain_size_byte && burst_size < 11 && + src_width < 6 && dst_width < 6 && + (src_addr & (src_width_byte - 1)) == 0 && + (dst_addr & (dst_width_byte - 1)) == 0 && + (remain_size_byte & (dst_width_byte - 1)) == 0 && + (burst_size_byte & (dst_width_byte - 1)) == 0) { + while (remain_size_byte > 0) { + if (s->ChAbort & (1 << ch)) { + /* check abort status before a dma brust start*/ + s->ChAbort &= ~(1 << ch); + atcdmac300_dma_reset_chan(s, ch); + atcdmac300_dma_int_stat_update(s, INT_STATUS_ABT, + ch); + if (!int_abort_mask) { + qemu_irq_raise(s->irq); + } + return; + } + int i; + src_burst_remain = MIN(burst_size_transfer, + dma_remain_transfer_size); + dst_remain_byte = src_burst_remain * src_width_byte; + buf_index = 0; + /* One DMA burst may need mutiple AXI bursts */ + while (src_burst_remain) { + if (src_addr_ctl == 0) { + axi_src_len = MIN(src_burst_remain, + AXI_BURST_INC_LEN_MAX + 1); + src_end_addr = src_width_byte * axi_src_len + src_addr; + if ((src_addr & AXI_BOUNDARY) != + (src_end_addr & AXI_BOUNDARY)) { + src_end_addr &= AXI_BOUNDARY; + axi_src_len = (src_end_addr - src_addr) / + src_width_byte; + } + /* Convert AXI signal to general IOPMP transaction */ + src_transaction.start_addr = src_addr; + src_transaction.end_addr = src_end_addr - 1; + } + if (src_addr_ctl == 1) { + /* AXI does not support decrement type, use fixed type */ + src_transaction.start_addr = src_addr; + src_transaction.end_addr = src_addr + src_width_byte - 1; + } + if (src_addr_ctl == 2) { + src_transaction.start_addr = src_addr; + src_transaction.end_addr = src_addr + src_width_byte - 1; + } + memset(buf, 0, sizeof(buf)); + /* src_burst */ + for (i = 0; i < axi_src_len; i++) { + if (src_addr_ctl == 1) { + /* Change AXI addr for decrement address mode */ + src_transaction.start_addr = src_addr; + src_transaction.end_addr = src_addr + src_width_byte + - 1; + } + buf_index += src_width_byte; + result = dma_iopmp_read(s, src_addr, &buf[buf_index], + src_width_byte, &src_transaction); + while (result == MEMTX_IOPMP_STALL) { + qemu_coroutine_yield(); + result = dma_iopmp_read(s, src_addr, &buf[buf_index], + src_width_byte, + &src_transaction); + } + if (result != MEMTX_OK) { + s->ChAbort &= ~(1 << ch); + atcdmac300_dma_int_stat_update(s, + INT_STATUS_ERR, ch); + atcdmac300_dma_reset_chan(s, ch); + if (!int_err_mask) { + qemu_irq_raise(s->irq); + } + return; + } + if (src_addr_ctl == 0) { + src_addr += src_width_byte; + } + if (src_addr_ctl == 1) { + src_addr -= src_width_byte; + } + } + src_burst_remain -= axi_src_len; + dma_remain_transfer_size -= axi_src_len; + remain_size_byte -= axi_src_len * src_width_byte; + } + buf_index = 0; + /* One src burst may need mutiple dst bursts*/ + while (dst_remain_byte > 0) { + if (dst_addr_ctl == 0) { + axi_dst_len = + (dst_remain_byte / dst_width_byte); + axi_dst_len = MIN(axi_dst_len, + AXI_BURST_INC_LEN_MAX + 1); + dst_end_addr = dst_width_byte * axi_dst_len + + dst_addr; + if ((dst_addr & AXI_BOUNDARY) != + (dst_end_addr & AXI_BOUNDARY)) { + dst_end_addr &= AXI_BOUNDARY; + axi_dst_len = (dst_end_addr - dst_addr) / + dst_width_byte; + } + dst_transaction.start_addr = dst_addr; + dst_transaction.end_addr = dst_end_addr - 1; + } + if (dst_addr_ctl == 1) { + dst_transaction.start_addr = dst_addr; + dst_transaction.end_addr = dst_addr + dst_width_byte + - 1; + } + if (dst_addr_ctl == 2) { + dst_transaction.start_addr = dst_addr; + dst_transaction.end_addr = dst_addr + dst_width_byte + - 1; + } + for (i = 0; i < axi_dst_len; i++) { + if (dst_addr_ctl == 1) { + /* Change AXI addr for decrement address mode */ + dst_transaction.start_addr = dst_addr; + dst_transaction.end_addr = dst_addr + dst_width_byte + - 1; + } + buf_index += dst_width_byte; + result = dma_iopmp_write(s, dst_addr, &buf[buf_index], + dst_width_byte, &dst_transaction); + while (result == MEMTX_IOPMP_STALL) { + qemu_coroutine_yield(); + result = dma_iopmp_write(s, dst_addr, &buf[buf_index], + dst_width_byte, + &dst_transaction); + } + if (result != MEMTX_OK) { + s->ChAbort &= ~(1 << ch); + atcdmac300_dma_int_stat_update(s, + INT_STATUS_ERR, ch); + atcdmac300_dma_reset_chan(s, ch); + if (!int_err_mask) { + qemu_irq_raise(s->irq); + } + return; + } + if (dst_addr_ctl == 0) { + dst_addr += dst_width_byte; + } + if (dst_addr_ctl == 1) { + dst_addr -= dst_width_byte; + } + } + dst_remain_byte -= dst_width_byte * axi_dst_len; + } + } + /* DMA transfer complete */ + s->ChAbort &= ~(1 << ch); + atcdmac300_dma_reset_chan(s, ch); + atcdmac300_dma_int_stat_update(s, INT_STATUS_TC, ch); + if (!int_tc_mask) { + qemu_irq_raise(s->irq); + } + return; + } else { + s->ChAbort &= ~(1 << ch); + atcdmac300_dma_int_stat_update(s, INT_STATUS_ERR, ch); + atcdmac300_dma_reset_chan(s, ch); + if (!int_err_mask) { + qemu_irq_raise(s->irq); + } + } +} + +static void atcdmac300_co_run(void *opaque) +{ + + while (1) { + for (int ch = 0; ch < ATCDMAC300_MAX_CHAN; ch++) { + atcdmac300_co_run_channel(opaque, ch); + qemu_coroutine_yield(); + } + } +} + +static void atcdmac300_bh_cb(void *opaque) +{ + ATCDMAC300State *s = opaque; + + int rearm = 0; + if (s->running) { + rearm = 1; + goto out; + } else { + s->running = 1; + } + + AioContext *ctx = qemu_get_current_aio_context(); + aio_co_enter(ctx, s->co); + + s->running = 0; +out: + if (rearm) { + qemu_bh_schedule_idle(s->bh); + s->dma_bh_scheduled = true; + } + qemu_bh_schedule_idle(s->bh); + s->dma_bh_scheduled = true; + s->running = 0; +} + +static void atcdmac300_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + ATCDMAC300State *s = opaque; + int ch = 0; + + LOG("@@@ atcdmac300_write()=0x%lx, value=0x%lx\n", offset, value); + + if (offset >= 0x40) { + ch = ATCDMAC300_GET_CHAN(offset); + offset = ATCDMAC300_GET_OFF(offset, ch); + } + + switch (offset) { + case ATCDMAC300_INT_STATUS: + /* Write 1 to clear */ + s->IntStatus = 0; + break; + case ATCDMAC300_DMAC_CTRL: + atcdmac300_dma_reset(s); + break; + case ATCDMAC300_CHN_ABT: + for (int i = 0; i < ATCDMAC300_MAX_CHAN; i++) { + if (value & 0x1 && (s->chan[i].ChnCtrl & (1 << CHAN_CTL_ENABLE))) { + s->ChAbort |= (0x1 << i); + } + value >>= 1; + } + break; + case ATCDMAC300_CHAN_CTL: + s->chan[ch].ChnCtrl = value; + qemu_bh_schedule_idle(s->bh); + break; + case ATCDMAC300_CHAN_TRAN_SZ: + s->chan[ch].ChnTranSize = value; + break; + case ATCDMAC300_CHAN_SRC_ADDR: + s->chan[ch].ChnSrcAddr = value; + break; + case ATCDMAC300_CHAN_SRC_ADDR_H: + s->chan[ch].ChnSrcAddrH = value; + break; + case ATCDMAC300_CHAN_DST_ADDR: + s->chan[ch].ChnDstAddr = value; + break; + case ATCDMAC300_CHAN_DST_ADDR_H: + s->chan[ch].ChnDstAddrH = value; + break; + case ATCDMAC300_CHAN_LL_POINTER: + s->chan[ch].ChnLLPointer = value; + break; + case ATCDMAC300_CHAN_LL_POINTER_H: + s->chan[ch].ChnLLPointerH = value; + break; + default: + LOGGE("%s: Bad offset 0x%" HWADDR_PRIX "\n", + __func__, offset); + break; + } +} + +static const MemoryRegionOps atcdmac300_ops = { + .read = atcdmac300_read, + .write = atcdmac300_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8 + } +}; + +static void atcdmac300_init(Object *obj) +{ + ATCDMAC300State *s = ATCDMAC300(obj); + SysBusDevice *sbus = SYS_BUS_DEVICE(obj); + + sysbus_init_irq(sbus, &s->irq); + memory_region_init_io(&s->mmio, obj, &atcdmac300_ops, s, TYPE_ATCDMAC300, + s->mmio_size); + sysbus_init_mmio(sbus, &s->mmio); + if (s->iothread) { + s->ctx = iothread_get_aio_context(s->iothread); + } else { + s->ctx = qemu_get_aio_context(); + } + s->bh = aio_bh_new(s->ctx, atcdmac300_bh_cb, s); + s->co = qemu_coroutine_create(atcdmac300_co_run, s); +} + +static Property atcdmac300_properties[] = { + DEFINE_PROP_UINT32("mmio-size", ATCDMAC300State, mmio_size, 0x100000), + DEFINE_PROP_UINT32("id-and-revision", ATCDMAC300State, IdRev, + (ATCDMAC300_PRODUCT_ID << 8) | + ((ATCDMAC300_PRODUCT_ID & 0x7) << 4) | + ((ATCDMAC300_PRODUCT_ID & 0x7))), + DEFINE_PROP_UINT32("inturrupt-status", ATCDMAC300State, IntStatus, 0), + DEFINE_PROP_UINT32("dmac-configuration", ATCDMAC300State, + DMACfg, 0xc3404108), + DEFINE_PROP_LINK("iothread", ATCDMAC300State, iothread, + TYPE_IOTHREAD, IOThread *), + + DEFINE_PROP_END_OF_LIST(), +}; + +static void atcdmac300_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k = DEVICE_CLASS(klass); + device_class_set_props(k, atcdmac300_properties); +} + +static const TypeInfo atcdmac300_info = { + .name = TYPE_ATCDMAC300, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ATCDMAC300State), + .class_init = atcdmac300_class_init, + .instance_init = atcdmac300_init, +}; + +DeviceState * +atcdmac300_create(const char *name, hwaddr addr, hwaddr mmio_size, qemu_irq irq) +{ + DeviceState *dev; + dev = sysbus_create_varargs(TYPE_ATCDMAC300, addr, irq, NULL); + return dev; +} + +static void atcdmac300_register_types(void) +{ + type_register_static(&atcdmac300_info); +} + +void atcdmac300_connect_iopmp(DeviceState *dev, AddressSpace *iopmp_as, + StreamSink *iopmp_address_sink, uint32_t sid) +{ + ATCDMAC300State *s = ATCDMAC300(dev); + s->iopmp_as = iopmp_as; + s->iopmp_address_sink = iopmp_address_sink; + s->sid = sid; +} + +type_init(atcdmac300_register_types) diff --git a/hw/dma/meson.build b/hw/dma/meson.build index a96c1be2c8..dfe37de32d 100644 --- a/hw/dma/meson.build +++ b/hw/dma/meson.build @@ -14,3 +14,4 @@ system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_dma.c')) system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c')) system_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c')) system_ss.add(when: 'CONFIG_XLNX_CSU_DMA', if_true: files('xlnx_csu_dma.c')) +system_ss.add(when: 'CONFIG_ATCDMAC300', if_true: files('atcdmac300.c')) \ No newline at end of file diff --git a/include/hw/dma/atcdmac300.h b/include/hw/dma/atcdmac300.h new file mode 100644 index 0000000000..4f798e2a68 --- /dev/null +++ b/include/hw/dma/atcdmac300.h @@ -0,0 +1,180 @@ +/* + * Andes ATCDMAC300 (Andes Technology DMA Controller) + * + * Copyright (c) 2022 Andes Tech. Corp. + * + * 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 ATCDMAC300_H +#define ATCDMAC300_H + +#include "hw/sysbus.h" +#include "qom/object.h" +#include "qemu/coroutine.h" +#include "block/aio.h" +#include "sysemu/iothread.h" +#include "sysemu/dma.h" +#include "hw/stream.h" + +#define TYPE_ATCDMAC300 "atcdmac300" +OBJECT_DECLARE_SIMPLE_TYPE(ATCDMAC300State, ATCDMAC300) + +#define ATCDMAC300_IOPMP_SID 0 + +#define ATCDMAC300_PRODUCT_ID 0x010230 +#define ATCDMAC300_REV_MAJOR 0x0 +#define ATCDMAC300_REV_MINOR 0x1 + +/* DMAC Configuration Register (Offset 0x10) */ +#define ATCDMAC300_DMA_CFG 0x10 +#define DMA_CFG_CHAIN_XFR 31 +#define DMA_CFG_REQ_SYNC 30 +#define DMA_CFG_DATA_WITDTH 24 +#define DMA_CFG_ADDR_WIDTH 17 +#define DMA_CFG_CORE_NUM 16 +#define DMA_CFG_BUS_NUM 15 +#define DMA_CFG_REQ_NUM 10 +#define DMA_CFG_FIFO_DEPTH 4 +#define DMA_CFG_CHAN_NUM 0 + +/* Interrupt Status Register (Offset 0x20) */ +#define ATCDMAC300_DMAC_CTRL 0x20 + +/* Channel Abort Register (Offset 0x24) */ +#define ATCDMAC300_CHN_ABT 0x24 + +/* Interrupt Status Register (Offset 0x30) */ +#define ATCDMAC300_INT_STATUS 0x30 +#define INT_STATUS_TC 16 +#define INT_STATUS_ABT 8 +#define INT_STATUS_ERR 0 + +/* Interrupt Status Register (Offset 0x34) */ +#define ATCDMAC300_CHAN_ENABLE 0x34 + +/* Channel n Control Register (Offset 0x40 + n*0x20) */ +#define CHAN_CTL_SRC_BUS_IDX 31 +#define CHAN_CTL_DST_BUS_IDX 30 +#define CHAN_CTL_PRIORITY 29 +#define CHAN_CTL_SRC_BURST_SZ 24 +#define CHAN_CTL_SRC_WIDTH 21 +#define CHAN_CTL_DST_WIDTH 18 +#define CHAN_CTL_SRC_MODE 17 +#define CHAN_CTL_DST_MODE 16 +#define CHAN_CTL_SRC_ADDR_CTL 14 +#define CHAN_CTL_DST_ADDR_CTL 12 +#define CHAN_CTL_SRC_REQ_SEL 8 +#define CHAN_CTL_DST_REQ_SEL 4 +#define CHAN_CTL_INT_ABT_MASK_POS 3 +#define CHAN_CTL_INT_ERR_MASK_POS 2 +#define CHAN_CTL_INT_TC_MASK_POS 1 +#define CHAN_CTL_ENABLE 0 + +#define CHAN_CTL_SRC_WIDTH_MASK 0x7 +#define CHAN_CTL_DST_WIDTH_MASK 0x7 +#define CHAN_CTL_SRC_BURST_SZ_MASK 0xf +#define CHAN_CTL_SRC_ADDR_CTL_MASK 0x3 +#define CHAN_CTL_DST_ADDR_CTL_MASK 0x3 + +#define ATCDMAC300_CHAN_CTL 0x40 +#define ATCDMAC300_CHAN_TRAN_SZ 0x44 +#define ATCDMAC300_CHAN_SRC_ADDR 0x48 +#define ATCDMAC300_CHAN_SRC_ADDR_H 0x4C +#define ATCDMAC300_CHAN_DST_ADDR 0x50 +#define ATCDMAC300_CHAN_DST_ADDR_H 0x54 +#define ATCDMAC300_CHAN_LL_POINTER 0x58 +#define ATCDMAC300_CHAN_LL_POINTER_H 0x5C + +#define ATCDMAC300_IRQ_START 0x40 +#define ATCDMAC300_IRQ_END (ATCDMAC300_IRQ_START + \ + ATCDMAC300_MAX_CHAN) + +#define ATCDMAC300_MAX_BURST_SIZE 1024 +#define ATCDMAC300_MAX_CHAN 0x8 + +#define AXI_BURST_TYPE_FIX 0 +#define AXI_BURST_TYPE_INC 1 +#define AXI_BURST_INC_LEN_MAX 255 +#define AXI_BURST_FIX_LEN_MAX 15 +#define AXI_BOUNDARY 0x1000 + +#define PER_CHAN_OFFSET 0x20 +#define ATCDMAC300_FIRST_CHAN_BASE ATCDMAC300_CHAN_CTL +#define ATCDMAC300_GET_CHAN(reg) (((reg - ATCDMAC300_FIRST_CHAN_BASE) / \ + PER_CHAN_OFFSET)) +#define ATCDMAC300_GET_OFF(reg, ch) (reg - (ch * PER_CHAN_OFFSET)) + +#define DMA_ABT_RESULT (1 << 3) + +typedef struct { + qemu_irq irq; + + /* Channel control registers (n=0~7) */ + uint32_t ChnCtrl; + uint32_t ChnTranSize; + uint32_t ChnSrcAddr; + uint64_t ChnSrcAddrH; + uint32_t ChnDstAddr; + uint64_t ChnDstAddrH; + uint32_t ChnLLPointer; + uint32_t ChnLLPointerH; +} ATCDMAC300Chan; + + +struct ATCDMAC300State { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + qemu_irq irq; + MemoryRegion mmio; + uint32_t mmio_size; + + /* ID and revision register */ + uint32_t IdRev; + + /* Configuration register */ + uint32_t DMACfg; + + /* Global control registers */ + uint32_t DMACtrl; + uint32_t ChAbort; + + /* Channel status registers */ + uint32_t IntStatus; + uint32_t ChEN; + + ATCDMAC300Chan chan[ATCDMAC300_MAX_CHAN]; + + /* To support iopmp */ + AddressSpace *iopmp_as; + StreamSink *iopmp_address_sink; + uint32_t sid; + + Coroutine *co; + QEMUBH *bh; + bool running; + bool dma_bh_scheduled; + AioContext *ctx; + IOThread *iothread; +}; + +DeviceState *atcdmac300_create(const char *name, hwaddr addr, hwaddr mmio_size, + qemu_irq irq); + +void atcdmac300_connect_iopmp(DeviceState *dev, AddressSpace *iopmp_as, + StreamSink *iopmp_address_sink, uint32_t sid); + +#endif /* ATCDMAC300_H */ -- 2.34.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v3 3/4] hw/dma: Add Andes ATCDMAC300 support 2023-11-14 9:47 ` [PATCH v3 3/4] hw/dma: Add Andes ATCDMAC300 support Ethan Chen via @ 2023-11-21 5:39 ` Alistair Francis 0 siblings, 0 replies; 14+ messages in thread From: Alistair Francis @ 2023-11-21 5:39 UTC (permalink / raw) To: Ethan Chen Cc: qemu-devel, peter.maydell, edgar.iglesias, richard.henderson, pbonzini, palmer, alistair.francis, in.meng, liweiwei, dbarboza, hiwei_liu, qemu-riscv, peterx, david On Tue, Nov 14, 2023 at 7:49 PM Ethan Chen via <qemu-devel@nongnu.org> wrote: > What is an "ATCDMAC300"? Can you provide more context? Alistair > Signed-off-by: Ethan Chen <ethan84@andestech.com> > --- > hw/dma/Kconfig | 4 + > hw/dma/atcdmac300.c | 566 ++++++++++++++++++++++++++++++++++++ > hw/dma/meson.build | 1 + > include/hw/dma/atcdmac300.h | 180 ++++++++++++ > 4 files changed, 751 insertions(+) > create mode 100644 hw/dma/atcdmac300.c > create mode 100644 include/hw/dma/atcdmac300.h > > diff --git a/hw/dma/Kconfig b/hw/dma/Kconfig > index 98fbb1bb04..a1d335b52f 100644 > --- a/hw/dma/Kconfig > +++ b/hw/dma/Kconfig > @@ -30,3 +30,7 @@ config SIFIVE_PDMA > config XLNX_CSU_DMA > bool > select REGISTER > + > +config ATCDMAC300 > + bool > + select STREAM > diff --git a/hw/dma/atcdmac300.c b/hw/dma/atcdmac300.c > new file mode 100644 > index 0000000000..bc6012f44e > --- /dev/null > +++ b/hw/dma/atcdmac300.c > @@ -0,0 +1,566 @@ > +/* > + * Andes ATCDMAC300 (Andes Technology DMA Controller) > + * > + * Copyright (c) 2022 Andes Tech. Corp. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2 or later, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see <http://www.gnu.org/licenses/> > + * > + */ > + > +#include "qemu/osdep.h" > +#include "qapi/error.h" > +#include "hw/dma/atcdmac300.h" > +#include "hw/irq.h" > +#include "hw/qdev-properties.h" > +#include "qemu/log.h" > +#include "qemu/module.h" > +#include "exec/memattrs.h" > +#include "exec/address-spaces.h" > +#include "hw/stream.h" > +#include "hw/misc/riscv_iopmp_transaction_info.h" > + > +/* #define DEBUG_ANDES_ATCDMAC300 */ > +#define LOGGE(x...) qemu_log_mask(LOG_GUEST_ERROR, x) > +#define xLOG(x...) > +#define yLOG(x...) qemu_log(x) > +#ifdef DEBUG_ANDES_ATCDMAC300 > + #define LOG(x...) yLOG(x) > +#else > + #define LOG(x...) xLOG(x) > +#endif > + > +#define MEMTX_IOPMP_STALL (1 << 3) > + > +static void atcdmac300_dma_int_stat_update(ATCDMAC300State *s, int status, > + int ch) > +{ > + s->IntStatus |= (1 << (status + ch)); > +} > + > +static void atcdmac300_dma_reset_chan(ATCDMAC300State *s, int ch) > +{ > + if (s) { > + s->chan[ch].ChnCtrl &= ~(1 << CHAN_CTL_ENABLE); > + s->ChEN &= ~(1 << ch); > + } > +} > + > +static void atcdmac300_dma_reset(ATCDMAC300State *s) > +{ > + int ch; > + for (ch = 0; ch < ATCDMAC300_MAX_CHAN; ch++) { > + atcdmac300_dma_reset_chan(s, ch); > + } > +} > + > +static uint64_t atcdmac300_read(void *opaque, hwaddr offset, unsigned size) > +{ > + ATCDMAC300State *s = opaque; > + int ch = 0; > + uint64_t result = 0; > + > + if (offset >= 0x40) { > + ch = ATCDMAC300_GET_CHAN(offset); > + offset = ATCDMAC300_GET_OFF(offset, ch); > + } > + > + switch (offset) { > + case ATCDMAC300_DMA_CFG: > + result = s->DMACfg; > + break; > + case ATCDMAC300_DMAC_CTRL: > + break; > + case ATCDMAC300_CHN_ABT: > + break; > + case ATCDMAC300_INT_STATUS: > + result = s->IntStatus; > + break; > + case ATCDMAC300_CHAN_ENABLE: > + result = s->ChEN; > + break; > + case ATCDMAC300_CHAN_CTL: > + result = s->chan[ch].ChnCtrl; > + break; > + default: > + LOGGE("%s: Bad offset 0x%" HWADDR_PRIX "\n", > + __func__, offset); > + break; > + } > + > + LOG("### atcdmac300_read()=0x%lx, val=0x%lx\n", offset, result); > + return result; > +} > + > +static void transaction_info_push(StreamSink *sink, uint8_t *buf, > + bool eop) > +{ > + if (sink == NULL) { > + /* Do nothing if address stream is not support*/ > + return; > + } > + if (eop) { > + while (stream_push(sink, buf, sizeof(iopmp_transaction_info), true) > + == 0) { > + ; > + } > + } else { > + while (stream_push(sink, buf, sizeof(iopmp_transaction_info), false) > + == 0) { > + ; > + } > + } > +} > + > +static MemTxResult dma_iopmp_read(ATCDMAC300State *s, hwaddr addr, void *buf, > + hwaddr len, > + iopmp_transaction_info *transaction) > +{ > + MemTxResult result; > + if (s->iopmp_as) { > + if (s->iopmp_address_sink) { > + transaction_info_push(s->iopmp_address_sink, > + (uint8_t *)transaction, false); > + } > + MemTxAttrs dma_attrs = {.requester_id = s->sid}; > + result = address_space_rw(s->iopmp_as, addr, dma_attrs, > + buf, len, false); > + if (s->iopmp_address_sink) { > + transaction_info_push(s->iopmp_address_sink, > + (uint8_t *)transaction, true); > + return result; > + } > + } > + cpu_physical_memory_read(addr, buf, len); > + return MEMTX_OK; > +} > + > +static MemTxResult dma_iopmp_write(ATCDMAC300State *s, hwaddr addr, void *buf, > + hwaddr len, > + iopmp_transaction_info *transaction) > +{ > + MemTxResult result = 0; > + if (s->iopmp_as) { > + if (s->iopmp_address_sink) { > + transaction_info_push(s->iopmp_address_sink, > + (uint8_t *)transaction, false); > + } > + MemTxAttrs dma_attrs = {.requester_id = s->sid}; > + result = address_space_rw(s->iopmp_as, addr, dma_attrs, > + buf, len, true); > + if (s->iopmp_address_sink) { > + transaction_info_push(s->iopmp_address_sink, > + (uint8_t *)transaction, true); > + return result; > + } > + } > + cpu_physical_memory_write(addr, buf, len); > + return MEMTX_OK; > +} > + > +static void atcdmac300_co_run_channel(void *opaque, int ch) > +{ > + ATCDMAC300State *s = opaque; > + int result; > + uint64_t src_addr, dst_addr; > + /* End address for AXI_BOUNDARY check */ > + uint64_t src_end_addr, dst_end_addr; > + /* DMA register bit field */ > + uint32_t src_addr_ctl, dst_addr_ctl, int_tc_mask, int_err_mask, > + int_abort_mask, burst_size, src_width, dst_width; > + /* Internal computation */ > + uint32_t remain_size_byte, dst_remain_byte, burst_size_transfer, > + src_burst_remain, src_width_byte, dst_width_byte, > + burst_size_byte, dma_remain_transfer_size, buf_index; > + uint32_t axi_src_len, axi_dst_len; > + uint8_t buf[ATCDMAC300_MAX_BURST_SIZE * 32]; > + iopmp_transaction_info src_transaction, dst_transaction; > + src_transaction.sid = s->sid; > + dst_transaction.sid = s->sid; > + if (((s->chan[ch].ChnCtrl >> CHAN_CTL_ENABLE) & 0x1) != 0x1) { > + return; > + } > + src_width = (s->chan[ch].ChnCtrl >> CHAN_CTL_SRC_WIDTH) & > + CHAN_CTL_SRC_WIDTH_MASK; > + dst_width = (s->chan[ch].ChnCtrl >> CHAN_CTL_DST_WIDTH) & > + CHAN_CTL_DST_WIDTH_MASK; > + burst_size = (s->chan[ch].ChnCtrl >> CHAN_CTL_SRC_BURST_SZ) & > + CHAN_CTL_SRC_BURST_SZ_MASK; > + src_addr = (s->chan[ch].ChnSrcAddrH << 32) | > + s->chan[ch].ChnSrcAddr; > + dst_addr = (s->chan[ch].ChnDstAddrH << 32) | > + s->chan[ch].ChnDstAddr; > + src_addr_ctl = (s->chan[ch].ChnCtrl >> CHAN_CTL_SRC_ADDR_CTL) & > + CHAN_CTL_SRC_ADDR_CTL_MASK; > + dst_addr_ctl = (s->chan[ch].ChnCtrl >> CHAN_CTL_DST_ADDR_CTL) & > + CHAN_CTL_DST_ADDR_CTL_MASK; > + > + src_width_byte = 1 << src_width; > + dst_width_byte = 1 << dst_width; > + dma_remain_transfer_size = s->chan[ch].ChnTranSize; > + remain_size_byte = dma_remain_transfer_size * src_width_byte; > + int_tc_mask = (s->chan[ch].ChnCtrl >> CHAN_CTL_INT_TC_MASK_POS) > + & 0x1; > + int_err_mask = (s->chan[ch].ChnCtrl >> > + CHAN_CTL_INT_ERR_MASK_POS) & 0x1; > + int_abort_mask = (s->chan[ch].ChnCtrl >> > + CHAN_CTL_INT_ABT_MASK_POS) & 0x1; > + burst_size_transfer = (1 << burst_size); > + burst_size_byte = burst_size_transfer * src_width_byte; > + if (remain_size_byte && burst_size < 11 && > + src_width < 6 && dst_width < 6 && > + (src_addr & (src_width_byte - 1)) == 0 && > + (dst_addr & (dst_width_byte - 1)) == 0 && > + (remain_size_byte & (dst_width_byte - 1)) == 0 && > + (burst_size_byte & (dst_width_byte - 1)) == 0) { > + while (remain_size_byte > 0) { > + if (s->ChAbort & (1 << ch)) { > + /* check abort status before a dma brust start*/ > + s->ChAbort &= ~(1 << ch); > + atcdmac300_dma_reset_chan(s, ch); > + atcdmac300_dma_int_stat_update(s, INT_STATUS_ABT, > + ch); > + if (!int_abort_mask) { > + qemu_irq_raise(s->irq); > + } > + return; > + } > + int i; > + src_burst_remain = MIN(burst_size_transfer, > + dma_remain_transfer_size); > + dst_remain_byte = src_burst_remain * src_width_byte; > + buf_index = 0; > + /* One DMA burst may need mutiple AXI bursts */ > + while (src_burst_remain) { > + if (src_addr_ctl == 0) { > + axi_src_len = MIN(src_burst_remain, > + AXI_BURST_INC_LEN_MAX + 1); > + src_end_addr = src_width_byte * axi_src_len + src_addr; > + if ((src_addr & AXI_BOUNDARY) != > + (src_end_addr & AXI_BOUNDARY)) { > + src_end_addr &= AXI_BOUNDARY; > + axi_src_len = (src_end_addr - src_addr) / > + src_width_byte; > + } > + /* Convert AXI signal to general IOPMP transaction */ > + src_transaction.start_addr = src_addr; > + src_transaction.end_addr = src_end_addr - 1; > + } > + if (src_addr_ctl == 1) { > + /* AXI does not support decrement type, use fixed type */ > + src_transaction.start_addr = src_addr; > + src_transaction.end_addr = src_addr + src_width_byte - 1; > + } > + if (src_addr_ctl == 2) { > + src_transaction.start_addr = src_addr; > + src_transaction.end_addr = src_addr + src_width_byte - 1; > + } > + memset(buf, 0, sizeof(buf)); > + /* src_burst */ > + for (i = 0; i < axi_src_len; i++) { > + if (src_addr_ctl == 1) { > + /* Change AXI addr for decrement address mode */ > + src_transaction.start_addr = src_addr; > + src_transaction.end_addr = src_addr + src_width_byte > + - 1; > + } > + buf_index += src_width_byte; > + result = dma_iopmp_read(s, src_addr, &buf[buf_index], > + src_width_byte, &src_transaction); > + while (result == MEMTX_IOPMP_STALL) { > + qemu_coroutine_yield(); > + result = dma_iopmp_read(s, src_addr, &buf[buf_index], > + src_width_byte, > + &src_transaction); > + } > + if (result != MEMTX_OK) { > + s->ChAbort &= ~(1 << ch); > + atcdmac300_dma_int_stat_update(s, > + INT_STATUS_ERR, ch); > + atcdmac300_dma_reset_chan(s, ch); > + if (!int_err_mask) { > + qemu_irq_raise(s->irq); > + } > + return; > + } > + if (src_addr_ctl == 0) { > + src_addr += src_width_byte; > + } > + if (src_addr_ctl == 1) { > + src_addr -= src_width_byte; > + } > + } > + src_burst_remain -= axi_src_len; > + dma_remain_transfer_size -= axi_src_len; > + remain_size_byte -= axi_src_len * src_width_byte; > + } > + buf_index = 0; > + /* One src burst may need mutiple dst bursts*/ > + while (dst_remain_byte > 0) { > + if (dst_addr_ctl == 0) { > + axi_dst_len = > + (dst_remain_byte / dst_width_byte); > + axi_dst_len = MIN(axi_dst_len, > + AXI_BURST_INC_LEN_MAX + 1); > + dst_end_addr = dst_width_byte * axi_dst_len > + + dst_addr; > + if ((dst_addr & AXI_BOUNDARY) != > + (dst_end_addr & AXI_BOUNDARY)) { > + dst_end_addr &= AXI_BOUNDARY; > + axi_dst_len = (dst_end_addr - dst_addr) / > + dst_width_byte; > + } > + dst_transaction.start_addr = dst_addr; > + dst_transaction.end_addr = dst_end_addr - 1; > + } > + if (dst_addr_ctl == 1) { > + dst_transaction.start_addr = dst_addr; > + dst_transaction.end_addr = dst_addr + dst_width_byte > + - 1; > + } > + if (dst_addr_ctl == 2) { > + dst_transaction.start_addr = dst_addr; > + dst_transaction.end_addr = dst_addr + dst_width_byte > + - 1; > + } > + for (i = 0; i < axi_dst_len; i++) { > + if (dst_addr_ctl == 1) { > + /* Change AXI addr for decrement address mode */ > + dst_transaction.start_addr = dst_addr; > + dst_transaction.end_addr = dst_addr + dst_width_byte > + - 1; > + } > + buf_index += dst_width_byte; > + result = dma_iopmp_write(s, dst_addr, &buf[buf_index], > + dst_width_byte, &dst_transaction); > + while (result == MEMTX_IOPMP_STALL) { > + qemu_coroutine_yield(); > + result = dma_iopmp_write(s, dst_addr, &buf[buf_index], > + dst_width_byte, > + &dst_transaction); > + } > + if (result != MEMTX_OK) { > + s->ChAbort &= ~(1 << ch); > + atcdmac300_dma_int_stat_update(s, > + INT_STATUS_ERR, ch); > + atcdmac300_dma_reset_chan(s, ch); > + if (!int_err_mask) { > + qemu_irq_raise(s->irq); > + } > + return; > + } > + if (dst_addr_ctl == 0) { > + dst_addr += dst_width_byte; > + } > + if (dst_addr_ctl == 1) { > + dst_addr -= dst_width_byte; > + } > + } > + dst_remain_byte -= dst_width_byte * axi_dst_len; > + } > + } > + /* DMA transfer complete */ > + s->ChAbort &= ~(1 << ch); > + atcdmac300_dma_reset_chan(s, ch); > + atcdmac300_dma_int_stat_update(s, INT_STATUS_TC, ch); > + if (!int_tc_mask) { > + qemu_irq_raise(s->irq); > + } > + return; > + } else { > + s->ChAbort &= ~(1 << ch); > + atcdmac300_dma_int_stat_update(s, INT_STATUS_ERR, ch); > + atcdmac300_dma_reset_chan(s, ch); > + if (!int_err_mask) { > + qemu_irq_raise(s->irq); > + } > + } > +} > + > +static void atcdmac300_co_run(void *opaque) > +{ > + > + while (1) { > + for (int ch = 0; ch < ATCDMAC300_MAX_CHAN; ch++) { > + atcdmac300_co_run_channel(opaque, ch); > + qemu_coroutine_yield(); > + } > + } > +} > + > +static void atcdmac300_bh_cb(void *opaque) > +{ > + ATCDMAC300State *s = opaque; > + > + int rearm = 0; > + if (s->running) { > + rearm = 1; > + goto out; > + } else { > + s->running = 1; > + } > + > + AioContext *ctx = qemu_get_current_aio_context(); > + aio_co_enter(ctx, s->co); > + > + s->running = 0; > +out: > + if (rearm) { > + qemu_bh_schedule_idle(s->bh); > + s->dma_bh_scheduled = true; > + } > + qemu_bh_schedule_idle(s->bh); > + s->dma_bh_scheduled = true; > + s->running = 0; > +} > + > +static void atcdmac300_write(void *opaque, hwaddr offset, uint64_t value, > + unsigned size) > +{ > + ATCDMAC300State *s = opaque; > + int ch = 0; > + > + LOG("@@@ atcdmac300_write()=0x%lx, value=0x%lx\n", offset, value); > + > + if (offset >= 0x40) { > + ch = ATCDMAC300_GET_CHAN(offset); > + offset = ATCDMAC300_GET_OFF(offset, ch); > + } > + > + switch (offset) { > + case ATCDMAC300_INT_STATUS: > + /* Write 1 to clear */ > + s->IntStatus = 0; > + break; > + case ATCDMAC300_DMAC_CTRL: > + atcdmac300_dma_reset(s); > + break; > + case ATCDMAC300_CHN_ABT: > + for (int i = 0; i < ATCDMAC300_MAX_CHAN; i++) { > + if (value & 0x1 && (s->chan[i].ChnCtrl & (1 << CHAN_CTL_ENABLE))) { > + s->ChAbort |= (0x1 << i); > + } > + value >>= 1; > + } > + break; > + case ATCDMAC300_CHAN_CTL: > + s->chan[ch].ChnCtrl = value; > + qemu_bh_schedule_idle(s->bh); > + break; > + case ATCDMAC300_CHAN_TRAN_SZ: > + s->chan[ch].ChnTranSize = value; > + break; > + case ATCDMAC300_CHAN_SRC_ADDR: > + s->chan[ch].ChnSrcAddr = value; > + break; > + case ATCDMAC300_CHAN_SRC_ADDR_H: > + s->chan[ch].ChnSrcAddrH = value; > + break; > + case ATCDMAC300_CHAN_DST_ADDR: > + s->chan[ch].ChnDstAddr = value; > + break; > + case ATCDMAC300_CHAN_DST_ADDR_H: > + s->chan[ch].ChnDstAddrH = value; > + break; > + case ATCDMAC300_CHAN_LL_POINTER: > + s->chan[ch].ChnLLPointer = value; > + break; > + case ATCDMAC300_CHAN_LL_POINTER_H: > + s->chan[ch].ChnLLPointerH = value; > + break; > + default: > + LOGGE("%s: Bad offset 0x%" HWADDR_PRIX "\n", > + __func__, offset); > + break; > + } > +} > + > +static const MemoryRegionOps atcdmac300_ops = { > + .read = atcdmac300_read, > + .write = atcdmac300_write, > + .endianness = DEVICE_NATIVE_ENDIAN, > + .valid = { > + .min_access_size = 4, > + .max_access_size = 8 > + } > +}; > + > +static void atcdmac300_init(Object *obj) > +{ > + ATCDMAC300State *s = ATCDMAC300(obj); > + SysBusDevice *sbus = SYS_BUS_DEVICE(obj); > + > + sysbus_init_irq(sbus, &s->irq); > + memory_region_init_io(&s->mmio, obj, &atcdmac300_ops, s, TYPE_ATCDMAC300, > + s->mmio_size); > + sysbus_init_mmio(sbus, &s->mmio); > + if (s->iothread) { > + s->ctx = iothread_get_aio_context(s->iothread); > + } else { > + s->ctx = qemu_get_aio_context(); > + } > + s->bh = aio_bh_new(s->ctx, atcdmac300_bh_cb, s); > + s->co = qemu_coroutine_create(atcdmac300_co_run, s); > +} > + > +static Property atcdmac300_properties[] = { > + DEFINE_PROP_UINT32("mmio-size", ATCDMAC300State, mmio_size, 0x100000), > + DEFINE_PROP_UINT32("id-and-revision", ATCDMAC300State, IdRev, > + (ATCDMAC300_PRODUCT_ID << 8) | > + ((ATCDMAC300_PRODUCT_ID & 0x7) << 4) | > + ((ATCDMAC300_PRODUCT_ID & 0x7))), > + DEFINE_PROP_UINT32("inturrupt-status", ATCDMAC300State, IntStatus, 0), > + DEFINE_PROP_UINT32("dmac-configuration", ATCDMAC300State, > + DMACfg, 0xc3404108), > + DEFINE_PROP_LINK("iothread", ATCDMAC300State, iothread, > + TYPE_IOTHREAD, IOThread *), > + > + DEFINE_PROP_END_OF_LIST(), > +}; > + > +static void atcdmac300_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *k = DEVICE_CLASS(klass); > + device_class_set_props(k, atcdmac300_properties); > +} > + > +static const TypeInfo atcdmac300_info = { > + .name = TYPE_ATCDMAC300, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(ATCDMAC300State), > + .class_init = atcdmac300_class_init, > + .instance_init = atcdmac300_init, > +}; > + > +DeviceState * > +atcdmac300_create(const char *name, hwaddr addr, hwaddr mmio_size, qemu_irq irq) > +{ > + DeviceState *dev; > + dev = sysbus_create_varargs(TYPE_ATCDMAC300, addr, irq, NULL); > + return dev; > +} > + > +static void atcdmac300_register_types(void) > +{ > + type_register_static(&atcdmac300_info); > +} > + > +void atcdmac300_connect_iopmp(DeviceState *dev, AddressSpace *iopmp_as, > + StreamSink *iopmp_address_sink, uint32_t sid) > +{ > + ATCDMAC300State *s = ATCDMAC300(dev); > + s->iopmp_as = iopmp_as; > + s->iopmp_address_sink = iopmp_address_sink; > + s->sid = sid; > +} > + > +type_init(atcdmac300_register_types) > diff --git a/hw/dma/meson.build b/hw/dma/meson.build > index a96c1be2c8..dfe37de32d 100644 > --- a/hw/dma/meson.build > +++ b/hw/dma/meson.build > @@ -14,3 +14,4 @@ system_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_dma.c')) > system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c')) > system_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c')) > system_ss.add(when: 'CONFIG_XLNX_CSU_DMA', if_true: files('xlnx_csu_dma.c')) > +system_ss.add(when: 'CONFIG_ATCDMAC300', if_true: files('atcdmac300.c')) > \ No newline at end of file > diff --git a/include/hw/dma/atcdmac300.h b/include/hw/dma/atcdmac300.h > new file mode 100644 > index 0000000000..4f798e2a68 > --- /dev/null > +++ b/include/hw/dma/atcdmac300.h > @@ -0,0 +1,180 @@ > +/* > + * Andes ATCDMAC300 (Andes Technology DMA Controller) > + * > + * Copyright (c) 2022 Andes Tech. Corp. > + * > + * 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 ATCDMAC300_H > +#define ATCDMAC300_H > + > +#include "hw/sysbus.h" > +#include "qom/object.h" > +#include "qemu/coroutine.h" > +#include "block/aio.h" > +#include "sysemu/iothread.h" > +#include "sysemu/dma.h" > +#include "hw/stream.h" > + > +#define TYPE_ATCDMAC300 "atcdmac300" > +OBJECT_DECLARE_SIMPLE_TYPE(ATCDMAC300State, ATCDMAC300) > + > +#define ATCDMAC300_IOPMP_SID 0 > + > +#define ATCDMAC300_PRODUCT_ID 0x010230 > +#define ATCDMAC300_REV_MAJOR 0x0 > +#define ATCDMAC300_REV_MINOR 0x1 > + > +/* DMAC Configuration Register (Offset 0x10) */ > +#define ATCDMAC300_DMA_CFG 0x10 > +#define DMA_CFG_CHAIN_XFR 31 > +#define DMA_CFG_REQ_SYNC 30 > +#define DMA_CFG_DATA_WITDTH 24 > +#define DMA_CFG_ADDR_WIDTH 17 > +#define DMA_CFG_CORE_NUM 16 > +#define DMA_CFG_BUS_NUM 15 > +#define DMA_CFG_REQ_NUM 10 > +#define DMA_CFG_FIFO_DEPTH 4 > +#define DMA_CFG_CHAN_NUM 0 > + > +/* Interrupt Status Register (Offset 0x20) */ > +#define ATCDMAC300_DMAC_CTRL 0x20 > + > +/* Channel Abort Register (Offset 0x24) */ > +#define ATCDMAC300_CHN_ABT 0x24 > + > +/* Interrupt Status Register (Offset 0x30) */ > +#define ATCDMAC300_INT_STATUS 0x30 > +#define INT_STATUS_TC 16 > +#define INT_STATUS_ABT 8 > +#define INT_STATUS_ERR 0 > + > +/* Interrupt Status Register (Offset 0x34) */ > +#define ATCDMAC300_CHAN_ENABLE 0x34 > + > +/* Channel n Control Register (Offset 0x40 + n*0x20) */ > +#define CHAN_CTL_SRC_BUS_IDX 31 > +#define CHAN_CTL_DST_BUS_IDX 30 > +#define CHAN_CTL_PRIORITY 29 > +#define CHAN_CTL_SRC_BURST_SZ 24 > +#define CHAN_CTL_SRC_WIDTH 21 > +#define CHAN_CTL_DST_WIDTH 18 > +#define CHAN_CTL_SRC_MODE 17 > +#define CHAN_CTL_DST_MODE 16 > +#define CHAN_CTL_SRC_ADDR_CTL 14 > +#define CHAN_CTL_DST_ADDR_CTL 12 > +#define CHAN_CTL_SRC_REQ_SEL 8 > +#define CHAN_CTL_DST_REQ_SEL 4 > +#define CHAN_CTL_INT_ABT_MASK_POS 3 > +#define CHAN_CTL_INT_ERR_MASK_POS 2 > +#define CHAN_CTL_INT_TC_MASK_POS 1 > +#define CHAN_CTL_ENABLE 0 > + > +#define CHAN_CTL_SRC_WIDTH_MASK 0x7 > +#define CHAN_CTL_DST_WIDTH_MASK 0x7 > +#define CHAN_CTL_SRC_BURST_SZ_MASK 0xf > +#define CHAN_CTL_SRC_ADDR_CTL_MASK 0x3 > +#define CHAN_CTL_DST_ADDR_CTL_MASK 0x3 > + > +#define ATCDMAC300_CHAN_CTL 0x40 > +#define ATCDMAC300_CHAN_TRAN_SZ 0x44 > +#define ATCDMAC300_CHAN_SRC_ADDR 0x48 > +#define ATCDMAC300_CHAN_SRC_ADDR_H 0x4C > +#define ATCDMAC300_CHAN_DST_ADDR 0x50 > +#define ATCDMAC300_CHAN_DST_ADDR_H 0x54 > +#define ATCDMAC300_CHAN_LL_POINTER 0x58 > +#define ATCDMAC300_CHAN_LL_POINTER_H 0x5C > + > +#define ATCDMAC300_IRQ_START 0x40 > +#define ATCDMAC300_IRQ_END (ATCDMAC300_IRQ_START + \ > + ATCDMAC300_MAX_CHAN) > + > +#define ATCDMAC300_MAX_BURST_SIZE 1024 > +#define ATCDMAC300_MAX_CHAN 0x8 > + > +#define AXI_BURST_TYPE_FIX 0 > +#define AXI_BURST_TYPE_INC 1 > +#define AXI_BURST_INC_LEN_MAX 255 > +#define AXI_BURST_FIX_LEN_MAX 15 > +#define AXI_BOUNDARY 0x1000 > + > +#define PER_CHAN_OFFSET 0x20 > +#define ATCDMAC300_FIRST_CHAN_BASE ATCDMAC300_CHAN_CTL > +#define ATCDMAC300_GET_CHAN(reg) (((reg - ATCDMAC300_FIRST_CHAN_BASE) / \ > + PER_CHAN_OFFSET)) > +#define ATCDMAC300_GET_OFF(reg, ch) (reg - (ch * PER_CHAN_OFFSET)) > + > +#define DMA_ABT_RESULT (1 << 3) > + > +typedef struct { > + qemu_irq irq; > + > + /* Channel control registers (n=0~7) */ > + uint32_t ChnCtrl; > + uint32_t ChnTranSize; > + uint32_t ChnSrcAddr; > + uint64_t ChnSrcAddrH; > + uint32_t ChnDstAddr; > + uint64_t ChnDstAddrH; > + uint32_t ChnLLPointer; > + uint32_t ChnLLPointerH; > +} ATCDMAC300Chan; > + > + > +struct ATCDMAC300State { > + /*< private >*/ > + SysBusDevice busdev; > + /*< public >*/ > + > + qemu_irq irq; > + MemoryRegion mmio; > + uint32_t mmio_size; > + > + /* ID and revision register */ > + uint32_t IdRev; > + > + /* Configuration register */ > + uint32_t DMACfg; > + > + /* Global control registers */ > + uint32_t DMACtrl; > + uint32_t ChAbort; > + > + /* Channel status registers */ > + uint32_t IntStatus; > + uint32_t ChEN; > + > + ATCDMAC300Chan chan[ATCDMAC300_MAX_CHAN]; > + > + /* To support iopmp */ > + AddressSpace *iopmp_as; > + StreamSink *iopmp_address_sink; > + uint32_t sid; > + > + Coroutine *co; > + QEMUBH *bh; > + bool running; > + bool dma_bh_scheduled; > + AioContext *ctx; > + IOThread *iothread; > +}; > + > +DeviceState *atcdmac300_create(const char *name, hwaddr addr, hwaddr mmio_size, > + qemu_irq irq); > + > +void atcdmac300_connect_iopmp(DeviceState *dev, AddressSpace *iopmp_as, > + StreamSink *iopmp_address_sink, uint32_t sid); > + > +#endif /* ATCDMAC300_H */ > -- > 2.34.1 > > ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v3 4/4] hw/riscv/virt: Add IOPMP support 2023-11-14 9:47 [PATCH v3 0/4] Support RISC-V IOPMP Ethan Chen via ` (2 preceding siblings ...) 2023-11-14 9:47 ` [PATCH v3 3/4] hw/dma: Add Andes ATCDMAC300 support Ethan Chen via @ 2023-11-14 9:47 ` Ethan Chen via 2023-11-14 17:50 ` Daniel Henrique Barboza 2023-11-21 5:22 ` Alistair Francis 3 siblings, 2 replies; 14+ messages in thread From: Ethan Chen via @ 2023-11-14 9:47 UTC (permalink / raw) To: qemu-devel Cc: peter.maydell, edgar.iglesias, richard.henderson, pbonzini, palmer, alistair.francis, in.meng, liweiwei, dbarboza, hiwei_liu, qemu-riscv, peterx, david, Ethan Chen - Add 'iopmp=on' option to enable a iopmp device and a dma device connect to the iopmp device - Add 'iopmp_cascade=on' option to enable iopmp cascading. Signed-off-by: Ethan Chen <ethan84@andestech.com> --- hw/riscv/Kconfig | 2 ++ hw/riscv/virt.c | 72 +++++++++++++++++++++++++++++++++++++++-- include/hw/riscv/virt.h | 10 +++++- 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index b6a5eb4452..c30a104aa4 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -45,6 +45,8 @@ config RISCV_VIRT select FW_CFG_DMA select PLATFORM_BUS select ACPI + select ATCDMAC300 + select RISCV_IOPMP config SHAKTI_C bool diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index c7fc97e273..3e23ee3afc 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -53,6 +53,8 @@ #include "hw/display/ramfb.h" #include "hw/acpi/aml-build.h" #include "qapi/qapi-visit-common.h" +#include "hw/misc/riscv_iopmp.h" +#include "hw/dma/atcdmac300.h" /* * The virt machine physical address space used by some of the devices @@ -97,6 +99,9 @@ static const MemMapEntry virt_memmap[] = { [VIRT_UART0] = { 0x10000000, 0x100 }, [VIRT_VIRTIO] = { 0x10001000, 0x1000 }, [VIRT_FW_CFG] = { 0x10100000, 0x18 }, + [VIRT_IOPMP] = { 0x10200000, 0x100000 }, + [VIRT_IOPMP2] = { 0x10300000, 0x100000 }, + [VIRT_DMAC] = { 0x10400000, 0x100000 }, [VIRT_FLASH] = { 0x20000000, 0x4000000 }, [VIRT_IMSIC_M] = { 0x24000000, VIRT_IMSIC_MAX_SIZE }, [VIRT_IMSIC_S] = { 0x28000000, VIRT_IMSIC_MAX_SIZE }, @@ -1527,13 +1532,33 @@ static void virt_machine_init(MachineState *machine) create_platform_bus(s, mmio_irqchip); - serial_mm_init(system_memory, memmap[VIRT_UART0].base, - 0, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 399193, + serial_mm_init(system_memory, memmap[VIRT_UART0].base + 0x20, + 0x2, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 38400, serial_hd(0), DEVICE_LITTLE_ENDIAN); sysbus_create_simple("goldfish_rtc", memmap[VIRT_RTC].base, qdev_get_gpio_in(mmio_irqchip, RTC_IRQ)); + /* DMAC */ + DeviceState *dmac_dev = atcdmac300_create("atcdmac300", + memmap[VIRT_DMAC].base, memmap[VIRT_DMAC].size, + qdev_get_gpio_in(DEVICE(mmio_irqchip), DMAC_IRQ)); + + if (s->have_iopmp) { + /* IOPMP */ + DeviceState *iopmp_dev = iopmp_create(memmap[VIRT_IOPMP].base, + qdev_get_gpio_in(DEVICE(mmio_irqchip), IOPMP_IRQ)); + /* DMA with IOPMP */ + atcdmac300_connect_iopmp(dmac_dev, &(IOPMP(iopmp_dev)->iopmp_as), + (StreamSink *)&(IOPMP(iopmp_dev)->transaction_info_sink), 0); + if (s->have_iopmp_cascade) { + DeviceState *iopmp_dev2 = iopmp_create(memmap[VIRT_IOPMP2].base, + qdev_get_gpio_in(DEVICE(mmio_irqchip), IOPMP2_IRQ)); + cascade_iopmp(iopmp_dev, iopmp_dev2); + } + } + + for (i = 0; i < ARRAY_SIZE(s->flash); i++) { /* Map legacy -drive if=pflash to machine properties */ pflash_cfi01_legacy_drive(s->flash[i], @@ -1628,6 +1653,35 @@ static void virt_set_aclint(Object *obj, bool value, Error **errp) s->have_aclint = value; } +static bool virt_get_iopmp(Object *obj, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + + return s->have_iopmp; +} + +static void virt_set_iopmp(Object *obj, bool value, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + + s->have_iopmp = value; +} + +static bool virt_get_iopmp_cascade(Object *obj, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + + return s->have_iopmp_cascade; +} + +static void virt_set_iopmp_cascade(Object *obj, bool value, Error **errp) +{ + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); + + s->have_iopmp_cascade = value; +} + + bool virt_is_acpi_enabled(RISCVVirtState *s) { return s->acpi != ON_OFF_AUTO_OFF; @@ -1730,6 +1784,20 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) NULL, NULL); object_class_property_set_description(oc, "acpi", "Enable ACPI"); + + object_class_property_add_bool(oc, "iopmp", virt_get_iopmp, + virt_set_iopmp); + object_class_property_set_description(oc, "iopmp", + "Set on/off to enable/disable " + "iopmp device"); + + object_class_property_add_bool(oc, "iopmp-cascade", + virt_get_iopmp_cascade, + virt_set_iopmp_cascade); + object_class_property_set_description(oc, "iopmp-cascade", + "Set on/off to enable/disable " + "iopmp2 device which is cascaded by " + "iopmp1 device"); } static const TypeInfo virt_machine_typeinfo = { diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index e5c474b26e..5fa2944d29 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -54,6 +54,8 @@ struct RISCVVirtState { int fdt_size; bool have_aclint; + bool have_iopmp; + bool have_iopmp_cascade; RISCVVirtAIAType aia_type; int aia_guests; char *oem_id; @@ -82,12 +84,18 @@ enum { VIRT_PCIE_MMIO, VIRT_PCIE_PIO, VIRT_PLATFORM_BUS, - VIRT_PCIE_ECAM + VIRT_PCIE_ECAM, + VIRT_IOPMP, + VIRT_IOPMP2, + VIRT_DMAC, }; enum { UART0_IRQ = 10, RTC_IRQ = 11, + DMAC_IRQ = 12, + IOPMP_IRQ = 13, + IOPMP2_IRQ = 14, VIRTIO_IRQ = 1, /* 1 to 8 */ VIRTIO_COUNT = 8, PCIE_IRQ = 0x20, /* 32 to 35 */ -- 2.34.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v3 4/4] hw/riscv/virt: Add IOPMP support 2023-11-14 9:47 ` [PATCH v3 4/4] hw/riscv/virt: Add IOPMP support Ethan Chen via @ 2023-11-14 17:50 ` Daniel Henrique Barboza 2023-11-15 1:51 ` Ethan Chen via 2023-11-21 5:22 ` Alistair Francis 1 sibling, 1 reply; 14+ messages in thread From: Daniel Henrique Barboza @ 2023-11-14 17:50 UTC (permalink / raw) To: Ethan Chen, qemu-devel Cc: peter.maydell, edgar.iglesias, richard.henderson, pbonzini, palmer, alistair.francis, in.meng, liweiwei, hiwei_liu, qemu-riscv, peterx, david On 11/14/23 06:47, Ethan Chen wrote: > - Add 'iopmp=on' option to enable a iopmp device and a dma device > connect to the iopmp device > - Add 'iopmp_cascade=on' option to enable iopmp cascading. > > Signed-off-by: Ethan Chen <ethan84@andestech.com> > --- > hw/riscv/Kconfig | 2 ++ > hw/riscv/virt.c | 72 +++++++++++++++++++++++++++++++++++++++-- > include/hw/riscv/virt.h | 10 +++++- > 3 files changed, 81 insertions(+), 3 deletions(-) > > diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig > index b6a5eb4452..c30a104aa4 100644 > --- a/hw/riscv/Kconfig > +++ b/hw/riscv/Kconfig > @@ -45,6 +45,8 @@ config RISCV_VIRT > select FW_CFG_DMA > select PLATFORM_BUS > select ACPI > + select ATCDMAC300 > + select RISCV_IOPMP > > config SHAKTI_C > bool > diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c > index c7fc97e273..3e23ee3afc 100644 > --- a/hw/riscv/virt.c > +++ b/hw/riscv/virt.c > @@ -53,6 +53,8 @@ > #include "hw/display/ramfb.h" > #include "hw/acpi/aml-build.h" > #include "qapi/qapi-visit-common.h" > +#include "hw/misc/riscv_iopmp.h" > +#include "hw/dma/atcdmac300.h" > > /* > * The virt machine physical address space used by some of the devices > @@ -97,6 +99,9 @@ static const MemMapEntry virt_memmap[] = { > [VIRT_UART0] = { 0x10000000, 0x100 }, > [VIRT_VIRTIO] = { 0x10001000, 0x1000 }, > [VIRT_FW_CFG] = { 0x10100000, 0x18 }, > + [VIRT_IOPMP] = { 0x10200000, 0x100000 }, > + [VIRT_IOPMP2] = { 0x10300000, 0x100000 }, > + [VIRT_DMAC] = { 0x10400000, 0x100000 }, > [VIRT_FLASH] = { 0x20000000, 0x4000000 }, > [VIRT_IMSIC_M] = { 0x24000000, VIRT_IMSIC_MAX_SIZE }, > [VIRT_IMSIC_S] = { 0x28000000, VIRT_IMSIC_MAX_SIZE }, > @@ -1527,13 +1532,33 @@ static void virt_machine_init(MachineState *machine) > > create_platform_bus(s, mmio_irqchip); > > - serial_mm_init(system_memory, memmap[VIRT_UART0].base, > - 0, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 399193, > + serial_mm_init(system_memory, memmap[VIRT_UART0].base + 0x20, > + 0x2, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 38400, > serial_hd(0), DEVICE_LITTLE_ENDIAN); It would be good to have some variables/macros to hold these literals like '0x20' since they aren't self-explanatory when reading the code. > > sysbus_create_simple("goldfish_rtc", memmap[VIRT_RTC].base, > qdev_get_gpio_in(mmio_irqchip, RTC_IRQ)); > > + /* DMAC */ > + DeviceState *dmac_dev = atcdmac300_create("atcdmac300", > + memmap[VIRT_DMAC].base, memmap[VIRT_DMAC].size, > + qdev_get_gpio_in(DEVICE(mmio_irqchip), DMAC_IRQ)); > + > + if (s->have_iopmp) { > + /* IOPMP */ > + DeviceState *iopmp_dev = iopmp_create(memmap[VIRT_IOPMP].base, > + qdev_get_gpio_in(DEVICE(mmio_irqchip), IOPMP_IRQ)); > + /* DMA with IOPMP */ > + atcdmac300_connect_iopmp(dmac_dev, &(IOPMP(iopmp_dev)->iopmp_as), > + (StreamSink *)&(IOPMP(iopmp_dev)->transaction_info_sink), 0); > + if (s->have_iopmp_cascade) { > + DeviceState *iopmp_dev2 = iopmp_create(memmap[VIRT_IOPMP2].base, > + qdev_get_gpio_in(DEVICE(mmio_irqchip), IOPMP2_IRQ)); > + cascade_iopmp(iopmp_dev, iopmp_dev2); > + } > + } > + > + Extra blank line. Everything else LGTM. Thanks, Daniel > for (i = 0; i < ARRAY_SIZE(s->flash); i++) { > /* Map legacy -drive if=pflash to machine properties */ > pflash_cfi01_legacy_drive(s->flash[i], > @@ -1628,6 +1653,35 @@ static void virt_set_aclint(Object *obj, bool value, Error **errp) > s->have_aclint = value; > } > > +static bool virt_get_iopmp(Object *obj, Error **errp) > +{ > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > + > + return s->have_iopmp; > +} > + > +static void virt_set_iopmp(Object *obj, bool value, Error **errp) > +{ > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > + > + s->have_iopmp = value; > +} > + > +static bool virt_get_iopmp_cascade(Object *obj, Error **errp) > +{ > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > + > + return s->have_iopmp_cascade; > +} > + > +static void virt_set_iopmp_cascade(Object *obj, bool value, Error **errp) > +{ > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > + > + s->have_iopmp_cascade = value; > +} > + > + > bool virt_is_acpi_enabled(RISCVVirtState *s) > { > return s->acpi != ON_OFF_AUTO_OFF; > @@ -1730,6 +1784,20 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) > NULL, NULL); > object_class_property_set_description(oc, "acpi", > "Enable ACPI"); > + > + object_class_property_add_bool(oc, "iopmp", virt_get_iopmp, > + virt_set_iopmp); > + object_class_property_set_description(oc, "iopmp", > + "Set on/off to enable/disable " > + "iopmp device"); > + > + object_class_property_add_bool(oc, "iopmp-cascade", > + virt_get_iopmp_cascade, > + virt_set_iopmp_cascade); > + object_class_property_set_description(oc, "iopmp-cascade", > + "Set on/off to enable/disable " > + "iopmp2 device which is cascaded by " > + "iopmp1 device"); > } > > static const TypeInfo virt_machine_typeinfo = { > diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h > index e5c474b26e..5fa2944d29 100644 > --- a/include/hw/riscv/virt.h > +++ b/include/hw/riscv/virt.h > @@ -54,6 +54,8 @@ struct RISCVVirtState { > > int fdt_size; > bool have_aclint; > + bool have_iopmp; > + bool have_iopmp_cascade; > RISCVVirtAIAType aia_type; > int aia_guests; > char *oem_id; > @@ -82,12 +84,18 @@ enum { > VIRT_PCIE_MMIO, > VIRT_PCIE_PIO, > VIRT_PLATFORM_BUS, > - VIRT_PCIE_ECAM > + VIRT_PCIE_ECAM, > + VIRT_IOPMP, > + VIRT_IOPMP2, > + VIRT_DMAC, > }; > > enum { > UART0_IRQ = 10, > RTC_IRQ = 11, > + DMAC_IRQ = 12, > + IOPMP_IRQ = 13, > + IOPMP2_IRQ = 14, > VIRTIO_IRQ = 1, /* 1 to 8 */ > VIRTIO_COUNT = 8, > PCIE_IRQ = 0x20, /* 32 to 35 */ ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3 4/4] hw/riscv/virt: Add IOPMP support 2023-11-14 17:50 ` Daniel Henrique Barboza @ 2023-11-15 1:51 ` Ethan Chen via 0 siblings, 0 replies; 14+ messages in thread From: Ethan Chen via @ 2023-11-15 1:51 UTC (permalink / raw) To: Daniel Henrique Barboza Cc: qemu-devel, peter.maydell, edgar.iglesias, richard.henderson, pbonzini, palmer, alistair.francis, in.meng, liweiwei, hiwei_liu, qemu-riscv, peterx, david On Tue, Nov 14, 2023 at 02:50:21PM -0300, Daniel Henrique Barboza wrote: > > > On 11/14/23 06:47, Ethan Chen wrote: > > - Add 'iopmp=on' option to enable a iopmp device and a dma device > > connect to the iopmp device > > - Add 'iopmp_cascade=on' option to enable iopmp cascading. > > > > Signed-off-by: Ethan Chen <ethan84@andestech.com> > > --- > > hw/riscv/Kconfig | 2 ++ > > hw/riscv/virt.c | 72 +++++++++++++++++++++++++++++++++++++++-- > > include/hw/riscv/virt.h | 10 +++++- > > 3 files changed, 81 insertions(+), 3 deletions(-) > > > > diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig > > index b6a5eb4452..c30a104aa4 100644 > > --- a/hw/riscv/Kconfig > > +++ b/hw/riscv/Kconfig > > @@ -45,6 +45,8 @@ config RISCV_VIRT > > select FW_CFG_DMA > > select PLATFORM_BUS > > select ACPI > > + select ATCDMAC300 > > + select RISCV_IOPMP > > config SHAKTI_C > > bool > > diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c > > index c7fc97e273..3e23ee3afc 100644 > > --- a/hw/riscv/virt.c > > +++ b/hw/riscv/virt.c > > @@ -53,6 +53,8 @@ > > #include "hw/display/ramfb.h" > > #include "hw/acpi/aml-build.h" > > #include "qapi/qapi-visit-common.h" > > +#include "hw/misc/riscv_iopmp.h" > > +#include "hw/dma/atcdmac300.h" > > /* > > * The virt machine physical address space used by some of the devices > > @@ -97,6 +99,9 @@ static const MemMapEntry virt_memmap[] = { > > [VIRT_UART0] = { 0x10000000, 0x100 }, > > [VIRT_VIRTIO] = { 0x10001000, 0x1000 }, > > [VIRT_FW_CFG] = { 0x10100000, 0x18 }, > > + [VIRT_IOPMP] = { 0x10200000, 0x100000 }, > > + [VIRT_IOPMP2] = { 0x10300000, 0x100000 }, > > + [VIRT_DMAC] = { 0x10400000, 0x100000 }, > > [VIRT_FLASH] = { 0x20000000, 0x4000000 }, > > [VIRT_IMSIC_M] = { 0x24000000, VIRT_IMSIC_MAX_SIZE }, > > [VIRT_IMSIC_S] = { 0x28000000, VIRT_IMSIC_MAX_SIZE }, > > @@ -1527,13 +1532,33 @@ static void virt_machine_init(MachineState *machine) > > create_platform_bus(s, mmio_irqchip); > > - serial_mm_init(system_memory, memmap[VIRT_UART0].base, > > - 0, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 399193, > > + serial_mm_init(system_memory, memmap[VIRT_UART0].base + 0x20, > > + 0x2, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 38400, > > serial_hd(0), DEVICE_LITTLE_ENDIAN); > > It would be good to have some variables/macros to hold these literals like '0x20' > since they aren't self-explanatory when reading the code. Sorry that this is my mistake. These two lines should not be modified. > > > sysbus_create_simple("goldfish_rtc", memmap[VIRT_RTC].base, > > qdev_get_gpio_in(mmio_irqchip, RTC_IRQ)); > > + /* DMAC */ > > + DeviceState *dmac_dev = atcdmac300_create("atcdmac300", > > + memmap[VIRT_DMAC].base, memmap[VIRT_DMAC].size, > > + qdev_get_gpio_in(DEVICE(mmio_irqchip), DMAC_IRQ)); > > + > > + if (s->have_iopmp) { > > + /* IOPMP */ > > + DeviceState *iopmp_dev = iopmp_create(memmap[VIRT_IOPMP].base, > > + qdev_get_gpio_in(DEVICE(mmio_irqchip), IOPMP_IRQ)); > > + /* DMA with IOPMP */ > > + atcdmac300_connect_iopmp(dmac_dev, &(IOPMP(iopmp_dev)->iopmp_as), > > + (StreamSink *)&(IOPMP(iopmp_dev)->transaction_info_sink), 0); > > + if (s->have_iopmp_cascade) { > > + DeviceState *iopmp_dev2 = iopmp_create(memmap[VIRT_IOPMP2].base, > > + qdev_get_gpio_in(DEVICE(mmio_irqchip), IOPMP2_IRQ)); > > + cascade_iopmp(iopmp_dev, iopmp_dev2); > > + } > > + } > > + > > + > > Extra blank line. > > Everything else LGTM. Thanks, > > > Daniel I will fix the issue you mentioned. Thanks, Ethan Chen > > > for (i = 0; i < ARRAY_SIZE(s->flash); i++) { > > /* Map legacy -drive if=pflash to machine properties */ > > pflash_cfi01_legacy_drive(s->flash[i], > > @@ -1628,6 +1653,35 @@ static void virt_set_aclint(Object *obj, bool value, Error **errp) > > s->have_aclint = value; > > } > > +static bool virt_get_iopmp(Object *obj, Error **errp) > > +{ > > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > > + > > + return s->have_iopmp; > > +} > > + > > +static void virt_set_iopmp(Object *obj, bool value, Error **errp) > > +{ > > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > > + > > + s->have_iopmp = value; > > +} > > + > > +static bool virt_get_iopmp_cascade(Object *obj, Error **errp) > > +{ > > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > > + > > + return s->have_iopmp_cascade; > > +} > > + > > +static void virt_set_iopmp_cascade(Object *obj, bool value, Error **errp) > > +{ > > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > > + > > + s->have_iopmp_cascade = value; > > +} > > + > > + > > bool virt_is_acpi_enabled(RISCVVirtState *s) > > { > > return s->acpi != ON_OFF_AUTO_OFF; > > @@ -1730,6 +1784,20 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) > > NULL, NULL); > > object_class_property_set_description(oc, "acpi", > > "Enable ACPI"); > > + > > + object_class_property_add_bool(oc, "iopmp", virt_get_iopmp, > > + virt_set_iopmp); > > + object_class_property_set_description(oc, "iopmp", > > + "Set on/off to enable/disable " > > + "iopmp device"); > > + > > + object_class_property_add_bool(oc, "iopmp-cascade", > > + virt_get_iopmp_cascade, > > + virt_set_iopmp_cascade); > > + object_class_property_set_description(oc, "iopmp-cascade", > > + "Set on/off to enable/disable " > > + "iopmp2 device which is cascaded by " > > + "iopmp1 device"); > > } > > static const TypeInfo virt_machine_typeinfo = { > > diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h > > index e5c474b26e..5fa2944d29 100644 > > --- a/include/hw/riscv/virt.h > > +++ b/include/hw/riscv/virt.h > > @@ -54,6 +54,8 @@ struct RISCVVirtState { > > int fdt_size; > > bool have_aclint; > > + bool have_iopmp; > > + bool have_iopmp_cascade; > > RISCVVirtAIAType aia_type; > > int aia_guests; > > char *oem_id; > > @@ -82,12 +84,18 @@ enum { > > VIRT_PCIE_MMIO, > > VIRT_PCIE_PIO, > > VIRT_PLATFORM_BUS, > > - VIRT_PCIE_ECAM > > + VIRT_PCIE_ECAM, > > + VIRT_IOPMP, > > + VIRT_IOPMP2, > > + VIRT_DMAC, > > }; > > enum { > > UART0_IRQ = 10, > > RTC_IRQ = 11, > > + DMAC_IRQ = 12, > > + IOPMP_IRQ = 13, > > + IOPMP2_IRQ = 14, > > VIRTIO_IRQ = 1, /* 1 to 8 */ > > VIRTIO_COUNT = 8, > > PCIE_IRQ = 0x20, /* 32 to 35 */ ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3 4/4] hw/riscv/virt: Add IOPMP support 2023-11-14 9:47 ` [PATCH v3 4/4] hw/riscv/virt: Add IOPMP support Ethan Chen via 2023-11-14 17:50 ` Daniel Henrique Barboza @ 2023-11-21 5:22 ` Alistair Francis 2023-11-21 9:54 ` Ethan Chen via 1 sibling, 1 reply; 14+ messages in thread From: Alistair Francis @ 2023-11-21 5:22 UTC (permalink / raw) To: Ethan Chen Cc: qemu-devel, peter.maydell, edgar.iglesias, richard.henderson, pbonzini, palmer, alistair.francis, in.meng, liweiwei, dbarboza, hiwei_liu, qemu-riscv, peterx, david On Tue, Nov 14, 2023 at 7:48 PM Ethan Chen via <qemu-devel@nongnu.org> wrote: > > - Add 'iopmp=on' option to enable a iopmp device and a dma device > connect to the iopmp device > - Add 'iopmp_cascade=on' option to enable iopmp cascading. Can we document these in docs/system/riscv/virt.rst Alistair > > Signed-off-by: Ethan Chen <ethan84@andestech.com> > --- > hw/riscv/Kconfig | 2 ++ > hw/riscv/virt.c | 72 +++++++++++++++++++++++++++++++++++++++-- > include/hw/riscv/virt.h | 10 +++++- > 3 files changed, 81 insertions(+), 3 deletions(-) > > diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig > index b6a5eb4452..c30a104aa4 100644 > --- a/hw/riscv/Kconfig > +++ b/hw/riscv/Kconfig > @@ -45,6 +45,8 @@ config RISCV_VIRT > select FW_CFG_DMA > select PLATFORM_BUS > select ACPI > + select ATCDMAC300 > + select RISCV_IOPMP > > config SHAKTI_C > bool > diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c > index c7fc97e273..3e23ee3afc 100644 > --- a/hw/riscv/virt.c > +++ b/hw/riscv/virt.c > @@ -53,6 +53,8 @@ > #include "hw/display/ramfb.h" > #include "hw/acpi/aml-build.h" > #include "qapi/qapi-visit-common.h" > +#include "hw/misc/riscv_iopmp.h" > +#include "hw/dma/atcdmac300.h" > > /* > * The virt machine physical address space used by some of the devices > @@ -97,6 +99,9 @@ static const MemMapEntry virt_memmap[] = { > [VIRT_UART0] = { 0x10000000, 0x100 }, > [VIRT_VIRTIO] = { 0x10001000, 0x1000 }, > [VIRT_FW_CFG] = { 0x10100000, 0x18 }, > + [VIRT_IOPMP] = { 0x10200000, 0x100000 }, > + [VIRT_IOPMP2] = { 0x10300000, 0x100000 }, > + [VIRT_DMAC] = { 0x10400000, 0x100000 }, > [VIRT_FLASH] = { 0x20000000, 0x4000000 }, > [VIRT_IMSIC_M] = { 0x24000000, VIRT_IMSIC_MAX_SIZE }, > [VIRT_IMSIC_S] = { 0x28000000, VIRT_IMSIC_MAX_SIZE }, > @@ -1527,13 +1532,33 @@ static void virt_machine_init(MachineState *machine) > > create_platform_bus(s, mmio_irqchip); > > - serial_mm_init(system_memory, memmap[VIRT_UART0].base, > - 0, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 399193, > + serial_mm_init(system_memory, memmap[VIRT_UART0].base + 0x20, > + 0x2, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 38400, > serial_hd(0), DEVICE_LITTLE_ENDIAN); > > sysbus_create_simple("goldfish_rtc", memmap[VIRT_RTC].base, > qdev_get_gpio_in(mmio_irqchip, RTC_IRQ)); > > + /* DMAC */ > + DeviceState *dmac_dev = atcdmac300_create("atcdmac300", > + memmap[VIRT_DMAC].base, memmap[VIRT_DMAC].size, > + qdev_get_gpio_in(DEVICE(mmio_irqchip), DMAC_IRQ)); > + > + if (s->have_iopmp) { > + /* IOPMP */ > + DeviceState *iopmp_dev = iopmp_create(memmap[VIRT_IOPMP].base, > + qdev_get_gpio_in(DEVICE(mmio_irqchip), IOPMP_IRQ)); > + /* DMA with IOPMP */ > + atcdmac300_connect_iopmp(dmac_dev, &(IOPMP(iopmp_dev)->iopmp_as), > + (StreamSink *)&(IOPMP(iopmp_dev)->transaction_info_sink), 0); > + if (s->have_iopmp_cascade) { > + DeviceState *iopmp_dev2 = iopmp_create(memmap[VIRT_IOPMP2].base, > + qdev_get_gpio_in(DEVICE(mmio_irqchip), IOPMP2_IRQ)); > + cascade_iopmp(iopmp_dev, iopmp_dev2); > + } > + } > + > + > for (i = 0; i < ARRAY_SIZE(s->flash); i++) { > /* Map legacy -drive if=pflash to machine properties */ > pflash_cfi01_legacy_drive(s->flash[i], > @@ -1628,6 +1653,35 @@ static void virt_set_aclint(Object *obj, bool value, Error **errp) > s->have_aclint = value; > } > > +static bool virt_get_iopmp(Object *obj, Error **errp) > +{ > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > + > + return s->have_iopmp; > +} > + > +static void virt_set_iopmp(Object *obj, bool value, Error **errp) > +{ > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > + > + s->have_iopmp = value; > +} > + > +static bool virt_get_iopmp_cascade(Object *obj, Error **errp) > +{ > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > + > + return s->have_iopmp_cascade; > +} > + > +static void virt_set_iopmp_cascade(Object *obj, bool value, Error **errp) > +{ > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > + > + s->have_iopmp_cascade = value; > +} > + > + > bool virt_is_acpi_enabled(RISCVVirtState *s) > { > return s->acpi != ON_OFF_AUTO_OFF; > @@ -1730,6 +1784,20 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) > NULL, NULL); > object_class_property_set_description(oc, "acpi", > "Enable ACPI"); > + > + object_class_property_add_bool(oc, "iopmp", virt_get_iopmp, > + virt_set_iopmp); > + object_class_property_set_description(oc, "iopmp", > + "Set on/off to enable/disable " > + "iopmp device"); > + > + object_class_property_add_bool(oc, "iopmp-cascade", > + virt_get_iopmp_cascade, > + virt_set_iopmp_cascade); > + object_class_property_set_description(oc, "iopmp-cascade", > + "Set on/off to enable/disable " > + "iopmp2 device which is cascaded by " > + "iopmp1 device"); > } > > static const TypeInfo virt_machine_typeinfo = { > diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h > index e5c474b26e..5fa2944d29 100644 > --- a/include/hw/riscv/virt.h > +++ b/include/hw/riscv/virt.h > @@ -54,6 +54,8 @@ struct RISCVVirtState { > > int fdt_size; > bool have_aclint; > + bool have_iopmp; > + bool have_iopmp_cascade; > RISCVVirtAIAType aia_type; > int aia_guests; > char *oem_id; > @@ -82,12 +84,18 @@ enum { > VIRT_PCIE_MMIO, > VIRT_PCIE_PIO, > VIRT_PLATFORM_BUS, > - VIRT_PCIE_ECAM > + VIRT_PCIE_ECAM, > + VIRT_IOPMP, > + VIRT_IOPMP2, > + VIRT_DMAC, > }; > > enum { > UART0_IRQ = 10, > RTC_IRQ = 11, > + DMAC_IRQ = 12, > + IOPMP_IRQ = 13, > + IOPMP2_IRQ = 14, > VIRTIO_IRQ = 1, /* 1 to 8 */ > VIRTIO_COUNT = 8, > PCIE_IRQ = 0x20, /* 32 to 35 */ > -- > 2.34.1 > > ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3 4/4] hw/riscv/virt: Add IOPMP support 2023-11-21 5:22 ` Alistair Francis @ 2023-11-21 9:54 ` Ethan Chen via 0 siblings, 0 replies; 14+ messages in thread From: Ethan Chen via @ 2023-11-21 9:54 UTC (permalink / raw) To: Alistair Francis Cc: qemu-devel, peter.maydell, edgar.iglesias, richard.henderson, pbonzini, palmer, alistair.francis, in.meng, liweiwei, dbarboza, hiwei_liu, qemu-riscv, peterx, david On Tue, Nov 21, 2023 at 03:22:18PM +1000, Alistair Francis wrote: > On Tue, Nov 14, 2023 at 7:48 PM Ethan Chen via <qemu-devel@nongnu.org> wrote: > > > > - Add 'iopmp=on' option to enable a iopmp device and a dma device > > connect to the iopmp device > > - Add 'iopmp_cascade=on' option to enable iopmp cascading. > > Can we document these in docs/system/riscv/virt.rst > > Alistair Sure. I will document these. Thanks, Ethan Chen > > > > > Signed-off-by: Ethan Chen <ethan84@andestech.com> > > --- > > hw/riscv/Kconfig | 2 ++ > > hw/riscv/virt.c | 72 +++++++++++++++++++++++++++++++++++++++-- > > include/hw/riscv/virt.h | 10 +++++- > > 3 files changed, 81 insertions(+), 3 deletions(-) > > > > diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig > > index b6a5eb4452..c30a104aa4 100644 > > --- a/hw/riscv/Kconfig > > +++ b/hw/riscv/Kconfig > > @@ -45,6 +45,8 @@ config RISCV_VIRT > > select FW_CFG_DMA > > select PLATFORM_BUS > > select ACPI > > + select ATCDMAC300 > > + select RISCV_IOPMP > > > > config SHAKTI_C > > bool > > diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c > > index c7fc97e273..3e23ee3afc 100644 > > --- a/hw/riscv/virt.c > > +++ b/hw/riscv/virt.c > > @@ -53,6 +53,8 @@ > > #include "hw/display/ramfb.h" > > #include "hw/acpi/aml-build.h" > > #include "qapi/qapi-visit-common.h" > > +#include "hw/misc/riscv_iopmp.h" > > +#include "hw/dma/atcdmac300.h" > > > > /* > > * The virt machine physical address space used by some of the devices > > @@ -97,6 +99,9 @@ static const MemMapEntry virt_memmap[] = { > > [VIRT_UART0] = { 0x10000000, 0x100 }, > > [VIRT_VIRTIO] = { 0x10001000, 0x1000 }, > > [VIRT_FW_CFG] = { 0x10100000, 0x18 }, > > + [VIRT_IOPMP] = { 0x10200000, 0x100000 }, > > + [VIRT_IOPMP2] = { 0x10300000, 0x100000 }, > > + [VIRT_DMAC] = { 0x10400000, 0x100000 }, > > [VIRT_FLASH] = { 0x20000000, 0x4000000 }, > > [VIRT_IMSIC_M] = { 0x24000000, VIRT_IMSIC_MAX_SIZE }, > > [VIRT_IMSIC_S] = { 0x28000000, VIRT_IMSIC_MAX_SIZE }, > > @@ -1527,13 +1532,33 @@ static void virt_machine_init(MachineState *machine) > > > > create_platform_bus(s, mmio_irqchip); > > > > - serial_mm_init(system_memory, memmap[VIRT_UART0].base, > > - 0, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 399193, > > + serial_mm_init(system_memory, memmap[VIRT_UART0].base + 0x20, > > + 0x2, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 38400, > > serial_hd(0), DEVICE_LITTLE_ENDIAN); > > > > sysbus_create_simple("goldfish_rtc", memmap[VIRT_RTC].base, > > qdev_get_gpio_in(mmio_irqchip, RTC_IRQ)); > > > > + /* DMAC */ > > + DeviceState *dmac_dev = atcdmac300_create("atcdmac300", > > + memmap[VIRT_DMAC].base, memmap[VIRT_DMAC].size, > > + qdev_get_gpio_in(DEVICE(mmio_irqchip), DMAC_IRQ)); > > + > > + if (s->have_iopmp) { > > + /* IOPMP */ > > + DeviceState *iopmp_dev = iopmp_create(memmap[VIRT_IOPMP].base, > > + qdev_get_gpio_in(DEVICE(mmio_irqchip), IOPMP_IRQ)); > > + /* DMA with IOPMP */ > > + atcdmac300_connect_iopmp(dmac_dev, &(IOPMP(iopmp_dev)->iopmp_as), > > + (StreamSink *)&(IOPMP(iopmp_dev)->transaction_info_sink), 0); > > + if (s->have_iopmp_cascade) { > > + DeviceState *iopmp_dev2 = iopmp_create(memmap[VIRT_IOPMP2].base, > > + qdev_get_gpio_in(DEVICE(mmio_irqchip), IOPMP2_IRQ)); > > + cascade_iopmp(iopmp_dev, iopmp_dev2); > > + } > > + } > > + > > + > > for (i = 0; i < ARRAY_SIZE(s->flash); i++) { > > /* Map legacy -drive if=pflash to machine properties */ > > pflash_cfi01_legacy_drive(s->flash[i], > > @@ -1628,6 +1653,35 @@ static void virt_set_aclint(Object *obj, bool value, Error **errp) > > s->have_aclint = value; > > } > > > > +static bool virt_get_iopmp(Object *obj, Error **errp) > > +{ > > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > > + > > + return s->have_iopmp; > > +} > > + > > +static void virt_set_iopmp(Object *obj, bool value, Error **errp) > > +{ > > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > > + > > + s->have_iopmp = value; > > +} > > + > > +static bool virt_get_iopmp_cascade(Object *obj, Error **errp) > > +{ > > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > > + > > + return s->have_iopmp_cascade; > > +} > > + > > +static void virt_set_iopmp_cascade(Object *obj, bool value, Error **errp) > > +{ > > + RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); > > + > > + s->have_iopmp_cascade = value; > > +} > > + > > + > > bool virt_is_acpi_enabled(RISCVVirtState *s) > > { > > return s->acpi != ON_OFF_AUTO_OFF; > > @@ -1730,6 +1784,20 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) > > NULL, NULL); > > object_class_property_set_description(oc, "acpi", > > "Enable ACPI"); > > + > > + object_class_property_add_bool(oc, "iopmp", virt_get_iopmp, > > + virt_set_iopmp); > > + object_class_property_set_description(oc, "iopmp", > > + "Set on/off to enable/disable " > > + "iopmp device"); > > + > > + object_class_property_add_bool(oc, "iopmp-cascade", > > + virt_get_iopmp_cascade, > > + virt_set_iopmp_cascade); > > + object_class_property_set_description(oc, "iopmp-cascade", > > + "Set on/off to enable/disable " > > + "iopmp2 device which is cascaded by " > > + "iopmp1 device"); > > } > > > > static const TypeInfo virt_machine_typeinfo = { > > diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h > > index e5c474b26e..5fa2944d29 100644 > > --- a/include/hw/riscv/virt.h > > +++ b/include/hw/riscv/virt.h > > @@ -54,6 +54,8 @@ struct RISCVVirtState { > > > > int fdt_size; > > bool have_aclint; > > + bool have_iopmp; > > + bool have_iopmp_cascade; > > RISCVVirtAIAType aia_type; > > int aia_guests; > > char *oem_id; > > @@ -82,12 +84,18 @@ enum { > > VIRT_PCIE_MMIO, > > VIRT_PCIE_PIO, > > VIRT_PLATFORM_BUS, > > - VIRT_PCIE_ECAM > > + VIRT_PCIE_ECAM, > > + VIRT_IOPMP, > > + VIRT_IOPMP2, > > + VIRT_DMAC, > > }; > > > > enum { > > UART0_IRQ = 10, > > RTC_IRQ = 11, > > + DMAC_IRQ = 12, > > + IOPMP_IRQ = 13, > > + IOPMP2_IRQ = 14, > > VIRTIO_IRQ = 1, /* 1 to 8 */ > > VIRTIO_COUNT = 8, > > PCIE_IRQ = 0x20, /* 32 to 35 */ > > -- > > 2.34.1 > > > > ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2023-11-21 10:09 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2023-11-14 9:47 [PATCH v3 0/4] Support RISC-V IOPMP Ethan Chen via 2023-11-14 9:47 ` [PATCH v3 1/4] hw/core: Add config stream Ethan Chen via 2023-11-21 5:24 ` Alistair Francis 2023-11-21 5:28 ` Alistair Francis 2023-11-21 10:09 ` Ethan Chen via 2023-11-14 9:47 ` [PATCH v3 2/4] Add RISC-V IOPMP support Ethan Chen via 2023-11-21 5:38 ` Alistair Francis 2023-11-14 9:47 ` [PATCH v3 3/4] hw/dma: Add Andes ATCDMAC300 support Ethan Chen via 2023-11-21 5:39 ` Alistair Francis 2023-11-14 9:47 ` [PATCH v3 4/4] hw/riscv/virt: Add IOPMP support Ethan Chen via 2023-11-14 17:50 ` Daniel Henrique Barboza 2023-11-15 1:51 ` Ethan Chen via 2023-11-21 5:22 ` Alistair Francis 2023-11-21 9:54 ` Ethan Chen via
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).