The SMMUv2 allows filtering bits when matching stream IDs before they're
passed to the TCU. In this way multiple streams legally get the same
translation.

On boards such as the ZCU Ultrascale+, the master ID needed to identify
the corresponding SMMU stream ID may be dependent on a specific AXI ID
that is set by the PL (and could be IP specific).

One single fixed mask to pass to the SMR to compact multiple stream IDs
before they "hit" the TCU is not flexible enough. The use-case is to
compact similar PL-originating masters and have the SMMU behaving the
same for them (e.g., they're assigned to the same inmate). At the
same time, one needs a full stream_id to assign e.g., different GEM
ethernets to different inmates.

For the MMU-500, provide an explicit mask + id in the configuration and
update the implementation accordingly.

Signed-off-by: Andrea Bastoni <[email protected]>
---
 hypervisor/arch/arm64/smmu.c    | 73 ++++++++++++++++++++++-----------
 include/jailhouse/cell-config.h | 15 +++++--
 2 files changed, 61 insertions(+), 27 deletions(-)

diff --git a/hypervisor/arch/arm64/smmu.c b/hypervisor/arch/arm64/smmu.c
index df92fb7a..9b824782 100644
--- a/hypervisor/arch/arm64/smmu.c
+++ b/hypervisor/arch/arm64/smmu.c
@@ -84,6 +84,10 @@
 #define SMR_VALID                      (1 << 31)
 #define SMR_MASK_SHIFT                 16
 #define SMR_ID_SHIFT                   0
+/* Ignore upper bit in ID and MASK */
+#define SMR_GET_ID(smr)                        ((smr) & 0x7fff)
+/* Mask is already specified from bit 0 in the configuration */
+#define SMR_GET_MASK(smr)              ((smr) & 0x7fff)
 
 /* Stream-to-Context Register */
 #define ARM_SMMU_GR0_S2CR(n)           (0xc00 + ((n) << 2))
@@ -152,7 +156,6 @@ struct arm_smmu_device {
        unsigned long                   pgshift;
        u32                             num_context_banks;
        u32                             num_mapping_groups;
-       u16                             arm_sid_mask;
        struct arm_smmu_smr             *smrs;
 };
 
@@ -164,6 +167,15 @@ static unsigned int num_smmu_devices;
             (counter) < num_smmu_devices;                      \
             (smmu)++, (counter)++)
 
+#define for_each_mmu500_sid(sid, config, counter)      \
+       u32 __stub_iter; \
+       for ((__stub_iter) = (jailhouse_cell_stream_ids(config)[0]), \
+               (counter) = 0, \
+               (sid) = (union jailhouse_stream_id)(__stub_iter); \
+            (counter) < (config)->num_stream_ids; \
+            (__stub_iter) = (jailhouse_cell_stream_ids(config)[++(counter)]), \
+               (sid) = (union jailhouse_stream_id)(__stub_iter))
+
 static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
 {
        struct arm_smmu_smr *smr = smmu->smrs + idx;
@@ -360,7 +372,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device 
*smmu)
        return 0;
 }
 
-static int arm_smmu_find_sme(u16 id, struct arm_smmu_device *smmu)
+static int arm_smmu_find_sme(u16 id, u16 mask, struct arm_smmu_device *smmu)
 {
        struct arm_smmu_smr *smrs = smmu->smrs;
        int free_idx = -EINVAL;
@@ -388,7 +400,7 @@ static int arm_smmu_find_sme(u16 id, struct arm_smmu_device 
*smmu)
                 * expect simply identical entries for this case, but there's
                 * no harm in accommodating the generalisation.
                 */
-               if ((smmu->arm_sid_mask & smrs[n].mask) == smmu->arm_sid_mask &&
+               if ((mask & smrs[n].mask) == mask &&
                    !((id ^ smrs[n].id) & ~smrs[n].mask)) {
                        return n;
                }
@@ -397,7 +409,7 @@ static int arm_smmu_find_sme(u16 id, struct arm_smmu_device 
*smmu)
                 * though, then there always exists at least one stream ID
                 * which would cause a conflict, and we can't allow that risk.
                 */
-               if (!((id ^ smrs[n].id) & ~(smrs[n].mask | smmu->arm_sid_mask)))
+               if (!((id ^ smrs[n].id) & ~(smrs[n].mask | mask)))
                        return -EINVAL;
        }
 
@@ -409,7 +421,9 @@ static int arm_smmu_cell_init(struct cell *cell)
        unsigned int vmid = cell->config->id;
        struct arm_smmu_device *smmu;
        struct arm_smmu_smr *smr;
-       unsigned int dev, n, sid;
+       unsigned int dev, n;
+       u16 sid, smask;
+       union jailhouse_stream_id fsid;
        int ret, idx;
 
        /* If no sids, ignore */
@@ -421,19 +435,22 @@ static int arm_smmu_cell_init(struct cell *cell)
 
                smr = smmu->smrs;
 
-               for_each_stream_id(sid, cell->config, n) {
-                       ret = arm_smmu_find_sme(sid, smmu);
+               for_each_mmu500_sid(fsid, cell->config, n) {
+                       sid = SMR_GET_ID(fsid.mmu500.id);
+                       smask = SMR_GET_MASK(fsid.mmu500.mask);
+
+                       ret = arm_smmu_find_sme(sid, smask, smmu);
                        if (ret < 0)
                                return trace_error(ret);
                        idx = ret;
 
-                       printk("Assigning StreamID 0x%x to cell \"%s\"\n",
-                              sid, cell->config->name);
+                       printk("Assigning SID 0x%x, Mask 0x%x to cell \"%s\"\n",
+                              sid, smask, cell->config->name);
 
                        arm_smmu_write_s2cr(smmu, idx, S2CR_TYPE_TRANS, vmid);
 
                        smr[idx].id = sid;
-                       smr[idx].mask = smmu->arm_sid_mask;
+                       smr[idx].mask = smask;
                        smr[idx].valid = true;
 
                        arm_smmu_write_smr(smmu, idx);
@@ -449,14 +466,18 @@ static int arm_smmu_cell_init(struct cell *cell)
 }
 
 static bool arm_smmu_return_sid_to_root_cell(struct arm_smmu_device *smmu,
-                                            unsigned int sid, int idx)
+                                            union jailhouse_stream_id fsid,
+                                            int idx)
 {
-       unsigned int root_sid, n;
+       unsigned int n;
+       union jailhouse_stream_id rsid;
 
-       for_each_stream_id(root_sid, root_cell.config, n) {
-               if (sid == root_sid) {
-                       printk("Assigning StreamID 0x%x to cell \"%s\"\n",
-                              sid, root_cell.config->name);
+       for_each_mmu500_sid(rsid, root_cell.config, n) {
+               if (fsid.id == rsid.id) {
+                       printk("Assigning SID 0x%x Mask: 0x%x to cell \"%s\"\n",
+                              SMR_GET_ID(fsid.mmu500.id),
+                              SMR_GET_MASK(fsid.mmu500.mask),
+                              root_cell.config->name);
 
                        /* We just need to update S2CR, SMR can stay as is. */
                        arm_smmu_write_s2cr(smmu, idx, S2CR_TYPE_TRANS,
@@ -471,7 +492,9 @@ static void arm_smmu_cell_exit(struct cell *cell)
 {
        int id = cell->config->id;
        struct arm_smmu_device *smmu;
-       unsigned int dev, n, sid;
+       unsigned int dev, n;
+       u16 sid, smask;
+       union jailhouse_stream_id fsid;
        int idx;
 
        /* If no sids, ignore */
@@ -479,10 +502,16 @@ static void arm_smmu_cell_exit(struct cell *cell)
                return;
 
        for_each_smmu(smmu, dev) {
-               for_each_stream_id(sid, cell->config, n) {
-                       idx = arm_smmu_find_sme(sid, smmu);
-                       if (idx < 0 ||
-                           arm_smmu_return_sid_to_root_cell(smmu, sid, idx))
+               for_each_mmu500_sid(fsid, cell->config, n) {
+                       sid = SMR_GET_ID(fsid.mmu500.id);
+                       smask = SMR_GET_MASK(fsid.mmu500.mask);
+
+                       idx = arm_smmu_find_sme(sid, smask, smmu);
+                       if (idx < 0)
+                               continue;
+
+                       /* return full stream ids */
+                       if (arm_smmu_return_sid_to_root_cell(smmu, fsid, idx))
                                continue;
 
                        if (smmu->smrs) {
@@ -546,8 +575,6 @@ static int arm_smmu_init(void)
                        continue;
 
                smmu = &smmu_device[num_smmu_devices];
-               smmu->arm_sid_mask = iommu->arm_mmu500.sid_mask;
-
                smmu->base = paging_map_device(iommu->base, iommu->size);
                if (!smmu->base) {
                        err = -ENOMEM;
diff --git a/include/jailhouse/cell-config.h b/include/jailhouse/cell-config.h
index 472cb4bb..d7a15990 100644
--- a/include/jailhouse/cell-config.h
+++ b/include/jailhouse/cell-config.h
@@ -279,13 +279,19 @@ struct jailhouse_iommu {
                        __u64 tlb_base;
                        __u32 tlb_size;
                } __attribute__((packed)) tipvu;
-
-               struct {
-                       __u32 sid_mask;
-               } __attribute__((packed)) arm_mmu500;
        };
 } __attribute__((packed));
 
+union jailhouse_stream_id {
+       /* Note: keep first */
+       __u32 id;
+       struct {
+               /* Note: both mask and id are only 15 bits wide */
+               __u16 mask;
+               __u16 id;
+       } __attribute__((packed)) mmu500;
+} __attribute__((packed));
+
 struct jailhouse_pio {
        __u16 base;
        __u16 length;
@@ -427,6 +433,7 @@ jailhouse_cell_pci_caps(const struct jailhouse_cell_desc 
*cell)
 static inline const __u32 *
 jailhouse_cell_stream_ids(const struct jailhouse_cell_desc *cell)
 {
+       /* Note: deliver the __u32 full id */
        return (const __u32 *)((void *)jailhouse_cell_pci_caps(cell) +
                cell->num_pci_caps * sizeof(struct jailhouse_pci_capability));
 }
-- 
2.28.0

-- 
You received this message because you are subscribed to the Google Groups 
"Jailhouse" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/jailhouse-dev/20201109160100.103095-1-andrea.bastoni%40tum.de.

Reply via email to