public inbox for opensbi@lists.infradead.org
 help / color / mirror / Atom feed
From: Nicholas Piggin <npiggin@gmail.com>
To: opensbi@lists.infradead.org
Cc: Nicholas Piggin <npiggin@gmail.com>
Subject: [PATCH 4/4] platform: generic: tenstorrent: Add RISC-V IOMMU support
Date: Tue, 10 Mar 2026 10:49:57 +1000	[thread overview]
Message-ID: <20260310005000.3837512-4-npiggin@gmail.com> (raw)
In-Reply-To: <20260310005000.3837512-1-npiggin@gmail.com>

Add support for the Tenstorrent RISC-V IOMMU, and enable it for the
Atlantis platform. The IOMMU must have PMA and PMP registers set up.
These are implemented as MMRs with the same format as the corresponding
HART CSRs, making it possible to reuse much existing PMP code. PMAs are
copied from HART CSRs directly because those are set up by a prior boot
stage.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 platform/generic/Kconfig                     |   1 +
 platform/generic/include/tenstorrent/iommu.h |  12 +
 platform/generic/tenstorrent/Kconfig         |   3 +
 platform/generic/tenstorrent/atlantis.c      |  18 ++
 platform/generic/tenstorrent/iommu.c         | 245 +++++++++++++++++++
 platform/generic/tenstorrent/objects.mk      |   1 +
 platform/generic/tenstorrent/pma.c           |   3 +-
 7 files changed, 282 insertions(+), 1 deletion(-)
 create mode 100644 platform/generic/include/tenstorrent/iommu.h
 create mode 100644 platform/generic/tenstorrent/iommu.c

diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
index 7de5e5a6..ba2cfa38 100644
--- a/platform/generic/Kconfig
+++ b/platform/generic/Kconfig
@@ -88,6 +88,7 @@ config PLATFORM_STARFIVE_JH7110
 config PLATFORM_TENSTORRENT_ATLANTIS
 	bool "Tenstorrent Atlantis support"
 	select CPU_TENSTORRENT_ASCALON
+	select CPU_TENSTORRENT_IOMMU
 	default n
 
 config PLATFORM_THEAD
diff --git a/platform/generic/include/tenstorrent/iommu.h b/platform/generic/include/tenstorrent/iommu.h
new file mode 100644
index 00000000..45231dfa
--- /dev/null
+++ b/platform/generic/include/tenstorrent/iommu.h
@@ -0,0 +1,12 @@
+/*
+ * SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef __TENSTORRENT_IOMMU_H__
+#define __TENSTORRENT_IOMMU_H__
+
+int tt_iommu_configure(unsigned long iommu_m_regs);
+int tt_iommu_fdt_configure(const void *fdt);
+
+#endif
diff --git a/platform/generic/tenstorrent/Kconfig b/platform/generic/tenstorrent/Kconfig
index 76c7fb32..5c524b33 100644
--- a/platform/generic/tenstorrent/Kconfig
+++ b/platform/generic/tenstorrent/Kconfig
@@ -3,3 +3,6 @@
 
 config CPU_TENSTORRENT_ASCALON
 	bool
+
+config CPU_TENSTORRENT_IOMMU
+	bool
diff --git a/platform/generic/tenstorrent/atlantis.c b/platform/generic/tenstorrent/atlantis.c
index 4c312f7e..59bbd0eb 100644
--- a/platform/generic/tenstorrent/atlantis.c
+++ b/platform/generic/tenstorrent/atlantis.c
@@ -12,12 +12,30 @@
 
 #include <tenstorrent/ascalon.h>
 #include <tenstorrent/pma.h>
+#include <tenstorrent/iommu.h>
 
 static int tt_atlantis_final_init(bool cold_boot)
 {
 	if (cold_boot) {
+		int rc;
+
 		/* Boot firmware sets HART PMAs. Read and verify them. */
 		tt_ascalon_discover_pmas_from_boot_hart();
+
+		/*
+		 * IOMMU must be configured at platform final_init time, to get
+		 * the right PMP settings, see init_coldboot() comment. IOMMU
+		 * is also configured with PMAs discovered from the boot HART,
+		 * above.
+		 */
+		rc = tt_iommu_fdt_configure(fdt_get_address());
+		if (rc) {
+			if (rc == SBI_ENOTSUPP)
+				sbi_printf("Tenstorrent Atlantis: No IOMMU "
+					   "in device tree, continuing.\n");
+			else
+				return rc;
+		}
 	} else {
 		/* Verify nonboot HARTs have PMAs matching boot HART */
 		tt_ascalon_verify_pmas_nonboot_hart();
diff --git a/platform/generic/tenstorrent/iommu.c b/platform/generic/tenstorrent/iommu.c
new file mode 100644
index 00000000..acc31c09
--- /dev/null
+++ b/platform/generic/tenstorrent/iommu.c
@@ -0,0 +1,245 @@
+/*
+ * SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_string.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_pmp.h>
+#include <libfdt.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+
+#include <tenstorrent/iommu.h>
+#include <tenstorrent/pma.h>
+
+/* PMA/PMP register offsets */
+#define RISCV_IOMMU_REG_PMA_CFG		0x1000
+#define RISCV_IOMMU_REG_PMP_CFG		0x2000
+#define RISCV_IOMMU_REG_PMP_ADDR	0x2040
+#define RISCV_IOMMU_REG_MACHINE_SIZE	0x3000 /* Minimum size of MMRs used */
+
+static int iommu_pmp_write(void *iommu, unsigned int n, pmp_t *pmp)
+{
+	void *pmpcfg_reg, *pmpaddr_reg;
+	u64 pmpcfg;
+	unsigned int pmpcfg_shift;
+
+	if (n >= PMP_COUNT)
+		return SBI_EINVAL;
+
+	pmpaddr_reg = iommu + RISCV_IOMMU_REG_PMP_ADDR + n * 8;
+	writeq(pmp->addr, pmpaddr_reg);
+
+	pmpcfg_reg  = iommu + RISCV_IOMMU_REG_PMP_CFG + (n / 8) * 8;
+	pmpcfg_shift = (n % 8) * 8;
+	pmpcfg = readq(pmpcfg_reg);
+	pmpcfg &= ~((u64)0xff << pmpcfg_shift);
+	pmpcfg |= (u64)pmp->cfg << pmpcfg_shift;
+	writeq(pmpcfg, pmpcfg_reg);
+
+	return SBI_OK;
+}
+
+static int iommu_pmp_disable(void *iommu, unsigned int n)
+{
+	struct pmp pmp = { .cfg = 0, .addr = 0 };
+
+	return iommu_pmp_write(iommu, n, &pmp);
+}
+
+static int iommu_pmp_set(void *iommu, unsigned int n,
+		unsigned long prot, unsigned long addr, unsigned long log2len)
+{
+	pmp_t pmp;
+	int rc;
+
+	rc = pmp_create(&pmp, prot, addr, log2len);
+	if (rc)
+		return rc;
+
+	return iommu_pmp_write(iommu, n, &pmp);
+}
+
+/* This matches sbi_hart_oldpmp_configure. IOMMU does not support Smepmp */
+static int iommu_pmp_configure(void *iommu, unsigned int pmp_count,
+			unsigned int pmp_log2gran, unsigned long pmp_addr_max)
+{
+	struct sbi_domain_memregion *reg;
+	struct sbi_domain *dom;
+	unsigned int pmp_idx, pmp_flags;
+
+	/* For now, attach IOMMUs to root domain */
+	dom = &root;
+
+	/* Disable all PMPs */
+	for (int i = 0; i < pmp_count; i++)
+		iommu_pmp_disable(iommu, i);
+
+	pmp_idx = 0;
+	sbi_domain_for_each_memregion(dom, reg) {
+		if (pmp_count <= pmp_idx)
+			break;
+
+		pmp_flags = 0;
+
+		/*
+		 * If permissions are to be enforced for all modes on
+		 * this region, the lock bit should be set.
+		 */
+		if (reg->flags & SBI_DOMAIN_MEMREGION_ENF_PERMISSIONS)
+			pmp_flags |= PMP_L;
+
+		if (reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE)
+			pmp_flags |= PMP_R;
+		if (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE)
+			pmp_flags |= PMP_W;
+		if (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)
+			pmp_flags |= PMP_X;
+
+		if (reg->order < pmp_log2gran || (reg->base >> PMP_SHIFT) >= pmp_addr_max) {
+			sbi_printf("Can not configure pmp for domain %s because"
+				   " memory region address 0x%lx or size 0x%lx "
+				   "is not in range.\n", dom->name, reg->base,
+				   reg->order);
+			continue;
+		}
+
+		iommu_pmp_set(iommu, pmp_idx, pmp_flags, reg->base, reg->order);
+		pmp_idx++;
+	}
+
+	return 0;
+}
+
+static int iommu_pma_write(void *iommu, unsigned int n, u64 pma)
+{
+	void *pmacfg_reg;
+
+	if (n >= TT_MAX_PMAS)
+		return SBI_EINVAL;
+
+	pmacfg_reg  = iommu + RISCV_IOMMU_REG_PMA_CFG + n*8;
+
+	writeq(pma, pmacfg_reg);
+
+	if (readq(pmacfg_reg) != pma)
+		return SBI_ENOTSUPP;
+
+	return SBI_OK;
+}
+
+static int iommu_pma_configure(void *iommu)
+{
+	for (int i = 0; i < TT_MAX_PMAS; i++) {
+		int rc = iommu_pma_write(iommu, i, tt_pma_get(i));
+		if (rc) {
+			sbi_printf("IOMMU: Error setting unimplemented PMA%02u\n", i);
+			return rc;
+		}
+	}
+
+	return SBI_OK;
+}
+
+static void tt_iommu_detect_pmp(void *iommu, unsigned int *pmp_count,
+			    unsigned int *pmp_log2gran,
+			    unsigned long *pmp_addr_max)
+{
+	unsigned long pmp_addr_bits;
+	void *pmpaddr_reg;
+	u64 val;
+	unsigned int i;
+
+	pmpaddr_reg = iommu + RISCV_IOMMU_REG_PMP_ADDR + 0 * 8;
+	writeq(PMP_ADDR_MASK, pmpaddr_reg);
+	val = readq(pmpaddr_reg);
+	*pmp_log2gran = sbi_ffs(val) + 2;
+	pmp_addr_bits = sbi_fls(val);
+
+	*pmp_addr_max = (1UL << pmp_addr_bits) | ((1UL << pmp_addr_bits) - 1);
+
+	for (i = 0; i < PMP_COUNT; i++) {
+		pmpaddr_reg = iommu + RISCV_IOMMU_REG_PMP_ADDR + i * 8;
+		writeq(val, pmpaddr_reg);
+		val = readq(pmpaddr_reg);
+		if (!val)
+			break;
+		writeq(0, pmpaddr_reg);
+	}
+	*pmp_count = i;
+}
+
+int tt_iommu_configure(unsigned long iommu_m_regs)
+{
+	unsigned int pmp_count;
+	unsigned int pmp_log2gran;
+	unsigned long pmp_addr_max;
+	int rc;
+
+	rc = iommu_pma_configure((void *)iommu_m_regs);
+	if (rc) {
+		sbi_printf("IOMMU: Failed to set PMAs\n");
+		return rc;
+	}
+
+	tt_iommu_detect_pmp((void *)iommu_m_regs, &pmp_count,
+			    &pmp_log2gran, &pmp_addr_max);
+	sbi_dprintf("IOMMU: detected %u PMPs %u log2gran 0x%016lx addr max\n",
+			pmp_count, pmp_log2gran, pmp_addr_max);
+
+	rc = iommu_pmp_configure((void *)iommu_m_regs, pmp_count,
+				 pmp_log2gran, pmp_addr_max);
+	if (rc) {
+		sbi_printf("IOMMU: Failed to set PMPs\n");
+		return rc;
+	}
+
+	return SBI_OK;
+}
+
+int tt_iommu_fdt_configure(const void *fdt)
+{
+	const char *compatible = "tenstorrent,riscv-iommu";
+	bool found_dt = false;
+	int offset = -1;
+
+	for (;;) {
+		uint64_t addr, size;
+		int rc;
+
+		offset = fdt_node_offset_by_compatible(fdt, offset, compatible);
+		if (offset < 0)
+			break;
+
+		rc = fdt_get_node_addr_size_by_name(fdt, offset, "machine",
+						    &addr, &size);
+		if (rc < 0 || !size) {
+			sbi_printf("tenstorrent,riscv-iommu did not find "
+					"machine regs\n");
+			continue;
+		}
+
+		if (size < RISCV_IOMMU_REG_MACHINE_SIZE) {
+			sbi_printf("tenstorrent,riscv-iommu@0x%016lx "
+				   " machine regs region too small\n", addr);
+			continue;
+		}
+
+		sbi_dprintf("tenstorrent,riscv-iommu found machine regs "
+			    "0x%016lx-0x%016lx\n", addr, addr + size - 1);
+
+		rc = tt_iommu_configure(addr);
+		if (rc)
+			sbi_printf("tenstorrent,riscv-iommu@0x%016lx "
+				   "init failed\n", addr);
+	}
+
+	if (!found_dt)
+		return SBI_ENOTSUPP;
+
+	return SBI_OK;
+}
diff --git a/platform/generic/tenstorrent/objects.mk b/platform/generic/tenstorrent/objects.mk
index 1e4aeb2d..0bf9c69b 100644
--- a/platform/generic/tenstorrent/objects.mk
+++ b/platform/generic/tenstorrent/objects.mk
@@ -5,6 +5,7 @@
 
 platform-objs-y += tenstorrent/pma.o
 platform-objs-$(CONFIG_CPU_TENSTORRENT_ASCALON) += tenstorrent/ascalon.o
+platform-objs-$(CONFIG_CPU_TENSTORRENT_IOMMU) += tenstorrent/iommu.o
 
 carray-platform_override_modules-$(CONFIG_PLATFORM_TENSTORRENT_ATLANTIS) += tenstorrent_atlantis
 platform-objs-$(CONFIG_PLATFORM_TENSTORRENT_ATLANTIS) += tenstorrent/atlantis.o
diff --git a/platform/generic/tenstorrent/pma.c b/platform/generic/tenstorrent/pma.c
index daf60192..1556707c 100644
--- a/platform/generic/tenstorrent/pma.c
+++ b/platform/generic/tenstorrent/pma.c
@@ -13,7 +13,8 @@
 
 /*
  * All PMAs in the system should be the same (after boot). The init code
- * must have set PMAs for all HARTs.
+ * must have set PMAs for all HARTs. IOMMU init programs IOMMU PMAs to
+ * match the HARTs.
  */
 
 /*
-- 
2.51.0


-- 
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi

  parent reply	other threads:[~2026-03-10  0:50 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-10  0:49 [PATCH 1/4] platform: generic: Tenstorrent Atlantis support Nicholas Piggin
2026-03-10  0:49 ` [PATCH 2/4] lib: sbi: Move PMP encoding into a new file Nicholas Piggin
2026-04-01 12:50   ` Joel Stanley
2026-03-10  0:49 ` [PATCH 3/4] lib: sbi: Add hart_ prefix to PMP functions Nicholas Piggin
2026-04-01 12:50   ` Joel Stanley
2026-03-10  0:49 ` Nicholas Piggin [this message]
2026-04-01 12:51   ` [PATCH 4/4] platform: generic: tenstorrent: Add RISC-V IOMMU support Joel Stanley

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260310005000.3837512-4-npiggin@gmail.com \
    --to=npiggin@gmail.com \
    --cc=opensbi@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox