public inbox for opensbi@lists.infradead.org
 help / color / mirror / Atom feed
* [PATCH 1/4] platform: generic: Tenstorrent Atlantis support
@ 2026-03-10  0:49 Nicholas Piggin
  2026-03-10  0:49 ` [PATCH 2/4] lib: sbi: Move PMP encoding into a new file Nicholas Piggin
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Nicholas Piggin @ 2026-03-10  0:49 UTC (permalink / raw)
  To: opensbi; +Cc: Nicholas Piggin

Add the Tenstorrent Atlantis as a generic-platform. This initial
support enables the single_fw_region option, and verifies and prints
HART PMA CSR configuration.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
Note the Atlantis hardware is not yet released, and QEMU models are in
the process of being upstreamed, but at the moment not complete. So for
now this can not really be tested with a public implementation, but we
plan to get implementation specific IOMMU MMRs and CPU CSRs into QEMU
that can meaningfully exercise this platform.

There will be some more Atlantis platform pieces to come, but we want to
get started with upstreaming and these are some relatively small and
stable pieces.

The IOMMU support depends on a proposed change to device-tree binding
for the Tenstorrent specific riscv iommu which I posted for RFC here:

https://lore.kernel.org/linux-riscv/20260310003850.3837030-1-npiggin@gmail.com/T/#u

Thanks,
Nick
---
 docs/platform/generic.md                      |   2 +
 docs/platform/tt-atlantis.md                  |  35 +++++
 platform/generic/Kconfig                      |   6 +
 platform/generic/configs/defconfig            |   1 +
 .../generic/include/tenstorrent/ascalon.h     |  12 ++
 platform/generic/include/tenstorrent/pma.h    |  18 +++
 platform/generic/tenstorrent/Kconfig          |   5 +
 platform/generic/tenstorrent/ascalon.c        |  53 +++++++
 platform/generic/tenstorrent/atlantis.c       |  50 +++++++
 platform/generic/tenstorrent/objects.mk       |  10 ++
 platform/generic/tenstorrent/pma.c            | 138 ++++++++++++++++++
 11 files changed, 330 insertions(+)
 create mode 100644 docs/platform/tt-atlantis.md
 create mode 100644 platform/generic/include/tenstorrent/ascalon.h
 create mode 100644 platform/generic/include/tenstorrent/pma.h
 create mode 100644 platform/generic/tenstorrent/Kconfig
 create mode 100644 platform/generic/tenstorrent/ascalon.c
 create mode 100644 platform/generic/tenstorrent/atlantis.c
 create mode 100644 platform/generic/tenstorrent/objects.mk
 create mode 100644 platform/generic/tenstorrent/pma.c

diff --git a/docs/platform/generic.md b/docs/platform/generic.md
index c48d6a9a..0b896ede 100644
--- a/docs/platform/generic.md
+++ b/docs/platform/generic.md
@@ -47,6 +47,7 @@ RISC-V Platforms Using Generic Platform
 * **SiFive HiFive Unleashed** (*[sifive_fu540.md]*)
 * **Spike** (*[spike.md]*)
 * **T-HEAD C9xx series Processors** (*[thead-c9xx.md]*)
+* **Tenstorrent Atlantis Platform** (*[tt-atlantis.md]*)
 * **OpenPiton FPGA SoC** (*[fpga-openpiton.md]*)
 * **Ariane FPGA SoC** (*[fpga-ariane.md]*)
 
@@ -57,5 +58,6 @@ RISC-V Platforms Using Generic Platform
 [sifive_fu540.md]: sifive_fu540.md
 [spike.md]: spike.md
 [thead-c9xx.md]: thead-c9xx.md
+[tt-atlantis.md]: tt-atlantis.md
 [fpga-openpiton.md]: fpga-openpiton.md
 [fpga-ariane.md]: fpga-ariane.md
diff --git a/docs/platform/tt-atlantis.md b/docs/platform/tt-atlantis.md
new file mode 100644
index 00000000..b9bdd238
--- /dev/null
+++ b/docs/platform/tt-atlantis.md
@@ -0,0 +1,35 @@
+Tenstorrent Atlantis Platform
+=============================
+
+The Tenstorrent Atlantis is an SoC and development board from
+Tenstorrent in partnership with CoreLab Technology. It contains 8 RISC-V
+RVA23 compliant Tenstorrent Ascalon cores with RISC-V AIA, RISC-V IOMMU,
+and a range of devices and IO connectivity.
+
+To build the platform-specific library and firmware images, provide the
+*PLATFORM=generic* parameter to the top level `make` command.
+
+Platform Options
+----------------
+
+The *Tenstorrent Atlantis* platform does not have any platform-specific
+options.
+
+Building Tenstorrent Atlantis Platform
+--------------------------------------
+
+The Atlantis Platform is still under development. This section will be
+expanded as firmware and support become available.
+
+QEMU support is currently being developed and initial support has been
+proposed for upstream. To run QEMU that is patched with 'tt-atlantis'
+machine support, run:
+
+```
+qemu-system-riscv64 -M tt-atlantis -nographic \
+	-bios build/platform/generic/firmware/fw_payload.bin \
+    -kernel <linux_build_dir>/Image
+```
+
+Recent (6.18) Linux/riscv 64-bit defconfig kernels should run the QEMU
+tt-atlantis machine.
diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
index 2627998a..7de5e5a6 100644
--- a/platform/generic/Kconfig
+++ b/platform/generic/Kconfig
@@ -85,6 +85,11 @@ config PLATFORM_STARFIVE_JH7110
 	bool "StarFive JH7110 support"
 	default n
 
+config PLATFORM_TENSTORRENT_ATLANTIS
+	bool "Tenstorrent Atlantis support"
+	select CPU_TENSTORRENT_ASCALON
+	default n
+
 config PLATFORM_THEAD
 	bool "THEAD C9xx support"
 	select THEAD_C9XX_ERRATA
@@ -110,6 +115,7 @@ config PLATFORM_MIPS_P8700_BOSTON
 
 source "$(OPENSBI_SRC_DIR)/platform/generic/andes/Kconfig"
 source "$(OPENSBI_SRC_DIR)/platform/generic/eswin/Kconfig"
+source "$(OPENSBI_SRC_DIR)/platform/generic/tenstorrent/Kconfig"
 source "$(OPENSBI_SRC_DIR)/platform/generic/thead/Kconfig"
 
 endif
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index 346058f5..153a3aa0 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -9,6 +9,7 @@ CONFIG_PLATFORM_SIFIVE_FU540=y
 CONFIG_PLATFORM_SIFIVE_FU740=y
 CONFIG_PLATFORM_SOPHGO_SG2042=y
 CONFIG_PLATFORM_STARFIVE_JH7110=y
+CONFIG_PLATFORM_TENSTORRENT_ATLANTIS=y
 CONFIG_PLATFORM_THEAD=y
 CONFIG_PLATFORM_MIPS_P8700_EYEQ7H=y
 CONFIG_PLATFORM_MIPS_P8700_BOSTON=y
diff --git a/platform/generic/include/tenstorrent/ascalon.h b/platform/generic/include/tenstorrent/ascalon.h
new file mode 100644
index 00000000..5d7b7635
--- /dev/null
+++ b/platform/generic/include/tenstorrent/ascalon.h
@@ -0,0 +1,12 @@
+/*
+ * SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef __TENSTORRENT_ASCALON_H__
+#define __TENSTORRENT_ASCALON_H__
+
+void tt_ascalon_discover_pmas_from_boot_hart(void);
+void tt_ascalon_verify_pmas_nonboot_hart(void);
+
+#endif
diff --git a/platform/generic/include/tenstorrent/pma.h b/platform/generic/include/tenstorrent/pma.h
new file mode 100644
index 00000000..051764ee
--- /dev/null
+++ b/platform/generic/include/tenstorrent/pma.h
@@ -0,0 +1,18 @@
+/*
+ * SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef __TENSTORRENT_PMA_H__
+#define __TENSTORRENT_PMA_H__
+
+/* Max number of PMAs for devices (CPU, IOMMU) for Tenstorrent platforms. */
+#define TT_MAX_PMAS	32
+
+u64 tt_pma_get(unsigned int n);
+void tt_pma_set(unsigned int n, u64 pma);
+bool tt_pma_validate(unsigned int i, u64 pma);
+void tt_pma_print(unsigned int i, u64 pma);
+
+#endif
+
diff --git a/platform/generic/tenstorrent/Kconfig b/platform/generic/tenstorrent/Kconfig
new file mode 100644
index 00000000..76c7fb32
--- /dev/null
+++ b/platform/generic/tenstorrent/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
+# SPDX-License-Identifier: BSD-2-Clause
+
+config CPU_TENSTORRENT_ASCALON
+	bool
diff --git a/platform/generic/tenstorrent/ascalon.c b/platform/generic/tenstorrent/ascalon.c
new file mode 100644
index 00000000..485144cd
--- /dev/null
+++ b/platform/generic/tenstorrent/ascalon.c
@@ -0,0 +1,53 @@
+/*
+ * SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_csr_detect.h>
+
+#include <tenstorrent/ascalon.h>
+#include <tenstorrent/pma.h>
+
+#define CSR_PMACFG0	0x7e0
+
+void tt_ascalon_discover_pmas_from_boot_hart(void)
+{
+	struct sbi_trap_info trap = {0};
+
+	/* Whisper virtual platform does not implement PMA */
+	csr_read_allowed(CSR_PMACFG0, &trap);
+	if (trap.cause)
+		return;
+
+	for (unsigned int i = 0; i < TT_MAX_PMAS; i++) {
+		u64 pma = csr_read_num(CSR_PMACFG0 + i);
+		if (!tt_pma_validate(i, pma)) {
+			sbi_printf("HART%d: Bad boot PMA%02d 0x%016lx\n",
+				   current_hartid(), i, pma);
+		}
+		tt_pma_set(i, pma);
+
+		if (pma)
+			tt_pma_print(i, pma);
+	}
+}
+
+void tt_ascalon_verify_pmas_nonboot_hart(void)
+{
+	struct sbi_trap_info trap = {0};
+
+	/* Whisper virtual platform does not implement PMA */
+	csr_read_allowed(CSR_PMACFG0, &trap);
+	if (trap.cause)
+		return;
+
+	for (unsigned int i = 0; i < TT_MAX_PMAS; i++) {
+		u64 pma = csr_read_num(CSR_PMACFG0 + i);
+		if (pma != tt_pma_get(i)) {
+			sbi_printf("HART%d: Bad boot PMA%02d 0x%016lx does not match boot HART\n",
+				   current_hartid(), i, pma);
+		}
+	}
+}
diff --git a/platform/generic/tenstorrent/atlantis.c b/platform/generic/tenstorrent/atlantis.c
new file mode 100644
index 00000000..4c312f7e
--- /dev/null
+++ b/platform/generic/tenstorrent/atlantis.c
@@ -0,0 +1,50 @@
+/*
+ * SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <platform_override.h>
+#include <libfdt.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_system.h>
+#include <sbi/sbi_console.h>
+
+#include <tenstorrent/ascalon.h>
+#include <tenstorrent/pma.h>
+
+static int tt_atlantis_final_init(bool cold_boot)
+{
+	if (cold_boot) {
+		/* Boot firmware sets HART PMAs. Read and verify them. */
+		tt_ascalon_discover_pmas_from_boot_hart();
+	} else {
+		/* Verify nonboot HARTs have PMAs matching boot HART */
+		tt_ascalon_verify_pmas_nonboot_hart();
+	}
+
+	return generic_final_init(cold_boot);
+}
+
+static bool tt_atlantis_single_fw_region(void)
+{
+	return true;
+}
+
+static int tt_atlantis_platform_init(const void *fdt, int nodeoff, const struct fdt_match *match)
+{
+	generic_platform_ops.final_init = tt_atlantis_final_init;
+	generic_platform_ops.single_fw_region = tt_atlantis_single_fw_region;
+
+	return 0;
+}
+
+static const struct fdt_match tt_atlantis_match[] = {
+	{ .compatible = "tenstorrent,atlantis" },
+	{ },
+};
+
+const struct fdt_driver tenstorrent_atlantis = {
+	.match_table = tt_atlantis_match,
+	.init = tt_atlantis_platform_init,
+};
diff --git a/platform/generic/tenstorrent/objects.mk b/platform/generic/tenstorrent/objects.mk
new file mode 100644
index 00000000..1e4aeb2d
--- /dev/null
+++ b/platform/generic/tenstorrent/objects.mk
@@ -0,0 +1,10 @@
+#
+# SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+platform-objs-y += tenstorrent/pma.o
+platform-objs-$(CONFIG_CPU_TENSTORRENT_ASCALON) += tenstorrent/ascalon.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
new file mode 100644
index 00000000..daf60192
--- /dev/null
+++ b/platform/generic/tenstorrent/pma.c
@@ -0,0 +1,138 @@
+/*
+ * SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_math.h>
+#include <libfdt.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+
+#include <tenstorrent/pma.h>
+
+/*
+ * All PMAs in the system should be the same (after boot). The init code
+ * must have set PMAs for all HARTs.
+ */
+
+/*
+ * Ascalon CPU and IOMMU PMA layout:
+ * Field
+ * [2:0]	Permission	[0] Read, [1] Write, [2] Execute
+ * [4:3]	Memory type	00: Main memory, 01: IO memory relaxed,
+ * 				10: IO memory channel 0, 11: IO memory channel 1
+ * [6:5]	AMO type	00: AMONone, 01: AMOSwap,
+ * 				10: AMOLogical, 11: AMOArithmetic
+ * [7]		Cacheability (main memory type)
+ * 				1: Cacheable, 0: Non-cacheable
+ * 		Combining Capability (IO memory type)
+ * 				1: Combining allowed, 0: Combining disallowed
+ * [8]		Routing (coherency)
+ * 				1: Coherent network, 0: Non-coherent network
+ * [11:9]	Reserved
+ * [51:12]	Physical address [51:12] base
+ * [63:58]	Size log 2 (number of address LSB to ignore when matching)
+ * 		0 = invalid entry (no match)
+ */
+
+#define PMA_PERMISSION_R	0x1
+#define PMA_PERMISSION_W	0x2
+#define PMA_PERMISSION_X	0x4
+#define PMA_PERMISSION_MASK	0x7
+
+#define PMA_TYPE_MAIN_MEMORY	0x0
+#define PMA_TYPE_IO_RELAXED	0x8
+#define PMA_TYPE_IO_ORDERED_0	0x10
+#define PMA_TYPE_IO_ORDERED_1	0x18
+#define PMA_TYPE_MASK		0x18
+
+#define PMA_AMO_NONE		0x0
+#define PMA_AMO_SWAP		0x20
+#define PMA_AMO_LOGICAL		0x40
+#define PMA_AMO_ARITHMETIC	0x60
+#define PMA_AMO_MASK		0x60
+
+#define PMA_MEMORY_CACHEABLE	0x80
+#define PMA_IO_COMBINING	0x80
+#define PMA_ROUTING_COHERENT	0x100
+
+#define PMA_FLAGS_MASK		0x00000000000001ffULL
+#define PMA_ADDRESS_MASK	0x000ffffffffff000ULL
+#define PMA_SIZE_MASK		0xfc00000000000000ULL
+#define PMA_RESERVED_MASK	0x0300000000000e00ULL
+
+#define PMA_SIZE_SHIFT		58
+
+static u64 tt_pma_size(u64 pma)
+{
+	if ((pma & PMA_SIZE_MASK) == 0)
+		return 0;
+
+	return 1ULL << ((pma & PMA_SIZE_MASK) >> PMA_SIZE_SHIFT);
+}
+
+static u64 tt_pma_address(u64 pma)
+{
+	return (pma & PMA_ADDRESS_MASK) & ~((tt_pma_size(pma) - 1));
+}
+
+bool tt_pma_validate(unsigned int i, u64 pma)
+{
+	if (!pma)
+		return true;
+
+	if (pma & PMA_RESERVED_MASK) {
+		sbi_printf("PMA%02u 0x%016lx contains reserved bits\n", i, pma);
+		return false;
+	}
+
+	if (tt_pma_size(pma) < 4096) {
+		sbi_printf("PMA%02u 0x%016lx size < 4KB\n", i, pma);
+		return false;
+	}
+
+	if (tt_pma_address(pma) != (pma & PMA_ADDRESS_MASK)) {
+		sbi_printf("PMA%02u 0x%016lx address is not aligned to size\n", i, pma);
+		return false;
+	}
+
+	return true;
+}
+
+void tt_pma_print(unsigned int i, u64 pma)
+{
+	sbi_printf("PMA%02d                       : 0x%016lx-0x%016lx perm:%s%s%s type:%s %s %s amo:%s\n", i,
+			tt_pma_address(pma), tt_pma_address(pma) + tt_pma_size(pma) - 1,
+			pma & PMA_PERMISSION_R ? "R" : " ",
+			pma & PMA_PERMISSION_W ? "W" : " ",
+			pma & PMA_PERMISSION_X ? "X" : " ",
+			(pma & PMA_TYPE_MASK) == PMA_TYPE_MAIN_MEMORY ? "main-memory" :
+			 ((pma & PMA_TYPE_MASK) == PMA_TYPE_IO_RELAXED ? "io-relaxed" :
+			  ((pma & PMA_TYPE_MASK) == PMA_TYPE_IO_ORDERED_0 ? "io-ordered-0" : "io-ordered-1")),
+			(pma & PMA_TYPE_MASK) == PMA_TYPE_MAIN_MEMORY ?
+			 (pma & PMA_MEMORY_CACHEABLE ? "cacheable" : "non-cacheable") :
+			 (pma & PMA_IO_COMBINING ? "combining" : "non-combining"),
+			pma & PMA_ROUTING_COHERENT ? "coherent" : "non-coherent",
+			(pma & PMA_AMO_MASK) == PMA_AMO_NONE ? "none" :
+			 ((pma & PMA_AMO_MASK) == PMA_AMO_SWAP ? "swap" :
+			  ((pma & PMA_AMO_MASK) == PMA_AMO_LOGICAL ? "logical" : "arithmetic")));
+}
+
+static u64 pmas[TT_MAX_PMAS];
+
+void tt_pma_set(unsigned int n, u64 pma)
+{
+	if (n >= TT_MAX_PMAS)
+		sbi_panic("PMA exceeded TT_MAX_PMAS");
+
+	pmas[n] = pma;
+}
+
+u64 tt_pma_get(unsigned int n)
+{
+	if (n >= TT_MAX_PMAS)
+		sbi_panic("PMA exceeded TT_MAX_PMAS");
+
+	return pmas[n];
+}
-- 
2.51.0


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

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/4] lib: sbi: Move PMP encoding into a new file
  2026-03-10  0:49 [PATCH 1/4] platform: generic: Tenstorrent Atlantis support Nicholas Piggin
@ 2026-03-10  0:49 ` 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-03-10  0:49 ` [PATCH 4/4] platform: generic: tenstorrent: Add RISC-V IOMMU support Nicholas Piggin
  2 siblings, 1 reply; 7+ messages in thread
From: Nicholas Piggin @ 2026-03-10  0:49 UTC (permalink / raw)
  To: opensbi; +Cc: Nicholas Piggin

The Tenstorrent RISC-V IOMMU PMP MMRs use the same encoding as PMP CSRs.
In preparation to support it, move the non hart-specific PMP operations
into their own file where they will also be used to build the IOMMU
PMPs.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 include/sbi/riscv_asm.h |   2 +-
 include/sbi/sbi_pmp.h   |  24 +++++++
 lib/sbi/objects.mk      |   1 +
 lib/sbi/riscv_asm.c     | 156 ++++++++++++----------------------------
 lib/sbi/sbi_pmp.c       |  91 +++++++++++++++++++++++
 5 files changed, 164 insertions(+), 110 deletions(-)
 create mode 100644 include/sbi/sbi_pmp.h
 create mode 100644 lib/sbi/sbi_pmp.c

diff --git a/include/sbi/riscv_asm.h b/include/sbi/riscv_asm.h
index 0cf3fc37..b97e1880 100644
--- a/include/sbi/riscv_asm.h
+++ b/include/sbi/riscv_asm.h
@@ -213,7 +213,7 @@ void misa_string(int xlen, char *out, unsigned int out_sz);
 int pmp_disable(unsigned int n);
 
 /* Check if the matching field is set */
-int is_pmp_entry_mapped(unsigned long entry);
+int is_pmp_entry_mapped(unsigned int entry);
 
 int pmp_set(unsigned int n, unsigned long prot, unsigned long addr,
 	    unsigned long log2len);
diff --git a/include/sbi/sbi_pmp.h b/include/sbi/sbi_pmp.h
new file mode 100644
index 00000000..66664bb0
--- /dev/null
+++ b/include/sbi/sbi_pmp.h
@@ -0,0 +1,24 @@
+/*
+ * SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc.
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef __SBI_PMP_H__
+#define __SBI_PMP_H__
+
+#include <sbi/sbi_types.h>
+
+struct pmp {
+	u8	cfg;
+	u64	addr;
+};
+typedef struct pmp pmp_t;
+
+bool pmp_enabled(pmp_t *pmp);
+
+int pmp_create(pmp_t *pmp, unsigned long prot, unsigned long addr,
+	    unsigned long log2len);
+int pmp_decode(pmp_t *pmp, unsigned long *prot, unsigned long *addr,
+	    unsigned long *log2len);
+
+#endif
diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk
index 07d13229..0e29a277 100644
--- a/lib/sbi/objects.mk
+++ b/lib/sbi/objects.mk
@@ -87,6 +87,7 @@ libsbi-objs-y += sbi_init.o
 libsbi-objs-y += sbi_ipi.o
 libsbi-objs-y += sbi_irqchip.o
 libsbi-objs-y += sbi_platform.o
+libsbi-objs-y += sbi_pmp.o
 libsbi-objs-y += sbi_pmu.o
 libsbi-objs-y += sbi_dbtr.o
 libsbi-objs-y += sbi_mpxy.o
diff --git a/lib/sbi/riscv_asm.c b/lib/sbi/riscv_asm.c
index 3e44320f..c11abb1a 100644
--- a/lib/sbi/riscv_asm.c
+++ b/lib/sbi/riscv_asm.c
@@ -12,6 +12,7 @@
 #include <sbi/sbi_error.h>
 #include <sbi/sbi_platform.h>
 #include <sbi/sbi_console.h>
+#include <sbi/sbi_pmp.h>
 
 /* determine CPU extension, return non-zero support */
 int misa_extension_imp(char ext)
@@ -272,24 +273,9 @@ void csr_write_num(int csr_num, unsigned long val)
 #undef switchcase_csr_write
 }
 
-static unsigned long ctz(unsigned long x)
+int hart_pmp_read(unsigned int n, pmp_t *pmp)
 {
-	unsigned long ret = 0;
-
-	if (x == 0)
-		return 8 * sizeof(x);
-
-	while (!(x & 1UL)) {
-		ret++;
-		x = x >> 1;
-	}
-
-	return ret;
-}
-
-int pmp_disable(unsigned int n)
-{
-	int pmpcfg_csr, pmpcfg_shift;
+	int pmpcfg_csr, pmpcfg_shift, pmpaddr_csr;
 	unsigned long cfgmask, pmpcfg;
 
 	if (n >= PMP_COUNT)
@@ -304,44 +290,24 @@ int pmp_disable(unsigned int n)
 #else
 # error "Unexpected __riscv_xlen"
 #endif
+	pmpaddr_csr = CSR_PMPADDR0 + n;
+	pmp->addr = csr_read_num(pmpaddr_csr);
 
-	/* Clear the address matching bits to disable the pmp entry */
-	cfgmask = ~(0xffUL << pmpcfg_shift);
-	pmpcfg	= (csr_read_num(pmpcfg_csr) & cfgmask);
-
-	csr_write_num(pmpcfg_csr, pmpcfg);
+	cfgmask = (0xffUL << pmpcfg_shift);
+	pmpcfg = csr_read_num(pmpcfg_csr) & cfgmask;
+	pmp->cfg = pmpcfg >> pmpcfg_shift;
 
 	return SBI_OK;
 }
 
-int is_pmp_entry_mapped(unsigned long entry)
-{
-	unsigned long prot;
-	unsigned long addr;
-	unsigned long log2len;
-
-	if (pmp_get(entry, &prot, &addr, &log2len) != 0)
-		return false;
-
-	/* If address matching bits are non-zero, the entry is enable */
-	if (prot & PMP_A)
-		return true;
-
-	return false;
-}
-
-int pmp_set(unsigned int n, unsigned long prot, unsigned long addr,
-	    unsigned long log2len)
+int hart_pmp_write(unsigned int n, pmp_t *pmp)
 {
 	int pmpcfg_csr, pmpcfg_shift, pmpaddr_csr;
 	unsigned long cfgmask, pmpcfg;
-	unsigned long addrmask, pmpaddr;
 
-	/* check parameters */
-	if (n >= PMP_COUNT || log2len > __riscv_xlen || log2len < PMP_SHIFT)
+	if (n >= PMP_COUNT)
 		return SBI_EINVAL;
 
-	/* calculate PMP register and offset */
 #if __riscv_xlen == 32
 	pmpcfg_csr   = CSR_PMPCFG0 + (n >> 2);
 	pmpcfg_shift = (n & 3) << 3;
@@ -353,82 +319,54 @@ int pmp_set(unsigned int n, unsigned long prot, unsigned long addr,
 #endif
 	pmpaddr_csr = CSR_PMPADDR0 + n;
 
-	/* encode PMP config */
-	prot &= ~PMP_A;
-	prot |= (log2len == PMP_SHIFT) ? PMP_A_NA4 : PMP_A_NAPOT;
 	cfgmask = ~(0xffUL << pmpcfg_shift);
 	pmpcfg	= (csr_read_num(pmpcfg_csr) & cfgmask);
-	pmpcfg |= ((prot << pmpcfg_shift) & ~cfgmask);
-
-	/* encode PMP address */
-	if (log2len == PMP_SHIFT) {
-		pmpaddr = (addr >> PMP_SHIFT);
-	} else {
-		if (log2len == __riscv_xlen) {
-			pmpaddr = -1UL;
-		} else {
-			addrmask = (1UL << (log2len - PMP_SHIFT)) - 1;
-			pmpaddr	 = ((addr >> PMP_SHIFT) & ~addrmask);
-			pmpaddr |= (addrmask >> 1);
-		}
-	}
+	pmpcfg |= (unsigned long)pmp->cfg << pmpcfg_shift;
 
-	/* write csrs */
-	csr_write_num(pmpaddr_csr, pmpaddr);
+	csr_write_num(pmpaddr_csr, pmp->addr);
 	csr_write_num(pmpcfg_csr, pmpcfg);
 
-	return 0;
+	return SBI_OK;
 }
 
-int pmp_get(unsigned int n, unsigned long *prot_out, unsigned long *addr_out,
-	    unsigned long *log2len)
+int pmp_disable(unsigned int n)
 {
-	int pmpcfg_csr, pmpcfg_shift, pmpaddr_csr;
-	unsigned long cfgmask, pmpcfg, prot;
-	unsigned long t1, addr, len;
+	struct pmp pmp = { .cfg = 0, .addr = 0 };
+	return hart_pmp_write(n, &pmp);
+}
 
-	/* check parameters */
-	if (n >= PMP_COUNT || !prot_out || !addr_out || !log2len)
-		return SBI_EINVAL;
-	*prot_out = *addr_out = *log2len = 0;
+int is_pmp_entry_mapped(unsigned int entry)
+{
+	pmp_t pmp;
 
-	/* calculate PMP register and offset */
-#if __riscv_xlen == 32
-	pmpcfg_csr   = CSR_PMPCFG0 + (n >> 2);
-	pmpcfg_shift = (n & 3) << 3;
-#elif __riscv_xlen == 64
-	pmpcfg_csr   = (CSR_PMPCFG0 + (n >> 2)) & ~1;
-	pmpcfg_shift = (n & 7) << 3;
-#else
-# error "Unexpected __riscv_xlen"
-#endif
-	pmpaddr_csr = CSR_PMPADDR0 + n;
+	if (hart_pmp_read(entry, &pmp))
+		return pmp_enabled(&pmp);
 
-	/* decode PMP config */
-	cfgmask = (0xffUL << pmpcfg_shift);
-	pmpcfg	= csr_read_num(pmpcfg_csr) & cfgmask;
-	prot	= pmpcfg >> pmpcfg_shift;
-
-	/* decode PMP address */
-	if ((prot & PMP_A) == PMP_A_NAPOT) {
-		addr = csr_read_num(pmpaddr_csr);
-		if (addr == -1UL) {
-			addr	= 0;
-			len	= __riscv_xlen;
-		} else {
-			t1	= ctz(~addr);
-			addr	= (addr & ~((1UL << t1) - 1)) << PMP_SHIFT;
-			len	= (t1 + PMP_SHIFT + 1);
-		}
-	} else {
-		addr	= csr_read_num(pmpaddr_csr) << PMP_SHIFT;
-		len	= PMP_SHIFT;
-	}
+	return false;
+}
+
+int pmp_set(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 hart_pmp_write(n, &pmp);
+}
+
+int pmp_get(unsigned int n, unsigned long *prot_out, unsigned long *addr_out,
+	    unsigned long *log2len)
+{
+	pmp_t pmp;
+	int rc;
 
-	/* return details */
-	*prot_out    = prot;
-	*addr_out    = addr;
-	*log2len     = len;
+	rc = hart_pmp_read(n, &pmp);
+	if (rc)
+		return rc;
 
-	return 0;
+	return pmp_decode(&pmp, prot_out, addr_out, log2len);
 }
diff --git a/lib/sbi/sbi_pmp.c b/lib/sbi/sbi_pmp.c
new file mode 100644
index 00000000..22132d81
--- /dev/null
+++ b/lib/sbi/sbi_pmp.c
@@ -0,0 +1,91 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ *   Anup Patel <anup.patel@wdc.com>
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_pmp.h>
+
+bool pmp_enabled(pmp_t *pmp)
+{
+	return pmp->cfg & PMP_A;
+}
+
+int pmp_create(pmp_t *pmp, unsigned long prot, unsigned long addr,
+	    unsigned long log2len)
+{
+	unsigned long addrmask, pmpaddr;
+
+	/* check parameters */
+	if (log2len > __riscv_xlen || log2len < PMP_SHIFT)
+		return SBI_EINVAL;
+
+	/* encode PMP config */
+	prot &= ~PMP_A;
+	prot |= (log2len == PMP_SHIFT) ? PMP_A_NA4 : PMP_A_NAPOT;
+
+	/* encode PMP address */
+	if (log2len == PMP_SHIFT) {
+		pmpaddr = (addr >> PMP_SHIFT);
+	} else {
+		if (log2len == __riscv_xlen) {
+			pmpaddr = -1UL;
+		} else {
+			addrmask = (1UL << (log2len - PMP_SHIFT)) - 1;
+			pmpaddr	 = ((addr >> PMP_SHIFT) & ~addrmask);
+			pmpaddr |= (addrmask >> 1);
+		}
+	}
+
+	pmp->cfg = prot;
+	pmp->addr = pmpaddr;
+
+	return SBI_OK;
+}
+
+static unsigned long cto(unsigned long x)
+{
+	unsigned long ret = 0;
+
+	while (x & 1UL) {
+		ret++;
+		x = x >> 1;
+	}
+
+	return ret;
+}
+
+int pmp_decode(pmp_t *pmp, unsigned long *prot_out, unsigned long *addr_out,
+	    unsigned long *log2len)
+{
+	/* check parameters */
+	if (!pmp || !prot_out || !addr_out || !log2len)
+		return SBI_EINVAL;
+
+	if (!pmp_enabled(pmp))
+		return SBI_EINVAL;
+
+	/* decode PMP address */
+	if ((pmp->cfg & PMP_A) == PMP_A_NAPOT) {
+		if (pmp->addr == -1UL) {
+			*addr_out = 0;
+			*log2len = __riscv_xlen + 3;
+		} else {
+			unsigned long mask = ~((1UL << cto(pmp->addr)) - 1);
+			*addr_out = (pmp->addr & mask) << PMP_SHIFT;
+			*log2len = (cto(pmp->addr) + PMP_SHIFT + 1);
+		}
+	} else {
+		*addr_out = pmp->addr << PMP_SHIFT;
+		*log2len = PMP_SHIFT;
+	}
+
+	return SBI_OK;
+}
-- 
2.51.0


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

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/4] lib: sbi: Add hart_ prefix to PMP functions
  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-03-10  0:49 ` Nicholas Piggin
  2026-04-01 12:50   ` Joel Stanley
  2026-03-10  0:49 ` [PATCH 4/4] platform: generic: tenstorrent: Add RISC-V IOMMU support Nicholas Piggin
  2 siblings, 1 reply; 7+ messages in thread
From: Nicholas Piggin @ 2026-03-10  0:49 UTC (permalink / raw)
  To: opensbi; +Cc: Nicholas Piggin

Give PMP functions that deal with HART CSRs a hart_ prefix, to help
distinguish from general PMP encoding functions that will be shared by
the Tenstorrent IOMMU.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 include/sbi/riscv_asm.h          |  8 +++----
 lib/sbi/riscv_asm.c              | 10 ++++-----
 lib/sbi/sbi_hart_pmp.c           | 14 ++++++-------
 platform/generic/eswin/eic770x.c | 36 ++++++++++++++++----------------
 4 files changed, 34 insertions(+), 34 deletions(-)

diff --git a/include/sbi/riscv_asm.h b/include/sbi/riscv_asm.h
index b97e1880..14bc35e5 100644
--- a/include/sbi/riscv_asm.h
+++ b/include/sbi/riscv_asm.h
@@ -210,15 +210,15 @@ int misa_xlen(void);
 void misa_string(int xlen, char *out, unsigned int out_sz);
 
 /* Disable pmp entry at a given index */
-int pmp_disable(unsigned int n);
+int hart_pmp_disable(unsigned int n);
 
 /* Check if the matching field is set */
-int is_pmp_entry_mapped(unsigned int entry);
+int hart_is_pmp_enabled(unsigned int n);
 
-int pmp_set(unsigned int n, unsigned long prot, unsigned long addr,
+int hart_pmp_set(unsigned int n, unsigned long prot, unsigned long addr,
 	    unsigned long log2len);
 
-int pmp_get(unsigned int n, unsigned long *prot_out, unsigned long *addr_out,
+int hart_pmp_get(unsigned int n, unsigned long *prot_out, unsigned long *addr_out,
 	    unsigned long *log2len);
 
 #endif /* !__ASSEMBLER__ */
diff --git a/lib/sbi/riscv_asm.c b/lib/sbi/riscv_asm.c
index c11abb1a..03c8e4d7 100644
--- a/lib/sbi/riscv_asm.c
+++ b/lib/sbi/riscv_asm.c
@@ -329,23 +329,23 @@ int hart_pmp_write(unsigned int n, pmp_t *pmp)
 	return SBI_OK;
 }
 
-int pmp_disable(unsigned int n)
+int hart_pmp_disable(unsigned int n)
 {
 	struct pmp pmp = { .cfg = 0, .addr = 0 };
 	return hart_pmp_write(n, &pmp);
 }
 
-int is_pmp_entry_mapped(unsigned int entry)
+int hart_is_pmp_enabled(unsigned int n)
 {
 	pmp_t pmp;
 
-	if (hart_pmp_read(entry, &pmp))
+	if (hart_pmp_read(n, &pmp))
 		return pmp_enabled(&pmp);
 
 	return false;
 }
 
-int pmp_set(unsigned int n, unsigned long prot, unsigned long addr,
+int hart_pmp_set(unsigned int n, unsigned long prot, unsigned long addr,
 	    unsigned long log2len)
 {
 	pmp_t pmp;
@@ -358,7 +358,7 @@ int pmp_set(unsigned int n, unsigned long prot, unsigned long addr,
 	return hart_pmp_write(n, &pmp);
 }
 
-int pmp_get(unsigned int n, unsigned long *prot_out, unsigned long *addr_out,
+int hart_pmp_get(unsigned int n, unsigned long *prot_out, unsigned long *addr_out,
 	    unsigned long *log2len)
 {
 	pmp_t pmp;
diff --git a/lib/sbi/sbi_hart_pmp.c b/lib/sbi/sbi_hart_pmp.c
index 02a3b3c4..0aa4752f 100644
--- a/lib/sbi/sbi_hart_pmp.c
+++ b/lib/sbi/sbi_hart_pmp.c
@@ -100,7 +100,7 @@ static void sbi_hart_smepmp_set(struct sbi_scratch *scratch,
 		sbi_platform_pmp_set(sbi_platform_ptr(scratch),
 				     pmp_idx, reg->flags, pmp_flags,
 				     reg->base, reg->order);
-		pmp_set(pmp_idx, pmp_flags, reg->base, reg->order);
+		hart_pmp_set(pmp_idx, pmp_flags, reg->base, reg->order);
 	} else {
 		sbi_printf("Can not configure pmp for domain %s because"
 			   " memory region address 0x%lx or size 0x%lx "
@@ -139,7 +139,7 @@ static int sbi_hart_smepmp_configure(struct sbi_scratch *scratch)
 	csr_set(CSR_MSECCFG, MSECCFG_RLB);
 
 	/* Disable the reserved entry */
-	pmp_disable(SBI_SMEPMP_RESV_ENTRY);
+	hart_pmp_disable(SBI_SMEPMP_RESV_ENTRY);
 
 	/* Program M-only regions when MML is not set. */
 	pmp_idx = 0;
@@ -224,7 +224,7 @@ static int sbi_hart_smepmp_map_range(struct sbi_scratch *scratch,
 	unsigned int pmp_flags = (PMP_W | PMP_X);
 	unsigned long order, base = 0;
 
-	if (is_pmp_entry_mapped(SBI_SMEPMP_RESV_ENTRY))
+	if (hart_is_pmp_enabled(SBI_SMEPMP_RESV_ENTRY))
 		return SBI_ENOSPC;
 
 	for (order = MAX(sbi_hart_pmp_log2gran(scratch), log2roundup(size));
@@ -244,7 +244,7 @@ static int sbi_hart_smepmp_map_range(struct sbi_scratch *scratch,
 	sbi_platform_pmp_set(sbi_platform_ptr(scratch), SBI_SMEPMP_RESV_ENTRY,
 			     SBI_DOMAIN_MEMREGION_SHARED_SURW_MRW,
 			     pmp_flags, base, order);
-	pmp_set(SBI_SMEPMP_RESV_ENTRY, pmp_flags, base, order);
+	hart_pmp_set(SBI_SMEPMP_RESV_ENTRY, pmp_flags, base, order);
 
 	return SBI_OK;
 }
@@ -253,7 +253,7 @@ static int sbi_hart_smepmp_unmap_range(struct sbi_scratch *scratch,
 				       unsigned long addr, unsigned long size)
 {
 	sbi_platform_pmp_disable(sbi_platform_ptr(scratch), SBI_SMEPMP_RESV_ENTRY);
-	return pmp_disable(SBI_SMEPMP_RESV_ENTRY);
+	return hart_pmp_disable(SBI_SMEPMP_RESV_ENTRY);
 }
 
 static int sbi_hart_oldpmp_configure(struct sbi_scratch *scratch)
@@ -281,7 +281,7 @@ static int sbi_hart_oldpmp_configure(struct sbi_scratch *scratch)
 			sbi_platform_pmp_set(sbi_platform_ptr(scratch),
 					     pmp_idx, reg->flags, pmp_flags,
 					     reg->base, reg->order);
-			pmp_set(pmp_idx++, pmp_flags, reg->base, reg->order);
+			hart_pmp_set(pmp_idx++, pmp_flags, reg->base, reg->order);
 		} else {
 			sbi_printf("Can not configure pmp for domain %s because"
 				   " memory region address 0x%lx or size 0x%lx "
@@ -307,7 +307,7 @@ static void sbi_hart_pmp_unconfigure(struct sbi_scratch *scratch)
 			continue;
 
 		sbi_platform_pmp_disable(sbi_platform_ptr(scratch), i);
-		pmp_disable(i);
+		hart_pmp_disable(i);
 	}
 }
 
diff --git a/platform/generic/eswin/eic770x.c b/platform/generic/eswin/eic770x.c
index 7330df9f..dfca0c41 100644
--- a/platform/generic/eswin/eic770x.c
+++ b/platform/generic/eswin/eic770x.c
@@ -254,21 +254,21 @@ static int eswin_eic7700_final_init(bool cold_boot)
 				   __func__);
 			return SBI_EFAIL;
 		}
-		pmp_set(pmp_idx++, sbi_domain_get_oldpmp_flags(reg),
-			reg->base, reg->order);
+		hart_pmp_set(pmp_idx++, sbi_domain_get_oldpmp_flags(reg),
+			     reg->base, reg->order);
 	}
 
-	pmp_set(PMP_RESERVED_A, PMP_L, EIC770X_L3_ZERO_REMOTE,
-			   log2roundup(EIC770X_L3_ZERO_SIZE));
+	hart_pmp_set(PMP_RESERVED_A, PMP_L, EIC770X_L3_ZERO_REMOTE,
+		     log2roundup(EIC770X_L3_ZERO_SIZE));
 	/**
 	 * Enable P550 internal + System Port, so OpenSBI can access
 	 * CLINT/PLIC/UART. Might be overwritten in pmp_configure.
 	 */
-	pmp_set(PMP_FREE_A_START + PMP_FREE_A_COUNT - 1, 0, 0,
-		log2roundup(EIC770X_MEMPORT_BASE));
+	hart_pmp_set(PMP_FREE_A_START + PMP_FREE_A_COUNT - 1, 0, 0,
+		     log2roundup(EIC770X_MEMPORT_BASE));
 
-	pmp_set(PMP_RESERVED_B, PMP_L, 0,
-		log2roundup(EIC770X_MEMPORT_LIMIT));
+	hart_pmp_set(PMP_RESERVED_B, PMP_L, 0,
+		     log2roundup(EIC770X_MEMPORT_LIMIT));
 	/**
 	 * These must come after the setup of PMP, as we are about to
 	 * enable speculation and HW prefetcher bits
@@ -321,13 +321,13 @@ static int eswin_eic7700_pmp_configure(struct sbi_scratch *scratch)
 		if (pmp_idx >= pmp_max)
 			goto no_more_pmp;
 
-		pmp_set(pmp_idx++, sbi_domain_get_oldpmp_flags(reg),
-			reg->base, reg->order);
+		hart_pmp_set(pmp_idx++, sbi_domain_get_oldpmp_flags(reg),
+			     reg->base, reg->order);
 		prev = reg;
 	}
 	/* Disable the rest */
 	while (pmp_idx < pmp_max)
-		pmp_disable(pmp_idx++);
+		hart_pmp_disable(pmp_idx++);
 
 	/* Process the second free range B [7-7] */
 	pmp_idx = PMP_FREE_B_START,
@@ -340,12 +340,12 @@ static int eswin_eic7700_pmp_configure(struct sbi_scratch *scratch)
 		if (pmp_idx >= pmp_max)
 			goto no_more_pmp;
 
-		pmp_set(pmp_idx++, sbi_domain_get_oldpmp_flags(reg),
-			reg->base, reg->order);
+		hart_pmp_set(pmp_idx++, sbi_domain_get_oldpmp_flags(reg),
+			     reg->base, reg->order);
 	}
 	/* Disable the rest */
 	while (pmp_idx < pmp_max)
-		pmp_disable(pmp_idx++);
+		hart_pmp_disable(pmp_idx++);
 
 	sbi_hart_pmp_fence();
 	return 0;
@@ -357,14 +357,14 @@ no_more_pmp:
 static void eswin_eic7700_pmp_unconfigure(struct sbi_scratch *scratch)
 {
 	/* Enable P550 internal + System Port */
-	pmp_set(PMP_FREE_A_START + PMP_FREE_A_COUNT - 1, 0, 0,
-		log2roundup(EIC770X_MEMPORT_BASE));
+	hart_pmp_set(PMP_FREE_A_START + PMP_FREE_A_COUNT - 1, 0, 0,
+		     log2roundup(EIC770X_MEMPORT_BASE));
 
 	for (unsigned int i = 0; i < PMP_FREE_A_COUNT - 1; i++)
-		pmp_disable(i + PMP_FREE_A_START);
+		hart_pmp_disable(i + PMP_FREE_A_START);
 
 	for (unsigned int i = 0; i < PMP_FREE_B_COUNT; i++)
-		pmp_disable(i + PMP_FREE_B_START);
+		hart_pmp_disable(i + PMP_FREE_B_START);
 }
 
 static struct sbi_hart_protection eswin_eic7700_pmp_protection = {
-- 
2.51.0


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

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/4] platform: generic: tenstorrent: Add RISC-V IOMMU support
  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-03-10  0:49 ` [PATCH 3/4] lib: sbi: Add hart_ prefix to PMP functions Nicholas Piggin
@ 2026-03-10  0:49 ` Nicholas Piggin
  2026-04-01 12:51   ` Joel Stanley
  2 siblings, 1 reply; 7+ messages in thread
From: Nicholas Piggin @ 2026-03-10  0:49 UTC (permalink / raw)
  To: opensbi; +Cc: Nicholas Piggin

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

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 3/4] lib: sbi: Add hart_ prefix to PMP functions
  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
  0 siblings, 0 replies; 7+ messages in thread
From: Joel Stanley @ 2026-04-01 12:50 UTC (permalink / raw)
  To: Nicholas Piggin; +Cc: opensbi

On Tue, 10 Mar 2026 at 11:19, Nicholas Piggin <npiggin@gmail.com> wrote:
>
> Give PMP functions that deal with HART CSRs a hart_ prefix, to help
> distinguish from general PMP encoding functions that will be shared by
> the Tenstorrent IOMMU.
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  include/sbi/riscv_asm.h          |  8 +++----
>  lib/sbi/riscv_asm.c              | 10 ++++-----
>  lib/sbi/sbi_hart_pmp.c           | 14 ++++++-------

Some changes have been made to master since you sent your patch. This
fixup is required for sbi_hart_pmp.c :

--- a/lib/sbi/sbi_hart_pmp.c
+++ b/lib/sbi/sbi_hart_pmp.c
@@ -206,7 +206,7 @@ static int sbi_hart_smepmp_configure(struct
sbi_scratch *scratch)
        }
        /* Disable remaining PMP entries */
        for(; pmp_idx < pmp_count; pmp_idx++)
-               pmp_disable(pmp_idx);
+               hart_pmp_disable(pmp_idx);

        /*
         * All entries are programmed.
@@ -291,7 +291,7 @@ static int sbi_hart_oldpmp_configure(struct
sbi_scratch *scratch)
        }
        /* Disable remaining PMP entries */
        for(; pmp_idx < pmp_count; pmp_idx++)
-               pmp_disable(pmp_idx);
+               hart_pmp_disable(pmp_idx);

        sbi_hart_pmp_fence();
        return 0;

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/4] lib: sbi: Move PMP encoding into a new file
  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
  0 siblings, 0 replies; 7+ messages in thread
From: Joel Stanley @ 2026-04-01 12:50 UTC (permalink / raw)
  To: Nicholas Piggin; +Cc: opensbi

On Tue, 10 Mar 2026 at 11:19, Nicholas Piggin <npiggin@gmail.com> wrote:
>
> The Tenstorrent RISC-V IOMMU PMP MMRs use the same encoding as PMP CSRs.
> In preparation to support it, move the non hart-specific PMP operations
> into their own file where they will also be used to build the IOMMU
> PMPs.
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>

> --- a/lib/sbi/riscv_asm.c
> +++ b/lib/sbi/riscv_asm.c

> +int is_pmp_entry_mapped(unsigned int entry)
> +{
> +       pmp_t pmp;

> +       if (hart_pmp_read(entry, &pmp))
> +               return pmp_enabled(&pmp);

The diff is a bit all over the place so I may have confused myself.
The patch keeps is_pmp_entry_mapped but changes the body from

    if (pmp_get(entry, &prot, &addr, &log2len) != 0)
        return false;

to

    if (hart_pmp_read(entry, &pmp))
        return pmp_enabled(&pmp);

but hart_pmp_read returns SBI_OK (0) on success. So we should keep the !=0?

> +int pmp_get(unsigned int n, unsigned long *prot_out, unsigned long *addr_out,
> +           unsigned long *log2len)
> +{
> +       pmp_t pmp;
> +       int rc;
>
> -       /* return details */
> -       *prot_out    = prot;

It looks like prot_out is no longer written to after your patch. This
hunk never made it into the new pmp_decode:

       /* decode PMP config */
       cfgmask = (0xffUL << pmpcfg_shift);
       pmpcfg  = csr_read_num(pmpcfg_csr) & cfgmask;
       prot    = pmpcfg >> pmpcfg_shift;


> --- /dev/null
> +++ b/lib/sbi/sbi_pmp.c
> @@ -0,0 +1,91 @@

> +int pmp_create(pmp_t *pmp, unsigned long prot, unsigned long addr,
> +           unsigned long log2len)
> +{
> +       unsigned long addrmask, pmpaddr;
> +
> +       /* check parameters */
> +       if (log2len > __riscv_xlen || log2len < PMP_SHIFT)
> +               return SBI_EINVAL;

Here we check that log2len <=__riscv_xlen, but in pmp_decode below we
set log2len =  __riscv_xlen + 3. Is that just how it works, or should
pmp_decode be able to decode what pmp_create does?

> +
> +       /* encode PMP config */
> +       prot &= ~PMP_A;
> +       prot |= (log2len == PMP_SHIFT) ? PMP_A_NA4 : PMP_A_NAPOT;
> +
> +       /* encode PMP address */
> +       if (log2len == PMP_SHIFT) {
> +               pmpaddr = (addr >> PMP_SHIFT);
> +       } else {
> +               if (log2len == __riscv_xlen) {
> +                       pmpaddr = -1UL;
> +               } else {
> +                       addrmask = (1UL << (log2len - PMP_SHIFT)) - 1;
> +                       pmpaddr  = ((addr >> PMP_SHIFT) & ~addrmask);
> +                       pmpaddr |= (addrmask >> 1);
> +               }
> +       }
> +
> +       pmp->cfg = prot;
> +       pmp->addr = pmpaddr;
> +
> +       return SBI_OK;
> +}
> +
> +int pmp_decode(pmp_t *pmp, unsigned long *prot_out, unsigned long *addr_out,
> +           unsigned long *log2len)
> +{
> +       /* check parameters */
> +       if (!pmp || !prot_out || !addr_out || !log2len)
> +               return SBI_EINVAL;
> +
> +       if (!pmp_enabled(pmp))
> +               return SBI_EINVAL;
> +
> +       /* decode PMP address */
> +       if ((pmp->cfg & PMP_A) == PMP_A_NAPOT) {
> +               if (pmp->addr == -1UL) {
> +                       *addr_out = 0;
> +                       *log2len = __riscv_xlen + 3;
> +               } else {
> +                       unsigned long mask = ~((1UL << cto(pmp->addr)) - 1);
> +                       *addr_out = (pmp->addr & mask) << PMP_SHIFT;
> +                       *log2len = (cto(pmp->addr) + PMP_SHIFT + 1);
> +               }
> +       } else {
> +               *addr_out = pmp->addr << PMP_SHIFT;
> +               *log2len = PMP_SHIFT;
> +       }
> +
> +       return SBI_OK;
> +}

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 4/4] platform: generic: tenstorrent: Add RISC-V IOMMU support
  2026-03-10  0:49 ` [PATCH 4/4] platform: generic: tenstorrent: Add RISC-V IOMMU support Nicholas Piggin
@ 2026-04-01 12:51   ` Joel Stanley
  0 siblings, 0 replies; 7+ messages in thread
From: Joel Stanley @ 2026-04-01 12:51 UTC (permalink / raw)
  To: Nicholas Piggin; +Cc: opensbi

On Tue, 10 Mar 2026 at 11:19, Nicholas Piggin <npiggin@gmail.com> wrote:

> --- /dev/null
> +++ b/platform/generic/tenstorrent/iommu.c

> +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;
> +               }

should this set found_dt = true here?

> +
> +               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;

because found_dt never gets modified.

> +
> +       return SBI_OK;
> +}

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-04-01 12:51 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH 4/4] platform: generic: tenstorrent: Add RISC-V IOMMU support Nicholas Piggin
2026-04-01 12:51   ` Joel Stanley

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox