A System Memory Management Unit(SMMU) performs a task analogous to a
CPU's MMU, translating addresses for device requests from system I/O
devices before the requests are passed into the system interconnect.

Implement a driver for SMMU v3 that maps and unmaps memory for specified
stream ids.

An emulated SMMU is presented to inmates by trapping access to the MMIO
registers to enable stage 1 translations.  Accesses to the SMMU memory
mapped registers are trapped and then routed to the emulated SMMU. This
is not emulation in the sense that we fully emulate the device top to
bottom. The emulation is used to provide an interface to the SMMU that
the hypervisor can control to make sure the inmates are not doing
anything they should not. The actual translations are done by hardware.

Emulation is needed because both stage 1 and stage 2 parameters are
configured in a single data structure, the stream table entry. For this
reason, the inmates can't be allowed to directly control the stream
table entries, and by extension, the stream table.

The guest cells are assigned stream IDs in their configs and only those
assigned stream IDs can be used by the cells. There is no checking in
place to make sure two cells do not use the same stream IDs. This must
be taken care of when creating the cell configs.

This driver is implemented based on the following assumptions:
- Running on a Little endian 64 bit core compatible with ARM v8
  architecture.
- SMMU supporting only AARCH64 mode.
- SMMU AARCH 64 stage 2 translation configurations are compatible with
  ARMv8 VMSA. So re-using the translation tables of CPU for SMMU.

Work left to do:
- Route event notifications to the correct cell and identify which event
  needs to go to which cell.
- Add support for IRQ and MSI routing.
- Add support for PRI queues and ATS.
- Identify which cell caused a command queue error and notify it.
- Support sub-streams.

A lot of the work left is optional features that the SMMU provides like
substreams, ATS, PRI. There is little reason to add them unless there is
a use case for them.

This driver is loosely based on the Linux kernel SMMU v3 driver.

Signed-off-by: Pratyush Yadav <[email protected]>
Signed-off-by: Lokesh Vutla <[email protected]>
---
 hypervisor/arch/arm-common/include/asm/cell.h |    2 +
 hypervisor/arch/arm64/Kbuild                  |    2 +-
 hypervisor/arch/arm64/smmu-v3.c               | 1983 +++++++++++++++++
 hypervisor/include/jailhouse/entry.h          |    1 +
 4 files changed, 1987 insertions(+), 1 deletion(-)
 create mode 100644 hypervisor/arch/arm64/smmu-v3.c

diff --git a/hypervisor/arch/arm-common/include/asm/cell.h 
b/hypervisor/arch/arm-common/include/asm/cell.h
index 5b1e4207..f60f2a14 100644
--- a/hypervisor/arch/arm-common/include/asm/cell.h
+++ b/hypervisor/arch/arm-common/include/asm/cell.h
@@ -19,6 +19,8 @@ struct arch_cell {
        struct paging_structures mm;
 
        u32 irq_bitmap[1024/32];
+
+       struct arm_smmu_state *smmu_states;
 };
 
 #endif /* !_JAILHOUSE_ASM_CELL_H */
diff --git a/hypervisor/arch/arm64/Kbuild b/hypervisor/arch/arm64/Kbuild
index 7283a008..323b78b6 100644
--- a/hypervisor/arch/arm64/Kbuild
+++ b/hypervisor/arch/arm64/Kbuild
@@ -20,4 +20,4 @@ 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
+lib-y += entry.o setup.o control.o mmio.o paging.o caches.o traps.o smmu-v3.o
diff --git a/hypervisor/arch/arm64/smmu-v3.c b/hypervisor/arch/arm64/smmu-v3.c
new file mode 100644
index 00000000..f70a40b4
--- /dev/null
+++ b/hypervisor/arch/arm64/smmu-v3.c
@@ -0,0 +1,1983 @@
+/*
+ * Jailhouse AArch64 support
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Authors:
+ *  Lokesh Vutla <[email protected]>
+ *  Pratyush Yadav <[email protected]>
+ *
+ * An emulated SMMU is presented to inmates by trapping access to MMIO
+ * registers to enable stage 1 translations. Accesses to the SMMU memory mapped
+ * registers are trapped and then routed to the emulated SMMU. This is not
+ * emulation in the sense that we fully emulate the device top to bottom. The
+ * emulation is used to provied an interface to the SMMU that the hypervisor
+ * can control to make sure the inmates are not doing anything they should not.
+ * The actual translations are done by hardware.
+ *
+ * Emulation is needed because both stage 1 and stage 2 parameters are
+ * configured in a single data structure, the stream table entry. For this
+ * reason, the inmates can't be allowed to directly control the stream table
+ * entries, and by extension, the stream table.
+ *
+ * The guest cells are assigned stream IDs in their configs and only those
+ * assigned stream IDs can be used by the cells. There is no checking in place
+ * to make sure two cells do not use the same stream IDs. This must be taken
+ * care of when creating the cell configs.
+ *
+ * This driver is implemented based on the following assumptions:
+ * - Running on a Little endian 64 bit core compatible with ARM v8 
architecture.
+ * - SMMU supporting only AARCH64 mode.
+ * - SMMU AARCH 64 stage 2 translation configurations are compatible with ARMv8
+ *   VMSA. So re using the translation tables of CPU for SMMU.
+ *
+ * This driver is loosely based on the Linux kernel SMMU v3 driver.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See the
+ * COPYING file in the top-level directory.
+ */
+
+#include <jailhouse/control.h>
+#include <jailhouse/paging.h>
+#include <jailhouse/printk.h>
+#include <jailhouse/string.h>
+#include <asm/control.h>
+#include <jailhouse/unit.h>
+#include <asm/iommu.h>
+#include <jailhouse/cell.h>
+#include <jailhouse/mmio.h>
+
+/* Offset of addr from start of the page. */
+#define PAGE_OFFSET(addr)              ((addr) & PAGE_OFFS_MASK)
+
+#define LOWER_32_BITS(n)               ((u32)(n))
+#define UPPER_32_BITS(n)               ((n) >> 32)
+
+/* MMIO registers */
+#define ARM_SMMU_IDR0                  0x0
+#define IDR0_ST_LVL                    BIT_MASK(28, 27)
+#define IDR0_TTENDIAN                  BIT_MASK(22, 21)
+#define IDR0_VATOS                     (1 << 20)
+#define IDR0_PRI                       (1 << 16)
+#define IDR0_ATOS                      (1 << 15)
+#define IDR0_MSI                       (1 << 13)
+#define IDR0_ASID16                    (1 << 12)
+#define IDR0_NS1ATS                    (1 << 11)
+#define IDR0_ATS                       (1 << 10)
+#define IDR0_S2P                       (1 << 0)
+#define IDR0_S1P                       (1 << 1)
+#define IDR0_HTTU                      BIT_MASK(7, 6)
+#define IDR0_COHACC                    (1 << 4)
+#define IDR0_TTF                       BIT_MASK(3, 2)
+
+#define IDR0_TTF_AARCH64               2
+#define IDR0_TTENDIAN_LE               2
+#define IDR0_ST_LVL_2LVL               1
+
+#define ARM_SMMU_IDR1                  0x4
+#define IDR1_TABLES_PRESET             (1 << 30)
+#define IDR1_QUEUES_PRESET             (1 << 29)
+#define IDR1_REL                       (1 << 28)
+#define IDR1_CMDQS                     BIT_MASK(25, 21)
+#define IDR1_EVTQS                     BIT_MASK(20, 16)
+#define IDR1_SSIDSIZE                  BIT_MASK(10, 6)
+#define IDR1_SIDSIZE                   BIT_MASK(5, 0)
+
+#define ARM_SMMU_IDR2                  0x8
+#define ARM_SMMU_IDR3                  0xC
+#define ARM_SMMU_IDR4                  0x10
+#define ARM_SMMU_IDR5                  0x14
+
+#define ARM_SMMU_CR0                   0x20
+#define CR0_CMDQEN                     (1 << 3)
+#define CR0_EVTQEN                     (1 << 2)
+#define CR0_SMMUEN                     (1 << 0)
+
+#define ARM_SMMU_CR0ACK                        0x24
+
+#define ARM_SMMU_CR1                   0x28
+#define CR1_TABLE_SH                   BIT_MASK(11, 10)
+#define CR1_TABLE_OC                   BIT_MASK(9, 8)
+#define CR1_TABLE_IC                   BIT_MASK(7, 6)
+#define CR1_QUEUE_SH                   BIT_MASK(5, 4)
+#define CR1_QUEUE_OC                   BIT_MASK(3, 2)
+#define CR1_QUEUE_IC                   BIT_MASK(1, 0)
+/* CR1 cacheability fields don't quite follow the usual TCR-style encoding */
+#define CR1_CACHE_NC                   0
+#define CR1_CACHE_WB                   1
+#define CR1_CACHE_WT                   2
+
+#define ARM_SMMU_CR2                   0x2c
+#define CR2_PTM                                (1 << 2)
+#define CR2_RECINVSID                  (1 << 1)
+#define CR2_E2H                                (1 << 0)
+
+#define ARM_SMMU_STRTAB_BASE           0x80
+#define STRTAB_BASE_RA                 (1UL << 62)
+#define STRTAB_BASE_ADDR_MASK          BIT_MASK(51, 6)
+
+#define ARM_SMMU_STRTAB_BASE_CFG       0x88
+#define STRTAB_BASE_CFG_FMT            BIT_MASK(17, 16)
+#define STRTAB_BASE_CFG_FMT_LINEAR     0
+#define STRTAB_BASE_CFG_FMT_2LVL       1
+#define STRTAB_BASE_CFG_SPLIT          BIT_MASK(10, 6)
+#define STRTAB_BASE_CFG_LOG2SIZE       BIT_MASK(5, 0)
+
+#define ARM_SMMU_CMDQ_BASE             0x90
+#define ARM_SMMU_CMDQ_PROD             0x98
+#define ARM_SMMU_CMDQ_CONS             0x9c
+
+#define ARM_SMMU_EVTQ_BASE             0xa0
+#define ARM_SMMU_EVTQ_PROD             0x100a8
+#define ARM_SMMU_EVTQ_CONS             0x100ac
+#define ARM_SMMU_EVTQ_IRQ_CFG0         0xb0
+#define ARM_SMMU_EVTQ_IRQ_CFG1         0xb8
+#define ARM_SMMU_EVTQ_IRQ_CFG2         0xbc
+
+#define ARM_SMMU_GERROR                        0x60
+#define GERROR_CMDQ_ERR                        (1 << 0)
+
+#define ARM_SMMU_GERRORN               0x64
+#define ARM_SMMU_IRQ_CTRL              0x50
+#define ARM_SMMU_IRQ_CTRLACK           0x54
+#define ARM_SMMU_GERROR_IRQ_CFG0       0x68
+#define ARM_SMMU_EVTQ_IRQ_CFG0         0xb0
+
+/* Common memory attribute values */
+#define ARM_SMMU_SH_NSH                        0
+#define ARM_SMMU_SH_OSH                        2
+#define ARM_SMMU_SH_ISH                        3
+#define ARM_SMMU_MEMATTR_DEVICE_nGnRE  0x1
+#define ARM_SMMU_MEMATTR_OIWB          0xf
+
+#define Q_IDX(reg, shift)              ((reg) & ((1 << (shift)) - 1))
+#define Q_WRP(reg, shift)              ((reg) & (1 << (shift)))
+#define Q_OVERFLOW_FLAG                        (1 << 31)
+#define Q_OVF(reg)                     ((reg) & Q_OVERFLOW_FLAG)
+#define Q_EMPTY(prod, cons, shift)     \
+                       (Q_IDX((prod), (shift)) == Q_IDX((cons), (shift)) && \
+                        Q_WRP((prod), (shift)) == Q_WRP((cons), (shift)))
+#define Q_FULL(prod, cons, shift)      \
+                       (Q_IDX((prod), (shift)) == Q_IDX((cons), (shift)) && \
+                        Q_WRP((prod), (shift)) != Q_WRP((cons), (shift)))
+
+#define Q_BASE_RWA                     (1UL << 62)
+#define Q_BASE_ADDR_MASK               BIT_MASK(51, 5)
+#define Q_BASE_LOG2SIZE                        BIT_MASK(4, 0)
+
+/*
+ * Stream table.
+ *
+ * Linear: Enough to cover 1 << IDR1.SIDSIZE entries
+ * 2lvl: 128k L1 entries,
+ *       256 lazy entries per table (each table covers a PCI bus)
+ */
+#define STRTAB_L1_SZ_SHIFT             20
+#define STRTAB_SPLIT                   8
+
+#define STRTAB_L1_DESC_DWORDS          1
+#define STRTAB_L1_DESC_SIZE            (STRTAB_L1_DESC_DWORDS << 3)
+#define STRTAB_L1_DESC_SPAN            BIT_MASK(4, 0)
+#define STRTAB_L1_DESC_L2PTR_MASK      BIT_MASK(51, 6)
+
+#define STRTAB_STE_DWORDS              8
+#define STRTAB_STE_DWORDS_BITS         3
+#define STRTAB_STE_SIZE                        (STRTAB_STE_DWORDS << 3)
+#define STRTAB_STE_0_V                 (1UL << 0)
+#define STRTAB_STE_0_CFG               BIT_MASK(3, 1)
+#define STRTAB_STE_0_CFG_ABORT         0
+#define STRTAB_STE_0_CFG_BYPASS                4
+#define STRTAB_STE_0_CFG_S1_TRANS      5
+#define STRTAB_STE_0_CFG_S2_TRANS      6
+#define STRTAB_STE_0_S1CTXPTR          BIT_MASK(51, 6)
+#define STRTAB_STE_0_S1CDMAX           BIT_MASK(63, 59)
+#define STRTAB_STE_1_S1DSS             BIT_MASK(1, 0)
+#define STRTAB_STE_1_S1CIR             BIT_MASK(3, 2)
+#define STRTAB_STE_1_S1COR             BIT_MASK(5, 4)
+#define STRTAB_STE_1_S1CSH             BIT_MASK(7, 6)
+#define STRTAB_STE_1_S1STALLD          (1UL << 27)
+#define STRTAB_CTXDESC_DWORDS          8
+#define STRTAB_CTXDESC_S1CTXPTR_SHIFT  6
+
+#define STRTAB_STE_1_SHCFG             BIT_MASK(45, 44)
+#define STRTAB_STE_1_SHCFG_INCOMING    1UL
+
+#define STRTAB_STE_2_S2VMID            BIT_MASK(15, 0)
+#define STRTAB_STE_2_VTCR              BIT_MASK(50, 32)
+#define STRTAB_STE_2_S2AA64            (1UL << 51)
+#define STRTAB_STE_2_S2ENDI            (1UL << 52)
+#define STRTAB_STE_2_S2PTW             (1UL << 54)
+#define STRTAB_STE_2_S2R               (1UL << 58)
+
+#define STRTAB_STE_3_S2TTB_MASK                BIT_MASK(51, 4)
+
+#define CTXDESC_1_TTB0                 BIT_MASK(51, 4)
+#define CTXDESC_2_TTB1                 BIT_MASK(51, 4)
+#define CTXDESC_TTB0_SHIFT             4
+#define CTXDESC_TTB1_SHIFT             4
+
+/* Command queue */
+#define CMDQ_ENT_DWORDS                        2
+#define CMDQ_ENT_SIZE                  (CMDQ_ENT_DWORDS << 3)
+#define CMDQ_MAX_SZ_SHIFT              8
+
+#define CMDQ_CONS_ERR                  BIT_MASK(30, 24)
+#define CMDQ_ERR_CERROR_NONE_IDX       0
+#define CMDQ_ERR_CERROR_ILL_IDX                1
+#define CMDQ_ERR_CERROR_ABT_IDX                2
+
+#define CMDQ_0_OP                      BIT_MASK(7, 0)
+#define CMDQ_0_SSV                     (1UL << 11)
+
+#define CMDQ_PREFETCH_0_SSID           BIT_MASK(31, 12)
+#define CMDQ_PREFETCH_0_SID            BIT_MASK(63, 32)
+#define CMDQ_PREFETCH_1_SIZE           BIT_MASK(4, 0)
+#define CMDQ_PREFETCH_1_ADDR_MASK      BIT_MASK(63, 12)
+
+#define CMDQ_CFGI_0_SID                        BIT_MASK(63, 32)
+#define CMDQ_CFGI_1_LEAF               (1UL << 0)
+#define CMDQ_CFGI_1_RANGE              BIT_MASK(4, 0)
+
+#define CMDQ_TLBI_0_VMID               BIT_MASK(47, 32)
+#define CMDQ_TLBI_0_ASID               BIT_MASK(63, 48)
+#define CMDQ_TLBI_1_LEAF               (1UL << 0)
+#define CMDQ_TLBI_1_VA_MASK            BIT_MASK(63, 12)
+#define CMDQ_TLBI_1_IPA_MASK           BIT_MASK(51, 12)
+
+#define CMDQ_PRI_0_SSID                        BIT_MASK(31, 12)
+#define CMDQ_PRI_0_SID                 BIT_MASK(63, 32)
+#define CMDQ_PRI_1_GRPID               BIT_MASK(8, 0)
+#define CMDQ_PRI_1_RESP                        BIT_MASK(13, 12)
+
+#define CMDQ_SYNC_0_CS                 BIT_MASK(13, 12)
+#define CMDQ_SYNC_0_CS_NONE            0
+#define CMDQ_SYNC_0_CS_IRQ             1
+#define CMDQ_SYNC_0_CS_SEV             2
+#define CMDQ_SYNC_0_MSH                        BIT_MASK(23, 22)
+#define CMDQ_SYNC_0_MSIATTR            BIT_MASK(27, 24)
+#define CMDQ_SYNC_0_MSIDATA            BIT_MASK(63, 32)
+#define CMDQ_SYNC_1_MSIADDR_MASK       BIT_MASK(51, 2)
+
+/* Event queue */
+#define EVTQ_ENT_DWORDS                        4
+#define EVTQ_MAX_SZ_SHIFT              7
+
+#define EVTQ_0_ID                      BIT_MASK(7, 0)
+
+#define ARM_SMMU_SYNC_TIMEOUT          1000000
+
+#define FIELD_PREP(mask, val)  \
+                       (((u64)(val) << (__builtin_ffsl((mask)) - 1)) & (mask))
+#define FIELD_GET(mask, reg)   \
+                       (((reg) & (mask)) >> (__builtin_ffsl((mask)) - 1))
+#define FIELD_CLEAR(mask, reg) \
+                       ((reg) & (~(mask)))
+
+#define CMDQ_OP_PREFETCH_CFG   0x1
+#define CMDQ_OP_PREFETCH_ADDR  0x2
+#define CMDQ_OP_CFGI_STE       0x3
+#define CMDQ_OP_CFGI_ALL       0x4
+#define CMDQ_OP_TLBI_NH_ASID   0x11
+#define CMDQ_OP_TLBI_NH_VA     0x12
+#define CMDQ_OP_TLBI_EL2_ALL   0x20
+#define CMDQ_OP_TLBI_S12_VMALL 0x28
+#define CMDQ_OP_TLBI_S2_IPA    0x2a
+#define CMDQ_OP_TLBI_NSNH_ALL  0x30
+#define CMDQ_OP_CMD_SYNC       0x46
+#define ARM_SMMU_FEAT_2_LVL_STRTAB     (1 << 0)
+
+/* High-level queue structures */
+struct arm_smmu_cmdq_ent {
+       /* Common fields */
+       u8                              opcode;
+       bool                            substream_valid;
+
+       /* Command-specific fields */
+       union {
+               struct {
+                       u32                     sid;
+                       u8                      size;
+                       u64                     addr;
+               } prefetch;
+
+               struct {
+                       u32                     sid;
+                       union {
+                               bool            leaf;
+                               u8              span;
+                       };
+               } cfgi;
+
+               struct {
+                       u16                     asid;
+                       u16                     vmid;
+                       bool                    leaf;
+                       u64                     addr;
+               } tlbi;
+
+               struct {
+                       u32                     msidata;
+                       u64                     msiaddr;
+               } sync;
+       };
+};
+
+struct arm_smmu_queue {
+       u64     *base;
+       u64     base_dma;
+       u64     q_base;
+       u64     ent_dwords;
+       u32     max_n_shift;
+       u32     prod;
+       u32     cons;
+       u32     *prod_reg;
+       u32     *cons_reg;
+};
+
+struct arm_smmu_cmdq {
+       struct arm_smmu_queue           q;
+       spinlock_t                      lock;
+};
+
+struct arm_smmu_evtq {
+       struct arm_smmu_queue           q;
+};
+
+/* High-level stream table structures */
+struct arm_smmu_strtab_l1_desc {
+       u8      span;
+       __u64   *l2ptr;
+       u64     l2ptr_dma;
+       u32     active_stes;
+};
+
+struct arm_smmu_strtab_cfg {
+       __u64                           *strtab;
+       u64                             strtab_dma;
+       struct arm_smmu_strtab_l1_desc  *l1_desc;
+       unsigned int                    num_l1_ents;
+       u64                             strtab_base;
+       u32                             strtab_base_cfg;
+};
+
+struct arm_smmu_state {
+       struct arm_smmu_device          *smmu;
+       u32                             idr[6];
+       u32                             cr[3];
+       u32                             cr0ack;
+       u32                             strtab_base_cfg;
+       u64                             strtab_base;
+       u64                             cmdq_base;
+       u32                             cmdq_prod;
+       u32                             cmdq_cons;
+       u64                             evtq_base;
+       u32                             evtq_prod;
+       u32                             evtq_cons;
+       u32                             gerror;
+       u32                             gerrorn;
+       u32                             irq_ctrl;
+       u32                             irq_ctrlack;
+       u64                             gerror_irq_cfg0;
+       u64                             evtq_irq_cfg0;
+       /* Not all registers included. Add more as needed. */
+};
+
+/* An SMMUv3 instance */
+struct arm_smmu_device {
+       void                            *base;
+       u32                             features;
+       struct arm_smmu_cmdq            cmdq;
+       struct arm_smmu_evtq            evtq;
+       unsigned int                    sid_bits;
+       struct arm_smmu_strtab_cfg      strtab_cfg;
+       struct arm_smmu_state           state;
+} smmu[JAILHOUSE_MAX_IOMMU_UNITS];
+
+struct arm_smmu_state state_dump[JAILHOUSE_MAX_IOMMU_UNITS];
+
+/* Low-level queue manipulation functions */
+static bool queue_full(struct arm_smmu_queue *q)
+{
+       u32 shift = q->max_n_shift;
+
+       return Q_FULL(q->prod, q->cons, shift);
+}
+
+static bool queue_empty(struct arm_smmu_queue *q)
+{
+       u32 shift = q->max_n_shift;
+
+       return Q_EMPTY(q->prod, q->cons, shift);
+}
+
+static void queue_sync_cons(struct arm_smmu_queue *q)
+{
+       q->cons = mmio_read32(q->cons_reg);
+}
+
+static bool queue_error(struct arm_smmu_queue *q)
+{
+       return mmio_read32(q->cons_reg) & 0x1;
+}
+
+static void queue_inc_prod(struct arm_smmu_queue *q)
+{
+       u32 shift = q->max_n_shift;
+       u32 prod = (Q_WRP(q->prod, shift) | Q_IDX(q->prod, shift)) + 1;
+
+       q->prod = Q_OVF(q->prod) | Q_WRP(prod, shift) | Q_IDX(prod, shift);
+       mmio_write32(q->prod_reg, q->prod);
+}
+
+static void queue_write(__u64 *dst, __u64 *src, u32 n_dwords)
+{
+       int i;
+
+       for (i = 0; i < n_dwords; ++i)
+               *dst++ = *src++;
+       dsb(ish);
+}
+
+static __u64 *queue_entry(struct arm_smmu_queue *q)
+{
+       return q->base + (Q_IDX(q->prod, q->max_n_shift) * q->ent_dwords);
+}
+
+static int queue_insert_raw(struct arm_smmu_queue *q, __u64 *ent)
+{
+       while (queue_full(q))
+       {}
+
+       queue_write(queue_entry(q), ent, q->ent_dwords);
+       queue_inc_prod(q);
+       while (!queue_empty(q) && !queue_error(q)) {
+               queue_sync_cons(q);
+       }
+       return 0;
+}
+
+/* High-level queue accessors */
+static int arm_smmu_cmdq_build_cmd(__u64 *cmd, struct arm_smmu_cmdq_ent *ent)
+{
+       u64 vmid = (u64)this_cell()->config->id;
+       memset(cmd, 0, CMDQ_ENT_SIZE);
+       cmd[0] |= FIELD_PREP(CMDQ_0_OP, ent->opcode);
+
+       switch (ent->opcode) {
+       case CMDQ_OP_TLBI_EL2_ALL:
+       case CMDQ_OP_TLBI_NSNH_ALL:
+               break;
+       case CMDQ_OP_PREFETCH_ADDR:
+               cmd[1] |= FIELD_PREP(CMDQ_PREFETCH_1_SIZE, ent->prefetch.size);
+               cmd[1] |= ent->prefetch.addr & CMDQ_PREFETCH_1_ADDR_MASK;
+               /* Fallthrough */
+       case CMDQ_OP_PREFETCH_CFG:
+               cmd[0] |= FIELD_PREP(CMDQ_PREFETCH_0_SID, ent->prefetch.sid);
+               break;
+       case CMDQ_OP_CFGI_STE:
+               cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SID, ent->cfgi.sid);
+               cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_LEAF, ent->cfgi.leaf);
+               break;
+       case CMDQ_OP_CFGI_ALL:
+               /* Cover the entire SID range */
+               cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_RANGE, 31);
+               break;
+       case CMDQ_OP_TLBI_NH_VA:
+               cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid);
+               cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid);
+               cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf);
+               cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_VA_MASK;
+               break;
+       case CMDQ_OP_TLBI_S2_IPA:
+               cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, vmid);
+               cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf);
+               cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_IPA_MASK;
+               break;
+       case CMDQ_OP_TLBI_NH_ASID:
+               cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid);
+               /* Fallthrough */
+       case CMDQ_OP_TLBI_S12_VMALL:
+               cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, vmid);
+               break;
+       case CMDQ_OP_CMD_SYNC:
+               if (ent->sync.msiaddr)
+                       cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, 
CMDQ_SYNC_0_CS_IRQ);
+               else
+                       cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, 
CMDQ_SYNC_0_CS_SEV);
+               cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSH, ARM_SMMU_SH_ISH);
+               cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSIATTR, 
ARM_SMMU_MEMATTR_OIWB);
+               /*
+                * Commands are written little-endian, but we want the SMMU to
+                * receive MSIData, and thus write it back to memory, in CPU
+                * byte order, so big-endian needs an extra byteswap here.
+                */
+               cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSIDATA, ent->sync.msidata);
+               cmd[1] |= ent->sync.msiaddr & CMDQ_SYNC_1_MSIADDR_MASK;
+               break;
+       default:
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
+static void arm_smmu_cmdq_insert_cmd(struct arm_smmu_device *smmu, __u64 *cmd)
+{
+       struct arm_smmu_queue *q = &smmu->cmdq.q;
+
+       queue_insert_raw(q, cmd);
+}
+
+static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
+                                   struct arm_smmu_cmdq_ent *ent)
+{
+       u64 cmd[CMDQ_ENT_DWORDS];
+
+       if (arm_smmu_cmdq_build_cmd(cmd, ent)) {
+               printk("WARN: SMMU ignoring unknown CMDQ opcode 0x%x\n",
+                        ent->opcode);
+               return;
+       }
+
+       spin_lock(&smmu->cmdq.lock);
+       arm_smmu_cmdq_insert_cmd(smmu, cmd);
+       spin_unlock(&smmu->cmdq.lock);
+}
+
+static void arm_smmu_cmdq_issue_sync(struct arm_smmu_device *smmu)
+{
+       struct arm_smmu_cmdq_ent ent = { .opcode = CMDQ_OP_CMD_SYNC };
+       u64 cmd[CMDQ_ENT_DWORDS];
+
+       arm_smmu_cmdq_build_cmd(cmd, &ent);
+
+       spin_lock(&smmu->cmdq.lock);
+       arm_smmu_cmdq_insert_cmd(smmu, cmd);
+       spin_unlock(&smmu->cmdq.lock);
+}
+
+/* Stream table manipulation functions */
+static void
+arm_smmu_write_strtab_l1_desc(__u64 *dst, struct arm_smmu_strtab_l1_desc *desc)
+{
+       u64 val = 0;
+
+       val |= FIELD_PREP(STRTAB_L1_DESC_SPAN, desc->span);
+       val |= desc->l2ptr_dma & STRTAB_L1_DESC_L2PTR_MASK;
+
+       /* Assuming running on Little endian cpu */
+       *dst = val;
+       dsb(ish);
+}
+
+static void arm_smmu_sync_ste_for_sid(struct arm_smmu_device *smmu, u32 sid)
+{
+       struct arm_smmu_cmdq_ent cmd = {
+               .opcode = CMDQ_OP_CFGI_STE,
+               .cfgi   = {
+                       .sid    = sid,
+                       .leaf   = true,
+               },
+       };
+
+       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+       arm_smmu_cmdq_issue_sync(smmu);
+}
+
+static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
+                                     __u64 *guest_ste, __u64 *dst,
+                                     bool bypass, u32 vmid)
+{
+       struct paging_structures *pg_structs = &this_cell()->arch.mm;
+       u64 val = dst[0], vttbr, mask;
+
+       /* Bypass */
+       if (bypass) {
+               dst[0] = 0;
+               dsb(ish);
+               val = FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS);
+               dst[0] = val;
+               dst[1] = FIELD_PREP(STRTAB_STE_1_SHCFG,
+                                   STRTAB_STE_1_SHCFG_INCOMING);
+               dst[2] = vmid;
+               dsb(ish);
+               return;
+       }
+
+       if (FIELD_GET(STRTAB_STE_0_S1CDMAX, guest_ste[0]) != 0) {
+               printk("WARN: SMMU sub-streams not supported\n");
+       }
+
+       if (guest_ste != NULL) {
+               dst[0] = guest_ste[0];
+
+               mask = STRTAB_STE_1_S1COR | STRTAB_STE_1_S1DSS |
+                      STRTAB_STE_1_S1CSH | STRTAB_STE_1_S1CIR;
+               dst[1] = guest_ste[1] & mask;
+       }
+
+       dst[2] = FIELD_PREP(STRTAB_STE_2_S2VMID, vmid) |
+                FIELD_PREP(STRTAB_STE_2_VTCR, VTCR_CELL) |
+                STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2AA64 |
+                STRTAB_STE_2_S2R;
+
+       vttbr = paging_hvirt2phys(pg_structs->root_table);
+       dst[3] = vttbr & STRTAB_STE_3_S2TTB_MASK;
+
+       dst[0] |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S2_TRANS);
+       dsb(ish);
+
+       arm_smmu_sync_ste_for_sid(smmu, sid);
+}
+
+static void arm_smmu_init_bypass_stes(u64 *strtab, unsigned int nent)
+{
+       unsigned int i;
+
+       for (i = 0; i < nent; ++i) {
+               arm_smmu_write_strtab_ent(NULL, -1, NULL, strtab, true,
+                                         (u32)this_cell()->config->id);
+               strtab += STRTAB_STE_DWORDS;
+       }
+}
+
+static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu)
+{
+       void *strtab;
+       u64 reg;
+       u32 size;
+       struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+
+       size = (1 << smmu->sid_bits) * STRTAB_STE_SIZE;
+       strtab = page_alloc_aligned(&mem_pool, PAGES(size));
+       if (!strtab) {
+               printk("ERROR: SMMU failed to allocate l1 stream table (%u 
bytes)\n",
+                      size);
+               return -ENOMEM;
+       }
+       cfg->strtab_dma = paging_hvirt2phys(strtab);
+       cfg->strtab = strtab;
+       cfg->num_l1_ents = 1 << smmu->sid_bits;
+
+       /* Configure strtab_base_cfg for a linear table covering all SIDs */
+       reg  = FIELD_PREP(STRTAB_BASE_CFG_FMT, STRTAB_BASE_CFG_FMT_LINEAR);
+       reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, smmu->sid_bits);
+       cfg->strtab_base_cfg = reg;
+
+       arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents);
+       return 0;
+}
+
+static int arm_smmu_init_l1_strtab(struct arm_smmu_device *smmu)
+{
+       struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+       u32 size = sizeof(*cfg->l1_desc) * cfg->num_l1_ents;
+       void *strtab = smmu->strtab_cfg.strtab;
+       unsigned int i;
+
+       cfg->l1_desc = page_alloc(&mem_pool, PAGES(size));
+       if (!cfg->l1_desc) {
+               printk("ERROR: SMMU failed to allocate l1 stream table desc\n");
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < cfg->num_l1_ents; ++i) {
+               memset(&cfg->l1_desc[i], 0, sizeof(*cfg->l1_desc));
+               arm_smmu_write_strtab_l1_desc(strtab, &cfg->l1_desc[i]);
+               cfg->l1_desc[i].active_stes = 0;
+               strtab += STRTAB_L1_DESC_SIZE;
+       }
+
+       return 0;
+}
+
+static int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu)
+{
+       struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+       u32 size, l1size;
+       void *strtab;
+       u64 reg;
+       int ret;
+
+       /* Calculate the L1 size, capped to the SIDSIZE. */
+       size = STRTAB_L1_SZ_SHIFT - 3;
+       size = MIN(size, smmu->sid_bits - STRTAB_SPLIT);
+       cfg->num_l1_ents = 1 << size;
+
+       size += STRTAB_SPLIT;
+       if (size < smmu->sid_bits)
+               printk("WARN: SMMU 2-level strtab only covers %u/%u bits of 
SID\n",
+                      size, smmu->sid_bits);
+
+       l1size = cfg->num_l1_ents * STRTAB_L1_DESC_SIZE;
+       strtab = page_alloc_aligned(&mem_pool, PAGES(l1size));
+       if (!strtab) {
+               printk("ERROR: SMMU failed to allocate l1 stream table (%u 
bytes)\n",
+                      size);
+               return -ENOMEM;
+       }
+       cfg->strtab_dma = paging_hvirt2phys(strtab);
+       cfg->strtab = strtab;
+
+       /* Configure strtab_base_cfg for 2 levels */
+       reg  = FIELD_PREP(STRTAB_BASE_CFG_FMT, STRTAB_BASE_CFG_FMT_2LVL);
+       reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, size);
+       reg |= FIELD_PREP(STRTAB_BASE_CFG_SPLIT, STRTAB_SPLIT);
+       cfg->strtab_base_cfg = reg;
+
+       ret = arm_smmu_init_l1_strtab(smmu);
+
+       if (ret) {
+               page_free(&mem_pool, strtab, PAGES(l1size));
+               return ret;
+       }
+
+       return 0;
+}
+
+static int arm_smmu_init_strtab(struct arm_smmu_device *smmu)
+{
+       u64 reg;
+       int ret;
+
+       if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)
+               ret = arm_smmu_init_strtab_2lvl(smmu);
+       else
+               ret = arm_smmu_init_strtab_linear(smmu);
+
+       if (ret)
+               return ret;
+
+       /* Set the strtab base address */
+       reg  = smmu->strtab_cfg.strtab_dma & STRTAB_BASE_ADDR_MASK;
+       reg |= STRTAB_BASE_RA;
+       smmu->strtab_cfg.strtab_base = reg;
+
+       return 0;
+}
+
+static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
+                                  struct arm_smmu_queue *q,
+                                  unsigned long prod_off,
+                                  unsigned long cons_off,
+                                  unsigned long dwords)
+{
+       /* Queue size is capped to 4K. So allocate 1 page */
+       q->base = page_alloc(&mem_pool, 1);
+       if (!q->base) {
+               printk("ERROR: SMMU failed to allocate queue\n");
+               return -ENOMEM;
+       }
+       q->base_dma = paging_hvirt2phys(q->base);;
+
+       q->prod_reg     = smmu->base + prod_off;
+       q->cons_reg     = smmu->base + cons_off;
+       q->ent_dwords   = dwords;
+
+       q->q_base  = Q_BASE_RWA;
+       q->q_base |= q->base_dma & Q_BASE_ADDR_MASK;
+       q->q_base |= FIELD_PREP(Q_BASE_LOG2SIZE, q->max_n_shift);
+
+       q->prod = q->cons = 0;
+       return 0;
+}
+
+static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
+{
+       int ret;
+
+       /* cmdq */
+       ret = arm_smmu_init_one_queue(smmu, &smmu->cmdq.q, ARM_SMMU_CMDQ_PROD,
+                                     ARM_SMMU_CMDQ_CONS, CMDQ_ENT_DWORDS);
+       if (ret)
+               return ret;
+
+       /* evtq */
+       ret = arm_smmu_init_one_queue(smmu, &smmu->evtq.q, ARM_SMMU_EVTQ_PROD,
+                                     ARM_SMMU_EVTQ_CONS, EVTQ_ENT_DWORDS);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
+static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
+{
+       int ret;
+
+       ret = arm_smmu_init_queues(smmu);
+       if (ret)
+               return ret;
+
+       return arm_smmu_init_strtab(smmu);
+}
+
+static int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val,
+                                  unsigned int reg_off, unsigned int ack_off)
+{
+       u32 i, timeout = ARM_SMMU_SYNC_TIMEOUT;
+
+       mmio_write32(smmu->base + reg_off, val);
+       for (i = 0; i < timeout; i++) {
+               if (mmio_read32(smmu->base + ack_off) == val)
+                       return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int arm_smmu_device_disable(struct arm_smmu_device *smmu)
+{
+       int ret;
+
+       ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_CR0, ARM_SMMU_CR0ACK);
+       if (ret)
+               printk("ERROR: SMMU failed to clear cr0\n");
+
+       return ret;
+}
+
+static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
+{
+       int ret;
+       u32 reg, enables;
+       struct arm_smmu_cmdq_ent cmd;
+
+       /* Clear CR0 and sync (disables SMMU and queue processing) */
+       reg = mmio_read32(smmu->base + ARM_SMMU_CR0);
+       if (reg & CR0_SMMUEN)
+               printk("ERROR: SMMU currently enabled! Resetting...\n");
+
+       ret = arm_smmu_device_disable(smmu);
+       if (ret)
+               return ret;
+
+       /* CR1 (table and queue memory attributes) */
+       reg = FIELD_PREP(CR1_TABLE_SH, ARM_SMMU_SH_ISH) |
+             FIELD_PREP(CR1_TABLE_OC, CR1_CACHE_WB) |
+             FIELD_PREP(CR1_TABLE_IC, CR1_CACHE_WB) |
+             FIELD_PREP(CR1_QUEUE_SH, ARM_SMMU_SH_ISH) |
+             FIELD_PREP(CR1_QUEUE_OC, CR1_CACHE_WB) |
+             FIELD_PREP(CR1_QUEUE_IC, CR1_CACHE_WB);
+       mmio_write32(smmu->base + ARM_SMMU_CR1, reg);
+
+       /* Stream table */
+       mmio_write64(smmu->base + ARM_SMMU_STRTAB_BASE,
+                    smmu->strtab_cfg.strtab_base);
+       mmio_write32(smmu->base + ARM_SMMU_STRTAB_BASE_CFG,
+                    smmu->strtab_cfg.strtab_base_cfg);
+
+       /* Command queue */
+       mmio_write64(smmu->base + ARM_SMMU_CMDQ_BASE, smmu->cmdq.q.q_base);
+       mmio_write32(smmu->base + ARM_SMMU_CMDQ_PROD, smmu->cmdq.q.prod);
+       mmio_write32(smmu->base + ARM_SMMU_CMDQ_CONS, smmu->cmdq.q.cons);
+
+       enables = CR0_CMDQEN;
+       ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+                                     ARM_SMMU_CR0ACK);
+       if (ret) {
+               printk("ERROR: SMMU failed to enable command queue\n");
+               return ret;
+       }
+
+       /* Invalidate any cached configuration */
+       cmd.opcode = CMDQ_OP_CFGI_ALL;
+       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+       arm_smmu_cmdq_issue_sync(smmu);
+
+       cmd.opcode = CMDQ_OP_TLBI_NSNH_ALL;
+       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+
+       /* Invalidate any stale TLB entries */
+       cmd.opcode = CMDQ_OP_TLBI_EL2_ALL;
+       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+       arm_smmu_cmdq_issue_sync(smmu);
+
+       /* Event queue */
+       mmio_write64(smmu->base + ARM_SMMU_EVTQ_BASE, smmu->evtq.q.q_base);
+       mmio_write32(smmu->base + ARM_SMMU_EVTQ_PROD, smmu->evtq.q.prod);
+       mmio_write32(smmu->base + ARM_SMMU_EVTQ_CONS, smmu->evtq.q.cons);
+
+       enables |= CR0_EVTQEN;
+       ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+                                     ARM_SMMU_CR0ACK);
+       if (ret) {
+               printk("ERROR: SMMU failed to enable event queue\n");
+               return ret;
+       }
+
+       /* ToDo: Add support for PRI queue and IRQs  */
+
+       enables |= CR0_SMMUEN;
+       ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+                                     ARM_SMMU_CR0ACK);
+       if (ret) {
+               printk("ERROR: SMMU failed to enable SMMU interface\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int arm_smmu_device_init_features(struct arm_smmu_device *smmu)
+{
+       u32 reg;
+
+       /* IDR0 */
+       reg = mmio_read32(smmu->base + ARM_SMMU_IDR0);
+
+       smmu->features = 0;
+       /* 2-level structures */
+       if (FIELD_GET(IDR0_ST_LVL, reg) == IDR0_ST_LVL_2LVL)
+               smmu->features |= ARM_SMMU_FEAT_2_LVL_STRTAB;
+
+       if (!(reg & IDR0_S2P)) {
+               printk("ERROR: SMMU stage2 translations not supported\n");
+               return -ENXIO;
+       }
+
+       if (FIELD_GET(IDR0_S1P, reg)) {
+               smmu->features |= IDR0_S1P;
+       }
+
+       /* IDR1 */
+       reg = mmio_read32(smmu->base + ARM_SMMU_IDR1);
+       if (reg & (IDR1_TABLES_PRESET | IDR1_QUEUES_PRESET | IDR1_REL)) {
+               printk("ERROR: SMMU embedded implementation not supported\n");
+               return -ENXIO;
+       }
+
+       /* Queue sizes, capped at 4k */
+       smmu->cmdq.q.max_n_shift = MIN(CMDQ_MAX_SZ_SHIFT,
+                                      FIELD_GET(IDR1_CMDQS, reg));
+       if (!smmu->cmdq.q.max_n_shift) {
+               printk("ERROR: SMMU unit-length command queue not supported\n");
+               return -ENXIO;
+       }
+       smmu->evtq.q.max_n_shift = MIN(EVTQ_MAX_SZ_SHIFT,
+                                      FIELD_GET(IDR1_EVTQS, reg));
+
+       /* SID sizes */
+       smmu->sid_bits = FIELD_GET(IDR1_SIDSIZE, reg);
+
+       /*
+        * If the SMMU supports fewer bits than would fill a single L2 stream
+        * table, use a linear table instead.
+        */
+       if (smmu->sid_bits <= STRTAB_SPLIT)
+               smmu->features &= ~ARM_SMMU_FEAT_2_LVL_STRTAB;
+
+       return 0;
+}
+
+static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
+{
+       struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+       struct arm_smmu_strtab_l1_desc *desc;
+       void *strtab;
+       u32 size;
+
+       desc = &cfg->l1_desc[sid >> STRTAB_SPLIT];
+       if (desc->l2ptr) {
+               desc->active_stes++;
+               return 0;
+       }
+
+       size = 1 << (STRTAB_SPLIT + STRTAB_STE_DWORDS_BITS + 3);
+       strtab = &cfg->strtab[(sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS];
+
+       desc->span = STRTAB_SPLIT + 1;
+       desc->l2ptr = page_alloc_aligned(&mem_pool, PAGES(size));
+       if (!desc->l2ptr) {
+               printk("ERROR: SMMU failed to allocate l2 stream table (%u 
bytes)\n",
+                      size);
+               return -ENOMEM;
+       }
+       desc->l2ptr_dma = paging_hvirt2phys(desc->l2ptr);
+       desc->active_stes = 1;
+       arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT);
+       arm_smmu_write_strtab_l1_desc(strtab, desc);
+
+       return 0;
+}
+
+static void arm_smmu_uninit_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
+{
+       struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+       struct arm_smmu_strtab_l1_desc *desc;
+       void *strtab;
+       u32 size;
+
+       desc = &cfg->l1_desc[sid >> STRTAB_SPLIT];
+
+       desc->active_stes--;
+       if (desc->active_stes)
+               return;
+
+       size = 1 << (STRTAB_SPLIT + STRTAB_STE_DWORDS_BITS + 3);
+       page_free(&mem_pool, desc->l2ptr, PAGES(size));
+       desc->l2ptr = NULL;
+       desc->l2ptr_dma = 0;
+       desc->span = 0;
+       strtab = &cfg->strtab[(sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS];
+       arm_smmu_write_strtab_l1_desc(strtab, desc);
+
+       return;
+}
+
+static __u64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
+{
+       __u64 *step;
+       struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+
+       if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
+               struct arm_smmu_strtab_l1_desc *l1_desc;
+               int idx;
+
+               /* Two-level walk */
+               idx = (sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS;
+               l1_desc = &cfg->l1_desc[idx];
+               idx = (sid & ((1 << STRTAB_SPLIT) - 1)) * STRTAB_STE_DWORDS;
+               step = &l1_desc->l2ptr[idx];
+       } else {
+               /* Simple linear lookup */
+               step = &cfg->strtab[sid * STRTAB_STE_DWORDS];
+       }
+
+       return step;
+}
+
+static int arm_smmu_init_ste(struct arm_smmu_device *smmu, u32 sid, u32 vmid)
+{
+       __u64 *step;
+
+       if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)
+               arm_smmu_init_l2_strtab(smmu, sid);
+
+       step = arm_smmu_get_step_for_sid(smmu, sid);
+       arm_smmu_write_strtab_ent(smmu, sid, NULL, step, true, vmid);
+
+       return 0;
+}
+
+static void arm_smmu_uninit_ste(struct arm_smmu_device *smmu, u32 sid, u32 
vmid)
+{
+       __u64 *step;
+
+       step = arm_smmu_get_step_for_sid(smmu, sid);
+       arm_smmu_write_strtab_ent(smmu, sid, NULL, step, true, vmid);
+
+       if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)
+               arm_smmu_uninit_l2_strtab(smmu, sid);
+}
+
+static void arm_smmu_dump_state(struct arm_smmu_state *state,
+                               struct arm_smmu_device *smmu)
+{
+       state->smmu = smmu;
+
+       state->idr[0] = mmio_read32(smmu->base + ARM_SMMU_IDR0);
+       state->idr[1] = mmio_read32(smmu->base + ARM_SMMU_IDR1);
+       state->idr[2] = mmio_read32(smmu->base + ARM_SMMU_IDR2);
+       state->idr[3] = mmio_read32(smmu->base + ARM_SMMU_IDR3);
+       state->idr[4] = mmio_read32(smmu->base + ARM_SMMU_IDR4);
+       state->idr[5] = mmio_read32(smmu->base + ARM_SMMU_IDR5);
+
+       state->cr[0] = mmio_read32(smmu->base + ARM_SMMU_CR0);
+       state->cr[1] = mmio_read32(smmu->base + ARM_SMMU_CR1);
+       state->cr[2] = mmio_read32(smmu->base + ARM_SMMU_CR2);
+       state->cr0ack = mmio_read32(smmu->base + ARM_SMMU_CR0ACK);
+
+       state->strtab_base = mmio_read64(smmu->base + ARM_SMMU_STRTAB_BASE);
+       state->strtab_base_cfg = mmio_read32(smmu->base +
+                                            ARM_SMMU_STRTAB_BASE_CFG);
+       state->cmdq_base = mmio_read64(smmu->base + ARM_SMMU_CMDQ_BASE);
+       state->cmdq_prod = mmio_read32(smmu->base + ARM_SMMU_CMDQ_PROD);
+       state->cmdq_cons = mmio_read32(smmu->base + ARM_SMMU_CMDQ_CONS);
+
+}
+
+static bool arm_smmu_cell_can_access_sid(struct cell *cell, u32 sid)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cell->config->stream_ids); i++) {
+               if (cell->config->stream_ids[i] == JAILHOUSE_INVALID_STREAMID) {
+                       break;
+               }
+
+               if (cell->config->stream_ids[i] == sid) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static int arm_smmu_state_init(struct arm_smmu_state *state,
+                              struct arm_smmu_device *smmu)
+{
+       u32 reg;
+
+       state->smmu = smmu;
+
+       reg = mmio_read32(smmu->base + ARM_SMMU_IDR0);
+       /* Tell guests stage 2 is not supported. */
+       reg = FIELD_CLEAR(IDR0_S2P, reg);
+       /* ATOS, VATOS, PRI, ATS, HTTU, MSI not supported yet. */
+       reg = FIELD_CLEAR(IDR0_ATOS, reg);
+       reg = FIELD_CLEAR(IDR0_VATOS, reg);
+       reg = FIELD_CLEAR(IDR0_PRI, reg);
+       reg = FIELD_CLEAR(IDR0_NS1ATS, reg);
+       reg = FIELD_CLEAR(IDR0_ATS, reg);
+       reg = FIELD_CLEAR(IDR0_HTTU, reg);
+       reg = FIELD_CLEAR(IDR0_MSI, reg);
+
+       state->idr[0] = reg;
+
+       reg = mmio_read32(smmu->base + ARM_SMMU_IDR1);
+       /* Substreams not supported for now. */
+       reg = FIELD_CLEAR(IDR1_SSIDSIZE, reg);
+
+       state->idr[1] = reg;
+
+       state->idr[2] = mmio_read32(smmu->base + ARM_SMMU_IDR2);
+       state->idr[3] = mmio_read32(smmu->base + ARM_SMMU_IDR3);
+       state->idr[4] = mmio_read32(smmu->base + ARM_SMMU_IDR4);
+       state->idr[5] = mmio_read32(smmu->base + ARM_SMMU_IDR5);
+
+       /* Reset the control registers. */
+       state->cr[0] = 0;
+       state->cr[1] = 0;
+       state->cr[2] = 0;
+
+       state->gerror = 0;
+       state->gerrorn = 0;
+
+       state->cmdq_base = 0;
+       state->cmdq_prod = 0;
+       state->cmdq_cons = 0;
+
+       state->evtq_base = 0;
+       state->evtq_prod = 0;
+       state->evtq_cons = 0;
+
+       state->irq_ctrl = 0;
+       state->irq_ctrlack = 0;
+       state->evtq_irq_cfg0 = 0;
+       state->gerror_irq_cfg0 = 0;
+
+       return 0;
+}
+
+static int arm_smmu_get_step_from_guest(struct arm_smmu_state *state, u32 sid,
+                                       u64 *dest)
+{
+       void *step_page;
+       u64 *step, base_cfg, strtab_base, ste_addr;
+       u32 log2size;
+
+       base_cfg = state->strtab_base_cfg;
+
+       /*
+        * Directly ANDing with the mask instead of using FIELD_GET() is
+        * intentional. The address has to be aligned by 64 bytes, so the
+        * bottom 6 bits are always 0, and so skipped when writing them to the
+        * register. By ANDing with the mask, we make the bottom 6 bits 0. This
+        * is the same for l2ptr calculation below.
+        *
+        * Using FIELD_GET(STRTAB_BASE_ADDR_MASK, state->strtab_base) << 6
+        * would have the same result.
+        */
+       strtab_base = state->strtab_base & STRTAB_BASE_ADDR_MASK;
+
+       log2size = FIELD_GET(STRTAB_BASE_CFG_LOG2SIZE, base_cfg);
+
+       /* Check if 2 level walk should be used or 1 level. */
+       if (FIELD_GET(STRTAB_BASE_CFG_FMT, base_cfg) == 
STRTAB_BASE_CFG_FMT_2LVL) {
+               u64 l2ptr, num_l2_ents;
+               u32 split, idx, span;
+
+               split = FIELD_GET(STRTAB_BASE_CFG_SPLIT, base_cfg);
+
+               idx = (sid >> split) * STRTAB_L1_DESC_SIZE;
+               ste_addr = strtab_base + idx;
+
+               step_page = paging_get_guest_pages(NULL, (ste_addr & PAGE_MASK),
+                                                  2, PAGE_DEFAULT_FLAGS);
+               if (step_page == NULL) {
+                       printk("%s: Failed to allocate memory for level 1 "
+                              "steam table walk\n", __func__);
+                       return -ENOMEM;
+               }
+
+               step = step_page + PAGE_OFFSET(ste_addr);
+
+               /* Get the second level table base. */
+               l2ptr = step[0] & STRTAB_L1_DESC_L2PTR_MASK;
+
+               span = FIELD_GET(STRTAB_L1_DESC_SPAN, step[0]);
+               num_l2_ents = 1 << (span - 1);
+
+               idx = (sid & ((1 << split) - 1)) * STRTAB_STE_SIZE;
+               ste_addr = l2ptr + idx;
+
+               if (span == 0) {
+                       return -ENOENT;
+               }
+
+               if (span > split + 1 ||
+                   ste_addr > (l2ptr + num_l2_ents * STRTAB_STE_SIZE)) {
+                       return -EINVAL;
+               }
+
+               step_page = paging_get_guest_pages(NULL, (ste_addr & PAGE_MASK),
+                                                  2, PAGE_DEFAULT_FLAGS);
+               if (step_page == NULL) {
+                       printk("%s: Failed to allocate memory for level 2 "
+                              "steam table walk\n", __func__);
+                       return -ENOMEM;
+               }
+
+               step = step_page + PAGE_OFFSET(ste_addr);
+       } else {
+               u32 num_stes, idx;
+
+               num_stes = 1 << log2size;
+
+               if (sid >= num_stes) {
+                       return -EINVAL;
+               }
+
+               idx = sid * STRTAB_STE_SIZE;
+               ste_addr = strtab_base + idx;
+
+               step_page = paging_get_guest_pages(NULL, (ste_addr & PAGE_MASK),
+                                                  2, PAGE_DEFAULT_FLAGS);
+               if (step_page == NULL) {
+                       printk("%s: Failed to allocate memory for linear "
+                              "steam table walk\n", __func__);
+                       return -ENOMEM;
+               }
+
+               step = step_page + PAGE_OFFSET(ste_addr);
+       }
+
+       /*
+        * Pages mapped via paging_get_guest_pages() are temporary, and valid
+        * only until the next call to it. That's why it is better to copy to
+        * a buffer rather than returning the pointer directly.
+        */
+       memcpy(dest, step, STRTAB_STE_SIZE);
+
+       return 0;
+}
+
+static int arm_smmu_cfgi_ste(struct arm_smmu_state *state, u32 sid)
+{
+       struct cell *cell;
+       u64 guest_ste[STRTAB_STE_DWORDS], *step;
+       int ret;
+
+       cell = this_cell();
+
+       /* Whoops, this cell is not allowed to touch this sid. */
+       if (!arm_smmu_cell_can_access_sid(cell, sid)) {
+               printk("ERROR: Cell %u trying to access stream ID %u. "
+                      "Access denied.\n", cell->config->id, sid);
+               return -EPERM;
+       }
+
+       ret = arm_smmu_get_step_from_guest(state, sid, guest_ste);
+       if (ret) {
+               return ret;
+       }
+
+       /*
+        * Get the hardware STE and update it with values from the guest.
+        */
+       step = arm_smmu_get_step_for_sid(state->smmu, sid);
+
+       arm_smmu_write_strtab_ent(state->smmu, sid, guest_ste, step, false,
+                                 cell->config->id);
+
+       return 0;
+}
+
+/*
+ * ToDo: Use the command queue error registers here.
+ */
+static int arm_smmu_consume_cmd(struct arm_smmu_state *state)
+{
+       struct cell *cell;
+       void *cmdq_base_page;
+       struct arm_smmu_cmdq_ent ent;
+       u64 cmd[CMDQ_ENT_DWORDS], *cmdq_base, cmdq_base_addr;
+       u32 sid, ssid, cons, shift, idx;
+       u8 op;
+       int ret, i;
+
+       cell = this_cell();
+       shift = FIELD_GET(Q_BASE_LOG2SIZE, state->cmdq_base);
+
+       if (Q_EMPTY(state->cmdq_prod, state->cmdq_cons, shift)) {
+               printk("WARN: %s() called but command queue is empty\n",
+                      __func__);
+               return 0;
+       }
+
+       cmdq_base_addr = state->cmdq_base & Q_BASE_ADDR_MASK;
+
+       /*
+        * Map 2 pages in case the base address is not aligned at a page
+        * boundary. The queue size is capped at 4k so it can't span more than
+        * 2 pages.
+        */
+       cmdq_base_page = paging_get_guest_pages(NULL, cmdq_base_addr, 2,
+                                           PAGE_DEFAULT_FLAGS);
+       if (cmdq_base_page == NULL) {
+               printk("ERROR: Failed to allocate memory for reading the SMMU "
+                      "command\n");
+               return -ENOMEM;
+       }
+       /*
+        * Offset the base page by the offset of q_base from the page boundary.
+        * This is to handle the case when the queue base is not page-aligned.
+        * For page-aligned base values, the address remains the same.
+        */
+       cmdq_base = cmdq_base_page + PAGE_OFFSET(cmdq_base_addr);
+
+       idx = Q_IDX(state->cmdq_cons, shift) * CMDQ_ENT_DWORDS;
+       cmd[0] = cmdq_base[idx];
+       cmd[1] = cmdq_base[idx + 1];
+
+       op = cmd[0] & CMDQ_0_OP;
+
+       ent.opcode = op;
+
+       switch (op) {
+       case CMDQ_OP_CFGI_STE:
+               sid = FIELD_GET(CMDQ_CFGI_0_SID, cmd[0]);
+
+               ret = arm_smmu_cfgi_ste(state, sid);
+               if (ret) {
+                       return ret;
+               }
+               break;
+       case CMDQ_OP_CFGI_ALL:
+               /*
+                * This might flood the command queue with too many invalidation
+                * commands, but we can't directly issue CFGI_ALL because it
+                * will affect other cell's STEs.
+                *
+                * Let's work on the assumption that the number of stream IDs
+                * allocated to a cell is a fairly small number.
+                */
+               for (i = 0; i < ARRAY_SIZE(cell->config->stream_ids); i++) {
+                       sid = cell->config->stream_ids[i];
+
+                       if (sid == JAILHOUSE_INVALID_STREAMID) {
+                               break;
+                       }
+
+                       ret = arm_smmu_cfgi_ste(state, sid);
+                       /*
+                        * -ENOENT means the STE was not installed by the guest
+                        * even though we give it access in the config file.
+                        * Just skip it.
+                        */
+                       if (ret && ret != -ENOENT) {
+                               return ret;
+                       }
+               }
+               break;
+       case CMDQ_OP_CMD_SYNC:
+               ent.sync.msiaddr = cmd[1] & CMDQ_SYNC_1_MSIADDR_MASK;
+               ent.sync.msidata = FIELD_GET(CMDQ_SYNC_0_MSIDATA, cmd[0]);
+               arm_smmu_cmdq_issue_cmd(state->smmu, &ent);
+               break;
+       case CMDQ_OP_PREFETCH_ADDR:
+               ent.prefetch.addr = cmd[1] & CMDQ_PREFETCH_1_ADDR_MASK;
+               ent.prefetch.size = FIELD_GET(CMDQ_PREFETCH_1_SIZE, cmd[1]);
+               /* Fallthrough */
+       case CMDQ_OP_PREFETCH_CFG:
+               sid = FIELD_GET(CMDQ_PREFETCH_0_SID, cmd[0]);
+               ssid = FIELD_GET(CMDQ_PREFETCH_0_SSID, cmd[0]);
+
+               if (ssid != 0) {
+                       printk("WARN: Substreams not supported\n");
+               }
+
+               if (!arm_smmu_cell_can_access_sid(cell, sid)) {
+                       printk("ERROR: Cell %u trying to access stream ID %u. "
+                              "Access denied.\n", cell->config->id, sid);
+                       return -EPERM;
+               }
+
+               ent.prefetch.sid = sid;
+               arm_smmu_cmdq_issue_cmd(state->smmu, &ent);
+               break;
+       case CMDQ_OP_TLBI_NH_VA:
+               ent.tlbi.addr = cmd[1] & CMDQ_TLBI_1_VA_MASK;
+               ent.tlbi.leaf = FIELD_GET(CMDQ_TLBI_1_LEAF, cmd[1]);
+               /* Fallthrough */
+       case CMDQ_OP_TLBI_NH_ASID:
+               ent.tlbi.asid = FIELD_GET(CMDQ_TLBI_0_ASID, cmd[0]);
+               /* Fallthrough */
+       case CMDQ_OP_TLBI_S12_VMALL:
+               ent.tlbi.vmid = cell->config->id;
+               arm_smmu_cmdq_issue_cmd(state->smmu, &ent);
+               break;
+       case CMDQ_OP_TLBI_NSNH_ALL:
+               /* Only invalidate TLB entries for this cell. */
+               ent.opcode = CMDQ_OP_TLBI_S12_VMALL;
+               ent.tlbi.vmid = cell->config->id;
+               arm_smmu_cmdq_issue_cmd(state->smmu, &ent);
+               break;
+       case CMDQ_OP_TLBI_EL2_ALL:
+               /* Don't let guest cells touch EL2 entries. */
+               break;
+       default:
+               printk("Command 0x%x not implemented yet\n", op);
+               return -EINVAL;
+       }
+
+       cons = state->cmdq_cons;
+       cons = (Q_WRP(cons, shift) | Q_IDX(cons, shift)) + 1;
+
+       state->cmdq_cons = Q_OVF(state->cmdq_cons) | Q_WRP(cons, shift) |
+                          Q_IDX(cons, shift);
+
+       dsb(ish);
+       return 0;
+}
+
+static enum mmio_result arm_smmu_state_write64(struct arm_smmu_state *state,
+                                              struct mmio_access *mmio)
+{
+       u64 offset;
+
+       offset = mmio->address;
+
+       switch (offset) {
+       case ARM_SMMU_CMDQ_BASE:
+               if (state->idr[1] & IDR1_QUEUES_PRESET) {
+                       /* Read only. */
+                       return MMIO_ERROR;
+               }
+               state->cmdq_base = mmio->value;
+               break;
+       case ARM_SMMU_EVTQ_BASE:
+               if (state->idr[1] & IDR1_QUEUES_PRESET) {
+                       /* Read only. */
+                       return MMIO_ERROR;
+               }
+               state->evtq_base = mmio->value;
+               break;
+       case ARM_SMMU_STRTAB_BASE:
+               if (state->idr[1] & IDR1_TABLES_PRESET) {
+                       /* This register is read-only in preset mode. */
+                       return MMIO_ERROR;
+               }
+               state->strtab_base = mmio->value;
+               break;
+       case ARM_SMMU_GERROR_IRQ_CFG0:
+               state->gerror_irq_cfg0 = mmio->value;
+               break;
+       case ARM_SMMU_EVTQ_IRQ_CFG0:
+               state->gerror_irq_cfg0 = mmio->value;
+               break;
+       default:
+               /* Not a writeable register. */
+               printk("ERROR: Writing in a non-writeable SMMU register at "
+                      "offset 0x%llx\n", offset);
+               return MMIO_ERROR;
+       }
+
+       return MMIO_HANDLED;
+}
+
+static enum mmio_result arm_smmu_state_write32(struct arm_smmu_state *state,
+                                              struct mmio_access *mmio)
+{
+       u64 offset, value;
+       u32 shift;
+       int ret;
+
+       offset = mmio->address;
+
+       ret = 0;
+
+       switch (offset) {
+       case ARM_SMMU_CR0:
+               state->cr[0] = mmio->value;
+               state->cr0ack = mmio->value;
+               break;
+       case ARM_SMMU_CR1:
+               state->cr[1] = mmio->value;
+               break;
+       case ARM_SMMU_CR2:
+               state->cr[2] = mmio->value;
+               break;
+       case ARM_SMMU_CMDQ_BASE:        /* 64b */
+               if (state->idr[1] & IDR1_QUEUES_PRESET) {
+                       /* Read only. */
+                       return MMIO_ERROR;
+               }
+
+               value = LOWER_32_BITS(state->cmdq_base);
+               value |= mmio->value << 32;
+               state->cmdq_base = value;
+               break;
+       case ARM_SMMU_CMDQ_BASE + 4:
+               if (state->idr[1] & IDR1_QUEUES_PRESET) {
+                       /* Read only. */
+                       return MMIO_ERROR;
+               }
+
+               value = UPPER_32_BITS(state->cmdq_base) << 32;
+               value |= mmio->value;
+               state->cmdq_base = value;
+               break;
+       case ARM_SMMU_CMDQ_PROD:
+               /* The guest is responsible for checking if queue is full. */
+               state->cmdq_prod = mmio->value;
+
+               if (!FIELD_GET(CR0_CMDQEN, state->cr[0])) {
+                       break;
+               }
+
+               if (FIELD_GET(GERROR_CMDQ_ERR, state->gerror)) {
+                       break;
+               }
+
+               shift = FIELD_GET(Q_BASE_LOG2SIZE, state->cmdq_base);
+
+               while (!ret && !Q_EMPTY(state->cmdq_prod, state->cmdq_cons,
+                      shift)) {
+                       ret = arm_smmu_consume_cmd(state);
+               }
+
+               break;
+       case ARM_SMMU_CMDQ_CONS:
+               state->cmdq_cons = mmio->value;
+               break;
+       case ARM_SMMU_EVTQ_BASE:        /* 64b */
+               if (state->idr[1] & IDR1_QUEUES_PRESET) {
+                       /* Read only. */
+                       return MMIO_ERROR;
+               }
+
+               value = LOWER_32_BITS(state->evtq_base);
+               value |= mmio->value << 32;
+               state->evtq_base = value;
+               break;
+       case ARM_SMMU_EVTQ_BASE + 4:
+               if (state->idr[1] & IDR1_QUEUES_PRESET) {
+                       /* Read only. */
+                       return MMIO_ERROR;
+               }
+
+               value = UPPER_32_BITS(state->evtq_base) << 32;
+               value |= mmio->value;
+               state->evtq_base = value;
+               break;
+       case ARM_SMMU_EVTQ_PROD:
+               state->evtq_prod = mmio->value;
+               break;
+       case ARM_SMMU_EVTQ_CONS:
+               state->evtq_cons = mmio->value;
+               break;
+       case ARM_SMMU_STRTAB_BASE:      /* 64b */
+               if (state->idr[1] & IDR1_TABLES_PRESET) {
+                       /* This register is read-only in preset mode. */
+                       return MMIO_ERROR;
+               }
+
+               value = LOWER_32_BITS(state->strtab_base);
+               value |= mmio->value << 32;
+               state->strtab_base = value;
+               break;
+       case ARM_SMMU_STRTAB_BASE + 4:
+               if (state->idr[1] & IDR1_TABLES_PRESET) {
+                       /* This register is read-only in preset mode. */
+                       return MMIO_ERROR;
+               }
+
+               value = UPPER_32_BITS(state->strtab_base) << 32;
+               value |= mmio->value;
+               state->strtab_base = value;
+               break;
+       case ARM_SMMU_STRTAB_BASE_CFG:
+               if (state->idr[1] & IDR1_TABLES_PRESET) {
+                       return MMIO_ERROR;
+               }
+
+               /*
+                * The split can only be 6, 8, 10 (4kB/16kB/64Bk leaf tables).
+                * All other values are reserved and are treated as 6.
+                */
+               if (!(FIELD_GET(STRTAB_BASE_CFG_SPLIT, mmio->value) == 6 ||
+                   FIELD_GET(STRTAB_BASE_CFG_SPLIT, mmio->value) == 8 ||
+                   FIELD_GET(STRTAB_BASE_CFG_SPLIT, mmio->value) == 10)) {
+                       mmio->value = FIELD_CLEAR(ARM_SMMU_STRTAB_BASE_CFG,
+                                                 mmio->value);
+                       mmio->value |= FIELD_PREP(STRTAB_BASE_CFG_SPLIT, 6);
+               }
+
+               state->strtab_base_cfg = mmio->value;
+               break;
+       case ARM_SMMU_GERRORN:
+               /*
+                * The SMMU driver will toggle fields in this register to
+                * acknowldge errors. Update GERROR too so software knows it
+                * can continue.
+                */
+               state->gerrorn = state->gerror = mmio->value;
+               break;
+       case ARM_SMMU_IRQ_CTRL:
+               /*
+                * XXX: IRQs are not supported yet. For now, just let the
+                * write go through without any effect. The guest expects to
+                * see an acknowldgement in ARM_SMMU_IRQ_CTRLACK.
+                */
+               state->irq_ctrl = mmio->value;
+               state->irq_ctrlack = mmio->value;
+               break;
+       default:
+               /* Not a writeable register. */
+               printk("ERROR: Writing in a non-writeable SMMU register at "
+                      "offset 0x%llx\n", offset);
+               return MMIO_ERROR;
+       }
+
+       if (ret) {
+               return MMIO_ERROR;
+       }
+
+       return MMIO_HANDLED;
+}
+
+static enum mmio_result arm_smmu_state_read64(struct arm_smmu_state *state,
+                                             struct mmio_access *mmio)
+{
+       u64 offset, value;
+
+       offset = mmio->address;
+
+       switch(offset) {
+       case ARM_SMMU_CMDQ_BASE:
+               value = state->cmdq_base;
+               break;
+       case ARM_SMMU_STRTAB_BASE:
+               value = state->strtab_base;
+               break;
+       case ARM_SMMU_EVTQ_BASE:
+               value = state->evtq_base;
+               break;
+       default:
+               printk("ERROR: Register at offset 0x%llx not implemented yet\n",
+                      offset);
+               return MMIO_ERROR;
+       }
+
+       mmio->value = value;
+       return MMIO_HANDLED;
+}
+
+static enum mmio_result arm_smmu_state_read32(struct arm_smmu_state *state,
+                                             struct mmio_access *mmio)
+{
+       u64 offset;
+       u32 value;
+
+       offset = mmio->address;
+
+       switch (offset) {
+       case ARM_SMMU_CR0:
+               value = state->cr[0];
+               break;
+       case ARM_SMMU_CR1:
+               value = state->cr[1];
+               break;
+       case ARM_SMMU_CR2:
+               value = state->cr[2];
+               break;
+       case ARM_SMMU_CR0ACK:
+               value = state->cr0ack;
+               break;
+       case ARM_SMMU_IDR0:
+               value = state->idr[0];
+               break;
+       case ARM_SMMU_IDR1:
+               value = state->idr[1];
+               break;
+       case ARM_SMMU_IDR2:
+               value = state->idr[2];
+               break;
+       case ARM_SMMU_IDR3:
+               value = state->idr[3];
+               break;
+       case ARM_SMMU_IDR4:
+               value = state->idr[4];
+               break;
+       case ARM_SMMU_IDR5:
+               value = state->idr[5];
+               break;
+       case ARM_SMMU_CMDQ_BASE:        /* 64b */
+               value = UPPER_32_BITS(state->cmdq_base);
+               break;
+       case ARM_SMMU_CMDQ_BASE + 4:
+               value = LOWER_32_BITS(state->cmdq_base);
+               break;
+       case ARM_SMMU_CMDQ_PROD:
+               value = state->cmdq_prod;
+               break;
+       case ARM_SMMU_CMDQ_CONS:
+               value = state->cmdq_cons;
+               break;
+       case ARM_SMMU_EVTQ_BASE:        /* 64b */
+               value = UPPER_32_BITS(state->evtq_base);
+               break;
+       case ARM_SMMU_EVTQ_BASE + 4:
+               value = LOWER_32_BITS(state->evtq_base);
+               break;
+       case ARM_SMMU_EVTQ_PROD:
+               value = state->evtq_prod;
+               break;
+       case ARM_SMMU_EVTQ_CONS:
+               value = state->evtq_cons;
+               break;
+       case ARM_SMMU_STRTAB_BASE:      /* 64b */
+               value = UPPER_32_BITS(state->strtab_base);
+               break;
+       case ARM_SMMU_STRTAB_BASE + 4:
+               value = LOWER_32_BITS(state->strtab_base);
+               break;
+       case ARM_SMMU_STRTAB_BASE_CFG:
+               value = state->strtab_base_cfg;
+               break;
+       case ARM_SMMU_GERROR:
+               value = state->gerror;
+               break;
+       case ARM_SMMU_GERRORN:
+               value = state->gerrorn;
+               break;
+       case ARM_SMMU_IRQ_CTRL:
+               value = state->irq_ctrl;
+               break;
+       case ARM_SMMU_IRQ_CTRLACK:
+               value = state->irq_ctrlack;
+               break;
+       default:
+               /*
+                * The SMMU spec says all undefined register accesses should be
+                * RAZ/WI. Keep it like this for now so we know when
+                * un-implemented registers are being used, rather than having
+                * silent failures all over. Same for handling writes.
+                */
+               printk("ERROR: Register at offset 0x%llx not implemented yet\n",
+                      offset);
+               return MMIO_ERROR;
+       }
+
+       mmio->value = value;
+
+       return MMIO_HANDLED;
+}
+
+static enum mmio_result arm_smmu_mmio_handler(void *arg,
+                                             struct mmio_access *mmio)
+{
+       struct arm_smmu_state *state;
+       enum mmio_result ret;
+
+       state = arg;
+
+       if (mmio->is_write) {
+               if (mmio->size == 4)
+                       ret = arm_smmu_state_write32(state, mmio);
+               else if (mmio->size == 8)
+                       ret = arm_smmu_state_write64(state, mmio);
+               else
+                       return MMIO_HANDLED;    /* Write-invalidate (WI) */
+       } else {
+               if (mmio->size == 4)
+                       ret = arm_smmu_state_read32(state, mmio);
+               else if (mmio->size == 8)
+                       ret = arm_smmu_state_read64(state, mmio);
+               else {
+                       mmio->value = 0;
+                       return MMIO_HANDLED;    /* Read As Zero (RAZ) */
+               }
+       }
+
+       return ret;
+}
+
+/*
+ * Before loading Jailhouse, the root cell might have set up stream table
+ * entries. Jailhouse replaces the stream table with its own, so install those
+ * entries in the hypervisor's stream table.
+ */
+static void arm_smmu_init_root_strtab(struct arm_smmu_state *state)
+{
+       u64 guest_ste[STRTAB_STE_DWORDS];
+       u32 sid;
+       int i, ret;
+
+       for (i = 0; i < ARRAY_SIZE(root_cell.config->stream_ids); i++) {
+               sid = root_cell.config->stream_ids[i];
+
+               if (sid == JAILHOUSE_INVALID_STREAMID) {
+                       break;
+               }
+
+               ret = arm_smmu_get_step_from_guest(state, sid, guest_ste);
+               if (ret) {
+                       continue;
+               }
+
+               if (guest_ste[0] & STRTAB_STE_0_V) {
+                       ret = arm_smmu_cfgi_ste(state, sid);
+                       if (ret) {
+                               continue;
+                       }
+               }
+       }
+}
+
+
+static int arm_smmuv3_cell_init(struct cell *cell)
+{
+       struct jailhouse_iommu *iommu;
+       struct arm_smmu_state *state;
+       struct arm_smmu_cmdq_ent cmd;
+       int ret, i, sid;
+
+       if (smmu->features & IDR0_S1P) {
+               cell->arch.smmu_states = page_alloc(&mem_pool,
+                       PAGES(sizeof(*cell->arch.smmu_states) *
+                       JAILHOUSE_MAX_IOMMU_UNITS));
+               if (cell->arch.smmu_states == NULL) {
+                       printk("ERROR: Unable to allocate per-cell SMMU 
data\n");
+                       return -ENOMEM;
+               }
+       }
+
+       for (i = 0; i < JAILHOUSE_MAX_IOMMU_UNITS; i++) {
+               iommu = &system_config->platform_info.arm.iommu_units[i];
+               if (iommu->type != JAILHOUSE_IOMMU_SMMUV3)
+                       continue;
+
+               for (sid = 0; sid < ARRAY_SIZE(cell->config->stream_ids); 
sid++) {
+                       if (cell->config->stream_ids[sid] == 
JAILHOUSE_INVALID_STREAMID)
+                               break;
+                       ret = arm_smmu_init_ste(&smmu[i],
+                                               cell->config->stream_ids[sid],
+                                               cell->config->id);
+                       if (ret) {
+                               printk("ERROR: SMMU INIT ste failed: sid = 
%d\n",
+                                      cell->config->stream_ids[sid]);
+                               return ret;
+                       }
+               }
+
+               /* Only initialize if stage 1 translations are supported. */
+               if (smmu->features & IDR0_S1P) {
+                       state = &cell->arch.smmu_states[i];
+                       /*
+                        * The root cell's OS has already initialised the
+                        * SMMU so in that case copy the state from the SMMU
+                        * dump.
+                        */
+                       if (cell == &root_cell) {
+                               memcpy(state, &state_dump[i], sizeof(*state));
+                               arm_smmu_init_root_strtab(state);
+                       } else {
+                               arm_smmu_state_init(state, &smmu[i]);
+                       }
+
+                       /* Register the SMMU as an MMIO region. */
+                       mmio_region_register(cell, iommu->smmuv3.smmu_base,
+                                            iommu->smmuv3.smmu_size,
+                                            arm_smmu_mmio_handler, state);
+               }
+
+       }
+
+       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);
+
+       return 0;
+}
+
+static void arm_smmuv3_cell_exit(struct cell *cell)
+{
+       struct jailhouse_iommu *iommu;
+       int i, sid;
+
+       for (i = 0; i < JAILHOUSE_MAX_IOMMU_UNITS; i++) {
+               iommu = &system_config->platform_info.arm.iommu_units[i];
+               if (iommu->type != JAILHOUSE_IOMMU_SMMUV3)
+                       continue;
+
+               for (sid = 0; sid < ARRAY_SIZE(cell->config->stream_ids); 
sid++) {
+                       if (cell->config->stream_ids[sid] == 
JAILHOUSE_INVALID_STREAMID)
+                               break;
+                       arm_smmu_uninit_ste(&smmu[i],
+                                           cell->config->stream_ids[sid], 
cell->config->id);
+               }
+       }
+
+}
+
+static int arm_smmuv3_init(void)
+{
+       struct jailhouse_iommu *iommu;
+       int ret, i;
+
+       for (i = 0; i < JAILHOUSE_MAX_IOMMU_UNITS; i++) {
+               iommu = &system_config->platform_info.arm.iommu_units[i];
+               if (iommu->type != JAILHOUSE_IOMMU_SMMUV3)
+                       continue;
+
+               smmu[i].base = paging_map_device(iommu->smmuv3.smmu_base,
+                                              iommu->smmuv3.smmu_size);
+
+               /* ToDo: irq allocation*/
+
+               ret = arm_smmu_device_init_features(&smmu[i]);
+               if (ret)
+                       return ret;
+
+               ret = arm_smmu_init_structures(&smmu[i]);
+               if (ret)
+                       return ret;
+
+               /*
+                * The root cell's OS has already initialised the SMMU
+                * so the emulated SMMU state won't be correct for the root
+                * cell. Dump the current SMMU registers and then cell_init()
+                * will copy it over.
+                */
+               arm_smmu_dump_state(&state_dump[i], &smmu[i]);
+
+               /* Reset the device */
+               ret = arm_smmu_device_reset(&smmu[i]);
+               if (ret)
+                       return ret;
+       }
+
+       return arm_smmuv3_cell_init(&root_cell);
+}
+
+static unsigned int arm_smmuv3_mmio_count_regions(struct cell *cell)
+{
+       return 1;
+}
+
+DEFINE_UNIT_SHUTDOWN_STUB(arm_smmuv3);
+DEFINE_UNIT(arm_smmuv3, "ARM SMMU v3");
diff --git a/hypervisor/include/jailhouse/entry.h 
b/hypervisor/include/jailhouse/entry.h
index 26360a6e..da1c9da2 100644
--- a/hypervisor/include/jailhouse/entry.h
+++ b/hypervisor/include/jailhouse/entry.h
@@ -21,6 +21,7 @@
 #define EPERM          1
 #define ENOENT         2
 #define EIO            5
+#define ENXIO          6
 #define E2BIG          7
 #define ENOMEM         12
 #define EBUSY          16
-- 
2.17.1

-- 
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/20190702143607.16525-7-p-yadav1%40ti.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to