* [PATCH 0/6] Support RISC-V IOPMP
@ 2023-10-25 5:14 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen via @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Ethan Chen
This series implements IOPMP specification v1.0.0-draft4 rapid-k model:
https://github.com/riscv-non-isa/iopmp-spec/blob/main/riscv_iopmp_specification.pdf
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
Ethan Chen (6):
exec/memory: Introduce the translate_size function within the IOMMU
class
system/physmem: IOMMU: Invoke the translate_size function if it is
implemented
exec/memattrs: Add iopmp source id to MemTxAttrs
Add RISC-V IOPMP support
hw/dma: Add Andes ATCDMAC300 support
hw/riscv/virt: Add IOPMP support
hw/dma/Kconfig | 3 +
hw/dma/atcdmac300.c | 435 +++++++++++++++++
hw/dma/meson.build | 1 +
hw/misc/Kconfig | 3 +
hw/misc/meson.build | 1 +
hw/misc/riscv_iopmp.c | 881 ++++++++++++++++++++++++++++++++++
hw/riscv/Kconfig | 2 +
hw/riscv/virt.c | 72 ++-
include/exec/memattrs.h | 3 +
include/exec/memory.h | 19 +
include/hw/dma/atcdmac300.h | 171 +++++++
include/hw/misc/riscv_iopmp.h | 322 +++++++++++++
include/hw/riscv/virt.h | 10 +-
system/physmem.c | 9 +-
14 files changed, 1927 insertions(+), 5 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
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 0/6] Support RISC-V IOPMP
@ 2023-10-25 5:14 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Ethan Chen
This series implements IOPMP specification v1.0.0-draft4 rapid-k model:
https://github.com/riscv-non-isa/iopmp-spec/blob/main/riscv_iopmp_specification.pdf
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
Ethan Chen (6):
exec/memory: Introduce the translate_size function within the IOMMU
class
system/physmem: IOMMU: Invoke the translate_size function if it is
implemented
exec/memattrs: Add iopmp source id to MemTxAttrs
Add RISC-V IOPMP support
hw/dma: Add Andes ATCDMAC300 support
hw/riscv/virt: Add IOPMP support
hw/dma/Kconfig | 3 +
hw/dma/atcdmac300.c | 435 +++++++++++++++++
hw/dma/meson.build | 1 +
hw/misc/Kconfig | 3 +
hw/misc/meson.build | 1 +
hw/misc/riscv_iopmp.c | 881 ++++++++++++++++++++++++++++++++++
hw/riscv/Kconfig | 2 +
hw/riscv/virt.c | 72 ++-
include/exec/memattrs.h | 3 +
include/exec/memory.h | 19 +
include/hw/dma/atcdmac300.h | 171 +++++++
include/hw/misc/riscv_iopmp.h | 322 +++++++++++++
include/hw/riscv/virt.h | 10 +-
system/physmem.c | 9 +-
14 files changed, 1927 insertions(+), 5 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
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 1/6] exec/memory: Introduce the translate_size function within the IOMMU class
@ 2023-10-25 5:14 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen via @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel
Cc: Ethan Chen, Paolo Bonzini, Peter Xu, David Hildenbrand,
Philippe Mathieu-Daudé
IOMMU have size information during translation.
Signed-off-by: Ethan Chen <ethan84@andestech.com>
---
include/exec/memory.h | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 9087d02769..5520b7c8c0 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -396,6 +396,25 @@ struct IOMMUMemoryRegionClass {
*/
IOMMUTLBEntry (*translate)(IOMMUMemoryRegion *iommu, hwaddr addr,
IOMMUAccessFlags flag, int iommu_idx);
+ /**
+ * @translate_size:
+ *
+ * Return a TLB entry that contains a given address and size.
+ *
+ * @iommu: the IOMMUMemoryRegion
+ *
+ * @hwaddr: address to be translated within the memory region
+ *
+ * @size: size to indicate the scope of the entire transaction
+ *
+ * @flag: requested access permission
+ *
+ * @iommu_idx: IOMMU index for the translation
+ */
+ IOMMUTLBEntry (*translate_size)(IOMMUMemoryRegion *iommu, hwaddr addr,
+ hwaddr size, IOMMUAccessFlags flag,
+ int iommu_idx);
+
/**
* @get_min_page_size:
*
--
2.34.1
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 1/6] exec/memory: Introduce the translate_size function within the IOMMU class
@ 2023-10-25 5:14 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel
Cc: Ethan Chen, Paolo Bonzini, Peter Xu, David Hildenbrand,
Philippe Mathieu-Daudé
IOMMU have size information during translation.
Signed-off-by: Ethan Chen <ethan84@andestech.com>
---
include/exec/memory.h | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 9087d02769..5520b7c8c0 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -396,6 +396,25 @@ struct IOMMUMemoryRegionClass {
*/
IOMMUTLBEntry (*translate)(IOMMUMemoryRegion *iommu, hwaddr addr,
IOMMUAccessFlags flag, int iommu_idx);
+ /**
+ * @translate_size:
+ *
+ * Return a TLB entry that contains a given address and size.
+ *
+ * @iommu: the IOMMUMemoryRegion
+ *
+ * @hwaddr: address to be translated within the memory region
+ *
+ * @size: size to indicate the scope of the entire transaction
+ *
+ * @flag: requested access permission
+ *
+ * @iommu_idx: IOMMU index for the translation
+ */
+ IOMMUTLBEntry (*translate_size)(IOMMUMemoryRegion *iommu, hwaddr addr,
+ hwaddr size, IOMMUAccessFlags flag,
+ int iommu_idx);
+
/**
* @get_min_page_size:
*
--
2.34.1
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
@ 2023-10-25 5:14 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen via @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel
Cc: Ethan Chen, Paolo Bonzini, Peter Xu, David Hildenbrand,
Philippe Mathieu-Daudé
Signed-off-by: Ethan Chen <ethan84@andestech.com>
---
system/physmem.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/system/physmem.c b/system/physmem.c
index fc2b0fee01..53b6ab735c 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -432,8 +432,13 @@ static MemoryRegionSection address_space_translate_iommu(IOMMUMemoryRegion *iomm
iommu_idx = imrc->attrs_to_index(iommu_mr, attrs);
}
- iotlb = imrc->translate(iommu_mr, addr, is_write ?
- IOMMU_WO : IOMMU_RO, iommu_idx);
+ if (imrc->translate_size) {
+ iotlb = imrc->translate_size(iommu_mr, addr, *plen_out, is_write ?
+ IOMMU_WO : IOMMU_RO, iommu_idx);
+ } else {
+ iotlb = imrc->translate(iommu_mr, addr, is_write ?
+ IOMMU_WO : IOMMU_RO, iommu_idx);
+ }
if (!(iotlb.perm & (1 << is_write))) {
goto unassigned;
--
2.34.1
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
@ 2023-10-25 5:14 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel
Cc: Ethan Chen, Paolo Bonzini, Peter Xu, David Hildenbrand,
Philippe Mathieu-Daudé
Signed-off-by: Ethan Chen <ethan84@andestech.com>
---
system/physmem.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/system/physmem.c b/system/physmem.c
index fc2b0fee01..53b6ab735c 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -432,8 +432,13 @@ static MemoryRegionSection address_space_translate_iommu(IOMMUMemoryRegion *iomm
iommu_idx = imrc->attrs_to_index(iommu_mr, attrs);
}
- iotlb = imrc->translate(iommu_mr, addr, is_write ?
- IOMMU_WO : IOMMU_RO, iommu_idx);
+ if (imrc->translate_size) {
+ iotlb = imrc->translate_size(iommu_mr, addr, *plen_out, is_write ?
+ IOMMU_WO : IOMMU_RO, iommu_idx);
+ } else {
+ iotlb = imrc->translate(iommu_mr, addr, is_write ?
+ IOMMU_WO : IOMMU_RO, iommu_idx);
+ }
if (!(iotlb.perm & (1 << is_write))) {
goto unassigned;
--
2.34.1
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 3/6] exec/memattrs: Add iopmp source id to MemTxAttrs
@ 2023-10-25 5:14 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen via @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Ethan Chen
Signed-off-by: Ethan Chen <ethan84@andestech.com>
---
include/exec/memattrs.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h
index d04170aa27..e27b4fab00 100644
--- a/include/exec/memattrs.h
+++ b/include/exec/memattrs.h
@@ -64,6 +64,9 @@ typedef struct MemTxAttrs {
unsigned int target_tlb_bit0 : 1;
unsigned int target_tlb_bit1 : 1;
unsigned int target_tlb_bit2 : 1;
+
+ /* IOPMP support up to 65535 sources */
+ unsigned int iopmp_sid:16;
} MemTxAttrs;
/* Bus masters which don't specify any attributes will get this,
--
2.34.1
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 3/6] exec/memattrs: Add iopmp source id to MemTxAttrs
@ 2023-10-25 5:14 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Ethan Chen
Signed-off-by: Ethan Chen <ethan84@andestech.com>
---
include/exec/memattrs.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h
index d04170aa27..e27b4fab00 100644
--- a/include/exec/memattrs.h
+++ b/include/exec/memattrs.h
@@ -64,6 +64,9 @@ typedef struct MemTxAttrs {
unsigned int target_tlb_bit0 : 1;
unsigned int target_tlb_bit1 : 1;
unsigned int target_tlb_bit2 : 1;
+
+ /* IOPMP support up to 65535 sources */
+ unsigned int iopmp_sid:16;
} MemTxAttrs;
/* Bus masters which don't specify any attributes will get this,
--
2.34.1
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 4/6] Add RISC-V IOPMP support
@ 2023-10-25 5:14 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen via @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Ethan Chen, Paolo Bonzini
Support specification Version 1.0.0-draft4 rapid-k model.
Signed-off-by: Ethan Chen <ethan84@andestech.com>
---
hw/misc/Kconfig | 3 +
hw/misc/meson.build | 1 +
hw/misc/riscv_iopmp.c | 881 ++++++++++++++++++++++++++++++++++
include/hw/misc/riscv_iopmp.h | 322 +++++++++++++
4 files changed, 1207 insertions(+)
create mode 100644 hw/misc/riscv_iopmp.c
create mode 100644 include/hw/misc/riscv_iopmp.h
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index dba41afe67..8c63eb4cef 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -197,4 +197,7 @@ config DJMEMC
config IOSB
bool
+config RISCV_IOPMP
+ bool
+
source macio/Kconfig
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index f60de33f9a..7826ed9b7b 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..5f9535501a
--- /dev/null
+++ b/hw/misc/riscv_iopmp.c
@@ -0,0 +1,881 @@
+/*
+ * 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 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 support 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 support 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) {
+ 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) {
+ 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 addr,
+ hwaddr size, uint32_t info)
+{
+ if (addr > s->prev_error[id].addr_start &&
+ addr + size == s->prev_error[id].addr_end &&
+ info == s->prev_error[id].reqinfo) {
+ /* skip following error */
+ ;
+ } else {
+ s->prev_error[id].addr_start = addr;
+ s->prev_error[id].addr_end = addr + size;
+ s->prev_error[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 = addr;
+ 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_size(IOMMUMemoryRegion *iommu,
+ hwaddr addr, hwaddr size,
+ IOMMUAccessFlags flags,
+ int sid)
+{
+ bool is_stalled = false;
+ IopmpState *s = IOPMP(container_of(iommu, IopmpState, iommu));
+ 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, addr, addr + size - 1,
+ &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, addr, size,
+ (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;
+ if (s->sid_transl_en) {
+ /* next iopmp */
+ if (s->next_iommu) {
+ return iopmp_translate_size(s->next_iommu, addr, size,
+ flags, s->sid_transl);
+ } else {
+ error_report("Next iopmp is not found.");
+ exit(1);
+ }
+ }
+ }
+ } else {
+ if (result == ENTRY_PAR_HIT) {
+ iopmp_error_reaction(s, sid, addr, size,
+ (1 << ERR_REQINFO_PAR_HIT) |
+ ((flags - 1) << ERR_REQINFO_TYPE));
+ } else {
+ iopmp_error_reaction(s, sid, addr, size,
+ (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);
+
+ 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");
+}
+
+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)
+{
+ /* Get source id(SID) */
+ return attrs.iopmp_sid;
+}
+
+
+static void iopmp_iommu_memory_region_class_init(ObjectClass *klass, void *data)
+{
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
+
+ imrc->translate_size = iopmp_translate_size;
+ 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 void
+iopmp_register_types(void)
+{
+ type_register_static(&iopmp_info);
+ type_register_static(&iopmp_iommu_memory_region_info);
+}
+
+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..b4e79ef0da
--- /dev/null
+++ b/include/hw/misc/riscv_iopmp.h
@@ -0,0 +1,322 @@
+/*
+ * 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"
+
+#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
+
+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_error_detail {
+ uint32_t reqinfo;
+ target_ulong addr_start;
+ target_ulong addr_end;
+} iopmp_error_detail;
+
+typedef struct IopmpState {
+ SysBusDevice parent_obj;
+ iopmp_addr_t entry_addr[IOPMP_MAX_ENTRY_NUM];
+ iopmp_error_detail prev_error[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;
+
+ 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
--
2.34.1
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 4/6] Add RISC-V IOPMP support
@ 2023-10-25 5:14 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Ethan Chen, Paolo Bonzini
Support specification Version 1.0.0-draft4 rapid-k model.
Signed-off-by: Ethan Chen <ethan84@andestech.com>
---
hw/misc/Kconfig | 3 +
hw/misc/meson.build | 1 +
hw/misc/riscv_iopmp.c | 881 ++++++++++++++++++++++++++++++++++
include/hw/misc/riscv_iopmp.h | 322 +++++++++++++
4 files changed, 1207 insertions(+)
create mode 100644 hw/misc/riscv_iopmp.c
create mode 100644 include/hw/misc/riscv_iopmp.h
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index dba41afe67..8c63eb4cef 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -197,4 +197,7 @@ config DJMEMC
config IOSB
bool
+config RISCV_IOPMP
+ bool
+
source macio/Kconfig
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index f60de33f9a..7826ed9b7b 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..5f9535501a
--- /dev/null
+++ b/hw/misc/riscv_iopmp.c
@@ -0,0 +1,881 @@
+/*
+ * 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 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 support 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 support 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) {
+ 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) {
+ 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 addr,
+ hwaddr size, uint32_t info)
+{
+ if (addr > s->prev_error[id].addr_start &&
+ addr + size == s->prev_error[id].addr_end &&
+ info == s->prev_error[id].reqinfo) {
+ /* skip following error */
+ ;
+ } else {
+ s->prev_error[id].addr_start = addr;
+ s->prev_error[id].addr_end = addr + size;
+ s->prev_error[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 = addr;
+ 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_size(IOMMUMemoryRegion *iommu,
+ hwaddr addr, hwaddr size,
+ IOMMUAccessFlags flags,
+ int sid)
+{
+ bool is_stalled = false;
+ IopmpState *s = IOPMP(container_of(iommu, IopmpState, iommu));
+ 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, addr, addr + size - 1,
+ &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, addr, size,
+ (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;
+ if (s->sid_transl_en) {
+ /* next iopmp */
+ if (s->next_iommu) {
+ return iopmp_translate_size(s->next_iommu, addr, size,
+ flags, s->sid_transl);
+ } else {
+ error_report("Next iopmp is not found.");
+ exit(1);
+ }
+ }
+ }
+ } else {
+ if (result == ENTRY_PAR_HIT) {
+ iopmp_error_reaction(s, sid, addr, size,
+ (1 << ERR_REQINFO_PAR_HIT) |
+ ((flags - 1) << ERR_REQINFO_TYPE));
+ } else {
+ iopmp_error_reaction(s, sid, addr, size,
+ (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);
+
+ 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");
+}
+
+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)
+{
+ /* Get source id(SID) */
+ return attrs.iopmp_sid;
+}
+
+
+static void iopmp_iommu_memory_region_class_init(ObjectClass *klass, void *data)
+{
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
+
+ imrc->translate_size = iopmp_translate_size;
+ 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 void
+iopmp_register_types(void)
+{
+ type_register_static(&iopmp_info);
+ type_register_static(&iopmp_iommu_memory_region_info);
+}
+
+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..b4e79ef0da
--- /dev/null
+++ b/include/hw/misc/riscv_iopmp.h
@@ -0,0 +1,322 @@
+/*
+ * 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"
+
+#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
+
+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_error_detail {
+ uint32_t reqinfo;
+ target_ulong addr_start;
+ target_ulong addr_end;
+} iopmp_error_detail;
+
+typedef struct IopmpState {
+ SysBusDevice parent_obj;
+ iopmp_addr_t entry_addr[IOPMP_MAX_ENTRY_NUM];
+ iopmp_error_detail prev_error[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;
+
+ 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
--
2.34.1
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 5/6] hw/dma: Add Andes ATCDMAC300 support
@ 2023-10-25 5:14 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen via @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Ethan Chen, Paolo Bonzini
Signed-off-by: Ethan Chen <ethan84@andestech.com>
---
hw/dma/Kconfig | 3 +
hw/dma/atcdmac300.c | 435 ++++++++++++++++++++++++++++++++++++
hw/dma/meson.build | 1 +
include/hw/dma/atcdmac300.h | 171 ++++++++++++++
4 files changed, 610 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..b3f4edc873 100644
--- a/hw/dma/Kconfig
+++ b/hw/dma/Kconfig
@@ -30,3 +30,6 @@ config SIFIVE_PDMA
config XLNX_CSU_DMA
bool
select REGISTER
+
+config ATCDMAC300
+ bool
diff --git a/hw/dma/atcdmac300.c b/hw/dma/atcdmac300.c
new file mode 100644
index 0000000000..4cc37060d6
--- /dev/null
+++ b/hw/dma/atcdmac300.c
@@ -0,0 +1,435 @@
+/*
+ * 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"
+
+/* #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 MemTxResult dma_iopmp_read(ATCDMAC300State *s, hwaddr addr, void *buf,
+ hwaddr len)
+{
+ if (s->iopmp_as) {
+ MemTxAttrs dma_attrs = {.iopmp_sid = s->sid};
+ return address_space_rw(s->iopmp_as, addr, dma_attrs,
+ buf, len, false);
+ }
+ cpu_physical_memory_read(addr, buf, len);
+ return MEMTX_OK;
+}
+
+static MemTxResult dma_iopmp_write(ATCDMAC300State *s, hwaddr addr, void *buf,
+ hwaddr len)
+{
+ if (s->iopmp_as) {
+ MemTxAttrs dma_attrs = {.iopmp_sid = s->sid};
+ return address_space_rw(s->iopmp_as, addr, dma_attrs,
+ buf, len, true);
+ }
+ cpu_physical_memory_write(addr, buf, len);
+ return MEMTX_OK;
+}
+
+static void atcdmac300_co_run(void *opaque)
+{
+ ATCDMAC300State *s = opaque;
+ int64_t remain_size, burst_remain_size, copy_size, copy_size_dst;
+ uint64_t src_addr, dst_addr, src_width, dst_width, burst_size,
+ src_addr_ctl, dst_addr_ctl, int_tc_mask, int_err_mask,
+ int_abort_mask;
+ uint64_t buf[ATCDMAC300_MAX_BURST_SIZE];
+ int result = 0;
+
+ while (1) {
+ for (int ch = 0; ch < ATCDMAC300_MAX_CHAN; ch++) {
+ if (((s->chan[ch].ChnCtrl >> CHAN_CTL_ENABLE) & 0x1) == 0x1) {
+ 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 = 1 << src_width;
+ dst_width = 1 << dst_width;
+ burst_size = 1 << burst_size;
+ remain_size = s->chan[ch].ChnTranSize * src_width;
+ 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;
+ if (remain_size && burst_size < (1 << 11) &&
+ src_width < (1 << 6) && dst_width < (1 << 6) &&
+ (src_addr & (src_width - 1)) == 0 &&
+ (dst_addr & (dst_width - 1)) == 0 &&
+ (remain_size & (dst_width - 1)) == 0 &&
+ (burst_size * src_width & (dst_width - 1)) == 0) {
+
+ while (remain_size > 0) {
+ /* check current abort status before a brust start*/
+ if (s->ChAbort & (1 << ch)) {
+ result = DMA_ABT_RESULT;
+ 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);
+ }
+ break;
+ }
+
+ int i;
+ for (i = 0; i < burst_size; i++) {
+ if (remain_size <= 0) {
+ break;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ copy_size = MIN(remain_size, src_width);
+ result = dma_iopmp_read(s, src_addr, buf,
+ copy_size);
+ while (result == MEMTX_IOPMP_STALL) {
+ qemu_coroutine_yield();
+ result = dma_iopmp_read(s, src_addr, buf,
+ copy_size);
+ }
+ burst_remain_size = copy_size;
+ uint64_t *curr_ptr = buf;
+ while (burst_remain_size > 0) {
+ copy_size_dst = MIN(burst_remain_size,
+ dst_width);
+ result = dma_iopmp_write(s, dst_addr, curr_ptr,
+ copy_size_dst);
+ while (result == MEMTX_IOPMP_STALL) {
+ qemu_coroutine_yield();
+ result = dma_iopmp_read(s, src_addr, buf,
+ copy_size);
+ }
+ curr_ptr += copy_size_dst;
+ burst_remain_size -= copy_size_dst;
+ }
+
+ if (result != MEMTX_OK) {
+ break;
+ }
+
+ remain_size -= copy_size;
+
+ if (src_addr_ctl == 0) {
+ src_addr += copy_size;
+ }
+ if (src_addr_ctl == 1) {
+ src_addr -= copy_size;
+ }
+ if (dst_addr_ctl == 0) {
+ dst_addr += copy_size;
+ }
+ if (dst_addr_ctl == 1) {
+ dst_addr -= copy_size;
+ }
+ }
+ if (result != MEMTX_OK) {
+ break;
+ }
+ qemu_coroutine_yield();
+ }
+ if (result == MEMTX_OK) {
+ 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);
+ }
+ } else if (result != DMA_ABT_RESULT) {
+ 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);
+ }
+ }
+ } 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);
+ }
+ }
+ }
+ }
+ 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_as(DeviceState *dev, AddressSpace *iopmp_as,
+ uint32_t sid)
+{
+ ATCDMAC300State *s = ATCDMAC300(dev);
+ s->iopmp_as = iopmp_as;
+ 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..c2688bc18f
--- /dev/null
+++ b/include/hw/dma/atcdmac300.h
@@ -0,0 +1,171 @@
+/*
+ * 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"
+
+#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 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;
+ 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_as(DeviceState *dev, AddressSpace *iopmp_as,
+ uint32_t sid);
+
+#endif /* ATCDMAC300_H */
--
2.34.1
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 5/6] hw/dma: Add Andes ATCDMAC300 support
@ 2023-10-25 5:14 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel; +Cc: Ethan Chen, Paolo Bonzini
Signed-off-by: Ethan Chen <ethan84@andestech.com>
---
hw/dma/Kconfig | 3 +
hw/dma/atcdmac300.c | 435 ++++++++++++++++++++++++++++++++++++
hw/dma/meson.build | 1 +
include/hw/dma/atcdmac300.h | 171 ++++++++++++++
4 files changed, 610 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..b3f4edc873 100644
--- a/hw/dma/Kconfig
+++ b/hw/dma/Kconfig
@@ -30,3 +30,6 @@ config SIFIVE_PDMA
config XLNX_CSU_DMA
bool
select REGISTER
+
+config ATCDMAC300
+ bool
diff --git a/hw/dma/atcdmac300.c b/hw/dma/atcdmac300.c
new file mode 100644
index 0000000000..4cc37060d6
--- /dev/null
+++ b/hw/dma/atcdmac300.c
@@ -0,0 +1,435 @@
+/*
+ * 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"
+
+/* #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 MemTxResult dma_iopmp_read(ATCDMAC300State *s, hwaddr addr, void *buf,
+ hwaddr len)
+{
+ if (s->iopmp_as) {
+ MemTxAttrs dma_attrs = {.iopmp_sid = s->sid};
+ return address_space_rw(s->iopmp_as, addr, dma_attrs,
+ buf, len, false);
+ }
+ cpu_physical_memory_read(addr, buf, len);
+ return MEMTX_OK;
+}
+
+static MemTxResult dma_iopmp_write(ATCDMAC300State *s, hwaddr addr, void *buf,
+ hwaddr len)
+{
+ if (s->iopmp_as) {
+ MemTxAttrs dma_attrs = {.iopmp_sid = s->sid};
+ return address_space_rw(s->iopmp_as, addr, dma_attrs,
+ buf, len, true);
+ }
+ cpu_physical_memory_write(addr, buf, len);
+ return MEMTX_OK;
+}
+
+static void atcdmac300_co_run(void *opaque)
+{
+ ATCDMAC300State *s = opaque;
+ int64_t remain_size, burst_remain_size, copy_size, copy_size_dst;
+ uint64_t src_addr, dst_addr, src_width, dst_width, burst_size,
+ src_addr_ctl, dst_addr_ctl, int_tc_mask, int_err_mask,
+ int_abort_mask;
+ uint64_t buf[ATCDMAC300_MAX_BURST_SIZE];
+ int result = 0;
+
+ while (1) {
+ for (int ch = 0; ch < ATCDMAC300_MAX_CHAN; ch++) {
+ if (((s->chan[ch].ChnCtrl >> CHAN_CTL_ENABLE) & 0x1) == 0x1) {
+ 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 = 1 << src_width;
+ dst_width = 1 << dst_width;
+ burst_size = 1 << burst_size;
+ remain_size = s->chan[ch].ChnTranSize * src_width;
+ 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;
+ if (remain_size && burst_size < (1 << 11) &&
+ src_width < (1 << 6) && dst_width < (1 << 6) &&
+ (src_addr & (src_width - 1)) == 0 &&
+ (dst_addr & (dst_width - 1)) == 0 &&
+ (remain_size & (dst_width - 1)) == 0 &&
+ (burst_size * src_width & (dst_width - 1)) == 0) {
+
+ while (remain_size > 0) {
+ /* check current abort status before a brust start*/
+ if (s->ChAbort & (1 << ch)) {
+ result = DMA_ABT_RESULT;
+ 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);
+ }
+ break;
+ }
+
+ int i;
+ for (i = 0; i < burst_size; i++) {
+ if (remain_size <= 0) {
+ break;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ copy_size = MIN(remain_size, src_width);
+ result = dma_iopmp_read(s, src_addr, buf,
+ copy_size);
+ while (result == MEMTX_IOPMP_STALL) {
+ qemu_coroutine_yield();
+ result = dma_iopmp_read(s, src_addr, buf,
+ copy_size);
+ }
+ burst_remain_size = copy_size;
+ uint64_t *curr_ptr = buf;
+ while (burst_remain_size > 0) {
+ copy_size_dst = MIN(burst_remain_size,
+ dst_width);
+ result = dma_iopmp_write(s, dst_addr, curr_ptr,
+ copy_size_dst);
+ while (result == MEMTX_IOPMP_STALL) {
+ qemu_coroutine_yield();
+ result = dma_iopmp_read(s, src_addr, buf,
+ copy_size);
+ }
+ curr_ptr += copy_size_dst;
+ burst_remain_size -= copy_size_dst;
+ }
+
+ if (result != MEMTX_OK) {
+ break;
+ }
+
+ remain_size -= copy_size;
+
+ if (src_addr_ctl == 0) {
+ src_addr += copy_size;
+ }
+ if (src_addr_ctl == 1) {
+ src_addr -= copy_size;
+ }
+ if (dst_addr_ctl == 0) {
+ dst_addr += copy_size;
+ }
+ if (dst_addr_ctl == 1) {
+ dst_addr -= copy_size;
+ }
+ }
+ if (result != MEMTX_OK) {
+ break;
+ }
+ qemu_coroutine_yield();
+ }
+ if (result == MEMTX_OK) {
+ 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);
+ }
+ } else if (result != DMA_ABT_RESULT) {
+ 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);
+ }
+ }
+ } 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);
+ }
+ }
+ }
+ }
+ 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_as(DeviceState *dev, AddressSpace *iopmp_as,
+ uint32_t sid)
+{
+ ATCDMAC300State *s = ATCDMAC300(dev);
+ s->iopmp_as = iopmp_as;
+ 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..c2688bc18f
--- /dev/null
+++ b/include/hw/dma/atcdmac300.h
@@ -0,0 +1,171 @@
+/*
+ * 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"
+
+#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 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;
+ 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_as(DeviceState *dev, AddressSpace *iopmp_as,
+ uint32_t sid);
+
+#endif /* ATCDMAC300_H */
--
2.34.1
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 6/6] hw/riscv/virt: Add IOPMP support
2023-10-25 5:14 ` Ethan Chen
(?)
@ 2023-10-25 5:14 ` Ethan Chen via
-1 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen via @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel
Cc: Ethan Chen, Paolo Bonzini, Palmer Dabbelt, Alistair Francis,
Bin Meng, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei,
open list:RISC-V TCG CPUs
- Add 'iopmp=on' option to enable iopmp
- 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 9de578c756..d3d4320c73 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -53,6 +53,9 @@
#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 +100,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 +1533,32 @@ 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(DEVICE(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_as(dmac_dev, &(IOPMP(iopmp_dev)->iopmp_as), 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] 34+ messages in thread
* [PATCH 6/6] hw/riscv/virt: Add IOPMP support
@ 2023-10-25 5:14 ` Ethan Chen via
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen via @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel
Cc: Ethan Chen, Paolo Bonzini, Palmer Dabbelt, Alistair Francis,
Bin Meng, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei,
open list:RISC-V TCG CPUs
- Add 'iopmp=on' option to enable iopmp
- 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 9de578c756..d3d4320c73 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -53,6 +53,9 @@
#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 +100,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 +1533,32 @@ 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(DEVICE(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_as(dmac_dev, &(IOPMP(iopmp_dev)->iopmp_as), 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] 34+ messages in thread
* [PATCH 6/6] hw/riscv/virt: Add IOPMP support
@ 2023-10-25 5:14 ` Ethan Chen via
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen @ 2023-10-25 5:14 UTC (permalink / raw)
To: qemu-devel
Cc: Ethan Chen, Paolo Bonzini, Palmer Dabbelt, Alistair Francis,
Bin Meng, Weiwei Li, Daniel Henrique Barboza, Liu Zhiwei,
open list:RISC-V TCG CPUs
- Add 'iopmp=on' option to enable iopmp
- 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 9de578c756..d3d4320c73 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -53,6 +53,9 @@
#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 +100,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 +1533,32 @@ 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(DEVICE(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_as(dmac_dev, &(IOPMP(iopmp_dev)->iopmp_as), 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] 34+ messages in thread
* Re: [PATCH 1/6] exec/memory: Introduce the translate_size function within the IOMMU class
2023-10-25 5:14 ` Ethan Chen
(?)
@ 2023-10-25 14:56 ` David Hildenbrand
2023-10-26 7:14 ` Ethan Chen
-1 siblings, 1 reply; 34+ messages in thread
From: David Hildenbrand @ 2023-10-25 14:56 UTC (permalink / raw)
To: Ethan Chen, qemu-devel
Cc: Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé
On 25.10.23 07:14, Ethan Chen wrote:
> IOMMU have size information during translation.
>
Can you add some more information why we would want this and how the
backend can do "better" things with the size at hand?
Note that I was not CCed on the cover letter.
> Signed-off-by: Ethan Chen <ethan84@andestech.com>
> ---
> include/exec/memory.h | 19 +++++++++++++++++++
> 1 file changed, 19 insertions(+)
>
> diff --git a/include/exec/memory.h b/include/exec/memory.h
> index 9087d02769..5520b7c8c0 100644
> --- a/include/exec/memory.h
> +++ b/include/exec/memory.h
> @@ -396,6 +396,25 @@ struct IOMMUMemoryRegionClass {
> */
> IOMMUTLBEntry (*translate)(IOMMUMemoryRegion *iommu, hwaddr addr,
> IOMMUAccessFlags flag, int iommu_idx);
> + /**
> + * @translate_size:
> + *
> + * Return a TLB entry that contains a given address and size.
> + *
> + * @iommu: the IOMMUMemoryRegion
> + *
> + * @hwaddr: address to be translated within the memory region
> + *
> + * @size: size to indicate the scope of the entire transaction
> + *
> + * @flag: requested access permission
> + *
> + * @iommu_idx: IOMMU index for the translation
> + */
> + IOMMUTLBEntry (*translate_size)(IOMMUMemoryRegion *iommu, hwaddr addr,
> + hwaddr size, IOMMUAccessFlags flag,
> + int iommu_idx);
> +
> /**
> * @get_min_page_size:
> *
--
Cheers,
David / dhildenb
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
2023-10-25 5:14 ` Ethan Chen
(?)
@ 2023-10-25 15:14 ` Peter Xu
2023-10-26 6:48 ` Ethan Chen
2023-10-27 3:28 ` Ethan Chen
-1 siblings, 2 replies; 34+ messages in thread
From: Peter Xu @ 2023-10-25 15:14 UTC (permalink / raw)
To: Ethan Chen
Cc: qemu-devel, Paolo Bonzini, David Hildenbrand,
Philippe Mathieu-Daudé
On Wed, Oct 25, 2023 at 01:14:26PM +0800, Ethan Chen wrote:
> Signed-off-by: Ethan Chen <ethan84@andestech.com>
> ---
> system/physmem.c | 9 +++++++--
> 1 file changed, 7 insertions(+), 2 deletions(-)
>
> diff --git a/system/physmem.c b/system/physmem.c
> index fc2b0fee01..53b6ab735c 100644
> --- a/system/physmem.c
> +++ b/system/physmem.c
> @@ -432,8 +432,13 @@ static MemoryRegionSection address_space_translate_iommu(IOMMUMemoryRegion *iomm
> iommu_idx = imrc->attrs_to_index(iommu_mr, attrs);
> }
>
> - iotlb = imrc->translate(iommu_mr, addr, is_write ?
> - IOMMU_WO : IOMMU_RO, iommu_idx);
> + if (imrc->translate_size) {
> + iotlb = imrc->translate_size(iommu_mr, addr, *plen_out, is_write ?
> + IOMMU_WO : IOMMU_RO, iommu_idx);
> + } else {
> + iotlb = imrc->translate(iommu_mr, addr, is_write ?
> + IOMMU_WO : IOMMU_RO, iommu_idx);
> + }
Currently the translation size is encoded in iotlb.addr_mask. Can riscv do
the same?
For example, lookup addr in match_entry_md() ranges, report size back into
iotlb.addr_mask, rather than enforcing *plen_out range always resides in
one translation only.
IMHO it's actually legal if *plen_out covers more than one IOMMU
translations. QEMU memory core should have taken care of that by
separately translate the ranges and apply RW on top. With current proposal
of translate_size() I think it'll fail instead, which is not wanted.
Thanks,
--
Peter Xu
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
@ 2023-10-26 6:48 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen via @ 2023-10-26 6:48 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Paolo Bonzini, David Hildenbrand,
Philippe Mathieu-Daudé
On Wed, Oct 25, 2023 at 11:14:42AM -0400, Peter Xu wrote:
> On Wed, Oct 25, 2023 at 01:14:26PM +0800, Ethan Chen wrote:
> > Signed-off-by: Ethan Chen <ethan84@andestech.com>
> > ---
> > system/physmem.c | 9 +++++++--
> > 1 file changed, 7 insertions(+), 2 deletions(-)
> >
> > diff --git a/system/physmem.c b/system/physmem.c
> > index fc2b0fee01..53b6ab735c 100644
> > --- a/system/physmem.c
> > +++ b/system/physmem.c
> > @@ -432,8 +432,13 @@ static MemoryRegionSection address_space_translate_iommu(IOMMUMemoryRegion *iomm
> > iommu_idx = imrc->attrs_to_index(iommu_mr, attrs);
> > }
> >
> > - iotlb = imrc->translate(iommu_mr, addr, is_write ?
> > - IOMMU_WO : IOMMU_RO, iommu_idx);
> > + if (imrc->translate_size) {
> > + iotlb = imrc->translate_size(iommu_mr, addr, *plen_out, is_write ?
> > + IOMMU_WO : IOMMU_RO, iommu_idx);
> > + } else {
> > + iotlb = imrc->translate(iommu_mr, addr, is_write ?
> > + IOMMU_WO : IOMMU_RO, iommu_idx);
> > + }
>
> Currently the translation size is encoded in iotlb.addr_mask. Can riscv do
> the same?
Riscv do the same, so translation size may be reduced by iotlb.addr_mask.
>
> For example, lookup addr in match_entry_md() ranges, report size back into
> iotlb.addr_mask, rather than enforcing *plen_out range always resides in
> one translation only.
>
> IMHO it's actually legal if *plen_out covers more than one IOMMU
> translations. QEMU memory core should have taken care of that by
> separately translate the ranges and apply RW on top. With current proposal
> of translate_size() I think it'll fail instead, which is not wanted.
>
My target is to support IOPMP partially hit error. IOPMP checks whole memory
access region is in the same entry. If not, reject the access instead of modify
the access size.
Because most of IOPMP permisson checking features can be implemented by
current IOMMU class, so I add this function in IOMMU class. There may be
other more suitable ways to support partially hit error.
> Thanks,
>
> --
> Peter Xu
>
Thanks,
Ethan Chen
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
@ 2023-10-26 6:48 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen @ 2023-10-26 6:48 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Paolo Bonzini, David Hildenbrand,
Philippe Mathieu-Daudé
On Wed, Oct 25, 2023 at 11:14:42AM -0400, Peter Xu wrote:
> On Wed, Oct 25, 2023 at 01:14:26PM +0800, Ethan Chen wrote:
> > Signed-off-by: Ethan Chen <ethan84@andestech.com>
> > ---
> > system/physmem.c | 9 +++++++--
> > 1 file changed, 7 insertions(+), 2 deletions(-)
> >
> > diff --git a/system/physmem.c b/system/physmem.c
> > index fc2b0fee01..53b6ab735c 100644
> > --- a/system/physmem.c
> > +++ b/system/physmem.c
> > @@ -432,8 +432,13 @@ static MemoryRegionSection address_space_translate_iommu(IOMMUMemoryRegion *iomm
> > iommu_idx = imrc->attrs_to_index(iommu_mr, attrs);
> > }
> >
> > - iotlb = imrc->translate(iommu_mr, addr, is_write ?
> > - IOMMU_WO : IOMMU_RO, iommu_idx);
> > + if (imrc->translate_size) {
> > + iotlb = imrc->translate_size(iommu_mr, addr, *plen_out, is_write ?
> > + IOMMU_WO : IOMMU_RO, iommu_idx);
> > + } else {
> > + iotlb = imrc->translate(iommu_mr, addr, is_write ?
> > + IOMMU_WO : IOMMU_RO, iommu_idx);
> > + }
>
> Currently the translation size is encoded in iotlb.addr_mask. Can riscv do
> the same?
Riscv do the same, so translation size may be reduced by iotlb.addr_mask.
>
> For example, lookup addr in match_entry_md() ranges, report size back into
> iotlb.addr_mask, rather than enforcing *plen_out range always resides in
> one translation only.
>
> IMHO it's actually legal if *plen_out covers more than one IOMMU
> translations. QEMU memory core should have taken care of that by
> separately translate the ranges and apply RW on top. With current proposal
> of translate_size() I think it'll fail instead, which is not wanted.
>
My target is to support IOPMP partially hit error. IOPMP checks whole memory
access region is in the same entry. If not, reject the access instead of modify
the access size.
Because most of IOPMP permisson checking features can be implemented by
current IOMMU class, so I add this function in IOMMU class. There may be
other more suitable ways to support partially hit error.
> Thanks,
>
> --
> Peter Xu
>
Thanks,
Ethan Chen
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 1/6] exec/memory: Introduce the translate_size function within the IOMMU class
@ 2023-10-26 7:14 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen via @ 2023-10-26 7:14 UTC (permalink / raw)
To: David Hildenbrand
Cc: qemu-devel, Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé
On Wed, Oct 25, 2023 at 04:56:22PM +0200, David Hildenbrand wrote:
> On 25.10.23 07:14, Ethan Chen wrote:
> > IOMMU have size information during translation.
> >
>
> Can you add some more information why we would want this and how the backend
> can do "better" things with the size at hand?
>
With size information, IOMMU can reject a memory access which is patially in
valid region.
Currently translation function limit memory access size with a mask, so the
valid part of access will success. My target is to detect partially hit and
reject whole access. Translation function cannot detect partially hit because
it lacks size information.
> Note that I was not CCed on the cover letter.
>
> > Signed-off-by: Ethan Chen <ethan84@andestech.com>
> > ---
> > include/exec/memory.h | 19 +++++++++++++++++++
> > 1 file changed, 19 insertions(+)
> >
> > diff --git a/include/exec/memory.h b/include/exec/memory.h
> > index 9087d02769..5520b7c8c0 100644
> > --- a/include/exec/memory.h
> > +++ b/include/exec/memory.h
> > @@ -396,6 +396,25 @@ struct IOMMUMemoryRegionClass {
> > */
> > IOMMUTLBEntry (*translate)(IOMMUMemoryRegion *iommu, hwaddr addr,
> > IOMMUAccessFlags flag, int iommu_idx);
> > + /**
> > + * @translate_size:
> > + *
> > + * Return a TLB entry that contains a given address and size.
> > + *
> > + * @iommu: the IOMMUMemoryRegion
> > + *
> > + * @hwaddr: address to be translated within the memory region
> > + *
> > + * @size: size to indicate the scope of the entire transaction
> > + *
> > + * @flag: requested access permission
> > + *
> > + * @iommu_idx: IOMMU index for the translation
> > + */
> > + IOMMUTLBEntry (*translate_size)(IOMMUMemoryRegion *iommu, hwaddr addr,
> > + hwaddr size, IOMMUAccessFlags flag,
> > + int iommu_idx);
> > +
> > /**
> > * @get_min_page_size:
> > *
>
> --
> Cheers,
>
> David / dhildenb
>
Thanks,
Ethan Chen
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 1/6] exec/memory: Introduce the translate_size function within the IOMMU class
@ 2023-10-26 7:14 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen @ 2023-10-26 7:14 UTC (permalink / raw)
To: David Hildenbrand
Cc: qemu-devel, Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé
On Wed, Oct 25, 2023 at 04:56:22PM +0200, David Hildenbrand wrote:
> On 25.10.23 07:14, Ethan Chen wrote:
> > IOMMU have size information during translation.
> >
>
> Can you add some more information why we would want this and how the backend
> can do "better" things with the size at hand?
>
With size information, IOMMU can reject a memory access which is patially in
valid region.
Currently translation function limit memory access size with a mask, so the
valid part of access will success. My target is to detect partially hit and
reject whole access. Translation function cannot detect partially hit because
it lacks size information.
> Note that I was not CCed on the cover letter.
>
> > Signed-off-by: Ethan Chen <ethan84@andestech.com>
> > ---
> > include/exec/memory.h | 19 +++++++++++++++++++
> > 1 file changed, 19 insertions(+)
> >
> > diff --git a/include/exec/memory.h b/include/exec/memory.h
> > index 9087d02769..5520b7c8c0 100644
> > --- a/include/exec/memory.h
> > +++ b/include/exec/memory.h
> > @@ -396,6 +396,25 @@ struct IOMMUMemoryRegionClass {
> > */
> > IOMMUTLBEntry (*translate)(IOMMUMemoryRegion *iommu, hwaddr addr,
> > IOMMUAccessFlags flag, int iommu_idx);
> > + /**
> > + * @translate_size:
> > + *
> > + * Return a TLB entry that contains a given address and size.
> > + *
> > + * @iommu: the IOMMUMemoryRegion
> > + *
> > + * @hwaddr: address to be translated within the memory region
> > + *
> > + * @size: size to indicate the scope of the entire transaction
> > + *
> > + * @flag: requested access permission
> > + *
> > + * @iommu_idx: IOMMU index for the translation
> > + */
> > + IOMMUTLBEntry (*translate_size)(IOMMUMemoryRegion *iommu, hwaddr addr,
> > + hwaddr size, IOMMUAccessFlags flag,
> > + int iommu_idx);
> > +
> > /**
> > * @get_min_page_size:
> > *
>
> --
> Cheers,
>
> David / dhildenb
>
Thanks,
Ethan Chen
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 1/6] exec/memory: Introduce the translate_size function within the IOMMU class
2023-10-26 7:14 ` Ethan Chen
(?)
@ 2023-10-26 7:26 ` David Hildenbrand
-1 siblings, 0 replies; 34+ messages in thread
From: David Hildenbrand @ 2023-10-26 7:26 UTC (permalink / raw)
To: Ethan Chen
Cc: qemu-devel, Paolo Bonzini, Peter Xu, Philippe Mathieu-Daudé
On 26.10.23 09:14, Ethan Chen wrote:
> On Wed, Oct 25, 2023 at 04:56:22PM +0200, David Hildenbrand wrote:
>> On 25.10.23 07:14, Ethan Chen wrote:
>>> IOMMU have size information during translation.
>>>
>>
>> Can you add some more information why we would want this and how the backend
>> can do "better" things with the size at hand?
>>
> With size information, IOMMU can reject a memory access which is patially in
> valid region.
>
> Currently translation function limit memory access size with a mask, so the
> valid part of access will success. My target is to detect partially hit and
> reject whole access. Translation function cannot detect partially hit because
> it lacks size information.
Thanks; please add some of that to the patch description.
--
Cheers,
David / dhildenb
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 0/6] Support RISC-V IOPMP
@ 2023-10-26 12:02 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen via @ 2023-10-26 12:02 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Xu, David Hildenbrand
I found that after add size information it is still not enough for IOPMP to
reject partially hit error. Access is separated in flatview_read_continue and
lost the start address information. I will fix it in next version.
Address start, address end will be added to MemTxAttr, translate_size will
beremoved and translate_attr which is have full MemTxAttr information will be
added in next version patch.
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 0/6] Support RISC-V IOPMP
@ 2023-10-26 12:02 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen @ 2023-10-26 12:02 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Xu, David Hildenbrand
I found that after add size information it is still not enough for IOPMP to
reject partially hit error. Access is separated in flatview_read_continue and
lost the start address information. I will fix it in next version.
Address start, address end will be added to MemTxAttr, translate_size will
beremoved and translate_attr which is have full MemTxAttr information will be
added in next version patch.
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
2023-10-26 6:48 ` Ethan Chen
(?)
@ 2023-10-26 14:20 ` Peter Xu
-1 siblings, 0 replies; 34+ messages in thread
From: Peter Xu @ 2023-10-26 14:20 UTC (permalink / raw)
To: Ethan Chen
Cc: qemu-devel, Paolo Bonzini, David Hildenbrand,
Philippe Mathieu-Daudé
On Thu, Oct 26, 2023 at 02:48:14PM +0800, Ethan Chen wrote:
> My target is to support IOPMP partially hit error. IOPMP checks whole memory
> access region is in the same entry. If not, reject the access instead of modify
> the access size.
Could you elaborate why is that important? In what use case?
Consider IOVA mapped for address range iova=[0, 4K] only, here we have a
DMA request with range=[0, 8K]. Now my understanding is what you want to
achieve is don't trigger the DMA to [0, 4K] and fail the whole [0, 8K]
request.
Can we just fail at the latter DMA [4K, 8K] when it happens? After all,
IIUC a device can split the 0-8K DMA into two smaller DMAs, then the 1st
chunk can succeed then if it falls in 0-4K. Some further explanation of
the failure use case could be helpful.
Thanks,
--
Peter Xu
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
@ 2023-10-27 3:28 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen via @ 2023-10-27 3:28 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Paolo Bonzini, David Hildenbrand,
Philippe Mathieu-Daudé
On Thu, Oct 26, 2023 at 10:20:41AM -0400, Peter Xu wrote:
> Could you elaborate why is that important? In what use case?
I was not involved in the formulation of the IOPMP specification, but I'll try
to explain my perspective. IOPMP use the same the idea as PMP. "The matching
PMP entry must match all bytes of an access, or the access fails."
>
> Consider IOVA mapped for address range iova=[0, 4K] only, here we have a
> DMA request with range=[0, 8K]. Now my understanding is what you want to
> achieve is don't trigger the DMA to [0, 4K] and fail the whole [0, 8K]
> request.
>
> Can we just fail at the latter DMA [4K, 8K] when it happens? After all,
> IIUC a device can split the 0-8K DMA into two smaller DMAs, then the 1st
> chunk can succeed then if it falls in 0-4K. Some further explanation of
> the failure use case could be helpful.
IOPMP can only detect partially hit in an access. DMA device will split a
large DMA transfer to small DMA transfers base on target and DMA transfer
width, so partially hit error only happens when an access cross the boundary.
But to ensure that an access is only within one entry is still important.
For example, an entry may mean permission of a device memory region. We do
not want to see one DMA transfer can access mutilple devices, although DMA
have permissions from multiple entries.
Thanks,
Ethan Chen
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
@ 2023-10-27 3:28 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen @ 2023-10-27 3:28 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Paolo Bonzini, David Hildenbrand,
Philippe Mathieu-Daudé
On Thu, Oct 26, 2023 at 10:20:41AM -0400, Peter Xu wrote:
> Could you elaborate why is that important? In what use case?
I was not involved in the formulation of the IOPMP specification, but I'll try
to explain my perspective. IOPMP use the same the idea as PMP. "The matching
PMP entry must match all bytes of an access, or the access fails."
>
> Consider IOVA mapped for address range iova=[0, 4K] only, here we have a
> DMA request with range=[0, 8K]. Now my understanding is what you want to
> achieve is don't trigger the DMA to [0, 4K] and fail the whole [0, 8K]
> request.
>
> Can we just fail at the latter DMA [4K, 8K] when it happens? After all,
> IIUC a device can split the 0-8K DMA into two smaller DMAs, then the 1st
> chunk can succeed then if it falls in 0-4K. Some further explanation of
> the failure use case could be helpful.
IOPMP can only detect partially hit in an access. DMA device will split a
large DMA transfer to small DMA transfers base on target and DMA transfer
width, so partially hit error only happens when an access cross the boundary.
But to ensure that an access is only within one entry is still important.
For example, an entry may mean permission of a device memory region. We do
not want to see one DMA transfer can access mutilple devices, although DMA
have permissions from multiple entries.
Thanks,
Ethan Chen
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
2023-10-27 3:28 ` Ethan Chen
(?)
@ 2023-10-27 16:02 ` Peter Xu
2023-10-27 16:13 ` Peter Xu
2023-10-30 6:00 ` Ethan Chen
-1 siblings, 2 replies; 34+ messages in thread
From: Peter Xu @ 2023-10-27 16:02 UTC (permalink / raw)
To: Ethan Chen
Cc: qemu-devel, Paolo Bonzini, David Hildenbrand,
Philippe Mathieu-Daudé
On Fri, Oct 27, 2023 at 11:28:36AM +0800, Ethan Chen wrote:
> On Thu, Oct 26, 2023 at 10:20:41AM -0400, Peter Xu wrote:
> > Could you elaborate why is that important? In what use case?
> I was not involved in the formulation of the IOPMP specification, but I'll try
> to explain my perspective. IOPMP use the same the idea as PMP. "The matching
> PMP entry must match all bytes of an access, or the access fails."
>
> >
> > Consider IOVA mapped for address range iova=[0, 4K] only, here we have a
> > DMA request with range=[0, 8K]. Now my understanding is what you want to
> > achieve is don't trigger the DMA to [0, 4K] and fail the whole [0, 8K]
> > request.
> >
> > Can we just fail at the latter DMA [4K, 8K] when it happens? After all,
> > IIUC a device can split the 0-8K DMA into two smaller DMAs, then the 1st
> > chunk can succeed then if it falls in 0-4K. Some further explanation of
> > the failure use case could be helpful.
>
> IOPMP can only detect partially hit in an access. DMA device will split a
> large DMA transfer to small DMA transfers base on target and DMA transfer
> width, so partially hit error only happens when an access cross the boundary.
> But to ensure that an access is only within one entry is still important.
> For example, an entry may mean permission of a device memory region. We do
> not want to see one DMA transfer can access mutilple devices, although DMA
> have permissions from multiple entries.
I was expecting a DMA request can be fulfilled successfully as long as the
DMA translations are valid for the whole range of the request, even if the
requested range may include two separate translated targets or more, each
point to different places (either RAM, or other devicie's MMIO regions).
AFAIK currently QEMU memory model will automatically split that large
request into two or more smaller requests, and fulfill them separately by
two/more IOMMU translations, with its memory access dispatched to the
specific memory regions.
The example you provided doesn't seem to be RISCV specific. Do you mean it
is a generic requirement from PCI/PCIe POV, or is it only a restriction of
IOPMP? If it's a valid PCI restriction, does it mean that all the rest
IOMMU implementations in QEMU currently are broken?
[copy Michael and Igor]
Thanks,
--
Peter Xu
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
2023-10-27 16:02 ` Peter Xu
@ 2023-10-27 16:13 ` Peter Xu
2023-10-30 6:00 ` Ethan Chen
1 sibling, 0 replies; 34+ messages in thread
From: Peter Xu @ 2023-10-27 16:13 UTC (permalink / raw)
To: Ethan Chen
Cc: qemu-devel, Paolo Bonzini, David Hildenbrand,
Philippe Mathieu-Daudé,
Michael S. Tsirkin, Igor Mammedov
Add cc list.
On Fri, Oct 27, 2023 at 12:02:24PM -0400, Peter Xu wrote:
> On Fri, Oct 27, 2023 at 11:28:36AM +0800, Ethan Chen wrote:
> > On Thu, Oct 26, 2023 at 10:20:41AM -0400, Peter Xu wrote:
> > > Could you elaborate why is that important? In what use case?
> > I was not involved in the formulation of the IOPMP specification, but I'll try
> > to explain my perspective. IOPMP use the same the idea as PMP. "The matching
> > PMP entry must match all bytes of an access, or the access fails."
> >
> > >
> > > Consider IOVA mapped for address range iova=[0, 4K] only, here we have a
> > > DMA request with range=[0, 8K]. Now my understanding is what you want to
> > > achieve is don't trigger the DMA to [0, 4K] and fail the whole [0, 8K]
> > > request.
> > >
> > > Can we just fail at the latter DMA [4K, 8K] when it happens? After all,
> > > IIUC a device can split the 0-8K DMA into two smaller DMAs, then the 1st
> > > chunk can succeed then if it falls in 0-4K. Some further explanation of
> > > the failure use case could be helpful.
> >
> > IOPMP can only detect partially hit in an access. DMA device will split a
> > large DMA transfer to small DMA transfers base on target and DMA transfer
> > width, so partially hit error only happens when an access cross the boundary.
> > But to ensure that an access is only within one entry is still important.
> > For example, an entry may mean permission of a device memory region. We do
> > not want to see one DMA transfer can access mutilple devices, although DMA
> > have permissions from multiple entries.
>
> I was expecting a DMA request can be fulfilled successfully as long as the
> DMA translations are valid for the whole range of the request, even if the
> requested range may include two separate translated targets or more, each
> point to different places (either RAM, or other devicie's MMIO regions).
>
> AFAIK currently QEMU memory model will automatically split that large
> request into two or more smaller requests, and fulfill them separately by
> two/more IOMMU translations, with its memory access dispatched to the
> specific memory regions.
>
> The example you provided doesn't seem to be RISCV specific. Do you mean it
> is a generic requirement from PCI/PCIe POV, or is it only a restriction of
> IOPMP? If it's a valid PCI restriction, does it mean that all the rest
> IOMMU implementations in QEMU currently are broken?
>
> [copy Michael and Igor]
>
> Thanks,
>
> --
> Peter Xu
--
Peter Xu
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
@ 2023-10-30 6:00 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen via @ 2023-10-30 6:00 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Paolo Bonzini, David Hildenbrand,
Philippe Mathieu-Daudé,
Michael S. Tsirkin, Igor Mammedov
On Fri, Oct 27, 2023 at 12:13:50PM -0400, Peter Xu wrote:
> Add cc list.
>
> On Fri, Oct 27, 2023 at 12:02:24PM -0400, Peter Xu wrote:
> > On Fri, Oct 27, 2023 at 11:28:36AM +0800, Ethan Chen wrote:
> > > On Thu, Oct 26, 2023 at 10:20:41AM -0400, Peter Xu wrote:
> > > > Could you elaborate why is that important? In what use case?
> > > I was not involved in the formulation of the IOPMP specification, but I'll try
> > > to explain my perspective. IOPMP use the same the idea as PMP. "The matching
> > > PMP entry must match all bytes of an access, or the access fails."
> > >
> > > >
> > > > Consider IOVA mapped for address range iova=[0, 4K] only, here we have a
> > > > DMA request with range=[0, 8K]. Now my understanding is what you want to
> > > > achieve is don't trigger the DMA to [0, 4K] and fail the whole [0, 8K]
> > > > request.
> > > >
> > > > Can we just fail at the latter DMA [4K, 8K] when it happens? After all,
> > > > IIUC a device can split the 0-8K DMA into two smaller DMAs, then the 1st
> > > > chunk can succeed then if it falls in 0-4K. Some further explanation of
> > > > the failure use case could be helpful.
> > >
> > > IOPMP can only detect partially hit in an access. DMA device will split a
> > > large DMA transfer to small DMA transfers base on target and DMA transfer
> > > width, so partially hit error only happens when an access cross the boundary.
> > > But to ensure that an access is only within one entry is still important.
> > > For example, an entry may mean permission of a device memory region. We do
> > > not want to see one DMA transfer can access mutilple devices, although DMA
> > > have permissions from multiple entries.
> >
> > I was expecting a DMA request can be fulfilled successfully as long as the
> > DMA translations are valid for the whole range of the request, even if the
> > requested range may include two separate translated targets or more, each
> > point to different places (either RAM, or other devicie's MMIO regions).
IOPMP is used to check DMA translation is vaild or not. In IOPMP specification
, a translation access more than one entry is not invalid.
Though it is not recommand, user can create an IOPMP entry contains mutiple
places to make this kind translations valid.
> >
> > AFAIK currently QEMU memory model will automatically split that large
> > request into two or more smaller requests, and fulfill them separately by
> > two/more IOMMU translations, with its memory access dispatched to the
> > specific memory regions.
Because of requests may be split, I need a method to take the original request
information to IOPMP.
> >
> > The example you provided doesn't seem to be RISCV specific. Do you mean it
> > is a generic requirement from PCI/PCIe POV, or is it only a restriction of
> > IOPMP? If it's a valid PCI restriction, does it mean that all the rest
> > IOMMU implementations in QEMU currently are broken?
> >
It only a restriction of IOPMP.
Thanks,
Ethan Chen
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
@ 2023-10-30 6:00 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen @ 2023-10-30 6:00 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Paolo Bonzini, David Hildenbrand,
Philippe Mathieu-Daudé,
Michael S. Tsirkin, Igor Mammedov
On Fri, Oct 27, 2023 at 12:13:50PM -0400, Peter Xu wrote:
> Add cc list.
>
> On Fri, Oct 27, 2023 at 12:02:24PM -0400, Peter Xu wrote:
> > On Fri, Oct 27, 2023 at 11:28:36AM +0800, Ethan Chen wrote:
> > > On Thu, Oct 26, 2023 at 10:20:41AM -0400, Peter Xu wrote:
> > > > Could you elaborate why is that important? In what use case?
> > > I was not involved in the formulation of the IOPMP specification, but I'll try
> > > to explain my perspective. IOPMP use the same the idea as PMP. "The matching
> > > PMP entry must match all bytes of an access, or the access fails."
> > >
> > > >
> > > > Consider IOVA mapped for address range iova=[0, 4K] only, here we have a
> > > > DMA request with range=[0, 8K]. Now my understanding is what you want to
> > > > achieve is don't trigger the DMA to [0, 4K] and fail the whole [0, 8K]
> > > > request.
> > > >
> > > > Can we just fail at the latter DMA [4K, 8K] when it happens? After all,
> > > > IIUC a device can split the 0-8K DMA into two smaller DMAs, then the 1st
> > > > chunk can succeed then if it falls in 0-4K. Some further explanation of
> > > > the failure use case could be helpful.
> > >
> > > IOPMP can only detect partially hit in an access. DMA device will split a
> > > large DMA transfer to small DMA transfers base on target and DMA transfer
> > > width, so partially hit error only happens when an access cross the boundary.
> > > But to ensure that an access is only within one entry is still important.
> > > For example, an entry may mean permission of a device memory region. We do
> > > not want to see one DMA transfer can access mutilple devices, although DMA
> > > have permissions from multiple entries.
> >
> > I was expecting a DMA request can be fulfilled successfully as long as the
> > DMA translations are valid for the whole range of the request, even if the
> > requested range may include two separate translated targets or more, each
> > point to different places (either RAM, or other devicie's MMIO regions).
IOPMP is used to check DMA translation is vaild or not. In IOPMP specification
, a translation access more than one entry is not invalid.
Though it is not recommand, user can create an IOPMP entry contains mutiple
places to make this kind translations valid.
> >
> > AFAIK currently QEMU memory model will automatically split that large
> > request into two or more smaller requests, and fulfill them separately by
> > two/more IOMMU translations, with its memory access dispatched to the
> > specific memory regions.
Because of requests may be split, I need a method to take the original request
information to IOPMP.
> >
> > The example you provided doesn't seem to be RISCV specific. Do you mean it
> > is a generic requirement from PCI/PCIe POV, or is it only a restriction of
> > IOPMP? If it's a valid PCI restriction, does it mean that all the rest
> > IOMMU implementations in QEMU currently are broken?
> >
It only a restriction of IOPMP.
Thanks,
Ethan Chen
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
2023-10-30 6:00 ` Ethan Chen
(?)
@ 2023-10-30 15:02 ` Peter Xu
2023-10-31 8:52 ` Ethan Chen
-1 siblings, 1 reply; 34+ messages in thread
From: Peter Xu @ 2023-10-30 15:02 UTC (permalink / raw)
To: Ethan Chen
Cc: qemu-devel, Paolo Bonzini, David Hildenbrand,
Philippe Mathieu-Daudé,
Michael S. Tsirkin, Igor Mammedov
On Mon, Oct 30, 2023 at 02:00:54PM +0800, Ethan Chen wrote:
> On Fri, Oct 27, 2023 at 12:13:50PM -0400, Peter Xu wrote:
> > Add cc list.
> >
> > On Fri, Oct 27, 2023 at 12:02:24PM -0400, Peter Xu wrote:
> > > On Fri, Oct 27, 2023 at 11:28:36AM +0800, Ethan Chen wrote:
> > > > On Thu, Oct 26, 2023 at 10:20:41AM -0400, Peter Xu wrote:
> > > > > Could you elaborate why is that important? In what use case?
> > > > I was not involved in the formulation of the IOPMP specification, but I'll try
> > > > to explain my perspective. IOPMP use the same the idea as PMP. "The matching
> > > > PMP entry must match all bytes of an access, or the access fails."
> > > >
> > > > >
> > > > > Consider IOVA mapped for address range iova=[0, 4K] only, here we have a
> > > > > DMA request with range=[0, 8K]. Now my understanding is what you want to
> > > > > achieve is don't trigger the DMA to [0, 4K] and fail the whole [0, 8K]
> > > > > request.
> > > > >
> > > > > Can we just fail at the latter DMA [4K, 8K] when it happens? After all,
> > > > > IIUC a device can split the 0-8K DMA into two smaller DMAs, then the 1st
> > > > > chunk can succeed then if it falls in 0-4K. Some further explanation of
> > > > > the failure use case could be helpful.
> > > >
> > > > IOPMP can only detect partially hit in an access. DMA device will split a
> > > > large DMA transfer to small DMA transfers base on target and DMA transfer
> > > > width, so partially hit error only happens when an access cross the boundary.
> > > > But to ensure that an access is only within one entry is still important.
> > > > For example, an entry may mean permission of a device memory region. We do
> > > > not want to see one DMA transfer can access mutilple devices, although DMA
> > > > have permissions from multiple entries.
> > >
> > > I was expecting a DMA request can be fulfilled successfully as long as the
> > > DMA translations are valid for the whole range of the request, even if the
> > > requested range may include two separate translated targets or more, each
> > > point to different places (either RAM, or other devicie's MMIO regions).
>
> IOPMP is used to check DMA translation is vaild or not. In IOPMP specification
> , a translation access more than one entry is not invalid.
> Though it is not recommand, user can create an IOPMP entry contains mutiple
> places to make this kind translations valid.
>
> > >
> > > AFAIK currently QEMU memory model will automatically split that large
> > > request into two or more smaller requests, and fulfill them separately by
> > > two/more IOMMU translations, with its memory access dispatched to the
> > > specific memory regions.
>
> Because of requests may be split, I need a method to take the original request
> information to IOPMP.
I'm not sure whether translate() is the "original request" either. The
problem is QEMU can split the request for various reasons already, afaict.
For example, address_space_translate_internal() has this:
if (memory_region_is_ram(mr)) {
diff = int128_sub(section->size, int128_make64(addr));
*plen = int128_get64(int128_min(diff, int128_make64(*plen)));
}
Which can already shrink the request size from the caller before reaching
translate(). So the length passed into translate() can already be
modified.
Another thing is, we have two other common call sites for translate():
memory_region_iommu_replay
address_space_translate_for_iotlb
I'm not sure whether you've looked into them and think they don't need to
be trapped: at least memory_region_iommu_replay() looks all fine in this
regard because it always translate in min page size granule. But I think
the restriction should apply to all translate()s.
translate_size() is weird on its own. If the only purpose is to pass the
length into translate(), another option is to add that parameter into
current translate(), allowing the implementation to ignore it. I think
that'll be better, but even if so, I'm not 100% sure it'll always do what
you wanted as discussed above.
Thanks,
--
Peter Xu
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
@ 2023-10-31 8:52 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen via @ 2023-10-31 8:52 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Paolo Bonzini, David Hildenbrand,
Philippe Mathieu-Daudé,
Michael S. Tsirkin, Igor Mammedov
On Mon, Oct 30, 2023 at 11:02:30AM -0400, Peter Xu wrote:
> On Mon, Oct 30, 2023 at 02:00:54PM +0800, Ethan Chen wrote:
> > On Fri, Oct 27, 2023 at 12:13:50PM -0400, Peter Xu wrote:
> > > Add cc list.
> > >
> > > On Fri, Oct 27, 2023 at 12:02:24PM -0400, Peter Xu wrote:
> > > > On Fri, Oct 27, 2023 at 11:28:36AM +0800, Ethan Chen wrote:
> > > > > On Thu, Oct 26, 2023 at 10:20:41AM -0400, Peter Xu wrote:
> > > > > > Could you elaborate why is that important? In what use case?
> > > > > I was not involved in the formulation of the IOPMP specification, but I'll try
> > > > > to explain my perspective. IOPMP use the same the idea as PMP. "The matching
> > > > > PMP entry must match all bytes of an access, or the access fails."
> > > > >
> > > > > >
> > > > > > Consider IOVA mapped for address range iova=[0, 4K] only, here we have a
> > > > > > DMA request with range=[0, 8K]. Now my understanding is what you want to
> > > > > > achieve is don't trigger the DMA to [0, 4K] and fail the whole [0, 8K]
> > > > > > request.
> > > > > >
> > > > > > Can we just fail at the latter DMA [4K, 8K] when it happens? After all,
> > > > > > IIUC a device can split the 0-8K DMA into two smaller DMAs, then the 1st
> > > > > > chunk can succeed then if it falls in 0-4K. Some further explanation of
> > > > > > the failure use case could be helpful.
> > > > >
> > > > > IOPMP can only detect partially hit in an access. DMA device will split a
> > > > > large DMA transfer to small DMA transfers base on target and DMA transfer
> > > > > width, so partially hit error only happens when an access cross the boundary.
> > > > > But to ensure that an access is only within one entry is still important.
> > > > > For example, an entry may mean permission of a device memory region. We do
> > > > > not want to see one DMA transfer can access mutilple devices, although DMA
> > > > > have permissions from multiple entries.
> > > >
> > > > I was expecting a DMA request can be fulfilled successfully as long as the
> > > > DMA translations are valid for the whole range of the request, even if the
> > > > requested range may include two separate translated targets or more, each
> > > > point to different places (either RAM, or other devicie's MMIO regions).
> >
> > IOPMP is used to check DMA translation is vaild or not. In IOPMP specification
> > , a translation access more than one entry is not invalid.
> > Though it is not recommand, user can create an IOPMP entry contains mutiple
> > places to make this kind translations valid.
> >
> > > >
> > > > AFAIK currently QEMU memory model will automatically split that large
> > > > request into two or more smaller requests, and fulfill them separately by
> > > > two/more IOMMU translations, with its memory access dispatched to the
> > > > specific memory regions.
> >
> > Because of requests may be split, I need a method to take the original request
> > information to IOPMP.
>
> I'm not sure whether translate() is the "original request" either. The
> problem is QEMU can split the request for various reasons already, afaict.
>
> For example, address_space_translate_internal() has this:
>
> if (memory_region_is_ram(mr)) {
> diff = int128_sub(section->size, int128_make64(addr));
> *plen = int128_get64(int128_min(diff, int128_make64(*plen)));
> }
>
> Which can already shrink the request size from the caller before reaching
> translate(). So the length passed into translate() can already be
> modified.
>
> Another thing is, we have two other common call sites for translate():
>
> memory_region_iommu_replay
> address_space_translate_for_iotlb
>
> I'm not sure whether you've looked into them and think they don't need to
> be trapped: at least memory_region_iommu_replay() looks all fine in this
> regard because it always translate in min page size granule. But I think
> the restriction should apply to all translate()s.
>
> translate_size() is weird on its own. If the only purpose is to pass the
> length into translate(), another option is to add that parameter into
> current translate(), allowing the implementation to ignore it. I think
> that'll be better, but even if so, I'm not 100% sure it'll always do what
> you wanted as discussed above.
It seems that there are too many things that have not been considered in my
current method. I am doing the revision that no new translation function but
adding start address and end address to MemTxAttrs.
Since attrs_to_index() only return one interger, IOPMP attrs_to_index() will
copy the address range to its device state and then handle the translate().
Thanks,
Ethan Chen
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented
@ 2023-10-31 8:52 ` Ethan Chen
0 siblings, 0 replies; 34+ messages in thread
From: Ethan Chen @ 2023-10-31 8:52 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Paolo Bonzini, David Hildenbrand,
Philippe Mathieu-Daudé,
Michael S. Tsirkin, Igor Mammedov
On Mon, Oct 30, 2023 at 11:02:30AM -0400, Peter Xu wrote:
> On Mon, Oct 30, 2023 at 02:00:54PM +0800, Ethan Chen wrote:
> > On Fri, Oct 27, 2023 at 12:13:50PM -0400, Peter Xu wrote:
> > > Add cc list.
> > >
> > > On Fri, Oct 27, 2023 at 12:02:24PM -0400, Peter Xu wrote:
> > > > On Fri, Oct 27, 2023 at 11:28:36AM +0800, Ethan Chen wrote:
> > > > > On Thu, Oct 26, 2023 at 10:20:41AM -0400, Peter Xu wrote:
> > > > > > Could you elaborate why is that important? In what use case?
> > > > > I was not involved in the formulation of the IOPMP specification, but I'll try
> > > > > to explain my perspective. IOPMP use the same the idea as PMP. "The matching
> > > > > PMP entry must match all bytes of an access, or the access fails."
> > > > >
> > > > > >
> > > > > > Consider IOVA mapped for address range iova=[0, 4K] only, here we have a
> > > > > > DMA request with range=[0, 8K]. Now my understanding is what you want to
> > > > > > achieve is don't trigger the DMA to [0, 4K] and fail the whole [0, 8K]
> > > > > > request.
> > > > > >
> > > > > > Can we just fail at the latter DMA [4K, 8K] when it happens? After all,
> > > > > > IIUC a device can split the 0-8K DMA into two smaller DMAs, then the 1st
> > > > > > chunk can succeed then if it falls in 0-4K. Some further explanation of
> > > > > > the failure use case could be helpful.
> > > > >
> > > > > IOPMP can only detect partially hit in an access. DMA device will split a
> > > > > large DMA transfer to small DMA transfers base on target and DMA transfer
> > > > > width, so partially hit error only happens when an access cross the boundary.
> > > > > But to ensure that an access is only within one entry is still important.
> > > > > For example, an entry may mean permission of a device memory region. We do
> > > > > not want to see one DMA transfer can access mutilple devices, although DMA
> > > > > have permissions from multiple entries.
> > > >
> > > > I was expecting a DMA request can be fulfilled successfully as long as the
> > > > DMA translations are valid for the whole range of the request, even if the
> > > > requested range may include two separate translated targets or more, each
> > > > point to different places (either RAM, or other devicie's MMIO regions).
> >
> > IOPMP is used to check DMA translation is vaild or not. In IOPMP specification
> > , a translation access more than one entry is not invalid.
> > Though it is not recommand, user can create an IOPMP entry contains mutiple
> > places to make this kind translations valid.
> >
> > > >
> > > > AFAIK currently QEMU memory model will automatically split that large
> > > > request into two or more smaller requests, and fulfill them separately by
> > > > two/more IOMMU translations, with its memory access dispatched to the
> > > > specific memory regions.
> >
> > Because of requests may be split, I need a method to take the original request
> > information to IOPMP.
>
> I'm not sure whether translate() is the "original request" either. The
> problem is QEMU can split the request for various reasons already, afaict.
>
> For example, address_space_translate_internal() has this:
>
> if (memory_region_is_ram(mr)) {
> diff = int128_sub(section->size, int128_make64(addr));
> *plen = int128_get64(int128_min(diff, int128_make64(*plen)));
> }
>
> Which can already shrink the request size from the caller before reaching
> translate(). So the length passed into translate() can already be
> modified.
>
> Another thing is, we have two other common call sites for translate():
>
> memory_region_iommu_replay
> address_space_translate_for_iotlb
>
> I'm not sure whether you've looked into them and think they don't need to
> be trapped: at least memory_region_iommu_replay() looks all fine in this
> regard because it always translate in min page size granule. But I think
> the restriction should apply to all translate()s.
>
> translate_size() is weird on its own. If the only purpose is to pass the
> length into translate(), another option is to add that parameter into
> current translate(), allowing the implementation to ignore it. I think
> that'll be better, but even if so, I'm not 100% sure it'll always do what
> you wanted as discussed above.
It seems that there are too many things that have not been considered in my
current method. I am doing the revision that no new translation function but
adding start address and end address to MemTxAttrs.
Since attrs_to_index() only return one interger, IOPMP attrs_to_index() will
copy the address range to its device state and then handle the translate().
Thanks,
Ethan Chen
^ permalink raw reply [flat|nested] 34+ messages in thread
end of thread, other threads:[~2023-10-31 8:54 UTC | newest]
Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-10-25 5:14 [PATCH 0/6] Support RISC-V IOPMP Ethan Chen via
2023-10-25 5:14 ` Ethan Chen
2023-10-25 5:14 ` [PATCH 1/6] exec/memory: Introduce the translate_size function within the IOMMU class Ethan Chen via
2023-10-25 5:14 ` Ethan Chen
2023-10-25 14:56 ` David Hildenbrand
2023-10-26 7:14 ` Ethan Chen via
2023-10-26 7:14 ` Ethan Chen
2023-10-26 7:26 ` David Hildenbrand
2023-10-25 5:14 ` [PATCH 2/6] system/physmem: IOMMU: Invoke the translate_size function if it is implemented Ethan Chen via
2023-10-25 5:14 ` Ethan Chen
2023-10-25 15:14 ` Peter Xu
2023-10-26 6:48 ` Ethan Chen via
2023-10-26 6:48 ` Ethan Chen
2023-10-26 14:20 ` Peter Xu
2023-10-27 3:28 ` Ethan Chen via
2023-10-27 3:28 ` Ethan Chen
2023-10-27 16:02 ` Peter Xu
2023-10-27 16:13 ` Peter Xu
2023-10-30 6:00 ` Ethan Chen via
2023-10-30 6:00 ` Ethan Chen
2023-10-30 15:02 ` Peter Xu
2023-10-31 8:52 ` Ethan Chen via
2023-10-31 8:52 ` Ethan Chen
2023-10-25 5:14 ` [PATCH 3/6] exec/memattrs: Add iopmp source id to MemTxAttrs Ethan Chen via
2023-10-25 5:14 ` Ethan Chen
2023-10-25 5:14 ` [PATCH 4/6] Add RISC-V IOPMP support Ethan Chen via
2023-10-25 5:14 ` Ethan Chen
2023-10-25 5:14 ` [PATCH 5/6] hw/dma: Add Andes ATCDMAC300 support Ethan Chen via
2023-10-25 5:14 ` Ethan Chen
2023-10-25 5:14 ` [PATCH 6/6] hw/riscv/virt: Add IOPMP support Ethan Chen via
2023-10-25 5:14 ` Ethan Chen
2023-10-25 5:14 ` Ethan Chen via
2023-10-26 12:02 ` [PATCH 0/6] Support RISC-V IOPMP Ethan Chen via
2023-10-26 12:02 ` Ethan Chen
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.