[PATCH v1 0/4] Add support for Texas Instrument's Peripheral Virtualization Unit

13 views
Skip to first unread message

Nikhil Devshatwar

unread,
Dec 30, 2019, 8:25:04 AM12/30/19
to jailho...@googlegroups.com, lokes...@ti.com, jan.k...@siemens.com, Nikhil Devshatwar
This series adds support for TI PVU as an iommu unit.
PVU is a 2nd stage only IOMMU which provides realtime address translation.

J721e has 3 instances of PVU and all the DMA traffic can be routed via PVU
when running inside a virtual machine.

Nikhil Devshatwar (4):
arm64: ti-pvu: Add support for ti-pvu iommu unit
arm64: smmu-v3: Fix crash when first iommu is not SMMUV3
configs: arm64: k3-j721e-evm: Describe PVU IOMMU devices in
platform_data
configs: arm64: k3-j721e-evm: Add stream ids for devices behind IOMMU

configs/arm64/k3-j721e-evm-linux-demo.c | 7 +
configs/arm64/k3-j721e-evm.c | 38 ++
hypervisor/arch/arm-common/include/asm/cell.h | 7 +
.../arch/arm-common/include/asm/iommu.h | 1 +
.../arch/arm-common/include/asm/ti-pvu.h | 32 +
hypervisor/arch/arm-common/iommu.c | 9 +
hypervisor/arch/arm64/Kbuild | 1 +
hypervisor/arch/arm64/smmu-v3.c | 20 +-
hypervisor/arch/arm64/ti-pvu.c | 556 ++++++++++++++++++
hypervisor/arch/arm64/ti-pvu_priv.h | 141 +++++
include/jailhouse/cell-config.h | 4 +
11 files changed, 806 insertions(+), 10 deletions(-)
create mode 100644 hypervisor/arch/arm-common/include/asm/ti-pvu.h
create mode 100644 hypervisor/arch/arm64/ti-pvu.c
create mode 100644 hypervisor/arch/arm64/ti-pvu_priv.h

--
2.17.1

Nikhil Devshatwar

unread,
Dec 30, 2019, 8:25:06 AM12/30/19
to jailho...@googlegroups.com, lokes...@ti.com, jan.k...@siemens.com, Nikhil Devshatwar
Add support for Texas Instrument's Peripheral Virtualization Unit
* Define a new IOMMU type and extra fields in the platform_data
* Add new cofig option CONFIG_IOMMU_TI_PVU
* Integrate with the arm iommu support such that multiple types
of IOMMU can be supported.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
---
hypervisor/arch/arm-common/include/asm/cell.h | 7 +
.../arch/arm-common/include/asm/iommu.h | 1 +
.../arch/arm-common/include/asm/ti-pvu.h | 32 +
hypervisor/arch/arm-common/iommu.c | 9 +
hypervisor/arch/arm64/Kbuild | 1 +
hypervisor/arch/arm64/ti-pvu.c | 556 ++++++++++++++++++
hypervisor/arch/arm64/ti-pvu_priv.h | 141 +++++
include/jailhouse/cell-config.h | 4 +
8 files changed, 751 insertions(+)
create mode 100644 hypervisor/arch/arm-common/include/asm/ti-pvu.h
create mode 100644 hypervisor/arch/arm64/ti-pvu.c
create mode 100644 hypervisor/arch/arm64/ti-pvu_priv.h

diff --git a/hypervisor/arch/arm-common/include/asm/cell.h b/hypervisor/arch/arm-common/include/asm/cell.h
index 5b1e4207..9c6e8c6f 100644
--- a/hypervisor/arch/arm-common/include/asm/cell.h
+++ b/hypervisor/arch/arm-common/include/asm/cell.h
@@ -15,10 +15,17 @@

#include <jailhouse/paging.h>

+struct pvu_tlb_entry;
+
struct arch_cell {
struct paging_structures mm;

u32 irq_bitmap[1024/32];
+
+ struct {
+ u8 ent_count;
+ struct pvu_tlb_entry *entries;
+ } iommu_pvu; /**< ARM PVU specific fields. */
};

#endif /* !_JAILHOUSE_ASM_CELL_H */
diff --git a/hypervisor/arch/arm-common/include/asm/iommu.h b/hypervisor/arch/arm-common/include/asm/iommu.h
index dde762c0..399248dc 100644
--- a/hypervisor/arch/arm-common/include/asm/iommu.h
+++ b/hypervisor/arch/arm-common/include/asm/iommu.h
@@ -16,6 +16,7 @@
#include <jailhouse/cell.h>
#include <jailhouse/utils.h>
#include <jailhouse/cell-config.h>
+#include <asm/ti-pvu.h>

#define for_each_stream_id(sid, config, counter) \
for ((sid) = (jailhouse_cell_stream_ids(config)[0]), (counter) = 0; \
diff --git a/hypervisor/arch/arm-common/include/asm/ti-pvu.h b/hypervisor/arch/arm-common/include/asm/ti-pvu.h
new file mode 100644
index 00000000..a3ef72f7
--- /dev/null
+++ b/hypervisor/arch/arm-common/include/asm/ti-pvu.h
@@ -0,0 +1,32 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * TI PVU IOMMU unit API headers
+ *
+ * Authors:
+ * Nikhil Devshatwar <nikh...@ti.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef _IOMMMU_PVU_H_
+#define _IOMMMU_PVU_H_
+
+#include <jailhouse/config.h>
+
+#ifdef CONFIG_IOMMU_TI_PVU
+
+int pvu_iommu_map_memory(struct cell *cell,
+ const struct jailhouse_memory *mem);
+
+int pvu_iommu_unmap_memory(struct cell *cell,
+ const struct jailhouse_memory *mem);
+
+int pvu_iommu_config_commit(struct cell *cell);
+
+#endif /* CONFIG_IOMMU_TI_PVU */
+
+#endif /* _IOMMMU_PVU_H_ */
diff --git a/hypervisor/arch/arm-common/iommu.c b/hypervisor/arch/arm-common/iommu.c
index b3100d03..b6b61f52 100644
--- a/hypervisor/arch/arm-common/iommu.c
+++ b/hypervisor/arch/arm-common/iommu.c
@@ -26,15 +26,24 @@ unsigned int iommu_count_units(void)
int iommu_map_memory_region(struct cell *cell,
const struct jailhouse_memory *mem)
{
+#ifdef CONFIG_IOMMU_TI_PVU
+ return pvu_iommu_map_memory(cell, mem);
+#endif
return 0;
}

int iommu_unmap_memory_region(struct cell *cell,
const struct jailhouse_memory *mem)
{
+#ifdef CONFIG_IOMMU_TI_PVU
+ return pvu_iommu_unmap_memory(cell, mem);
+#endif
return 0;
}

void iommu_config_commit(struct cell *cell)
{
+#ifdef CONFIG_IOMMU_TI_PVU
+ pvu_iommu_config_commit(cell);
+#endif
}
diff --git a/hypervisor/arch/arm64/Kbuild b/hypervisor/arch/arm64/Kbuild
index 323b78b6..8012c46e 100644
--- a/hypervisor/arch/arm64/Kbuild
+++ b/hypervisor/arch/arm64/Kbuild
@@ -21,3 +21,4 @@ always := lib.a

lib-y := $(common-objs-y)
lib-y += entry.o setup.o control.o mmio.o paging.o caches.o traps.o smmu-v3.o
+lib-$(CONFIG_IOMMU_TI_PVU) += ti-pvu.o
diff --git a/hypervisor/arch/arm64/ti-pvu.c b/hypervisor/arch/arm64/ti-pvu.c
new file mode 100644
index 00000000..02380baa
--- /dev/null
+++ b/hypervisor/arch/arm64/ti-pvu.c
@@ -0,0 +1,556 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * TI PVU IOMMU unit
+ *
+ * Peripheral Virtualization Unit(PVU) is an IOMMU (memory management
+ * unit for DMA) which is designed for 2nd stage address translation in a
+ * real time manner.
+ *
+ * Unlike ARM-SMMU, all the memory mapping information is stored in the
+ * local registers instead of the in-memory page tables.
+ *
+ * There are limitations on the number of available contexts, page sizes,
+ * number of pages that can be mapped, etc.
+ *
+ * PVU is desgined to be programmed with all the memory mapping at once.
+ * Therefore, it defers the actual register programming till config_commit.
+ * Also, it does not support unmapping of the pages at runtime.
+ *
+ * Authors:
+ * Nikhil Devshatwar <nikh...@ti.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <jailhouse/unit.h>
+#include <jailhouse/cell.h>
+#include <jailhouse/entry.h>
+#include <jailhouse/paging.h>
+#include <jailhouse/control.h>
+#include <jailhouse/printk.h>
+#include <asm/iommu.h>
+#include <asm/ti-pvu.h>
+#include "ti-pvu_priv.h"
+
+#define MAX_PVU_ENTRIES (PAGE_SIZE / sizeof (struct pvu_tlb_entry))
+#define MAX_VIRTID 7
+
+static struct pvu_dev pvu_units[JAILHOUSE_MAX_IOMMU_UNITS];
+static unsigned int pvu_count;
+
+static const u64 PVU_PAGE_SIZE_BYTES[] = {
+ [LPAE_PAGE_SZ_4K] = 4 * 1024,
+ [LPAE_PAGE_SZ_16K] = 16 * 1024,
+ [LPAE_PAGE_SZ_64K] = 64 * 1024,
+ [LPAE_PAGE_SZ_2M] = 2 * 1024 * 1024,
+ [LPAE_PAGE_SZ_32M] = 32 * 1024 * 1024,
+ [LPAE_PAGE_SZ_512M] = 512 * 1024 * 1024,
+ [LPAE_PAGE_SZ_1G] = 1 * 1024 * 1024 * 1024,
+ [LPAE_PAGE_SZ_16G] = 16ULL * 1024 * 1024 * 1024,
+};
+
+static inline u32 is_aligned(u64 addr, u64 size)
+{
+ return (addr % size) == 0;
+}
+
+static void pvu_tlb_enable(struct pvu_dev *dev, u16 tlbnum)
+{
+ struct pvu_hw_tlb *tlb;
+
+ tlb = (struct pvu_hw_tlb *)dev->tlb_base + tlbnum;
+ mmio_write32_field(&tlb->chain, PVU_TLB_LOG_DIS_MASK, 0);
+ mmio_write32_field(&tlb->chain, PVU_TLB_EN_MASK, 1);
+}
+
+static void pvu_tlb_disable(struct pvu_dev *dev, u16 tlbnum)
+{
+ struct pvu_hw_tlb *tlb;
+
+ tlb = (struct pvu_hw_tlb *)dev->tlb_base + tlbnum;
+ mmio_write32_field(&tlb->chain, PVU_TLB_EN_MASK, 0);
+ mmio_write32_field(&tlb->chain, PVU_TLB_LOG_DIS_MASK, 1);
+}
+
+static u32 pvu_tlb_is_enabled(struct pvu_dev *dev, u16 tlbnum)
+{
+ struct pvu_hw_tlb *tlb;
+
+ tlb = (struct pvu_hw_tlb *)dev->tlb_base + tlbnum;
+ if (mmio_read32_field(&tlb->chain, PVU_TLB_EN_MASK))
+ return 1;
+ else
+ return 0;
+}
+
+static int pvu_tlb_chain(struct pvu_dev *dev, u16 tlbnum, u16 tlb_next)
+{
+ struct pvu_hw_tlb *tlb;
+
+ if (tlb_next <= tlbnum || tlb_next <= dev->max_virtid)
+ return -EINVAL;
+
+ tlb = (struct pvu_hw_tlb *)dev->tlb_base + tlbnum;
+ mmio_write32_field(&tlb->chain, PVU_TLB_CHAIN_MASK, tlb_next);
+ return 0;
+}
+
+static u32 pvu_tlb_next(struct pvu_dev *dev, u16 tlbnum)
+{
+ struct pvu_hw_tlb *tlb;
+
+ tlb = (struct pvu_hw_tlb *)dev->tlb_base + tlbnum;
+ return mmio_read32_field(&tlb->chain, PVU_TLB_CHAIN_MASK);
+}
+
+static u32 pvu_tlb_alloc(struct pvu_dev *dev, u16 virtid)
+{
+ int i;
+
+ for (i = dev->max_virtid + 1; i < dev->num_tlbs; i++) {
+ if (dev->tlb_data[i] == 0) {
+ dev->tlb_data[i] = virtid << dev->num_entries;
+ return i;
+ }
+ }
+ return 0;
+}
+
+static void pvu_tlb_flush(struct pvu_dev *dev, u16 tlbnum)
+{
+ struct pvu_hw_tlb_entry *entry;
+ struct pvu_hw_tlb *tlb;
+ u32 i;
+
+ pvu_tlb_disable(dev, tlbnum);
+ tlb = (struct pvu_hw_tlb *)dev->tlb_base + tlbnum;
+
+ for (i = 0; i < dev->num_entries; i++) {
+
+ entry = &tlb->entry[i];
+ mmio_write32(&entry->reg0, 0x0);
+ mmio_write32(&entry->reg1, 0x0);
+ mmio_write32(&entry->reg2, 0x0);
+ mmio_write32(&entry->reg4, 0x0);
+ mmio_write32(&entry->reg5, 0x0);
+ mmio_write32(&entry->reg6, 0x0);
+ }
+
+ mmio_write32(&tlb->chain, 0x0);
+ pvu_tlb_disable(dev, tlbnum);
+
+ if (i < dev->max_virtid)
+ dev->tlb_data[tlbnum] = 0x0 | i << dev->num_entries;
+ else
+ dev->tlb_data[tlbnum] = 0x0;
+
+}
+
+static void pvu_entry_enable(struct pvu_dev *dev, u16 tlbnum, u8 index)
+{
+ struct pvu_hw_tlb_entry *entry;
+ struct pvu_hw_tlb *tlb;
+
+ tlb = (struct pvu_hw_tlb *)dev->tlb_base + tlbnum;
+ entry = &tlb->entry[index];
+
+ mmio_write32_field(&entry->reg2, PVU_TLB_ENTRY_MODE_MASK,
+ PVU_TLB_ENTRY_VALID);
+
+ dev->tlb_data[tlbnum] |= (1 << index);
+}
+
+static int pvu_entry_write(struct pvu_dev *dev, u16 tlbnum, u8 index,
+ struct pvu_tlb_entry *ent)
+{
+ struct pvu_hw_tlb_entry *entry;
+ struct pvu_hw_tlb *tlb;
+ u8 pgsz;
+
+ tlb = (struct pvu_hw_tlb *)dev->tlb_base + tlbnum;
+ entry = &tlb->entry[index];
+
+ for (pgsz = 0; pgsz < ARRAY_SIZE(PVU_PAGE_SIZE_BYTES); pgsz++) {
+ if (ent->size == PVU_PAGE_SIZE_BYTES[pgsz])
+ break;
+ }
+
+ if (pgsz >= ARRAY_SIZE(PVU_PAGE_SIZE_BYTES)) {
+ printk("ERROR: PVU: %s: Unsupported page size %llx\n",
+ __func__, ent->size);
+ return -EINVAL;
+ }
+
+ if (!is_aligned(ent->virt_addr, ent->size) ||
+ !is_aligned(ent->phys_addr, ent->size)) {
+ printk("ERROR: PVU: %s: Address %llx => %llx is not aligned with size %llx\n",
+ __func__, ent->virt_addr, ent->phys_addr, ent->size);
+ return -EINVAL;
+ }
+
+ mmio_write32(&entry->reg0, ent->virt_addr & 0xffffffff);
+ mmio_write32_field(&entry->reg1, 0xffff, (ent->virt_addr >> 32));
+ mmio_write32(&entry->reg2, 0x0);
+
+ mmio_write32(&entry->reg4, ent->phys_addr & 0xffffffff);
+ mmio_write32_field(&entry->reg5, 0xffff, (ent->phys_addr >> 32));
+ mmio_write32(&entry->reg6, 0x0);
+
+ mmio_write32_field(&entry->reg2, PVU_TLB_ENTRY_PGSIZE_MASK, pgsz);
+ mmio_write32_field(&entry->reg2, PVU_TLB_ENTRY_FLAG_MASK, ent->flags);
+
+ /* Do we need "DSB NSH" here to make sure all writes are finised? */
+ pvu_entry_enable(dev, tlbnum, index);
+ return 0;
+}
+
+static u32 pvu_init_device(struct pvu_dev *dev, u16 max_virtid)
+{
+ struct pvu_hw_cfg *cfg;
+ int i;
+
+ cfg = (struct pvu_hw_cfg *)dev->cfg_base;
+
+ dev->num_tlbs = mmio_read32_field(&cfg->config,
+ PVU_CONFIG_NTLB_MASK);
+ dev->num_entries = mmio_read32_field(&cfg->config,
+ PVU_CONFIG_NENT_MASK);
+
+ if (max_virtid >= dev->num_tlbs) {
+ printk("ERROR: PVU: Max virtid(%d) should be less than num_tlbs(%d)\n",
+ max_virtid, dev->num_tlbs);
+ return -EINVAL;
+ }
+
+ dev->max_virtid = max_virtid;
+ mmio_write32(&cfg->virtid_map1, 0);
+ mmio_write32_field(&cfg->virtid_map2, PVU_MAX_VIRTID_MASK, max_virtid);
+
+ for (i = 0; i < dev->num_tlbs; i++) {
+
+ pvu_tlb_disable(dev, i);
+ if (i < dev->max_virtid)
+ dev->tlb_data[i] = 0x0 | i << dev->num_entries;
+ else
+ dev->tlb_data[i] = 0x0;
+ }
+
+ /* Enable all types of exceptions */
+ mmio_write32(&cfg->exception_logging_disable, 0x0);
+ mmio_write32(&cfg->exception_logging_control, 0x0);
+ mmio_write32_field(&cfg->enable, PVU_enable_MASK, PVU_enable_EN);
+ return 0;
+}
+
+
+
+/*
+ * Split a memory region into multiple pages, where page size is one of the PVU
+ * supported size and the start address is aligned to page size
+ */
+static int pvu_entrylist_create(u64 ipa, u64 pa, u64 map_size,
+ u64 flags, struct pvu_tlb_entry *entlist, u32 num_entries)
+{
+ u8 num_sizes = ARRAY_SIZE(PVU_PAGE_SIZE_BYTES);
+ u64 page_size, vaddr, paddr;
+ s64 size, i, aligned, count;
+
+ vaddr = ipa;
+ paddr = pa;
+ size = map_size;
+ count = 0;
+
+ while (size) {
+
+ if (count == num_entries) {
+ printk("ERROR: PVU: Need more TLB entries for mapping %llx => %llx with size %llx\n",
+ ipa, pa, map_size);
+ return -EINVAL;
+ }
+
+ aligned = 0;
+
+ /* Try size from largest to smallest */
+ for (i = num_sizes - 1; i >= 0; i--) {
+
+ page_size = PVU_PAGE_SIZE_BYTES[i];
+
+ if (is_aligned(vaddr, page_size) &&
+ is_aligned(paddr, page_size) &&
+ size >= page_size) {
+
+ entlist[count].virt_addr = vaddr;
+ entlist[count].phys_addr = paddr;
+ entlist[count].size = page_size;
+ entlist[count].flags = flags;
+
+ count++;
+ vaddr += page_size;
+ paddr += page_size;
+ size -= page_size;
+ aligned = 1;
+ break;
+ }
+ }
+
+ if (!aligned) {
+ printk("ERROR: PVU: Addresses %llx %llx aren't aligned to any of the allowed page sizes\n",
+ vaddr, paddr);
+ return -EINVAL;
+ }
+ }
+ return count;
+}
+
+static void pvu_entrylist_sort(struct pvu_tlb_entry *entlist, u32 num_entries)
+{
+ struct pvu_tlb_entry temp;
+ int i, j;
+
+ for (i = 0; i < num_entries; i++) {
+ for (j = i; j < num_entries; j++) {
+
+ if (entlist[i].size < entlist[j].size) {
+ temp = entlist[i];
+ entlist[i] = entlist[j];
+ entlist[j] = temp;
+ }
+ }
+ }
+}
+
+static int pvu_iommu_program_entries(struct cell *cell, u8 virtid)
+{
+ int i, ret, tlb_next, tlbnum, idx, num_ent;
+ struct pvu_tlb_entry *ent, *cell_entries;
+ struct pvu_dev *dev;
+ u32 inst;
+
+ cell_entries = cell->arch.iommu_pvu.entries;
+ num_ent = cell->arch.iommu_pvu.ent_count;
+ if (num_ent == 0 || cell_entries == NULL)
+ return 0;
+
+ /* Program same memory mapping for all of the instances */
+ for (inst = 0; inst < pvu_count; inst++) {
+ dev = &pvu_units[inst];
+ if (pvu_tlb_is_enabled(dev, virtid))
+ continue;
+
+ tlbnum = virtid;
+ for (i = 0; i < num_ent; i++) {
+
+ ent = &cell_entries[i];
+ idx = i % dev->num_entries;
+
+ if (idx == 0 && i >= dev->num_entries) {
+ /* Find next available TLB and chain to it */
+ tlb_next = pvu_tlb_alloc(dev, virtid);
+ if (tlb_next < 0)
+ return -ENOMEM;
+ pvu_tlb_chain(dev, tlbnum, tlb_next);
+ pvu_tlb_enable(dev, tlbnum);
+ tlbnum = tlb_next;
+ }
+
+ ret = pvu_entry_write(dev, tlbnum, idx, ent);
+ if (ret)
+ return ret;
+ }
+ pvu_tlb_enable(dev, tlbnum);
+ }
+ return 0;
+}
+
+/*
+ * Actual TLB entry programming is deferred till config_commit
+ * Only populate the pvu_entries array for now
+ */
+int pvu_iommu_map_memory(struct cell *cell,
+ const struct jailhouse_memory *mem)
+{
+ struct pvu_tlb_entry *ent;
+ int size, ret;
+ u32 flags = 0;
+
+ if (pvu_count == 0)
+ return 0;
+
+ if ((mem->flags & JAILHOUSE_MEM_DMA) == 0)
+ return 0;
+
+ if (cell->arch.iommu_pvu.ent_count == MAX_PVU_ENTRIES)
+ return -ENOMEM;
+
+ if (mem->flags & JAILHOUSE_MEM_READ)
+ flags |= (LPAE_PAGE_PERM_UR | LPAE_PAGE_PERM_SR);
+ if (mem->flags & JAILHOUSE_MEM_WRITE)
+ flags |= (LPAE_PAGE_PERM_UW | LPAE_PAGE_PERM_SW);
+ if (mem->flags & JAILHOUSE_MEM_EXECUTE)
+ flags |= (LPAE_PAGE_PERM_UX | LPAE_PAGE_PERM_SX);
+
+ flags |= (LPAE_PAGE_MEM_WRITETHROUGH | LPAE_PAGE_OUTER_SHARABLE |
+ LPAE_PAGE_IS_NOALLOC | LPAE_PAGE_OS_NOALLOC);
+
+ ent = &cell->arch.iommu_pvu.entries[cell->arch.iommu_pvu.ent_count];
+ size = MAX_PVU_ENTRIES - cell->arch.iommu_pvu.ent_count;
+
+ ret = pvu_entrylist_create(mem->virt_start, mem->phys_start, mem->size,
+ flags, ent, size);
+ if (ret < 0)
+ return ret;
+
+ cell->arch.iommu_pvu.ent_count += ret;
+ return 0;
+}
+
+int pvu_iommu_unmap_memory(struct cell *cell,
+ const struct jailhouse_memory *mem)
+{
+ /*
+ * dummy unmap for now
+ * PVU does not support dynamic unmap
+ * Works well for static partitioning
+ */
+ return 0;
+}
+
+int pvu_iommu_config_commit(struct cell *cell)
+{
+ int ret, i, virtid;
+
+ if (pvu_count == 0)
+ return 0;
+
+ if (!cell) {
+ return 0;
+ }
+
+ /*
+ * Chaining the TLB entries adds extra latency to translate those
+ * addresses.
+ * Sort the entries in descending order of page sizes to reduce effects
+ * of chaining and thus reducing average translation latency
+ */
+ pvu_entrylist_sort(cell->arch.iommu_pvu.entries,
+ cell->arch.iommu_pvu.ent_count);
+
+ for_each_stream_id(virtid, cell->config, i) {
+ if (virtid == JAILHOUSE_INVALID_STREAMID)
+ break;
+ if (virtid > MAX_VIRTID)
+ continue;
+
+ ret = pvu_iommu_program_entries(cell, virtid);
+ if (ret)
+ return ret;
+ }
+
+ cell->arch.iommu_pvu.ent_count = 0;
+ return ret;
+}
+
+static int pvu_iommu_cell_init(struct cell *cell)
+{
+ struct pvu_dev *dev;
+ int i, virtid;
+
+ if (pvu_count == 0)
+ return 0;
+
+ cell->arch.iommu_pvu.ent_count = 0;
+ cell->arch.iommu_pvu.entries = page_alloc(&mem_pool, 1);
+ if (!cell->arch.iommu_pvu.entries)
+ return -ENOMEM;
+
+ dev = &pvu_units[0];
+ for_each_stream_id(virtid, cell->config, i) {
+
+ if (virtid == JAILHOUSE_INVALID_STREAMID)
+ break;
+ if (virtid > MAX_VIRTID)
+ continue;
+
+ if (pvu_tlb_is_enabled(dev, virtid))
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int pvu_iommu_flush_context(u16 virtid)
+{
+ struct pvu_dev *dev;
+ int i, tlbnum, next;
+
+ for (i = 0; i < pvu_count; i++) {
+
+ dev = &pvu_units[i];
+ tlbnum = virtid;
+
+ while (tlbnum) {
+
+ next = pvu_tlb_next(dev, tlbnum);
+ pvu_tlb_flush(dev, tlbnum);
+ tlbnum = next;
+ }
+ }
+ return 0;
+}
+
+static void pvu_iommu_cell_exit(struct cell *cell)
+{
+ int i, virtid;
+
+ if (pvu_count == 0)
+ return;
+
+ for_each_stream_id(virtid, cell->config, i) {
+
+ if (virtid == JAILHOUSE_INVALID_STREAMID)
+ break;
+ if (virtid > MAX_VIRTID)
+ continue;
+
+ pvu_iommu_flush_context(virtid);
+ }
+
+ cell->arch.iommu_pvu.ent_count = 0;
+ page_free(&mem_pool, cell->arch.iommu_pvu.entries, 1);
+ cell->arch.iommu_pvu.entries = NULL;
+}
+
+static int pvu_iommu_init(void)
+{
+ struct jailhouse_iommu *iommu;
+ struct pvu_dev *dev;
+ int i, ret;
+
+ iommu = &system_config->platform_info.arm.iommu_units[0];
+ for (i = 0; i < iommu_count_units(); iommu++, i++) {
+
+ if (iommu->type != JAILHOUSE_IOMMU_PVU)
+ continue;
+
+ dev = &pvu_units[pvu_count];
+ dev->cfg_base = paging_map_device(iommu->base,
+ iommu->size);
+ dev->tlb_base = paging_map_device(iommu->tipvu_tlb_base,
+ iommu->tipvu_tlb_size);
+
+ ret = pvu_init_device(dev, MAX_VIRTID);
+ if (ret)
+ return ret;
+
+ pvu_count++;
+ }
+
+ return pvu_iommu_cell_init(&root_cell);
+}
+
+DEFINE_UNIT_SHUTDOWN_STUB(pvu_iommu);
+DEFINE_UNIT_MMIO_COUNT_REGIONS_STUB(pvu_iommu);
+DEFINE_UNIT(pvu_iommu, "PVU IOMMU");
diff --git a/hypervisor/arch/arm64/ti-pvu_priv.h b/hypervisor/arch/arm64/ti-pvu_priv.h
new file mode 100644
index 00000000..acba338b
--- /dev/null
+++ b/hypervisor/arch/arm64/ti-pvu_priv.h
@@ -0,0 +1,141 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * TI PVU IOMMU unit private headers
+ *
+ * Authors:
+ * Nikhil Devshatwar <nikh...@ti.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef __TI_PVU_PRIV_H__
+#define __TI_PVU_PRIV_H__
+
+#define PVU_NUM_TLBS 64
+#define PVU_NUM_ENTRIES 8
+
+#define PVU_CONFIG_NTLB_MASK (0xff)
+#define PVU_CONFIG_NENT_MASK (0xf << 16)
+
+#define PVU_MAX_VIRTID_MASK (0xfff)
+
+#define PVU_enable_EN (0x1)
+#define PVU_enable_DIS (0x0)
+#define PVU_enable_MASK (0x1)
+
+struct pvu_hw_cfg {
+ u32 pid;
+ u32 config;
+ u8 resv_16[8];
+ u32 enable;
+ u32 virtid_map1;
+ u32 virtid_map2;
+ u8 resv_48[20];
+ u32 exception_logging_disable;
+ u8 resv_260[208];
+ u32 destination_id;
+ u8 resv_288[24];
+ u32 exception_logging_control;
+ u32 exception_logging_header0;
+ u32 exception_logging_header1;
+ u32 exception_logging_data0;
+ u32 exception_logging_data1;
+ u32 exception_logging_data2;
+ u32 exception_logging_data3;
+ u8 resv_320[4];
+ u32 exception_pend_set;
+ u32 exception_pend_clear;
+ u32 exception_ENABLE_set;
+ u32 exception_ENABLE_clear;
+ u32 eoi_reg;
+};
+
+#define PVU_TLB_ENTRY_VALID (2)
+#define PVU_TLB_ENTRY_INVALID (0)
+#define PVU_TLB_ENTRY_MODE_MASK (0x3 << 30)
+#define PVU_TLB_ENTRY_FLAG_MASK (0xff7f)
+#define PVU_TLB_ENTRY_PGSIZE_MASK (0xf << 16)
+
+#define PVU_ENTRY_INVALID (0 << 30)
+#define PVU_ENTRY_VALID (2 << 30)
+
+#define LPAE_PAGE_SZ_4K 0
+#define LPAE_PAGE_SZ_16K 1
+#define LPAE_PAGE_SZ_64K 2
+#define LPAE_PAGE_SZ_2M 3
+#define LPAE_PAGE_SZ_32M 4
+#define LPAE_PAGE_SZ_512M 5
+#define LPAE_PAGE_SZ_1G 6
+#define LPAE_PAGE_SZ_16G 7
+
+#define LPAE_PAGE_PERM_UR (1 << 15)
+#define LPAE_PAGE_PERM_UW (1 << 14)
+#define LPAE_PAGE_PERM_UX (1 << 13)
+#define LPAE_PAGE_PERM_SR (1 << 12)
+#define LPAE_PAGE_PERM_SW (1 << 11)
+#define LPAE_PAGE_PERM_SX (1 << 10)
+
+#define LPAE_PAGE_MEM_DEVICE (0 << 8)
+#define LPAE_PAGE_MEM_WRITEBACK (1 << 8)
+#define LPAE_PAGE_MEM_WRITETHROUGH (2 << 8)
+
+#define LPAE_PAGE_PREFETCH (1 << 6)
+#define LPAE_PAGE_INNER_SHARABLE (1 << 5)
+#define LPAE_PAGE_OUTER_SHARABLE (1 << 4)
+
+#define LPAE_PAGE_IS_NOALLOC (0 << 2)
+#define LPAE_PAGE_IS_WR_ALLOC (1 << 2)
+#define LPAE_PAGE_IS_RD_ALLOC (2 << 2)
+#define LPAE_PAGE_IS_RDWR_ALLOC (3 << 2)
+
+#define LPAE_PAGE_OS_NOALLOC (0 << 0)
+#define LPAE_PAGE_OS_WR_ALLOC (1 << 0)
+#define LPAE_PAGE_OS_RD_ALLOC (2 << 0)
+#define LPAE_PAGE_OS_RDWR_ALLOC (3 << 0)
+
+struct pvu_hw_tlb_entry {
+ u32 reg0;
+ u32 reg1;
+ u32 reg2;
+ u32 reg3;
+ u32 reg4;
+ u32 reg5;
+ u32 reg6;
+ u32 reg7;
+};
+
+#define PVU_TLB_EN_MASK (1 << 31)
+#define PVU_TLB_LOG_DIS_MASK (1 << 30)
+#define PVU_TLB_FAULT_MASK (1 << 29)
+#define PVU_TLB_CHAIN_MASK (0xfff)
+
+struct pvu_hw_tlb {
+ u32 chain;
+ u8 resv_32[28];
+ struct pvu_hw_tlb_entry entry[8];
+ u8 resv_4096[3808];
+};
+
+struct pvu_tlb_entry {
+ u64 virt_addr;
+ u64 phys_addr;
+ u64 size;
+ u64 flags;
+};
+
+struct pvu_dev {
+ u32 *cfg_base;
+ u32 *tlb_base;
+
+ u32 num_tlbs;
+ u32 num_entries;
+ u16 max_virtid;
+
+ u16 tlb_data[PVU_NUM_TLBS];
+};
+
+#endif /* __TI_PVU_PRIV_H__ */
diff --git a/include/jailhouse/cell-config.h b/include/jailhouse/cell-config.h
index d435b9f7..9bb84492 100644
--- a/include/jailhouse/cell-config.h
+++ b/include/jailhouse/cell-config.h
@@ -203,6 +203,7 @@ struct jailhouse_pci_capability {
#define JAILHOUSE_IOMMU_AMD 1
#define JAILHOUSE_IOMMU_INTEL 2
#define JAILHOUSE_IOMMU_SMMUV3 3
+#define JAILHOUSE_IOMMU_PVU 4

struct jailhouse_iommu {
__u32 type;
@@ -213,6 +214,9 @@ struct jailhouse_iommu {
__u8 amd_base_cap;
__u8 amd_msi_cap;
__u32 amd_features;
+
+ __u64 tipvu_tlb_base;
+ __u32 tipvu_tlb_size;
} __attribute__((packed));

struct jailhouse_pio {
--
2.17.1

Nikhil Devshatwar

unread,
Dec 30, 2019, 8:25:08 AM12/30/19
to jailho...@googlegroups.com, lokes...@ti.com, jan.k...@siemens.com, Nikhil Devshatwar
arm_smmuv3_cell_init and arm_smmuv3_cell_exit calls iterate over all
iommu_units to process the ones with SMMUV3 type.

After the loop, it unconditionally issues commands with first element
of smmu. This causes Unhandled HYP exception while enabling jailhouse.

Fix this by issuing the sync command only if the iommu is SMMUV3 type.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
---
hypervisor/arch/arm64/smmu-v3.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/hypervisor/arch/arm64/smmu-v3.c b/hypervisor/arch/arm64/smmu-v3.c
index dd33bda2..3889d32b 100644
--- a/hypervisor/arch/arm64/smmu-v3.c
+++ b/hypervisor/arch/arm64/smmu-v3.c
@@ -1057,12 +1057,12 @@ static int arm_smmuv3_cell_init(struct cell *cell)
if (ret)
return ret;
}
- }

- cmd.opcode = CMDQ_OP_TLBI_S12_VMALL;
- cmd.tlbi.vmid = cell->config->id;
- arm_smmu_cmdq_issue_cmd(smmu, &cmd);
- arm_smmu_cmdq_issue_sync(smmu);
+ cmd.opcode = CMDQ_OP_TLBI_S12_VMALL;
+ cmd.tlbi.vmid = cell->config->id;
+ arm_smmu_cmdq_issue_cmd(&smmu[i], &cmd);
+ arm_smmu_cmdq_issue_sync(&smmu[i]);
+ }

return 0;
}
@@ -1084,12 +1084,12 @@ static void arm_smmuv3_cell_exit(struct cell *cell)
for_each_stream_id(sid, cell->config, j) {
arm_smmu_uninit_ste(&smmu[i], sid, cell->config->id);
}
- }

- cmd.opcode = CMDQ_OP_TLBI_S12_VMALL;
- cmd.tlbi.vmid = cell->config->id;
- arm_smmu_cmdq_issue_cmd(smmu, &cmd);
- arm_smmu_cmdq_issue_sync(smmu);
+ cmd.opcode = CMDQ_OP_TLBI_S12_VMALL;
+ cmd.tlbi.vmid = cell->config->id;
+ arm_smmu_cmdq_issue_cmd(&smmu[i], &cmd);
+ arm_smmu_cmdq_issue_sync(&smmu[i]);
+ }
}

static int arm_smmuv3_init(void)
--
2.17.1

Nikhil Devshatwar

unread,
Dec 30, 2019, 8:25:09 AM12/30/19
to jailho...@googlegroups.com, lokes...@ti.com, jan.k...@siemens.com, Nikhil Devshatwar
J721e device has 3 instance of PVU which can be used as IOMMU.
Each PVU has a config region and a TLB region where the memory
mapping information is stored.
Describe these as part of the root cell's platform_data.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
---
configs/arm64/k3-j721e-evm.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)

diff --git a/configs/arm64/k3-j721e-evm.c b/configs/arm64/k3-j721e-evm.c
index ff6dcff4..8fb4773b 100644
--- a/configs/arm64/k3-j721e-evm.c
+++ b/configs/arm64/k3-j721e-evm.c
@@ -50,6 +50,30 @@ struct {
.gicr_base = 0x01900000,
.maintenance_irq = 25,
},
+ .arm.iommu_units= {
+ {
+ .type = JAILHOUSE_IOMMU_PVU,
+ .base = 0x30f80000,
+ .size = 0x1000,
+ .tipvu_tlb_base = 0x36000000,
+ .tipvu_tlb_size = 0x40000,
+ },
+ {
+ .type = JAILHOUSE_IOMMU_PVU,
+ .base = 0x30f81000,
+ .size = 0x1000,
+ .tipvu_tlb_base = 0x36040000,
+ .tipvu_tlb_size = 0x40000,
+ },
+ {
+ .type = JAILHOUSE_IOMMU_PVU,
+ .base = 0x30f83000,
+ .size = 0x1000,
+ .tipvu_tlb_base = 0x360c0000,
+ .tipvu_tlb_size = 0x40000,
+ },
+ },
+
},
.root_cell = {
.name = "k3-j721e-evm",
--
2.17.1

Nikhil Devshatwar

unread,
Dec 30, 2019, 8:25:12 AM12/30/19
to jailho...@googlegroups.com, lokes...@ti.com, jan.k...@siemens.com, Nikhil Devshatwar
Add stream_ids for peripherals which are behind IOMMU instances.
PVU and SMMU-V3 sets up memory mapping for all of these contexts
for correct 2nd stage translation.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
---
configs/arm64/k3-j721e-evm-linux-demo.c | 7 +++++++
configs/arm64/k3-j721e-evm.c | 14 ++++++++++++++
2 files changed, 21 insertions(+)

diff --git a/configs/arm64/k3-j721e-evm-linux-demo.c b/configs/arm64/k3-j721e-evm-linux-demo.c
index 55938b85..e9ef0121 100644
--- a/configs/arm64/k3-j721e-evm-linux-demo.c
+++ b/configs/arm64/k3-j721e-evm-linux-demo.c
@@ -27,6 +27,7 @@ struct {
struct jailhouse_memory mem_regions[22];
struct jailhouse_irqchip irqchips[4];
struct jailhouse_pci_device pci_devices[1];
+ __u32 stream_ids[2];
} __attribute__((packed)) config = {
.cell = {
.signature = JAILHOUSE_CELL_DESC_SIGNATURE,
@@ -38,6 +39,7 @@ struct {
.num_memory_regions = ARRAY_SIZE(config.mem_regions),
.num_irqchips = ARRAY_SIZE(config.irqchips),
.num_pci_devices = ARRAY_SIZE(config.pci_devices),
+ .num_stream_ids = ARRAY_SIZE(config.stream_ids),
.cpu_reset_address = 0x0,
.vpci_irq_base = 195 - 32,
.console = {
@@ -273,4 +275,9 @@ struct {
.shmem_protocol = JAILHOUSE_SHMEM_PROTO_VETH,
},
},
+
+ .stream_ids = {
+ /* Non PCIe peripherals */
+ 0x0003, 0xf003,
+ },
};
diff --git a/configs/arm64/k3-j721e-evm.c b/configs/arm64/k3-j721e-evm.c
index 8fb4773b..688d58f4 100644
--- a/configs/arm64/k3-j721e-evm.c
+++ b/configs/arm64/k3-j721e-evm.c
@@ -22,6 +22,7 @@ struct {
struct jailhouse_memory mem_regions[33];
struct jailhouse_irqchip irqchips[6];
struct jailhouse_pci_device pci_devices[1];
+ __u32 stream_ids[30];
} __attribute__((packed)) config = {
.header = {
.signature = JAILHOUSE_SYSTEM_SIGNATURE,
@@ -82,6 +83,7 @@ struct {
.num_memory_regions = ARRAY_SIZE(config.mem_regions),
.num_irqchips = ARRAY_SIZE(config.irqchips),
.num_pci_devices = ARRAY_SIZE(config.pci_devices),
+ .num_stream_ids = ARRAY_SIZE(config.stream_ids),
.vpci_irq_base = 191 - 32,
},
},
@@ -384,4 +386,16 @@ struct {
.shmem_protocol = JAILHOUSE_SHMEM_PROTO_VETH,
},
},
+
+ .stream_ids = {
+ /* Non PCIe peripherals */
+ 0x0002, 0xf002,
+ /* PCI1 */
+ 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107,
+ 0x0108, 0x0109, 0x010a, 0x010b, 0x010c, 0x010d, 0x010e, 0x010f,
+ /* PCI2 */
+ 0x4100, 0x4101, 0x4102, 0x4103, 0x4104, 0x4105,
+ /* PCI3 */
+ 0x8100, 0x8101, 0x8102, 0x8103, 0x8104, 0x8105,
+ },
};
--
2.17.1

Jan Kiszka

unread,
Jan 3, 2020, 11:52:38 AM1/3/20
to Nikhil Devshatwar, jailho...@googlegroups.com, lokes...@ti.com
Please make sure a "From:" line is at the top so that I don't have to
add that manually. git will add that line when you set a different
--from when calling format-patch.
Re-inclusion protection should only be added when actually needed.

> +
> +#include <jailhouse/config.h>
> +
> +#ifdef CONFIG_IOMMU_TI_PVU

Can we try to model this without a compile-time switch?
Single-user header - let's fold this in.

> +
> +#define MAX_PVU_ENTRIES (PAGE_SIZE / sizeof (struct pvu_tlb_entry))
> +#define MAX_VIRTID 7
> +
> +static struct pvu_dev pvu_units[JAILHOUSE_MAX_IOMMU_UNITS];
> +static unsigned int pvu_count;
> +
> +static const u64 PVU_PAGE_SIZE_BYTES[] = {

This is not a macro or define, so let's decapitalize its name.

> + [LPAE_PAGE_SZ_4K] = 4 * 1024,
> + [LPAE_PAGE_SZ_16K] = 16 * 1024,
> + [LPAE_PAGE_SZ_64K] = 64 * 1024,
> + [LPAE_PAGE_SZ_2M] = 2 * 1024 * 1024,
> + [LPAE_PAGE_SZ_32M] = 32 * 1024 * 1024,
> + [LPAE_PAGE_SZ_512M] = 512 * 1024 * 1024,
> + [LPAE_PAGE_SZ_1G] = 1 * 1024 * 1024 * 1024,
> + [LPAE_PAGE_SZ_16G] = 16ULL * 1024 * 1024 * 1024,

Is there another use case the LPAE_PAGE_SZ constants in sight?
Otherwise, I would simply fill the array in the right order with the
actual values.
Let's use an appropriate unsigned type here, e.g. unsigned int.
Again, please use unsigned where there are only unsigned indexes involved.

> +
> + cfg = (struct pvu_hw_cfg *)dev->cfg_base;
> +
> + dev->num_tlbs = mmio_read32_field(&cfg->config,
> + PVU_CONFIG_NTLB_MASK);

No need to wrap this line.

> + dev->num_entries = mmio_read32_field(&cfg->config,
> + PVU_CONFIG_NENT_MASK);

Nicer this way:

dev->num_entries =
mmio_read32_field(&cfg->config, PVU_CONFIG_NENT_MASK);
i is not really "integer". Choose a better name.

count neither needs to be signed nor that wide.

> +
> + vaddr = ipa;
> + paddr = pa;
> + size = map_size;
> + count = 0;
> +
> + while (size) {
> +
> + if (count == num_entries) {
> + printk("ERROR: PVU: Need more TLB entries for mapping %llx => %llx with size %llx\n",
> + ipa, pa, map_size);
> + return -EINVAL;
> + }
> +
> + aligned = 0;
> +
> + /* Try size from largest to smallest */
> + for (i = num_sizes - 1; i >= 0; i--) {
> +
> + page_size = PVU_PAGE_SIZE_BYTES[i];
> +
> + if (is_aligned(vaddr, page_size) &&
> + is_aligned(paddr, page_size) &&
> + size >= page_size) {

This compares signed with unsigned. If size can become negative along
this match, I would recommend defining page_size also the array it come
from signed as well.

> +
> + entlist[count].virt_addr = vaddr;
> + entlist[count].phys_addr = paddr;
> + entlist[count].size = page_size;
> + entlist[count].flags = flags;
> +
> + count++;
> + vaddr += page_size;
> + paddr += page_size;
> + size -= page_size;
> + aligned = 1;
> + break;
> + }
> + }
> +
> + if (!aligned) {

You can save this extra variable: if (i < 0)

> + printk("ERROR: PVU: Addresses %llx %llx aren't aligned to any of the allowed page sizes\n",

Let's break up this long line, even if that splits the message.

> + vaddr, paddr);
> + return -EINVAL;
> + }
> + }
> + return count;
> +}
> +
> +static void pvu_entrylist_sort(struct pvu_tlb_entry *entlist, u32 num_entries)
> +{
> + struct pvu_tlb_entry temp;
> + int i, j;

Same remark regarding signedness.

> +
> + for (i = 0; i < num_entries; i++) {
> + for (j = i; j < num_entries; j++) {
> +
> + if (entlist[i].size < entlist[j].size) {
> + temp = entlist[i];
> + entlist[i] = entlist[j];
> + entlist[j] = temp;
> + }
> + }
> + }
> +}
> +
> +static int pvu_iommu_program_entries(struct cell *cell, u8 virtid)
> +{
> + int i, ret, tlb_next, tlbnum, idx, num_ent;

Also check required signedness here as well, please.
Wrong indention.
Wrong indention.

> + if (ret < 0)
> + return ret;
> +
> + cell->arch.iommu_pvu.ent_count += ret;
> + return 0;
> +}
> +
> +int pvu_iommu_unmap_memory(struct cell *cell,
> + const struct jailhouse_memory *mem)

also here

> +{
> + /*
> + * dummy unmap for now
> + * PVU does not support dynamic unmap
> + * Works well for static partitioning

Huh!? But this breaks cell create/destroy cycles, without any user
notice, no? And will root cell devices keep access to inmate memory that
is carved out during cell creation?

Is that a hardware limitation?

Looks like a blocker...

> + */
> + return 0;
> +}
> +
> +int pvu_iommu_config_commit(struct cell *cell)
> +{
> + int ret, i, virtid;

signed/unsigned

> +
> + if (pvu_count == 0)
> + return 0;
> +
> + if (!cell) {
> + return 0;
> + }

No need for brackets. In fact:

if (pvu_count == 0 || !cell)
return 0;

> +
> + /*
signed/unsigned
signed/unsigned

> +
> + for (i = 0; i < pvu_count; i++) {
> +
> + dev = &pvu_units[i];
> + tlbnum = virtid;
> +
> + while (tlbnum) {
> +
> + next = pvu_tlb_next(dev, tlbnum);
> + pvu_tlb_flush(dev, tlbnum);
> + tlbnum = next;
> + }
> + }
> + return 0;
> +}
> +
> +static void pvu_iommu_cell_exit(struct cell *cell)
> +{
> + int i, virtid;

signed/unsigned

> +
> + if (pvu_count == 0)
> + return;
> +
> + for_each_stream_id(virtid, cell->config, i) {
> +
> + if (virtid == JAILHOUSE_INVALID_STREAMID)
> + break;
> + if (virtid > MAX_VIRTID)
> + continue;
> +
> + pvu_iommu_flush_context(virtid);
> + }
> +
> + cell->arch.iommu_pvu.ent_count = 0;
> + page_free(&mem_pool, cell->arch.iommu_pvu.entries, 1);
> + cell->arch.iommu_pvu.entries = NULL;
> +}
> +
> +static int pvu_iommu_init(void)
> +{
> + struct jailhouse_iommu *iommu;
> + struct pvu_dev *dev;
> + int i, ret;

signed/unsigned
These two are unused - accidentally?

> +#define LPAE_PAGE_MEM_WRITETHROUGH (2 << 8) > +
> +#define LPAE_PAGE_PREFETCH (1 << 6)
> +#define LPAE_PAGE_INNER_SHARABLE (1 << 5)

These two as well. I would recommend adding only used constants.

> +#define LPAE_PAGE_OUTER_SHARABLE (1 << 4)
> +
> +#define LPAE_PAGE_IS_NOALLOC (0 << 2)
> +#define LPAE_PAGE_IS_WR_ALLOC (1 << 2)
> +#define LPAE_PAGE_IS_RD_ALLOC (2 << 2)
> +#define LPAE_PAGE_IS_RDWR_ALLOC (3 << 2)
> +
> +#define LPAE_PAGE_OS_NOALLOC (0 << 0)
> +#define LPAE_PAGE_OS_WR_ALLOC (1 << 0)
> +#define LPAE_PAGE_OS_RD_ALLOC (2 << 0)
> +#define LPAE_PAGE_OS_RDWR_ALLOC (3 << 0)

Here are some unused consts as well.

> +
> +struct pvu_hw_tlb_entry {
> + u32 reg0;
> + u32 reg1;
> + u32 reg2;
> + u32 reg3;
> + u32 reg4;
> + u32 reg5;
> + u32 reg6;
> + u32 reg7;

Do these regs really have no names? Can we use an array then?
Time to stick the amd fields in their own sub-struct, as well as the
tipvu ones, and then put both struct into a union. Analogously to
jailhouse_system.platform_info.

> } __attribute__((packed));
>
> struct jailhouse_pio {
>

Jan

Jan Kiszka

unread,
Jan 3, 2020, 11:57:49 AM1/3/20
to Nikhil Devshatwar, jailho...@googlegroups.com, lokes...@ti.com
On 30.12.19 14:24, 'Nikhil Devshatwar' via Jailhouse wrote:
> arm_smmuv3_cell_init and arm_smmuv3_cell_exit calls iterate over all
> iommu_units to process the ones with SMMUV3 type.
>
> After the loop, it unconditionally issues commands with first element
> of smmu. This causes Unhandled HYP exception while enabling jailhouse.

Even worse, it would fail to issue those commands on the second and
following units, no? This should be reflected in the subject and commit
message as it seems to fix that case as well.

>
> Fix this by issuing the sync command only if the iommu is SMMUV3 type.
>

Looks like this should come before patch 1 then.
Jan

Nikhil Devshatwar

unread,
Jan 6, 2020, 3:13:01 AM1/6/20
to Jan Kiszka, jailho...@googlegroups.com, lokes...@ti.com


On 03/01/20 10:22 pm, Jan Kiszka wrote:
Please make sure a "From:" line is at the top so that I don't have to
add that manually. git will add that line when you set a different
--from when calling format-patch.
Sure, This time i used git format-patch ..... -4 to format, annotate and send directly.
will check why the from: line is missing
Got it

+
+#include <jailhouse/config.h>
+
+#ifdef CONFIG_IOMMU_TI_PVU

Can we try to model this without a compile-time switch?

Only reason I put a config is because this applies only to one platform.
Will remove this in v2
alright


+
+#define MAX_PVU_ENTRIES        (PAGE_SIZE / sizeof (struct pvu_tlb_entry))
+#define MAX_VIRTID          7
+
+static struct pvu_dev pvu_units[JAILHOUSE_MAX_IOMMU_UNITS];
+static unsigned int pvu_count;
+
+static const u64 PVU_PAGE_SIZE_BYTES[] = {

This is not a macro or define, so let's decapitalize its name.
will do


+    [LPAE_PAGE_SZ_4K]        =   4 * 1024,
+    [LPAE_PAGE_SZ_16K]        =  16 * 1024,
+    [LPAE_PAGE_SZ_64K]        =  64 * 1024,
+    [LPAE_PAGE_SZ_2M]        =   2 * 1024 * 1024,
+    [LPAE_PAGE_SZ_32M]        =  32 * 1024 * 1024,
+    [LPAE_PAGE_SZ_512M]        = 512 * 1024 * 1024,
+    [LPAE_PAGE_SZ_1G]        =   1 * 1024 * 1024 * 1024,
+    [LPAE_PAGE_SZ_16G]        =  16ULL * 1024 * 1024 * 1024,

Is there another use case the LPAE_PAGE_SZ constants in sight?
Otherwise, I would simply fill the array in the right order with the
actual values.

will do
will review signedness of all variables and fix it


+
+    for (i = dev->max_virtid + 1; i < dev->num_tlbs; i++) {
+        if (dev->tlb_data[i] == 0) {
+            dev->tlb_data[i] = virtid << dev->num_entries;
+            return i;
+        }


[snip]


+{
+    /*
+     * dummy unmap for now
+     * PVU does not support dynamic unmap
+     * Works well for static partitioning

Huh!? But this breaks cell create/destroy cycles, without any user
notice, no? And will root cell devices keep access to inmate memory that
is carved out during cell creation?

Is that a hardware limitation?

Looks like a blocker...
Yes, this is a hardware limitation. I it designed for static partitioning.
Although, I made sure to not break memory isolatio with the following workaround:

When booting a root cell for Jailhouse, you would typically carveout  memory for the
inmate cell. I have defined the cell configs such that, in the root cell config, RAM region for inmate is
NOT marked with MEM_DMA, this way it never gets mapped in PVU.

When creating cell, root cell maps the inmate RAM loadable region, here that memory is not
mapped in IO space.
---> Limitation of this is that you cannot DMA copy the images in the loadable sections,
   which we are not doing anyways

When destroying the cell, Jailhouse should map the memory back to the root cell.
Here, again, the inmate RAM region gets ignored in IO mapping because of lacking flag MEM_DMA

cell_create  and cell_destroy work in regression, tested successfully.




+     */
+    return 0;
+}
+
+int pvu_iommu_config_commit(struct cell *cell)
+{

[snip]


+#define LPAE_PAGE_SZ_2M            3
+#define LPAE_PAGE_SZ_32M        4
+#define LPAE_PAGE_SZ_512M        5
+#define LPAE_PAGE_SZ_1G            6
+#define LPAE_PAGE_SZ_16G        7
+
+#define LPAE_PAGE_PERM_UR        (1 << 15)
+#define LPAE_PAGE_PERM_UW        (1 << 14)
+#define LPAE_PAGE_PERM_UX        (1 << 13)
+#define LPAE_PAGE_PERM_SR        (1 << 12)
+#define LPAE_PAGE_PERM_SW        (1 << 11)
+#define LPAE_PAGE_PERM_SX        (1 << 10)
+
+#define LPAE_PAGE_MEM_DEVICE        (0 << 8)
+#define LPAE_PAGE_MEM_WRITEBACK        (1 << 8)

These two are unused - accidentally?

Unused, will remove

+#define LPAE_PAGE_MEM_WRITETHROUGH    (2 << 8) > +
+#define LPAE_PAGE_PREFETCH        (1 << 6)
+#define LPAE_PAGE_INNER_SHARABLE    (1 << 5)

These two as well. I would recommend adding only used constants.
will do


+#define LPAE_PAGE_OUTER_SHARABLE    (1 << 4)
+
+#define LPAE_PAGE_IS_NOALLOC        (0 << 2)
+#define LPAE_PAGE_IS_WR_ALLOC        (1 << 2)
+#define LPAE_PAGE_IS_RD_ALLOC        (2 << 2)
+#define LPAE_PAGE_IS_RDWR_ALLOC        (3 << 2)
+
+#define LPAE_PAGE_OS_NOALLOC        (0 << 0)
+#define LPAE_PAGE_OS_WR_ALLOC        (1 << 0)
+#define LPAE_PAGE_OS_RD_ALLOC        (2 << 0)
+#define LPAE_PAGE_OS_RDWR_ALLOC        (3 << 0)

Here are some unused consts as well.
will remove


+
+struct pvu_hw_tlb_entry {
+    u32        reg0;
+    u32        reg1;
+    u32        reg2;
+    u32        reg3;
+    u32        reg4;
+    u32        reg5;
+    u32        reg6;
+    u32        reg7;

Do these regs really have no names? Can we use an array then?
Yes, the TRM actually has these names for the TLB entry
Agreed, I will do that.

  } __attribute__((packed));

  struct jailhouse_pio {


Jan

Thanks for the review
Nikhil Devshatwar

Jan Kiszka

unread,
Jan 6, 2020, 4:22:40 AM1/6/20
to Nikhil Devshatwar, jailho...@googlegroups.com, lokes...@ti.com
On 06.01.20 09:12, Nikhil Devshatwar wrote:
>>> +{
>>> +    /*
>>> +     * dummy unmap for now
>>> +     * PVU does not support dynamic unmap
>>> +     * Works well for static partitioning
>>
>> Huh!? But this breaks cell create/destroy cycles, without any user
>> notice, no? And will root cell devices keep access to inmate memory that
>> is carved out during cell creation?
>>
>> Is that a hardware limitation?
>>
>> Looks like a blocker...
> Yes, this is a hardware limitation. I it designed for static partitioning.

IOW, we can also not change the configuration by destroying and
recreating non-root cells with different memory layouts?

> Although, I made sure to not break memory isolatio with the following
> workaround:
>
> When booting a root cell for Jailhouse, you would typically carveout
> memory for the
> inmate cell. I have defined the cell configs such that, in the root cell
> config, RAM region for inmate is
> NOT marked with MEM_DMA, this way it never gets mapped in PVU.
>
> When creating cell, root cell maps the inmate RAM loadable region, here
> that memory is not
> mapped in IO space.
> ---> Limitation of this is that you cannot DMA copy the images in the
> loadable sections,
>    which we are not doing anyways
>
> When destroying the cell, Jailhouse should map the memory back to the
> root cell.
> Here, again, the inmate RAM region gets ignored in IO mapping because of
> lacking flag MEM_DMA
>
> cell_create  and cell_destroy work in regression, tested successfully.
>

Please add at least a test to the unmap function that warns when a
config is violating the hardware constraints. It's still not clear to
me, though, how well that goes with changing inmate cell configs.

Jan

Nikhil Devshatwar

unread,
Jan 6, 2020, 5:11:36 AM1/6/20
to Jan Kiszka, jailho...@googlegroups.com, lokes...@ti.com


On 06/01/20 2:52 pm, Jan Kiszka wrote:
On 06.01.20 09:12, Nikhil Devshatwar wrote:
+{
+    /*
+     * dummy unmap for now
+     * PVU does not support dynamic unmap
+     * Works well for static partitioning

Huh!? But this breaks cell create/destroy cycles, without any user
notice, no? And will root cell devices keep access to inmate memory that
is carved out during cell creation?

Is that a hardware limitation?

Looks like a blocker...
Yes, this is a hardware limitation. I it designed for static partitioning.

IOW, we can also not change the configuration by destroying and
recreating non-root cells with different memory layouts?


For now, we have gic-demo, uart-demo and linux-demo
You can interchangeably create/destroy cells in any order.


Although, I made sure to not break memory isolatio with the following
workaround:

When booting a root cell for Jailhouse, you would typically carveout
memory for the
inmate cell. I have defined the cell configs such that, in the root cell
config, RAM region for inmate is
NOT marked with MEM_DMA, this way it never gets mapped in PVU.

When creating cell, root cell maps the inmate RAM loadable region, here
that memory is not
mapped in IO space.
---> Limitation of this is that you cannot DMA copy the images in the
loadable sections,
    which we are not doing anyways

When destroying the cell, Jailhouse should map the memory back to the
root cell.
Here, again, the inmate RAM region gets ignored in IO mapping because of
lacking flag MEM_DMA

cell_create  and cell_destroy work in regression, tested successfully.


Please add at least a test to the unmap function that warns when a
config is violating the hardware constraints. It's still not clear to
me, though, how well that goes with changing inmate cell configs.

Let me explain via the code

root cell config for j721e-evm:

1        /* RAM - first bank*/ {
            .phys_start = 0x80000000,
            .virt_start = 0x80000000,
            .size = 0x80000000,
            .flags = JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE |
                JAILHOUSE_MEM_EXECUTE | JAILHOUSE_MEM_DMA |
                JAILHOUSE_MEM_LOADABLE,
        },
2        /* RAM - second bank */ {
            .phys_start = 0x880000000,
            .virt_start = 0x880000000,
            .size = 0x1fa00000,
            .flags = JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE |
                JAILHOUSE_MEM_EXECUTE | JAILHOUSE_MEM_DMA |
                JAILHOUSE_MEM_LOADABLE,
        },
3        /* RAM - reserved for ivshmem and baremetal apps */ {
            .phys_start = 0x89fe00000,
            .virt_start = 0x89fe00000,
            .size = 0x200000,
            .flags = JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE |
                JAILHOUSE_MEM_EXECUTE | JAILHOUSE_MEM_LOADABLE,
        },
4        /* RAM - reserved for inmate */ {
            .phys_start = 0x8a0000000,
            .virt_start = 0x8a0000000,
            .size = 0x60000000,
            .flags = JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE |
                JAILHOUSE_MEM_EXECUTE | JAILHOUSE_MEM_LOADABLE,
        },

Here, note that all of 1,2,34 gets mapped in CPU MMU, but only 1,2 gets mapped in PVU

inmate cell config for j721e-evm-linux-demo:

5        /* RAM. */ {
            .phys_start = 0x8a0000000,
            .virt_start = 0x8a0000000,
            .size = 0x60000000,
            .flags = JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE |
                JAILHOUSE_MEM_EXECUTE | JAILHOUSE_MEM_DMA |
                JAILHOUSE_MEM_LOADABLE,
        },


* When enabling jailhouse:
    In ideal world, all of the 1,2,3,4(same as 5) should be mapped in CPU MMU and PVU
    With current config, only 1,2,3 is mapped. root cell kernel continues without any trouble
        Since the inmate memory is reserved, no driver attempts DMA to that region, no faults seen

* When creating inmate cell:
    In ideal world, the IO mapping from PVU should be removed from root cell stream ID and to be added in the inmate cell stream ID
    With current config, unmap return 0 because nothing was ever mapped

* When loading images (SET_LOADABLE):
    In ideal world, loadable regions should be mapped in the PVU map for root cell streamID
    Since the MEM_DMA is missing, PVU unit skips this chunk and never maps to root cell
    If you DMA copy the images to the root cell view of inmate loadable memory, there will be faults
    We do not do this currently (I believe we CPU copy the images) Correct me if I am wrong here

* When starting cell
    Again, ideally the mapping should be removed from root cell and added to inmate cell
    unmap returns 0 becasuse it was never mapped
    pvu_iommu_program_entries called in inmate 2nd time does nothing if the pvu_tlb_is_enabled returns true

Nowhere, PVU unit reprograms the memory map to add or remove entires. Because it doesn't have to do.

Sure, there are some limitations of this appoach:
* DMA copy of boot images not supported
* Root cell memory map should be split to mark all inmate used RAM regions without MEM_DMA flag.

Regards,
Nikhil D

Jan Kiszka

unread,
Jan 6, 2020, 5:44:47 AM1/6/20
to Nikhil Devshatwar, jailho...@googlegroups.com, lokes...@ti.com
On 06.01.20 11:10, Nikhil Devshatwar wrote:
>
>
> On 06/01/20 2:52 pm, Jan Kiszka wrote:
>> On 06.01.20 09:12, Nikhil Devshatwar wrote:
>>>>> +{
>>>>> +    /*
>>>>> +     * dummy unmap for now
>>>>> +     * PVU does not support dynamic unmap
>>>>> +     * Works well for static partitioning
>>>>
>>>> Huh!? But this breaks cell create/destroy cycles, without any user
>>>> notice, no? And will root cell devices keep access to inmate memory
>>>> that
>>>> is carved out during cell creation?
>>>>
>>>> Is that a hardware limitation?
>>>>
>>>> Looks like a blocker...
>>> Yes, this is a hardware limitation. I it designed for static
>>> partitioning.
>>
>> IOW, we can also not change the configuration by destroying and
>> recreating non-root cells with different memory layouts?
>>
>
> For now, we have gic-demo, uart-demo and linux-demo
> You can interchangeably create/destroy cells in any order.

I was rather referring to

1. create linux-demo with, say, 256 MB DMA-capable RAM
2. destroy linux-demo
3. edit config to use 128 MB only
4. create linux-demo with reduced RAM
I'm asking for a check in pvu_iommu_unmap_memory that the passed memory
region does not have JAILHOUSE_MEM_DMA set. When that is the case,
something went wrong because the request cannot be fulfilled on the PVU.

However, I'm afraid that test will trigger on non-root cell destruction
when that cell contained DMA-capable memory. That would mean any device
that this cell owned and that is DMA-capable could continue to write the
cell memory, possibly competing with the root cell loading new content
into it. This gets rather nasty because now you need to prevent such
writes be resetting all devices reliably. But Jailhouse can only do that
for PCI devices (removing the master bit), not for random platform devices.

Jan

Nikhil Devshatwar

unread,
Jan 6, 2020, 6:58:08 AM1/6/20
to Jan Kiszka, jailho...@googlegroups.com, lokes...@ti.com
This should be fine I guess. cell_exit hook resets the PVU context for that stream_id (pvu_iommu_flush_context()
subsequent cell_init programs everything as required.

The limitation is on reprogramming while the context is already enabled.
You can disable the context and reprogram a new memory map.

Sorry for causing confusion.
Sure, I will add the check, yes it will print while destroying inmate cells.
However, as I said, the PVU context can be reset for that stream_id and
It will cause faults for all DMA requests on that context. No corruption to root cell or broken isolation.

Regards,
Nikhil D

Jan Kiszka

unread,
Jan 6, 2020, 7:05:54 AM1/6/20
to Nikhil Devshatwar, jailho...@googlegroups.com, lokes...@ti.com
OK, that sounds better.
Is the context disabled prior to the unmap calls hitting the pvu? Then
the check could be skipped if !pvu_tlb_is_enabled, right?

Jan

Nikhil Devshatwar

unread,
Jan 6, 2020, 7:15:30 AM1/6/20
to Jan Kiszka, jailho...@googlegroups.com, lokes...@ti.com
I checked that, Unfortunately, all unit exit hooks are called after the arch_unmap_memory
So the disable check cannot be used.
I will see if there is some way around this warning.


Jan


Jan Kiszka

unread,
Jan 6, 2020, 7:23:58 AM1/6/20
to Nikhil Devshatwar, jailho...@googlegroups.com, lokes...@ti.com
Otherwise, make it depend on cell == root_cell, with the reasoning that
cell_exit will disable the mapping.

Jan

nikh...@ti.com

unread,
Jan 8, 2020, 6:19:29 AM1/8/20
to jailho...@googlegroups.com, jan.k...@siemens.com, Nikhil Devshatwar
From: Nikhil Devshatwar <nikh...@ti.com>

This series adds support for TI PVU as an iommu unit.
PVU is a 2nd stage only IOMMU which provides realtime address translation.

J721e has 3 instances of PVU and all the DMA traffic can be routed via PVU
when running inside a virtual machine.

Changes from v1:
* New patch1 for avoiding warning when unmap is called from cell_destroy
* Split the patch affecting amd fields into separate one
* SMMU fixes are sent in another series

Nikhil Devshatwar (5):
core: Update cell_state while destroying the cell
configs: Introduce tipvu IOMMU specific fields in platform_data
arm64: ti-pvu: Add support for ti-pvu iommu unit
configs: arm64: k3-j721e-evm: Add PVU IOMMU devices in platform_data
configs: arm64: k3-j721e-evm: Add stream ids for devices behind IOMMU

configs/arm64/k3-j721e-evm-linux-demo.c | 7 +
configs/arm64/k3-j721e-evm.c | 36 ++
hypervisor/arch/arm-common/include/asm/cell.h | 7 +
.../arch/arm-common/include/asm/iommu.h | 1 +
.../arch/arm-common/include/asm/ti-pvu.h | 129 ++++
hypervisor/arch/arm-common/iommu.c | 5 +-
hypervisor/arch/arm64/Kbuild | 3 +-
hypervisor/arch/arm64/ti-pvu.c | 581 ++++++++++++++++++
hypervisor/control.c | 2 +
include/jailhouse/cell-config.h | 17 +-
10 files changed, 781 insertions(+), 7 deletions(-)
create mode 100644 hypervisor/arch/arm-common/include/asm/ti-pvu.h
create mode 100644 hypervisor/arch/arm64/ti-pvu.c

--
2.17.1

nikh...@ti.com

unread,
Jan 8, 2020, 6:19:30 AM1/8/20
to jailho...@googlegroups.com, jan.k...@siemens.com, Nikhil Devshatwar
From: Nikhil Devshatwar <nikh...@ti.com>

Update the cell_state to SHUT_DOWN as part of the cell_destroy
This will make sure that the memory_unmap calls and unit's
cell_exit calls can see the correct status of the cell.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
---
hypervisor/control.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/hypervisor/control.c b/hypervisor/control.c
index b6d748d4..f4e06165 100644
--- a/hypervisor/control.c
+++ b/hypervisor/control.c
@@ -363,6 +363,8 @@ static void cell_destroy_internal(struct cell *cell)
unsigned int cpu, n;
struct unit *unit;

+ cell->comm_page.comm_region.cell_state = JAILHOUSE_CELL_SHUT_DOWN;
+
for_each_cpu(cpu, cell->cpu_set) {
arch_park_cpu(cpu);

--
2.17.1

nikh...@ti.com

unread,
Jan 8, 2020, 6:19:32 AM1/8/20
to jailho...@googlegroups.com, jan.k...@siemens.com, Nikhil Devshatwar
From: Nikhil Devshatwar <nikh...@ti.com>

Move the amd specific fields in separate structures.
Add new tipvu fields for TLB base address and size.
Wrap all the vendor specific IOMMU structs in single union.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
---
include/jailhouse/cell-config.h | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/include/jailhouse/cell-config.h b/include/jailhouse/cell-config.h
index 5b983b38..b22275eb 100644
--- a/include/jailhouse/cell-config.h
+++ b/include/jailhouse/cell-config.h
@@ -251,10 +251,18 @@ struct jailhouse_iommu {
__u64 base;
__u32 size;

- __u16 amd_bdf;
- __u8 amd_base_cap;
- __u8 amd_msi_cap;
- __u32 amd_features;
+ union {
+ struct {
+ __u16 amd_bdf;
+ __u8 amd_base_cap;
+ __u8 amd_msi_cap;
+ __u32 amd_features;
+ };
+ struct {
+ __u64 tipvu_tlb_base;
+ __u32 tipvu_tlb_size;
+ };
+ };

nikh...@ti.com

unread,
Jan 8, 2020, 6:19:34 AM1/8/20
to jailho...@googlegroups.com, jan.k...@siemens.com, Nikhil Devshatwar
From: Nikhil Devshatwar <nikh...@ti.com>

Add support for Texas Instrument's Peripheral Virtualization Unit
* Define a new IOMMU type and extra fields in the platform_data
* Add new cofig option CONFIG_IOMMU_TI_PVU
* Integrate with the arm iommu support such that multiple types
of IOMMU can be supported.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
---

Notes:
Changes from v1:
* Fold header files into single header
* Keep the re-inclusion protection for iommu.h and pvu.h
* Remove compile time switch CONFIG_IOMMU_TI_PVU
* Review signedness qualifiers for all variables
* Implement the shutdown hook for cleaning the hardware state
* Add warning when unsupported unmap_memory is called
* Other cosmetic updates

hypervisor/arch/arm-common/include/asm/cell.h | 7 +
.../arch/arm-common/include/asm/iommu.h | 1 +
.../arch/arm-common/include/asm/ti-pvu.h | 129 ++++
hypervisor/arch/arm-common/iommu.c | 5 +-
hypervisor/arch/arm64/Kbuild | 3 +-
hypervisor/arch/arm64/ti-pvu.c | 581 ++++++++++++++++++
include/jailhouse/cell-config.h | 1 +
7 files changed, 724 insertions(+), 3 deletions(-)
create mode 100644 hypervisor/arch/arm-common/include/asm/ti-pvu.h
create mode 100644 hypervisor/arch/arm64/ti-pvu.c

index 00000000..76cd4c7f
--- /dev/null
+++ b/hypervisor/arch/arm-common/include/asm/ti-pvu.h
@@ -0,0 +1,129 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * TI PVU IOMMU unit API headers
+ *
+ * Authors:
+ * Nikhil Devshatwar <nikh...@ti.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef _IOMMMU_PVU_H_
+#define _IOMMMU_PVU_H_
+
+#include <jailhouse/config.h>
+
+#define PVU_NUM_TLBS 64
+#define PVU_NUM_ENTRIES 8
+
+#define PVU_CONFIG_NTLB_MASK (0xff)
+#define PVU_CONFIG_NENT_MASK (0xf << 16)
+
+#define PVU_MAX_VIRTID_MASK (0xfff)
+
+#define PVU_ENABLE_DIS (0x0)
+#define PVU_ENABLE_EN (0x1)
+#define PVU_ENABLE_MASK (0x1)
+
+#define LPAE_PAGE_PERM_UR (1 << 15)
+#define LPAE_PAGE_PERM_UW (1 << 14)
+#define LPAE_PAGE_PERM_UX (1 << 13)
+#define LPAE_PAGE_PERM_SR (1 << 12)
+#define LPAE_PAGE_PERM_SW (1 << 11)
+#define LPAE_PAGE_PERM_SX (1 << 10)
+
+#define LPAE_PAGE_MEM_WRITETHROUGH (2 << 8)
+#define LPAE_PAGE_OUTER_SHARABLE (1 << 4)
+#define LPAE_PAGE_IS_NOALLOC (0 << 2)
+#define LPAE_PAGE_OS_NOALLOC (0 << 0)
+
+struct pvu_hw_tlb_entry {
+ u32 reg0;
+ u32 reg1;
+ u32 reg2;
+ u32 reg3;
+ u32 reg4;
+ u32 reg5;
+ u32 reg6;
+ u32 reg7;
+int pvu_iommu_map_memory(struct cell *cell,
+ const struct jailhouse_memory *mem);
+
+int pvu_iommu_unmap_memory(struct cell *cell,
+ const struct jailhouse_memory *mem);
+
+int pvu_iommu_config_commit(struct cell *cell);
+
+#endif /* _IOMMMU_PVU_H_ */
diff --git a/hypervisor/arch/arm-common/iommu.c b/hypervisor/arch/arm-common/iommu.c
index b3100d03..4f4a4034 100644
--- a/hypervisor/arch/arm-common/iommu.c
+++ b/hypervisor/arch/arm-common/iommu.c
@@ -26,15 +26,16 @@ unsigned int iommu_count_units(void)
int iommu_map_memory_region(struct cell *cell,
const struct jailhouse_memory *mem)
{
- return 0;
+ return pvu_iommu_map_memory(cell, mem);
}

int iommu_unmap_memory_region(struct cell *cell,
const struct jailhouse_memory *mem)
{
- return 0;
+ return pvu_iommu_unmap_memory(cell, mem);
}

void iommu_config_commit(struct cell *cell)
{
+ pvu_iommu_config_commit(cell);
}
diff --git a/hypervisor/arch/arm64/Kbuild b/hypervisor/arch/arm64/Kbuild
index 323b78b6..33e8b79b 100644
--- a/hypervisor/arch/arm64/Kbuild
+++ b/hypervisor/arch/arm64/Kbuild
@@ -20,4 +20,5 @@ always := lib.a
# irqchip (common-objs-y), <generic units>

lib-y := $(common-objs-y)
-lib-y += entry.o setup.o control.o mmio.o paging.o caches.o traps.o smmu-v3.o
+lib-y += entry.o setup.o control.o mmio.o paging.o caches.o traps.o
+lib-y += smmu-v3.o ti-pvu.o
diff --git a/hypervisor/arch/arm64/ti-pvu.c b/hypervisor/arch/arm64/ti-pvu.c
new file mode 100644
index 00000000..87ef9522
--- /dev/null
+++ b/hypervisor/arch/arm64/ti-pvu.c
@@ -0,0 +1,581 @@
+
+#define MAX_PVU_ENTRIES (PAGE_SIZE / sizeof (struct pvu_tlb_entry))
+#define MAX_VIRTID 7
+
+static struct pvu_dev pvu_units[JAILHOUSE_MAX_IOMMU_UNITS];
+static unsigned int pvu_count;
+
+static const u64 pvu_page_size_bytes[] = {
+ 4 * 1024,
+ 16 * 1024,
+ 64 * 1024,
+ 2 * 1024 * 1024,
+ 32 * 1024 * 1024,
+ 512 * 1024 * 1024,
+ 1 * 1024 * 1024 * 1024,
+ 16ULL * 1024 * 1024 * 1024,
+ unsigned int i;
+
+ for (i = dev->max_virtid + 1; i < dev->num_tlbs; i++) {
+ if (dev->tlb_data[i] == 0) {
+ dev->tlb_data[i] = virtid << dev->num_entries;
+ return i;
+ }
+ }
+ return 0;
+}
+
+static void pvu_tlb_flush(struct pvu_dev *dev, u16 tlbnum)
+{
+ struct pvu_hw_tlb_entry *entry;
+ struct pvu_hw_tlb *tlb;
+ u32 i;
+
+ pvu_tlb_disable(dev, tlbnum);
+ tlb = (struct pvu_hw_tlb *)dev->tlb_base + tlbnum;
+
+ for (i = 0; i < dev->num_entries; i++) {
+
+ entry = &tlb->entry[i];
+ mmio_write32(&entry->reg0, 0x0);
+ mmio_write32(&entry->reg1, 0x0);
+ mmio_write32(&entry->reg2, 0x0);
+ mmio_write32(&entry->reg4, 0x0);
+ mmio_write32(&entry->reg5, 0x0);
+ mmio_write32(&entry->reg6, 0x0);
+ }
+
+ mmio_write32(&tlb->chain, 0x0);
+
+ if (i < dev->max_virtid)
+ dev->tlb_data[tlbnum] = 0x0 | i << dev->num_entries;
+ else
+ dev->tlb_data[tlbnum] = 0x0;
+
+}
+
+static void pvu_entry_enable(struct pvu_dev *dev, u16 tlbnum, u8 index)
+{
+ struct pvu_hw_tlb_entry *entry;
+ struct pvu_hw_tlb *tlb;
+
+ tlb = (struct pvu_hw_tlb *)dev->tlb_base + tlbnum;
+ entry = &tlb->entry[index];
+
+ mmio_write32_field(&entry->reg2, PVU_TLB_ENTRY_MODE_MASK,
+ PVU_TLB_ENTRY_VALID);
+
+ dev->tlb_data[tlbnum] |= (1 << index);
+}
+
+static int pvu_entry_write(struct pvu_dev *dev, u16 tlbnum, u8 index,
+ struct pvu_tlb_entry *ent)
+{
+ struct pvu_hw_tlb_entry *entry;
+ struct pvu_hw_tlb *tlb;
+ u8 pgsz;
+
+ tlb = (struct pvu_hw_tlb *)dev->tlb_base + tlbnum;
+ entry = &tlb->entry[index];
+
+ for (pgsz = 0; pgsz < ARRAY_SIZE(pvu_page_size_bytes); pgsz++) {
+ if (ent->size == pvu_page_size_bytes[pgsz])
+ break;
+ }
+
+ if (pgsz >= ARRAY_SIZE(pvu_page_size_bytes)) {
+ printk("ERROR: PVU: %s: Unsupported page size %llx\n",
+ __func__, ent->size);
+ return -EINVAL;
+ }
+
+ if (!is_aligned(ent->virt_addr, ent->size) ||
+ !is_aligned(ent->phys_addr, ent->size)) {
+ printk("ERROR: PVU: %s: Address %llx => %llx is not aligned with size %llx\n",
+ __func__, ent->virt_addr, ent->phys_addr, ent->size);
+ return -EINVAL;
+ }
+
+ mmio_write32(&entry->reg0, ent->virt_addr & 0xffffffff);
+ mmio_write32_field(&entry->reg1, 0xffff, (ent->virt_addr >> 32));
+ mmio_write32(&entry->reg2, 0x0);
+
+ mmio_write32(&entry->reg4, ent->phys_addr & 0xffffffff);
+ mmio_write32_field(&entry->reg5, 0xffff, (ent->phys_addr >> 32));
+ mmio_write32(&entry->reg6, 0x0);
+
+ mmio_write32_field(&entry->reg2, PVU_TLB_ENTRY_PGSIZE_MASK, pgsz);
+ mmio_write32_field(&entry->reg2, PVU_TLB_ENTRY_FLAG_MASK, ent->flags);
+
+ /* Do we need "DSB NSH" here to make sure all writes are finised? */
+ pvu_entry_enable(dev, tlbnum, index);
+ return 0;
+}
+
+static u32 pvu_init_device(struct pvu_dev *dev, u16 max_virtid)
+{
+ struct pvu_hw_cfg *cfg;
+ unsigned int i;
+
+ cfg = (struct pvu_hw_cfg *)dev->cfg_base;
+
+ dev->num_tlbs = mmio_read32_field(&cfg->config, PVU_CONFIG_NTLB_MASK);
+ dev->num_entries =
+ mmio_read32_field(&cfg->config, PVU_CONFIG_NENT_MASK);
+
+ if (max_virtid >= dev->num_tlbs) {
+ printk("ERROR: PVU: Max virtid(%d) should be less than num_tlbs(%d)\n",
+ max_virtid, dev->num_tlbs);
+ return -EINVAL;
+ }
+
+ dev->max_virtid = max_virtid;
+ mmio_write32(&cfg->virtid_map1, 0);
+ mmio_write32_field(&cfg->virtid_map2, PVU_MAX_VIRTID_MASK, max_virtid);
+
+ for (i = 0; i < dev->num_tlbs; i++) {
+
+ pvu_tlb_disable(dev, i);
+ if (i < dev->max_virtid)
+ dev->tlb_data[i] = 0x0 | i << dev->num_entries;
+ else
+ dev->tlb_data[i] = 0x0;
+ }
+
+ /* Enable all types of exceptions */
+ mmio_write32(&cfg->exception_logging_disable, 0x0);
+ mmio_write32(&cfg->exception_logging_control, 0x0);
+ mmio_write32_field(&cfg->enable, PVU_ENABLE_MASK, PVU_ENABLE_EN);
+ return 0;
+}
+
+static void pvu_shutdown_device(struct pvu_dev *dev)
+{
+ struct pvu_hw_cfg *cfg;
+ unsigned int i;
+
+ cfg = (struct pvu_hw_cfg *)dev->cfg_base;
+
+ for (i = 0; i < dev->num_tlbs; i++) {
+ pvu_tlb_flush(dev, i);
+ }
+
+ mmio_write32_field(&cfg->enable, PVU_ENABLE_MASK, PVU_ENABLE_DIS);
+}
+
+/*
+ * Split a memory region into multiple pages, where page size is one of the PVU
+ * supported size and the start address is aligned to page size
+ */
+static int pvu_entrylist_create(u64 ipa, u64 pa, u64 map_size, u64 flags,
+ struct pvu_tlb_entry *entlist, u32 num_entries)
+{
+ u64 page_size, vaddr, paddr;
+ unsigned int count;
+ s64 size;
+ int idx;
+
+ vaddr = ipa;
+ paddr = pa;
+ size = map_size;
+ count = 0;
+
+ while (size > 0) {
+
+ if (count == num_entries) {
+ printk("ERROR: PVU: Need more TLB entries for mapping %llx => %llx with size %llx\n",
+ ipa, pa, map_size);
+ return -EINVAL;
+ }
+
+ /* Try size from largest to smallest */
+ for (idx = ARRAY_SIZE(pvu_page_size_bytes) - 1; idx >= 0; idx--) {
+
+ page_size = pvu_page_size_bytes[idx];
+
+ if (is_aligned(vaddr, page_size) &&
+ is_aligned(paddr, page_size) &&
+ size >= page_size) {
+
+ entlist[count].virt_addr = vaddr;
+ entlist[count].phys_addr = paddr;
+ entlist[count].size = page_size;
+ entlist[count].flags = flags;
+
+ count++;
+ vaddr += page_size;
+ paddr += page_size;
+ size -= page_size;
+ break;
+ }
+ }
+
+ if (idx < 0) {
+ printk("ERROR: PVU: Addresses %llx %llx" \
+ "aren't aligned to any of the allowed page sizes\n",
+ vaddr, paddr);
+ return -EINVAL;
+ }
+ }
+ return count;
+}
+
+static void pvu_entrylist_sort(struct pvu_tlb_entry *entlist, u32 num_entries)
+{
+ struct pvu_tlb_entry temp;
+ unsigned int i, j;
+
+ for (i = 0; i < num_entries; i++) {
+ for (j = i; j < num_entries; j++) {
+
+ if (entlist[i].size < entlist[j].size) {
+ temp = entlist[i];
+ entlist[i] = entlist[j];
+ entlist[j] = temp;
+ }
+ }
+ }
+}
+
+static int pvu_iommu_program_entries(struct cell *cell, u8 virtid)
+{
+ unsigned int inst, i, tlbnum, idx, ent_count;
+ struct pvu_tlb_entry *ent, *cell_entries;
+ struct pvu_dev *dev;
+ int ret, tlb_next;
+
+ cell_entries = cell->arch.iommu_pvu.entries;
+ ent_count = cell->arch.iommu_pvu.ent_count;
+ if (ent_count == 0 || cell_entries == NULL)
+ return 0;
+
+ /* Program same memory mapping for all of the instances */
+ for (inst = 0; inst < pvu_count; inst++) {
+
+ dev = &pvu_units[inst];
+ if (pvu_tlb_is_enabled(dev, virtid))
+ continue;
+
+ tlbnum = virtid;
+ for (i = 0; i < ent_count; i++) {
+
+ ent = &cell_entries[i];
+ idx = i % dev->num_entries;
+
+ if (idx == 0 && i >= dev->num_entries) {
+ /* Find next available TLB and chain to it */
+ tlb_next = pvu_tlb_alloc(dev, virtid);
+ if (tlb_next < 0)
+ return -ENOMEM;
+ pvu_tlb_chain(dev, tlbnum, tlb_next);
+ pvu_tlb_enable(dev, tlbnum);
+ tlbnum = tlb_next;
+ }
+
+ ret = pvu_entry_write(dev, tlbnum, idx, ent);
+ if (ret)
+ return ret;
+ }
+ pvu_tlb_enable(dev, tlbnum);
+ }
+ return 0;
+}
+
+/*
+ * Actual TLB entry programming is deferred till config_commit
+ * Only populate the pvu_entries array for now
+ */
+int pvu_iommu_map_memory(struct cell *cell,
+ const struct jailhouse_memory *mem)
+{
+ struct pvu_tlb_entry *ent;
+ unsigned int size;
+ u32 flags = 0;
+ int ret;
+
+ if (pvu_count == 0 || (mem->flags & JAILHOUSE_MEM_DMA) == 0)
+ return 0;
+
+ if (cell->arch.iommu_pvu.ent_count == MAX_PVU_ENTRIES)
+ return -ENOMEM;
+
+ if (mem->flags & JAILHOUSE_MEM_READ)
+ flags |= (LPAE_PAGE_PERM_UR | LPAE_PAGE_PERM_SR);
+ if (mem->flags & JAILHOUSE_MEM_WRITE)
+ flags |= (LPAE_PAGE_PERM_UW | LPAE_PAGE_PERM_SW);
+ if (mem->flags & JAILHOUSE_MEM_EXECUTE)
+ flags |= (LPAE_PAGE_PERM_UX | LPAE_PAGE_PERM_SX);
+
+ flags |= (LPAE_PAGE_MEM_WRITETHROUGH | LPAE_PAGE_OUTER_SHARABLE |
+ LPAE_PAGE_IS_NOALLOC | LPAE_PAGE_OS_NOALLOC);
+
+ ent = &cell->arch.iommu_pvu.entries[cell->arch.iommu_pvu.ent_count];
+ size = MAX_PVU_ENTRIES - cell->arch.iommu_pvu.ent_count;
+
+ ret = pvu_entrylist_create(mem->virt_start, mem->phys_start, mem->size,
+ flags, ent, size);
+ if (ret < 0)
+ return ret;
+
+ cell->arch.iommu_pvu.ent_count += ret;
+ return 0;
+}
+
+int pvu_iommu_unmap_memory(struct cell *cell,
+ const struct jailhouse_memory *mem)
+{
+ u32 cell_state;
+
+ if (pvu_count == 0 || (mem->flags & JAILHOUSE_MEM_DMA) == 0)
+ return 0;
+
+ /*
+ * PVU does not support dynamic unmapping of memory
+ * With correct static partitioning, following WARNING shouldn't appear
+ */
+
+ cell_state = cell->comm_page.comm_region.cell_state;
+
+ if (cell_state == JAILHOUSE_CELL_RUNNING ||
+ cell_state == JAILHOUSE_CELL_RUNNING_LOCKED)
+ printk("WARNING: PVU cannot unmap memory at runtime for cell %s\n",
+ cell->config->name);
+
+ return 0;
+}
+
+int pvu_iommu_config_commit(struct cell *cell)
+{
+ unsigned int i, virtid;
+ int ret;
+
+ if (pvu_count == 0 || !cell)
+ return 0;
+
+ /*
+ * Chaining the TLB entries adds extra latency to translate those
+ * addresses.
+ * Sort the entries in descending order of page sizes to reduce effects
+ * of chaining and thus reducing average translation latency
+ */
+ pvu_entrylist_sort(cell->arch.iommu_pvu.entries,
+ cell->arch.iommu_pvu.ent_count);
+
+ for_each_stream_id(virtid, cell->config, i) {
+ if (virtid > MAX_VIRTID)
+ continue;
+
+ ret = pvu_iommu_program_entries(cell, virtid);
+ if (ret)
+ return ret;
+ }
+
+ cell->arch.iommu_pvu.ent_count = 0;
+ return ret;
+}
+
+static int pvu_iommu_cell_init(struct cell *cell)
+{
+ unsigned int i, virtid;
+ struct pvu_dev *dev;
+
+ if (pvu_count == 0)
+ return 0;
+
+ cell->arch.iommu_pvu.ent_count = 0;
+ cell->arch.iommu_pvu.entries = page_alloc(&mem_pool, 1);
+ if (!cell->arch.iommu_pvu.entries)
+ return -ENOMEM;
+
+ dev = &pvu_units[0];
+ for_each_stream_id(virtid, cell->config, i) {
+
+ if (virtid > MAX_VIRTID)
+ continue;
+
+ if (pvu_tlb_is_enabled(dev, virtid))
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int pvu_iommu_flush_context(u16 virtid)
+{
+ unsigned int i, tlbnum, next;
+ struct pvu_dev *dev;
+
+ for (i = 0; i < pvu_count; i++) {
+
+ dev = &pvu_units[i];
+ tlbnum = virtid;
+
+ while (tlbnum) {
+
+ next = pvu_tlb_next(dev, tlbnum);
+ pvu_tlb_flush(dev, tlbnum);
+ tlbnum = next;
+ }
+ }
+ return 0;
+}
+
+static void pvu_iommu_cell_exit(struct cell *cell)
+{
+ unsigned int i, virtid;
+
+ if (pvu_count == 0)
+ return;
+
+ for_each_stream_id(virtid, cell->config, i) {
+
+ if (virtid > MAX_VIRTID)
+ continue;
+
+ pvu_iommu_flush_context(virtid);
+ }
+
+ cell->arch.iommu_pvu.ent_count = 0;
+ page_free(&mem_pool, cell->arch.iommu_pvu.entries, 1);
+ cell->arch.iommu_pvu.entries = NULL;
+}
+
+static int pvu_iommu_init(void)
+{
+ struct jailhouse_iommu *iommu;
+ struct pvu_dev *dev;
+ unsigned int i;
+ int ret;
+
+ iommu = &system_config->platform_info.arm.iommu_units[0];
+ for (i = 0; i < iommu_count_units(); iommu++, i++) {
+
+ if (iommu->type != JAILHOUSE_IOMMU_PVU)
+ continue;
+
+ dev = &pvu_units[pvu_count];
+ dev->cfg_base = paging_map_device(iommu->base,
+ iommu->size);
+ dev->tlb_base = paging_map_device(iommu->tipvu_tlb_base,
+ iommu->tipvu_tlb_size);
+
+ ret = pvu_init_device(dev, MAX_VIRTID);
+ if (ret)
+ return ret;
+
+ pvu_count++;
+ }
+
+ return pvu_iommu_cell_init(&root_cell);
+}
+
+static void pvu_iommu_shutdown(void)
+{
+ struct pvu_dev *dev;
+ unsigned int i;
+
+ pvu_iommu_cell_exit(&root_cell);
+
+ for (i = 0; i < pvu_count; i++) {
+
+ dev = &pvu_units[i];
+ pvu_shutdown_device(dev);
+ }
+
+}
+
+DEFINE_UNIT_MMIO_COUNT_REGIONS_STUB(pvu_iommu);
+DEFINE_UNIT(pvu_iommu, "PVU IOMMU");
diff --git a/include/jailhouse/cell-config.h b/include/jailhouse/cell-config.h
index b22275eb..3ac28af2 100644
--- a/include/jailhouse/cell-config.h
+++ b/include/jailhouse/cell-config.h
@@ -245,6 +245,7 @@ struct jailhouse_pci_capability {
#define JAILHOUSE_IOMMU_AMD 1
#define JAILHOUSE_IOMMU_INTEL 2
#define JAILHOUSE_IOMMU_SMMUV3 3
+#define JAILHOUSE_IOMMU_PVU 4

struct jailhouse_iommu {
__u32 type;
--
2.17.1

nikh...@ti.com

unread,
Jan 8, 2020, 6:19:36 AM1/8/20
to jailho...@googlegroups.com, jan.k...@siemens.com, Nikhil Devshatwar
From: Nikhil Devshatwar <nikh...@ti.com>

J721e device has 3 instance of PVU which can be used as IOMMU.
Each PVU has a config region and a TLB region where the memory
mapping information is stored.
Describe these as part of the root cell's platform_data.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
---
configs/arm64/k3-j721e-evm.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

diff --git a/configs/arm64/k3-j721e-evm.c b/configs/arm64/k3-j721e-evm.c
index 1de90416..825883c8 100644
--- a/configs/arm64/k3-j721e-evm.c
+++ b/configs/arm64/k3-j721e-evm.c
@@ -56,7 +56,29 @@ struct {
.base = 0x36600000,
.size = 0x100000,
},
+ {
+ .type = JAILHOUSE_IOMMU_PVU,
+ .base = 0x30f80000,
+ .size = 0x1000,
+ .tipvu_tlb_base = 0x36000000,
+ .tipvu_tlb_size = 0x40000,
+ },
+ {
+ .type = JAILHOUSE_IOMMU_PVU,
+ .base = 0x30f81000,
+ .size = 0x1000,
+ .tipvu_tlb_base = 0x36040000,
+ .tipvu_tlb_size = 0x40000,
+ },
+ {
+ .type = JAILHOUSE_IOMMU_PVU,
+ .base = 0x30f83000,
+ .size = 0x1000,
+ .tipvu_tlb_base = 0x360c0000,
+ .tipvu_tlb_size = 0x40000,
+ },
},
+
},
.root_cell = {

nikh...@ti.com

unread,
Jan 8, 2020, 6:19:37 AM1/8/20
to jailho...@googlegroups.com, jan.k...@siemens.com, Nikhil Devshatwar
From: Nikhil Devshatwar <nikh...@ti.com>

Add stream_ids for peripherals which are behind IOMMU instances.
PVU and SMMU-V3 sets up memory mapping for all of these contexts
for correct 2nd stage translation.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
---
configs/arm64/k3-j721e-evm-linux-demo.c | 7 +++++++
configs/arm64/k3-j721e-evm.c | 14 ++++++++++++++
2 files changed, 21 insertions(+)

diff --git a/configs/arm64/k3-j721e-evm-linux-demo.c b/configs/arm64/k3-j721e-evm-linux-demo.c
index 177fb2e1..47ad32ea 100644
--- a/configs/arm64/k3-j721e-evm-linux-demo.c
+++ b/configs/arm64/k3-j721e-evm-linux-demo.c
@@ -27,6 +27,7 @@ struct {
struct jailhouse_memory mem_regions[27];
struct jailhouse_irqchip irqchips[4];
struct jailhouse_pci_device pci_devices[1];
+ __u32 stream_ids[2];
} __attribute__((packed)) config = {
.cell = {
.signature = JAILHOUSE_CELL_DESC_SIGNATURE,
@@ -38,6 +39,7 @@ struct {
.num_memory_regions = ARRAY_SIZE(config.mem_regions),
.num_irqchips = ARRAY_SIZE(config.irqchips),
.num_pci_devices = ARRAY_SIZE(config.pci_devices),
+ .num_stream_ids = ARRAY_SIZE(config.stream_ids),
.cpu_reset_address = 0x0,
.vpci_irq_base = 195 - 32,
.console = {
@@ -281,4 +283,9 @@ struct {
.shmem_protocol = JAILHOUSE_SHMEM_PROTO_VETH,
},
},
+
+ .stream_ids = {
+ /* Non PCIe peripherals */
+ 0x0003, 0xf003,
+ },
};
diff --git a/configs/arm64/k3-j721e-evm.c b/configs/arm64/k3-j721e-evm.c
index 825883c8..f1c99415 100644
--- a/configs/arm64/k3-j721e-evm.c
+++ b/configs/arm64/k3-j721e-evm.c
@@ -22,6 +22,7 @@ struct {
struct jailhouse_memory mem_regions[36];
struct jailhouse_irqchip irqchips[6];
struct jailhouse_pci_device pci_devices[1];
+ __u32 stream_ids[30];
} __attribute__((packed)) config = {
.header = {
.signature = JAILHOUSE_SYSTEM_SIGNATURE,
@@ -87,6 +88,7 @@ struct {

Nikhil Devshatwar

unread,
Jan 10, 2020, 4:21:29 AM1/10/20
to jailho...@googlegroups.com, jan.k...@siemens.com
ping

Nikhil D

Jan Kiszka

unread,
Jan 10, 2020, 8:10:21 AM1/10/20
to Nikhil Devshatwar, jailho...@googlegroups.com
On 10.01.20 10:20, Nikhil Devshatwar wrote:
> ping

In my backlog queue, but that is long ATM, sorry.

Jan
--
Siemens AG, Corporate Technology, CT RDA IOT SES-DE
Corporate Competence Center Embedded Linux

Jan Kiszka

unread,
Jan 13, 2020, 1:13:15 AM1/13/20
to nikh...@ti.com, jailho...@googlegroups.com
Let's give the struct names, amd and tipvu. Then you can remove the
prefixes from the fields.

And better add __attribute__((packed)) to the substructures as well. I'm
not sure of the packed from the superstruct makes it there.

Jan

nikh...@ti.com

unread,
Jan 13, 2020, 5:48:12 AM1/13/20
to jailho...@googlegroups.com, jan.k...@siemens.com, Nikhil Devshatwar
From: Nikhil Devshatwar <nikh...@ti.com>

This series adds support for TI PVU as an iommu unit.
PVU is a 2nd stage only IOMMU which provides realtime address translation.

J721e has 3 instances of PVU and all the DMA traffic can be routed via PVU
when running inside a virtual machine.

Nikhil Devshatwar (5):
core: Update cell_state while destroying the cell
configs: Move amd specific fields in separate struct
arm64: ti-pvu: Add support for ti-pvu iommu unit
configs: arm64: k3-j721e-evm: Add PVU IOMMU devices in platform_data
configs: arm64: k3-j721e-evm: Add stream ids for devices behind IOMMU

configs/arm64/k3-j721e-evm-linux-demo.c | 7 +
configs/arm64/k3-j721e-evm.c | 36 ++
hypervisor/arch/arm-common/include/asm/cell.h | 7 +
.../arch/arm-common/include/asm/iommu.h | 1 +
.../arch/arm-common/include/asm/ti-pvu.h | 129 ++++
hypervisor/arch/arm-common/iommu.c | 5 +-
hypervisor/arch/arm64/Kbuild | 3 +-
hypervisor/arch/arm64/ti-pvu.c | 580 ++++++++++++++++++
hypervisor/arch/x86/amd_iommu.c | 26 +-
hypervisor/control.c | 2 +
include/jailhouse/cell-config.h | 18 +-
11 files changed, 794 insertions(+), 20 deletions(-)
create mode 100644 hypervisor/arch/arm-common/include/asm/ti-pvu.h
create mode 100644 hypervisor/arch/arm64/ti-pvu.c

--
2.17.1

nikh...@ti.com

unread,
Jan 13, 2020, 5:48:14 AM1/13/20
to jailho...@googlegroups.com, jan.k...@siemens.com, Nikhil Devshatwar
From: Nikhil Devshatwar <nikh...@ti.com>

Update the cell_state to SHUT_DOWN as part of the cell_destroy
This will make sure that the memory_unmap calls and unit's
cell_exit calls can see the correct status of the cell.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
---
hypervisor/control.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/hypervisor/control.c b/hypervisor/control.c
index a0a7532c..2ab6beac 100644

nikh...@ti.com

unread,
Jan 13, 2020, 5:48:16 AM1/13/20
to jailho...@googlegroups.com, jan.k...@siemens.com, Nikhil Devshatwar
From: Nikhil Devshatwar <nikh...@ti.com>

Create a union for all vendor specific fields and move the
amd specific fields in separate struct.
Also update the amd unit references of these fields.

This is to handle multiple iommu devices and their custom fields
separately.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
---

Notes:
Changes from v2:
* Use named structs for amd specific fields
* Update the references in the amd unit

hypervisor/arch/x86/amd_iommu.c | 26 +++++++++++++-------------
include/jailhouse/cell-config.h | 12 ++++++++----
2 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/hypervisor/arch/x86/amd_iommu.c b/hypervisor/arch/x86/amd_iommu.c
index 2fc6d033..6161ccf8 100644
--- a/hypervisor/arch/x86/amd_iommu.c
+++ b/hypervisor/arch/x86/amd_iommu.c
@@ -448,14 +448,14 @@ static void amd_iommu_init_fault_nmi(void)
&system_config->platform_info.x86.iommu_units[iommu->idx];

/* Disable MSI during interrupt reprogramming. */
- pci_write_config(cfg->amd_bdf, cfg->amd_msi_cap + 2, 0, 2);
+ pci_write_config(cfg->amd.bdf, cfg->amd.msi_cap + 2, 0, 2);

/*
* Write new MSI capability block, re-enabling interrupts with
* the last word.
*/
for (n = 3; n >= 0; n--)
- pci_write_config(cfg->amd_bdf, cfg->amd_msi_cap + 4 * n,
+ pci_write_config(cfg->amd.bdf, cfg->amd.msi_cap + 4 * n,
msi_reg.raw[n], 4);
}

@@ -637,14 +637,14 @@ static int amd_iommu_init_pci(struct amd_iommu *entry,
return trace_error(-EINVAL);

/* Check that EFR is supported */
- caps_header = pci_read_config(iommu->amd_bdf, iommu->amd_base_cap, 4);
+ caps_header = pci_read_config(iommu->amd.bdf, iommu->amd.base_cap, 4);
if (!(caps_header & CAPS_IOMMU_EFR_SUP))
return trace_error(-EIO);

- lo = pci_read_config(iommu->amd_bdf,
- iommu->amd_base_cap + CAPS_IOMMU_BASE_LOW_REG, 4);
- hi = pci_read_config(iommu->amd_bdf,
- iommu->amd_base_cap + CAPS_IOMMU_BASE_HI_REG, 4);
+ lo = pci_read_config(iommu->amd.bdf,
+ iommu->amd.base_cap + CAPS_IOMMU_BASE_LOW_REG, 4);
+ hi = pci_read_config(iommu->amd.bdf,
+ iommu->amd.base_cap + CAPS_IOMMU_BASE_HI_REG, 4);

if (lo & CAPS_IOMMU_ENABLE &&
((hi << 32) | lo) != (iommu->base | CAPS_IOMMU_ENABLE)) {
@@ -654,11 +654,11 @@ static int amd_iommu_init_pci(struct amd_iommu *entry,
}

/* Should be configured by BIOS, but we want to be sure */
- pci_write_config(iommu->amd_bdf,
- iommu->amd_base_cap + CAPS_IOMMU_BASE_HI_REG,
+ pci_write_config(iommu->amd.bdf,
+ iommu->amd.base_cap + CAPS_IOMMU_BASE_HI_REG,
(u32)(iommu->base >> 32), 4);
- pci_write_config(iommu->amd_bdf,
- iommu->amd_base_cap + CAPS_IOMMU_BASE_LOW_REG,
+ pci_write_config(iommu->amd.bdf,
+ iommu->amd.base_cap + CAPS_IOMMU_BASE_LOW_REG,
(u32)(iommu->base & 0xffffffff) | CAPS_IOMMU_ENABLE,
4);

@@ -687,9 +687,9 @@ static int amd_iommu_init_features(struct amd_iommu *entry,
return trace_error(-EIO);

/* Figure out if hardware events are supported. */
- if (iommu->amd_features)
+ if (iommu->amd.features)
entry->he_supported =
- iommu->amd_features & ACPI_REPORTING_HE_SUP;
+ iommu->amd.features & ACPI_REPORTING_HE_SUP;
else
entry->he_supported = efr & AMD_EXT_FEAT_HE_SUP;

diff --git a/include/jailhouse/cell-config.h b/include/jailhouse/cell-config.h
index 9b018de1..68446853 100644
--- a/include/jailhouse/cell-config.h
+++ b/include/jailhouse/cell-config.h
@@ -251,10 +251,14 @@ struct jailhouse_iommu {
__u64 base;
__u32 size;

- __u16 amd_bdf;
- __u8 amd_base_cap;
- __u8 amd_msi_cap;
- __u32 amd_features;
+ union {
+ struct {
+ __u16 bdf;
+ __u8 base_cap;
+ __u8 msi_cap;
+ __u32 features;
+ } __attribute__((packed)) amd;

nikh...@ti.com

unread,
Jan 13, 2020, 5:48:18 AM1/13/20
to jailho...@googlegroups.com, jan.k...@siemens.com, Nikhil Devshatwar
From: Nikhil Devshatwar <nikh...@ti.com>

Add support for Texas Instrument's Peripheral Virtualization Unit
* Define a new IOMMU type and extra fields in the platform_data
* Add new cofig option CONFIG_IOMMU_TI_PVU
* Integrate with the arm iommu support such that multiple types
of IOMMU can be supported.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
---

Notes:
Changes from v2:
* Use named struct tipvu for all custom fields in jailhouse_iommu
* Update references to these in tipvu unit

Changes from v1:
* Fold header files into single header
* Keep the re-inclusion protection for iommu.h and pvu.h
* Remove compile time switch CONFIG_IOMMU_TI_PVU
* Review signedness qualifiers for all variables
* Implement the shutdown hook for cleaning the hardware state
* Add warning when unsupported unmap_memory is called
* Other cosmetic updates

hypervisor/arch/arm-common/include/asm/cell.h | 7 +
.../arch/arm-common/include/asm/iommu.h | 1 +
.../arch/arm-common/include/asm/ti-pvu.h | 129 ++++
hypervisor/arch/arm-common/iommu.c | 5 +-
hypervisor/arch/arm64/Kbuild | 3 +-
hypervisor/arch/arm64/ti-pvu.c | 580 ++++++++++++++++++
include/jailhouse/cell-config.h | 6 +
7 files changed, 728 insertions(+), 3 deletions(-)
create mode 100644 hypervisor/arch/arm-common/include/asm/ti-pvu.h
create mode 100644 hypervisor/arch/arm64/ti-pvu.c

index 00000000..3f05f445
--- /dev/null
+++ b/hypervisor/arch/arm64/ti-pvu.c
@@ -0,0 +1,580 @@
+ dev->tlb_base = paging_map_device(iommu->tipvu.tlb_base,
+ iommu->tipvu.tlb_size);
diff --git a/include/jailhouse/cell-config.h b/include/jailhouse/cell-config.h
index 68446853..b8e1f038 100644
--- a/include/jailhouse/cell-config.h
+++ b/include/jailhouse/cell-config.h
@@ -245,6 +245,7 @@ struct jailhouse_pci_capability {
#define JAILHOUSE_IOMMU_AMD 1
#define JAILHOUSE_IOMMU_INTEL 2
#define JAILHOUSE_IOMMU_SMMUV3 3
+#define JAILHOUSE_IOMMU_PVU 4

struct jailhouse_iommu {
__u32 type;
@@ -258,6 +259,11 @@ struct jailhouse_iommu {
__u8 msi_cap;
__u32 features;
} __attribute__((packed)) amd;
+
+ struct {
+ __u64 tlb_base;
+ __u32 tlb_size;
+ } __attribute__((packed)) tipvu;
};
} __attribute__((packed));

--
2.17.1

nikh...@ti.com

unread,
Jan 13, 2020, 5:48:20 AM1/13/20
to jailho...@googlegroups.com, jan.k...@siemens.com, Nikhil Devshatwar
From: Nikhil Devshatwar <nikh...@ti.com>

J721e device has 3 instance of PVU which can be used as IOMMU.
Each PVU has a config region and a TLB region where the memory
mapping information is stored.
Describe these as part of the root cell's platform_data.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
---

Notes:
Changes from v2:
* Update references to tipvu custom fields

configs/arm64/k3-j721e-evm.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

diff --git a/configs/arm64/k3-j721e-evm.c b/configs/arm64/k3-j721e-evm.c
index 1de90416..ca91636d 100644
--- a/configs/arm64/k3-j721e-evm.c
+++ b/configs/arm64/k3-j721e-evm.c
@@ -56,7 +56,29 @@ struct {
.base = 0x36600000,
.size = 0x100000,
},
+ {
+ .type = JAILHOUSE_IOMMU_PVU,
+ .base = 0x30f80000,
+ .size = 0x1000,
+ .tipvu.tlb_base = 0x36000000,
+ .tipvu.tlb_size = 0x40000,
+ },
+ {
+ .type = JAILHOUSE_IOMMU_PVU,
+ .base = 0x30f81000,
+ .size = 0x1000,
+ .tipvu.tlb_base = 0x36040000,
+ .tipvu.tlb_size = 0x40000,
+ },
+ {
+ .type = JAILHOUSE_IOMMU_PVU,
+ .base = 0x30f83000,
+ .size = 0x1000,
+ .tipvu.tlb_base = 0x360c0000,
+ .tipvu.tlb_size = 0x40000,

nikh...@ti.com

unread,
Jan 13, 2020, 5:48:21 AM1/13/20
to jailho...@googlegroups.com, jan.k...@siemens.com, Nikhil Devshatwar
From: Nikhil Devshatwar <nikh...@ti.com>

Add stream_ids for peripherals which are behind IOMMU instances.
PVU and SMMU-V3 sets up memory mapping for all of these contexts
for correct 2nd stage translation.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
---
diff --git a/configs/arm64/k3-j721e-evm.c b/configs/arm64/k3-j721e-evm.c
index ca91636d..4f9755a8 100644
--- a/configs/arm64/k3-j721e-evm.c
+++ b/configs/arm64/k3-j721e-evm.c

Jan Kiszka

unread,
Jan 13, 2020, 7:38:55 AM1/13/20
to nikh...@ti.com, jailho...@googlegroups.com
Applied to next - with fixups (build fix for patch 2, whitespace warning
for patch 3).

Jan

Nikhil Devshatwar

unread,
Jan 13, 2020, 8:10:30 AM1/13/20
to Jan Kiszka, jailho...@googlegroups.com
Thanks. I didn't search for those fields in the configs. Thanks for fixing it.
Nikhil D

Jan


Jan Kiszka

unread,
Jan 13, 2020, 8:15:07 AM1/13/20
to Nikhil Devshatwar, jailho...@googlegroups.com
There will be another fix squeezed in soon: ARM is broken.

Jan Kiszka

unread,
Jan 13, 2020, 8:37:50 AM1/13/20
to nikh...@ti.com, jailho...@googlegroups.com
Unfortunately, the ARM build is broken now. Looks like you need to
rework patch

Jan Kiszka

unread,
Jan 13, 2020, 8:37:55 AM1/13/20
to Nikhil Devshatwar, Jailhouse
From: Nikhil Devshatwar <nikh...@ti.com>

Add support for Texas Instrument's Peripheral Virtualization Unit
* Define a new IOMMU type and extra fields in the platform_data
* Add new cofig option CONFIG_IOMMU_TI_PVU
* Integrate with the arm iommu support such that multiple types
of IOMMU can be supported.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
[Jan: moved into arm64 completely, fixed whitespace warnings, fixed includes]
Signed-off-by: Jan Kiszka <jan.k...@siemens.com>
---

Resending the fixed-up version as there were too many changes. Please
validate. Is in next as well.

Depends on the iommu split-up patch I sent before.

hypervisor/arch/arm-common/include/asm/cell.h | 7 +
hypervisor/arch/arm64/Kbuild | 2 +-
hypervisor/arch/arm64/include/asm/ti-pvu.h | 130 ++++++
hypervisor/arch/arm64/iommu.c | 6 +-
hypervisor/arch/arm64/ti-pvu.c | 577 ++++++++++++++++++++++++++
include/jailhouse/cell-config.h | 6 +
6 files changed, 725 insertions(+), 3 deletions(-)
create mode 100644 hypervisor/arch/arm64/include/asm/ti-pvu.h
create mode 100644 hypervisor/arch/arm64/ti-pvu.c

diff --git a/hypervisor/arch/arm-common/include/asm/cell.h b/hypervisor/arch/arm-common/include/asm/cell.h
index 5b1e4207..9c6e8c6f 100644
--- a/hypervisor/arch/arm-common/include/asm/cell.h
+++ b/hypervisor/arch/arm-common/include/asm/cell.h
@@ -15,10 +15,17 @@

#include <jailhouse/paging.h>

+struct pvu_tlb_entry;
+
struct arch_cell {
struct paging_structures mm;

u32 irq_bitmap[1024/32];
+
+ struct {
+ u8 ent_count;
+ struct pvu_tlb_entry *entries;
+ } iommu_pvu; /**< ARM PVU specific fields. */
};

#endif /* !_JAILHOUSE_ASM_CELL_H */
diff --git a/hypervisor/arch/arm64/Kbuild b/hypervisor/arch/arm64/Kbuild
index aa018ae7..c34b0f32 100644
--- a/hypervisor/arch/arm64/Kbuild
+++ b/hypervisor/arch/arm64/Kbuild
@@ -21,4 +21,4 @@ always := lib.a

lib-y := $(common-objs-y)
lib-y += entry.o setup.o control.o mmio.o paging.o caches.o traps.o
-lib-y += iommu.o smmu-v3.o
+lib-y += iommu.o smmu-v3.o ti-pvu.o
diff --git a/hypervisor/arch/arm64/include/asm/ti-pvu.h b/hypervisor/arch/arm64/include/asm/ti-pvu.h
new file mode 100644
index 00000000..2c340b3a
--- /dev/null
+++ b/hypervisor/arch/arm64/include/asm/ti-pvu.h
@@ -0,0 +1,130 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * TI PVU IOMMU unit API headers
+ *
+ * Authors:
+ * Nikhil Devshatwar <nikh...@ti.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef _IOMMMU_PVU_H_
+#define _IOMMMU_PVU_H_
+
+#include <jailhouse/cell.h>
+#include <jailhouse/cell-config.h>
diff --git a/hypervisor/arch/arm64/iommu.c b/hypervisor/arch/arm64/iommu.c
index b3100d03..b3ab51b4 100644
--- a/hypervisor/arch/arm64/iommu.c
+++ b/hypervisor/arch/arm64/iommu.c
@@ -12,6 +12,7 @@

#include <jailhouse/control.h>
#include <asm/iommu.h>
+#include <asm/ti-pvu.h>

unsigned int iommu_count_units(void)
{
@@ -26,15 +27,16 @@ unsigned int iommu_count_units(void)
int iommu_map_memory_region(struct cell *cell,
const struct jailhouse_memory *mem)
{
- return 0;
+ return pvu_iommu_map_memory(cell, mem);
}

int iommu_unmap_memory_region(struct cell *cell,
const struct jailhouse_memory *mem)
{
- return 0;
+ return pvu_iommu_unmap_memory(cell, mem);
}

void iommu_config_commit(struct cell *cell)
{
+ pvu_iommu_config_commit(cell);
}
diff --git a/hypervisor/arch/arm64/ti-pvu.c b/hypervisor/arch/arm64/ti-pvu.c
new file mode 100644
index 00000000..39dff875
--- /dev/null
+++ b/hypervisor/arch/arm64/ti-pvu.c
@@ -0,0 +1,577 @@
+#include <jailhouse/control.h>
+#include <jailhouse/printk.h>
+#include <jailhouse/unit.h>
2.16.4

Jan Kiszka

unread,
Jan 13, 2020, 8:39:12 AM1/13/20
to nikh...@ti.com, jailho...@googlegroups.com
Forget it, I hit the send button in the wrong draft window. I reworked
the patch and just submitted it.

Nikhil Devshatwar

unread,
Jan 13, 2020, 10:26:11 AM1/13/20
to Jan Kiszka, Jailhouse


On 13/01/20 7:07 pm, Jan Kiszka wrote:
From: Nikhil Devshatwar <nikh...@ti.com>

Add support for Texas Instrument's Peripheral Virtualization Unit
* Define a new IOMMU type and extra fields in the platform_data
* Add new cofig option CONFIG_IOMMU_TI_PVU
* Integrate with the arm iommu support such that multiple types
  of IOMMU can be supported.

Signed-off-by: Nikhil Devshatwar <nikh...@ti.com>
[Jan: moved into arm64 completely, fixed whitespace warnings, fixed includes]
Signed-off-by: Jan Kiszka <jan.k...@siemens.com>
---

Resending the fixed-up version as there were too many changes. Please 
validate. Is in next as well.
Tested-by: Nikhil Devshatwar <nikh...@ti.com>
Reply all
Reply to author
Forward
0 new messages