All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support
@ 2016-08-22 16:17 Prem Mallappa
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 1/9] log: Add new IOMMU type Prem Mallappa
                   ` (10 more replies)
  0 siblings, 11 replies; 34+ messages in thread
From: Prem Mallappa @ 2016-08-22 16:17 UTC (permalink / raw)
  To: Peter Maydell, Edgar E . Iglesias, qemu-devel; +Cc: Prem Mallappa

v1 -> v2:
	- Adopted review comments from Eric Auger
		- Make SMMU_DPRINTF to internally call qemu_log
	            (since translation requests are too many, we need control
		     on the type of log we want)
		- SMMUTransCfg modified to suite simplicity
		- Change RegInfo to uint64 register array
		- Code cleanup
		- Test cleanups
	- Reshuffled patches

RFC -> v1:
	- As per SMMUv3 spec 16.0 (only is_ste_consistant() is noticeable)
	- Reworked register access/update logic
	- Factored out translation code for
		- single point bug fix
		- sharing/removal in future
	- (optional) Unit tests added, with PCI test device
		- S1 with 4k/64k, S1+S2 with 4k/64k
		- (S1 or S2) only can be verified by Linux 4.7 driver
	- (optional) Priliminary ACPI support

RFC:
	- Implements SMMUv3 spec 11.0
	- Supported for PCIe devices, 
	- Command Queue and Event Queue supported
	- LPAE only, S1 is supported and Tested, S2 not tested
	- BE mode Translation not supported
	- IRQ support (legacy, no MSI)
	- Tested with DPDK and e1000 

Patch 1: Add new log type for IOMMU transactions

Patch 2: Adds support in virt.c to create both SMMUv3 device and dts entries

Patch 2: Adds SMMUv3 model to QEMU
	Multiple files, big ones, translate functionality is split across to
	accomodate SMMUv2 model, and to remove when common translation feature
	(if) becomes available.

Patch 3: Adds SMMU build support

Patch 4: Some devicetree function to add support for SMMU's multiple interrupt
	 assignment with names

<< optional patches >>
Optional patches are posted for completeness or for those who wants to test.

Patch 5: A simple PCI device which does DMA from 'src' to 'dst' given
	 src_addr, dst_addr and size, and is used by unit test, uses
	 pci_dma_read and pci_dma_write in a crude way but serves the purpose.

Patch 6: Current libqos PCI helpers are x86 only, this addes a generic interface

Patch 7: Unit tests for SMMU, 
		- initializes SMMU device 
		- initializes Test device
		- allocates page tables 1:1 mapping va == pa
		- allocates STE/CD accordingly for S1, S2, S1+S2
		- initiates DMA via PCI test device
		- verifies transfered data

Patch 8: Added ACPI IORT tables, was needed for internal project purpose, but 
	 posting here for anyone looking for testing ACPI on ARM platforms.
	 (P.S: Linux side IORT patches are WIP)

Repo:
https://github.com/pmallappa/qemu/tree/upstream/smmuv3/v2

To Test:
$ make tests/smmuv3-test
$ QTEST_QEMU_BINARY=aarch64-softmmu/qemu-system-aarch64 tests/smmuv3-test
<< expect lot of prints >>

Any comments welcome..

Cheers
/Prem

Prem Mallappa (9):
  log: Add new IOMMU type
  devicetree: Added new APIs to make use of more fdt functions
  hw: arm: SMMUv3 emulation model
  hw: arm: Added SMMUv3 files for build
  hw: arm: Add SMMUv3 to virt platform, create DTS accordingly
  [optional] hw: misc: added testdev for smmu
  [optional] tests: libqos: generic pci probing helpers
  [optional] tests: SMMUv3 unit tests
  [optional] arm: smmu-v3: ACPI IORT initial support

 default-configs/aarch64-softmmu.mak |    1 +
 device_tree.c                       |   35 +
 hw/arm/Makefile.objs                |    1 +
 hw/arm/smmu-common.c                |  152 ++++
 hw/arm/smmu-common.h                |  141 ++++
 hw/arm/smmu-v3.c                    | 1369 +++++++++++++++++++++++++++++++++++
 hw/arm/smmuv3-internal.h            |  432 +++++++++++
 hw/arm/virt-acpi-build.c            |   43 ++
 hw/arm/virt.c                       |   62 ++
 hw/misc/Makefile.objs               |    2 +-
 hw/misc/pci-testdev-smmu.c          |  239 ++++++
 hw/misc/pci-testdev-smmu.h          |   22 +
 hw/vfio/common.c                    |    2 +-
 include/hw/acpi/acpi-defs.h         |   84 +++
 include/hw/arm/smmu.h               |   33 +
 include/hw/arm/virt.h               |    2 +
 include/qemu/log.h                  |    1 +
 include/sysemu/device_tree.h        |   18 +
 tests/Makefile.include              |    4 +
 tests/libqos/pci-generic.c          |  197 +++++
 tests/libqos/pci-generic.h          |   58 ++
 tests/smmuv3-test.c                 |  952 ++++++++++++++++++++++++
 util/log.c                          |    2 +
 23 files changed, 3850 insertions(+), 2 deletions(-)
 create mode 100644 hw/arm/smmu-common.c
 create mode 100644 hw/arm/smmu-common.h
 create mode 100644 hw/arm/smmu-v3.c
 create mode 100644 hw/arm/smmuv3-internal.h
 create mode 100644 hw/misc/pci-testdev-smmu.c
 create mode 100644 hw/misc/pci-testdev-smmu.h
 create mode 100644 include/hw/arm/smmu.h
 create mode 100644 tests/libqos/pci-generic.c
 create mode 100644 tests/libqos/pci-generic.h
 create mode 100644 tests/smmuv3-test.c

-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 1/9] log: Add new IOMMU type
  2016-08-22 16:17 [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Prem Mallappa
@ 2016-08-22 16:17 ` Prem Mallappa
  2016-09-09 15:36   ` Auger Eric
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 2/9] devicetree: Added new APIs to make use of more fdt functions Prem Mallappa
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Prem Mallappa @ 2016-08-22 16:17 UTC (permalink / raw)
  To: Peter Maydell, Edgar E . Iglesias, qemu-devel; +Cc: Prem Mallappa

Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
---
 include/qemu/log.h | 1 +
 util/log.c         | 2 ++
 2 files changed, 3 insertions(+)

diff --git a/include/qemu/log.h b/include/qemu/log.h
index 234fa81..3dd2131 100644
--- a/include/qemu/log.h
+++ b/include/qemu/log.h
@@ -42,6 +42,7 @@ static inline bool qemu_log_separate(void)
 #define CPU_LOG_TB_NOCHAIN (1 << 13)
 #define CPU_LOG_PAGE       (1 << 14)
 #define LOG_TRACE          (1 << 15)
+#define CPU_LOG_IOMMU      (1 << 16)
 
 /* Returns true if a bit is set in the current loglevel mask
  */
diff --git a/util/log.c b/util/log.c
index 5ad72c1..62c4378 100644
--- a/util/log.c
+++ b/util/log.c
@@ -241,6 +241,8 @@ const QEMULogItem qemu_log_items[] = {
       "show CPU registers before entering a TB (lots of logs)" },
     { CPU_LOG_MMU, "mmu",
       "log MMU-related activities" },
+    { CPU_LOG_IOMMU, "iommu",
+      "log IOMMU-related activities" },
     { CPU_LOG_PCALL, "pcall",
       "x86 only: show protected mode far calls/returns/exceptions" },
     { CPU_LOG_RESET, "cpu_reset",
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 2/9] devicetree: Added new APIs to make use of more fdt functions
  2016-08-22 16:17 [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Prem Mallappa
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 1/9] log: Add new IOMMU type Prem Mallappa
@ 2016-08-22 16:17 ` Prem Mallappa
  2016-09-09 16:02   ` Auger Eric
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 3/9] hw: arm: SMMUv3 emulation model Prem Mallappa
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Prem Mallappa @ 2016-08-22 16:17 UTC (permalink / raw)
  To: Peter Maydell, Edgar E . Iglesias, qemu-devel; +Cc: Prem Mallappa

SMMUv3 needs device tree entry like below

     interrupt-names = "gerror", "priq", "eventq", "cmdq-sync";

This patch introduces helper function to add entries like above

Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
---
 device_tree.c                | 35 +++++++++++++++++++++++++++++++++++
 include/sysemu/device_tree.h | 18 ++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/device_tree.c b/device_tree.c
index 6e06320..5d5966e 100644
--- a/device_tree.c
+++ b/device_tree.c
@@ -297,6 +297,24 @@ int qemu_fdt_setprop(void *fdt, const char *node_path,
     return r;
 }
 
+int qemu_fdt_appendprop(void *fdt, const char *node_path,
+                     const char *property, const void *val, int size)
+{
+    int r;
+
+    r = fdt_appendprop(fdt, findnode_nofail(fdt, node_path), property,
+                       val, size);
+    if (r < 0) {
+        error_report("%s: Couldn't set %s/%s: %s", __func__, node_path,
+                     property, fdt_strerror(r));
+        exit(1);
+    }
+
+    return r;
+}
+
+
+
 int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
                           const char *property, uint32_t val)
 {
@@ -319,6 +337,23 @@ int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
     return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val));
 }
 
+int qemu_fdt_appendprop_string(void *fdt, const char *node_path,
+                            const char *property, const char *string)
+{
+    int r;
+
+    r = fdt_appendprop_string(fdt, findnode_nofail(fdt, node_path),
+                              property, string);
+    if (r < 0) {
+        error_report("%s: Couldn't set %s/%s = %s: %s", __func__,
+                     node_path, property, string, fdt_strerror(r));
+        exit(1);
+    }
+
+    return r;
+}
+
+
 int qemu_fdt_setprop_string(void *fdt, const char *node_path,
                             const char *property, const char *string)
 {
diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h
index 705650a..5a0a297 100644
--- a/include/sysemu/device_tree.h
+++ b/include/sysemu/device_tree.h
@@ -45,12 +45,16 @@ char **qemu_fdt_node_path(void *fdt, const char *name, char *compat,
 
 int qemu_fdt_setprop(void *fdt, const char *node_path,
                      const char *property, const void *val, int size);
+int qemu_fdt_appendprop(void *fdt, const char *node_path,
+                     const char *property, const void *val, int size);
 int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
                           const char *property, uint32_t val);
 int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
                          const char *property, uint64_t val);
 int qemu_fdt_setprop_string(void *fdt, const char *node_path,
                             const char *property, const char *string);
+int qemu_fdt_appendprop_string(void *fdt, const char *node_path,
+                               const char *property, const char *string);
 int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
                              const char *property,
                              const char *target_node_path);
@@ -98,6 +102,20 @@ int qemu_fdt_add_subnode(void *fdt, const char *name);
                          sizeof(qdt_tmp));                                    \
     } while (0)
 
+
+#define qemu_fdt_appendprop_cells(fdt, node_path, property, ...)              \
+    do {                                                                      \
+        uint32_t qdt_tmp[] = { __VA_ARGS__ };                                 \
+        int i;                                                                \
+                                                                              \
+        for (i = 0; i < ARRAY_SIZE(qdt_tmp); i++) {                           \
+            qdt_tmp[i] = cpu_to_be32(qdt_tmp[i]);                             \
+        }                                                                     \
+        qemu_fdt_appendprop(fdt, node_path, property, qdt_tmp,                \
+                         sizeof(qdt_tmp));                                    \
+    } while (0)
+
+
 void qemu_fdt_dumpdtb(void *fdt, int size);
 
 /**
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 3/9] hw: arm: SMMUv3 emulation model
  2016-08-22 16:17 [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Prem Mallappa
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 1/9] log: Add new IOMMU type Prem Mallappa
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 2/9] devicetree: Added new APIs to make use of more fdt functions Prem Mallappa
@ 2016-08-22 16:17 ` Prem Mallappa
  2016-09-25 16:37   ` Edgar E. Iglesias
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 4/9] hw: arm: Added SMMUv3 files for build Prem Mallappa
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Prem Mallappa @ 2016-08-22 16:17 UTC (permalink / raw)
  To: Peter Maydell, Edgar E . Iglesias, qemu-devel; +Cc: Prem Mallappa

Big patch adds SMMUv3 model to Qemu
  - As per SMMUv3 spec 16.0
  - Works with SMMUv3 driver in Linux 4.7rc1
  - Only LPAE mode translation supported
  - BE mode is not supported yet
  - Stage1, Stage2 and S1+S2
  - Suspend/resume not tested

Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
---
 hw/arm/smmu-common.c     |  152 +++++
 hw/arm/smmu-common.h     |  141 +++++
 hw/arm/smmu-v3.c         | 1369 ++++++++++++++++++++++++++++++++++++++++++++++
 hw/arm/smmuv3-internal.h |  432 +++++++++++++++
 hw/vfio/common.c         |    2 +-
 5 files changed, 2095 insertions(+), 1 deletion(-)
 create mode 100644 hw/arm/smmu-common.c
 create mode 100644 hw/arm/smmu-common.h
 create mode 100644 hw/arm/smmu-v3.c
 create mode 100644 hw/arm/smmuv3-internal.h

diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
new file mode 100644
index 0000000..bf2039b
--- /dev/null
+++ b/hw/arm/smmu-common.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014-2016 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Prem Mallappa <pmallapp@broadcom.com>
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "sysemu/sysemu.h"
+#include "exec/address-spaces.h"
+
+#include "smmu-common.h"
+
+inline MemTxResult smmu_read_sysmem(hwaddr addr, void *buf, int len,
+                                    bool secure)
+{
+    MemTxAttrs attrs = {.unspecified = 1, .secure = secure};
+
+    switch (len) {
+    case 4:
+        *(uint32_t *)buf = ldl_le_phys(&address_space_memory, addr);
+        break;
+    case 8:
+        *(uint64_t *)buf = ldq_le_phys(&address_space_memory, addr);
+        break;
+    default:
+        return address_space_rw(&address_space_memory, addr,
+                                attrs, buf, len, false);
+    }
+    return MEMTX_OK;
+}
+
+inline void
+smmu_write_sysmem(hwaddr addr, void *buf, int len, bool secure)
+{
+    MemTxAttrs attrs = {.unspecified = 1, .secure = secure};
+
+    switch (len) {
+    case 4:
+        stl_le_phys(&address_space_memory, addr, *(uint32_t *)buf);
+        break;
+    case 8:
+        stq_le_phys(&address_space_memory, addr, *(uint64_t *)buf);
+        break;
+    default:
+        address_space_rw(&address_space_memory, addr,
+                         attrs, buf, len, true);
+    }
+}
+
+SMMUTransErr
+smmu_translate_64(SMMUTransCfg *cfg, uint32_t *pagesize,
+                  uint32_t *perm, bool is_write)
+{
+    int     ret, level;
+    int     stage      = cfg->stage;
+    int     granule_sz = cfg->granule_sz[stage];
+    int     va_size    = cfg->va_size[stage];
+    hwaddr  va, addr, mask;
+    hwaddr *outaddr;
+
+
+    va = addr = cfg->va;        /* or ipa in Stage2 */
+    SMMU_DPRINTF(TT_1, "stage:%d\n", stage);
+    assert(va_size == 64);      /* We dont support 32-bit yet */
+    /* same location, for clearity */
+    outaddr = &cfg->pa;
+
+    level = 4 - (va_size - cfg->tsz[stage] - 4) / granule_sz;
+
+    mask = (1ULL << (granule_sz + 3)) - 1;
+
+    addr = extract64(cfg->ttbr[stage], 0, 48);
+    addr &= ~((1ULL << (va_size - cfg->tsz[stage] -
+                        (granule_sz * (4 - level)))) - 1);
+
+    for (;;) {
+        uint64_t desc;
+#ifdef ARM_SMMU_DEBUG
+        uint64_t ored = (va >> (granule_sz * (4 - level))) & mask;
+        SMMU_DPRINTF(TT_1,
+                     "Level: %d va:%lx addr:%lx ored:%lx\n",
+                     level, va, addr, ored);
+#endif
+        addr |= (va >> (granule_sz * (4 - level))) & mask;
+        addr &= ~7ULL;
+
+        if (smmu_read_sysmem(addr, &desc, sizeof(desc), false)) {
+            ret = SMMU_TRANS_ERR_WALK_EXT_ABRT;
+            SMMU_DPRINTF(CRIT, "Translation table read error lvl:%d\n", level);
+            break;
+        }
+
+        SMMU_DPRINTF(TT_1,
+                     "Level: %d gran_sz:%d mask:%lx addr:%lx desc:%lx\n",
+                     level, granule_sz, mask, addr, desc);
+
+        if (!(desc & 1) ||
+            (!(desc & 2) && (level == 3))) {
+            ret = SMMU_TRANS_ERR_TRANS;
+            break;
+        }
+
+        /* We call again to resolve address at this 'level' */
+        if (cfg->s2_needed) {
+            uint32_t perm_s2, pagesize_s2;
+            SMMUTransCfg s2cfg = *cfg;
+
+            s2cfg.stage++;
+            s2cfg.va = desc;
+            s2cfg.s2_needed = false;
+
+            ret = smmu_translate_64(&s2cfg, &pagesize_s2,
+                                    &perm_s2, is_write);
+            if (ret) {
+                break;
+            }
+
+            desc = (uint64_t)s2cfg.pa;
+            SMMU_DPRINTF(TT_2, "addr:%lx pagesize:%x\n", addr, *pagesize);
+        }
+
+        addr = desc & 0xffffffff000ULL;
+        if ((desc & 2) && (level < 3)) {
+            level++;
+            continue;
+        }
+        *pagesize = (1ULL << ((granule_sz * (4 - level)) + 3));
+        addr |= (va & (*pagesize - 1));
+        SMMU_DPRINTF(TT_1, "addr:%lx pagesize:%x\n", addr, *pagesize);
+        break;
+    }
+
+    if (ret == 0) {
+        *outaddr = addr;
+    }
+
+    return ret;
+}
diff --git a/hw/arm/smmu-common.h b/hw/arm/smmu-common.h
new file mode 100644
index 0000000..91f7194
--- /dev/null
+++ b/hw/arm/smmu-common.h
@@ -0,0 +1,141 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2015-2016 Broadcom Corporation
+ *
+ * Author: Prem Mallappa <pmallapp@broadcom.com>
+ *
+ */
+#ifndef HW_ARM_SMMU_COMMON_H
+#define HW_ARM_SMMU_COMMON_H
+
+#include <qemu/log.h>
+#include <hw/sysbus.h>
+
+#define TYPE_SMMU_DEV_BASE "smmu-base"
+#define TYPE_SMMU_V3_DEV   "smmuv3"
+
+typedef struct SMMUState {
+    /* <private> */
+    SysBusDevice  dev;
+
+    uint32_t cid[4];            /* Coresight registers */
+    uint32_t pid[8];
+
+    MemoryRegion iomem;
+} SMMUState;
+
+#define SMMU_SYS_DEV(obj) OBJECT_CHECK(SMMUState, (obj), TYPE_SMMU_DEV_BASE)
+
+typedef enum {
+    SMMU_TRANS_ERR_WALK_EXT_ABRT = 0x1,  /* Translation walk external abort */
+    SMMU_TRANS_ERR_TRANS         = 0x10, /* Translation fault */
+    SMMU_TRANS_ERR_ADDR_SZ,              /* Address Size fault */
+    SMMU_TRANS_ERR_ACCESS,               /* Access fault */
+    SMMU_TRANS_ERR_PERM,                 /* Permission fault */
+    SMMU_TRANS_ERR_TLB_CONFLICT  = 0x20, /* TLB Conflict */
+} SMMUTransErr;
+
+
+/*
+ * This needs to be populated by SMMUv2 and SMMUv3
+ * each do it in their own way
+ * translate functions use it to call translations
+ */
+typedef struct SMMUTransCfg {
+    hwaddr   va;                        /* Input to S1 */
+    int      stage;
+    uint32_t oas[3];
+    uint32_t tsz[3];
+    uint64_t ttbr[3];
+    uint32_t granule[3];
+    uint32_t va_size[3];
+    uint32_t granule_sz[3];
+
+    hwaddr pa;                          /* Output from S1, Final PA */
+    bool    s2_needed;
+} SMMUTransCfg;
+
+struct SMMUTransReq {
+    uint32_t stage;
+    SMMUTransCfg cfg[2];
+};
+
+typedef struct {
+    /* <private> */
+    SysBusDeviceClass parent_class;
+
+    /* public */
+    SMMUTransErr (*translate_32)(SMMUTransCfg *cfg, uint32_t *pagesize,
+                                 uint32_t *perm, bool is_write);
+    SMMUTransErr (*translate_64)(SMMUTransCfg *cfg, uint32_t *pagesize,
+                                 uint32_t *perm, bool is_write);
+} SMMUBaseClass;
+
+#define SMMU_DEVICE_GET_CLASS(obj)                              \
+    OBJECT_GET_CLASS(SMMUBaseClass, (obj), TYPE_SMMU_DEV_BASE)
+
+/* #define ARM_SMMU_DEBUG */
+#ifdef ARM_SMMU_DEBUG
+
+extern uint32_t  dbg_bits;
+
+#define HERE()  printf("%s:%d\n", __func__, __LINE__)
+
+enum {
+    SMMU_DBG_PANIC, SMMU_DBG_CRIT, SMMU_DBG_WARN, /* error level */
+    SMMU_DBG_DBG1, SMMU_DBG_DBG2, SMMU_DBG_INFO, /* info level */
+    SMMU_DBG_CMDQ,                               /* Just command queue */
+    SMMU_DBG_STE, SMMU_DBG_CD,                   /* Specific parts STE/CD */
+    SMMU_DBG_TT_1, SMMU_DBG_TT_2,                /* Translation Stage 1/2 */
+    SMMU_DBG_IRQ,                                /* IRQ  */
+};
+
+#define DBG_BIT(bit)    (1 << SMMU_DBG_##bit)
+
+#define IS_DBG_ENABLED(bit) (dbg_bits & (1 << SMMU_DBG_##bit))
+
+#define DBG_DEFAULT  (DBG_BIT(PANIC) | DBG_BIT(CRIT) | DBG_BIT(IRQ))
+#define DBG_EXTRA    (DBG_BIT(STE) | DBG_BIT(CD) | DBG_BIT(TT_1))
+#define DBG_VERBOSE1 DBG_BIT(DBG1)
+#define DBG_VERBOSE2 (DBG_VERBOSE1 | DBG_BIT(DBG1))
+#define DBG_VERBOSE3 (DBG_VERBOSE2 | DBG_BIT(DBG2))
+#define DBG_VERBOSE4 (DBG_VERBOSE3 | DBG_BIT(INFO))
+
+#define SMMU_DPRINTF(lvl, fmt, ...)             \
+    do {                                        \
+        if (dbg_bits & DBG_BIT(lvl)) {          \
+            qemu_log_mask(CPU_LOG_IOMMU,        \
+                          "(smmu)%s: " fmt ,    \
+                          __func__,             \
+                          ## __VA_ARGS__);      \
+        }                                       \
+    } while (0)
+
+#else
+#define IS_DBG_ENABLED(bit) false
+#define SMMU_DPRINTF(lvl, fmt, ...)
+
+#endif  /* SMMU_DEBUG */
+
+SMMUTransErr smmu_translate_64(SMMUTransCfg *cfg, uint32_t *pagesize,
+                               uint32_t *perm, bool is_write);
+
+SMMUTransErr smmu_translate_32(SMMUTransCfg *cfg, uint32_t *pagesize,
+                               uint32_t *perm, bool is_write);
+
+MemTxResult smmu_read_sysmem(hwaddr addr, void *buf, int len, bool secure);
+void smmu_write_sysmem(hwaddr addr, void *buf, int len, bool secure);
+
+#endif  /* HW_ARM_SMMU_COMMON */
diff --git a/hw/arm/smmu-v3.c b/hw/arm/smmu-v3.c
new file mode 100644
index 0000000..7260468
--- /dev/null
+++ b/hw/arm/smmu-v3.c
@@ -0,0 +1,1369 @@
+/*
+ * Copyright (C) 2014-2016 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Prem Mallappa <pmallapp@broadcom.com>
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/boards.h"
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+#include "hw/pci/pci.h"
+#include "exec/address-spaces.h"
+
+#include "hw/arm/smmu.h"
+#include "smmu-common.h"
+#include "smmuv3-internal.h"
+
+#define SMMU_NREGS       0x200
+#define PCI_BUS_MAX      256
+#define PCI_DEVFN_MAX    256
+
+#ifdef ARM_SMMU_DEBUG
+uint32_t dbg_bits =                             \
+    DBG_DEFAULT |                               \
+    DBG_VERBOSE3 |                              \
+    DBG_EXTRA |                                 \
+    DBG_VERBOSE1;
+#else
+const uint32_t dbg_bits;
+#endif
+
+typedef struct SMMUDevice SMMUDevice;
+
+struct SMMUDevice {
+    void         *smmu;
+    PCIBus       *bus;
+    int           devfn;
+    MemoryRegion  iommu;
+    AddressSpace  as;
+};
+
+typedef struct SMMUPciBus SMMUPciBus;
+struct SMMUPciBus {
+    PCIBus       *bus;
+    SMMUDevice   *pbdev[0]; /* Parent array is sparse, so dynamically alloc */
+};
+
+typedef struct SMMUV3State SMMUV3State;
+
+struct SMMUV3State {
+    SMMUState     smmu_state;
+
+#define SMMU_FEATURE_2LVL_STE (1 << 0)
+    /* Local cache of most-frequently used register */
+    uint32_t     features;
+    uint16_t     sid_size;
+    uint16_t     sid_split;
+    uint64_t     strtab_base;
+
+    uint64_t    regs[SMMU_NREGS];
+
+    qemu_irq     irq[4];
+
+    SMMUQueue    cmdq, evtq, priq;
+
+    /* IOMMU Address space */
+    MemoryRegion iommu;
+    AddressSpace iommu_as;
+    /*
+     * Bus number is not populated in the beginning, hence we need
+     * a mechanism to retrieve the corresponding address space for each
+     * pci device.
+    */
+    GHashTable   *smmu_as_by_busptr;
+};
+
+#define SMMU_V3_DEV(obj) OBJECT_CHECK(SMMUV3State, (obj), TYPE_SMMU_V3_DEV)
+
+static void smmu_write64_reg(SMMUV3State *s, uint32_t addr, uint64_t val)
+{
+    addr >>= 2;
+    s->regs[addr] = val & 0xFFFFFFFFULL;
+    s->regs[addr + 1] = val & ~0xFFFFFFFFULL;
+}
+
+static void smmu_write_reg(SMMUV3State *s, uint32_t addr, uint64_t val)
+{
+    s->regs[addr >> 2] = val;
+}
+
+static inline uint32_t smmu_read_reg(SMMUV3State *s, uint32_t addr)
+{
+    return s->regs[addr >> 2];
+}
+
+static inline uint64_t smmu_read64_reg(SMMUV3State *s, uint32_t addr)
+{
+    addr >>= 2;
+    return s->regs[addr] | (s->regs[addr + 1] << 32);
+}
+
+#define smmu_read32_reg smmu_read_reg
+#define smmu_write32_reg smmu_write_reg
+
+static inline int smmu_enabled(SMMUV3State *s)
+{
+    return (smmu_read32_reg(s, SMMU_REG_CR0) & SMMU_CR0_SMMU_ENABLE) != 0;
+}
+
+typedef enum {
+    CMD_Q_EMPTY,
+    CMD_Q_FULL,
+    CMD_Q_INUSE,
+} SMMUQStatus;
+
+static inline SMMUQStatus
+__smmu_queue_status(SMMUV3State *s, SMMUQueue *q)
+{
+    uint32_t prod = Q_IDX(q, q->prod), cons = Q_IDX(q, q->cons);
+    if ((prod == cons) && (q->wrap.prod != q->wrap.cons)) {
+        return CMD_Q_FULL;
+    } else if ((prod == cons) && (q->wrap.prod == q->wrap.cons)) {
+        return CMD_Q_EMPTY;
+    }
+    return CMD_Q_INUSE;
+}
+#define smmu_is_q_full(s, q) (__smmu_queue_status(s, q) == CMD_Q_FULL)
+#define smmu_is_q_empty(s, q) (__smmu_queue_status(s, q) == CMD_Q_EMPTY)
+
+static int __smmu_q_enabled(SMMUV3State *s, uint32_t q)
+{
+    return smmu_read32_reg(s, SMMU_REG_CR0) & q;
+}
+#define smmu_cmd_q_enabled(s) __smmu_q_enabled(s, SMMU_CR0_CMDQ_ENABLE)
+#define smmu_evt_q_enabled(s) __smmu_q_enabled(s, SMMU_CR0_EVTQ_ENABLE)
+
+static inline int __smmu_irq_enabled(SMMUV3State *s, uint32_t q)
+{
+    return smmu_read64_reg(s, SMMU_REG_IRQ_CTRL) & q;
+}
+#define smmu_evt_irq_enabled(s)                   \
+    __smmu_irq_enabled(s, SMMU_IRQ_CTRL_EVENT_EN)
+#define smmu_gerror_irq_enabled(s)                  \
+    __smmu_irq_enabled(s, SMMU_IRQ_CTRL_GERROR_EN)
+#define smmu_pri_irq_enabled(s)                 \
+    __smmu_irq_enabled(s, SMMU_IRQ_CTRL_PRI_EN)
+
+
+static inline int is_cd_valid(SMMUV3State *s, Ste *ste, Cd *cd)
+{
+    return CD_VALID(cd);
+}
+
+static inline int is_ste_valid(SMMUV3State *s, Ste *ste)
+{
+    return STE_VALID(ste);
+}
+
+static inline int is_ste_bypass(SMMUV3State *s, Ste *ste)
+{
+    return STE_CONFIG(ste) == STE_CONFIG_BYPASS;
+}
+
+static inline uint16_t smmu_get_sid(SMMUDevice *sdev)
+{
+    return  ((pci_bus_num(sdev->bus) & 0xff) << 8) | sdev->devfn;
+}
+
+static void smmu_coresight_regs_init(SMMUV3State *sv3)
+{
+    SMMUState *s = SMMU_SYS_DEV(sv3);
+    int i;
+
+    /* Primecell ID registers */
+    s->cid[0] = 0x0D;
+    s->cid[1] = 0xF0;
+    s->cid[2] = 0x05;
+    s->cid[3] = 0xB1;
+
+    for (i = 0; i < ARRAY_SIZE(s->pid); i++) {
+        s->pid[i] = 0x1;
+    }
+}
+
+/*
+ * smmu_irq_update:
+ * update corresponding register,
+ * return > 0 when IRQ is supposed to be rased
+ * Spec req:
+ *      - Raise irq only when it not active already,
+ *        blindly toggling bits may actually clear the error
+ */
+static int
+smmu_irq_update(SMMUV3State *s, int irq, uint64_t data)
+{
+    uint32_t error = 0;
+
+    switch (irq) {
+    case SMMU_IRQ_EVTQ:
+        if (smmu_evt_irq_enabled(s)) {
+            error = SMMU_GERROR_EVENTQ;
+        }
+        break;
+    case SMMU_IRQ_CMD_SYNC:
+        if (smmu_gerror_irq_enabled(s)) {
+            uint32_t err_type = (uint32_t)data;
+            if (err_type) {
+                uint32_t regval = smmu_read32_reg(s, SMMU_REG_CMDQ_CONS);
+                smmu_write32_reg(s, SMMU_REG_CMDQ_CONS,
+                                 regval | err_type << SMMU_CMD_CONS_ERR_SHIFT);
+            }
+            error = SMMU_GERROR_CMDQ;
+        }
+        break;
+    case SMMU_IRQ_PRIQ:
+        if (smmu_pri_irq_enabled(s)) {
+            error = SMMU_GERROR_PRIQ;
+        }
+        break;
+    }
+    SMMU_DPRINTF(IRQ, "<< error:%x\n", error);
+
+    if (error && smmu_gerror_irq_enabled(s)) {
+        uint32_t gerror = smmu_read32_reg(s, SMMU_REG_GERROR);
+        uint32_t gerrorn = smmu_read32_reg(s, SMMU_REG_GERRORN);
+        SMMU_DPRINTF(IRQ, "<<<< error:%x gerror:%x gerrorn:%x\n",
+                     error, gerror, gerrorn);
+        if (!((gerror ^ gerrorn) & error)) {
+            smmu_write32_reg(s, SMMU_REG_GERROR, gerror ^ error);
+        }
+    }
+
+    return error;
+}
+
+static void smmu_irq_raise(SMMUV3State *s, int irq, uint64_t data)
+{
+    SMMU_DPRINTF(IRQ, "irq:%d\n", irq);
+    if (smmu_irq_update(s, irq, data)) {
+            qemu_irq_raise(s->irq[irq]);
+    }
+}
+
+static MemTxResult smmu_q_read(SMMUV3State *s, SMMUQueue *q, void *data)
+{
+    uint64_t addr = Q_ENTRY(q, Q_IDX(q, q->cons));
+
+    q->cons++;
+    if (q->cons == q->entries) {
+        q->cons = 0;
+        q->wrap.cons++;     /* this will toggle */
+    }
+
+    return smmu_read_sysmem(addr, data, q->ent_size, false);
+}
+
+static MemTxResult smmu_q_write(SMMUV3State *s, SMMUQueue *q, void *data)
+{
+    uint64_t addr = Q_ENTRY(q, Q_IDX(q, q->prod));
+
+    if (q->prod == q->entries) {
+        q->prod = 0;
+        q->wrap.prod++;     /* this will toggle */
+    }
+
+    q->prod++;
+
+    smmu_write_sysmem(addr, data, q->ent_size, false);
+
+    return MEMTX_OK;
+}
+
+static MemTxResult smmu_read_cmdq(SMMUV3State *s, Cmd *cmd)
+{
+    SMMUQueue *q = &s->cmdq;
+    MemTxResult ret = smmu_q_read(s, q, cmd);
+    uint32_t val = 0;
+
+    val |= (q->wrap.cons << q->shift) | q->cons;
+
+    /* Update consumer pointer */
+    smmu_write32_reg(s, SMMU_REG_CMDQ_CONS, val);
+
+    return ret;
+}
+
+#define SMMU_CMDQ_ERR(s) ((smmu_read32_reg(s, SMMU_REG_GERROR) ^    \
+                           smmu_read32_reg(s, SMMU_REG_GERRORN)) &  \
+                          SMMU_GERROR_CMDQ)
+
+static int smmu_cmdq_consume(SMMUV3State *s)
+{
+    uint32_t error = SMMU_CMD_ERR_NONE;
+
+    SMMU_DPRINTF(CMDQ, "CMDQ_ERR: %d\n", SMMU_CMDQ_ERR(s));
+
+    if (!smmu_cmd_q_enabled(s))
+        goto out_while;
+
+    while (!SMMU_CMDQ_ERR(s) && !smmu_is_q_empty(s, &s->cmdq)) {
+        Cmd cmd;
+#ifdef ARM_SMMU_DEBUG
+        SMMUQueue *q = &s->cmdq;
+#endif
+        if (smmu_read_cmdq(s, &cmd) != MEMTX_OK) {
+            error = SMMU_CMD_ERR_ABORT;
+            goto out_while;
+        }
+
+        SMMU_DPRINTF(DBG2, "CMDQ base: %lx cons:%d prod:%d val:%x wrap:%d\n",
+                     q->base, q->cons, q->prod, cmd.word[0], q->wrap.cons);
+
+        switch (CMD_TYPE(&cmd)) {
+        case SMMU_CMD_CFGI_STE:
+        case SMMU_CMD_CFGI_STE_RANGE:
+            break;
+        case SMMU_CMD_TLBI_NSNH_ALL: /* TLB not implemented */
+        case SMMU_CMD_TLBI_EL2_ALL:  /* Fallthrough */
+        case SMMU_CMD_TLBI_EL3_ALL:
+        case SMMU_CMD_TLBI_NH_ALL:
+        case SMMU_CMD_TLBI_S2_IPA:
+            break;
+        case SMMU_CMD_SYNC:     /* Fallthrough */
+            if (CMD_CS(&cmd) & CMD_SYNC_SIG_IRQ) {
+                smmu_irq_raise(s, SMMU_IRQ_CMD_SYNC, SMMU_CMD_ERR_NONE);
+            }
+            break;
+        case SMMU_CMD_PREFETCH_CONFIG:
+            break;
+        case SMMU_CMD_TLBI_NH_ASID:
+        case SMMU_CMD_TLBI_NH_VA:   /* too many of this is sent */
+            break;
+
+        default:
+            error = SMMU_CMD_ERR_ILLEGAL;
+            SMMU_DPRINTF(CRIT, "Unknown Command type: %x, ignoring\n",
+                         CMD_TYPE(&cmd));
+            if (IS_DBG_ENABLED(CD)) {
+                dump_cmd(&cmd);
+            }
+            break;
+        }
+
+        if (error != SMMU_CMD_ERR_NONE) {
+            SMMU_DPRINTF(INFO, "CMD Error\n");
+            goto out_while;
+        }
+    }
+
+out_while:
+    if (error) {
+        smmu_irq_raise(s, SMMU_IRQ_GERROR, error);
+    }
+
+    SMMU_DPRINTF(CMDQ, "prod_wrap:%d, prod:%x cons_wrap:%d cons:%x\n",
+                 s->cmdq.wrap.prod, s->cmdq.prod,
+                 s->cmdq.wrap.cons, s->cmdq.cons);
+
+    return 0;
+}
+
+static inline bool
+smmu_is_irq_pending(SMMUV3State *s, int irq)
+{
+    return smmu_read32_reg(s, SMMU_REG_GERROR) ^
+        smmu_read32_reg(s, SMMU_REG_GERRORN);
+}
+
+/*
+ * GERROR is updated when rasing an interrupt, GERRORN will be updated
+ * by s/w and should match GERROR before normal operation resumes.
+ */
+static void smmu_irq_clear(SMMUV3State *s, uint64_t gerrorn)
+{
+    int irq = SMMU_IRQ_GERROR;
+    uint32_t toggled;
+
+    toggled = smmu_read32_reg(s, SMMU_REG_GERRORN) ^ gerrorn;
+
+    while (toggled) {
+        irq = ctz32(toggled);
+
+        qemu_irq_lower(s->irq[irq]);
+
+        toggled &= toggled - 1;
+    }
+}
+
+static int smmu_evtq_update(SMMUV3State *s)
+{
+    if (!smmu_enabled(s)) {
+        return 0;
+    }
+
+    if (!smmu_is_q_empty(s, &s->evtq)) {
+        if (smmu_evt_irq_enabled(s))
+            smmu_irq_raise(s, SMMU_IRQ_EVTQ, 0);
+    }
+
+    if (smmu_is_q_empty(s, &s->evtq)) {
+        smmu_irq_clear(s, SMMU_GERROR_EVENTQ);
+    }
+
+    return 1;
+}
+
+static void smmu_create_event(SMMUV3State *s, hwaddr iova,
+                              uint32_t sid, bool is_write, int error);
+
+static void smmu_update(SMMUV3State *s)
+{
+    int error = 0;
+
+    /* SMMU starts processing commands even when not enabled */
+    if (!smmu_enabled(s)) {
+        goto check_cmdq;
+    }
+
+    /* EVENT Q updates takes more priority */
+    if ((smmu_evt_q_enabled(s)) && !smmu_is_q_empty(s, &s->evtq)) {
+        SMMU_DPRINTF(CRIT, "q empty:%d prod:%d cons:%d p.wrap:%d p.cons:%d\n",
+                     smmu_is_q_empty(s, &s->evtq), s->evtq.prod,
+                     s->evtq.cons, s->evtq.wrap.prod, s->evtq.wrap.cons);
+        error = smmu_evtq_update(s);
+    }
+
+    if (error) {
+        /* TODO: May be in future we create proper event queue entry */
+        /* an error condition is not a recoverable event, like other devices */
+        SMMU_DPRINTF(CRIT, "An unfavourable condition\n");
+        smmu_create_event(s, 0, 0, 0, error);
+    }
+
+check_cmdq:
+    if (smmu_cmd_q_enabled(s) && !SMMU_CMDQ_ERR(s)) {
+        smmu_cmdq_consume(s);
+    } else {
+        SMMU_DPRINTF(INFO, "cmdq not enabled or error :%x\n", SMMU_CMDQ_ERR(s));
+    }
+
+}
+
+static void smmu_update_irq(SMMUV3State *s, uint64_t addr, uint64_t val)
+{
+    smmu_irq_clear(s, val);
+
+    smmu_write32_reg(s, SMMU_REG_GERRORN, val);
+
+    SMMU_DPRINTF(IRQ, "irq pend: %d gerror:%x gerrorn:%x\n",
+                 smmu_is_irq_pending(s, 0),
+                 smmu_read32_reg(s, SMMU_REG_GERROR),
+                 smmu_read32_reg(s, SMMU_REG_GERRORN));
+
+    /* Clear only when no more left */
+    if (!smmu_is_irq_pending(s, 0)) {
+        qemu_irq_lower(s->irq[0]);
+    }
+}
+
+#define SMMU_ID_REG_INIT(s, reg, d) do {        \
+    s->regs[reg >> 2] = d;                      \
+    } while (0)
+
+static void smmuv3_id_reg_init(SMMUV3State *s)
+{
+    uint32_t data =
+        1 << 27 |                   /* 2 Level stream id */
+        1 << 26 |                   /* Term Model  */
+        1 << 24 |                   /* Stall model not supported */
+        1 << 18 |                   /* VMID 16 bits */
+        1 << 16 |                   /* PRI */
+        1 << 12 |                   /* ASID 16 bits */
+        1 << 10 |                   /* ATS */
+        1 << 9 |                    /* HYP */
+        2 << 6 |                    /* HTTU */
+        1 << 4 |                    /* COHACC */
+        2 << 2 |                    /* TTF=Arch64 */
+        1 << 1 |                    /* Stage 1 */
+        1 << 0;                     /* Stage 2 */
+
+    SMMU_ID_REG_INIT(s, SMMU_REG_IDR0, data);
+
+#define SMMU_SID_SIZE         16
+#define SMMU_QUEUE_SIZE_LOG2  19
+    data =
+        1 << 27 |                    /* Attr Types override */
+        SMMU_QUEUE_SIZE_LOG2 << 21 | /* Cmd Q size */
+        SMMU_QUEUE_SIZE_LOG2 << 16 | /* Event Q size */
+        SMMU_QUEUE_SIZE_LOG2 << 11 | /* PRI Q size */
+        0  << 6 |                    /* SSID not supported */
+        SMMU_SID_SIZE << 0 ;         /* SID size  */
+
+    SMMU_ID_REG_INIT(s, SMMU_REG_IDR1, data);
+
+    data =
+        1 << 6 |                    /* Granule 64K */
+        1 << 4 |                    /* Granule 4K */
+        4 << 0;                     /* OAS = 44 bits */
+
+    SMMU_ID_REG_INIT(s, SMMU_REG_IDR5, data);
+
+}
+
+static void smmuv3_init(SMMUV3State *s)
+{
+    smmu_coresight_regs_init(s);
+
+    smmuv3_id_reg_init(s);      /* Update ID regs alone */
+
+    s->sid_size = SMMU_SID_SIZE;
+
+    s->cmdq.entries = (smmu_read32_reg(s, SMMU_REG_IDR1) >> 21) & 0x1f;
+    s->cmdq.ent_size = sizeof(Cmd);
+    s->evtq.entries = (smmu_read32_reg(s, SMMU_REG_IDR1) >> 16) & 0x1f;
+    s->evtq.ent_size = sizeof(Evt);
+}
+
+/*
+ * All SMMU data structures are little endian, and are aligned to 8 bytes
+ * L1STE/STE/L1CD/CD, Queue entries in CMDQ/EVTQ/PRIQ
+ */
+static inline int smmu_get_ste(SMMUV3State *s, hwaddr addr, Ste *buf)
+{
+    return dma_memory_read(&address_space_memory, addr, buf, sizeof(*buf));
+}
+
+/*
+ * For now we only support CD with a single entry, 'ssid' is used to identify
+ * otherwise
+ */
+static inline int smmu_get_cd(SMMUV3State *s, Ste *ste, uint32_t ssid, Cd *buf)
+{
+    hwaddr addr = STE_CTXPTR(ste);
+
+    if (STE_S1CDMAX(ste) != 0) {
+        SMMU_DPRINTF(CRIT, "Multilevel Ctx Descriptor not supported yet\n");
+    }
+
+    return dma_memory_read(&address_space_memory, addr, buf, sizeof(*buf));
+}
+
+static int
+is_ste_consistent(SMMUV3State *s, Ste *ste)
+{
+    uint32_t _config = STE_CONFIG(ste) & 0x7,
+        idr0 = smmu_read32_reg(s, SMMU_REG_IDR0),
+        idr5 = smmu_read32_reg(s, SMMU_REG_IDR5);
+
+    uint32_t httu = extract32(idr0, 6, 2);
+    bool config[] = {_config & 0x1,
+                     _config & 0x2,
+                     _config & 0x3};
+    bool granule_supported;
+
+    bool s1p = idr0 & SMMU_IDR0_S1P,
+        s2p = idr0 & SMMU_IDR0_S2P,
+        hyp = idr0 & SMMU_IDR0_HYP,
+        cd2l = idr0 & SMMU_IDR0_CD2L,
+        idr0_vmid = idr0 & SMMU_IDR0_VMID16,
+        ats = idr0 & SMMU_IDR0_ATS,
+        ttf0 = (idr0 >> 2) & 0x1,
+        ttf1 = (idr0 >> 3) & 0x1;
+
+    int ssidsz = (smmu_read32_reg(s, SMMU_REG_IDR1) >> 6) & 0x1f;
+
+    uint32_t ste_vmid = STE_S2VMID(ste),
+        ste_eats = STE_EATS(ste),
+        ste_s2s = STE_S2S(ste),
+        ste_s1fmt = STE_S1FMT(ste),
+        aa64 = STE_S2AA64(ste),
+        ste_s1cdmax = STE_S1CDMAX(ste);
+
+    uint8_t ste_strw = STE_STRW(ste);
+    uint64_t oas, max_pa;
+    bool strw_ign;
+    bool addr_out_of_range;
+
+    if (!STE_VALID(ste)) {
+        SMMU_DPRINTF(STE, "STE NOT valid\n");
+        return false;
+    }
+
+    switch (STE_S2TG(ste)) {
+    case 1:
+        granule_supported = 0x4; break;
+    case 2:
+        granule_supported = 0x2; break;
+    case 0:
+        granule_supported = 0x1; break;
+    }
+    granule_supported &= (idr5 >> 4);
+
+    if (!config[2]) {
+        if ((!s1p && config[0]) ||
+            (!s2p && config[1]) ||
+            (s2p && config[1])) {
+            SMMU_DPRINTF(STE, "STE inconsistant, S2P mismatch\n");
+            return false;
+        }
+        if (!ssidsz && ste_s1cdmax && config[0] && !cd2l &&
+            (ste_s1fmt == 1 || ste_s1fmt == 2)) {
+            SMMU_DPRINTF(STE, "STE inconsistant, CD mismatch\n");
+            return false;
+        }
+        if (ats && ((_config & 0x3) == 0) &&
+            ((ste_eats == 2 && (_config != 0x7 || ste_s2s)) ||
+             (ste_eats == 1 && !ste_s2s))) {
+            SMMU_DPRINTF(STE, "STE inconsistant, EATS/S2S mismatch\n");
+            return false;
+        }
+        if (config[0] && (ssidsz && (ste_s1cdmax > ssidsz))) {
+            SMMU_DPRINTF(STE, "STE inconsistant, SSID out of range\n");
+            return false;
+        }
+    }
+
+    oas = MIN(STE_S2PS(ste), idr5 & 0x7);
+
+    if (oas == 3) {
+        max_pa = deposit64(0, 0, 42, ~0UL);
+    } else {
+        max_pa = deposit64(0, 0, (32 + (oas * 4)), ~0UL);
+    }
+
+    strw_ign = (!s1p || !hyp || (_config == 4));
+
+    addr_out_of_range = (int64_t)(max_pa - STE_S2TTB(ste)) < 0;
+
+    if (config[1] && (
+        (aa64 && !granule_supported) ||
+        (!aa64 && !ttf0) ||
+        (aa64 && !ttf1)  ||
+        ((STE_S2HA(ste) || STE_S2HD(ste)) && !aa64) ||
+        ((STE_S2HA(ste) || STE_S2HD(ste)) && !httu) ||
+        (STE_S2HD(ste) && (httu == 1)) ||
+        addr_out_of_range)) {
+        SMMU_DPRINTF(STE, "STE inconsistant\n");
+        SMMU_DPRINTF(STE, "config[1]:%d gran:%d addr:%d\n"
+                     " aa64:%d ttf0:%d ttf1:%d s2ha:%d s2hd:%d httu:%d\n",
+                     config[1], granule_supported,
+                     addr_out_of_range, aa64, ttf0, ttf1, STE_S2HA(ste),
+                     STE_S2HD(ste), httu);
+        SMMU_DPRINTF(STE, "maxpa:%lx s2ttb:%lx\n", max_pa, STE_S2TTB(ste));
+        return false;
+    }
+    if (s2p && (config[0] == 0 && config[1]) &&
+        (strw_ign || !ste_strw) && !idr0_vmid && !(ste_vmid >> 8)) {
+        SMMU_DPRINTF(STE, "STE inconsistant, VMID out of range\n");
+        return false;
+    }
+
+    return true;
+}
+
+static int tg2granule(int bits, bool tg1)
+{
+    switch (bits) {
+    case 1:
+        return tg1 ? 14 : 16;
+    case 2:
+        return tg1 ? 14 : 12;
+    case 3:
+        return tg1 ? 16 : 12;
+    default:
+        return 12;
+    }
+}
+
+static inline int oas2bits(int oas)
+{
+    switch (oas) {
+    case 2:
+        return 40;
+    case 3:
+        return 42;
+    case 4:
+        return 44;
+    case 5:
+    default: return 48;
+    }
+}
+
+#define STM2U64(stm) ({                                 \
+            uint64_t hi, lo;                            \
+            hi = (stm)->word[1];                        \
+            lo = (stm)->word[0] & ~(uint64_t)0x1f;      \
+            hi << 32 | lo;                              \
+        })
+
+#define STMSPAN(stm) (1 << (extract32((stm)->word[0], 0, 4) - 1))
+
+static int smmu_find_ste(SMMUV3State *s, uint16_t sid, Ste *ste)
+{
+    hwaddr addr;
+
+    SMMU_DPRINTF(STE, "SID:%x\n", sid);
+    /* Check SID range */
+    if (sid > (1 << s->sid_size)) {
+        return SMMU_EVT_C_BAD_SID;
+    }
+    SMMU_DPRINTF(STE, "features:%x\n", s->features);
+    if (s->features & SMMU_FEATURE_2LVL_STE) {
+        int span;
+        hwaddr stm_addr;
+        STEDesc stm;
+        int l1_ste_offset, l2_ste_offset;
+        SMMU_DPRINTF(STE, "no. ste: %x\n", s->sid_split);
+
+        l1_ste_offset = sid >> s->sid_split;
+        l2_ste_offset = sid & ((1 << s->sid_split) - 1);
+        SMMU_DPRINTF(STE, "l1_off:%x, l2_off:%x\n", l1_ste_offset,
+                     l2_ste_offset);
+        stm_addr = (hwaddr)(s->strtab_base + l1_ste_offset * sizeof(stm));
+        smmu_read_sysmem(stm_addr, &stm, sizeof(stm), false);
+
+        SMMU_DPRINTF(STE, "strtab_base:%lx stm_addr:%lx\n"
+                     "l1_ste_offset:%x l1(64):%#016lx\n",
+                     s->strtab_base, stm_addr, l1_ste_offset,
+                     STM2U64(&stm));
+
+        span = STMSPAN(&stm);
+        SMMU_DPRINTF(STE, "l2_ste_offset:%x ~ span:%d\n", l2_ste_offset, span);
+        if (l2_ste_offset > span) {
+            SMMU_DPRINTF(CRIT, "l2_ste_offset > span\n");
+            return SMMU_EVT_C_BAD_STE;
+        }
+        addr = STM2U64(&stm) + l2_ste_offset * sizeof(*ste);
+    } else {
+        addr = s->strtab_base + sid * sizeof(*ste);
+    }
+    SMMU_DPRINTF(STE, "ste:%lx\n", addr);
+    if (smmu_get_ste(s, addr, ste)) {
+        SMMU_DPRINTF(CRIT, "Unable to Fetch STE\n");
+        return SMMU_EVT_F_UUT;
+    }
+
+    return 0;
+}
+
+static void smmu_cfg_populate_s2(SMMUTransCfg *cfg, Ste *ste)
+{                           /* stage 2 cfg */
+    bool s2a64 = STE_S2AA64(ste);
+    const int stage = 2;
+
+    cfg->granule[stage] = STE_S2TG(ste);
+    cfg->tsz[stage] = STE_S2T0SZ(ste);
+    cfg->ttbr[stage] = STE_S2TTB(ste);
+    cfg->oas[stage] = oas2bits(STE_S2PS(ste));
+
+    if (s2a64) {
+        cfg->tsz[stage] = MIN(cfg->tsz[stage], 39);
+        cfg->tsz[stage] = MAX(cfg->tsz[stage], 16);
+    }
+    cfg->va_size[stage] = STE_S2AA64(ste) ? 64 : 32;
+    cfg->granule_sz[stage] = tg2granule(cfg->granule[stage], 0) - 3;
+}
+
+static void smmu_cfg_populate_s1(SMMUTransCfg *cfg, Cd *cd)
+{                           /* stage 1 cfg */
+    bool s1a64 = CD_AARCH64(cd);
+    const int stage = 1;
+
+    cfg->granule[stage] = (CD_EPD0(cd)) ? CD_TG1(cd) : CD_TG0(cd);
+    cfg->tsz[stage] = (CD_EPD0(cd)) ? CD_T1SZ(cd) : CD_T0SZ(cd);
+    cfg->ttbr[stage] = (CD_EPD0(cd)) ? CD_TTB1(cd) : CD_TTB0(cd);
+    cfg->oas[stage] = oas2bits(CD_IPS(cd));
+
+    if (s1a64) {
+        cfg->tsz[stage] = MIN(cfg->tsz[stage], 39);
+        cfg->tsz[stage] = MAX(cfg->tsz[stage], 16);
+    }
+    cfg->va_size[stage] = CD_AARCH64(cd) ? 64 : 32;
+    cfg->granule_sz[stage] = tg2granule(cfg->granule[stage], CD_EPD0(cd)) - 3;
+}
+
+static SMMUEvtErr smmu_walk_pgtable(SMMUV3State *s, Ste *ste, Cd *cd,
+                                    IOMMUTLBEntry *tlbe, bool is_write)
+{
+    SMMUState *sys = SMMU_SYS_DEV(s);
+    SMMUBaseClass *sbc = SMMU_DEVICE_GET_CLASS(sys);
+    SMMUTransCfg _cfg = {};
+    SMMUTransCfg *cfg = &_cfg;
+    SMMUEvtErr retval = 0;
+    uint32_t ste_cfg = STE_CONFIG(ste);
+    uint32_t page_size = 0, perm = 0;
+    hwaddr pa;                 /* Input address, output address */
+    int stage = 0;
+
+    SMMU_DPRINTF(DBG1, "ste_cfg :%x\n", ste_cfg);
+    /* Both Bypass, we dont need to do anything */
+    if (is_ste_bypass(s, ste)) {
+        return 0;
+    }
+
+    SMMU_DPRINTF(TT_1, "Input addr: %lx ste_config:%d\n",
+                 tlbe->iova, ste_cfg);
+
+    if (ste_cfg & STE_CONFIG_S1TR) {
+        stage = cfg->stage = 1;
+        smmu_cfg_populate_s1(cfg, cd);
+
+        cfg->oas[stage] = MIN(oas2bits(smmu_read32_reg(s, SMMU_REG_IDR5) & 0xf),
+                              cfg->oas[stage]);
+        /* fix ttbr - make top bits zero*/
+        cfg->ttbr[stage] = extract64(cfg->ttbr[stage], 0, cfg->oas[stage]);
+        cfg->s2_needed = (STE_CONFIG(ste) == STE_CONFIG_S1TR_S2TR) ? 1 : 0;
+
+        SMMU_DPRINTF(DBG1, "S1 populated\n ");
+    }
+
+    if (ste_cfg & STE_CONFIG_S2TR) {
+        stage = 2;
+        if (cfg->stage) {               /* S1+S2 */
+            cfg->s2_needed = true;
+        } else                          /* Stage2 only */
+            cfg->stage = stage;
+
+        /* Stage2 only configuratoin */
+        smmu_cfg_populate_s2(cfg, ste);
+
+        cfg->oas[stage] = MIN(oas2bits(smmu_read32_reg(s, SMMU_REG_IDR5) & 0xf),
+                              cfg->oas[stage]);
+        /* fix ttbr - make top bits zero*/
+        cfg->ttbr[stage] = extract64(cfg->ttbr[stage], 0, cfg->oas[stage]);
+
+        SMMU_DPRINTF(DBG1, "S2 populated\n ");
+    }
+
+    cfg->va = tlbe->iova;
+
+    if ((cfg->stage == 1 && CD_AARCH64(cd)) ||
+        STE_S2AA64(ste)) {
+        SMMU_DPRINTF(DBG1, "Translate 64\n");
+        retval = sbc->translate_64(cfg, &page_size, &perm,
+                                   is_write);
+    } else {
+        SMMU_DPRINTF(DBG1, "Translate 32\n");
+        retval = sbc->translate_32(cfg, &page_size, &perm, is_write);
+    }
+
+    if (retval != 0) {
+        SMMU_DPRINTF(CRIT, "FAILED Stage1 translation\n");
+        goto exit;
+    }
+    pa = cfg->pa;
+
+    SMMU_DPRINTF(TT_1, "DONE: o/p addr:%lx mask:%x is_write:%d\n ",
+                 pa, page_size - 1, is_write);
+    tlbe->translated_addr = pa;
+    tlbe->addr_mask = page_size - 1;
+    tlbe->perm = perm;
+
+exit:
+    dump_smmutranscfg(cfg);
+    return retval;
+}
+
+static MemTxResult smmu_write_evtq(SMMUV3State *s, Evt *evt)
+{
+    SMMUQueue *q = &s->evtq;
+    int ret = smmu_q_write(s, q, evt);
+    uint32_t val = 0;
+
+    val |= (q->wrap.prod << q->shift) | q->prod;
+
+    smmu_write32_reg(s, SMMU_REG_EVTQ_PROD, val);
+
+    return ret;
+}
+
+/*
+ * Events created on the EventQ
+ */
+static void smmu_create_event(SMMUV3State *s, hwaddr iova,
+                              uint32_t sid, bool is_write, int error)
+{
+    SMMUQueue *q = &s->evtq;
+    uint64_t head;
+    Evt evt;
+
+    if (!smmu_evt_q_enabled(s)) {
+        return;
+    }
+
+    EVT_SET_TYPE(&evt, error);
+    EVT_SET_SID(&evt, sid);
+
+    switch (error) {
+    case SMMU_EVT_F_UUT:
+    case SMMU_EVT_C_BAD_STE:
+        break;
+    case SMMU_EVT_C_BAD_CD:
+    case SMMU_EVT_F_CD_FETCH:
+        break;
+    case SMMU_EVT_F_TRANS_FORBIDDEN:
+    case SMMU_EVT_F_WALK_EXT_ABRT:
+        EVT_SET_INPUT_ADDR(&evt, iova);
+    default:
+        break;
+    }
+
+    smmu_write_evtq(s, &evt);
+
+    head = Q_IDX(q, q->prod);
+
+    if (smmu_is_q_full(s, &s->evtq)) {
+        head = q->prod ^ (1 << 31);     /* Set overflow */
+    }
+
+    smmu_write32_reg(s, SMMU_REG_EVTQ_PROD, head);
+
+    smmu_irq_raise(s, SMMU_IRQ_EVTQ, (uint64_t)&evt);
+}
+
+/*
+ * TR - Translation Request
+ * TT - Translated Tansaction
+ * OT - Other Transaction
+ */
+static IOMMUTLBEntry
+smmuv3_translate(MemoryRegion *mr, hwaddr addr, bool is_write)
+{
+    SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu);
+    SMMUV3State *s = sdev->smmu;
+    uint16_t sid = 0, config;
+    Ste ste;
+    Cd cd;
+    SMMUEvtErr error = 0;
+
+    IOMMUTLBEntry ret = {
+        .target_as = &address_space_memory,
+        .iova = addr,
+        .translated_addr = addr,
+        .addr_mask = ~(hwaddr)0,
+        .perm = IOMMU_NONE,
+    };
+
+    /* SMMU Bypass, We allow traffic through if SMMU is disabled  */
+    if (!smmu_enabled(s)) {
+        SMMU_DPRINTF(CRIT, "SMMU Not enabled.. bypassing addr:%lx\n", addr);
+        goto bypass;
+    }
+
+    sid = smmu_get_sid(sdev);
+    SMMU_DPRINTF(TT_1, "SID:%x bus:%d ste_base:%lx\n",
+                 sid, pci_bus_num(sdev->bus), s->strtab_base);
+
+    /* Fetch & Check STE */
+    error = smmu_find_ste(s, sid, &ste);
+    if (error) {
+        goto error_out;  /* F_STE_FETCH or F_CFG_CONFLICT */
+    }
+
+    if (IS_DBG_ENABLED(STE)) {
+        dump_ste(&ste);
+    }
+
+    if (is_ste_valid(s, &ste) && is_ste_bypass(s, &ste)) {
+        goto bypass;
+    }
+
+    SMMU_DPRINTF(STE, "STE is not bypass\n");
+    if (!is_ste_consistent(s, &ste)) {
+        error = SMMU_EVT_C_BAD_STE;
+        goto error_out;
+    }
+    SMMU_DPRINTF(INFO, "Valid STE Found\n");
+
+    /* Stream Bypass */
+    config = STE_CONFIG(&ste) & 0x3;
+
+    if (config & (STE_CONFIG_S1TR)) {
+        smmu_get_cd(s, &ste, 0, &cd); /* We dont have SSID yet, so 0 */
+        SMMU_DPRINTF(CRIT, "GET_CD CTXPTR:%p\n", (void *)STE_CTXPTR(&ste));
+        if (1 || IS_DBG_ENABLED(CD)) {
+            dump_cd(&cd);
+        }
+
+        if (!is_cd_valid(s, &ste, &cd)) {
+            error = SMMU_EVT_C_BAD_CD;
+            goto error_out;
+        }
+    }
+
+    /* Walk Stage1, if S2 is enabled, S2 walked for Every access on S1 */
+    error = smmu_walk_pgtable(s, &ste, &cd, &ret, is_write);
+
+    SMMU_DPRINTF(INFO, "DONE walking tables \n");
+
+error_out:
+    if (error) {        /* Post the Error using Event Q */
+        SMMU_DPRINTF(CRIT, "Translation Error: %x\n", error);
+        smmu_create_event(s, ret.iova, sid, is_write, error);
+        goto out;
+    }
+
+bypass:
+    ret.perm = is_write ? IOMMU_RW : IOMMU_RO;
+
+out:
+    return ret;
+}
+
+static const MemoryRegionIOMMUOps smmu_iommu_ops = {
+    .translate = smmuv3_translate,
+};
+
+static AddressSpace *smmu_init_pci_iommu(PCIBus *bus, void *opaque, int devfn)
+{
+    SMMUV3State *s = opaque;
+    SMMUState *sys = SMMU_SYS_DEV(s);
+    uintptr_t key = (uintptr_t)bus;
+    SMMUPciBus *sbus = g_hash_table_lookup(s->smmu_as_by_busptr, &key);
+    SMMUDevice *sdev;
+
+    if (!sbus) {
+        sbus = g_malloc0(sizeof(SMMUPciBus) +
+                         sizeof(SMMUDevice) * PCI_DEVFN_MAX);
+        sbus->bus = bus;
+        g_hash_table_insert(s->smmu_as_by_busptr, &key, sbus);
+    }
+
+    sdev = sbus->pbdev[devfn];
+    if (!sdev) {
+        sdev = sbus->pbdev[devfn] = g_malloc0(sizeof(SMMUDevice));
+
+        sdev->smmu = s;
+        sdev->bus = bus;
+        sdev->devfn = devfn;
+
+        memory_region_init_iommu(&sdev->iommu, OBJECT(sys),
+                                 &smmu_iommu_ops, TYPE_SMMU_V3_DEV, UINT64_MAX);
+        address_space_init(&sdev->as, &sdev->iommu, TYPE_SMMU_V3_DEV);
+    }
+
+    return &sdev->as;
+}
+
+static inline void smmu_update_base_reg(SMMUV3State *s, uint64_t *base, uint64_t val)
+{
+    *base = val & ~(SMMU_BASE_RA | 0x3fULL);
+}
+
+static void smmu_update_qreg(SMMUV3State *s, SMMUQueue *q, hwaddr reg,
+                             uint32_t off, uint64_t val, unsigned size)
+{
+    if (size == 8 && off == 0) {
+        smmu_write64_reg(s, reg, val);
+    } else
+        smmu_write_reg(s, reg, val);
+
+    switch (off) {
+    case 0:                             /* BASE register */
+        val = smmu_read64_reg(s, reg);
+        q->shift = val & 0x1f;
+        q->entries = 1 << (q->shift);
+        smmu_update_base_reg(s, &q->base, val);
+        break;
+
+    case 4:                             /* CONS */
+        q->cons = Q_IDX(q, val);
+        q->wrap.cons = val >> q->shift;
+        SMMU_DPRINTF(DBG2, "cons written : %d val:%lx\n", q->cons, val);
+        break;
+
+    case 8:                             /* PROD */
+        q->prod = Q_IDX(q, val);
+        q->wrap.prod = val >> q->shift;
+        break;
+    }
+
+    switch (reg) {
+    case SMMU_REG_CMDQ_PROD:            /* should be only for CMDQ_PROD */
+    case SMMU_REG_CMDQ_CONS:            /* but we do it anyway */
+        smmu_update(s);
+        break;
+    }
+}
+
+static void smmu_write_mmio_fixup(SMMUV3State *s, hwaddr *addr)
+{
+    switch (*addr) {
+    case 0x100a8: case 0x100ac:         /* Aliasing => page0 registers */
+    case 0x100c8: case 0x100cc:
+        *addr ^= (hwaddr)0x10000;
+    }
+}
+
+static void smmu_write_mmio(void *opaque, hwaddr addr,
+                            uint64_t val, unsigned size)
+{
+    SMMUState *sys = opaque;
+    SMMUV3State *s = SMMU_V3_DEV(sys);
+    bool update = false;
+
+    smmu_write_mmio_fixup(s, &addr);
+
+    SMMU_DPRINTF(DBG2, "addr: %lx val:%lx\n", addr, val);
+
+    switch (addr) {
+    case 0xFDC ... 0xFFC:
+    case SMMU_REG_IDR0 ... SMMU_REG_IDR5:
+        SMMU_DPRINTF(CRIT, "write to RO/Unimpl reg %lx val64:%lx\n",
+                     addr, val);
+        return;
+
+    case SMMU_REG_GERRORN:
+        smmu_update_irq(s, addr, val);
+        return;
+
+    case SMMU_REG_CR0:
+        smmu_write32_reg(s, SMMU_REG_CR0_ACK, val);
+        update = true;
+        break;
+
+    case SMMU_REG_IRQ_CTRL:
+        smmu_write32_reg(s, SMMU_REG_IRQ_CTRL_ACK, val);
+        update = true;
+        break;
+
+    case SMMU_REG_STRTAB_BASE:
+        smmu_update_base_reg(s, &s->strtab_base, val);
+        return;
+
+    case SMMU_REG_STRTAB_BASE_CFG:
+        if (((val >> 16) & 0x3) == 0x1) {
+            s->sid_split = (val >> 6) & 0x1f;
+            s->features |= SMMU_FEATURE_2LVL_STE;
+        }
+        break;
+
+    case SMMU_REG_CMDQ_PROD:
+    case SMMU_REG_CMDQ_CONS:
+    case SMMU_REG_CMDQ_BASE:
+    case SMMU_REG_CMDQ_BASE + 4:
+        smmu_update_qreg(s, &s->cmdq, addr, addr - SMMU_REG_CMDQ_BASE,
+                         val, size);
+        return;
+
+    case SMMU_REG_EVTQ_CONS:            /* fallthrough */
+    {
+        SMMUQueue *evtq = &s->evtq;
+        evtq->cons = Q_IDX(evtq, val);
+        evtq->wrap.cons = Q_WRAP(evtq, val);
+
+        SMMU_DPRINTF(IRQ, "Before clearing interrupt "
+                     "prod:%x cons:%x prod.w:%d cons.w:%d\n",
+                     evtq->prod, evtq->cons, evtq->wrap.prod, evtq->wrap.cons);
+        if (smmu_is_q_empty(s, &s->evtq)) {
+            SMMU_DPRINTF(IRQ, "Clearing interrupt"
+                         " prod:%x cons:%x prod.w:%d cons.w:%d\n",
+                         evtq->prod, evtq->cons, evtq->wrap.prod,
+                         evtq->wrap.cons);
+            qemu_irq_lower(s->irq[SMMU_IRQ_EVTQ]);
+        }
+    }
+    case SMMU_REG_EVTQ_BASE:
+    case SMMU_REG_EVTQ_BASE + 4:
+    case SMMU_REG_EVTQ_PROD:
+        smmu_update_qreg(s, &s->evtq, addr, addr - SMMU_REG_EVTQ_BASE,
+                         val, size);
+        return;
+
+    case SMMU_REG_PRIQ_CONS:
+    case SMMU_REG_PRIQ_BASE:
+    case SMMU_REG_PRIQ_BASE + 4:
+    case SMMU_REG_PRIQ_PROD:
+        smmu_update_qreg(s, &s->priq, addr, addr - SMMU_REG_PRIQ_BASE,
+                         val, size);
+        return;
+    }
+
+    if (size == 8) {
+        smmu_write_reg(s, addr, val);
+    } else {
+        smmu_write32_reg(s, addr, (uint32_t)val);
+    }
+
+    if (update)
+        smmu_update(s);
+}
+
+static uint64_t smmu_read_mmio(void *opaque, hwaddr addr, unsigned size)
+{
+    SMMUState *sys = opaque;
+    SMMUV3State *s = SMMU_V3_DEV(sys);
+    uint64_t val;
+
+    smmu_write_mmio_fixup(s, &addr);
+
+    /* Primecell/Corelink ID registers */
+    switch (addr) {
+    case 0xFF0 ... 0xFFC:
+        val = (uint64_t)sys->cid[(addr - 0xFF0) >> 2];
+        break;
+
+    case 0xFDC ... 0xFE4:
+        val = (uint64_t)sys->pid[(addr - 0xFDC) >> 2];
+        break;
+
+    default:
+        val = (uint64_t)smmu_read32_reg(s, addr);
+        break;
+
+    case SMMU_REG_STRTAB_BASE ... SMMU_REG_CMDQ_BASE:
+    case SMMU_REG_EVTQ_BASE:
+    case SMMU_REG_PRIQ_BASE ... SMMU_REG_PRIQ_IRQ_CFG1:
+        val = smmu_read64_reg(s, addr);
+        break;
+    }
+
+    SMMU_DPRINTF(DBG2, "addr: %lx val:%lx\n", addr, val);
+    SMMU_DPRINTF(DBG2, "cmdq cons:%d\n", s->cmdq.cons);
+    return val;
+}
+
+static const MemoryRegionOps smmu_mem_ops = {
+    .read = smmu_read_mmio,
+    .write = smmu_write_mmio,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 8,
+    },
+};
+
+static void smmu_init_irq(SMMUV3State *s, SysBusDevice *dev)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
+        sysbus_init_irq(dev, &s->irq[i]);
+    }
+}
+
+static void smmu_init_iommu_as(SMMUV3State *sys)
+{
+    SMMUState *s = SMMU_SYS_DEV(sys);
+    PCIBus *pcibus = pci_find_primary_bus();
+
+    if (pcibus) {
+        SMMU_DPRINTF(CRIT, "Found PCI bus, setting up iommu\n");
+        pci_setup_iommu(pcibus, smmu_init_pci_iommu, s);
+    } else {
+        SMMU_DPRINTF(CRIT, "No PCI bus, SMMU is not registered\n");
+    }
+}
+
+static void smmu_reset(DeviceState *dev)
+{
+    SMMUV3State *s = SMMU_V3_DEV(dev);
+    smmuv3_init(s);
+}
+
+static int smmu_populate_internal_state(void *opaque, int version_id)
+{
+    SMMUV3State *s = opaque;
+    /* SMMUState *sys = SMMU_SYS_DEV(s); */
+
+    smmu_update(s);
+
+    return 0;
+}
+
+static gboolean smmu_uint64_equal(gconstpointer v1, gconstpointer v2)
+{
+    return *((const uint64_t *)v1) == *((const uint64_t *)v2);
+}
+
+static guint smmu_uint64_hash(gconstpointer v)
+{
+    return (guint)*(const uint64_t *)v;
+}
+
+static void smmu_realize(DeviceState *d, Error **errp)
+{
+    SMMUState *sys = SMMU_SYS_DEV(d);
+    SMMUV3State *s = SMMU_V3_DEV(sys);
+    SysBusDevice *dev = SYS_BUS_DEVICE(d);
+
+    /* Register Access */
+    memory_region_init_io(&sys->iomem, OBJECT(s),
+                          &smmu_mem_ops, sys, TYPE_SMMU_V3_DEV, 0x20000);
+
+    s->smmu_as_by_busptr = g_hash_table_new_full(smmu_uint64_hash,
+                                                 smmu_uint64_equal,
+                                                 g_free, g_free);
+    sysbus_init_mmio(dev, &sys->iomem);
+
+    smmu_init_irq(s, dev);
+
+    smmu_init_iommu_as(s);
+}
+
+static const VMStateDescription vmstate_smmu = {
+    .name = "smmu",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = smmu_populate_internal_state,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(cid, SMMUState, 4),
+        VMSTATE_UINT32_ARRAY(pid, SMMUState, 8),
+        VMSTATE_UINT64_ARRAY(regs, SMMUV3State, SMMU_NREGS),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static void smmu_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SMMUBaseClass *sbc = SMMU_DEVICE_CLASS(klass);
+
+    sbc->translate_64 = smmu_translate_64;
+
+    dc->reset = smmu_reset;
+    dc->vmsd = &vmstate_smmu;
+    dc->realize = smmu_realize;
+}
+
+static void smmu_base_instance_init(Object *obj)
+{
+     /* Nothing much to do here as of now */
+}
+
+static void smmu_instance_init(Object *obj)
+{
+    /* Nothing much to do here as of now */
+}
+
+static const TypeInfo smmu_base_info = {
+    .name          = TYPE_SMMU_DEV_BASE,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SMMUV3State),
+    .instance_init = smmu_base_instance_init,
+    .class_size    = sizeof(SMMUBaseClass),
+    .abstract      = true,
+};
+
+static void smmu_register_types(void)
+{
+    TypeInfo type_info = {
+        .name = TYPE_SMMU_V3_DEV,
+        .parent = TYPE_SMMU_DEV_BASE,
+        .class_data = NULL,
+        .class_init = smmu_class_init,
+        .instance_init = smmu_instance_init,
+    };
+
+    type_register_static(&smmu_base_info);
+
+    type_register(&type_info);
+}
+
+type_init(smmu_register_types)
+
diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h
new file mode 100644
index 0000000..8d34f2a
--- /dev/null
+++ b/hw/arm/smmuv3-internal.h
@@ -0,0 +1,432 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * Author: Prem Mallappa <pmallapp@broadcom.com>
+ *
+ */
+
+#ifndef HW_ARM_SMMU_V3_INTERNAL_H
+#define HW_ARM_SMMU_V3_INTERNAL_H
+
+/*****************************
+ * MMIO Register
+ *****************************/
+enum {
+    SMMU_REG_IDR0            = 0x0,
+
+#define SMMU_IDR0_S2P            (1 << 0)
+#define SMMU_IDR0_S1P            (1 << 1)
+#define SMMU_IDR0_TTF            (0x3 << 2)
+#define SMMU_IDR0_HTTU           (0x3 << 6)
+#define SMMU_IDR0_HYP            (1 << 9)
+#define SMMU_IDR0_ATS            (1 << 10)
+#define SMMU_IDR0_VMID16         (1 << 18)
+#define SMMU_IDR0_CD2L           (1 << 19)
+
+    SMMU_REG_IDR1            = 0x4,
+    SMMU_REG_IDR2            = 0x8,
+    SMMU_REG_IDR3            = 0xc,
+    SMMU_REG_IDR4            = 0x10,
+    SMMU_REG_IDR5            = 0x14,
+    SMMU_REG_IIDR            = 0x1c,
+    SMMU_REG_CR0             = 0x20,
+
+#define SMMU_CR0_SMMU_ENABLE (1 << 0)
+#define SMMU_CR0_PRIQ_ENABLE (1 << 1)
+#define SMMU_CR0_EVTQ_ENABLE (1 << 2)
+#define SMMU_CR0_CMDQ_ENABLE (1 << 3)
+#define SMMU_CR0_ATS_CHECK   (1 << 4)
+
+    SMMU_REG_CR0_ACK         = 0x24,
+    SMMU_REG_CR1             = 0x28,
+    SMMU_REG_CR2             = 0x2c,
+
+    SMMU_REG_STATUSR         = 0x40,
+
+    SMMU_REG_IRQ_CTRL        = 0x50,
+    SMMU_REG_IRQ_CTRL_ACK    = 0x54,
+
+#define SMMU_IRQ_CTRL_GERROR_EN (1 << 0)
+#define SMMU_IRQ_CTRL_EVENT_EN  (1 << 1)
+#define SMMU_IRQ_CTRL_PRI_EN    (1 << 2)
+
+    SMMU_REG_GERROR          = 0x60,
+
+#define SMMU_GERROR_CMDQ       (1 << 0)
+#define SMMU_GERROR_EVENTQ     (1 << 2)
+#define SMMU_GERROR_PRIQ       (1 << 3)
+#define SMMU_GERROR_MSI_CMDQ   (1 << 4)
+#define SMMU_GERROR_MSI_EVENTQ (1 << 5)
+#define SMMU_GERROR_MSI_PRIQ   (1 << 6)
+#define SMMU_GERROR_MSI_GERROR (1 << 7)
+#define SMMU_GERROR_SFM_ERR    (1 << 8)
+
+    SMMU_REG_GERRORN         = 0x64,
+    SMMU_REG_GERROR_IRQ_CFG0 = 0x68,
+    SMMU_REG_GERROR_IRQ_CFG1 = 0x70,
+    SMMU_REG_GERROR_IRQ_CFG2 = 0x74,
+
+    /* SMMU_BASE_RA Applies to STRTAB_BASE, CMDQ_BASE and EVTQ_BASE */
+#define SMMU_BASE_RA        (1ULL << 62)
+    SMMU_REG_STRTAB_BASE     = 0x80,
+    SMMU_REG_STRTAB_BASE_CFG = 0x88,
+
+    SMMU_REG_CMDQ_BASE       = 0x90,
+    SMMU_REG_CMDQ_PROD       = 0x98,
+    SMMU_REG_CMDQ_CONS       = 0x9c,
+    /* CMD Consumer (CONS) */
+#define SMMU_CMD_CONS_ERR_SHIFT        24
+#define SMMU_CMD_CONS_ERR_BITS         7
+
+    SMMU_REG_EVTQ_BASE       = 0xa0,
+    SMMU_REG_EVTQ_PROD       = 0xa8,
+    SMMU_REG_EVTQ_CONS       = 0xac,
+    SMMU_REG_EVTQ_IRQ_CFG0   = 0xb0,
+    SMMU_REG_EVTQ_IRQ_CFG1   = 0xb8,
+    SMMU_REG_EVTQ_IRQ_CFG2   = 0xbc,
+
+    SMMU_REG_PRIQ_BASE       = 0xc0,
+    SMMU_REG_PRIQ_PROD       = 0xc8,
+    SMMU_REG_PRIQ_CONS       = 0xcc,
+    SMMU_REG_PRIQ_IRQ_CFG0   = 0xd0,
+    SMMU_REG_PRIQ_IRQ_CFG1   = 0xd8,
+    SMMU_REG_PRIQ_IRQ_CFG2   = 0xdc,
+
+    SMMU_ID_REGS_OFFSET      = 0xfd0,
+
+    /* Secure registers are not used for now */
+    SMMU_SECURE_OFFSET       = 0x8000,
+};
+
+/*****************************
+ * STE fields
+ *****************************/
+#define STE_VALID(x)   extract32((x)->word[0], 0, 1) /* 0 */
+#define STE_CONFIG(x)  (extract32((x)->word[0], 1, 3) & 0x7)
+enum {
+    STE_CONFIG_NONE      = 0,
+    STE_CONFIG_BYPASS    = 4,           /* S1 Bypass, S2 Bypass */
+    STE_CONFIG_S1TR      = 1,           /* S1 Translate, S2 Bypass */
+    STE_CONFIG_S2TR      = 2,           /* S1 Bypass, S2 Translate */
+    STE_CONFIG_S1TR_S2TR = 3,           /* S1 Translate, S2 Translate */
+};
+#define STE_S1FMT(x)   extract32((x)->word[0], 4, 2)
+#define STE_S1CDMAX(x) extract32((x)->word[1], 8, 2)
+#define STE_EATS(x)    extract32((x)->word[2], 28, 2)
+#define STE_STRW(x)    extract32((x)->word[2], 30, 2)
+#define STE_S2VMID(x)  extract32((x)->word[4], 0, 16) /* 4 */
+#define STE_S2T0SZ(x)  extract32((x)->word[5], 0, 6) /* 5 */
+#define STE_S2TG(x)    extract32((x)->word[5], 14, 2)
+#define STE_S2PS(x)    extract32((x)->word[5], 16, 3)
+#define STE_S2AA64(x)  extract32((x)->word[5], 19, 1)
+#define STE_S2HD(x)    extract32((x)->word[5], 24, 1)
+#define STE_S2HA(x)    extract32((x)->word[5], 25, 1)
+#define STE_S2S(x)     extract32((x)->word[5], 26, 1)
+#define STE_CTXPTR(x)                                           \
+    ({                                                          \
+        unsigned long addr;                                     \
+        addr = (uint64_t)extract32((x)->word[1], 0, 16) << 32;  \
+        addr |= (uint64_t)((x)->word[0] & 0xffffffc0);          \
+        addr;                                                   \
+    })
+
+#define STE_S2TTB(x)                                            \
+    ({                                                          \
+        unsigned long addr;                                     \
+        addr = (uint64_t)extract32((x)->word[7], 0, 16) << 32;  \
+        addr |= (uint64_t)((x)->word[6] & 0xfffffff0);          \
+        addr;                                                   \
+    })
+
+/*****************************
+ * CD fields
+ *****************************/
+#define CD_VALID(x)   extract32((x)->word[0], 30, 1)
+#define CD_ASID(x)    extract32((x)->word[1], 16, 16)
+#define CD_TTB(x, sel)                                      \
+    ({                                                      \
+        uint64_t hi, lo;                                    \
+        hi = extract32((x)->word[(sel) * 2 + 3], 0, 16);    \
+        hi <<= 32;                                          \
+        lo = (x)->word[(sel) * 2 + 2] & ~0xf;               \
+        hi | lo;                                            \
+    })
+
+#define CD_TSZ(x, sel)   extract32((x)->word[0], (16 * (sel)) + 0, 6)
+#define CD_TG(x, sel)    extract32((x)->word[0], (16 * (sel)) + 6, 2)
+#define CD_EPD(x, sel)   extract32((x)->word[0], (16 * (sel)) + 14, 1)
+
+#define CD_T0SZ(x)    CD_TSZ((x), 0)
+#define CD_T1SZ(x)    CD_TSZ((x), 1)
+#define CD_TG0(x)     CD_TG((x), 0)
+#define CD_TG1(x)     CD_TG((x), 1)
+#define CD_EPD0(x)    CD_EPD((x), 0)
+#define CD_EPD1(x)    CD_EPD((x), 1)
+#define CD_IPS(x)     extract32((x)->word[1], 0, 3)
+#define CD_AARCH64(x) extract32((x)->word[1], 9, 1)
+#define CD_TTB0(x)    CD_TTB((x), 0)
+#define CD_TTB1(x)    CD_TTB((x), 1)
+
+#define CDM_VALID(x)    ((x)->word[0] & 0x1)
+
+/*****************************
+ * Commands
+ *****************************/
+enum {
+    SMMU_CMD_PREFETCH_CONFIG = 0x01,
+    SMMU_CMD_PREFETCH_ADDR,
+    SMMU_CMD_CFGI_STE,
+    SMMU_CMD_CFGI_STE_RANGE,
+    SMMU_CMD_CFGI_CD,
+    SMMU_CMD_CFGI_CD_ALL,
+    SMMU_CMD_TLBI_NH_ALL     = 0x10,
+    SMMU_CMD_TLBI_NH_ASID,
+    SMMU_CMD_TLBI_NH_VA,
+    SMMU_CMD_TLBI_NH_VAA,
+    SMMU_CMD_TLBI_EL3_ALL    = 0x18,
+    SMMU_CMD_TLBI_EL3_VA     = 0x1a,
+    SMMU_CMD_TLBI_EL2_ALL    = 0x20,
+    SMMU_CMD_TLBI_EL2_ASID,
+    SMMU_CMD_TLBI_EL2_VA,
+    SMMU_CMD_TLBI_EL2_VAA,  /* 0x23 */
+    SMMU_CMD_TLBI_S12_VMALL  = 0x28,
+    SMMU_CMD_TLBI_S2_IPA     = 0x2a,
+    SMMU_CMD_TLBI_NSNH_ALL   = 0x30,
+    SMMU_CMD_ATC_INV         = 0x40,
+    SMMU_CMD_PRI_RESP,
+    SMMU_CMD_RESUME          = 0x44,
+    SMMU_CMD_STALL_TERM,
+    SMMU_CMD_SYNC,          /* 0x46 */
+};
+
+/*****************************
+ * CMDQ fields
+ *****************************/
+
+enum { /* Command Errors */
+    SMMU_CMD_ERR_NONE = 0,
+    SMMU_CMD_ERR_ILLEGAL,
+    SMMU_CMD_ERR_ABORT
+};
+
+enum { /* Command completion notification */
+    CMD_SYNC_SIG_NONE,
+    CMD_SYNC_SIG_IRQ,
+    CMD_SYNC_SIG_SEV,
+};
+
+#define CMD_TYPE(x)  extract32((x)->word[0], 0, 8)
+#define CMD_SEC(x)   extract32((x)->word[0], 9, 1)
+#define CMD_SEV(x)   extract32((x)->word[0], 10, 1)
+#define CMD_AC(x)    extract32((x)->word[0], 12, 1)
+#define CMD_AB(x)    extract32((x)->word[0], 13, 1)
+#define CMD_CS(x)    extract32((x)->word[0], 12, 2)
+#define CMD_SSID(x)  extract32((x)->word[0], 16, 16)
+#define CMD_SID(x)   ((x)->word[1])
+#define CMD_VMID(x)  extract32((x)->word[1], 0, 16)
+#define CMD_ASID(x)  extract32((x)->word[1], 16, 16)
+#define CMD_STAG(x)  extract32((x)->word[2], 0, 16)
+#define CMD_RESP(x)  extract32((x)->word[2], 11, 2)
+#define CMD_GRPID(x) extract32((x)->word[3], 0, 8)
+#define CMD_SIZE(x)  extract32((x)->word[3], 0, 16)
+#define CMD_LEAF(x)  extract32((x)->word[3], 0, 1)
+#define CMD_SPAN(x)  extract32((x)->word[3], 0, 5)
+#define CMD_ADDR(x) ({                                  \
+            uint64_t addr = (uint64_t)(x)->word[3];     \
+            addr <<= 32;                                \
+            addr |=  extract32((x)->word[3], 12, 20);   \
+            addr;                                       \
+        })
+
+/*****************************
+ * EVTQ fields
+ *****************************/
+#define EVT_Q_OVERFLOW        (1 << 31)
+
+#define EVT_SET_TYPE(x, t)    deposit32((x)->word[0], 0, 8, t)
+#define EVT_SET_SID(x, s)     ((x)->word[1] =  s)
+#define EVT_SET_INPUT_ADDR(x, addr) ({                    \
+            (x)->word[5] = (uint32_t)(addr >> 32);        \
+            (x)->word[4] = (uint32_t)(addr & 0xffffffff); \
+            addr;                                         \
+        })
+
+/*****************************
+ * Events
+ *****************************/
+enum evt_err {
+    SMMU_EVT_F_UUT    = 0x1,
+    SMMU_EVT_C_BAD_SID,
+    SMMU_EVT_F_STE_FETCH,
+    SMMU_EVT_C_BAD_STE,
+    SMMU_EVT_F_BAD_ATS_REQ,
+    SMMU_EVT_F_STREAM_DISABLED,
+    SMMU_EVT_F_TRANS_FORBIDDEN,
+    SMMU_EVT_C_BAD_SSID,
+    SMMU_EVT_F_CD_FETCH,
+    SMMU_EVT_C_BAD_CD,
+    SMMU_EVT_F_WALK_EXT_ABRT,
+    SMMU_EVT_F_TRANS        = 0x10,
+    SMMU_EVT_F_ADDR_SZ,
+    SMMU_EVT_F_ACCESS,
+    SMMU_EVT_F_PERM,
+    SMMU_EVT_F_TLB_CONFLICT = 0x20,
+    SMMU_EVT_F_CFG_CONFLICT = 0x21,
+    SMMU_EVT_E_PAGE_REQ     = 0x24,
+};
+
+typedef enum evt_err SMMUEvtErr;
+
+
+/*****************************
+ * SMMU Data structures
+ *****************************/
+#define ARM_SMMU_FEAT_PASSID_SUPPORT  (1 << 24) /* Some random bits for now */
+#define ARM_SMMU_FEAT_CD_2LVL         (1 << 25)
+
+struct SMMUQueue {
+     hwaddr base;
+     uint32_t prod;
+     uint32_t cons;
+     union {
+          struct {
+               uint8_t prod:1;
+               uint8_t cons:1;
+          };
+          uint8_t unused;
+     } wrap;
+
+     uint16_t entries;           /* Number of entries */
+     uint8_t  ent_size;          /* Size of entry in bytes */
+     uint8_t  shift;             /* Size in log2 */
+};
+typedef struct SMMUQueue SMMUQueue;
+
+#define Q_ENTRY(q, idx)  (q->base + q->ent_size * idx)
+#define Q_WRAP(q, pc)    ((pc) >> (q)->shift)
+#define Q_IDX(q, pc)     ((pc) & ((1 << (q)->shift) - 1))
+
+struct __smmu_data2 {
+    uint32_t word[2];
+};
+
+struct __smmu_data8 {
+    uint32_t word[8];
+};
+
+struct __smmu_data16 {
+    uint32_t word[16];
+};
+
+struct __smmu_data4 {
+    uint32_t word[4];
+};
+
+typedef struct __smmu_data2  STEDesc; /* STE Level 1 Descriptor */
+typedef struct __smmu_data16 Ste;     /* Stream Table Entry(STE) */
+typedef struct __smmu_data2  CDDesc;  /* CD Level 1 Descriptor */
+typedef struct __smmu_data16 Cd;      /* Context Descriptor(CD) */
+
+typedef struct __smmu_data4  Cmd; /* Command Entry */
+typedef struct __smmu_data8  Evt; /* Event Entry */
+typedef struct __smmu_data4  Pri; /* PRI entry */
+
+
+/*****************************
+ * Broadcom Specific register and bits
+ *****************************/
+#define SMMU_REG_CNTL         (0x410 << 2)
+#define SMMU_REG_CNTL_1       (0x411 << 2)
+#define SMMU_REG_INTERRUPT    (0x412 << 2)
+/* BIT encoding is same as SMMU_REG_INTERRUPT, except for last 4 bits */
+#define SMMU_REG_INTERRUPT_EN (0x413 << 2)
+
+#define SMMU_INTR_BMI_ERR  (1 << 6) /* Smmu BMI Rd Wr Error*/
+#define SMMU_INTR_BSI_ERR  (1 << 5) /* Smmu BSI Rd Wr Error*/
+#define SMMU_INTR_SBU_INTR (1 << 4) /* SBU interrupt 0 */
+#define SMMU_INTR_CMD_SYNC (1 << 3) /* CmdSync completion set to interrupt */
+#define SMMU_INTR_EVENT    (1 << 2) /* high till EventQ.PROD != EventQ.CONS */
+#define SMMU_INTR_PRI      (1 << 1) /* PriQ. high till PriQ.PROD != PriQ.CONS */
+#define SMMU_INTR_GERROR   (1 << 0) /* cleared when  GERRORN is written */
+
+/*****************************
+ * QEMu related
+ *****************************/
+
+typedef struct {
+    SMMUBaseClass smmu_base_class;
+} SMMUV3Class;
+
+#define SMMU_DEVICE_CLASS(klass)                                    \
+    OBJECT_CLASS_CHECK(SMMUBaseClass, (klass), TYPE_SMMU_DEV_BASE)
+
+#define SMMU_V3_DEVICE_GET_CLASS(obj)                              \
+    OBJECT_GET_CLASS(SMMUBaseClass, (obj), TYPE_SMMU_V3_DEV)
+
+#ifdef ARM_SMMU_DEBUG
+static inline void dump_ste(Ste *ste)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(ste->word); i += 2) {
+        SMMU_DPRINTF(STE, "STE[%2d]: %#010x\t STE[%2d]: %#010x\n",
+                i, ste->word[i], i + 1, ste->word[i + 1]);
+    }
+}
+
+static inline void dump_cd(Cd *cd)
+{
+    int i;
+    for (i = 0; i < ARRAY_SIZE(cd->word); i += 2) {
+        SMMU_DPRINTF(CD, "CD[%2d]: %#010x\t CD[%2d]: %#010x\n",
+                i, cd->word[i], i + 1, cd->word[i + 1]);
+    }
+}
+
+static inline void dump_evt(Evt *e)
+{}
+
+static inline void dump_cmd(Cmd *cmd)
+{
+    int i;
+    for (i = 0; i < ARRAY_SIZE(cmd->word); i += 2) {
+        SMMU_DPRINTF(CMDQ, "CMD[%2d]: %#010x\t CMD[%2d]: %#010x\n",
+                i, cmd->word[i], i + 1, cmd->word[i + 1]);
+    }
+}
+
+static void dump_smmutranscfg(SMMUTransCfg *cfg)
+{
+    int i;
+    SMMU_DPRINTF(TT_1, "TransCFG stage:%d va:%lx pa:%lx s2_needed:%d\n",
+                 cfg->stage, cfg->va, cfg->pa, cfg->s2_needed);
+    for (i = 1; i <= 2; i++) {
+        SMMU_DPRINTF(TT_1, "TransCFG i:%d oas:%x tsz:%x ttbr:%lx granule:%x"
+                     " va_size:%x gran_sz:%x\n", i, cfg->oas[i], cfg->tsz[i],
+                     cfg->ttbr[i], cfg->granule[i], cfg->va_size[i],
+                     cfg->granule_sz[i]);
+    }
+}
+
+#else
+#define dump_ste(...) do {} while (0)
+#define dump_cd(...) do {} while (0)
+#define dump_evt(...) do {} while (0)
+#define dump_cmd(...) do {} while (0)
+static void dump_smmutranscfg(SMMUTransCfg *cfg) {}
+#endif
+
+#endif
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index e51ed3a..96da537 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -412,10 +412,10 @@ static void vfio_listener_region_add(MemoryListener *listener,
 
     ret = vfio_dma_map(container, iova, int128_get64(llsize),
                        vaddr, section->readonly);
-    if (ret) {
         error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
                      "0x%"HWADDR_PRIx", %p) = %d (%m)",
                      container, iova, int128_get64(llsize), vaddr, ret);
+    if (ret) {
         goto fail;
     }
 
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 4/9] hw: arm: Added SMMUv3 files for build
  2016-08-22 16:17 [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Prem Mallappa
                   ` (2 preceding siblings ...)
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 3/9] hw: arm: SMMUv3 emulation model Prem Mallappa
@ 2016-08-22 16:17 ` Prem Mallappa
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 5/9] hw: arm: Add SMMUv3 to virt platform, create DTS accordingly Prem Mallappa
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 34+ messages in thread
From: Prem Mallappa @ 2016-08-22 16:17 UTC (permalink / raw)
  To: Peter Maydell, Edgar E . Iglesias, qemu-devel; +Cc: Prem Mallappa

Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
---
 default-configs/aarch64-softmmu.mak | 1 +
 hw/arm/Makefile.objs                | 1 +
 2 files changed, 2 insertions(+)

diff --git a/default-configs/aarch64-softmmu.mak b/default-configs/aarch64-softmmu.mak
index 2449483..83a2932 100644
--- a/default-configs/aarch64-softmmu.mak
+++ b/default-configs/aarch64-softmmu.mak
@@ -7,3 +7,4 @@ CONFIG_AUX=y
 CONFIG_DDC=y
 CONFIG_DPCD=y
 CONFIG_XLNX_ZYNQMP=y
+CONFIG_ARM_SMMUV3=y
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 12764ef..5207e99 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -18,3 +18,4 @@ obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
 obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
 obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o sabrelite.o
 obj-$(CONFIG_ASPEED_SOC) += ast2400.o palmetto-bmc.o
+obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmu-v3.o
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 5/9] hw: arm: Add SMMUv3 to virt platform, create DTS accordingly
  2016-08-22 16:17 [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Prem Mallappa
                   ` (3 preceding siblings ...)
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 4/9] hw: arm: Added SMMUv3 files for build Prem Mallappa
@ 2016-08-22 16:17 ` Prem Mallappa
  2016-09-09 16:31   ` Auger Eric
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 6/9] [optional] hw: misc: added testdev for smmu Prem Mallappa
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 34+ messages in thread
From: Prem Mallappa @ 2016-08-22 16:17 UTC (permalink / raw)
  To: Peter Maydell, Edgar E . Iglesias, qemu-devel; +Cc: Prem Mallappa

Default virt platform now creates SMMU device.
Default config to build SMMU device along is in previous patches.

Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
---
 hw/arm/virt.c         | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/smmu.h | 33 +++++++++++++++++++++++++++
 include/hw/arm/virt.h |  2 ++
 3 files changed, 97 insertions(+)
 create mode 100644 include/hw/arm/smmu.h

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index c5c125e..f3c7891 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -57,6 +57,7 @@
 #include "hw/smbios/smbios.h"
 #include "qapi/visitor.h"
 #include "standard-headers/linux/input.h"
+#include "hw/arm/smmu.h"
 
 /* Number of external interrupt lines to configure the GIC with */
 #define NUM_IRQS 256
@@ -77,6 +78,7 @@ typedef struct VirtBoardInfo {
     uint32_t gic_phandle;
     uint32_t v2m_phandle;
     bool using_psci;
+    uint32_t smmu_phandle;
 } VirtBoardInfo;
 
 typedef struct {
@@ -175,6 +177,7 @@ static const MemMapEntry a15memmap[] = {
     [VIRT_FW_CFG] =             { 0x09020000, 0x00000018 },
     [VIRT_GPIO] =               { 0x09030000, 0x00001000 },
     [VIRT_SECURE_UART] =        { 0x09040000, 0x00001000 },
+    [VIRT_SMMU] =               { 0x09050000, 0x00020000 }, /* 128K, needed */
     [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
     /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
     [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
@@ -195,9 +198,19 @@ static const int a15irqmap[] = {
     [VIRT_SECURE_UART] = 8,
     [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
     [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
+    [VIRT_SMMU] = 74,    /* ...to 74 + NUM_SMMU_IRQS - 1 */
     [VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */
 };
 
+static const struct smmuirq {
+    const char *name;
+} smmuirqmap[NUM_SMMU_IRQS] = {
+    [SMMU_IRQ_EVTQ] = {"eventq"},
+    [SMMU_IRQ_PRIQ] = {"priq"},
+    [SMMU_IRQ_CMD_SYNC] = {"cmdq-sync"},
+    [SMMU_IRQ_GERROR] = {"gerror"},
+};
+
 static VirtBoardInfo machines[] = {
     {
         .cpu_model = "cortex-a15",
@@ -938,6 +951,50 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle,
                            0x7           /* PCI irq */);
 }
 
+static void alloc_smmu_phandle(VirtBoardInfo *vbi)
+{
+    if (!vbi->smmu_phandle)
+        vbi->smmu_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
+}
+
+static void create_smmu(VirtBoardInfo *vbi, qemu_irq *pic)
+{
+    int i;
+    char *smmu;
+    const char compat[] = "arm,smmu-v3";
+    int irq =  vbi->irqmap[VIRT_SMMU];
+    hwaddr base = vbi->memmap[VIRT_SMMU].base;
+    hwaddr size = vbi->memmap[VIRT_SMMU].size;
+    int type = GIC_FDT_IRQ_TYPE_SPI;
+
+    sysbus_create_varargs("smmuv3", base,
+                          pic[irq],
+                          pic[irq + 1],
+                          pic[irq + 2],
+                          pic[irq + 3],
+                          NULL);
+
+    smmu = g_strdup_printf("/smmuv3@%" PRIx64, base);
+    qemu_fdt_add_subnode(vbi->fdt, smmu);
+    qemu_fdt_setprop(vbi->fdt, smmu, "compatible", compat, sizeof(compat));
+    qemu_fdt_setprop_sized_cells(vbi->fdt, smmu, "reg", 2, base, 2, size);
+
+    for (i = 0; i < NUM_SMMU_IRQS; i++) {
+        qemu_fdt_appendprop_cells(vbi->fdt, smmu, "interrupts",
+                                  type, irq + i,
+                                  GIC_FDT_IRQ_FLAGS_LEVEL_HI);
+        qemu_fdt_appendprop_string(vbi->fdt, smmu, "interrupt-names",
+                                   smmuirqmap[i].name);
+    }
+
+    qemu_fdt_setprop_cell(vbi->fdt, smmu, "clocks", vbi->clock_phandle);
+    qemu_fdt_setprop_cell(vbi->fdt, smmu, "#iommu-cells", 0);
+    qemu_fdt_setprop_string(vbi->fdt, smmu, "clock-names", "apb_pclk");
+
+    qemu_fdt_setprop_cell(vbi->fdt, smmu, "phandle", vbi->smmu_phandle);
+    g_free(smmu);
+}
+
 static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
                         bool use_highmem)
 {
@@ -1048,6 +1105,7 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
     }
 
     qemu_fdt_setprop_cell(vbi->fdt, nodename, "#interrupt-cells", 1);
+    qemu_fdt_setprop_cells(vbi->fdt, nodename, "iommus", vbi->smmu_phandle);
     create_pcie_irq_map(vbi, vbi->gic_phandle, irq, nodename);
 
     g_free(nodename);
@@ -1332,8 +1390,12 @@ static void machvirt_init(MachineState *machine)
 
     create_rtc(vbi, pic);
 
+    alloc_smmu_phandle(vbi);
+
     create_pcie(vbi, pic, vms->highmem);
 
+    create_smmu(vbi, pic);
+
     create_gpio(vbi, pic);
 
     /* Create mmio transports, so the user can create virtio backends
diff --git a/include/hw/arm/smmu.h b/include/hw/arm/smmu.h
new file mode 100644
index 0000000..bbb5e5d
--- /dev/null
+++ b/include/hw/arm/smmu.h
@@ -0,0 +1,33 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * Author: Prem Mallappa <pmallapp@broadcom.com>
+ *
+ */
+#ifndef HW_ARM_SMMU_H
+#define HW_ARM_SMMU_H
+
+#define TYPE_SMMU_DEV_BASE "smmu-base"
+#define TYPE_SMMU_DEV      "smmuv3"
+
+typedef enum {
+    SMMU_IRQ_GERROR,
+    SMMU_IRQ_PRIQ,
+    SMMU_IRQ_EVTQ,
+    SMMU_IRQ_CMD_SYNC,
+} SMMUIrq;
+
+#endif
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 9650193..0b7138b 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -35,6 +35,7 @@
 
 #define NUM_GICV2M_SPIS       64
 #define NUM_VIRTIO_TRANSPORTS 32
+#define NUM_SMMU_IRQS          4
 
 #define ARCH_TIMER_VIRT_IRQ   11
 #define ARCH_TIMER_S_EL1_IRQ  13
@@ -54,6 +55,7 @@ enum {
     VIRT_GIC_V2M,
     VIRT_GIC_ITS,
     VIRT_GIC_REDIST,
+    VIRT_SMMU,
     VIRT_UART,
     VIRT_MMIO,
     VIRT_RTC,
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 6/9] [optional] hw: misc: added testdev for smmu
  2016-08-22 16:17 [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Prem Mallappa
                   ` (4 preceding siblings ...)
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 5/9] hw: arm: Add SMMUv3 to virt platform, create DTS accordingly Prem Mallappa
@ 2016-08-22 16:17 ` Prem Mallappa
  2017-03-27 15:24   ` Philippe Mathieu-Daudé
  2017-03-27 15:41   ` Andrew Jones
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 7/9] [optional] tests: libqos: generic pci probing helpers Prem Mallappa
                   ` (4 subsequent siblings)
  10 siblings, 2 replies; 34+ messages in thread
From: Prem Mallappa @ 2016-08-22 16:17 UTC (permalink / raw)
  To: Peter Maydell, Edgar E . Iglesias, qemu-devel; +Cc: Prem Mallappa

A simple PCI device which does DMA from 'src' to 'dst' given
src_addr, dst_addr and size, and is used by unit test. uses
pci_dma_read and pci_dma_write in a crude way but serves the purpose.

Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
---
 hw/misc/Makefile.objs      |   2 +-
 hw/misc/pci-testdev-smmu.c | 239 +++++++++++++++++++++++++++++++++++++++++++++
 hw/misc/pci-testdev-smmu.h |  22 +++++
 3 files changed, 262 insertions(+), 1 deletion(-)
 create mode 100644 hw/misc/pci-testdev-smmu.c
 create mode 100644 hw/misc/pci-testdev-smmu.h

diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index ffb49c1..fc34c5f 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -29,7 +29,6 @@ obj-$(CONFIG_IMX) += imx_ccm.o
 obj-$(CONFIG_IMX) += imx31_ccm.o
 obj-$(CONFIG_IMX) += imx25_ccm.o
 obj-$(CONFIG_IMX) += imx6_ccm.o
-obj-$(CONFIG_IMX) += imx6_src.o
 obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
 obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
 obj-$(CONFIG_MAINSTONE) += mst_fpga.o
@@ -52,3 +51,4 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o
 obj-$(CONFIG_EDU) += edu.o
 obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
 obj-$(CONFIG_AUX) += aux.o
+obj-$(CONFIG_ARM_SMMUV3) += pci-testdev-smmu.o
diff --git a/hw/misc/pci-testdev-smmu.c b/hw/misc/pci-testdev-smmu.c
new file mode 100644
index 0000000..b605912
--- /dev/null
+++ b/hw/misc/pci-testdev-smmu.c
@@ -0,0 +1,239 @@
+/*
+ * QEMU PCI test device
+ *
+ * Copyright (c) 2012 Red Hat Inc.
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "qemu/event_notifier.h"
+
+#include "pci-testdev-smmu.h"
+
+/*
+ * pci-testdev-smmu:
+ *          Simple PCIe device, to enable read and write from memory.
+ * Architecture:
+ *          Following registers are supported.
+ *          TST_COMMAND = 0x0
+ *          TST_STATUS  = 0x4
+ *          TST_SRC_ADDRESS = 0x8
+ *          TST_SIZE        = 0x10
+ *          TST_DST_ADDRESS = 0x18
+ */
+#define PCI_TSTDEV_NREGS 0x10
+
+/*
+ *  TST_COMMAND Register bits
+ *      OP[0]
+ *          READ = 0x0
+ *          WRITE = 0x1
+ */
+
+struct RegInfo {
+        uint64_t data;
+        char *name;
+};
+typedef struct RegInfo RegInfo;
+
+typedef struct PCITestDevState {
+    /*< private >*/
+    PCIDevice dev;
+    /*< public >*/
+
+    MemoryRegion mmio;
+    RegInfo regs[PCI_TSTDEV_NREGS];
+} PCITestDevState;
+
+#define TYPE_PCI_TEST_DEV "pci-testdev-smmu"
+
+#define PCI_TEST_DEV(obj) \
+    OBJECT_CHECK(PCITestDevState, (obj), TYPE_PCI_TEST_DEV)
+
+static void
+pci_tstdev_reset(PCITestDevState *d)
+{
+    memset(d->regs, 0, sizeof(d->regs));
+}
+
+static inline void
+pci_tstdev_write_reg(PCITestDevState *pdev, hwaddr addr, uint64_t val)
+{
+    RegInfo *reg = &pdev->regs[addr >> 2];
+    reg->data = val;
+}
+
+static inline uint32_t
+pci_tstdev_read32_reg(PCITestDevState *pdev, hwaddr addr)
+{
+    RegInfo *reg = &pdev->regs[addr >> 2];
+    return (uint32_t) reg->data;
+}
+
+static inline uint64_t
+pci_tstdev_read64_reg(PCITestDevState *pdev, hwaddr addr)
+{
+        RegInfo *reg = &pdev->regs[addr >> 2];
+        return reg->data;
+}
+
+static void
+pci_tstdev_handle_cmd(PCITestDevState *pdev, hwaddr addr, uint64_t val,
+                            unsigned _unused_size)
+{
+    uint64_t s = pci_tstdev_read64_reg(pdev, TST_REG_SRC_ADDR);
+    uint64_t d = pci_tstdev_read64_reg(pdev, TST_REG_DST_ADDR);
+    uint32_t size = pci_tstdev_read32_reg(pdev, TST_REG_SIZE);
+    uint8_t buf[128];
+
+    printf("+++++++++++++++++++++> src:%lx, dst:%lx size:%d\n",
+           s, d, size);
+    while (size) {
+        int nbytes = (size < sizeof(buf)) ? size: sizeof(buf);
+        int ret = 0;
+        printf("nbytes:%d\n", nbytes);
+        if (val & CMD_READ) {
+            printf("doing pci_dma_read\n");
+            ret = pci_dma_read(&pdev->dev, s, (void*)buf, nbytes);
+        }
+        if (ret)
+            return;
+
+        if (val & CMD_WRITE) {
+            printf("doing pci_dma_write\n");
+            ret = pci_dma_write(&pdev->dev, d, (void*)buf, nbytes);
+        }
+        size -= nbytes;
+        s += nbytes;
+        d += nbytes;
+    }
+}
+
+static void
+pci_tstdev_mmio_write(void *opaque, hwaddr addr,
+                      uint64_t val, unsigned size)
+{
+    PCITestDevState *d = opaque;
+    uint64_t lo;
+
+    printf("=================> addr:%ld act:%d val:%lx reg_addr:%p\n",
+           addr, TST_REG_COMMAND, val, &d->regs[addr]);
+    //addr >>= 2;
+    switch (addr) {
+    case TST_REG_COMMAND:
+            printf("Calling command handler.....\n");
+            pci_tstdev_handle_cmd(d, addr, val, size);
+    case TST_REG_SRC_ADDR:
+    case TST_REG_DST_ADDR:
+    case TST_REG_SIZE:
+            pci_tstdev_write_reg(d, addr, val);
+            break;
+    case TST_REG_SRC_ADDR + 4:
+    case TST_REG_DST_ADDR + 4:
+            lo = pci_tstdev_read32_reg(d, addr);
+            lo &= (0xffffffffULL << 32);
+            pci_tstdev_write_reg(d, addr, (val << 32) | lo);
+            break;
+    case TST_REG_STATUS:        /* Read only reg */
+    default:
+        printf("Unkown/RO register write\n");
+        break;
+    }
+}
+
+static uint64_t
+pci_tstdev_mmio_read(void *opaque, hwaddr addr, unsigned size)
+{
+    PCITestDevState *d = opaque;
+
+    switch (addr) {
+    case TST_REG_SRC_ADDR:
+    case TST_REG_DST_ADDR:
+            return pci_tstdev_read64_reg(d, addr);
+    }
+
+    return pci_tstdev_read32_reg(d, addr);
+}
+
+static const MemoryRegionOps pci_testdev_mmio_ops = {
+    .read = pci_tstdev_mmio_read,
+    .write = pci_tstdev_mmio_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+};
+
+static void pci_tstdev_realize(PCIDevice *pci_dev, Error **errp)
+{
+    PCITestDevState *d = PCI_TEST_DEV(pci_dev);
+    uint8_t *pci_conf;
+
+    pci_conf = pci_dev->config;
+
+    pci_conf[PCI_INTERRUPT_PIN] = 0; /* no interrupt pin */
+
+    memory_region_init_io(&d->mmio, OBJECT(d), &pci_testdev_mmio_ops, d,
+                          "pci-testdev-smmu-mmio", 1 << 10);
+
+    pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
+}
+
+static void
+pci_tstdev_uninit(PCIDevice *dev)
+{
+    PCITestDevState *d = PCI_TEST_DEV(dev);
+
+    pci_tstdev_reset(d);
+}
+
+static void qdev_pci_tstdev_reset(DeviceState *dev)
+{
+    PCITestDevState *d = PCI_TEST_DEV(dev);
+    pci_tstdev_reset(d);
+}
+
+static void pci_tstdev_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->realize = pci_tstdev_realize;
+    k->exit = pci_tstdev_uninit;
+    k->vendor_id = PCI_VENDOR_ID_REDHAT;
+    k->device_id = PCI_DEVICE_ID_REDHAT_TEST;
+    k->revision = 0x00;
+    k->class_id = PCI_CLASS_OTHERS;
+    dc->desc = "PCI Test Device - for smmu";
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+    dc->reset = qdev_pci_tstdev_reset;
+}
+
+static const TypeInfo pci_tstdev_info = {
+    .name          = TYPE_PCI_TEST_DEV,
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCITestDevState),
+    .class_init    = pci_tstdev_class_init,
+};
+
+static void pci_tstdev_register_types(void)
+{
+    type_register_static(&pci_tstdev_info);
+}
+
+type_init(pci_tstdev_register_types)
diff --git a/hw/misc/pci-testdev-smmu.h b/hw/misc/pci-testdev-smmu.h
new file mode 100644
index 0000000..4a2d463
--- /dev/null
+++ b/hw/misc/pci-testdev-smmu.h
@@ -0,0 +1,22 @@
+#ifndef HW_MISC_PCI_TESTDEV_SMMU
+#define HW_MISC_PCI_TESTDEV_SMMU
+
+enum reg {
+        TST_REG_COMMAND  = 0x0,
+        TST_REG_STATUS   = 0x4,
+        TST_REG_SRC_ADDR = 0x8,
+        TST_REG_SIZE     = 0x10,
+        TST_REG_DST_ADDR = 0x18,
+
+        TST_REG_LAST     = 0x30,
+};
+
+#define CMD_READ    0x100
+#define CMD_WRITE   0x200
+#define CMD_RW      (CMD_READ | CMD_WRITE)
+
+#define STATUS_OK   (1 << 0)
+#define STATUS_CMD_ERROR (1 << 1)
+#define STATUS_CMD_INVALID (1 << 2)
+
+#endif
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 7/9] [optional] tests: libqos: generic pci probing helpers
  2016-08-22 16:17 [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Prem Mallappa
                   ` (5 preceding siblings ...)
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 6/9] [optional] hw: misc: added testdev for smmu Prem Mallappa
@ 2016-08-22 16:17 ` Prem Mallappa
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 8/9] [optional] tests: SMMUv3 unit tests Prem Mallappa
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 34+ messages in thread
From: Prem Mallappa @ 2016-08-22 16:17 UTC (permalink / raw)
  To: Peter Maydell, Edgar E . Iglesias, qemu-devel; +Cc: Prem Mallappa

Current libqos PCI helpers are x86 only, this addes a generic interface.

Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
---
 tests/libqos/pci-generic.c | 197 +++++++++++++++++++++++++++++++++++++++++++++
 tests/libqos/pci-generic.h |  58 +++++++++++++
 2 files changed, 255 insertions(+)
 create mode 100644 tests/libqos/pci-generic.c
 create mode 100644 tests/libqos/pci-generic.h

diff --git a/tests/libqos/pci-generic.c b/tests/libqos/pci-generic.c
new file mode 100644
index 0000000..1820c0e
--- /dev/null
+++ b/tests/libqos/pci-generic.c
@@ -0,0 +1,197 @@
+/*
+ * libqos PCI bindings for non-PC
+ *
+ * Copyright IBM, Corp. 2012-2013
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Prem Mallappa     <prem.mallappa@gmail.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/pci-generic.h"
+
+#include "hw/pci/pci_regs.h"
+
+#include "qemu-common.h"
+#include "qemu/host-utils.h"
+
+#include <glib.h>
+
+static uint8_t qpci_generic_io_readb(QPCIBus *bus, void *addr)
+{
+    return readb((uintptr_t)addr);
+}
+
+static uint16_t qpci_generic_io_readw(QPCIBus *bus, void *addr)
+{
+    return readw((uintptr_t)addr);
+}
+
+static uint32_t qpci_generic_io_readl(QPCIBus *bus, void *addr)
+{
+    return readl((uintptr_t)addr);
+}
+
+static void qpci_generic_io_writeb(QPCIBus *bus, void *addr, uint8_t value)
+{
+    writeb((uintptr_t)addr, value);
+}
+
+static void qpci_generic_io_writew(QPCIBus *bus, void *addr, uint16_t value)
+{
+    writew((uintptr_t)addr, value);
+}
+
+static void qpci_generic_io_writel(QPCIBus *bus, void *addr, uint32_t value)
+{
+    writel((uintptr_t)addr, value);
+}
+
+#define devfn2addr(base, devfn, offset) \
+	((base) | ((devfn) << 12) | (offset))
+
+#define bdf2offset(bus, devfn) \
+    ((bus) << 20 | (devfn) << 12)
+
+static uint8_t qpci_generic_config_readb(QPCIBus *bus, int devfn, uint8_t offset)
+{
+    QPCIBusGen *s = container_of(bus, QPCIBusGen, bus);
+    return readb(devfn2addr(s->base, devfn, offset));
+}
+
+static uint16_t qpci_generic_config_readw(QPCIBus *bus, int devfn, uint8_t offset)
+{ 
+    QPCIBusGen *s = container_of(bus, QPCIBusGen, bus);
+    return readw(devfn2addr(s->base, devfn, offset));
+}
+
+static uint32_t qpci_generic_config_readl(QPCIBus *bus, int devfn, uint8_t offset)
+{
+    QPCIBusGen *s = container_of(bus, QPCIBusGen, bus);
+    return readl(devfn2addr(s->base, devfn, offset));
+}
+
+static void qpci_generic_config_writeb(QPCIBus *bus, int devfn, uint8_t offset, uint8_t value)
+{
+    QPCIBusGen *s = container_of(bus, QPCIBusGen, bus);
+    writeb(devfn2addr(s->base, devfn, offset), value);
+}
+
+static void qpci_generic_config_writew(QPCIBus *bus, int devfn, uint8_t offset, uint16_t value)
+{
+    QPCIBusGen *s = container_of(bus, QPCIBusGen, bus);
+    writew(devfn2addr(s->base, devfn, offset), value);
+}
+
+static void qpci_generic_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value)
+{
+    QPCIBusGen *s = container_of(bus, QPCIBusGen, bus);
+    writel(devfn2addr(s->base, devfn, offset), value);
+}
+
+static void *qpci_generic_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr)
+{
+    QPCIBusGen *s = container_of(bus, QPCIBusGen, bus);
+    static const int bar_reg_map[] = {
+        PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2,
+        PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5,
+    };
+    int bar_reg;
+    uint32_t addr;
+    uint64_t size;
+    uint32_t io_type;
+
+    g_assert(barno >= 0 && barno <= 5);
+    bar_reg = bar_reg_map[barno];
+
+    qpci_config_writel(dev, bar_reg, 0xFFFFFFFF);
+    addr = qpci_config_readl(dev, bar_reg);
+
+    io_type = addr & PCI_BASE_ADDRESS_SPACE;
+    if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
+        addr &= PCI_BASE_ADDRESS_IO_MASK;
+    } else {
+        addr &= PCI_BASE_ADDRESS_MEM_MASK;
+    }
+
+    size = (1ULL << ctzl(addr));
+    if (size == 0) {
+        return NULL;
+    }
+    if (sizeptr) {
+        *sizeptr = size;
+    }
+
+    if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
+        uint16_t loc;
+
+        g_assert(QEMU_ALIGN_UP(s->pci_iohole_alloc, size) + size
+                 <= s->pci_iohole_size);
+        s->pci_iohole_alloc = QEMU_ALIGN_UP(s->pci_iohole_alloc, size);
+        loc = s->pci_iohole_start + s->pci_iohole_alloc;
+        s->pci_iohole_alloc += size;
+
+        qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO);
+
+        return (void *)(intptr_t)loc;
+    } else {
+        uint64_t loc;
+
+        g_assert(QEMU_ALIGN_UP(s->pci_hole_alloc, size) + size
+                 <= s->pci_hole_size);
+        s->pci_hole_alloc = QEMU_ALIGN_UP(s->pci_hole_alloc, size);
+        loc = s->pci_hole_start + s->pci_hole_alloc;
+        s->pci_hole_alloc += size;
+        printf("%s: hole_start:%x hole_alloc:%x\n", __func__,
+               s->pci_hole_start, s->pci_hole_alloc);
+        qpci_config_writel(dev, bar_reg, loc);
+
+        return (void *)(intptr_t)loc;
+    }
+}
+
+static void qpci_generic_iounmap(QPCIBus *bus, void *data)
+{
+    /* FIXME */
+}
+
+QPCIBus *qpci_init_generic(QPCIBusGen *conf)
+{
+    QPCIBusGen *ret;
+
+    ret = g_malloc(sizeof(*ret));
+    memcpy(ret, conf, sizeof(*ret));
+
+    ret->bus.io_readb = qpci_generic_io_readb;
+    ret->bus.io_readw = qpci_generic_io_readw;
+    ret->bus.io_readl = qpci_generic_io_readl;
+
+    ret->bus.io_writeb = qpci_generic_io_writeb;
+    ret->bus.io_writew = qpci_generic_io_writew;
+    ret->bus.io_writel = qpci_generic_io_writel;
+
+    ret->bus.config_readb = qpci_generic_config_readb;
+    ret->bus.config_readw = qpci_generic_config_readw;
+    ret->bus.config_readl = qpci_generic_config_readl;
+
+    ret->bus.config_writeb = qpci_generic_config_writeb;
+    ret->bus.config_writew = qpci_generic_config_writew;
+    ret->bus.config_writel = qpci_generic_config_writel;
+
+    ret->bus.iomap = qpci_generic_iomap;
+    ret->bus.iounmap = qpci_generic_iounmap;
+
+    return &ret->bus;
+}
+
+void qpci_free_generic(QPCIBus *bus)
+{
+    QPCIBusGen *s = container_of(bus, QPCIBusGen, bus);
+
+    g_free(s);
+}
diff --git a/tests/libqos/pci-generic.h b/tests/libqos/pci-generic.h
new file mode 100644
index 0000000..0dbaaf1
--- /dev/null
+++ b/tests/libqos/pci-generic.h
@@ -0,0 +1,58 @@
+/*
+ * libqos PCI bindings for PC
+ *
+ * Copyright IBM, Corp. 2012-2013
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Prem Mallappa     <prem.mallappa@gmail.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_PCI_PC_H
+#define LIBQOS_PCI_PC_H
+
+#include "libqos/pci.h"
+
+typedef struct QPCIBusGen
+{
+    QPCIBus bus;
+
+    uint64_t base;
+    uint32_t pci_hole_start;
+    uint32_t pci_hole_size;
+    uint32_t pci_hole_alloc;
+
+    uint16_t pci_iohole_start;
+    uint16_t pci_iohole_size;
+    uint16_t pci_iohole_alloc;
+} QPCIBusGen;
+
+QPCIBus *qpci_init_generic(QPCIBusGen *);
+void     qpci_free_generic(QPCIBus *bus);
+#if 0
+uint8_t qpci_generic_io_readb(QPCIBus *bus, void *addr);
+uint16_t qpci_generic_io_readw(QPCIBus *bus, void *addr);
+uint32_t qpci_generic_io_readl(QPCIBus *bus, void *addr);
+
+void qpci_generic_io_writeb(QPCIBus *bus, void *addr, uint8_t value);
+void qpci_generic_io_writew(QPCIBus *bus, void *addr, uint16_t value);
+void qpci_generic_io_writel(QPCIBus *bus, void *addr, uint32_t value);
+
+uint8_t qpci_generic_config_readb(QPCIBus *bus, int devfn, uint8_t offset);
+uint16_t qpci_generic_config_readw(QPCIBus *bus, int devfn, uint8_t offset);
+uint32_t qpci_generic_config_readl(QPCIBus *bus, int devfn, uint8_t offset);
+
+void qpci_generic_config_writeb(QPCIBus *bus, int devfn,
+                      uint8_t offset, uint8_t value);
+void qpci_generic_config_writew(QPCIBus *bus, int devfn,
+                      uint8_t offset, uint16_t value);
+void qpci_generic_config_writel(QPCIBus *bus, int devfn,
+                      uint8_t offset, uint32_t value);
+
+void * qpci_generic_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr);
+void qpci_genericiounmap(QPCIBus *bus, void *data);
+#endif
+#endif
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 8/9] [optional] tests: SMMUv3 unit tests
  2016-08-22 16:17 [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Prem Mallappa
                   ` (6 preceding siblings ...)
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 7/9] [optional] tests: libqos: generic pci probing helpers Prem Mallappa
@ 2016-08-22 16:17 ` Prem Mallappa
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 9/9] [optional] arm: smmu-v3: ACPI IORT initial support Prem Mallappa
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 34+ messages in thread
From: Prem Mallappa @ 2016-08-22 16:17 UTC (permalink / raw)
  To: Peter Maydell, Edgar E . Iglesias, qemu-devel; +Cc: Prem Mallappa

 - initializes SMMU device
 - initializes Test device
 - allocates page tables 1:1 mapping va == pa
 - allocates STE/CD accordingly for S1, S2, S1+S2
 - initiates DMA via PCI test device
 - verifies transfered data

Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
---
 tests/Makefile.include |   4 +
 tests/smmuv3-test.c    | 952 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 956 insertions(+)
 create mode 100644 tests/smmuv3-test.c

diff --git a/tests/Makefile.include b/tests/Makefile.include
index 7d63d16..08bee81 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -251,6 +251,8 @@ gcov-files-sparc-y += hw/timer/m48t59.c
 gcov-files-sparc64-y += hw/timer/m48t59.c
 check-qtest-arm-y = tests/tmp105-test$(EXESUF)
 check-qtest-arm-y = tests/ds1338-test$(EXESUF)
+check-qtest-aarch64-y += tests/smmuv3-test$(EXESUF)
+gcov-files-aarch64-y += hw/arm/smmu-v3.c
 gcov-files-arm-y += hw/misc/tmp105.c
 check-qtest-arm-y += tests/virtio-blk-test$(EXESUF)
 gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c
@@ -535,6 +537,7 @@ tests/test-crypto-afsplit$(EXESUF): tests/test-crypto-afsplit.o $(test-crypto-ob
 tests/test-crypto-block$(EXESUF): tests/test-crypto-block.o $(test-crypto-obj-y)
 
 libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o
+libqos-obj-y += tests/libqos/pci-generic.o
 libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
 libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o
 libqos-pc-obj-y += tests/libqos/malloc-pc.o tests/libqos/libqos-pc.o
@@ -608,6 +611,7 @@ tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-y)
 tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-obj-y)
 tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y)
 tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o
+tests/smmuv3-test$(EXESUF): tests/smmuv3-test.o $(libqos-obj-y) tests/libqos/malloc-generic.o
 
 ifeq ($(CONFIG_POSIX),y)
 LIBS += -lutil
diff --git a/tests/smmuv3-test.c b/tests/smmuv3-test.c
new file mode 100644
index 0000000..40837ef
--- /dev/null
+++ b/tests/smmuv3-test.c
@@ -0,0 +1,952 @@
+/*
+ * Copyright (C) 2014-2016 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Prem Mallappa <pmallapp@broadcom.com>
+ *                       <prem.mallappa@gmail.com>
+ */
+
+#include "qemu/osdep.h"
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "libqtest.h"
+#include "libqos/libqos.h"
+#include "libqos/pci-generic.h"
+#include "libqos/malloc-generic.h"
+
+#include "qemu-common.h"
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/pci_regs.h"
+
+/* PCIe test device */
+#include "hw/misc/pci-testdev-smmu.h"
+
+/* SMMU */
+#include "hw/arm/smmu-common.h"
+#include "hw/arm/smmuv3-internal.h"
+
+
+/*
+ * STE/CD modification helpers
+ */
+#define ___SET(ste, off, start, len, val)                               \
+    ({                                                                  \
+        uint32_t *ptr = &(ste)->word[(off)];                            \
+        *ptr = deposit32(*ptr, start, len, val);                        \
+    })
+
+#define STE_SET_VALID(ste, val)   ___SET(ste, 0, 0, 1, val)
+#define STE_SET_CONFIG(ste, val)  ___SET(ste, 0, 1, 3, val)
+#define STE_SET_S1FMT(ste, val)   ___SET(ste, 0, 4, 2, val)
+#define STE_SET_S1CDMAX(ste, val) ___SET(ste, 1, 8, 2, val)
+#define STE_SET_EATS(ste, val)    ___SET(ste, 2, 28, 2, val)
+#define STE_SET_STRW(ste, val)    ___SET(ste, 2, 30, 2, val)
+#define STE_SET_S2VMID(ste, val)  ___SET(ste, 4, 0, 16, val) /* 4 */
+#define STE_SET_S2T0SZ(ste, val)  ___SET(ste, 5, 0, 6, val) /* 5 */
+#define STE_SET_S2TG(ste, val)    ___SET(ste, 5, 14, 2, val)
+#define STE_SET_S2PS(ste, val)    ___SET(ste, 5, 16, 3, val)
+#define STE_SET_S2AA64(ste, val)  ___SET(ste, 5, 19, 1, val)
+#define STE_SET_S2HD(ste, val)    ___SET(ste, 5, 24, 1, val)
+#define STE_SET_S2HA(ste, val)    ___SET(ste, 5, 25, 1, val)
+#define STE_SET_S2S(ste, val)     ___SET(ste, 5, 26, 1, val)
+#define STE_SET_CTXPTR(ste, val)                \
+    ({                                          \
+        uint64_t __val = val;                   \
+        __val >>= 6;                            \
+        ___SET((ste), 0, 6, 26, __val);         \
+        __val >>= 32;                           \
+        ___SET((ste), 1, 0, 16, __val);         \
+    })
+
+#define STE_SET_S2TTB(ste, val)                   \
+    ({                                            \
+        uint64_t __val = val;                     \
+        __val >>= 4;                              \
+        ___SET(ste, 6, 4, 28, __val);             \
+        __val >>= 32;                             \
+        ___SET(ste, 7, 0, 16, __val);             \
+    })
+
+#define CD_SET_VALID(cd, val)   ___SET((cd), 0, 31, 1, val)
+#define CD_SET_ASID(cd, val)    ___SET((cd), 1, 16, 16, val)
+#define CD_SET_TTB(cd, sel, val)                               \
+    ({                                                         \
+        uint64_t __val = val;                                  \
+        ___SET((cd), (sel)*2 + 2, 0, 32, __val & ~0xf);        \
+        __val >>= 32;                                          \
+        ___SET((cd), (sel)*2 + 3, 0, 16, __val & 0xffff);      \
+    })
+
+#define CD_SET_TSZ(cd, sel, val)   ___SET((cd), 0, (16*(sel)) + 0, 6, val)
+#define CD_SET_TG(cd, sel, val)    ___SET((cd), 0, (16*(sel)) + 6, 2, val)
+#define CD_SET_EPD(cd, sel, val)   ___SET((cd), 0, (16*(sel)) + 14, 1, val)
+
+#define CD_SET_T0SZ(cd, val)    CD_SET_TSZ((cd), 0, val)
+#define CD_SET_T1SZ(cd, val)    CD_SET_TSZ((cd), 1, val)
+#define CD_SET_TG0(cd, val)     CD_SET_TG((cd), 0, val)
+#define CD_SET_TG1(cd, val)     CD_SET_TG((cd), 1, val)
+#define CD_SET_EPD0(cd, val)    CD_SET_EPD((cd), 0, val)
+#define CD_SET_EPD1(cd, val)    CD_SET_EPD((cd), 1, val)
+#define CD_SET_IPS(cd, val)     ___SET((cd), 1, 0, 3, val)
+#define CD_SET_AARCH64(cd, val) ___SET((cd), 1, 9, 1, val)
+#define CD_SET_TTB0(cd, val)    CD_SET_TTB((cd), 0, val)
+#define CD_SET_TTB1(cd, val)    CD_SET_TTB((cd), 1, val)
+
+/* SMMU Device local state */
+struct SMMUDevState {
+    /* SMMU related fields */
+    void *reg_base;
+    uint64_t strtab_base;
+    QGuestAllocator *strtab_alloc;
+    QGuestAllocator *cd_alloc;
+    QGuestAllocator *pgtbl_alloc;
+    SMMUQueue cmdq;
+};
+typedef struct SMMUDevState SMMUDevState;
+
+static inline void smmu_write64_reg(SMMUDevState *s, uint32_t reg, uint64_t val)
+{
+    uint64_t addr = (uint64_t)(s->reg_base + reg);
+    writel(addr, val);
+}
+
+static inline void smmu_write_reg(SMMUDevState *s, uint32_t reg, uint64_t val)
+{
+    uint64_t addr = (uint64_t)(s->reg_base + reg);
+    writew(addr, val);
+}
+
+/* Our Test device */
+struct SMMUTestDevState {
+    QPCIDevice *dev;
+    void *reg_base;
+};
+typedef struct SMMUTestDevState SMMUTestDevState;
+
+typedef struct SMMUTestVMCfg {
+    uint64_t ram_base;
+    uint64_t ram_size;
+    uint32_t page_size;
+
+    QPCIBusGen virt_pci;
+} SMMUTestVMCfg;
+
+typedef struct SMMUTestCfg {
+    uint8_t sid_size;
+    uint8_t sid_split;
+    uint32_t cmdq_shift;
+} SMMUTestCfg;
+
+/* SMMU Test state */
+struct SMMUTestState {
+    QPCIBus *pcibus;
+    QTestState *qtest;
+
+    QGuestAllocator *alloc;
+
+    SMMUDevState sdev;
+
+    SMMUTestDevState tdev;
+
+    SMMUTestCfg  cfg;
+
+    SMMUTestVMCfg vm_cfg;
+};
+typedef struct SMMUTestState SMMUTestState;
+
+static void cleanup_vm(SMMUTestState *s)
+{
+    qpci_free_generic(s->pcibus);
+}
+
+static void test_smmu_cleanup(SMMUTestState *state)
+{
+    printf("Cleanup called\n");
+    generic_alloc_uninit(state->alloc);
+    qtest_quit(state->qtest);
+    cleanup_vm(state);
+}
+
+static void abort_handler(void *data)
+{
+    SMMUTestState *state = (SMMUTestState *)data;
+    g_test_message("abort handler called");
+    test_smmu_cleanup(state);
+}
+
+static void save_fn(QPCIDevice *dev, int devfn, void *data)
+{
+    QPCIDevice **pdev = (QPCIDevice **) data;
+    printf("dev->devfn:%d\n", devfn);
+    *pdev = dev;
+}
+
+static QPCIDevice *get_device(QPCIBus *pcibus)
+{
+    QPCIDevice *dev;
+
+    dev = NULL;
+    qpci_device_foreach(pcibus, 0x1b36, 0x0005, save_fn, &dev);
+    g_assert(dev != NULL);
+    //while(1);
+    return dev;
+}
+
+static void testdev_write64_reg(SMMUTestDevState *tdev, uint32_t reg,
+                                uint64_t val)
+{
+    uint64_t addr = (uint64_t)(tdev->reg_base + reg);
+    writel(addr, val);
+}
+
+static void testdev_write_reg(SMMUTestDevState *tdev, uint32_t reg,
+                              uint64_t val)
+{
+    uint64_t addr = (uint64_t)(tdev->reg_base + reg);
+    writew(addr, val);
+}
+
+static void testdev_dma(SMMUTestDevState *tdev, void *src,
+                        void *dst, int nbytes)
+{
+    testdev_write64_reg(tdev, TST_REG_SRC_ADDR, (uint64_t)src);
+    testdev_write64_reg(tdev, TST_REG_DST_ADDR, (uint64_t)dst);
+    testdev_write_reg(tdev, TST_REG_SIZE, nbytes);
+
+    testdev_write_reg(tdev, TST_REG_COMMAND, CMD_RW);
+}
+
+static void testdev_setup(SMMUTestState *s)
+{
+    SMMUTestDevState *tdev = &s->tdev;
+    uint64_t barsize;
+
+    tdev->dev = get_device(s->pcibus);
+    g_assert_nonnull(tdev->dev);
+
+    tdev->reg_base = qpci_iomap(tdev->dev, 0, &barsize);
+    g_assert_nonnull(tdev->reg_base);
+
+    qpci_device_enable(tdev->dev);
+}
+
+/* following values are taken from virt.c virt.h files */
+#define MMIO_RAM_ADDR           0x40000000ULL
+#define MMIO_RAM_SIZE           4096ULL /* in MB */
+
+static void setup_vm_cmd(SMMUTestState *s, const char *cmd, bool msix)
+{
+    s->vm_cfg = (SMMUTestVMCfg) {
+        .virt_pci = {
+            .base = 0x3f000000,
+            .pci_hole_start = 0x10000000,
+            .pci_hole_size = 0x2eff0000,
+            .pci_hole_alloc = 0,
+        },
+        .ram_base = MMIO_RAM_ADDR,
+        .ram_size = MMIO_RAM_SIZE << 20,
+        .page_size = s->vm_cfg.page_size,
+    };
+
+    QPCIBusGen *vpci = &s->vm_cfg.virt_pci;
+
+    s->qtest = qtest_start(cmd);
+
+    s->pcibus = qpci_init_generic(vpci);
+
+    printf("VM setup with cmdline:%s\n", cmd);
+}
+
+static void setup_vm(SMMUTestState *s, bool is_gdb_start)
+{
+    const char *gdb = is_gdb_start? "-s -S": "";
+    const char *mon = is_gdb_start? " -chardev socket,id=mon0,host=localhost,port=6001,server,telnet,nowait -monitor chardev:mon0 ": "";
+    char *cmd = g_strdup_printf(
+        " -cpu cortex-a57 -m %llu -machine virt " //-smp 4 "
+        " -device i82801b11-bridge,multifunction=on,bus=pcie.0,addr=05,id=pcie.1 "
+        " %s "
+        " -device pci-testdev-smmu,bus=pcie.0,addr=04 "
+        " -d iommu "
+        " %s ", MMIO_RAM_SIZE, /* in MB */
+        mon, gdb);
+
+    setup_vm_cmd(s, cmd, false);
+
+    g_free(cmd);
+}
+
+#define SIZE_MB(x)              ((x) << 20)
+/* 20 is MB, 1/4th of the size we start the allocator */
+#define TEST_ALLOCATOR_START    (SIZE_MB(MMIO_RAM_SIZE) >> 2)
+#define TEST_ALLOCATOR_SIZE     SIZE_MB(32) /* 32MB */
+
+#define STRTAB_ALLOCATOR_START  (TEST_ALLOCATOR_START + \
+                                 TEST_ALLOCATOR_SIZE + SIZE_MB(32))
+#define STRTAB_ALLOCATOR_SIZE   SIZE_MB(32)
+
+#define CD_ALLOCATOR_START      (STRTAB_ALLOCATOR_START + \
+                                 STRTAB_ALLOCATOR_SIZE + SIZE_MB(32))
+#define CD_ALLOCATOR_SIZE       (SIZE_MB(32))
+
+#define PGTABLE_ALLOCATOR_START (CD_ALLOCATOR_START + \
+                                 CD_ALLOCATOR_SIZE + SIZE_MB(32))
+#define PGTABLE_ALLOCATOR_SIZE  (SIZE_MB(32))
+
+
+static int smmu_init_cmdq(SMMUTestState *state)
+{
+    SMMUDevState *smmu = &state->sdev;
+    SMMUQueue *q = &smmu->cmdq;
+    SMMUTestCfg *cfg = &state->cfg;
+
+    q->shift = cfg->cmdq_shift;
+    q->ent_size = sizeof(Cmd);
+    q->entries = 1 << cfg->cmdq_shift;
+
+    q->base = guest_alloc(state->alloc, q->entries * sizeof(Cmd));
+    if (!q->base)
+        return -ENOMEM;
+    q->base &= ~0x1fULL;    /* last 5 bits are for size in log2 */
+    smmu_write64_reg(smmu, SMMU_REG_CMDQ_BASE,
+                     q->base | q->shift);
+    smmu_write_reg(smmu, SMMU_REG_CMDQ_PROD, 0x0);
+    smmu_write_reg(smmu, SMMU_REG_CMDQ_CONS, 0x0);
+
+    return 0;
+}
+
+static uint64_t smmu_get_ste(SMMUDevState *smmu, int devfn)
+{
+    uint64_t stmp, stm, step;
+    SMMUTestState *state = container_of(smmu, SMMUTestState, sdev);
+    SMMUTestCfg *cfg = &state->cfg;
+    int split = cfg->sid_split;
+    int l1_off = devfn >> split,
+        l2_off = devfn & ~(1 << split);
+    uint64_t span_mask = 0x3fULL;
+
+    printf("devfn:%x l1_off:%x l2_off:%x\n", devfn, l1_off, l2_off);
+    stmp = smmu->strtab_base + (l1_off * sizeof(STEDesc));
+    //stm = readl(stmp);
+    qtest_memread(state->qtest, stmp, &stm, sizeof(stm));
+    printf("stmp:%lx stm:%lx\n", stmp, stm);
+
+    if (stm && (stm & span_mask)) {
+        printf("already allocated ste \n");
+    } else {
+        uint64_t page;
+        int size = sizeof(Ste) * (1 << split);
+
+        page = guest_alloc(smmu->strtab_alloc, size);
+        assert(!(page & span_mask));
+        qmemset(page, 0, size);
+
+        page &= ~span_mask;
+        /*
+         * 2^(span - 1) entries, should cover current devfn
+         * if devfn is 0x20, devfn >> 2 = 8
+         * this should cover 2^7 = 128 STE's, which covers devfn 0x20
+         */
+        page |= (devfn >> 2);
+
+        printf("page:%lx size:%d qtest:%p global:%p\n",
+               page, size, state->qtest, global_qtest);
+        stm = page;
+        //qtest_memwrite(state->qtest, stmp, &stm, sizeof(stmp));
+        writeq(stmp, stm);
+
+        //qtest_memread(state->qtest, stmp, &page, sizeof(stmp));
+        page = readq(stmp);
+        printf("stm:%lx page:%lx\n", stm, page);
+        assert(stm == page);
+    }
+
+    stm &= ~span_mask;
+    step = stm + (l2_off * sizeof(Ste));
+    printf("step:%lx\n", step);
+    return step;
+}
+
+static void smmu_strtab_initone(SMMUTestState *state, int devfn)
+{
+
+    SMMUDevState *smmu = &state->sdev;
+    uint64_t step = smmu_get_ste(smmu, devfn);
+    Ste ste;
+
+    qtest_memread(state->qtest, step, &ste, sizeof(ste));
+    STE_SET_CONFIG(&ste, 0x4);          /* bypass */
+    STE_SET_VALID(&ste, 0x1);
+    printf("%s: ste.word[0]:%x\n", __func__, ste.word[0]);
+    qtest_memwrite(state->qtest, step, &ste, sizeof(ste));
+}
+
+static int smmu_strtab_init(SMMUTestState *state)
+{
+    uint32_t size;
+    SMMUDevState *smmu = &state->sdev;
+    SMMUTestCfg *cfg = &state->cfg;
+
+    size = sizeof(STEDesc) * (1 << cfg->sid_split);
+    smmu->strtab_base = guest_alloc(state->alloc, size);
+    if (!smmu->strtab_base)
+        return -ENOMEM;
+
+    qmemset(smmu->strtab_base, 0, size);
+    smmu_write64_reg(smmu, SMMU_REG_STRTAB_BASE, smmu->strtab_base);
+    smmu_write64_reg(smmu, SMMU_REG_STRTAB_BASE_CFG, 0x10210);
+
+    smmu->strtab_alloc = generic_alloc_init(MMIO_RAM_ADDR +
+                                            STRTAB_ALLOCATOR_START,
+                                            STRTAB_ALLOCATOR_SIZE,
+                                            sizeof(Ste));
+
+    smmu->cd_alloc = generic_alloc_init(MMIO_RAM_ADDR +
+                                        CD_ALLOCATOR_START,
+                                        CD_ALLOCATOR_SIZE,
+                                        sizeof(Cd));
+
+    smmu->pgtbl_alloc = generic_alloc_init(MMIO_RAM_ADDR +
+                                           PGTABLE_ALLOCATOR_START,
+                                           PGTABLE_ALLOCATOR_SIZE,
+                                           state->vm_cfg.page_size);
+
+    smmu_strtab_initone(state, state->tdev.dev->devfn);
+
+    return 0;
+}
+
+static int smmu_pgtable_alloc(SMMUTestState *s)
+{
+    SMMUDevState *smmu = &s->sdev;
+    uint64_t step = smmu_get_ste(smmu, s->tdev.dev->devfn);
+
+    return step ? 0 : 1;
+}
+
+static int smmu_init(SMMUTestState *s)
+{
+    SMMUDevState *smmu = &s->sdev;
+    int ret = 0;
+
+    ret = smmu_init_cmdq(s);
+    if (ret)
+        return ret;
+
+    ret = smmu_strtab_init(s);
+    if (ret)
+        return ret;
+
+    ret = smmu_pgtable_alloc(s);
+    if (ret)
+        return ret;
+
+    /* At last enable SMMU */
+    smmu_write_reg(smmu, SMMU_REG_CR0, 0x1);
+    return ret;
+}
+
+static void testdev_init(SMMUTestDevState *tdev)
+{
+    /* Nothing to be done at the moment */
+}
+
+static void smmu_setup(SMMUTestState *s)
+{
+    SMMUDevState *smmu = &s->sdev;
+    /* Write command queue base, enable command queue and issue cmd_sync */
+    /* This value is taken from hw/arm/virt.c, no portable way of getting it ? */
+    smmu->reg_base = (void*)0x09050000;
+}
+
+static void test_smmu_setup(SMMUTestState *state)
+{
+    SMMUTestVMCfg *vm_cfg = &state->vm_cfg;
+
+    qtest_add_abrt_handler(abort_handler, state);
+
+    setup_vm(state, true);
+
+    state->alloc = generic_alloc_init(vm_cfg->ram_base +
+                                      TEST_ALLOCATOR_START,
+                                      TEST_ALLOCATOR_SIZE,
+                                      vm_cfg->page_size);
+
+    smmu_setup(state);
+
+    testdev_setup(state);
+
+    if (smmu_init(state))
+        return;
+
+    testdev_init(&state->tdev);
+}
+
+static uint64_t
+alloc_pgtable(SMMUDevState *smmu, SMMUTransCfg *cfg, bool s2needed)
+{
+
+    SMMUTestState *state = container_of(smmu, SMMUTestState, sdev);
+    int stage = cfg->stage;
+    int level, granule_sz = cfg->granule_sz[stage];
+    int page_size = 1 << (cfg->granule_sz[stage] + 3);
+    //hwaddr pagesize;
+    hwaddr addr, mask, va = cfg->va, pa = cfg->pa;
+    uint64_t ttbr;
+
+    static const char *gap = "";
+
+    printf("%s %s stage:%d va_size:%d va:%lx pa:%lx\n", gap,
+           __func__, stage, cfg->va_size[stage], va, pa);
+
+    if (!cfg->ttbr[stage]) {
+        cfg->ttbr[stage] = guest_alloc(smmu->pgtbl_alloc, page_size);
+        if (!cfg->ttbr[stage]) {
+            printf("Unable to allocate guest memory for ttbr\n");
+            return 0;
+        }
+        qmemset(cfg->ttbr[stage], 0, page_size);
+    }
+
+    ttbr = cfg->ttbr[stage];
+
+    level = 4 - (cfg->va_size[stage] - cfg->tsz[stage] - 4) / cfg->granule_sz[stage];
+    mask = (1ULL << (cfg->granule_sz[stage] + 3)) - 1;
+
+    addr = extract64(ttbr, 0, 48);
+    printf("%sTTBR:%lx va:%lx\n", gap, addr, va);
+    addr &= ~((1ULL < (cfg->va_size[stage] - cfg->tsz[stage] -
+                       (granule_sz * (4 - level)))) - 1);
+    for (;;) {
+        uint64_t desc;
+        uint64_t ored = (va >> (granule_sz * (4 - level))) & mask;
+
+        printf("%sstage%d LEVEL:%d addr:%lx ored:%lx\n", gap, stage, level, addr, ored);
+
+        addr |= (va >> (granule_sz * (4 - level))) & mask;
+        addr &= ~0x7ULL;
+
+        qtest_memread(state->qtest, addr, &desc, sizeof(desc));
+
+        if ((level < 3) && !desc) {
+            desc = guest_alloc(smmu->pgtbl_alloc, page_size);
+            if (!desc) {
+                printf("Unable to allocate page table memory\n");
+                break;
+            }
+            printf("%snew pgtable level@%d: %lx\n", gap, level, desc);
+
+            qmemset(desc, 0, page_size); /* memset() */
+
+            desc |= 3;
+            qtest_memwrite(state->qtest, addr, &desc, sizeof(desc));
+        }
+
+        printf("%sLEVEL:%d gran_sz:%d mask:%lx addr:%lx desc:%lx\n",
+               gap, level, granule_sz, mask, addr, desc);
+
+        if (s2needed) {
+            SMMUTransCfg s2cfg = *cfg;
+            gap = "\t";
+            if (level < 3)
+                s2cfg.pa = s2cfg.va = desc & ~0x3UL;
+            else
+                s2cfg.pa = s2cfg.va = pa & ~mask;
+
+            s2cfg.stage = 2;
+            s2cfg.s2_needed = false;
+
+            alloc_pgtable(smmu, &s2cfg, false);
+
+            cfg->ttbr[2] = s2cfg.ttbr[2];
+
+            gap = "";
+        }
+
+        if ((level < 3)) {
+            //addr = desc & ~mask;
+            addr = desc & 0xfffffffff000ULL;
+            desc |= 0x2;
+            level++;
+            continue;
+        }
+
+        pa &= ~(page_size-1);
+        pa |= 3;
+
+        qtest_memwrite(state->qtest, addr, &pa, sizeof(pa));
+        qtest_memread(state->qtest, addr, &desc, sizeof(va));
+        assert(desc == pa);
+        printf("LEVEL:%d final written value @:%lx is :%lx page_size:%x\n",
+               level, addr, va, page_size);
+
+        break;
+    }
+
+    return ttbr;
+}
+
+static inline void dump_ste1(Ste *ste)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(ste->word); i += 2) {
+        printf("STE[%2d]: %#010x\t STE[%2d]: %#010x\n",
+               i, ste->word[i], i + 1, ste->word[i + 1]);
+    }
+}
+
+static inline void dump_cd1(Cd *cd)
+{
+    int i;
+    for (i = 0; i < ARRAY_SIZE(cd->word); i += 2) {
+        printf("CD[%2d]: %#010x\t CD[%2d]: %#010x\n",
+               i, cd->word[i], i + 1, cd->word[i + 1]);
+    }
+}
+
+/*
+ * This part is little complecated, we use the same page tables
+ * 1-1 mapped page table, the resulting addr is same as original addr,
+ * just that page table walk is done.
+ */
+static int
+update_pgtable(SMMUDevState *smmu, bool s1needed,
+               SMMUTestDevState *tdev, SMMUTransCfg *cfg, bool s2needed)
+{
+    uint64_t step = smmu_get_ste(smmu, tdev->dev->devfn);
+    SMMUTestState *state = container_of(smmu, SMMUTestState, sdev);
+    Cd cd = {0,};
+    Ste ste = {0,};
+    int stage = cfg->stage;
+
+    if (!step) {
+        printf("+++Could not find STE pointer\n");
+        return -1;
+    }
+    qtest_memread(state->qtest, step, &ste, sizeof(ste));
+    printf("===> step:%lx ste read config:%d stage:%d\n",
+           step, STE_CONFIG(&ste), stage);
+
+    if (s1needed) {           /* S1+S2 */
+        uint64_t cdp = STE_CTXPTR(&ste);
+
+        /* use single level CD pointer */
+        STE_SET_S1FMT(&ste, 0);
+
+        printf("==== setting up CD :%lx\n", cdp);
+        if (!cdp) {
+            cdp = guest_alloc(smmu->cd_alloc, sizeof(cd));
+            qmemset(cdp, 0, sizeof(cd));
+            printf("==== allocated cd:%lx\n", cdp);
+
+            CD_SET_EPD1(&cd, 1);
+            CD_SET_VALID(&cd, 1);
+
+            CD_SET_T0SZ(&cd, cfg->tsz[stage]);
+            CD_SET_TG0(&cd, cfg->granule[stage]);
+            CD_SET_IPS(&cd, cfg->oas[stage]);
+            CD_SET_AARCH64(&cd, 1);
+
+            STE_SET_CTXPTR(&ste, cdp);
+
+            STE_SET_CONFIG(&ste, STE_CONFIG(&ste) | 0x5);
+            printf("=== cdp:%lx cd[0]:%x\n", cdp, cd.word[0]);
+        } else
+            qtest_memread(state->qtest, cdp, &cd, sizeof(cd));
+
+        cfg->ttbr[stage] = CD_TTB0(&cd);
+        cfg->pa = cfg->va;              /* 1-1 mapping */
+        alloc_pgtable(smmu, cfg,  s2needed);
+
+        CD_SET_TTB0(&cd, cfg->ttbr[stage]);
+        dump_cd1(&cd);
+        qtest_memwrite(state->qtest, cdp, &cd, sizeof(cd));
+    }
+
+    STE_SET_EATS(&ste, 0x1);
+    dump_smmutranscfg(cfg);
+    /* most values are what Linux fills */
+    if (s2needed) {
+        stage = 2;
+        STE_SET_CONFIG(&ste, STE_CONFIG(&ste) | 0x6);
+
+        if (!s1needed) {                /* S2 only  */
+            cfg->ttbr[stage] = STE_S2TTB(&ste);
+            cfg->pa = cfg->va;          /* 1-1 mapping */
+            alloc_pgtable(smmu, cfg, false);
+        }
+
+        /* should we consider 16k case? then S2TG= 0x2 */
+        STE_SET_S2TG(&ste, (cfg->granule[stage])? 1: 0);
+        STE_SET_S2PS(&ste, 0x7);
+        STE_SET_S2S(&ste, 0x1);
+        STE_SET_S2AA64(&ste, 0x1);
+        STE_SET_S2T0SZ(&ste, cfg->tsz[stage]);
+        STE_SET_S2TTB(&ste, cfg->ttbr[stage]);
+    }
+
+    STE_SET_VALID(&ste, 0x1);
+
+    dump_ste1(&ste);
+
+    qtest_memwrite(state->qtest, step, &ste, sizeof(ste));
+
+    return 0;
+}
+
+static void __do_dma(SMMUTestState *s, SMMUTransCfg *cfg,
+                     bool s1needed, bool s2needed)
+{
+    int i;
+    uint16_t *src, *dst;
+    uint64_t g_src, g_dst;
+
+    if (s2needed && !s1needed)
+        cfg->stage = 2;
+    else
+        cfg->stage = 1;
+
+#define TST_BUFFER_SIZE 0x200
+    src = g_malloc(TST_BUFFER_SIZE);
+    dst = g_malloc(TST_BUFFER_SIZE);
+    g_src = guest_alloc(s->alloc, TST_BUFFER_SIZE);
+    g_dst = guest_alloc(s->alloc, TST_BUFFER_SIZE);
+
+    /* Fill array with integers */
+    for (i = 0; i < TST_BUFFER_SIZE/sizeof(uint16_t); i++) {
+        uint16_t *ptr = (uint16_t*)src + i;
+        *ptr = i;
+    }
+    qtest_memwrite(s->qtest, g_src, src, TST_BUFFER_SIZE);
+
+    /* Install tables for this device and src/dst addresses */
+
+    cfg->va = g_src;
+    update_pgtable(&s->sdev, s1needed, &s->tdev, cfg, s2needed);
+
+    cfg->va = g_dst;
+    update_pgtable(&s->sdev, s1needed, &s->tdev, cfg, s2needed);
+
+    /* Start dma */
+    testdev_dma(&s->tdev, (void*)g_src, (void*)g_dst,
+                TST_BUFFER_SIZE);
+
+    qtest_memread(s->qtest, g_dst, dst, TST_BUFFER_SIZE);
+    for (i = 0; i < TST_BUFFER_SIZE/sizeof(uint16_t); i++) {
+        if (src[i] != dst[i]) {
+            printf("No match off:%d src:%x dst:%x\n",
+                   i, src[i], dst[i]);
+            printf("\n=========TEST FAILED=============\n");
+            return;
+        }
+    }
+    printf("\n=========TEST PASSED=============\n");
+}
+
+static void __test_smmu(SMMUTransCfg *cfg,
+                        bool s1needed, bool s2needed)
+{
+    SMMUTestState state = {0,};
+    int stage = cfg->stage;
+
+    state.cfg = (SMMUTestCfg){
+        .sid_size = 16,
+        .sid_split = 8,
+        .cmdq_shift = 3,                /* q size, 8 deep */
+    };
+    SMMUTestState *s = &state;
+
+    s->vm_cfg.page_size = 1 << (cfg->granule_sz[stage] + 3);
+
+    test_smmu_setup(s);
+
+    __do_dma(s, cfg, s1needed, s2needed);
+
+    printf("state->qtest:%p global:%p\n", s->qtest, global_qtest);
+
+    test_smmu_cleanup(s);
+}
+
+static void __make_test(const char *pattern, const char *test,
+                        void (*testfn)(void))
+{
+    char *_test =  g_strdup_printf("%s%s%s", pattern,
+                                   !g_str_has_suffix(pattern, "/") ? "/": "",
+                                   test);
+    qtest_add_func(_test, testfn);
+    g_free(_test);
+}
+
+static void test_smmu_cmdq(void)
+{
+
+    /*
+     * First we setup command queue to be 64 entry deep and
+     * test if it wraps around correctly
+     */
+    /* Actually it does, tested with Linux driver */
+
+}
+
+const SMMUTransCfg cfg4k = {
+    .tsz = {0, 24, 24},
+    .granule = {0, 0, 0},
+    .va_size = {0, 64, 64},
+    .granule_sz = {0, 9, 9},
+};
+
+const SMMUTransCfg cfg64k = {
+    .tsz = {0, 16, 16},
+    .granule = {0, 1, 1},
+    .va_size = {0, 64, 64},
+    .granule_sz = {0, 13, 13},
+};
+
+const SMMUTransCfg cfgs14k_s264k = {
+    .tsz = {0, 24, 16},
+    .granule = {0, 0, 1},
+    .va_size = {0, 64, 64},
+    .granule_sz = {0, 9, 13},
+    .stage = 1,
+    .s2_needed = true,
+};
+
+const SMMUTransCfg cfgs164k_s24k = {
+    .tsz = {0, 16, 24},
+    .granule = {0, 1, 0},
+    .va_size = {0, 64, 64},
+    .granule_sz = {0, 13, 9},
+    .stage = 1,
+    .s2_needed = true,
+};
+
+
+static void test_smmu_s1_4k(void)
+{
+    SMMUTransCfg cfg;
+
+    memcpy(&cfg, &cfg4k, sizeof(cfg4k));
+
+    __test_smmu(&cfg, true, false);
+}
+
+static void test_smmu_s1_64k(void)
+{
+    SMMUTransCfg cfg;
+
+    memcpy(&cfg, &cfg64k, sizeof(cfg));
+
+    __test_smmu(&cfg, true, false);
+}
+
+static void test_smmu_s1s2_4k(void)
+{
+    SMMUTransCfg cfg;
+
+    memcpy(&cfg, &cfg4k, sizeof(cfg));
+    cfg.stage = 1;
+    cfg.s2_needed = true;
+    __test_smmu(&cfg, true, true);
+}
+
+static void test_smmu_s1s2_64k(void)
+{
+    SMMUTransCfg cfg;
+
+    memcpy(&cfg, &cfg64k, sizeof(cfg));
+
+    __test_smmu(&cfg, true, true);
+
+}
+
+static void test_smmu_s14k_s264k(void)
+{
+    SMMUTransCfg cfg;
+
+    memcpy(&cfg, &cfgs14k_s264k, sizeof(cfg));
+
+    __test_smmu(&cfg, true, true);
+}
+
+static void test_smmu_s164k_s24k(void)
+{
+    SMMUTransCfg cfg;
+
+    memcpy(&cfg, &cfgs164k_s24k, sizeof(cfg));
+
+    __test_smmu(&cfg, true, true);
+}
+
+static void test_smmu_s2_4k(void)
+{
+    SMMUTransCfg cfg;
+
+    memcpy(&cfg, &cfg4k, sizeof(cfg));
+    cfg.stage = 2;
+
+    __test_smmu(&cfg, false, true);
+
+}
+
+static void test_smmu_s2_64k(void)
+{
+    SMMUTransCfg cfg;
+
+    memcpy(&cfg, &cfg64k, sizeof(cfg));
+    cfg.stage = 2;
+
+    __test_smmu(&cfg, false, true);
+}
+
+struct test_matrix {
+    const char *pattern;
+    const char *test;
+    void (*testfn)(void);
+} test_matrix[] = {
+    {"/smmuv3/init", "cmdq", test_smmu_cmdq},
+    {"/smmuv3/tt/s1", "4k", test_smmu_s1_4k},
+    {"/smmuv3/tt/s1", "64k", test_smmu_s1_64k},
+    {"/smmuv3/tt/s1s2", "4k", test_smmu_s1s2_4k},
+    {"/smmuv3/tt/s1s2", "64k", test_smmu_s1s2_64k},
+    {"/smmuv3/tt/s1s2", "4k/64k", test_smmu_s14k_s264k},
+    {"/smmuv3/tt/s1s2", "64k/4k", test_smmu_s164k_s24k},
+    {"/smmuv3/tt/s2", "4k", test_smmu_s2_4k},
+    {"/smmuv3/tt/s2", "64k", test_smmu_s2_64k},
+};
+
+int main(int argc, char **argv)
+{
+    int ret = 0, i;
+
+    if (strcmp(qtest_get_arch(), "aarch64") != 0) {
+        return 0;
+    }
+
+    /* Run the tests */
+    g_test_init(&argc, &argv, NULL);
+
+    for (i = 0; i < ARRAY_SIZE(test_matrix); i++) {
+        struct test_matrix *entry = &test_matrix[i];
+        __make_test(entry->pattern, entry->test, entry->testfn);
+    }
+
+    ret = g_test_run();
+
+    return ret;
+}
+
-- 
2.9.3

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

* [Qemu-devel] [PATCH v2 9/9] [optional] arm: smmu-v3: ACPI IORT initial support
  2016-08-22 16:17 [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Prem Mallappa
                   ` (7 preceding siblings ...)
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 8/9] [optional] tests: SMMUv3 unit tests Prem Mallappa
@ 2016-08-22 16:17 ` Prem Mallappa
  2016-09-09 15:24   ` Auger Eric
  2016-08-31 21:44 ` [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Auger Eric
  2017-03-08 17:46 ` Auger Eric
  10 siblings, 1 reply; 34+ messages in thread
From: Prem Mallappa @ 2016-08-22 16:17 UTC (permalink / raw)
  To: Peter Maydell, Edgar E . Iglesias, qemu-devel; +Cc: Prem Mallappa

Added ACPI IORT tables, was needed for internal project purpose, but
posting here for anyone looking for testing ACPI on ARM platforms.
(P.S: Linux side IORT patches are WIP)

Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
---
 hw/arm/virt-acpi-build.c    | 43 +++++++++++++++++++++++
 include/hw/acpi/acpi-defs.h | 84 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 127 insertions(+)

diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 1fa0581..d5fb69e 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -382,6 +382,45 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset)
     return rsdp_table;
 }
 
+/*
+ * TODO: Simple IORT for now, will add ID mappings as we go
+ * basic idea is to instantiate SMMU from ACPI
+ */
+static void
+build_iort(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
+{
+    int iort_start = table_data->len;
+    AcpiIortTable *iort;
+    AcpiIortNode *iort_node;
+    AcpiIortSmmu3 *smmu;
+    AcpiIortRC *rc;
+    const MemMapEntry *memmap = guest_info->memmap;
+
+    iort = acpi_data_push(table_data, sizeof(*iort));
+
+    iort->length = sizeof(*iort);
+    iort->node_offset = table_data->len - iort_start;
+    iort->num_nodes++;
+
+    smmu = acpi_data_push(table_data, sizeof(*smmu));
+    iort_node = &smmu->iort_node;
+    iort_node->type = 0x04;          /* SMMUv3 */
+    iort_node->length = sizeof(*smmu);
+    smmu->base_addr = cpu_to_le64(memmap[VIRT_SMMU].base);
+
+    iort->num_nodes++;
+
+    rc = acpi_data_push(table_data, sizeof(*rc));
+    iort_node = &rc->iort_node;
+    iort_node->type = 0x02;          /* RC */
+    iort_node->length = sizeof(*rc);
+    rc->ats_attr = 1;
+    rc->pci_seg_num = 0;
+
+    build_header(linker, table_data, (void *)(table_data->data + iort_start),
+                 "IORT", table_data->len - iort_start, 0, NULL, NULL);
+}
+
 static void
 build_spcr(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
 {
@@ -667,6 +706,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
      * MADT
      * MCFG
      * DSDT
+     * IORT = ACPI 6.0
      */
 
     /* DSDT is pointed to by FADT */
@@ -694,6 +734,9 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
         build_srat(tables_blob, tables->linker, guest_info);
     }
 
+    acpi_add_table(table_offsets, tables_blob);
+    build_iort(tables_blob, tables->linker, guest_info);
+
     /* RSDT is pointed to by RSDP */
     rsdt = tables_blob->len;
     build_rsdt(tables_blob, tables->linker, table_offsets, NULL, NULL);
diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h
index 850a962..d60f390 100644
--- a/include/hw/acpi/acpi-defs.h
+++ b/include/hw/acpi/acpi-defs.h
@@ -259,6 +259,90 @@ typedef struct AcpiFacsDescriptorRev1 AcpiFacsDescriptorRev1;
  */
 
 /*
+ * IORT Table
+ */
+struct AcpiIortTable
+{
+    ACPI_TABLE_HEADER_DEF     /* ACPI common table header */
+    uint32_t num_nodes;
+    uint32_t node_offset;
+    uint32_t reserved;
+} QEMU_PACKED;
+typedef struct AcpiIortTable AcpiIortTable;
+
+struct AcpiIortIdMapping
+{
+    uint32_t input_base;
+    uint32_t num_ids;
+    uint32_t output_base;
+    uint32_t output_ref;
+    uint32_t flags;
+} QEMU_PACKED;
+typedef struct AcpiIortIdMapping AcpiIortIdMapping;
+
+struct AcpiIortNode
+{
+    uint8_t  type;
+    uint16_t length;
+    uint8_t  revision;
+    uint32_t reserved1;
+    uint32_t num_id_maps;
+    uint32_t id_array_offset;
+} QEMU_PACKED;
+typedef struct AcpiIortNode AcpiIortNode;
+
+struct AcpiIortSmmu2
+{
+    AcpiIortNode iort_node;
+    uint64_t base_addr;
+    uint64_t span;
+    uint32_t model;
+    uint32_t flags;
+    uint32_t gbl_intr_array_off;
+    uint32_t ctx_intr_cnt;
+    uint32_t ctx_intr_array_off;
+    uint32_t pmr_intr_cnt;
+    uint32_t pmr_intr_array_off;
+
+    // Global interrupt array
+    uint32_t gintr;
+    uint32_t gintr_flags;
+    uint32_t gcfgintr;
+    uint32_t gcfgintr_flags;
+
+    //AcpiIortIdMapping id_mapping_array[0];
+} QEMU_PACKED;
+typedef struct AcpiIortSmmu2 AcpiIortSmmu2;
+
+struct AcpiIortSmmu3
+{
+    AcpiIortNode iort_node;
+    uint64_t base_addr;
+    uint32_t flags;
+    uint32_t reserved2;
+    uint64_t vatos_addr;
+    uint32_t model;
+    uint32_t event_irq;
+    uint32_t pri_irq;
+    uint32_t gerr_irq;
+    uint32_t sync_irq;
+
+    //AcpiIortIdMapping id_mapping_array[0];
+} QEMU_PACKED;
+typedef struct AcpiIortSmmu3 AcpiIortSmmu3;
+
+struct AcpiIortRC
+{
+    AcpiIortNode iort_node;
+    uint64_t mem_access_prop;
+    uint32_t ats_attr;
+    uint32_t pci_seg_num;
+
+    AcpiIortIdMapping id_mapping_array[0];
+} QEMU_PACKED;
+typedef struct AcpiIortRC AcpiIortRC;
+
+/*
  * MADT values and structures
  */
 
-- 
2.9.3

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

* Re: [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support
  2016-08-22 16:17 [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Prem Mallappa
                   ` (8 preceding siblings ...)
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 9/9] [optional] arm: smmu-v3: ACPI IORT initial support Prem Mallappa
@ 2016-08-31 21:44 ` Auger Eric
  2016-09-01  5:24   ` Prem Mallappa
  2017-03-08 17:46 ` Auger Eric
  10 siblings, 1 reply; 34+ messages in thread
From: Auger Eric @ 2016-08-31 21:44 UTC (permalink / raw)
  To: Prem Mallappa, Peter Maydell, Edgar E . Iglesias, qemu-devel

Hi Prem,
On 22/08/2016 18:17, Prem Mallappa wrote:
> v1 -> v2:
> 	- Adopted review comments from Eric Auger
Although I am really interested in your series, those comments are not
mine and credit should be given to somebody else (Edgar?)

I will do my utmost to review it too ;-)

Thanks

Eric
> 		- Make SMMU_DPRINTF to internally call qemu_log
> 	            (since translation requests are too many, we need control
> 		     on the type of log we want)
> 		- SMMUTransCfg modified to suite simplicity
> 		- Change RegInfo to uint64 register array
> 		- Code cleanup
> 		- Test cleanups
> 	- Reshuffled patches
> 
> RFC -> v1:
> 	- As per SMMUv3 spec 16.0 (only is_ste_consistant() is noticeable)
> 	- Reworked register access/update logic
> 	- Factored out translation code for
> 		- single point bug fix
> 		- sharing/removal in future
> 	- (optional) Unit tests added, with PCI test device
> 		- S1 with 4k/64k, S1+S2 with 4k/64k
> 		- (S1 or S2) only can be verified by Linux 4.7 driver
> 	- (optional) Priliminary ACPI support
> 
> RFC:
> 	- Implements SMMUv3 spec 11.0
> 	- Supported for PCIe devices, 
> 	- Command Queue and Event Queue supported
> 	- LPAE only, S1 is supported and Tested, S2 not tested
> 	- BE mode Translation not supported
> 	- IRQ support (legacy, no MSI)
> 	- Tested with DPDK and e1000 
> 
> Patch 1: Add new log type for IOMMU transactions
> 
> Patch 2: Adds support in virt.c to create both SMMUv3 device and dts entries
> 
> Patch 2: Adds SMMUv3 model to QEMU
> 	Multiple files, big ones, translate functionality is split across to
> 	accomodate SMMUv2 model, and to remove when common translation feature
> 	(if) becomes available.
> 
> Patch 3: Adds SMMU build support
> 
> Patch 4: Some devicetree function to add support for SMMU's multiple interrupt
> 	 assignment with names
> 
> << optional patches >>
> Optional patches are posted for completeness or for those who wants to test.
> 
> Patch 5: A simple PCI device which does DMA from 'src' to 'dst' given
> 	 src_addr, dst_addr and size, and is used by unit test, uses
> 	 pci_dma_read and pci_dma_write in a crude way but serves the purpose.
> 
> Patch 6: Current libqos PCI helpers are x86 only, this addes a generic interface
> 
> Patch 7: Unit tests for SMMU, 
> 		- initializes SMMU device 
> 		- initializes Test device
> 		- allocates page tables 1:1 mapping va == pa
> 		- allocates STE/CD accordingly for S1, S2, S1+S2
> 		- initiates DMA via PCI test device
> 		- verifies transfered data
> 
> Patch 8: Added ACPI IORT tables, was needed for internal project purpose, but 
> 	 posting here for anyone looking for testing ACPI on ARM platforms.
> 	 (P.S: Linux side IORT patches are WIP)
> 
> Repo:
> https://github.com/pmallappa/qemu/tree/upstream/smmuv3/v2
> 
> To Test:
> $ make tests/smmuv3-test
> $ QTEST_QEMU_BINARY=aarch64-softmmu/qemu-system-aarch64 tests/smmuv3-test
> << expect lot of prints >>
> 
> Any comments welcome..
> 
> Cheers
> /Prem
> 
> Prem Mallappa (9):
>   log: Add new IOMMU type
>   devicetree: Added new APIs to make use of more fdt functions
>   hw: arm: SMMUv3 emulation model
>   hw: arm: Added SMMUv3 files for build
>   hw: arm: Add SMMUv3 to virt platform, create DTS accordingly
>   [optional] hw: misc: added testdev for smmu
>   [optional] tests: libqos: generic pci probing helpers
>   [optional] tests: SMMUv3 unit tests
>   [optional] arm: smmu-v3: ACPI IORT initial support
> 
>  default-configs/aarch64-softmmu.mak |    1 +
>  device_tree.c                       |   35 +
>  hw/arm/Makefile.objs                |    1 +
>  hw/arm/smmu-common.c                |  152 ++++
>  hw/arm/smmu-common.h                |  141 ++++
>  hw/arm/smmu-v3.c                    | 1369 +++++++++++++++++++++++++++++++++++
>  hw/arm/smmuv3-internal.h            |  432 +++++++++++
>  hw/arm/virt-acpi-build.c            |   43 ++
>  hw/arm/virt.c                       |   62 ++
>  hw/misc/Makefile.objs               |    2 +-
>  hw/misc/pci-testdev-smmu.c          |  239 ++++++
>  hw/misc/pci-testdev-smmu.h          |   22 +
>  hw/vfio/common.c                    |    2 +-
>  include/hw/acpi/acpi-defs.h         |   84 +++
>  include/hw/arm/smmu.h               |   33 +
>  include/hw/arm/virt.h               |    2 +
>  include/qemu/log.h                  |    1 +
>  include/sysemu/device_tree.h        |   18 +
>  tests/Makefile.include              |    4 +
>  tests/libqos/pci-generic.c          |  197 +++++
>  tests/libqos/pci-generic.h          |   58 ++
>  tests/smmuv3-test.c                 |  952 ++++++++++++++++++++++++
>  util/log.c                          |    2 +
>  23 files changed, 3850 insertions(+), 2 deletions(-)
>  create mode 100644 hw/arm/smmu-common.c
>  create mode 100644 hw/arm/smmu-common.h
>  create mode 100644 hw/arm/smmu-v3.c
>  create mode 100644 hw/arm/smmuv3-internal.h
>  create mode 100644 hw/misc/pci-testdev-smmu.c
>  create mode 100644 hw/misc/pci-testdev-smmu.h
>  create mode 100644 include/hw/arm/smmu.h
>  create mode 100644 tests/libqos/pci-generic.c
>  create mode 100644 tests/libqos/pci-generic.h
>  create mode 100644 tests/smmuv3-test.c
> 

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

* Re: [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support
  2016-08-31 21:44 ` [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Auger Eric
@ 2016-09-01  5:24   ` Prem Mallappa
  0 siblings, 0 replies; 34+ messages in thread
From: Prem Mallappa @ 2016-09-01  5:24 UTC (permalink / raw)
  To: Auger Eric; +Cc: Peter Maydell, Edgar E . Iglesias, QEMU Developers

Oops, my mistake, copy-paste from different part.
I'll correct it in next spin.

Eric, you are most welcome to review though :)



On Thu, Sep 1, 2016 at 3:14 AM, Auger Eric <eric.auger@redhat.com> wrote:

> Hi Prem,
> On 22/08/2016 18:17, Prem Mallappa wrote:
> > v1 -> v2:
> >       - Adopted review comments from Eric Auger
> Although I am really interested in your series, those comments are not
> mine and credit should be given to somebody else (Edgar?)
>
> I will do my utmost to review it too ;-)
>
> Thanks
>
> Eric
> >               - Make SMMU_DPRINTF to internally call qemu_log
> >                   (since translation requests are too many, we need
> control
> >                    on the type of log we want)
> >               - SMMUTransCfg modified to suite simplicity
> >               - Change RegInfo to uint64 register array
> >               - Code cleanup
> >               - Test cleanups
> >       - Reshuffled patches
> >
> > RFC -> v1:
> >       - As per SMMUv3 spec 16.0 (only is_ste_consistant() is noticeable)
> >       - Reworked register access/update logic
> >       - Factored out translation code for
> >               - single point bug fix
> >               - sharing/removal in future
> >       - (optional) Unit tests added, with PCI test device
> >               - S1 with 4k/64k, S1+S2 with 4k/64k
> >               - (S1 or S2) only can be verified by Linux 4.7 driver
> >       - (optional) Priliminary ACPI support
> >
> > RFC:
> >       - Implements SMMUv3 spec 11.0
> >       - Supported for PCIe devices,
> >       - Command Queue and Event Queue supported
> >       - LPAE only, S1 is supported and Tested, S2 not tested
> >       - BE mode Translation not supported
> >       - IRQ support (legacy, no MSI)
> >       - Tested with DPDK and e1000
> >
> > Patch 1: Add new log type for IOMMU transactions
> >
> > Patch 2: Adds support in virt.c to create both SMMUv3 device and dts
> entries
> >
> > Patch 2: Adds SMMUv3 model to QEMU
> >       Multiple files, big ones, translate functionality is split across
> to
> >       accomodate SMMUv2 model, and to remove when common translation
> feature
> >       (if) becomes available.
> >
> > Patch 3: Adds SMMU build support
> >
> > Patch 4: Some devicetree function to add support for SMMU's multiple
> interrupt
> >        assignment with names
> >
> > << optional patches >>
> > Optional patches are posted for completeness or for those who wants to
> test.
> >
> > Patch 5: A simple PCI device which does DMA from 'src' to 'dst' given
> >        src_addr, dst_addr and size, and is used by unit test, uses
> >        pci_dma_read and pci_dma_write in a crude way but serves the
> purpose.
> >
> > Patch 6: Current libqos PCI helpers are x86 only, this addes a generic
> interface
> >
> > Patch 7: Unit tests for SMMU,
> >               - initializes SMMU device
> >               - initializes Test device
> >               - allocates page tables 1:1 mapping va == pa
> >               - allocates STE/CD accordingly for S1, S2, S1+S2
> >               - initiates DMA via PCI test device
> >               - verifies transfered data
> >
> > Patch 8: Added ACPI IORT tables, was needed for internal project
> purpose, but
> >        posting here for anyone looking for testing ACPI on ARM platforms.
> >        (P.S: Linux side IORT patches are WIP)
> >
> > Repo:
> > https://github.com/pmallappa/qemu/tree/upstream/smmuv3/v2
> >
> > To Test:
> > $ make tests/smmuv3-test
> > $ QTEST_QEMU_BINARY=aarch64-softmmu/qemu-system-aarch64
> tests/smmuv3-test
> > << expect lot of prints >>
> >
> > Any comments welcome..
> >
> > Cheers
> > /Prem
> >
> > Prem Mallappa (9):
> >   log: Add new IOMMU type
> >   devicetree: Added new APIs to make use of more fdt functions
> >   hw: arm: SMMUv3 emulation model
> >   hw: arm: Added SMMUv3 files for build
> >   hw: arm: Add SMMUv3 to virt platform, create DTS accordingly
> >   [optional] hw: misc: added testdev for smmu
> >   [optional] tests: libqos: generic pci probing helpers
> >   [optional] tests: SMMUv3 unit tests
> >   [optional] arm: smmu-v3: ACPI IORT initial support
> >
> >  default-configs/aarch64-softmmu.mak |    1 +
> >  device_tree.c                       |   35 +
> >  hw/arm/Makefile.objs                |    1 +
> >  hw/arm/smmu-common.c                |  152 ++++
> >  hw/arm/smmu-common.h                |  141 ++++
> >  hw/arm/smmu-v3.c                    | 1369
> +++++++++++++++++++++++++++++++++++
> >  hw/arm/smmuv3-internal.h            |  432 +++++++++++
> >  hw/arm/virt-acpi-build.c            |   43 ++
> >  hw/arm/virt.c                       |   62 ++
> >  hw/misc/Makefile.objs               |    2 +-
> >  hw/misc/pci-testdev-smmu.c          |  239 ++++++
> >  hw/misc/pci-testdev-smmu.h          |   22 +
> >  hw/vfio/common.c                    |    2 +-
> >  include/hw/acpi/acpi-defs.h         |   84 +++
> >  include/hw/arm/smmu.h               |   33 +
> >  include/hw/arm/virt.h               |    2 +
> >  include/qemu/log.h                  |    1 +
> >  include/sysemu/device_tree.h        |   18 +
> >  tests/Makefile.include              |    4 +
> >  tests/libqos/pci-generic.c          |  197 +++++
> >  tests/libqos/pci-generic.h          |   58 ++
> >  tests/smmuv3-test.c                 |  952 ++++++++++++++++++++++++
> >  util/log.c                          |    2 +
> >  23 files changed, 3850 insertions(+), 2 deletions(-)
> >  create mode 100644 hw/arm/smmu-common.c
> >  create mode 100644 hw/arm/smmu-common.h
> >  create mode 100644 hw/arm/smmu-v3.c
> >  create mode 100644 hw/arm/smmuv3-internal.h
> >  create mode 100644 hw/misc/pci-testdev-smmu.c
> >  create mode 100644 hw/misc/pci-testdev-smmu.h
> >  create mode 100644 include/hw/arm/smmu.h
> >  create mode 100644 tests/libqos/pci-generic.c
> >  create mode 100644 tests/libqos/pci-generic.h
> >  create mode 100644 tests/smmuv3-test.c
> >
>



-- 
Cheers,
/Prem

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

* Re: [Qemu-devel] [PATCH v2 9/9] [optional] arm: smmu-v3: ACPI IORT initial support
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 9/9] [optional] arm: smmu-v3: ACPI IORT initial support Prem Mallappa
@ 2016-09-09 15:24   ` Auger Eric
  2016-09-12 20:42     ` Prem Mallappa
  0 siblings, 1 reply; 34+ messages in thread
From: Auger Eric @ 2016-09-09 15:24 UTC (permalink / raw)
  To: Prem Mallappa, Peter Maydell, Edgar E . Iglesias, qemu-devel

Hi Prem,

On 22/08/2016 18:17, Prem Mallappa wrote:
> Added ACPI IORT tables, was needed for internal project purpose, but
> posting here for anyone looking for testing ACPI on ARM platforms.
> (P.S: Linux side IORT patches are WIP)
I am also interested in IORT ITS group and currently prototyping
something, hence my few comments/questions.
> 
> Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
> ---
>  hw/arm/virt-acpi-build.c    | 43 +++++++++++++++++++++++
>  include/hw/acpi/acpi-defs.h | 84 +++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 127 insertions(+)
> 
> diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
> index 1fa0581..d5fb69e 100644
> --- a/hw/arm/virt-acpi-build.c
> +++ b/hw/arm/virt-acpi-build.c
> @@ -382,6 +382,45 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset)
>      return rsdp_table;
>  }
>  
> +/*
> + * TODO: Simple IORT for now, will add ID mappings as we go
> + * basic idea is to instantiate SMMU from ACPI
> + */
> +static void
> +build_iort(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
> +{
> +    int iort_start = table_data->len;
> +    AcpiIortTable *iort;
> +    AcpiIortNode *iort_node;
> +    AcpiIortSmmu3 *smmu;
> +    AcpiIortRC *rc;
> +    const MemMapEntry *memmap = guest_info->memmap;
> +
> +    iort = acpi_data_push(table_data, sizeof(*iort));
> +
> +    iort->length = sizeof(*iort);
Isn't is supposed to be the length of the whole IORT (including the node
cumulated sizes?)
> +    iort->node_offset = table_data->len - iort_start;
> +    iort->num_nodes++;
> +
> +    smmu = acpi_data_push(table_data, sizeof(*smmu));
> +    iort_node = &smmu->iort_node;
> +    iort_node->type = 0x04;          /* SMMUv3 */
To match existing code (include/hw/acpi/acpi-defs.h), maybe enum values
can be created (ACPI_IORT_NODE_SMMU_V3). This also matches kernel enum.

More generally Shannon advised to use the same field names as the ones
used in the kernel header: acpi_iort_node_type in include/acpi/actbl2.h
> +    iort_node->length = sizeof(*smmu);
> +    smmu->base_addr = cpu_to_le64(memmap[VIRT_SMMU].base);
> +
> +    iort->num_nodes++;
> +
> +    rc = acpi_data_push(table_data, sizeof(*rc));
> +    iort_node = &rc->iort_node;
> +    iort_node->type = 0x02;          /* RC */
> +    iort_node->length = sizeof(*rc);
I think the mem_access_prop field should be set to 1 now the host
controller is assumed to be cache coherent.
> +    rc->ats_attr = 1;
no ATS support instead?
> +    rc->pci_seg_num = 0;
ID mappings are mandated for me to support MSIs with ITS.
> +
> +    build_header(linker, table_data, (void *)(table_data->data + iort_start),
> +                 "IORT", table_data->len - iort_start, 0, NULL, NULL);
> +}
> +
>  static void
>  build_spcr(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
>  {
> @@ -667,6 +706,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
>       * MADT
>       * MCFG
>       * DSDT
> +     * IORT = ACPI 6.0
>       */
>  
>      /* DSDT is pointed to by FADT */
> @@ -694,6 +734,9 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
>          build_srat(tables_blob, tables->linker, guest_info);
>      }
>  
> +    acpi_add_table(table_offsets, tables_blob);
> +    build_iort(tables_blob, tables->linker, guest_info);
> +
>      /* RSDT is pointed to by RSDP */
>      rsdt = tables_blob->len;
>      build_rsdt(tables_blob, tables->linker, table_offsets, NULL, NULL);
> diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h
> index 850a962..d60f390 100644
> --- a/include/hw/acpi/acpi-defs.h
> +++ b/include/hw/acpi/acpi-defs.h
> @@ -259,6 +259,90 @@ typedef struct AcpiFacsDescriptorRev1 AcpiFacsDescriptorRev1;
>   */
>  
>  /*
> + * IORT Table
> + */
> +struct AcpiIortTable
> +{
> +    ACPI_TABLE_HEADER_DEF     /* ACPI common table header */
> +    uint32_t num_nodes;
> +    uint32_t node_offset;
> +    uint32_t reserved;
> +} QEMU_PACKED;
> +typedef struct AcpiIortTable AcpiIortTable;
> +
> +struct AcpiIortIdMapping
> +{
> +    uint32_t input_base;
> +    uint32_t num_ids;
> +    uint32_t output_base;
> +    uint32_t output_ref;
> +    uint32_t flags;
> +} QEMU_PACKED;
> +typedef struct AcpiIortIdMapping AcpiIortIdMapping;
Shannon told me we should match the kernel datatypes & fields

for instance in include/acpi/actbl2.h we have:

struct acpi_iort_id_mapping {
        u32 input_base;         /* Lowest value in input range */
        u32 id_count;           /* Number of IDs */
        u32 output_base;        /* Lowest value in output range */
        u32 output_reference;   /* A reference to the output node */
        u32 flags;
};

This also holds for other struct definitions.

Thanks!

Eric

> +
> +struct AcpiIortNode
> +{
> +    uint8_t  type;
> +    uint16_t length;
> +    uint8_t  revision;
> +    uint32_t reserved1;
> +    uint32_t num_id_maps;
> +    uint32_t id_array_offset;
> +} QEMU_PACKED;
> +typedef struct AcpiIortNode AcpiIortNode;
> +
> +struct AcpiIortSmmu2
> +{
> +    AcpiIortNode iort_node;
> +    uint64_t base_addr;
> +    uint64_t span;
> +    uint32_t model;
> +    uint32_t flags;
> +    uint32_t gbl_intr_array_off;
> +    uint32_t ctx_intr_cnt;
> +    uint32_t ctx_intr_array_off;
> +    uint32_t pmr_intr_cnt;
> +    uint32_t pmr_intr_array_off;
> +
> +    // Global interrupt array
> +    uint32_t gintr;
> +    uint32_t gintr_flags;
> +    uint32_t gcfgintr;
> +    uint32_t gcfgintr_flags;
> +
> +    //AcpiIortIdMapping id_mapping_array[0];
> +} QEMU_PACKED;
> +typedef struct AcpiIortSmmu2 AcpiIortSmmu2;
> +
> +struct AcpiIortSmmu3
> +{
> +    AcpiIortNode iort_node;
> +    uint64_t base_addr;
> +    uint32_t flags;
> +    uint32_t reserved2;
> +    uint64_t vatos_addr;
> +    uint32_t model;
> +    uint32_t event_irq;
> +    uint32_t pri_irq;
> +    uint32_t gerr_irq;
> +    uint32_t sync_irq;
> +
> +    //AcpiIortIdMapping id_mapping_array[0];
> +} QEMU_PACKED;
> +typedef struct AcpiIortSmmu3 AcpiIortSmmu3;
> +
> +struct AcpiIortRC
> +{
> +    AcpiIortNode iort_node;
> +    uint64_t mem_access_prop;
> +    uint32_t ats_attr;
> +    uint32_t pci_seg_num;
> +
> +    AcpiIortIdMapping id_mapping_array[0];
> +} QEMU_PACKED;
> +typedef struct AcpiIortRC AcpiIortRC;
> +
> +/*
>   * MADT values and structures
>   */
>  
> 

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

* Re: [Qemu-devel] [PATCH v2 1/9] log: Add new IOMMU type
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 1/9] log: Add new IOMMU type Prem Mallappa
@ 2016-09-09 15:36   ` Auger Eric
  2016-09-12 20:23     ` Prem Mallappa
  0 siblings, 1 reply; 34+ messages in thread
From: Auger Eric @ 2016-09-09 15:36 UTC (permalink / raw)
  To: Prem Mallappa, Peter Maydell, Edgar E . Iglesias, qemu-devel

Hi Prem,

Missing commit message

> Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
> ---
>  include/qemu/log.h | 1 +
>  util/log.c         | 2 ++
>  2 files changed, 3 insertions(+)
> 
> diff --git a/include/qemu/log.h b/include/qemu/log.h
> index 234fa81..3dd2131 100644
> --- a/include/qemu/log.h
> +++ b/include/qemu/log.h
> @@ -42,6 +42,7 @@ static inline bool qemu_log_separate(void)
>  #define CPU_LOG_TB_NOCHAIN (1 << 13)
>  #define CPU_LOG_PAGE       (1 << 14)
>  #define LOG_TRACE          (1 << 15)
> +#define CPU_LOG_IOMMU      (1 << 16)
why is it prefixed with CPU_ ?
besides all arm gic devices seem to use LOG_GUEST_ERROR. what is the
rationale behind introducing a new enum?

Thanks

Eric
>  
>  /* Returns true if a bit is set in the current loglevel mask
>   */
> diff --git a/util/log.c b/util/log.c
> index 5ad72c1..62c4378 100644
> --- a/util/log.c
> +++ b/util/log.c
> @@ -241,6 +241,8 @@ const QEMULogItem qemu_log_items[] = {
>        "show CPU registers before entering a TB (lots of logs)" },
>      { CPU_LOG_MMU, "mmu",
>        "log MMU-related activities" },
> +    { CPU_LOG_IOMMU, "iommu",
> +      "log IOMMU-related activities" },
>      { CPU_LOG_PCALL, "pcall",
>        "x86 only: show protected mode far calls/returns/exceptions" },
>      { CPU_LOG_RESET, "cpu_reset",
> 

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

* Re: [Qemu-devel] [PATCH v2 2/9] devicetree: Added new APIs to make use of more fdt functions
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 2/9] devicetree: Added new APIs to make use of more fdt functions Prem Mallappa
@ 2016-09-09 16:02   ` Auger Eric
  2016-09-12 20:21     ` Prem Mallappa
  0 siblings, 1 reply; 34+ messages in thread
From: Auger Eric @ 2016-09-09 16:02 UTC (permalink / raw)
  To: Prem Mallappa, Peter Maydell, Edgar E . Iglesias, qemu-devel

Hi Prem,

> SMMUv3 needs device tree entry like below
To me the commit message should be more explicit and mention appendprop
functionality
> 
>      interrupt-names = "gerror", "priq", "eventq", "cmdq-sync";
> 
> This patch introduces helper function to add entries like above
> 
> Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
> ---
>  device_tree.c                | 35 +++++++++++++++++++++++++++++++++++
>  include/sysemu/device_tree.h | 18 ++++++++++++++++++
>  2 files changed, 53 insertions(+)
> 
> diff --git a/device_tree.c b/device_tree.c
> index 6e06320..5d5966e 100644
> --- a/device_tree.c
> +++ b/device_tree.c
> @@ -297,6 +297,24 @@ int qemu_fdt_setprop(void *fdt, const char *node_path,
>      return r;
>  }
>  
> +int qemu_fdt_appendprop(void *fdt, const char *node_path,
> +                     const char *property, const void *val, int size)
> +{
> +    int r;
> +
> +    r = fdt_appendprop(fdt, findnode_nofail(fdt, node_path), property,
> +                       val, size);
> +    if (r < 0) {
> +        error_report("%s: Couldn't set %s/%s: %s", __func__, node_path,
> +                     property, fdt_strerror(r));
> +        exit(1);
> +    }
> +
> +    return r;
> +}
> +
> +
spare void lines
> +
>  int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
>                            const char *property, uint32_t val)
>  {
> @@ -319,6 +337,23 @@ int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
>      return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val));
>  }
>  
> +int qemu_fdt_appendprop_string(void *fdt, const char *node_path,
> +                            const char *property, const char *string)
> +{
> +    int r;
> +
> +    r = fdt_appendprop_string(fdt, findnode_nofail(fdt, node_path),
> +                              property, string);
> +    if (r < 0) {
> +        error_report("%s: Couldn't set %s/%s = %s: %s", __func__,
> +                     node_path, property, string, fdt_strerror(r));
> +        exit(1);
> +    }
> +
> +    return r;
> +}
> +
same
> +
>  int qemu_fdt_setprop_string(void *fdt, const char *node_path,
>                              const char *property, const char *string)
>  {
> diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h
> index 705650a..5a0a297 100644
> --- a/include/sysemu/device_tree.h
> +++ b/include/sysemu/device_tree.h
> @@ -45,12 +45,16 @@ char **qemu_fdt_node_path(void *fdt, const char *name, char *compat,
>  
>  int qemu_fdt_setprop(void *fdt, const char *node_path,
>                       const char *property, const void *val, int size);
> +int qemu_fdt_appendprop(void *fdt, const char *node_path,
> +                     const char *property, const void *val, int size);
>  int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
>                            const char *property, uint32_t val);
>  int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
>                           const char *property, uint64_t val);
>  int qemu_fdt_setprop_string(void *fdt, const char *node_path,
>                              const char *property, const char *string);
> +int qemu_fdt_appendprop_string(void *fdt, const char *node_path,
> +                               const char *property, const char *string);
>  int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
>                               const char *property,
>                               const char *target_node_path);
> @@ -98,6 +102,20 @@ int qemu_fdt_add_subnode(void *fdt, const char *name);
>                           sizeof(qdt_tmp));                                    \
>      } while (0)
>  
> +
same
> +#define qemu_fdt_appendprop_cells(fdt, node_path, property, ...)              \
> +    do {                                                                      \
> +        uint32_t qdt_tmp[] = { __VA_ARGS__ };                                 \
> +        int i;                                                                \
> +                                                                              \
> +        for (i = 0; i < ARRAY_SIZE(qdt_tmp); i++) {                           \
> +            qdt_tmp[i] = cpu_to_be32(qdt_tmp[i]);                             \
> +        }                                                                     \
> +        qemu_fdt_appendprop(fdt, node_path, property, qdt_tmp,                \
> +                         sizeof(qdt_tmp));                                    \
> +    } while (0)
> +
> +
same here.

While I understand the benefits I think we could manage without this new
API by using qemu_fdt_setprop and populating the uint32_t array separately.

I understood the QEMU API for manipulating flattened trees was not
supposed to grow too much but I don't have a strong option here. You
should CC David Gibson I think.

Thanks

Eric


>  void qemu_fdt_dumpdtb(void *fdt, int size);
>  
>  /**
> 

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

* Re: [Qemu-devel] [PATCH v2 5/9] hw: arm: Add SMMUv3 to virt platform, create DTS accordingly
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 5/9] hw: arm: Add SMMUv3 to virt platform, create DTS accordingly Prem Mallappa
@ 2016-09-09 16:31   ` Auger Eric
  2016-09-12 20:20     ` Prem Mallappa
  0 siblings, 1 reply; 34+ messages in thread
From: Auger Eric @ 2016-09-09 16:31 UTC (permalink / raw)
  To: Prem Mallappa, Peter Maydell, Edgar E . Iglesias, qemu-devel

Hi Prem,

> Default virt platform now creates SMMU device.
> Default config to build SMMU device along is in previous patches.
> 
> Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
> ---
>  hw/arm/virt.c         | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/arm/smmu.h | 33 +++++++++++++++++++++++++++
>  include/hw/arm/virt.h |  2 ++
>  3 files changed, 97 insertions(+)
>  create mode 100644 include/hw/arm/smmu.h
> 
> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> index c5c125e..f3c7891 100644
> --- a/hw/arm/virt.c
> +++ b/hw/arm/virt.c
> @@ -57,6 +57,7 @@
>  #include "hw/smbios/smbios.h"
>  #include "qapi/visitor.h"
>  #include "standard-headers/linux/input.h"
> +#include "hw/arm/smmu.h"
>  
>  /* Number of external interrupt lines to configure the GIC with */
>  #define NUM_IRQS 256
> @@ -77,6 +78,7 @@ typedef struct VirtBoardInfo {
>      uint32_t gic_phandle;
>      uint32_t v2m_phandle;
>      bool using_psci;
> +    uint32_t smmu_phandle;
nit: would group the phandles together
>  } VirtBoardInfo;
>  
>  typedef struct {
> @@ -175,6 +177,7 @@ static const MemMapEntry a15memmap[] = {
>      [VIRT_FW_CFG] =             { 0x09020000, 0x00000018 },
>      [VIRT_GPIO] =               { 0x09030000, 0x00001000 },
>      [VIRT_SECURE_UART] =        { 0x09040000, 0x00001000 },
> +    [VIRT_SMMU] =               { 0x09050000, 0x00020000 }, /* 128K, needed */
>      [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
>      /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
>      [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
> @@ -195,9 +198,19 @@ static const int a15irqmap[] = {
>      [VIRT_SECURE_UART] = 8,
>      [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
>      [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
Currently don' we have NUM_GICV2M_SPIS = 64?
> +    [VIRT_SMMU] = 74,    /* ...to 74 + NUM_SMMU_IRQS - 1 */
>      [VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */
>  };
>  
> +static const struct smmuirq {
> +    const char *name;
> +} smmuirqmap[NUM_SMMU_IRQS] = {
> +    [SMMU_IRQ_EVTQ] = {"eventq"},
> +    [SMMU_IRQ_PRIQ] = {"priq"},
> +    [SMMU_IRQ_CMD_SYNC] = {"cmdq-sync"},
> +    [SMMU_IRQ_GERROR] = {"gerror"},
> +};
> +
>  static VirtBoardInfo machines[] = {
>      {
>          .cpu_model = "cortex-a15",
> @@ -938,6 +951,50 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle,
>                             0x7           /* PCI irq */);
>  }
>  
> +static void alloc_smmu_phandle(VirtBoardInfo *vbi)
> +{
I would rather put that code in create_smmu as it is done in create_gic
for instance
> +    if (!vbi->smmu_phandle)
> +        vbi->smmu_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
> +}
> +
> +static void create_smmu(VirtBoardInfo *vbi, qemu_irq *pic)
> +{
> +    int i;
> +    char *smmu;
> +    const char compat[] = "arm,smmu-v3";
> +    int irq =  vbi->irqmap[VIRT_SMMU];
> +    hwaddr base = vbi->memmap[VIRT_SMMU].base;
> +    hwaddr size = vbi->memmap[VIRT_SMMU].size;
> +    int type = GIC_FDT_IRQ_TYPE_SPI;
> +
> +    sysbus_create_varargs("smmuv3", base,
> +                          pic[irq],
> +                          pic[irq + 1],
> +                          pic[irq + 2],
> +                          pic[irq + 3],
> +                          NULL);
> +
> +    smmu = g_strdup_printf("/smmuv3@%" PRIx64, base);
> +    qemu_fdt_add_subnode(vbi->fdt, smmu);
> +    qemu_fdt_setprop(vbi->fdt, smmu, "compatible", compat, sizeof(compat));
> +    qemu_fdt_setprop_sized_cells(vbi->fdt, smmu, "reg", 2, base, 2, size);
> +
> +    for (i = 0; i < NUM_SMMU_IRQS; i++) {
> +        qemu_fdt_appendprop_cells(vbi->fdt, smmu, "interrupts",
> +                                  type, irq + i,
> +                                  GIC_FDT_IRQ_FLAGS_LEVEL_HI);
> +        qemu_fdt_appendprop_string(vbi->fdt, smmu, "interrupt-names",
> +                                   smmuirqmap[i].name);
so as I mentionned before I think we can manage with appendprop helpers
but let's see ...
> +    }
> +
> +    qemu_fdt_setprop_cell(vbi->fdt, smmu, "clocks", vbi->clock_phandle);
> +    qemu_fdt_setprop_cell(vbi->fdt, smmu, "#iommu-cells", 0);
> +    qemu_fdt_setprop_string(vbi->fdt, smmu, "clock-names", "apb_pclk");
> +
> +    qemu_fdt_setprop_cell(vbi->fdt, smmu, "phandle", vbi->smmu_phandle);
> +    g_free(smmu);
> +}
> +
>  static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
>                          bool use_highmem)
>  {
> @@ -1048,6 +1105,7 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
>      }
>  
>      qemu_fdt_setprop_cell(vbi->fdt, nodename, "#interrupt-cells", 1);
> +    qemu_fdt_setprop_cells(vbi->fdt, nodename, "iommus", vbi->smmu_phandle);
So if I understand correctly this connects the PCIe host controller to
the emulated smmu, always? If confirmed I suspect this is not what we
want. At leat we need a machine option. Or can't we target dynamic
instantiation using -device qemu option (as it is done on x86) using the
platform bus infra, as we do for VFIO platform devices? This currently
work with dt but maybe we can achieve ACPI table dynamic creation.

Thanks

Eric
>      create_pcie_irq_map(vbi, vbi->gic_phandle, irq, nodename);
>  
>      g_free(nodename);
> @@ -1332,8 +1390,12 @@ static void machvirt_init(MachineState *machine)
>  
>      create_rtc(vbi, pic);
>  
> +    alloc_smmu_phandle(vbi);
if you follow my comment this disappears

Thanks

Eric
> +
>      create_pcie(vbi, pic, vms->highmem);
>  
> +    create_smmu(vbi, pic);
> +
>      create_gpio(vbi, pic);
>  
>      /* Create mmio transports, so the user can create virtio backends
> diff --git a/include/hw/arm/smmu.h b/include/hw/arm/smmu.h
> new file mode 100644
> index 0000000..bbb5e5d
> --- /dev/null
> +++ b/include/hw/arm/smmu.h
> @@ -0,0 +1,33 @@
> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + *
> + * Copyright (C) 2014-2015 Broadcom Corporation
> + *
> + * Author: Prem Mallappa <pmallapp@broadcom.com>
> + *
> + */
> +#ifndef HW_ARM_SMMU_H
> +#define HW_ARM_SMMU_H
> +
> +#define TYPE_SMMU_DEV_BASE "smmu-base"
> +#define TYPE_SMMU_DEV      "smmuv3"
> +
> +typedef enum {
> +    SMMU_IRQ_GERROR,
> +    SMMU_IRQ_PRIQ,
> +    SMMU_IRQ_EVTQ,
> +    SMMU_IRQ_CMD_SYNC,
> +} SMMUIrq;
> +
> +#endif
> diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
> index 9650193..0b7138b 100644
> --- a/include/hw/arm/virt.h
> +++ b/include/hw/arm/virt.h
> @@ -35,6 +35,7 @@
>  
>  #define NUM_GICV2M_SPIS       64
>  #define NUM_VIRTIO_TRANSPORTS 32
> +#define NUM_SMMU_IRQS          4
>  
>  #define ARCH_TIMER_VIRT_IRQ   11
>  #define ARCH_TIMER_S_EL1_IRQ  13
> @@ -54,6 +55,7 @@ enum {
>      VIRT_GIC_V2M,
>      VIRT_GIC_ITS,
>      VIRT_GIC_REDIST,
> +    VIRT_SMMU,
>      VIRT_UART,
>      VIRT_MMIO,
>      VIRT_RTC,
> 

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

* Re: [Qemu-devel] [PATCH v2 5/9] hw: arm: Add SMMUv3 to virt platform, create DTS accordingly
  2016-09-09 16:31   ` Auger Eric
@ 2016-09-12 20:20     ` Prem Mallappa
  0 siblings, 0 replies; 34+ messages in thread
From: Prem Mallappa @ 2016-09-12 20:20 UTC (permalink / raw)
  To: Auger Eric; +Cc: Peter Maydell, Edgar E . Iglesias, QEMU Developers

On Fri, Sep 9, 2016 at 10:01 PM, Auger Eric <eric.auger@redhat.com> wrote:

> Hi Prem,
>
> > Default virt platform now creates SMMU device.
> > Default config to build SMMU device along is in previous patches.
> >
> > Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
> > ---
> >  hw/arm/virt.c         | 62 ++++++++++++++++++++++++++++++
> +++++++++++++++++++++
> >  include/hw/arm/smmu.h | 33 +++++++++++++++++++++++++++
> >  include/hw/arm/virt.h |  2 ++
> >  3 files changed, 97 insertions(+)
> >  create mode 100644 include/hw/arm/smmu.h
> >
> > diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> > index c5c125e..f3c7891 100644
> > --- a/hw/arm/virt.c
> > +++ b/hw/arm/virt.c
> > @@ -57,6 +57,7 @@
> >  #include "hw/smbios/smbios.h"
> >  #include "qapi/visitor.h"
> >  #include "standard-headers/linux/input.h"
> > +#include "hw/arm/smmu.h"
> >
> >  /* Number of external interrupt lines to configure the GIC with */
> >  #define NUM_IRQS 256
> > @@ -77,6 +78,7 @@ typedef struct VirtBoardInfo {
> >      uint32_t gic_phandle;
> >      uint32_t v2m_phandle;
> >      bool using_psci;
> > +    uint32_t smmu_phandle;
> nit: would group the phandles together
> >  } VirtBoardInfo;
> >
> >  typedef struct {
> > @@ -175,6 +177,7 @@ static const MemMapEntry a15memmap[] = {
> >      [VIRT_FW_CFG] =             { 0x09020000, 0x00000018 },
> >      [VIRT_GPIO] =               { 0x09030000, 0x00001000 },
> >      [VIRT_SECURE_UART] =        { 0x09040000, 0x00001000 },
> > +    [VIRT_SMMU] =               { 0x09050000, 0x00020000 }, /* 128K,
> needed */
> >      [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
> >      /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that
> size */
> >      [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
> > @@ -195,9 +198,19 @@ static const int a15irqmap[] = {
> >      [VIRT_SECURE_UART] = 8,
> >      [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
> >      [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
> Currently don' we have NUM_GICV2M_SPIS = 64?
> > +    [VIRT_SMMU] = 74,    /* ...to 74 + NUM_SMMU_IRQS - 1 */
> >      [VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1
> */
> >  };
> >
> > +static const struct smmuirq {
> > +    const char *name;
> > +} smmuirqmap[NUM_SMMU_IRQS] = {
> > +    [SMMU_IRQ_EVTQ] = {"eventq"},
> > +    [SMMU_IRQ_PRIQ] = {"priq"},
> > +    [SMMU_IRQ_CMD_SYNC] = {"cmdq-sync"},
> > +    [SMMU_IRQ_GERROR] = {"gerror"},
> > +};
> > +
> >  static VirtBoardInfo machines[] = {
> >      {
> >          .cpu_model = "cortex-a15",
> > @@ -938,6 +951,50 @@ static void create_pcie_irq_map(const VirtBoardInfo
> *vbi, uint32_t gic_phandle,
> >                             0x7           /* PCI irq */);
> >  }
> >
> > +static void alloc_smmu_phandle(VirtBoardInfo *vbi)
> > +{
> I would rather put that code in create_smmu as it is done in create_gic
> for instance
> > +    if (!vbi->smmu_phandle)
> > +        vbi->smmu_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
> > +}
> > +
> > +static void create_smmu(VirtBoardInfo *vbi, qemu_irq *pic)
> > +{
> > +    int i;
> > +    char *smmu;
> > +    const char compat[] = "arm,smmu-v3";
> > +    int irq =  vbi->irqmap[VIRT_SMMU];
> > +    hwaddr base = vbi->memmap[VIRT_SMMU].base;
> > +    hwaddr size = vbi->memmap[VIRT_SMMU].size;
> > +    int type = GIC_FDT_IRQ_TYPE_SPI;
> > +
> > +    sysbus_create_varargs("smmuv3", base,
> > +                          pic[irq],
> > +                          pic[irq + 1],
> > +                          pic[irq + 2],
> > +                          pic[irq + 3],
> > +                          NULL);
> > +
> > +    smmu = g_strdup_printf("/smmuv3@%" PRIx64, base);
> > +    qemu_fdt_add_subnode(vbi->fdt, smmu);
> > +    qemu_fdt_setprop(vbi->fdt, smmu, "compatible", compat,
> sizeof(compat));
> > +    qemu_fdt_setprop_sized_cells(vbi->fdt, smmu, "reg", 2, base, 2,
> size);
> > +
> > +    for (i = 0; i < NUM_SMMU_IRQS; i++) {
> > +        qemu_fdt_appendprop_cells(vbi->fdt, smmu, "interrupts",
> > +                                  type, irq + i,
> > +                                  GIC_FDT_IRQ_FLAGS_LEVEL_HI);
> > +        qemu_fdt_appendprop_string(vbi->fdt, smmu, "interrupt-names",
> > +                                   smmuirqmap[i].name);
> so as I mentionned before I think we can manage with appendprop helpers
> but let's see ...
> > +    }
> > +
> > +    qemu_fdt_setprop_cell(vbi->fdt, smmu, "clocks",
> vbi->clock_phandle);
> > +    qemu_fdt_setprop_cell(vbi->fdt, smmu, "#iommu-cells", 0);
> > +    qemu_fdt_setprop_string(vbi->fdt, smmu, "clock-names", "apb_pclk");
> > +
> > +    qemu_fdt_setprop_cell(vbi->fdt, smmu, "phandle",
> vbi->smmu_phandle);
> > +    g_free(smmu);
> > +}
> > +
> >  static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
> >                          bool use_highmem)
> >  {
> > @@ -1048,6 +1105,7 @@ static void create_pcie(const VirtBoardInfo *vbi,
> qemu_irq *pic,
> >      }
> >
>

Hi Eric,


> >      qemu_fdt_setprop_cell(vbi->fdt, nodename, "#interrupt-cells", 1);
> > +    qemu_fdt_setprop_cells(vbi->fdt, nodename, "iommus",
> vbi->smmu_phandle);
> So if I understand correctly this connects the PCIe host controller to
> the emulated smmu, always? If confirmed I suspect this is not what we
> want. At leat we need a machine option. Or can't we target dynamic
> instantiation using -device qemu option (as it is done on x86) using the
> platform bus infra, as we do for VFIO platform devices? This currently
> work with dt but maybe we can achieve ACPI table dynamic creation.
>

SMMUv3 is designed to work with PCIe most importantly (however it can work
with other buses), For the platform bus, there is SMMUv2.

-device or -M virt,iommu=on or similar can be a good idea.

I am working on ACPI tables, (the optional patch in this series). however
it is more
 complete now, will send out ASAP.

-- 
Cheers,
/Prem

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

* Re: [Qemu-devel] [PATCH v2 2/9] devicetree: Added new APIs to make use of more fdt functions
  2016-09-09 16:02   ` Auger Eric
@ 2016-09-12 20:21     ` Prem Mallappa
  0 siblings, 0 replies; 34+ messages in thread
From: Prem Mallappa @ 2016-09-12 20:21 UTC (permalink / raw)
  To: Auger Eric; +Cc: Peter Maydell, Edgar E . Iglesias, QEMU Developers

On Fri, Sep 9, 2016 at 9:32 PM, Auger Eric <eric.auger@redhat.com> wrote:

> Hi Prem,
>
> > SMMUv3 needs device tree entry like below
> To me the commit message should be more explicit and mention appendprop
> functionality
> >
> >      interrupt-names = "gerror", "priq", "eventq", "cmdq-sync";
> >
> > This patch introduces helper function to add entries like above
> >
> > Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
> > ---
> >  device_tree.c                | 35 +++++++++++++++++++++++++++++++++++
> >  include/sysemu/device_tree.h | 18 ++++++++++++++++++
> >  2 files changed, 53 insertions(+)
> >
> > diff --git a/device_tree.c b/device_tree.c
> > index 6e06320..5d5966e 100644
> > --- a/device_tree.c
> > +++ b/device_tree.c
> > @@ -297,6 +297,24 @@ int qemu_fdt_setprop(void *fdt, const char
> *node_path,
> >      return r;
> >  }
> >
> > +int qemu_fdt_appendprop(void *fdt, const char *node_path,
> > +                     const char *property, const void *val, int size)
> > +{
> > +    int r;
> > +
> > +    r = fdt_appendprop(fdt, findnode_nofail(fdt, node_path), property,
> > +                       val, size);
> > +    if (r < 0) {
> > +        error_report("%s: Couldn't set %s/%s: %s", __func__, node_path,
> > +                     property, fdt_strerror(r));
> > +        exit(1);
> > +    }
> > +
> > +    return r;
> > +}
> > +
> > +
> spare void lines
> > +
> >  int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
> >                            const char *property, uint32_t val)
> >  {
> > @@ -319,6 +337,23 @@ int qemu_fdt_setprop_u64(void *fdt, const char
> *node_path,
> >      return qemu_fdt_setprop(fdt, node_path, property, &val,
> sizeof(val));
> >  }
> >
> > +int qemu_fdt_appendprop_string(void *fdt, const char *node_path,
> > +                            const char *property, const char *string)
> > +{
> > +    int r;
> > +
> > +    r = fdt_appendprop_string(fdt, findnode_nofail(fdt, node_path),
> > +                              property, string);
> > +    if (r < 0) {
> > +        error_report("%s: Couldn't set %s/%s = %s: %s", __func__,
> > +                     node_path, property, string, fdt_strerror(r));
> > +        exit(1);
> > +    }
> > +
> > +    return r;
> > +}
> > +
> same
> > +
> >  int qemu_fdt_setprop_string(void *fdt, const char *node_path,
> >                              const char *property, const char *string)
> >  {
> > diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h
> > index 705650a..5a0a297 100644
> > --- a/include/sysemu/device_tree.h
> > +++ b/include/sysemu/device_tree.h
> > @@ -45,12 +45,16 @@ char **qemu_fdt_node_path(void *fdt, const char
> *name, char *compat,
> >
> >  int qemu_fdt_setprop(void *fdt, const char *node_path,
> >                       const char *property, const void *val, int size);
> > +int qemu_fdt_appendprop(void *fdt, const char *node_path,
> > +                     const char *property, const void *val, int size);
> >  int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
> >                            const char *property, uint32_t val);
> >  int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
> >                           const char *property, uint64_t val);
> >  int qemu_fdt_setprop_string(void *fdt, const char *node_path,
> >                              const char *property, const char *string);
> > +int qemu_fdt_appendprop_string(void *fdt, const char *node_path,
> > +                               const char *property, const char
> *string);
> >  int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
> >                               const char *property,
> >                               const char *target_node_path);
> > @@ -98,6 +102,20 @@ int qemu_fdt_add_subnode(void *fdt, const char
> *name);
> >                           sizeof(qdt_tmp));
>       \
> >      } while (0)
> >
> > +
> same
> > +#define qemu_fdt_appendprop_cells(fdt, node_path, property, ...)
>       \
> > +    do {
>       \
> > +        uint32_t qdt_tmp[] = { __VA_ARGS__ };
>        \
> > +        int i;
>       \
> > +
>       \
> > +        for (i = 0; i < ARRAY_SIZE(qdt_tmp); i++) {
>        \
> > +            qdt_tmp[i] = cpu_to_be32(qdt_tmp[i]);
>        \
> > +        }
>        \
> > +        qemu_fdt_appendprop(fdt, node_path, property, qdt_tmp,
>       \
> > +                         sizeof(qdt_tmp));
>       \
> > +    } while (0)
> > +
> > +
> same here.
>
> Will take care of the extra blank lines.


> While I understand the benefits I think we could manage without this new
> API by using qemu_fdt_setprop and populating the uint32_t array separately.
>
> I'll give this a try and see how it goes.


> I understood the QEMU API for manipulating flattened trees was not
> supposed to grow too much but I don't have a strong option here. You
> should CC David Gibson I think.
>
>
Will sure do in the next drop.



-- 
Cheers,
/Prem

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

* Re: [Qemu-devel] [PATCH v2 1/9] log: Add new IOMMU type
  2016-09-09 15:36   ` Auger Eric
@ 2016-09-12 20:23     ` Prem Mallappa
  2016-09-25 14:58       ` Edgar E. Iglesias
  0 siblings, 1 reply; 34+ messages in thread
From: Prem Mallappa @ 2016-09-12 20:23 UTC (permalink / raw)
  To: Auger Eric; +Cc: Peter Maydell, Edgar E . Iglesias, QEMU Developers

On Fri, Sep 9, 2016 at 9:06 PM, Auger Eric <eric.auger@redhat.com> wrote:

> Hi Prem,
>
> Missing commit message
>
> > Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
> > ---
> >  include/qemu/log.h | 1 +
> >  util/log.c         | 2 ++
> >  2 files changed, 3 insertions(+)
> >
> > diff --git a/include/qemu/log.h b/include/qemu/log.h
> > index 234fa81..3dd2131 100644
> > --- a/include/qemu/log.h
> > +++ b/include/qemu/log.h
> > @@ -42,6 +42,7 @@ static inline bool qemu_log_separate(void)
> >  #define CPU_LOG_TB_NOCHAIN (1 << 13)
> >  #define CPU_LOG_PAGE       (1 << 14)
> >  #define LOG_TRACE          (1 << 15)
> > +#define CPU_LOG_IOMMU      (1 << 16)
> why is it prefixed with CPU_ ?
> besides all arm gic devices seem to use LOG_GUEST_ERROR. what is the
> rationale behind introducing a new enum?
>

Will change this to LOG_GUEST_ERROR, if others on the list are okay.


-- 
Cheers,
/Prem

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

* Re: [Qemu-devel] [PATCH v2 9/9] [optional] arm: smmu-v3: ACPI IORT initial support
  2016-09-09 15:24   ` Auger Eric
@ 2016-09-12 20:42     ` Prem Mallappa
  2016-09-23 13:10       ` Auger Eric
  0 siblings, 1 reply; 34+ messages in thread
From: Prem Mallappa @ 2016-09-12 20:42 UTC (permalink / raw)
  To: Auger Eric; +Cc: Peter Maydell, Edgar E . Iglesias, QEMU Developers

On Fri, Sep 9, 2016 at 8:54 PM, Auger Eric <eric.auger@redhat.com> wrote:

> Hi Prem,
>
> On 22/08/2016 18:17, Prem Mallappa wrote:
> > Added ACPI IORT tables, was needed for internal project purpose, but
> > posting here for anyone looking for testing ACPI on ARM platforms.
> > (P.S: Linux side IORT patches are WIP)
> I am also interested in IORT ITS group and currently prototyping
> something, hence my few comments/questions.
> >
> > Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
> > ---
> >  hw/arm/virt-acpi-build.c    | 43 +++++++++++++++++++++++
> >  include/hw/acpi/acpi-defs.h | 84 ++++++++++++++++++++++++++++++
> +++++++++++++++
> >  2 files changed, 127 insertions(+)
> >
> > diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
> > index 1fa0581..d5fb69e 100644
> > --- a/hw/arm/virt-acpi-build.c
> > +++ b/hw/arm/virt-acpi-build.c
> > @@ -382,6 +382,45 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker,
> unsigned rsdt_tbl_offset)
> >      return rsdp_table;
> >  }
> >
> > +/*
> > + * TODO: Simple IORT for now, will add ID mappings as we go
> > + * basic idea is to instantiate SMMU from ACPI
> > + */
> > +static void
> > +build_iort(GArray *table_data, BIOSLinker *linker, VirtGuestInfo
> *guest_info)
> > +{
> > +    int iort_start = table_data->len;
> > +    AcpiIortTable *iort;
> > +    AcpiIortNode *iort_node;
> > +    AcpiIortSmmu3 *smmu;
> > +    AcpiIortRC *rc;
> > +    const MemMapEntry *memmap = guest_info->memmap;
> > +
> > +    iort = acpi_data_push(table_data, sizeof(*iort));
> > +
> > +    iort->length = sizeof(*iort);
> Isn't is supposed to be the length of the whole IORT (including the node
> cumulated sizes?)
> > +    iort->node_offset = table_data->len - iort_start;
> > +    iort->num_nodes++;
> > +
> > +    smmu = acpi_data_push(table_data, sizeof(*smmu));
> > +    iort_node = &smmu->iort_node;
> > +    iort_node->type = 0x04;          /* SMMUv3 */
> To match existing code (include/hw/acpi/acpi-defs.h), maybe enum values
> can be created (ACPI_IORT_NODE_SMMU_V3). This also matches kernel enum.
>
> I have made these changes, will send out ASAP.


> More generally Shannon advised to use the same field names as the ones
> used in the kernel header: acpi_iort_node_type in include/acpi/actbl2.h
>

Will change this accordingly


> > +    iort_node->length = sizeof(*smmu);
> > +    smmu->base_addr = cpu_to_le64(memmap[VIRT_SMMU].base);
> > +
> > +    iort->num_nodes++;
> > +
> > +    rc = acpi_data_push(table_data, sizeof(*rc));
> > +    iort_node = &rc->iort_node;
> > +    iort_node->type = 0x02;          /* RC */
> > +    iort_node->length = sizeof(*rc);
> I think the mem_access_prop field should be set to 1 now the host
> controller is assumed to be cache coherent.
> > +    rc->ats_attr = 1;
> no ATS support instead?
> > +    rc->pci_seg_num = 0;
> ID mappings are mandated for me to support MSIs with ITS.
>

These changes are made as I write,


> Shannon told me we should match the kernel datatypes & fields
>
> for instance in include/acpi/actbl2.h we have:
>
> struct acpi_iort_id_mapping {
>         u32 input_base;         /* Lowest value in input range */
>         u32 id_count;           /* Number of IDs */
>         u32 output_base;        /* Lowest value in output range */
>         u32 output_reference;   /* A reference to the output node */
>         u32 flags;
> };
>
> This also holds for other struct definitions.
>
>
Sure will change this accordingly.


-- 
Cheers,
/Prem

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

* Re: [Qemu-devel] [PATCH v2 9/9] [optional] arm: smmu-v3: ACPI IORT initial support
  2016-09-12 20:42     ` Prem Mallappa
@ 2016-09-23 13:10       ` Auger Eric
  2016-09-23 14:07         ` Prem Mallappa
  0 siblings, 1 reply; 34+ messages in thread
From: Auger Eric @ 2016-09-23 13:10 UTC (permalink / raw)
  To: Prem Mallappa; +Cc: Peter Maydell, QEMU Developers, Edgar E . Iglesias

Hi Prem,

On 12/09/2016 22:42, Prem Mallappa wrote:
> On Fri, Sep 9, 2016 at 8:54 PM, Auger Eric <eric.auger@redhat.com> wrote:
> 
>> Hi Prem,
>>
>> On 22/08/2016 18:17, Prem Mallappa wrote:
>>> Added ACPI IORT tables, was needed for internal project purpose, but
>>> posting here for anyone looking for testing ACPI on ARM platforms.
>>> (P.S: Linux side IORT patches are WIP)
>> I am also interested in IORT ITS group and currently prototyping
>> something, hence my few comments/questions.
>>>
>>> Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
>>> ---
>>>  hw/arm/virt-acpi-build.c    | 43 +++++++++++++++++++++++
>>>  include/hw/acpi/acpi-defs.h | 84 ++++++++++++++++++++++++++++++
>> +++++++++++++++
>>>  2 files changed, 127 insertions(+)
>>>
>>> diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
>>> index 1fa0581..d5fb69e 100644
>>> --- a/hw/arm/virt-acpi-build.c
>>> +++ b/hw/arm/virt-acpi-build.c
>>> @@ -382,6 +382,45 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker,
>> unsigned rsdt_tbl_offset)
>>>      return rsdp_table;
>>>  }
>>>
>>> +/*
>>> + * TODO: Simple IORT for now, will add ID mappings as we go
>>> + * basic idea is to instantiate SMMU from ACPI
>>> + */
>>> +static void
>>> +build_iort(GArray *table_data, BIOSLinker *linker, VirtGuestInfo
>> *guest_info)
>>> +{
>>> +    int iort_start = table_data->len;
>>> +    AcpiIortTable *iort;
>>> +    AcpiIortNode *iort_node;
>>> +    AcpiIortSmmu3 *smmu;
>>> +    AcpiIortRC *rc;
>>> +    const MemMapEntry *memmap = guest_info->memmap;
>>> +
>>> +    iort = acpi_data_push(table_data, sizeof(*iort));
>>> +
>>> +    iort->length = sizeof(*iort);
>> Isn't is supposed to be the length of the whole IORT (including the node
>> cumulated sizes?)
>>> +    iort->node_offset = table_data->len - iort_start;
>>> +    iort->num_nodes++;
>>> +
>>> +    smmu = acpi_data_push(table_data, sizeof(*smmu));
>>> +    iort_node = &smmu->iort_node;
>>> +    iort_node->type = 0x04;          /* SMMUv3 */
>> To match existing code (include/hw/acpi/acpi-defs.h), maybe enum values
>> can be created (ACPI_IORT_NODE_SMMU_V3). This also matches kernel enum.
>>
>> I have made these changes, will send out ASAP.
> 
> 
>> More generally Shannon advised to use the same field names as the ones
>> used in the kernel header: acpi_iort_node_type in include/acpi/actbl2.h
>>
> 
> Will change this accordingly
> 
> 
>>> +    iort_node->length = sizeof(*smmu);
>>> +    smmu->base_addr = cpu_to_le64(memmap[VIRT_SMMU].base);
>>> +
>>> +    iort->num_nodes++;
>>> +
>>> +    rc = acpi_data_push(table_data, sizeof(*rc));
>>> +    iort_node = &rc->iort_node;
>>> +    iort_node->type = 0x02;          /* RC */
>>> +    iort_node->length = sizeof(*rc);
>> I think the mem_access_prop field should be set to 1 now the host
>> controller is assumed to be cache coherent.
>>> +    rc->ats_attr = 1;
>> no ATS support instead?
>>> +    rc->pci_seg_num = 0;
>> ID mappings are mandated for me to support MSIs with ITS.
>>
> 
> These changes are made as I write,
> 
> 
>> Shannon told me we should match the kernel datatypes & fields
>>
>> for instance in include/acpi/actbl2.h we have:
>>
>> struct acpi_iort_id_mapping {
>>         u32 input_base;         /* Lowest value in input range */
>>         u32 id_count;           /* Number of IDs */
>>         u32 output_base;        /* Lowest value in output range */
>>         u32 output_reference;   /* A reference to the output node */
>>         u32 flags;
>> };
>>
>> This also holds for other struct definitions.
>>
>>
> Sure will change this accordingly.

I currently have a series creating the IORT with an RC node and an ITS
node. It is needed to complete the integration of the virtual ITS (to
connect it with the PCI host controller). This originates from this
patch: I added the RC->ITS ID mapping + the ITS node and tested it.

I don't know how to proceed to get the 2 features (vSMMU and vITS)
progress separately. Do you plan to respin this patch quickly?
Otherwise, if you are busy with other things I propose you to respin
fixing the few things above, splitting it into 3 patches, header [1],
ITS node creation [2], RC node creation with RC->ITS mapping [3] while
keeping credit to you on [1] and [3].

Then we can have a 4th patch adding RC-> SMMU ID > ITS mapping?

Please let me know what are your plans and what do you think.

Thanks

Eric
> 
> 

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

* Re: [Qemu-devel] [PATCH v2 9/9] [optional] arm: smmu-v3: ACPI IORT initial support
  2016-09-23 13:10       ` Auger Eric
@ 2016-09-23 14:07         ` Prem Mallappa
  2016-09-23 16:38           ` Auger Eric
  0 siblings, 1 reply; 34+ messages in thread
From: Prem Mallappa @ 2016-09-23 14:07 UTC (permalink / raw)
  To: Auger Eric; +Cc: Peter Maydell, QEMU Developers, Edgar E . Iglesias

On Fri, Sep 23, 2016 at 6:40 PM, Auger Eric <eric.auger@redhat.com> wrote:

> Hi Prem,
>
> On 12/09/2016 22:42, Prem Mallappa wrote:
> > On Fri, Sep 9, 2016 at 8:54 PM, Auger Eric <eric.auger@redhat.com>
> wrote:
> >
> >> Hi Prem,
> >>
> >> On 22/08/2016 18:17, Prem Mallappa wrote:
> >>> Added ACPI IORT tables, was needed for internal project purpose, but
> >>> posting here for anyone looking for testing ACPI on ARM platforms.
> >>> (P.S: Linux side IORT patches are WIP)
> >> I am also interested in IORT ITS group and currently prototyping
> >> something, hence my few comments/questions.
> >>>
> >>> Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
> >>> ---
> >>>  hw/arm/virt-acpi-build.c    | 43 +++++++++++++++++++++++
> >>>  include/hw/acpi/acpi-defs.h | 84 ++++++++++++++++++++++++++++++
> >> +++++++++++++++
> >>>  2 files changed, 127 insertions(+)
> >>>
> >>> diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
> >>> index 1fa0581..d5fb69e 100644
> >>> --- a/hw/arm/virt-acpi-build.c
> >>> +++ b/hw/arm/virt-acpi-build.c
> >>> @@ -382,6 +382,45 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker,
> >> unsigned rsdt_tbl_offset)
> >>>      return rsdp_table;
> >>>  }
> >>>
> >>> +/*
> >>> + * TODO: Simple IORT for now, will add ID mappings as we go
> >>> + * basic idea is to instantiate SMMU from ACPI
> >>> + */
> >>> +static void
> >>> +build_iort(GArray *table_data, BIOSLinker *linker, VirtGuestInfo
> >> *guest_info)
> >>> +{
> >>> +    int iort_start = table_data->len;
> >>> +    AcpiIortTable *iort;
> >>> +    AcpiIortNode *iort_node;
> >>> +    AcpiIortSmmu3 *smmu;
> >>> +    AcpiIortRC *rc;
> >>> +    const MemMapEntry *memmap = guest_info->memmap;
> >>> +
> >>> +    iort = acpi_data_push(table_data, sizeof(*iort));
> >>> +
> >>> +    iort->length = sizeof(*iort);
> >> Isn't is supposed to be the length of the whole IORT (including the node
> >> cumulated sizes?)
> >>> +    iort->node_offset = table_data->len - iort_start;
> >>> +    iort->num_nodes++;
> >>> +
> >>> +    smmu = acpi_data_push(table_data, sizeof(*smmu));
> >>> +    iort_node = &smmu->iort_node;
> >>> +    iort_node->type = 0x04;          /* SMMUv3 */
> >> To match existing code (include/hw/acpi/acpi-defs.h), maybe enum values
> >> can be created (ACPI_IORT_NODE_SMMU_V3). This also matches kernel enum.
> >>
> >> I have made these changes, will send out ASAP.
> >
> >
> >> More generally Shannon advised to use the same field names as the ones
> >> used in the kernel header: acpi_iort_node_type in include/acpi/actbl2.h
> >>
> >
> > Will change this accordingly
> >
> >
> >>> +    iort_node->length = sizeof(*smmu);
> >>> +    smmu->base_addr = cpu_to_le64(memmap[VIRT_SMMU].base);
> >>> +
> >>> +    iort->num_nodes++;
> >>> +
> >>> +    rc = acpi_data_push(table_data, sizeof(*rc));
> >>> +    iort_node = &rc->iort_node;
> >>> +    iort_node->type = 0x02;          /* RC */
> >>> +    iort_node->length = sizeof(*rc);
> >> I think the mem_access_prop field should be set to 1 now the host
> >> controller is assumed to be cache coherent.
> >>> +    rc->ats_attr = 1;
> >> no ATS support instead?
> >>> +    rc->pci_seg_num = 0;
> >> ID mappings are mandated for me to support MSIs with ITS.
> >>
> >
> > These changes are made as I write,
> >
> >
> >> Shannon told me we should match the kernel datatypes & fields
> >>
> >> for instance in include/acpi/actbl2.h we have:
> >>
> >> struct acpi_iort_id_mapping {
> >>         u32 input_base;         /* Lowest value in input range */
> >>         u32 id_count;           /* Number of IDs */
> >>         u32 output_base;        /* Lowest value in output range */
> >>         u32 output_reference;   /* A reference to the output node */
> >>         u32 flags;
> >> };
> >>
> >> This also holds for other struct definitions.
> >>
> >>
> > Sure will change this accordingly.
>
> I currently have a series creating the IORT with an RC node and an ITS
> node. It is needed to complete the integration of the virtual ITS (to
> connect it with the PCI host controller). This originates from this
> patch: I added the RC->ITS ID mapping + the ITS node and tested it.
>
> I don't know how to proceed to get the 2 features (vSMMU and vITS)
> progress separately. Do you plan to respin this patch quickly?
> Otherwise, if you are busy with other things I propose you to respin
> fixing the few things above, splitting it into 3 patches, header [1],
> ITS node creation [2], RC node creation with RC->ITS mapping [3] while
> keeping credit to you on [1] and [3].
>
> Then we can have a 4th patch adding RC-> SMMU ID > ITS mapping?
>
> Please let me know what are your plans and what do you think.
>
> Thanks
>
> Eric
> >
> >
>

Hi Eric,
I have been busy with something else, however I have a wokring patch set
(unclean version)
which creates the IORT tables, with SMMU->RC->ITS with ITS id mapping
(routing all interrupts to single ITS).
I'll push them by to unstable branch by this Sunday.

-- 
Cheers,
/Prem

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

* Re: [Qemu-devel] [PATCH v2 9/9] [optional] arm: smmu-v3: ACPI IORT initial support
  2016-09-23 14:07         ` Prem Mallappa
@ 2016-09-23 16:38           ` Auger Eric
  0 siblings, 0 replies; 34+ messages in thread
From: Auger Eric @ 2016-09-23 16:38 UTC (permalink / raw)
  To: Prem Mallappa; +Cc: Peter Maydell, QEMU Developers, Edgar E . Iglesias

Hi Prem,
On 23/09/2016 16:07, Prem Mallappa wrote:
> On Fri, Sep 23, 2016 at 6:40 PM, Auger Eric <eric.auger@redhat.com
> <mailto:eric.auger@redhat.com>> wrote:
> 
>     Hi Prem,
> 
>     On 12/09/2016 22:42, Prem Mallappa wrote:
>     > On Fri, Sep 9, 2016 at 8:54 PM, Auger Eric <eric.auger@redhat.com
>     <mailto:eric.auger@redhat.com>> wrote:
>     >
>     >> Hi Prem,
>     >>
>     >> On 22/08/2016 18:17, Prem Mallappa wrote:
>     >>> Added ACPI IORT tables, was needed for internal project purpose, but
>     >>> posting here for anyone looking for testing ACPI on ARM platforms.
>     >>> (P.S: Linux side IORT patches are WIP)
>     >> I am also interested in IORT ITS group and currently prototyping
>     >> something, hence my few comments/questions.
>     >>>
>     >>> Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com
>     <mailto:prem.mallappa@broadcom.com>>
>     >>> ---
>     >>>  hw/arm/virt-acpi-build.c    | 43 +++++++++++++++++++++++
>     >>>  include/hw/acpi/acpi-defs.h | 84 ++++++++++++++++++++++++++++++
>     >> +++++++++++++++
>     >>>  2 files changed, 127 insertions(+)
>     >>>
>     >>> diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
>     >>> index 1fa0581..d5fb69e 100644
>     >>> --- a/hw/arm/virt-acpi-build.c
>     >>> +++ b/hw/arm/virt-acpi-build.c
>     >>> @@ -382,6 +382,45 @@ build_rsdp(GArray *rsdp_table, BIOSLinker
>     *linker,
>     >> unsigned rsdt_tbl_offset)
>     >>>      return rsdp_table;
>     >>>  }
>     >>>
>     >>> +/*
>     >>> + * TODO: Simple IORT for now, will add ID mappings as we go
>     >>> + * basic idea is to instantiate SMMU from ACPI
>     >>> + */
>     >>> +static void
>     >>> +build_iort(GArray *table_data, BIOSLinker *linker, VirtGuestInfo
>     >> *guest_info)
>     >>> +{
>     >>> +    int iort_start = table_data->len;
>     >>> +    AcpiIortTable *iort;
>     >>> +    AcpiIortNode *iort_node;
>     >>> +    AcpiIortSmmu3 *smmu;
>     >>> +    AcpiIortRC *rc;
>     >>> +    const MemMapEntry *memmap = guest_info->memmap;
>     >>> +
>     >>> +    iort = acpi_data_push(table_data, sizeof(*iort));
>     >>> +
>     >>> +    iort->length = sizeof(*iort);
>     >> Isn't is supposed to be the length of the whole IORT (including
>     the node
>     >> cumulated sizes?)
>     >>> +    iort->node_offset = table_data->len - iort_start;
>     >>> +    iort->num_nodes++;
>     >>> +
>     >>> +    smmu = acpi_data_push(table_data, sizeof(*smmu));
>     >>> +    iort_node = &smmu->iort_node;
>     >>> +    iort_node->type = 0x04;          /* SMMUv3 */
>     >> To match existing code (include/hw/acpi/acpi-defs.h), maybe enum
>     values
>     >> can be created (ACPI_IORT_NODE_SMMU_V3). This also matches kernel
>     enum.
>     >>
>     >> I have made these changes, will send out ASAP.
>     >
>     >
>     >> More generally Shannon advised to use the same field names as the
>     ones
>     >> used in the kernel header: acpi_iort_node_type in
>     include/acpi/actbl2.h
>     >>
>     >
>     > Will change this accordingly
>     >
>     >
>     >>> +    iort_node->length = sizeof(*smmu);
>     >>> +    smmu->base_addr = cpu_to_le64(memmap[VIRT_SMMU].base);
>     >>> +
>     >>> +    iort->num_nodes++;
>     >>> +
>     >>> +    rc = acpi_data_push(table_data, sizeof(*rc));
>     >>> +    iort_node = &rc->iort_node;
>     >>> +    iort_node->type = 0x02;          /* RC */
>     >>> +    iort_node->length = sizeof(*rc);
>     >> I think the mem_access_prop field should be set to 1 now the host
>     >> controller is assumed to be cache coherent.
>     >>> +    rc->ats_attr = 1;
>     >> no ATS support instead?
>     >>> +    rc->pci_seg_num = 0;
>     >> ID mappings are mandated for me to support MSIs with ITS.
>     >>
>     >
>     > These changes are made as I write,
>     >
>     >
>     >> Shannon told me we should match the kernel datatypes & fields
>     >>
>     >> for instance in include/acpi/actbl2.h we have:
>     >>
>     >> struct acpi_iort_id_mapping {
>     >>         u32 input_base;         /* Lowest value in input range */
>     >>         u32 id_count;           /* Number of IDs */
>     >>         u32 output_base;        /* Lowest value in output range */
>     >>         u32 output_reference;   /* A reference to the output node */
>     >>         u32 flags;
>     >> };
>     >>
>     >> This also holds for other struct definitions.
>     >>
>     >>
>     > Sure will change this accordingly.
> 
>     I currently have a series creating the IORT with an RC node and an ITS
>     node. It is needed to complete the integration of the virtual ITS (to
>     connect it with the PCI host controller). This originates from this
>     patch: I added the RC->ITS ID mapping + the ITS node and tested it.
> 
>     I don't know how to proceed to get the 2 features (vSMMU and vITS)
>     progress separately. Do you plan to respin this patch quickly?
>     Otherwise, if you are busy with other things I propose you to respin
>     fixing the few things above, splitting it into 3 patches, header [1],
>     ITS node creation [2], RC node creation with RC->ITS mapping [3] while
>     keeping credit to you on [1] and [3].
> 
>     Then we can have a 4th patch adding RC-> SMMU ID > ITS mapping?
> 
>     Please let me know what are your plans and what do you think.

OK thanks.

Looking forward to reviewing your patch

Best Regards

Eric
> 
>     Thanks
> 
>     Eric
>     >
>     >
> 
> 
> Hi Eric,
> I have been busy with something else, however I have a wokring patch set
> (unclean version)
> which creates the IORT tables, with SMMU->RC->ITS with ITS id mapping
> (routing all interrupts to single ITS).
> I'll push them by to unstable branch by this Sunday.
> 
> -- 
> Cheers,
> /Prem

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

* Re: [Qemu-devel] [PATCH v2 1/9] log: Add new IOMMU type
  2016-09-12 20:23     ` Prem Mallappa
@ 2016-09-25 14:58       ` Edgar E. Iglesias
  2016-09-26  6:54         ` Auger Eric
  0 siblings, 1 reply; 34+ messages in thread
From: Edgar E. Iglesias @ 2016-09-25 14:58 UTC (permalink / raw)
  To: Prem Mallappa; +Cc: Auger Eric, Peter Maydell, QEMU Developers

On Tue, Sep 13, 2016 at 01:53:39AM +0530, Prem Mallappa wrote:
> On Fri, Sep 9, 2016 at 9:06 PM, Auger Eric <eric.auger@redhat.com> wrote:
> 
> > Hi Prem,
> >
> > Missing commit message
> >
> > > Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
> > > ---
> > >  include/qemu/log.h | 1 +
> > >  util/log.c         | 2 ++
> > >  2 files changed, 3 insertions(+)
> > >
> > > diff --git a/include/qemu/log.h b/include/qemu/log.h
> > > index 234fa81..3dd2131 100644
> > > --- a/include/qemu/log.h
> > > +++ b/include/qemu/log.h
> > > @@ -42,6 +42,7 @@ static inline bool qemu_log_separate(void)
> > >  #define CPU_LOG_TB_NOCHAIN (1 << 13)
> > >  #define CPU_LOG_PAGE       (1 << 14)
> > >  #define LOG_TRACE          (1 << 15)
> > > +#define CPU_LOG_IOMMU      (1 << 16)
> > why is it prefixed with CPU_ ?
> > besides all arm gic devices seem to use LOG_GUEST_ERROR. what is the
> > rationale behind introducing a new enum?
> >
> 
> Will change this to LOG_GUEST_ERROR, if others on the list are okay.

Hi,

LOG_GUEST_ERROR is used for cases when the guest programs things in bad
way. E.g sets up a register in an invalid manner or writes to regs that
don't exist.

In this case we're logging information for valid translation steps, I
would prefer if we could use something else than LOG_GUEST_ERROR.
An IOMMU logging class sounds good to me.

Best regards,
Edgar

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

* Re: [Qemu-devel] [PATCH v2 3/9] hw: arm: SMMUv3 emulation model
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 3/9] hw: arm: SMMUv3 emulation model Prem Mallappa
@ 2016-09-25 16:37   ` Edgar E. Iglesias
  2016-09-26  5:27     ` Prem Mallappa
  0 siblings, 1 reply; 34+ messages in thread
From: Edgar E. Iglesias @ 2016-09-25 16:37 UTC (permalink / raw)
  To: Prem Mallappa; +Cc: Peter Maydell, qemu-devel

On Mon, Aug 22, 2016 at 09:47:34PM +0530, Prem Mallappa wrote:
> Big patch adds SMMUv3 model to Qemu
>   - As per SMMUv3 spec 16.0
>   - Works with SMMUv3 driver in Linux 4.7rc1
>   - Only LPAE mode translation supported
>   - BE mode is not supported yet
>   - Stage1, Stage2 and S1+S2
>   - Suspend/resume not tested

Thanks Prem,

I'm going to look at the PCI parts and get back to you with
comments on that.

I've put another round of comments inline:


> 
> Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
> ---
>  hw/arm/smmu-common.c     |  152 +++++
>  hw/arm/smmu-common.h     |  141 +++++
>  hw/arm/smmu-v3.c         | 1369 ++++++++++++++++++++++++++++++++++++++++++++++
>  hw/arm/smmuv3-internal.h |  432 +++++++++++++++
>  hw/vfio/common.c         |    2 +-
>  5 files changed, 2095 insertions(+), 1 deletion(-)
>  create mode 100644 hw/arm/smmu-common.c
>  create mode 100644 hw/arm/smmu-common.h
>  create mode 100644 hw/arm/smmu-v3.c
>  create mode 100644 hw/arm/smmuv3-internal.h
> 
> diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
> new file mode 100644
> index 0000000..bf2039b
> --- /dev/null
> +++ b/hw/arm/smmu-common.c
> @@ -0,0 +1,152 @@
> +/*
> + * Copyright (C) 2014-2016 Broadcom Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + *
> + * Author: Prem Mallappa <pmallapp@broadcom.com>
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "sysemu/sysemu.h"
> +#include "exec/address-spaces.h"
> +
> +#include "smmu-common.h"
> +
> +inline MemTxResult smmu_read_sysmem(hwaddr addr, void *buf, int len,
> +                                    bool secure)
> +{
> +    MemTxAttrs attrs = {.unspecified = 1, .secure = secure};
> +
> +    switch (len) {
> +    case 4:
> +        *(uint32_t *)buf = ldl_le_phys(&address_space_memory, addr);
> +        break;
> +    case 8:
> +        *(uint64_t *)buf = ldq_le_phys(&address_space_memory, addr);
> +        break;
> +    default:
> +        return address_space_rw(&address_space_memory, addr,
> +                                attrs, buf, len, false);
> +    }
> +    return MEMTX_OK;
> +}
> +
> +inline void
> +smmu_write_sysmem(hwaddr addr, void *buf, int len, bool secure)
> +{
> +    MemTxAttrs attrs = {.unspecified = 1, .secure = secure};
> +
> +    switch (len) {
> +    case 4:
> +        stl_le_phys(&address_space_memory, addr, *(uint32_t *)buf);
> +        break;
> +    case 8:
> +        stq_le_phys(&address_space_memory, addr, *(uint64_t *)buf);
> +        break;
> +    default:
> +        address_space_rw(&address_space_memory, addr,
> +                         attrs, buf, len, true);
> +    }
> +}

Thinking about this, I think you should just remove these functions and
always call dma_memory_read/write directly.

It would be nice if you could add a property/link so that machine code
can specify the MemoryRegion/address space to be used. You'll need a
link to allow setup of the MemoryRegion and also some code to create
an address space from the selected MR.

You can have a look at the following code to see how it's done:
exec.c cpu_exec_init()    see object_property_add_link
cpus.c qemu_init_vcpu()   see address_space_init_shareable



> +
> +SMMUTransErr
> +smmu_translate_64(SMMUTransCfg *cfg, uint32_t *pagesize,
> +                  uint32_t *perm, bool is_write)
> +{
> +    int     ret, level;
> +    int     stage      = cfg->stage;
> +    int     granule_sz = cfg->granule_sz[stage];
> +    int     va_size    = cfg->va_size[stage];
> +    hwaddr  va, addr, mask;
> +    hwaddr *outaddr;
> +
> +
> +    va = addr = cfg->va;        /* or ipa in Stage2 */
> +    SMMU_DPRINTF(TT_1, "stage:%d\n", stage);
> +    assert(va_size == 64);      /* We dont support 32-bit yet */
> +    /* same location, for clearity */
> +    outaddr = &cfg->pa;
> +
> +    level = 4 - (va_size - cfg->tsz[stage] - 4) / granule_sz;
> +
> +    mask = (1ULL << (granule_sz + 3)) - 1;
> +
> +    addr = extract64(cfg->ttbr[stage], 0, 48);
> +    addr &= ~((1ULL << (va_size - cfg->tsz[stage] -
> +                        (granule_sz * (4 - level)))) - 1);
> +
> +    for (;;) {
> +        uint64_t desc;
> +#ifdef ARM_SMMU_DEBUG
> +        uint64_t ored = (va >> (granule_sz * (4 - level))) & mask;
> +        SMMU_DPRINTF(TT_1,
> +                     "Level: %d va:%lx addr:%lx ored:%lx\n",
> +                     level, va, addr, ored);
> +#endif
> +        addr |= (va >> (granule_sz * (4 - level))) & mask;
> +        addr &= ~7ULL;
> +
> +        if (smmu_read_sysmem(addr, &desc, sizeof(desc), false)) {
> +            ret = SMMU_TRANS_ERR_WALK_EXT_ABRT;
> +            SMMU_DPRINTF(CRIT, "Translation table read error lvl:%d\n", level);
> +            break;
> +        }
> +
> +        SMMU_DPRINTF(TT_1,
> +                     "Level: %d gran_sz:%d mask:%lx addr:%lx desc:%lx\n",
> +                     level, granule_sz, mask, addr, desc);
> +
> +        if (!(desc & 1) ||
> +            (!(desc & 2) && (level == 3))) {
> +            ret = SMMU_TRANS_ERR_TRANS;
> +            break;
> +        }
> +
> +        /* We call again to resolve address at this 'level' */
> +        if (cfg->s2_needed) {
> +            uint32_t perm_s2, pagesize_s2;
> +            SMMUTransCfg s2cfg = *cfg;
> +
> +            s2cfg.stage++;
> +            s2cfg.va = desc;
> +            s2cfg.s2_needed = false;
> +
> +            ret = smmu_translate_64(&s2cfg, &pagesize_s2,
> +                                    &perm_s2, is_write);
> +            if (ret) {
> +                break;
> +            }
> +
> +            desc = (uint64_t)s2cfg.pa;
> +            SMMU_DPRINTF(TT_2, "addr:%lx pagesize:%x\n", addr, *pagesize);
> +        }
> +
> +        addr = desc & 0xffffffff000ULL;
> +        if ((desc & 2) && (level < 3)) {
> +            level++;
> +            continue;
> +        }
> +        *pagesize = (1ULL << ((granule_sz * (4 - level)) + 3));
> +        addr |= (va & (*pagesize - 1));
> +        SMMU_DPRINTF(TT_1, "addr:%lx pagesize:%x\n", addr, *pagesize);
> +        break;
> +    }
> +
> +    if (ret == 0) {
> +        *outaddr = addr;
> +    }
> +
> +    return ret;
> +}
> diff --git a/hw/arm/smmu-common.h b/hw/arm/smmu-common.h
> new file mode 100644
> index 0000000..91f7194
> --- /dev/null
> +++ b/hw/arm/smmu-common.h
> @@ -0,0 +1,141 @@
> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + *
> + * Copyright (C) 2015-2016 Broadcom Corporation
> + *
> + * Author: Prem Mallappa <pmallapp@broadcom.com>
> + *
> + */
> +#ifndef HW_ARM_SMMU_COMMON_H
> +#define HW_ARM_SMMU_COMMON_H
> +
> +#include <qemu/log.h>
> +#include <hw/sysbus.h>
> +
> +#define TYPE_SMMU_DEV_BASE "smmu-base"
> +#define TYPE_SMMU_V3_DEV   "smmuv3"
> +
> +typedef struct SMMUState {
> +    /* <private> */
> +    SysBusDevice  dev;
> +
> +    uint32_t cid[4];            /* Coresight registers */
> +    uint32_t pid[8];
> +
> +    MemoryRegion iomem;
> +} SMMUState;
> +
> +#define SMMU_SYS_DEV(obj) OBJECT_CHECK(SMMUState, (obj), TYPE_SMMU_DEV_BASE)
> +
> +typedef enum {
> +    SMMU_TRANS_ERR_WALK_EXT_ABRT = 0x1,  /* Translation walk external abort */
> +    SMMU_TRANS_ERR_TRANS         = 0x10, /* Translation fault */
> +    SMMU_TRANS_ERR_ADDR_SZ,              /* Address Size fault */
> +    SMMU_TRANS_ERR_ACCESS,               /* Access fault */
> +    SMMU_TRANS_ERR_PERM,                 /* Permission fault */
> +    SMMU_TRANS_ERR_TLB_CONFLICT  = 0x20, /* TLB Conflict */
> +} SMMUTransErr;
> +
> +
> +/*
> + * This needs to be populated by SMMUv2 and SMMUv3
> + * each do it in their own way
> + * translate functions use it to call translations
> + */
> +typedef struct SMMUTransCfg {
> +    hwaddr   va;                        /* Input to S1 */
> +    int      stage;
> +    uint32_t oas[3];
> +    uint32_t tsz[3];
> +    uint64_t ttbr[3];
> +    uint32_t granule[3];
> +    uint32_t va_size[3];
> +    uint32_t granule_sz[3];
> +
> +    hwaddr pa;                          /* Output from S1, Final PA */
> +    bool    s2_needed;
> +} SMMUTransCfg;
> +
> +struct SMMUTransReq {
> +    uint32_t stage;
> +    SMMUTransCfg cfg[2];
> +};
> +
> +typedef struct {
> +    /* <private> */
> +    SysBusDeviceClass parent_class;
> +
> +    /* public */
> +    SMMUTransErr (*translate_32)(SMMUTransCfg *cfg, uint32_t *pagesize,
> +                                 uint32_t *perm, bool is_write);
> +    SMMUTransErr (*translate_64)(SMMUTransCfg *cfg, uint32_t *pagesize,
> +                                 uint32_t *perm, bool is_write);
> +} SMMUBaseClass;
> +
> +#define SMMU_DEVICE_GET_CLASS(obj)                              \
> +    OBJECT_GET_CLASS(SMMUBaseClass, (obj), TYPE_SMMU_DEV_BASE)
> +
> +/* #define ARM_SMMU_DEBUG */
> +#ifdef ARM_SMMU_DEBUG
> +
> +extern uint32_t  dbg_bits;
> +
> +#define HERE()  printf("%s:%d\n", __func__, __LINE__)

Can you please remove HERE?


> +
> +enum {
> +    SMMU_DBG_PANIC, SMMU_DBG_CRIT, SMMU_DBG_WARN, /* error level */
> +    SMMU_DBG_DBG1, SMMU_DBG_DBG2, SMMU_DBG_INFO, /* info level */
> +    SMMU_DBG_CMDQ,                               /* Just command queue */
> +    SMMU_DBG_STE, SMMU_DBG_CD,                   /* Specific parts STE/CD */
> +    SMMU_DBG_TT_1, SMMU_DBG_TT_2,                /* Translation Stage 1/2 */
> +    SMMU_DBG_IRQ,                                /* IRQ  */
> +};
> +
> +#define DBG_BIT(bit)    (1 << SMMU_DBG_##bit)
> +
> +#define IS_DBG_ENABLED(bit) (dbg_bits & (1 << SMMU_DBG_##bit))
> +
> +#define DBG_DEFAULT  (DBG_BIT(PANIC) | DBG_BIT(CRIT) | DBG_BIT(IRQ))
> +#define DBG_EXTRA    (DBG_BIT(STE) | DBG_BIT(CD) | DBG_BIT(TT_1))
> +#define DBG_VERBOSE1 DBG_BIT(DBG1)
> +#define DBG_VERBOSE2 (DBG_VERBOSE1 | DBG_BIT(DBG1))
> +#define DBG_VERBOSE3 (DBG_VERBOSE2 | DBG_BIT(DBG2))
> +#define DBG_VERBOSE4 (DBG_VERBOSE3 | DBG_BIT(INFO))
> +
> +#define SMMU_DPRINTF(lvl, fmt, ...)             \
> +    do {                                        \
> +        if (dbg_bits & DBG_BIT(lvl)) {          \
> +            qemu_log_mask(CPU_LOG_IOMMU,        \
> +                          "(smmu)%s: " fmt ,    \
> +                          __func__,             \
> +                          ## __VA_ARGS__);      \
> +        }                                       \
> +    } while (0)
> +
> +#else
> +#define IS_DBG_ENABLED(bit) false
> +#define SMMU_DPRINTF(lvl, fmt, ...)
> +
> +#endif  /* SMMU_DEBUG */
> +
> +SMMUTransErr smmu_translate_64(SMMUTransCfg *cfg, uint32_t *pagesize,
> +                               uint32_t *perm, bool is_write);
> +
> +SMMUTransErr smmu_translate_32(SMMUTransCfg *cfg, uint32_t *pagesize,
> +                               uint32_t *perm, bool is_write);
> +
> +MemTxResult smmu_read_sysmem(hwaddr addr, void *buf, int len, bool secure);
> +void smmu_write_sysmem(hwaddr addr, void *buf, int len, bool secure);
> +
> +#endif  /* HW_ARM_SMMU_COMMON */
> diff --git a/hw/arm/smmu-v3.c b/hw/arm/smmu-v3.c
> new file mode 100644
> index 0000000..7260468
> --- /dev/null
> +++ b/hw/arm/smmu-v3.c
> @@ -0,0 +1,1369 @@
> +/*
> + * Copyright (C) 2014-2016 Broadcom Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + *
> + * Author: Prem Mallappa <pmallapp@broadcom.com>
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/boards.h"
> +#include "sysemu/sysemu.h"
> +#include "hw/sysbus.h"
> +#include "hw/pci/pci.h"
> +#include "exec/address-spaces.h"
> +
> +#include "hw/arm/smmu.h"
> +#include "smmu-common.h"
> +#include "smmuv3-internal.h"
> +
> +#define SMMU_NREGS       0x200
> +#define PCI_BUS_MAX      256
> +#define PCI_DEVFN_MAX    256
> +
> +#ifdef ARM_SMMU_DEBUG
> +uint32_t dbg_bits =                             \
> +    DBG_DEFAULT |                               \
> +    DBG_VERBOSE3 |                              \
> +    DBG_EXTRA |                                 \
> +    DBG_VERBOSE1;
> +#else
> +const uint32_t dbg_bits;
> +#endif
> +
> +typedef struct SMMUDevice SMMUDevice;
> +
> +struct SMMUDevice {
> +    void         *smmu;
> +    PCIBus       *bus;
> +    int           devfn;
> +    MemoryRegion  iommu;
> +    AddressSpace  as;
> +};
> +
> +typedef struct SMMUPciBus SMMUPciBus;
> +struct SMMUPciBus {
> +    PCIBus       *bus;
> +    SMMUDevice   *pbdev[0]; /* Parent array is sparse, so dynamically alloc */
> +};
> +
> +typedef struct SMMUV3State SMMUV3State;
> +
> +struct SMMUV3State {
> +    SMMUState     smmu_state;
> +
> +#define SMMU_FEATURE_2LVL_STE (1 << 0)
> +    /* Local cache of most-frequently used register */
> +    uint32_t     features;
> +    uint16_t     sid_size;
> +    uint16_t     sid_split;
> +    uint64_t     strtab_base;
> +
> +    uint64_t    regs[SMMU_NREGS];
> +
> +    qemu_irq     irq[4];
> +
> +    SMMUQueue    cmdq, evtq, priq;
> +
> +    /* IOMMU Address space */
> +    MemoryRegion iommu;
> +    AddressSpace iommu_as;
> +    /*
> +     * Bus number is not populated in the beginning, hence we need
> +     * a mechanism to retrieve the corresponding address space for each
> +     * pci device.
> +    */
> +    GHashTable   *smmu_as_by_busptr;
> +};
> +
> +#define SMMU_V3_DEV(obj) OBJECT_CHECK(SMMUV3State, (obj), TYPE_SMMU_V3_DEV)
> +
> +static void smmu_write64_reg(SMMUV3State *s, uint32_t addr, uint64_t val)
> +{
> +    addr >>= 2;
> +    s->regs[addr] = val & 0xFFFFFFFFULL;
> +    s->regs[addr + 1] = val & ~0xFFFFFFFFULL;
> +}
> +
> +static void smmu_write_reg(SMMUV3State *s, uint32_t addr, uint64_t val)
> +{
> +    s->regs[addr >> 2] = val;
> +}
> +
> +static inline uint32_t smmu_read_reg(SMMUV3State *s, uint32_t addr)
> +{
> +    return s->regs[addr >> 2];
> +}
> +
> +static inline uint64_t smmu_read64_reg(SMMUV3State *s, uint32_t addr)
> +{
> +    addr >>= 2;
> +    return s->regs[addr] | (s->regs[addr + 1] << 32);
> +}
> +
> +#define smmu_read32_reg smmu_read_reg
> +#define smmu_write32_reg smmu_write_reg
> +
> +static inline int smmu_enabled(SMMUV3State *s)
> +{
> +    return (smmu_read32_reg(s, SMMU_REG_CR0) & SMMU_CR0_SMMU_ENABLE) != 0;
> +}
> +
> +typedef enum {
> +    CMD_Q_EMPTY,
> +    CMD_Q_FULL,
> +    CMD_Q_INUSE,
> +} SMMUQStatus;
> +
> +static inline SMMUQStatus
> +__smmu_queue_status(SMMUV3State *s, SMMUQueue *q)
> +{
> +    uint32_t prod = Q_IDX(q, q->prod), cons = Q_IDX(q, q->cons);
> +    if ((prod == cons) && (q->wrap.prod != q->wrap.cons)) {
> +        return CMD_Q_FULL;
> +    } else if ((prod == cons) && (q->wrap.prod == q->wrap.cons)) {
> +        return CMD_Q_EMPTY;
> +    }
> +    return CMD_Q_INUSE;
> +}
> +#define smmu_is_q_full(s, q) (__smmu_queue_status(s, q) == CMD_Q_FULL)
> +#define smmu_is_q_empty(s, q) (__smmu_queue_status(s, q) == CMD_Q_EMPTY)
> +
> +static int __smmu_q_enabled(SMMUV3State *s, uint32_t q)
> +{
> +    return smmu_read32_reg(s, SMMU_REG_CR0) & q;
> +}
> +#define smmu_cmd_q_enabled(s) __smmu_q_enabled(s, SMMU_CR0_CMDQ_ENABLE)
> +#define smmu_evt_q_enabled(s) __smmu_q_enabled(s, SMMU_CR0_EVTQ_ENABLE)
> +
> +static inline int __smmu_irq_enabled(SMMUV3State *s, uint32_t q)
> +{
> +    return smmu_read64_reg(s, SMMU_REG_IRQ_CTRL) & q;
> +}
> +#define smmu_evt_irq_enabled(s)                   \
> +    __smmu_irq_enabled(s, SMMU_IRQ_CTRL_EVENT_EN)
> +#define smmu_gerror_irq_enabled(s)                  \
> +    __smmu_irq_enabled(s, SMMU_IRQ_CTRL_GERROR_EN)
> +#define smmu_pri_irq_enabled(s)                 \
> +    __smmu_irq_enabled(s, SMMU_IRQ_CTRL_PRI_EN)

Please drop the __ prefix on functions. _ prefixed functions are reserved and
we usually avoid them.

I don't think smmu_evt_irq_enabled() is very useful,
smmu_irq_enabled(s, SMMU_IRQ_CTRL_EVENT_EN) is readable enough.







> +
> +
> +static inline int is_cd_valid(SMMUV3State *s, Ste *ste, Cd *cd)
> +{
> +    return CD_VALID(cd);
> +}
> +
> +static inline int is_ste_valid(SMMUV3State *s, Ste *ste)
> +{
> +    return STE_VALID(ste);
> +}
> +
> +static inline int is_ste_bypass(SMMUV3State *s, Ste *ste)
> +{
> +    return STE_CONFIG(ste) == STE_CONFIG_BYPASS;
> +}
> +
> +static inline uint16_t smmu_get_sid(SMMUDevice *sdev)
> +{
> +    return  ((pci_bus_num(sdev->bus) & 0xff) << 8) | sdev->devfn;
> +}
> +
> +static void smmu_coresight_regs_init(SMMUV3State *sv3)
> +{
> +    SMMUState *s = SMMU_SYS_DEV(sv3);
> +    int i;
> +
> +    /* Primecell ID registers */
> +    s->cid[0] = 0x0D;
> +    s->cid[1] = 0xF0;
> +    s->cid[2] = 0x05;
> +    s->cid[3] = 0xB1;
> +
> +    for (i = 0; i < ARRAY_SIZE(s->pid); i++) {
> +        s->pid[i] = 0x1;
> +    }
> +}
> +
> +/*
> + * smmu_irq_update:
> + * update corresponding register,
> + * return > 0 when IRQ is supposed to be rased
> + * Spec req:
> + *      - Raise irq only when it not active already,
> + *        blindly toggling bits may actually clear the error
> + */
> +static int
> +smmu_irq_update(SMMUV3State *s, int irq, uint64_t data)
> +{
> +    uint32_t error = 0;
> +
> +    switch (irq) {
> +    case SMMU_IRQ_EVTQ:
> +        if (smmu_evt_irq_enabled(s)) {
> +            error = SMMU_GERROR_EVENTQ;
> +        }
> +        break;
> +    case SMMU_IRQ_CMD_SYNC:
> +        if (smmu_gerror_irq_enabled(s)) {
> +            uint32_t err_type = (uint32_t)data;
> +            if (err_type) {
> +                uint32_t regval = smmu_read32_reg(s, SMMU_REG_CMDQ_CONS);
> +                smmu_write32_reg(s, SMMU_REG_CMDQ_CONS,
> +                                 regval | err_type << SMMU_CMD_CONS_ERR_SHIFT);
> +            }
> +            error = SMMU_GERROR_CMDQ;
> +        }
> +        break;
> +    case SMMU_IRQ_PRIQ:
> +        if (smmu_pri_irq_enabled(s)) {
> +            error = SMMU_GERROR_PRIQ;
> +        }
> +        break;
> +    }
> +    SMMU_DPRINTF(IRQ, "<< error:%x\n", error);
> +
> +    if (error && smmu_gerror_irq_enabled(s)) {
> +        uint32_t gerror = smmu_read32_reg(s, SMMU_REG_GERROR);
> +        uint32_t gerrorn = smmu_read32_reg(s, SMMU_REG_GERRORN);
> +        SMMU_DPRINTF(IRQ, "<<<< error:%x gerror:%x gerrorn:%x\n",
> +                     error, gerror, gerrorn);
> +        if (!((gerror ^ gerrorn) & error)) {
> +            smmu_write32_reg(s, SMMU_REG_GERROR, gerror ^ error);
> +        }
> +    }
> +
> +    return error;
> +}
> +
> +static void smmu_irq_raise(SMMUV3State *s, int irq, uint64_t data)
> +{
> +    SMMU_DPRINTF(IRQ, "irq:%d\n", irq);
> +    if (smmu_irq_update(s, irq, data)) {
> +            qemu_irq_raise(s->irq[irq]);
> +    }
> +}
> +
> +static MemTxResult smmu_q_read(SMMUV3State *s, SMMUQueue *q, void *data)
> +{
> +    uint64_t addr = Q_ENTRY(q, Q_IDX(q, q->cons));
> +
> +    q->cons++;
> +    if (q->cons == q->entries) {
> +        q->cons = 0;
> +        q->wrap.cons++;     /* this will toggle */
> +    }
> +
> +    return smmu_read_sysmem(addr, data, q->ent_size, false);
> +}
> +
> +static MemTxResult smmu_q_write(SMMUV3State *s, SMMUQueue *q, void *data)
> +{
> +    uint64_t addr = Q_ENTRY(q, Q_IDX(q, q->prod));
> +
> +    if (q->prod == q->entries) {
> +        q->prod = 0;
> +        q->wrap.prod++;     /* this will toggle */
> +    }
> +
> +    q->prod++;
> +
> +    smmu_write_sysmem(addr, data, q->ent_size, false);
> +
> +    return MEMTX_OK;
> +}
> +
> +static MemTxResult smmu_read_cmdq(SMMUV3State *s, Cmd *cmd)
> +{
> +    SMMUQueue *q = &s->cmdq;
> +    MemTxResult ret = smmu_q_read(s, q, cmd);
> +    uint32_t val = 0;
> +
> +    val |= (q->wrap.cons << q->shift) | q->cons;
> +
> +    /* Update consumer pointer */
> +    smmu_write32_reg(s, SMMU_REG_CMDQ_CONS, val);
> +
> +    return ret;
> +}
> +
> +#define SMMU_CMDQ_ERR(s) ((smmu_read32_reg(s, SMMU_REG_GERROR) ^    \
> +                           smmu_read32_reg(s, SMMU_REG_GERRORN)) &  \
> +                          SMMU_GERROR_CMDQ)
> +
> +static int smmu_cmdq_consume(SMMUV3State *s)
> +{
> +    uint32_t error = SMMU_CMD_ERR_NONE;
> +
> +    SMMU_DPRINTF(CMDQ, "CMDQ_ERR: %d\n", SMMU_CMDQ_ERR(s));
> +
> +    if (!smmu_cmd_q_enabled(s))
> +        goto out_while;
> +
> +    while (!SMMU_CMDQ_ERR(s) && !smmu_is_q_empty(s, &s->cmdq)) {
> +        Cmd cmd;
> +#ifdef ARM_SMMU_DEBUG
> +        SMMUQueue *q = &s->cmdq;
> +#endif
> +        if (smmu_read_cmdq(s, &cmd) != MEMTX_OK) {
> +            error = SMMU_CMD_ERR_ABORT;
> +            goto out_while;
> +        }
> +
> +        SMMU_DPRINTF(DBG2, "CMDQ base: %lx cons:%d prod:%d val:%x wrap:%d\n",
> +                     q->base, q->cons, q->prod, cmd.word[0], q->wrap.cons);
> +
> +        switch (CMD_TYPE(&cmd)) {
> +        case SMMU_CMD_CFGI_STE:
> +        case SMMU_CMD_CFGI_STE_RANGE:
> +            break;
> +        case SMMU_CMD_TLBI_NSNH_ALL: /* TLB not implemented */
> +        case SMMU_CMD_TLBI_EL2_ALL:  /* Fallthrough */
> +        case SMMU_CMD_TLBI_EL3_ALL:
> +        case SMMU_CMD_TLBI_NH_ALL:
> +        case SMMU_CMD_TLBI_S2_IPA:
> +            break;
> +        case SMMU_CMD_SYNC:     /* Fallthrough */
> +            if (CMD_CS(&cmd) & CMD_SYNC_SIG_IRQ) {
> +                smmu_irq_raise(s, SMMU_IRQ_CMD_SYNC, SMMU_CMD_ERR_NONE);
> +            }
> +            break;
> +        case SMMU_CMD_PREFETCH_CONFIG:
> +            break;
> +        case SMMU_CMD_TLBI_NH_ASID:
> +        case SMMU_CMD_TLBI_NH_VA:   /* too many of this is sent */
> +            break;
> +
> +        default:
> +            error = SMMU_CMD_ERR_ILLEGAL;
> +            SMMU_DPRINTF(CRIT, "Unknown Command type: %x, ignoring\n",
> +                         CMD_TYPE(&cmd));
> +            if (IS_DBG_ENABLED(CD)) {
> +                dump_cmd(&cmd);
> +            }
> +            break;
> +        }
> +
> +        if (error != SMMU_CMD_ERR_NONE) {
> +            SMMU_DPRINTF(INFO, "CMD Error\n");
> +            goto out_while;
> +        }
> +    }
> +
> +out_while:
> +    if (error) {
> +        smmu_irq_raise(s, SMMU_IRQ_GERROR, error);
> +    }
> +
> +    SMMU_DPRINTF(CMDQ, "prod_wrap:%d, prod:%x cons_wrap:%d cons:%x\n",
> +                 s->cmdq.wrap.prod, s->cmdq.prod,
> +                 s->cmdq.wrap.cons, s->cmdq.cons);
> +
> +    return 0;
> +}
> +
> +static inline bool
> +smmu_is_irq_pending(SMMUV3State *s, int irq)
> +{
> +    return smmu_read32_reg(s, SMMU_REG_GERROR) ^
> +        smmu_read32_reg(s, SMMU_REG_GERRORN);
> +}
> +
> +/*
> + * GERROR is updated when rasing an interrupt, GERRORN will be updated
> + * by s/w and should match GERROR before normal operation resumes.
> + */
> +static void smmu_irq_clear(SMMUV3State *s, uint64_t gerrorn)
> +{
> +    int irq = SMMU_IRQ_GERROR;
> +    uint32_t toggled;
> +
> +    toggled = smmu_read32_reg(s, SMMU_REG_GERRORN) ^ gerrorn;
> +
> +    while (toggled) {
> +        irq = ctz32(toggled);
> +
> +        qemu_irq_lower(s->irq[irq]);
> +
> +        toggled &= toggled - 1;
> +    }
> +}
> +
> +static int smmu_evtq_update(SMMUV3State *s)
> +{
> +    if (!smmu_enabled(s)) {
> +        return 0;
> +    }
> +
> +    if (!smmu_is_q_empty(s, &s->evtq)) {
> +        if (smmu_evt_irq_enabled(s))
> +            smmu_irq_raise(s, SMMU_IRQ_EVTQ, 0);
> +    }
> +
> +    if (smmu_is_q_empty(s, &s->evtq)) {
> +        smmu_irq_clear(s, SMMU_GERROR_EVENTQ);
> +    }
> +
> +    return 1;
> +}
> +
> +static void smmu_create_event(SMMUV3State *s, hwaddr iova,
> +                              uint32_t sid, bool is_write, int error);
> +
> +static void smmu_update(SMMUV3State *s)
> +{
> +    int error = 0;
> +
> +    /* SMMU starts processing commands even when not enabled */
> +    if (!smmu_enabled(s)) {
> +        goto check_cmdq;
> +    }
> +
> +    /* EVENT Q updates takes more priority */
> +    if ((smmu_evt_q_enabled(s)) && !smmu_is_q_empty(s, &s->evtq)) {
> +        SMMU_DPRINTF(CRIT, "q empty:%d prod:%d cons:%d p.wrap:%d p.cons:%d\n",
> +                     smmu_is_q_empty(s, &s->evtq), s->evtq.prod,
> +                     s->evtq.cons, s->evtq.wrap.prod, s->evtq.wrap.cons);
> +        error = smmu_evtq_update(s);
> +    }
> +
> +    if (error) {
> +        /* TODO: May be in future we create proper event queue entry */
> +        /* an error condition is not a recoverable event, like other devices */
> +        SMMU_DPRINTF(CRIT, "An unfavourable condition\n");
> +        smmu_create_event(s, 0, 0, 0, error);
> +    }
> +
> +check_cmdq:
> +    if (smmu_cmd_q_enabled(s) && !SMMU_CMDQ_ERR(s)) {
> +        smmu_cmdq_consume(s);
> +    } else {
> +        SMMU_DPRINTF(INFO, "cmdq not enabled or error :%x\n", SMMU_CMDQ_ERR(s));
> +    }
> +
> +}
> +
> +static void smmu_update_irq(SMMUV3State *s, uint64_t addr, uint64_t val)
> +{
> +    smmu_irq_clear(s, val);
> +
> +    smmu_write32_reg(s, SMMU_REG_GERRORN, val);
> +
> +    SMMU_DPRINTF(IRQ, "irq pend: %d gerror:%x gerrorn:%x\n",
> +                 smmu_is_irq_pending(s, 0),
> +                 smmu_read32_reg(s, SMMU_REG_GERROR),
> +                 smmu_read32_reg(s, SMMU_REG_GERRORN));
> +
> +    /* Clear only when no more left */
> +    if (!smmu_is_irq_pending(s, 0)) {
> +        qemu_irq_lower(s->irq[0]);
> +    }
> +}
> +
> +#define SMMU_ID_REG_INIT(s, reg, d) do {        \
> +    s->regs[reg >> 2] = d;                      \
> +    } while (0)
> +
> +static void smmuv3_id_reg_init(SMMUV3State *s)
> +{
> +    uint32_t data =
> +        1 << 27 |                   /* 2 Level stream id */
> +        1 << 26 |                   /* Term Model  */
> +        1 << 24 |                   /* Stall model not supported */
> +        1 << 18 |                   /* VMID 16 bits */
> +        1 << 16 |                   /* PRI */
> +        1 << 12 |                   /* ASID 16 bits */
> +        1 << 10 |                   /* ATS */
> +        1 << 9 |                    /* HYP */
> +        2 << 6 |                    /* HTTU */
> +        1 << 4 |                    /* COHACC */
> +        2 << 2 |                    /* TTF=Arch64 */
> +        1 << 1 |                    /* Stage 1 */
> +        1 << 0;                     /* Stage 2 */
> +
> +    SMMU_ID_REG_INIT(s, SMMU_REG_IDR0, data);
> +
> +#define SMMU_SID_SIZE         16
> +#define SMMU_QUEUE_SIZE_LOG2  19
> +    data =
> +        1 << 27 |                    /* Attr Types override */
> +        SMMU_QUEUE_SIZE_LOG2 << 21 | /* Cmd Q size */
> +        SMMU_QUEUE_SIZE_LOG2 << 16 | /* Event Q size */
> +        SMMU_QUEUE_SIZE_LOG2 << 11 | /* PRI Q size */
> +        0  << 6 |                    /* SSID not supported */
> +        SMMU_SID_SIZE << 0 ;         /* SID size  */
> +
> +    SMMU_ID_REG_INIT(s, SMMU_REG_IDR1, data);
> +
> +    data =
> +        1 << 6 |                    /* Granule 64K */
> +        1 << 4 |                    /* Granule 4K */
> +        4 << 0;                     /* OAS = 44 bits */
> +
> +    SMMU_ID_REG_INIT(s, SMMU_REG_IDR5, data);
> +
> +}
> +
> +static void smmuv3_init(SMMUV3State *s)
> +{
> +    smmu_coresight_regs_init(s);
> +
> +    smmuv3_id_reg_init(s);      /* Update ID regs alone */
> +
> +    s->sid_size = SMMU_SID_SIZE;
> +
> +    s->cmdq.entries = (smmu_read32_reg(s, SMMU_REG_IDR1) >> 21) & 0x1f;
> +    s->cmdq.ent_size = sizeof(Cmd);
> +    s->evtq.entries = (smmu_read32_reg(s, SMMU_REG_IDR1) >> 16) & 0x1f;
> +    s->evtq.ent_size = sizeof(Evt);
> +}
> +
> +/*
> + * All SMMU data structures are little endian, and are aligned to 8 bytes
> + * L1STE/STE/L1CD/CD, Queue entries in CMDQ/EVTQ/PRIQ
> + */
> +static inline int smmu_get_ste(SMMUV3State *s, hwaddr addr, Ste *buf)
> +{
> +    return dma_memory_read(&address_space_memory, addr, buf, sizeof(*buf));
> +}
> +
> +/*
> + * For now we only support CD with a single entry, 'ssid' is used to identify
> + * otherwise
> + */
> +static inline int smmu_get_cd(SMMUV3State *s, Ste *ste, uint32_t ssid, Cd *buf)
> +{
> +    hwaddr addr = STE_CTXPTR(ste);
> +
> +    if (STE_S1CDMAX(ste) != 0) {
> +        SMMU_DPRINTF(CRIT, "Multilevel Ctx Descriptor not supported yet\n");
> +    }
> +
> +    return dma_memory_read(&address_space_memory, addr, buf, sizeof(*buf));
> +}
> +
> +static int
> +is_ste_consistent(SMMUV3State *s, Ste *ste)
> +{
> +    uint32_t _config = STE_CONFIG(ste) & 0x7,
> +        idr0 = smmu_read32_reg(s, SMMU_REG_IDR0),
> +        idr5 = smmu_read32_reg(s, SMMU_REG_IDR5);
> +
> +    uint32_t httu = extract32(idr0, 6, 2);
> +    bool config[] = {_config & 0x1,
> +                     _config & 0x2,
> +                     _config & 0x3};
> +    bool granule_supported;
> +
> +    bool s1p = idr0 & SMMU_IDR0_S1P,
> +        s2p = idr0 & SMMU_IDR0_S2P,
> +        hyp = idr0 & SMMU_IDR0_HYP,
> +        cd2l = idr0 & SMMU_IDR0_CD2L,
> +        idr0_vmid = idr0 & SMMU_IDR0_VMID16,
> +        ats = idr0 & SMMU_IDR0_ATS,
> +        ttf0 = (idr0 >> 2) & 0x1,
> +        ttf1 = (idr0 >> 3) & 0x1;
> +
> +    int ssidsz = (smmu_read32_reg(s, SMMU_REG_IDR1) >> 6) & 0x1f;
> +
> +    uint32_t ste_vmid = STE_S2VMID(ste),
> +        ste_eats = STE_EATS(ste),
> +        ste_s2s = STE_S2S(ste),
> +        ste_s1fmt = STE_S1FMT(ste),
> +        aa64 = STE_S2AA64(ste),
> +        ste_s1cdmax = STE_S1CDMAX(ste);
> +
> +    uint8_t ste_strw = STE_STRW(ste);
> +    uint64_t oas, max_pa;
> +    bool strw_ign;
> +    bool addr_out_of_range;
> +
> +    if (!STE_VALID(ste)) {
> +        SMMU_DPRINTF(STE, "STE NOT valid\n");
> +        return false;
> +    }
> +
> +    switch (STE_S2TG(ste)) {
> +    case 1:
> +        granule_supported = 0x4; break;
> +    case 2:
> +        granule_supported = 0x2; break;
> +    case 0:
> +        granule_supported = 0x1; break;
> +    }
> +    granule_supported &= (idr5 >> 4);
> +
> +    if (!config[2]) {
> +        if ((!s1p && config[0]) ||
> +            (!s2p && config[1]) ||
> +            (s2p && config[1])) {
> +            SMMU_DPRINTF(STE, "STE inconsistant, S2P mismatch\n");
> +            return false;
> +        }
> +        if (!ssidsz && ste_s1cdmax && config[0] && !cd2l &&
> +            (ste_s1fmt == 1 || ste_s1fmt == 2)) {
> +            SMMU_DPRINTF(STE, "STE inconsistant, CD mismatch\n");
> +            return false;
> +        }
> +        if (ats && ((_config & 0x3) == 0) &&
> +            ((ste_eats == 2 && (_config != 0x7 || ste_s2s)) ||
> +             (ste_eats == 1 && !ste_s2s))) {
> +            SMMU_DPRINTF(STE, "STE inconsistant, EATS/S2S mismatch\n");
> +            return false;
> +        }
> +        if (config[0] && (ssidsz && (ste_s1cdmax > ssidsz))) {
> +            SMMU_DPRINTF(STE, "STE inconsistant, SSID out of range\n");
> +            return false;
> +        }
> +    }
> +
> +    oas = MIN(STE_S2PS(ste), idr5 & 0x7);
> +
> +    if (oas == 3) {
> +        max_pa = deposit64(0, 0, 42, ~0UL);
> +    } else {
> +        max_pa = deposit64(0, 0, (32 + (oas * 4)), ~0UL);
> +    }
> +
> +    strw_ign = (!s1p || !hyp || (_config == 4));
> +
> +    addr_out_of_range = (int64_t)(max_pa - STE_S2TTB(ste)) < 0;
> +
> +    if (config[1] && (
> +        (aa64 && !granule_supported) ||
> +        (!aa64 && !ttf0) ||
> +        (aa64 && !ttf1)  ||
> +        ((STE_S2HA(ste) || STE_S2HD(ste)) && !aa64) ||
> +        ((STE_S2HA(ste) || STE_S2HD(ste)) && !httu) ||
> +        (STE_S2HD(ste) && (httu == 1)) ||
> +        addr_out_of_range)) {
> +        SMMU_DPRINTF(STE, "STE inconsistant\n");
> +        SMMU_DPRINTF(STE, "config[1]:%d gran:%d addr:%d\n"
> +                     " aa64:%d ttf0:%d ttf1:%d s2ha:%d s2hd:%d httu:%d\n",
> +                     config[1], granule_supported,
> +                     addr_out_of_range, aa64, ttf0, ttf1, STE_S2HA(ste),
> +                     STE_S2HD(ste), httu);
> +        SMMU_DPRINTF(STE, "maxpa:%lx s2ttb:%lx\n", max_pa, STE_S2TTB(ste));
> +        return false;
> +    }
> +    if (s2p && (config[0] == 0 && config[1]) &&
> +        (strw_ign || !ste_strw) && !idr0_vmid && !(ste_vmid >> 8)) {
> +        SMMU_DPRINTF(STE, "STE inconsistant, VMID out of range\n");
> +        return false;
> +    }
> +
> +    return true;
> +}
> +
> +static int tg2granule(int bits, bool tg1)
> +{
> +    switch (bits) {
> +    case 1:
> +        return tg1 ? 14 : 16;
> +    case 2:
> +        return tg1 ? 14 : 12;
> +    case 3:
> +        return tg1 ? 16 : 12;
> +    default:
> +        return 12;
> +    }
> +}
> +
> +static inline int oas2bits(int oas)
> +{
> +    switch (oas) {
> +    case 2:
> +        return 40;
> +    case 3:
> +        return 42;
> +    case 4:
> +        return 44;
> +    case 5:
> +    default: return 48;
> +    }
> +}
> +
> +#define STM2U64(stm) ({                                 \
> +            uint64_t hi, lo;                            \
> +            hi = (stm)->word[1];                        \
> +            lo = (stm)->word[0] & ~(uint64_t)0x1f;      \
> +            hi << 32 | lo;                              \
> +        })
> +
> +#define STMSPAN(stm) (1 << (extract32((stm)->word[0], 0, 4) - 1))
> +
> +static int smmu_find_ste(SMMUV3State *s, uint16_t sid, Ste *ste)
> +{
> +    hwaddr addr;
> +
> +    SMMU_DPRINTF(STE, "SID:%x\n", sid);
> +    /* Check SID range */
> +    if (sid > (1 << s->sid_size)) {
> +        return SMMU_EVT_C_BAD_SID;
> +    }
> +    SMMU_DPRINTF(STE, "features:%x\n", s->features);
> +    if (s->features & SMMU_FEATURE_2LVL_STE) {
> +        int span;
> +        hwaddr stm_addr;
> +        STEDesc stm;
> +        int l1_ste_offset, l2_ste_offset;
> +        SMMU_DPRINTF(STE, "no. ste: %x\n", s->sid_split);
> +
> +        l1_ste_offset = sid >> s->sid_split;
> +        l2_ste_offset = sid & ((1 << s->sid_split) - 1);
> +        SMMU_DPRINTF(STE, "l1_off:%x, l2_off:%x\n", l1_ste_offset,
> +                     l2_ste_offset);
> +        stm_addr = (hwaddr)(s->strtab_base + l1_ste_offset * sizeof(stm));
> +        smmu_read_sysmem(stm_addr, &stm, sizeof(stm), false);
> +
> +        SMMU_DPRINTF(STE, "strtab_base:%lx stm_addr:%lx\n"
> +                     "l1_ste_offset:%x l1(64):%#016lx\n",
> +                     s->strtab_base, stm_addr, l1_ste_offset,
> +                     STM2U64(&stm));
> +
> +        span = STMSPAN(&stm);
> +        SMMU_DPRINTF(STE, "l2_ste_offset:%x ~ span:%d\n", l2_ste_offset, span);
> +        if (l2_ste_offset > span) {
> +            SMMU_DPRINTF(CRIT, "l2_ste_offset > span\n");
> +            return SMMU_EVT_C_BAD_STE;
> +        }
> +        addr = STM2U64(&stm) + l2_ste_offset * sizeof(*ste);
> +    } else {
> +        addr = s->strtab_base + sid * sizeof(*ste);
> +    }
> +    SMMU_DPRINTF(STE, "ste:%lx\n", addr);
> +    if (smmu_get_ste(s, addr, ste)) {
> +        SMMU_DPRINTF(CRIT, "Unable to Fetch STE\n");
> +        return SMMU_EVT_F_UUT;
> +    }
> +
> +    return 0;
> +}
> +
> +static void smmu_cfg_populate_s2(SMMUTransCfg *cfg, Ste *ste)
> +{                           /* stage 2 cfg */
> +    bool s2a64 = STE_S2AA64(ste);
> +    const int stage = 2;
> +
> +    cfg->granule[stage] = STE_S2TG(ste);
> +    cfg->tsz[stage] = STE_S2T0SZ(ste);
> +    cfg->ttbr[stage] = STE_S2TTB(ste);
> +    cfg->oas[stage] = oas2bits(STE_S2PS(ste));
> +
> +    if (s2a64) {
> +        cfg->tsz[stage] = MIN(cfg->tsz[stage], 39);
> +        cfg->tsz[stage] = MAX(cfg->tsz[stage], 16);
> +    }
> +    cfg->va_size[stage] = STE_S2AA64(ste) ? 64 : 32;
> +    cfg->granule_sz[stage] = tg2granule(cfg->granule[stage], 0) - 3;
> +}
> +
> +static void smmu_cfg_populate_s1(SMMUTransCfg *cfg, Cd *cd)
> +{                           /* stage 1 cfg */
> +    bool s1a64 = CD_AARCH64(cd);
> +    const int stage = 1;
> +
> +    cfg->granule[stage] = (CD_EPD0(cd)) ? CD_TG1(cd) : CD_TG0(cd);
> +    cfg->tsz[stage] = (CD_EPD0(cd)) ? CD_T1SZ(cd) : CD_T0SZ(cd);
> +    cfg->ttbr[stage] = (CD_EPD0(cd)) ? CD_TTB1(cd) : CD_TTB0(cd);
> +    cfg->oas[stage] = oas2bits(CD_IPS(cd));
> +
> +    if (s1a64) {
> +        cfg->tsz[stage] = MIN(cfg->tsz[stage], 39);
> +        cfg->tsz[stage] = MAX(cfg->tsz[stage], 16);
> +    }
> +    cfg->va_size[stage] = CD_AARCH64(cd) ? 64 : 32;
> +    cfg->granule_sz[stage] = tg2granule(cfg->granule[stage], CD_EPD0(cd)) - 3;
> +}
> +
> +static SMMUEvtErr smmu_walk_pgtable(SMMUV3State *s, Ste *ste, Cd *cd,
> +                                    IOMMUTLBEntry *tlbe, bool is_write)
> +{
> +    SMMUState *sys = SMMU_SYS_DEV(s);
> +    SMMUBaseClass *sbc = SMMU_DEVICE_GET_CLASS(sys);
> +    SMMUTransCfg _cfg = {};
> +    SMMUTransCfg *cfg = &_cfg;
> +    SMMUEvtErr retval = 0;
> +    uint32_t ste_cfg = STE_CONFIG(ste);
> +    uint32_t page_size = 0, perm = 0;
> +    hwaddr pa;                 /* Input address, output address */
> +    int stage = 0;
> +
> +    SMMU_DPRINTF(DBG1, "ste_cfg :%x\n", ste_cfg);
> +    /* Both Bypass, we dont need to do anything */
> +    if (is_ste_bypass(s, ste)) {
> +        return 0;
> +    }
> +
> +    SMMU_DPRINTF(TT_1, "Input addr: %lx ste_config:%d\n",
> +                 tlbe->iova, ste_cfg);
> +
> +    if (ste_cfg & STE_CONFIG_S1TR) {
> +        stage = cfg->stage = 1;
> +        smmu_cfg_populate_s1(cfg, cd);
> +
> +        cfg->oas[stage] = MIN(oas2bits(smmu_read32_reg(s, SMMU_REG_IDR5) & 0xf),
> +                              cfg->oas[stage]);
> +        /* fix ttbr - make top bits zero*/
> +        cfg->ttbr[stage] = extract64(cfg->ttbr[stage], 0, cfg->oas[stage]);
> +        cfg->s2_needed = (STE_CONFIG(ste) == STE_CONFIG_S1TR_S2TR) ? 1 : 0;
> +
> +        SMMU_DPRINTF(DBG1, "S1 populated\n ");
> +    }
> +
> +    if (ste_cfg & STE_CONFIG_S2TR) {
> +        stage = 2;
> +        if (cfg->stage) {               /* S1+S2 */
> +            cfg->s2_needed = true;
> +        } else                          /* Stage2 only */
> +            cfg->stage = stage;
> +
> +        /* Stage2 only configuratoin */
> +        smmu_cfg_populate_s2(cfg, ste);
> +
> +        cfg->oas[stage] = MIN(oas2bits(smmu_read32_reg(s, SMMU_REG_IDR5) & 0xf),
> +                              cfg->oas[stage]);
> +        /* fix ttbr - make top bits zero*/
> +        cfg->ttbr[stage] = extract64(cfg->ttbr[stage], 0, cfg->oas[stage]);
> +
> +        SMMU_DPRINTF(DBG1, "S2 populated\n ");
> +    }
> +
> +    cfg->va = tlbe->iova;
> +
> +    if ((cfg->stage == 1 && CD_AARCH64(cd)) ||
> +        STE_S2AA64(ste)) {
> +        SMMU_DPRINTF(DBG1, "Translate 64\n");
> +        retval = sbc->translate_64(cfg, &page_size, &perm,
> +                                   is_write);
> +    } else {
> +        SMMU_DPRINTF(DBG1, "Translate 32\n");
> +        retval = sbc->translate_32(cfg, &page_size, &perm, is_write);
> +    }
> +
> +    if (retval != 0) {
> +        SMMU_DPRINTF(CRIT, "FAILED Stage1 translation\n");
> +        goto exit;
> +    }
> +    pa = cfg->pa;
> +
> +    SMMU_DPRINTF(TT_1, "DONE: o/p addr:%lx mask:%x is_write:%d\n ",
> +                 pa, page_size - 1, is_write);
> +    tlbe->translated_addr = pa;
> +    tlbe->addr_mask = page_size - 1;
> +    tlbe->perm = perm;
> +
> +exit:
> +    dump_smmutranscfg(cfg);
> +    return retval;
> +}
> +
> +static MemTxResult smmu_write_evtq(SMMUV3State *s, Evt *evt)
> +{
> +    SMMUQueue *q = &s->evtq;
> +    int ret = smmu_q_write(s, q, evt);
> +    uint32_t val = 0;
> +
> +    val |= (q->wrap.prod << q->shift) | q->prod;
> +
> +    smmu_write32_reg(s, SMMU_REG_EVTQ_PROD, val);
> +
> +    return ret;
> +}
> +
> +/*
> + * Events created on the EventQ
> + */
> +static void smmu_create_event(SMMUV3State *s, hwaddr iova,
> +                              uint32_t sid, bool is_write, int error)
> +{
> +    SMMUQueue *q = &s->evtq;
> +    uint64_t head;
> +    Evt evt;
> +
> +    if (!smmu_evt_q_enabled(s)) {
> +        return;
> +    }
> +
> +    EVT_SET_TYPE(&evt, error);
> +    EVT_SET_SID(&evt, sid);
> +
> +    switch (error) {
> +    case SMMU_EVT_F_UUT:
> +    case SMMU_EVT_C_BAD_STE:
> +        break;
> +    case SMMU_EVT_C_BAD_CD:
> +    case SMMU_EVT_F_CD_FETCH:
> +        break;
> +    case SMMU_EVT_F_TRANS_FORBIDDEN:
> +    case SMMU_EVT_F_WALK_EXT_ABRT:
> +        EVT_SET_INPUT_ADDR(&evt, iova);
> +    default:
> +        break;
> +    }
> +
> +    smmu_write_evtq(s, &evt);
> +
> +    head = Q_IDX(q, q->prod);
> +
> +    if (smmu_is_q_full(s, &s->evtq)) {
> +        head = q->prod ^ (1 << 31);     /* Set overflow */
> +    }
> +
> +    smmu_write32_reg(s, SMMU_REG_EVTQ_PROD, head);
> +
> +    smmu_irq_raise(s, SMMU_IRQ_EVTQ, (uint64_t)&evt);
> +}
> +
> +/*
> + * TR - Translation Request
> + * TT - Translated Tansaction
> + * OT - Other Transaction
> + */
> +static IOMMUTLBEntry
> +smmuv3_translate(MemoryRegion *mr, hwaddr addr, bool is_write)
> +{
> +    SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu);
> +    SMMUV3State *s = sdev->smmu;
> +    uint16_t sid = 0, config;
> +    Ste ste;
> +    Cd cd;
> +    SMMUEvtErr error = 0;
> +
> +    IOMMUTLBEntry ret = {
> +        .target_as = &address_space_memory,
> +        .iova = addr,
> +        .translated_addr = addr,
> +        .addr_mask = ~(hwaddr)0,
> +        .perm = IOMMU_NONE,
> +    };
> +
> +    /* SMMU Bypass, We allow traffic through if SMMU is disabled  */
> +    if (!smmu_enabled(s)) {
> +        SMMU_DPRINTF(CRIT, "SMMU Not enabled.. bypassing addr:%lx\n", addr);
> +        goto bypass;
> +    }
> +
> +    sid = smmu_get_sid(sdev);
> +    SMMU_DPRINTF(TT_1, "SID:%x bus:%d ste_base:%lx\n",
> +                 sid, pci_bus_num(sdev->bus), s->strtab_base);
> +
> +    /* Fetch & Check STE */
> +    error = smmu_find_ste(s, sid, &ste);
> +    if (error) {
> +        goto error_out;  /* F_STE_FETCH or F_CFG_CONFLICT */
> +    }
> +
> +    if (IS_DBG_ENABLED(STE)) {
> +        dump_ste(&ste);
> +    }
> +
> +    if (is_ste_valid(s, &ste) && is_ste_bypass(s, &ste)) {
> +        goto bypass;
> +    }
> +
> +    SMMU_DPRINTF(STE, "STE is not bypass\n");
> +    if (!is_ste_consistent(s, &ste)) {
> +        error = SMMU_EVT_C_BAD_STE;
> +        goto error_out;
> +    }
> +    SMMU_DPRINTF(INFO, "Valid STE Found\n");
> +
> +    /* Stream Bypass */
> +    config = STE_CONFIG(&ste) & 0x3;
> +
> +    if (config & (STE_CONFIG_S1TR)) {
> +        smmu_get_cd(s, &ste, 0, &cd); /* We dont have SSID yet, so 0 */
> +        SMMU_DPRINTF(CRIT, "GET_CD CTXPTR:%p\n", (void *)STE_CTXPTR(&ste));
> +        if (1 || IS_DBG_ENABLED(CD)) {
> +            dump_cd(&cd);
> +        }
> +
> +        if (!is_cd_valid(s, &ste, &cd)) {
> +            error = SMMU_EVT_C_BAD_CD;
> +            goto error_out;
> +        }
> +    }
> +
> +    /* Walk Stage1, if S2 is enabled, S2 walked for Every access on S1 */
> +    error = smmu_walk_pgtable(s, &ste, &cd, &ret, is_write);
> +
> +    SMMU_DPRINTF(INFO, "DONE walking tables \n");
> +
> +error_out:
> +    if (error) {        /* Post the Error using Event Q */
> +        SMMU_DPRINTF(CRIT, "Translation Error: %x\n", error);
> +        smmu_create_event(s, ret.iova, sid, is_write, error);
> +        goto out;
> +    }
> +
> +bypass:
> +    ret.perm = is_write ? IOMMU_RW : IOMMU_RO;
> +
> +out:
> +    return ret;
> +}
> +
> +static const MemoryRegionIOMMUOps smmu_iommu_ops = {
> +    .translate = smmuv3_translate,
> +};
> +
> +static AddressSpace *smmu_init_pci_iommu(PCIBus *bus, void *opaque, int devfn)
> +{
> +    SMMUV3State *s = opaque;
> +    SMMUState *sys = SMMU_SYS_DEV(s);
> +    uintptr_t key = (uintptr_t)bus;
> +    SMMUPciBus *sbus = g_hash_table_lookup(s->smmu_as_by_busptr, &key);
> +    SMMUDevice *sdev;
> +
> +    if (!sbus) {
> +        sbus = g_malloc0(sizeof(SMMUPciBus) +
> +                         sizeof(SMMUDevice) * PCI_DEVFN_MAX);
> +        sbus->bus = bus;
> +        g_hash_table_insert(s->smmu_as_by_busptr, &key, sbus);
> +    }
> +
> +    sdev = sbus->pbdev[devfn];
> +    if (!sdev) {
> +        sdev = sbus->pbdev[devfn] = g_malloc0(sizeof(SMMUDevice));
> +
> +        sdev->smmu = s;
> +        sdev->bus = bus;
> +        sdev->devfn = devfn;
> +
> +        memory_region_init_iommu(&sdev->iommu, OBJECT(sys),
> +                                 &smmu_iommu_ops, TYPE_SMMU_V3_DEV, UINT64_MAX);
> +        address_space_init(&sdev->as, &sdev->iommu, TYPE_SMMU_V3_DEV);
> +    }
> +
> +    return &sdev->as;
> +}
> +
> +static inline void smmu_update_base_reg(SMMUV3State *s, uint64_t *base, uint64_t val)
> +{
> +    *base = val & ~(SMMU_BASE_RA | 0x3fULL);
> +}
> +
> +static void smmu_update_qreg(SMMUV3State *s, SMMUQueue *q, hwaddr reg,
> +                             uint32_t off, uint64_t val, unsigned size)
> +{
> +    if (size == 8 && off == 0) {
> +        smmu_write64_reg(s, reg, val);
> +    } else
> +        smmu_write_reg(s, reg, val);
> +
> +    switch (off) {
> +    case 0:                             /* BASE register */
> +        val = smmu_read64_reg(s, reg);
> +        q->shift = val & 0x1f;
> +        q->entries = 1 << (q->shift);
> +        smmu_update_base_reg(s, &q->base, val);
> +        break;
> +
> +    case 4:                             /* CONS */
> +        q->cons = Q_IDX(q, val);
> +        q->wrap.cons = val >> q->shift;
> +        SMMU_DPRINTF(DBG2, "cons written : %d val:%lx\n", q->cons, val);
> +        break;
> +
> +    case 8:                             /* PROD */
> +        q->prod = Q_IDX(q, val);
> +        q->wrap.prod = val >> q->shift;
> +        break;
> +    }
> +
> +    switch (reg) {
> +    case SMMU_REG_CMDQ_PROD:            /* should be only for CMDQ_PROD */
> +    case SMMU_REG_CMDQ_CONS:            /* but we do it anyway */
> +        smmu_update(s);
> +        break;
> +    }
> +}
> +
> +static void smmu_write_mmio_fixup(SMMUV3State *s, hwaddr *addr)
> +{
> +    switch (*addr) {
> +    case 0x100a8: case 0x100ac:         /* Aliasing => page0 registers */
> +    case 0x100c8: case 0x100cc:
> +        *addr ^= (hwaddr)0x10000;
> +    }
> +}
> +
> +static void smmu_write_mmio(void *opaque, hwaddr addr,
> +                            uint64_t val, unsigned size)
> +{
> +    SMMUState *sys = opaque;
> +    SMMUV3State *s = SMMU_V3_DEV(sys);
> +    bool update = false;
> +
> +    smmu_write_mmio_fixup(s, &addr);
> +
> +    SMMU_DPRINTF(DBG2, "addr: %lx val:%lx\n", addr, val);
> +
> +    switch (addr) {
> +    case 0xFDC ... 0xFFC:
> +    case SMMU_REG_IDR0 ... SMMU_REG_IDR5:
> +        SMMU_DPRINTF(CRIT, "write to RO/Unimpl reg %lx val64:%lx\n",
> +                     addr, val);
> +        return;
> +
> +    case SMMU_REG_GERRORN:
> +        smmu_update_irq(s, addr, val);
> +        return;
> +
> +    case SMMU_REG_CR0:
> +        smmu_write32_reg(s, SMMU_REG_CR0_ACK, val);
> +        update = true;
> +        break;
> +
> +    case SMMU_REG_IRQ_CTRL:
> +        smmu_write32_reg(s, SMMU_REG_IRQ_CTRL_ACK, val);
> +        update = true;
> +        break;
> +
> +    case SMMU_REG_STRTAB_BASE:
> +        smmu_update_base_reg(s, &s->strtab_base, val);
> +        return;
> +
> +    case SMMU_REG_STRTAB_BASE_CFG:
> +        if (((val >> 16) & 0x3) == 0x1) {
> +            s->sid_split = (val >> 6) & 0x1f;
> +            s->features |= SMMU_FEATURE_2LVL_STE;
> +        }
> +        break;
> +
> +    case SMMU_REG_CMDQ_PROD:
> +    case SMMU_REG_CMDQ_CONS:
> +    case SMMU_REG_CMDQ_BASE:
> +    case SMMU_REG_CMDQ_BASE + 4:
> +        smmu_update_qreg(s, &s->cmdq, addr, addr - SMMU_REG_CMDQ_BASE,
> +                         val, size);
> +        return;
> +
> +    case SMMU_REG_EVTQ_CONS:            /* fallthrough */
> +    {
> +        SMMUQueue *evtq = &s->evtq;
> +        evtq->cons = Q_IDX(evtq, val);
> +        evtq->wrap.cons = Q_WRAP(evtq, val);
> +
> +        SMMU_DPRINTF(IRQ, "Before clearing interrupt "
> +                     "prod:%x cons:%x prod.w:%d cons.w:%d\n",
> +                     evtq->prod, evtq->cons, evtq->wrap.prod, evtq->wrap.cons);
> +        if (smmu_is_q_empty(s, &s->evtq)) {
> +            SMMU_DPRINTF(IRQ, "Clearing interrupt"
> +                         " prod:%x cons:%x prod.w:%d cons.w:%d\n",
> +                         evtq->prod, evtq->cons, evtq->wrap.prod,
> +                         evtq->wrap.cons);
> +            qemu_irq_lower(s->irq[SMMU_IRQ_EVTQ]);
> +        }
> +    }
> +    case SMMU_REG_EVTQ_BASE:
> +    case SMMU_REG_EVTQ_BASE + 4:
> +    case SMMU_REG_EVTQ_PROD:
> +        smmu_update_qreg(s, &s->evtq, addr, addr - SMMU_REG_EVTQ_BASE,
> +                         val, size);
> +        return;
> +
> +    case SMMU_REG_PRIQ_CONS:
> +    case SMMU_REG_PRIQ_BASE:
> +    case SMMU_REG_PRIQ_BASE + 4:
> +    case SMMU_REG_PRIQ_PROD:
> +        smmu_update_qreg(s, &s->priq, addr, addr - SMMU_REG_PRIQ_BASE,
> +                         val, size);
> +        return;
> +    }
> +
> +    if (size == 8) {
> +        smmu_write_reg(s, addr, val);
> +    } else {
> +        smmu_write32_reg(s, addr, (uint32_t)val);
> +    }
> +
> +    if (update)
> +        smmu_update(s);
> +}
> +
> +static uint64_t smmu_read_mmio(void *opaque, hwaddr addr, unsigned size)
> +{
> +    SMMUState *sys = opaque;
> +    SMMUV3State *s = SMMU_V3_DEV(sys);
> +    uint64_t val;
> +
> +    smmu_write_mmio_fixup(s, &addr);
> +
> +    /* Primecell/Corelink ID registers */
> +    switch (addr) {
> +    case 0xFF0 ... 0xFFC:
> +        val = (uint64_t)sys->cid[(addr - 0xFF0) >> 2];
> +        break;
> +
> +    case 0xFDC ... 0xFE4:
> +        val = (uint64_t)sys->pid[(addr - 0xFDC) >> 2];
> +        break;
> +
> +    default:
> +        val = (uint64_t)smmu_read32_reg(s, addr);
> +        break;
> +
> +    case SMMU_REG_STRTAB_BASE ... SMMU_REG_CMDQ_BASE:
> +    case SMMU_REG_EVTQ_BASE:
> +    case SMMU_REG_PRIQ_BASE ... SMMU_REG_PRIQ_IRQ_CFG1:
> +        val = smmu_read64_reg(s, addr);
> +        break;
> +    }
> +
> +    SMMU_DPRINTF(DBG2, "addr: %lx val:%lx\n", addr, val);
> +    SMMU_DPRINTF(DBG2, "cmdq cons:%d\n", s->cmdq.cons);
> +    return val;
> +}
> +
> +static const MemoryRegionOps smmu_mem_ops = {
> +    .read = smmu_read_mmio,
> +    .write = smmu_write_mmio,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 8,
> +    },
> +};
> +
> +static void smmu_init_irq(SMMUV3State *s, SysBusDevice *dev)
> +{
> +    int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
> +        sysbus_init_irq(dev, &s->irq[i]);
> +    }
> +}
> +
> +static void smmu_init_iommu_as(SMMUV3State *sys)
> +{
> +    SMMUState *s = SMMU_SYS_DEV(sys);
> +    PCIBus *pcibus = pci_find_primary_bus();
> +
> +    if (pcibus) {
> +        SMMU_DPRINTF(CRIT, "Found PCI bus, setting up iommu\n");
> +        pci_setup_iommu(pcibus, smmu_init_pci_iommu, s);
> +    } else {
> +        SMMU_DPRINTF(CRIT, "No PCI bus, SMMU is not registered\n");
> +    }
> +}
> +
> +static void smmu_reset(DeviceState *dev)
> +{
> +    SMMUV3State *s = SMMU_V3_DEV(dev);
> +    smmuv3_init(s);
> +}
> +
> +static int smmu_populate_internal_state(void *opaque, int version_id)
> +{
> +    SMMUV3State *s = opaque;
> +    /* SMMUState *sys = SMMU_SYS_DEV(s); */
> +
> +    smmu_update(s);
> +
> +    return 0;
> +}
> +
> +static gboolean smmu_uint64_equal(gconstpointer v1, gconstpointer v2)
> +{
> +    return *((const uint64_t *)v1) == *((const uint64_t *)v2);
> +}
> +
> +static guint smmu_uint64_hash(gconstpointer v)
> +{
> +    return (guint)*(const uint64_t *)v;
> +}
> +
> +static void smmu_realize(DeviceState *d, Error **errp)
> +{
> +    SMMUState *sys = SMMU_SYS_DEV(d);
> +    SMMUV3State *s = SMMU_V3_DEV(sys);
> +    SysBusDevice *dev = SYS_BUS_DEVICE(d);
> +
> +    /* Register Access */
> +    memory_region_init_io(&sys->iomem, OBJECT(s),
> +                          &smmu_mem_ops, sys, TYPE_SMMU_V3_DEV, 0x20000);
> +
> +    s->smmu_as_by_busptr = g_hash_table_new_full(smmu_uint64_hash,
> +                                                 smmu_uint64_equal,
> +                                                 g_free, g_free);
> +    sysbus_init_mmio(dev, &sys->iomem);
> +
> +    smmu_init_irq(s, dev);
> +
> +    smmu_init_iommu_as(s);
> +}
> +
> +static const VMStateDescription vmstate_smmu = {
> +    .name = "smmu",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .post_load = smmu_populate_internal_state,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(cid, SMMUState, 4),
> +        VMSTATE_UINT32_ARRAY(pid, SMMUState, 8),
> +        VMSTATE_UINT64_ARRAY(regs, SMMUV3State, SMMU_NREGS),
> +        VMSTATE_END_OF_LIST(),
> +    },
> +};
> +
> +static void smmu_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SMMUBaseClass *sbc = SMMU_DEVICE_CLASS(klass);
> +
> +    sbc->translate_64 = smmu_translate_64;
> +
> +    dc->reset = smmu_reset;
> +    dc->vmsd = &vmstate_smmu;
> +    dc->realize = smmu_realize;
> +}
> +
> +static void smmu_base_instance_init(Object *obj)
> +{
> +     /* Nothing much to do here as of now */
> +}
> +
> +static void smmu_instance_init(Object *obj)
> +{
> +    /* Nothing much to do here as of now */
> +}
> +
> +static const TypeInfo smmu_base_info = {
> +    .name          = TYPE_SMMU_DEV_BASE,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(SMMUV3State),
> +    .instance_init = smmu_base_instance_init,
> +    .class_size    = sizeof(SMMUBaseClass),
> +    .abstract      = true,
> +};
> +
> +static void smmu_register_types(void)
> +{
> +    TypeInfo type_info = {
> +        .name = TYPE_SMMU_V3_DEV,
> +        .parent = TYPE_SMMU_DEV_BASE,
> +        .class_data = NULL,
> +        .class_init = smmu_class_init,
> +        .instance_init = smmu_instance_init,
> +    };
> +
> +    type_register_static(&smmu_base_info);
> +
> +    type_register(&type_info);
> +}
> +
> +type_init(smmu_register_types)
> +
> diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h
> new file mode 100644
> index 0000000..8d34f2a
> --- /dev/null
> +++ b/hw/arm/smmuv3-internal.h
> @@ -0,0 +1,432 @@
> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + *
> + * Copyright (C) 2014-2015 Broadcom Corporation
> + *
> + * Author: Prem Mallappa <pmallapp@broadcom.com>
> + *
> + */
> +
> +#ifndef HW_ARM_SMMU_V3_INTERNAL_H
> +#define HW_ARM_SMMU_V3_INTERNAL_H
> +
> +/*****************************
> + * MMIO Register
> + *****************************/
> +enum {
> +    SMMU_REG_IDR0            = 0x0,

For all regs, I think you should prefix regs with R_.
And also do / 4, e.g:

R_SMMU_REG_IDR1            = 0x4 / 4,

That way you can do s->regs[R_SMMU_REG_IDR1] and remove smmu_read32_reg.
If you use the REG32 and FIELD macros from the register API you'll
also be able to use the FIELD_ family of macros (e.g ARRAY_FIELD_EX32)
to extract fields from regs.


> +
> +#define SMMU_IDR0_S2P            (1 << 0)
> +#define SMMU_IDR0_S1P            (1 << 1)
> +#define SMMU_IDR0_TTF            (0x3 << 2)
> +#define SMMU_IDR0_HTTU           (0x3 << 6)
> +#define SMMU_IDR0_HYP            (1 << 9)
> +#define SMMU_IDR0_ATS            (1 << 10)
> +#define SMMU_IDR0_VMID16         (1 << 18)
> +#define SMMU_IDR0_CD2L           (1 << 19)
> +
> +    SMMU_REG_IDR1            = 0x4,
> +    SMMU_REG_IDR2            = 0x8,
> +    SMMU_REG_IDR3            = 0xc,
> +    SMMU_REG_IDR4            = 0x10,
> +    SMMU_REG_IDR5            = 0x14,
> +    SMMU_REG_IIDR            = 0x1c,
> +    SMMU_REG_CR0             = 0x20,
> +
> +#define SMMU_CR0_SMMU_ENABLE (1 << 0)
> +#define SMMU_CR0_PRIQ_ENABLE (1 << 1)
> +#define SMMU_CR0_EVTQ_ENABLE (1 << 2)
> +#define SMMU_CR0_CMDQ_ENABLE (1 << 3)
> +#define SMMU_CR0_ATS_CHECK   (1 << 4)
> +
> +    SMMU_REG_CR0_ACK         = 0x24,
> +    SMMU_REG_CR1             = 0x28,
> +    SMMU_REG_CR2             = 0x2c,
> +
> +    SMMU_REG_STATUSR         = 0x40,
> +
> +    SMMU_REG_IRQ_CTRL        = 0x50,
> +    SMMU_REG_IRQ_CTRL_ACK    = 0x54,
> +
> +#define SMMU_IRQ_CTRL_GERROR_EN (1 << 0)
> +#define SMMU_IRQ_CTRL_EVENT_EN  (1 << 1)
> +#define SMMU_IRQ_CTRL_PRI_EN    (1 << 2)
> +
> +    SMMU_REG_GERROR          = 0x60,
> +
> +#define SMMU_GERROR_CMDQ       (1 << 0)
> +#define SMMU_GERROR_EVENTQ     (1 << 2)
> +#define SMMU_GERROR_PRIQ       (1 << 3)
> +#define SMMU_GERROR_MSI_CMDQ   (1 << 4)
> +#define SMMU_GERROR_MSI_EVENTQ (1 << 5)
> +#define SMMU_GERROR_MSI_PRIQ   (1 << 6)
> +#define SMMU_GERROR_MSI_GERROR (1 << 7)
> +#define SMMU_GERROR_SFM_ERR    (1 << 8)
> +
> +    SMMU_REG_GERRORN         = 0x64,
> +    SMMU_REG_GERROR_IRQ_CFG0 = 0x68,
> +    SMMU_REG_GERROR_IRQ_CFG1 = 0x70,
> +    SMMU_REG_GERROR_IRQ_CFG2 = 0x74,
> +
> +    /* SMMU_BASE_RA Applies to STRTAB_BASE, CMDQ_BASE and EVTQ_BASE */
> +#define SMMU_BASE_RA        (1ULL << 62)
> +    SMMU_REG_STRTAB_BASE     = 0x80,
> +    SMMU_REG_STRTAB_BASE_CFG = 0x88,
> +
> +    SMMU_REG_CMDQ_BASE       = 0x90,
> +    SMMU_REG_CMDQ_PROD       = 0x98,
> +    SMMU_REG_CMDQ_CONS       = 0x9c,
> +    /* CMD Consumer (CONS) */
> +#define SMMU_CMD_CONS_ERR_SHIFT        24
> +#define SMMU_CMD_CONS_ERR_BITS         7
> +
> +    SMMU_REG_EVTQ_BASE       = 0xa0,
> +    SMMU_REG_EVTQ_PROD       = 0xa8,
> +    SMMU_REG_EVTQ_CONS       = 0xac,
> +    SMMU_REG_EVTQ_IRQ_CFG0   = 0xb0,
> +    SMMU_REG_EVTQ_IRQ_CFG1   = 0xb8,
> +    SMMU_REG_EVTQ_IRQ_CFG2   = 0xbc,
> +
> +    SMMU_REG_PRIQ_BASE       = 0xc0,
> +    SMMU_REG_PRIQ_PROD       = 0xc8,
> +    SMMU_REG_PRIQ_CONS       = 0xcc,
> +    SMMU_REG_PRIQ_IRQ_CFG0   = 0xd0,
> +    SMMU_REG_PRIQ_IRQ_CFG1   = 0xd8,
> +    SMMU_REG_PRIQ_IRQ_CFG2   = 0xdc,
> +
> +    SMMU_ID_REGS_OFFSET      = 0xfd0,
> +
> +    /* Secure registers are not used for now */
> +    SMMU_SECURE_OFFSET       = 0x8000,
> +};
> +
> +/*****************************
> + * STE fields
> + *****************************/
> +#define STE_VALID(x)   extract32((x)->word[0], 0, 1) /* 0 */
> +#define STE_CONFIG(x)  (extract32((x)->word[0], 1, 3) & 0x7)
> +enum {
> +    STE_CONFIG_NONE      = 0,
> +    STE_CONFIG_BYPASS    = 4,           /* S1 Bypass, S2 Bypass */
> +    STE_CONFIG_S1TR      = 1,           /* S1 Translate, S2 Bypass */
> +    STE_CONFIG_S2TR      = 2,           /* S1 Bypass, S2 Translate */
> +    STE_CONFIG_S1TR_S2TR = 3,           /* S1 Translate, S2 Translate */
> +};
> +#define STE_S1FMT(x)   extract32((x)->word[0], 4, 2)
> +#define STE_S1CDMAX(x) extract32((x)->word[1], 8, 2)
> +#define STE_EATS(x)    extract32((x)->word[2], 28, 2)
> +#define STE_STRW(x)    extract32((x)->word[2], 30, 2)
> +#define STE_S2VMID(x)  extract32((x)->word[4], 0, 16) /* 4 */
> +#define STE_S2T0SZ(x)  extract32((x)->word[5], 0, 6) /* 5 */
> +#define STE_S2TG(x)    extract32((x)->word[5], 14, 2)
> +#define STE_S2PS(x)    extract32((x)->word[5], 16, 3)
> +#define STE_S2AA64(x)  extract32((x)->word[5], 19, 1)
> +#define STE_S2HD(x)    extract32((x)->word[5], 24, 1)
> +#define STE_S2HA(x)    extract32((x)->word[5], 25, 1)
> +#define STE_S2S(x)     extract32((x)->word[5], 26, 1)
> +#define STE_CTXPTR(x)                                           \
> +    ({                                                          \
> +        unsigned long addr;                                     \
> +        addr = (uint64_t)extract32((x)->word[1], 0, 16) << 32;  \
> +        addr |= (uint64_t)((x)->word[0] & 0xffffffc0);          \
> +        addr;                                                   \
> +    })
> +
> +#define STE_S2TTB(x)                                            \
> +    ({                                                          \
> +        unsigned long addr;                                     \
> +        addr = (uint64_t)extract32((x)->word[7], 0, 16) << 32;  \
> +        addr |= (uint64_t)((x)->word[6] & 0xfffffff0);          \
> +        addr;                                                   \
> +    })
> +
> +/*****************************
> + * CD fields
> + *****************************/
> +#define CD_VALID(x)   extract32((x)->word[0], 30, 1)
> +#define CD_ASID(x)    extract32((x)->word[1], 16, 16)
> +#define CD_TTB(x, sel)                                      \
> +    ({                                                      \
> +        uint64_t hi, lo;                                    \
> +        hi = extract32((x)->word[(sel) * 2 + 3], 0, 16);    \
> +        hi <<= 32;                                          \
> +        lo = (x)->word[(sel) * 2 + 2] & ~0xf;               \
> +        hi | lo;                                            \
> +    })
> +
> +#define CD_TSZ(x, sel)   extract32((x)->word[0], (16 * (sel)) + 0, 6)
> +#define CD_TG(x, sel)    extract32((x)->word[0], (16 * (sel)) + 6, 2)
> +#define CD_EPD(x, sel)   extract32((x)->word[0], (16 * (sel)) + 14, 1)
> +
> +#define CD_T0SZ(x)    CD_TSZ((x), 0)
> +#define CD_T1SZ(x)    CD_TSZ((x), 1)
> +#define CD_TG0(x)     CD_TG((x), 0)
> +#define CD_TG1(x)     CD_TG((x), 1)
> +#define CD_EPD0(x)    CD_EPD((x), 0)
> +#define CD_EPD1(x)    CD_EPD((x), 1)
> +#define CD_IPS(x)     extract32((x)->word[1], 0, 3)
> +#define CD_AARCH64(x) extract32((x)->word[1], 9, 1)
> +#define CD_TTB0(x)    CD_TTB((x), 0)
> +#define CD_TTB1(x)    CD_TTB((x), 1)
> +
> +#define CDM_VALID(x)    ((x)->word[0] & 0x1)
> +
> +/*****************************
> + * Commands
> + *****************************/
> +enum {
> +    SMMU_CMD_PREFETCH_CONFIG = 0x01,
> +    SMMU_CMD_PREFETCH_ADDR,
> +    SMMU_CMD_CFGI_STE,
> +    SMMU_CMD_CFGI_STE_RANGE,
> +    SMMU_CMD_CFGI_CD,
> +    SMMU_CMD_CFGI_CD_ALL,
> +    SMMU_CMD_TLBI_NH_ALL     = 0x10,
> +    SMMU_CMD_TLBI_NH_ASID,
> +    SMMU_CMD_TLBI_NH_VA,
> +    SMMU_CMD_TLBI_NH_VAA,
> +    SMMU_CMD_TLBI_EL3_ALL    = 0x18,
> +    SMMU_CMD_TLBI_EL3_VA     = 0x1a,
> +    SMMU_CMD_TLBI_EL2_ALL    = 0x20,
> +    SMMU_CMD_TLBI_EL2_ASID,
> +    SMMU_CMD_TLBI_EL2_VA,
> +    SMMU_CMD_TLBI_EL2_VAA,  /* 0x23 */
> +    SMMU_CMD_TLBI_S12_VMALL  = 0x28,
> +    SMMU_CMD_TLBI_S2_IPA     = 0x2a,
> +    SMMU_CMD_TLBI_NSNH_ALL   = 0x30,
> +    SMMU_CMD_ATC_INV         = 0x40,
> +    SMMU_CMD_PRI_RESP,
> +    SMMU_CMD_RESUME          = 0x44,
> +    SMMU_CMD_STALL_TERM,
> +    SMMU_CMD_SYNC,          /* 0x46 */
> +};
> +
> +/*****************************
> + * CMDQ fields
> + *****************************/
> +
> +enum { /* Command Errors */
> +    SMMU_CMD_ERR_NONE = 0,
> +    SMMU_CMD_ERR_ILLEGAL,
> +    SMMU_CMD_ERR_ABORT
> +};
> +
> +enum { /* Command completion notification */
> +    CMD_SYNC_SIG_NONE,
> +    CMD_SYNC_SIG_IRQ,
> +    CMD_SYNC_SIG_SEV,
> +};
> +
> +#define CMD_TYPE(x)  extract32((x)->word[0], 0, 8)
> +#define CMD_SEC(x)   extract32((x)->word[0], 9, 1)
> +#define CMD_SEV(x)   extract32((x)->word[0], 10, 1)
> +#define CMD_AC(x)    extract32((x)->word[0], 12, 1)
> +#define CMD_AB(x)    extract32((x)->word[0], 13, 1)
> +#define CMD_CS(x)    extract32((x)->word[0], 12, 2)
> +#define CMD_SSID(x)  extract32((x)->word[0], 16, 16)
> +#define CMD_SID(x)   ((x)->word[1])
> +#define CMD_VMID(x)  extract32((x)->word[1], 0, 16)
> +#define CMD_ASID(x)  extract32((x)->word[1], 16, 16)
> +#define CMD_STAG(x)  extract32((x)->word[2], 0, 16)
> +#define CMD_RESP(x)  extract32((x)->word[2], 11, 2)
> +#define CMD_GRPID(x) extract32((x)->word[3], 0, 8)
> +#define CMD_SIZE(x)  extract32((x)->word[3], 0, 16)
> +#define CMD_LEAF(x)  extract32((x)->word[3], 0, 1)
> +#define CMD_SPAN(x)  extract32((x)->word[3], 0, 5)
> +#define CMD_ADDR(x) ({                                  \
> +            uint64_t addr = (uint64_t)(x)->word[3];     \
> +            addr <<= 32;                                \
> +            addr |=  extract32((x)->word[3], 12, 20);   \
> +            addr;                                       \
> +        })
> +
> +/*****************************
> + * EVTQ fields
> + *****************************/
> +#define EVT_Q_OVERFLOW        (1 << 31)
> +
> +#define EVT_SET_TYPE(x, t)    deposit32((x)->word[0], 0, 8, t)
> +#define EVT_SET_SID(x, s)     ((x)->word[1] =  s)
> +#define EVT_SET_INPUT_ADDR(x, addr) ({                    \
> +            (x)->word[5] = (uint32_t)(addr >> 32);        \
> +            (x)->word[4] = (uint32_t)(addr & 0xffffffff); \
> +            addr;                                         \
> +        })
> +
> +/*****************************
> + * Events
> + *****************************/
> +enum evt_err {
> +    SMMU_EVT_F_UUT    = 0x1,
> +    SMMU_EVT_C_BAD_SID,
> +    SMMU_EVT_F_STE_FETCH,
> +    SMMU_EVT_C_BAD_STE,
> +    SMMU_EVT_F_BAD_ATS_REQ,
> +    SMMU_EVT_F_STREAM_DISABLED,
> +    SMMU_EVT_F_TRANS_FORBIDDEN,
> +    SMMU_EVT_C_BAD_SSID,
> +    SMMU_EVT_F_CD_FETCH,
> +    SMMU_EVT_C_BAD_CD,
> +    SMMU_EVT_F_WALK_EXT_ABRT,
> +    SMMU_EVT_F_TRANS        = 0x10,
> +    SMMU_EVT_F_ADDR_SZ,
> +    SMMU_EVT_F_ACCESS,
> +    SMMU_EVT_F_PERM,
> +    SMMU_EVT_F_TLB_CONFLICT = 0x20,
> +    SMMU_EVT_F_CFG_CONFLICT = 0x21,
> +    SMMU_EVT_E_PAGE_REQ     = 0x24,
> +};
> +
> +typedef enum evt_err SMMUEvtErr;
> +
> +
> +/*****************************
> + * SMMU Data structures
> + *****************************/
> +#define ARM_SMMU_FEAT_PASSID_SUPPORT  (1 << 24) /* Some random bits for now */
> +#define ARM_SMMU_FEAT_CD_2LVL         (1 << 25)
> +
> +struct SMMUQueue {
> +     hwaddr base;
> +     uint32_t prod;
> +     uint32_t cons;
> +     union {
> +          struct {
> +               uint8_t prod:1;
> +               uint8_t cons:1;

Hi, Peter generally doesn't like bitfields. I'd stay away form
them unless you have a good case. Just change them too bool.


> +          };
> +          uint8_t unused;
> +     } wrap;
> +
> +     uint16_t entries;           /* Number of entries */
> +     uint8_t  ent_size;          /* Size of entry in bytes */
> +     uint8_t  shift;             /* Size in log2 */
> +};
> +typedef struct SMMUQueue SMMUQueue;
> +
> +#define Q_ENTRY(q, idx)  (q->base + q->ent_size * idx)
> +#define Q_WRAP(q, pc)    ((pc) >> (q)->shift)
> +#define Q_IDX(q, pc)     ((pc) & ((1 << (q)->shift) - 1))

You probably want 1U << (q)->shift

> +
> +struct __smmu_data2 {
> +    uint32_t word[2];
> +};
> +
> +struct __smmu_data8 {
> +    uint32_t word[8];
> +};
> +
> +struct __smmu_data16 {
> +    uint32_t word[16];
> +};
> +
> +struct __smmu_data4 {
> +    uint32_t word[4];
> +};
> +
> +typedef struct __smmu_data2  STEDesc; /* STE Level 1 Descriptor */
> +typedef struct __smmu_data16 Ste;     /* Stream Table Entry(STE) */
> +typedef struct __smmu_data2  CDDesc;  /* CD Level 1 Descriptor */
> +typedef struct __smmu_data16 Cd;      /* Context Descriptor(CD) */
> +
> +typedef struct __smmu_data4  Cmd; /* Command Entry */
> +typedef struct __smmu_data8  Evt; /* Event Entry */
> +typedef struct __smmu_data4  Pri; /* PRI entry */


For all of these, I think it would be more useful if you would declare
structs with actual fields representing the data structures.
You can then declare load functions that load the STE from memory and
decode the fields.

E.g:

typedef struct SMMUv3_STEDesc {
    bool valid;
    .... etc...
} SMMUv3_STEDesc;

void smmuv3_load_ste(AddressSpace *as, dma_addr_t addr, SMMUv3_STEDesc *ste)
{
    uint32_t buf[16];
    dma_memory_read(as, addr, buf, sizeof(*buf));

    ste->valid = extract32(buf[0], 0, 1);
}


Then, instead of for example doing STE_VALID(x), you can do ste->valid.



> +
> +
> +/*****************************
> + * Broadcom Specific register and bits
> + *****************************/
> +#define SMMU_REG_CNTL         (0x410 << 2)
> +#define SMMU_REG_CNTL_1       (0x411 << 2)
> +#define SMMU_REG_INTERRUPT    (0x412 << 2)
> +/* BIT encoding is same as SMMU_REG_INTERRUPT, except for last 4 bits */
> +#define SMMU_REG_INTERRUPT_EN (0x413 << 2)
> +
> +#define SMMU_INTR_BMI_ERR  (1 << 6) /* Smmu BMI Rd Wr Error*/
> +#define SMMU_INTR_BSI_ERR  (1 << 5) /* Smmu BSI Rd Wr Error*/
> +#define SMMU_INTR_SBU_INTR (1 << 4) /* SBU interrupt 0 */
> +#define SMMU_INTR_CMD_SYNC (1 << 3) /* CmdSync completion set to interrupt */
> +#define SMMU_INTR_EVENT    (1 << 2) /* high till EventQ.PROD != EventQ.CONS */
> +#define SMMU_INTR_PRI      (1 << 1) /* PriQ. high till PriQ.PROD != PriQ.CONS */
> +#define SMMU_INTR_GERROR   (1 << 0) /* cleared when  GERRORN is written */
> +
> +/*****************************
> + * QEMu related
> + *****************************/
> +
> +typedef struct {
> +    SMMUBaseClass smmu_base_class;
> +} SMMUV3Class;
> +
> +#define SMMU_DEVICE_CLASS(klass)                                    \
> +    OBJECT_CLASS_CHECK(SMMUBaseClass, (klass), TYPE_SMMU_DEV_BASE)
> +
> +#define SMMU_V3_DEVICE_GET_CLASS(obj)                              \
> +    OBJECT_GET_CLASS(SMMUBaseClass, (obj), TYPE_SMMU_V3_DEV)
> +
> +#ifdef ARM_SMMU_DEBUG
> +static inline void dump_ste(Ste *ste)
> +{
> +    int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(ste->word); i += 2) {
> +        SMMU_DPRINTF(STE, "STE[%2d]: %#010x\t STE[%2d]: %#010x\n",
> +                i, ste->word[i], i + 1, ste->word[i + 1]);
> +    }
> +}
> +
> +static inline void dump_cd(Cd *cd)
> +{
> +    int i;
> +    for (i = 0; i < ARRAY_SIZE(cd->word); i += 2) {
> +        SMMU_DPRINTF(CD, "CD[%2d]: %#010x\t CD[%2d]: %#010x\n",
> +                i, cd->word[i], i + 1, cd->word[i + 1]);
> +    }
> +}
> +
> +static inline void dump_evt(Evt *e)
> +{}
> +
> +static inline void dump_cmd(Cmd *cmd)
> +{
> +    int i;
> +    for (i = 0; i < ARRAY_SIZE(cmd->word); i += 2) {
> +        SMMU_DPRINTF(CMDQ, "CMD[%2d]: %#010x\t CMD[%2d]: %#010x\n",
> +                i, cmd->word[i], i + 1, cmd->word[i + 1]);
> +    }
> +}
> +
> +static void dump_smmutranscfg(SMMUTransCfg *cfg)
> +{
> +    int i;
> +    SMMU_DPRINTF(TT_1, "TransCFG stage:%d va:%lx pa:%lx s2_needed:%d\n",
> +                 cfg->stage, cfg->va, cfg->pa, cfg->s2_needed);
> +    for (i = 1; i <= 2; i++) {
> +        SMMU_DPRINTF(TT_1, "TransCFG i:%d oas:%x tsz:%x ttbr:%lx granule:%x"
> +                     " va_size:%x gran_sz:%x\n", i, cfg->oas[i], cfg->tsz[i],
> +                     cfg->ttbr[i], cfg->granule[i], cfg->va_size[i],
> +                     cfg->granule_sz[i]);
> +    }
> +}
> +
> +#else
> +#define dump_ste(...) do {} while (0)
> +#define dump_cd(...) do {} while (0)
> +#define dump_evt(...) do {} while (0)
> +#define dump_cmd(...) do {} while (0)
> +static void dump_smmutranscfg(SMMUTransCfg *cfg) {}
> +#endif
> +
> +#endif
> diff --git a/hw/vfio/common.c b/hw/vfio/common.c
> index e51ed3a..96da537 100644
> --- a/hw/vfio/common.c
> +++ b/hw/vfio/common.c
> @@ -412,10 +412,10 @@ static void vfio_listener_region_add(MemoryListener *listener,
>  
>      ret = vfio_dma_map(container, iova, int128_get64(llsize),
>                         vaddr, section->readonly);
> -    if (ret) {
>          error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
>                       "0x%"HWADDR_PRIx", %p) = %d (%m)",
>                       container, iova, int128_get64(llsize), vaddr, ret);
> +    if (ret) {
>          goto fail;
>      }


Shouldn't this be in a separate patch?




>  
> -- 
> 2.9.3
> 

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

* Re: [Qemu-devel] [PATCH v2 3/9] hw: arm: SMMUv3 emulation model
  2016-09-25 16:37   ` Edgar E. Iglesias
@ 2016-09-26  5:27     ` Prem Mallappa
  0 siblings, 0 replies; 34+ messages in thread
From: Prem Mallappa @ 2016-09-26  5:27 UTC (permalink / raw)
  To: Edgar E. Iglesias; +Cc: Peter Maydell, QEMU Developers

Hi Edger,

I'm going to look at the PCI parts and get back to you with
> comments on that.
>
> Please do, by the time, I'll address your and Eric's comments.


> I've put another round of comments inline:
>
> Thanks



> > +inline void
> > +smmu_write_sysmem(hwaddr addr, void *buf, int len, bool secure)
> > +{
> > +    MemTxAttrs attrs = {.unspecified = 1, .secure = secure};
> > +
> > +    switch (len) {
> > +    case 4:
> > +        stl_le_phys(&address_space_memory, addr, *(uint32_t *)buf);
> > +        break;
> > +    case 8:
> > +        stq_le_phys(&address_space_memory, addr, *(uint64_t *)buf);
> > +        break;
> > +    default:
> > +        address_space_rw(&address_space_memory, addr,
> > +                         attrs, buf, len, true);
> > +    }
> > +}
>
> Thinking about this, I think you should just remove these functions and
> always call dma_memory_read/write directly.
>
> It would be nice if you could add a property/link so that machine code
> can specify the MemoryRegion/address space to be used. You'll need a
> link to allow setup of the MemoryRegion and also some code to create
> an address space from the selected MR.
>
> You can have a look at the following code to see how it's done:
> exec.c cpu_exec_init()    see object_property_add_link
> cpus.c qemu_init_vcpu()   see address_space_init_shareable
>
>
Sure, will do.


> > +#define smmu_evt_irq_enabled(s)                   \
> > +    __smmu_irq_enabled(s, SMMU_IRQ_CTRL_EVENT_EN)
> > +#define smmu_gerror_irq_enabled(s)                  \
> > +    __smmu_irq_enabled(s, SMMU_IRQ_CTRL_GERROR_EN)
> > +#define smmu_pri_irq_enabled(s)                 \
> > +    __smmu_irq_enabled(s, SMMU_IRQ_CTRL_PRI_EN)
>
> Please drop the __ prefix on functions. _ prefixed functions are reserved
> and
> we usually avoid them.
>
> I don't think smmu_evt_irq_enabled() is very useful,
> smmu_irq_enabled(s, SMMU_IRQ_CTRL_EVENT_EN) is readable enough.
>
>
Got it.


> > +
> > +/*****************************
> > + * MMIO Register
> > + *****************************/
> > +enum {
> > +    SMMU_REG_IDR0            = 0x0,
>
> For all regs, I think you should prefix regs with R_.
> And also do / 4, e.g:
>
> R_SMMU_REG_IDR1            = 0x4 / 4,
>
> That way you can do s->regs[R_SMMU_REG_IDR1] and remove smmu_read32_reg.
> If you use the REG32 and FIELD macros from the register API you'll
> also be able to use the FIELD_ family of macros (e.g ARRAY_FIELD_EX32)
> to extract fields from regs.
>
> Will change this


> > +struct SMMUQueue {
> > +     hwaddr base;
> > +     uint32_t prod;
> > +     uint32_t cons;
> > +     union {
> > +          struct {
> > +               uint8_t prod:1;
> > +               uint8_t cons:1;
>
> Hi, Peter generally doesn't like bitfields. I'd stay away form
> them unless you have a good case. Just change them too bool.
>
>
This a wrap field, and used as a circular buffer full/empty indicator.
changing it to bool would loose its meaning, I'll change if its too much
off the
coding standard.



>
> > +
> > +typedef struct __smmu_data2  STEDesc; /* STE Level 1 Descriptor */
> > +typedef struct __smmu_data16 Ste;     /* Stream Table Entry(STE) */
> > +typedef struct __smmu_data2  CDDesc;  /* CD Level 1 Descriptor */
> > +typedef struct __smmu_data16 Cd;      /* Context Descriptor(CD) */
> > +
> > +typedef struct __smmu_data4  Cmd; /* Command Entry */
> > +typedef struct __smmu_data8  Evt; /* Event Entry */
> > +typedef struct __smmu_data4  Pri; /* PRI entry */
>
>
> For all of these, I think it would be more useful if you would declare
> structs with actual fields representing the data structures.
> You can then declare load functions that load the STE from memory and
> decode the fields.
>
> E.g:
>
> typedef struct SMMUv3_STEDesc {
>     bool valid;
>     .... etc...
> } SMMUv3_STEDesc;
>
> void smmuv3_load_ste(AddressSpace *as, dma_addr_t addr, SMMUv3_STEDesc
> *ste)
> {
>     uint32_t buf[16];
>     dma_memory_read(as, addr, buf, sizeof(*buf));
>
>     ste->valid = extract32(buf[0], 0, 1);
> }
>
>
> Then, instead of for example doing STE_VALID(x), you can do ste->valid.
>
>
Thanks
I'll change it to appropriate names and delete where possible.


>
> > +#endif
> > diff --git a/hw/vfio/common.c b/hw/vfio/common.c
> > index e51ed3a..96da537 100644
> > --- a/hw/vfio/common.c
> > +++ b/hw/vfio/common.c
> > @@ -412,10 +412,10 @@ static void vfio_listener_region_add(MemoryListener
> *listener,
> >
> >      ret = vfio_dma_map(container, iova, int128_get64(llsize),
> >                         vaddr, section->readonly);
> > -    if (ret) {
> >          error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
> >                       "0x%"HWADDR_PRIx", %p) = %d (%m)",
> >                       container, iova, int128_get64(llsize), vaddr, ret);
> > +    if (ret) {
> >          goto fail;
> >      }
>
>
> Shouldn't this be in a separate patch?
>
>
>
Will do this, thanks for your time


-- 
Cheers,
/Prem

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

* Re: [Qemu-devel] [PATCH v2 1/9] log: Add new IOMMU type
  2016-09-25 14:58       ` Edgar E. Iglesias
@ 2016-09-26  6:54         ` Auger Eric
  2016-09-26 18:30           ` Edgar E. Iglesias
  0 siblings, 1 reply; 34+ messages in thread
From: Auger Eric @ 2016-09-26  6:54 UTC (permalink / raw)
  To: Edgar E. Iglesias, Prem Mallappa; +Cc: Peter Maydell, QEMU Developers

Hi Edgar, Prem,

On 25/09/2016 16:58, Edgar E. Iglesias wrote:
> On Tue, Sep 13, 2016 at 01:53:39AM +0530, Prem Mallappa wrote:
>> On Fri, Sep 9, 2016 at 9:06 PM, Auger Eric <eric.auger@redhat.com> wrote:
>>
>>> Hi Prem,
>>>
>>> Missing commit message
>>>
>>>> Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
>>>> ---
>>>>  include/qemu/log.h | 1 +
>>>>  util/log.c         | 2 ++
>>>>  2 files changed, 3 insertions(+)
>>>>
>>>> diff --git a/include/qemu/log.h b/include/qemu/log.h
>>>> index 234fa81..3dd2131 100644
>>>> --- a/include/qemu/log.h
>>>> +++ b/include/qemu/log.h
>>>> @@ -42,6 +42,7 @@ static inline bool qemu_log_separate(void)
>>>>  #define CPU_LOG_TB_NOCHAIN (1 << 13)
>>>>  #define CPU_LOG_PAGE       (1 << 14)
>>>>  #define LOG_TRACE          (1 << 15)
>>>> +#define CPU_LOG_IOMMU      (1 << 16)
>>> why is it prefixed with CPU_ ?
>>> besides all arm gic devices seem to use LOG_GUEST_ERROR. what is the
>>> rationale behind introducing a new enum?
>>>
>>
>> Will change this to LOG_GUEST_ERROR, if others on the list are okay.
> 
> Hi,
> 
> LOG_GUEST_ERROR is used for cases when the guest programs things in bad
> way. E.g sets up a register in an invalid manner or writes to regs that
> don't exist.
> 
> In this case we're logging information for valid translation steps, I
> would prefer if we could use something else than LOG_GUEST_ERROR.
> An IOMMU logging class sounds good to me.

Thank you for the clarification; so indeed LOG_GUEST_ERROR which was my
suggestion is not a good idea. With respect to that patch I was also
wondering whether the CPU_ prefix was relevant.

Thanks

Eric
> 
> Best regards,
> Edgar
> 

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

* Re: [Qemu-devel] [PATCH v2 1/9] log: Add new IOMMU type
  2016-09-26  6:54         ` Auger Eric
@ 2016-09-26 18:30           ` Edgar E. Iglesias
  0 siblings, 0 replies; 34+ messages in thread
From: Edgar E. Iglesias @ 2016-09-26 18:30 UTC (permalink / raw)
  To: Auger Eric; +Cc: Prem Mallappa, Peter Maydell, QEMU Developers

On Mon, Sep 26, 2016 at 08:54:52AM +0200, Auger Eric wrote:
> Hi Edgar, Prem,
> 
> On 25/09/2016 16:58, Edgar E. Iglesias wrote:
> > On Tue, Sep 13, 2016 at 01:53:39AM +0530, Prem Mallappa wrote:
> >> On Fri, Sep 9, 2016 at 9:06 PM, Auger Eric <eric.auger@redhat.com> wrote:
> >>
> >>> Hi Prem,
> >>>
> >>> Missing commit message
> >>>
> >>>> Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
> >>>> ---
> >>>>  include/qemu/log.h | 1 +
> >>>>  util/log.c         | 2 ++
> >>>>  2 files changed, 3 insertions(+)
> >>>>
> >>>> diff --git a/include/qemu/log.h b/include/qemu/log.h
> >>>> index 234fa81..3dd2131 100644
> >>>> --- a/include/qemu/log.h
> >>>> +++ b/include/qemu/log.h
> >>>> @@ -42,6 +42,7 @@ static inline bool qemu_log_separate(void)
> >>>>  #define CPU_LOG_TB_NOCHAIN (1 << 13)
> >>>>  #define CPU_LOG_PAGE       (1 << 14)
> >>>>  #define LOG_TRACE          (1 << 15)
> >>>> +#define CPU_LOG_IOMMU      (1 << 16)
> >>> why is it prefixed with CPU_ ?
> >>> besides all arm gic devices seem to use LOG_GUEST_ERROR. what is the
> >>> rationale behind introducing a new enum?
> >>>
> >>
> >> Will change this to LOG_GUEST_ERROR, if others on the list are okay.
> > 
> > Hi,
> > 
> > LOG_GUEST_ERROR is used for cases when the guest programs things in bad
> > way. E.g sets up a register in an invalid manner or writes to regs that
> > don't exist.
> > 
> > In this case we're logging information for valid translation steps, I
> > would prefer if we could use something else than LOG_GUEST_ERROR.
> > An IOMMU logging class sounds good to me.
> 
> Thank you for the clarification; so indeed LOG_GUEST_ERROR which was my
> suggestion is not a good idea. With respect to that patch I was also
> wondering whether the CPU_ prefix was relevant.

Right, the CPU_ prefix should probably be dropped if we use a new IOMMU log mask.

Cheers,
Edgar

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

* Re: [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support
  2016-08-22 16:17 [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Prem Mallappa
                   ` (9 preceding siblings ...)
  2016-08-31 21:44 ` [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Auger Eric
@ 2017-03-08 17:46 ` Auger Eric
  2017-03-27 11:44   ` Edgar E. Iglesias
  10 siblings, 1 reply; 34+ messages in thread
From: Auger Eric @ 2017-03-08 17:46 UTC (permalink / raw)
  To: Prem Mallappa, Peter Maydell, Edgar E . Iglesias, qemu-devel,
	Andrew Jones, Christoffer Dall

Hi,
On 22/08/2016 18:17, Prem Mallappa wrote:
> v1 -> v2:
> 	- Adopted review comments from Eric Auger
> 		- Make SMMU_DPRINTF to internally call qemu_log
> 	            (since translation requests are too many, we need control
> 		     on the type of log we want)
> 		- SMMUTransCfg modified to suite simplicity
> 		- Change RegInfo to uint64 register array
> 		- Code cleanup
> 		- Test cleanups
> 	- Reshuffled patches
> 
> RFC -> v1:
> 	- As per SMMUv3 spec 16.0 (only is_ste_consistant() is noticeable)
> 	- Reworked register access/update logic
> 	- Factored out translation code for
> 		- single point bug fix
> 		- sharing/removal in future
> 	- (optional) Unit tests added, with PCI test device
> 		- S1 with 4k/64k, S1+S2 with 4k/64k
> 		- (S1 or S2) only can be verified by Linux 4.7 driver
> 	- (optional) Priliminary ACPI support
> 
> RFC:
> 	- Implements SMMUv3 spec 11.0
> 	- Supported for PCIe devices, 
> 	- Command Queue and Event Queue supported
> 	- LPAE only, S1 is supported and Tested, S2 not tested
> 	- BE mode Translation not supported
> 	- IRQ support (legacy, no MSI)
> 	- Tested with DPDK and e1000 
> 
> Patch 1: Add new log type for IOMMU transactions
> 
> Patch 2: Adds support in virt.c to create both SMMUv3 device and dts entries
> 
> Patch 2: Adds SMMUv3 model to QEMU
> 	Multiple files, big ones, translate functionality is split across to
> 	accomodate SMMUv2 model, and to remove when common translation feature
> 	(if) becomes available.
> 
> Patch 3: Adds SMMU build support
> 
> Patch 4: Some devicetree function to add support for SMMU's multiple interrupt
> 	 assignment with names
> 
> << optional patches >>
> Optional patches are posted for completeness or for those who wants to test.
> 
> Patch 5: A simple PCI device which does DMA from 'src' to 'dst' given
> 	 src_addr, dst_addr and size, and is used by unit test, uses
> 	 pci_dma_read and pci_dma_write in a crude way but serves the purpose.
> 
> Patch 6: Current libqos PCI helpers are x86 only, this addes a generic interface
> 
> Patch 7: Unit tests for SMMU, 
> 		- initializes SMMU device 
> 		- initializes Test device
> 		- allocates page tables 1:1 mapping va == pa
> 		- allocates STE/CD accordingly for S1, S2, S1+S2
> 		- initiates DMA via PCI test device
> 		- verifies transfered data
> 
> Patch 8: Added ACPI IORT tables, was needed for internal project purpose, but 
> 	 posting here for anyone looking for testing ACPI on ARM platforms.
> 	 (P.S: Linux side IORT patches are WIP)
> 
> Repo:
> https://github.com/pmallappa/qemu/tree/upstream/smmuv3/v2
> 
> To Test:
> $ make tests/smmuv3-test
> $ QTEST_QEMU_BINARY=aarch64-softmmu/qemu-system-aarch64 tests/smmuv3-test
> << expect lot of prints >>
> 
> Any comments welcome..
As Prem was forced to stop his activity on this series, I volunteer to
pursue his work. Prior to starting the work, I just would like to check
nobody works on this already or objects.

If not, I intend to rebase and will do my utmost to align, when sensible
with what was done on Xilinx vsmmuv2/intel iommu.

Thanks

Eric
> 
> Cheers
> /Prem
> 
> Prem Mallappa (9):
>   log: Add new IOMMU type
>   devicetree: Added new APIs to make use of more fdt functions
>   hw: arm: SMMUv3 emulation model
>   hw: arm: Added SMMUv3 files for build
>   hw: arm: Add SMMUv3 to virt platform, create DTS accordingly
>   [optional] hw: misc: added testdev for smmu
>   [optional] tests: libqos: generic pci probing helpers
>   [optional] tests: SMMUv3 unit tests
>   [optional] arm: smmu-v3: ACPI IORT initial support
> 
>  default-configs/aarch64-softmmu.mak |    1 +
>  device_tree.c                       |   35 +
>  hw/arm/Makefile.objs                |    1 +
>  hw/arm/smmu-common.c                |  152 ++++
>  hw/arm/smmu-common.h                |  141 ++++
>  hw/arm/smmu-v3.c                    | 1369 +++++++++++++++++++++++++++++++++++
>  hw/arm/smmuv3-internal.h            |  432 +++++++++++
>  hw/arm/virt-acpi-build.c            |   43 ++
>  hw/arm/virt.c                       |   62 ++
>  hw/misc/Makefile.objs               |    2 +-
>  hw/misc/pci-testdev-smmu.c          |  239 ++++++
>  hw/misc/pci-testdev-smmu.h          |   22 +
>  hw/vfio/common.c                    |    2 +-
>  include/hw/acpi/acpi-defs.h         |   84 +++
>  include/hw/arm/smmu.h               |   33 +
>  include/hw/arm/virt.h               |    2 +
>  include/qemu/log.h                  |    1 +
>  include/sysemu/device_tree.h        |   18 +
>  tests/Makefile.include              |    4 +
>  tests/libqos/pci-generic.c          |  197 +++++
>  tests/libqos/pci-generic.h          |   58 ++
>  tests/smmuv3-test.c                 |  952 ++++++++++++++++++++++++
>  util/log.c                          |    2 +
>  23 files changed, 3850 insertions(+), 2 deletions(-)
>  create mode 100644 hw/arm/smmu-common.c
>  create mode 100644 hw/arm/smmu-common.h
>  create mode 100644 hw/arm/smmu-v3.c
>  create mode 100644 hw/arm/smmuv3-internal.h
>  create mode 100644 hw/misc/pci-testdev-smmu.c
>  create mode 100644 hw/misc/pci-testdev-smmu.h
>  create mode 100644 include/hw/arm/smmu.h
>  create mode 100644 tests/libqos/pci-generic.c
>  create mode 100644 tests/libqos/pci-generic.h
>  create mode 100644 tests/smmuv3-test.c
> 

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

* Re: [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support
  2017-03-08 17:46 ` Auger Eric
@ 2017-03-27 11:44   ` Edgar E. Iglesias
  2017-03-27 12:18     ` Auger Eric
  0 siblings, 1 reply; 34+ messages in thread
From: Edgar E. Iglesias @ 2017-03-27 11:44 UTC (permalink / raw)
  To: Auger Eric
  Cc: Prem Mallappa, Peter Maydell, qemu-devel, Andrew Jones, Christoffer Dall

On Wed, Mar 08, 2017 at 06:46:13PM +0100, Auger Eric wrote:
> Hi,
> On 22/08/2016 18:17, Prem Mallappa wrote:
> > v1 -> v2:
> > 	- Adopted review comments from Eric Auger
> > 		- Make SMMU_DPRINTF to internally call qemu_log
> > 	            (since translation requests are too many, we need control
> > 		     on the type of log we want)
> > 		- SMMUTransCfg modified to suite simplicity
> > 		- Change RegInfo to uint64 register array
> > 		- Code cleanup
> > 		- Test cleanups
> > 	- Reshuffled patches
> > 
> > RFC -> v1:
> > 	- As per SMMUv3 spec 16.0 (only is_ste_consistant() is noticeable)
> > 	- Reworked register access/update logic
> > 	- Factored out translation code for
> > 		- single point bug fix
> > 		- sharing/removal in future
> > 	- (optional) Unit tests added, with PCI test device
> > 		- S1 with 4k/64k, S1+S2 with 4k/64k
> > 		- (S1 or S2) only can be verified by Linux 4.7 driver
> > 	- (optional) Priliminary ACPI support
> > 
> > RFC:
> > 	- Implements SMMUv3 spec 11.0
> > 	- Supported for PCIe devices, 
> > 	- Command Queue and Event Queue supported
> > 	- LPAE only, S1 is supported and Tested, S2 not tested
> > 	- BE mode Translation not supported
> > 	- IRQ support (legacy, no MSI)
> > 	- Tested with DPDK and e1000 
> > 
> > Patch 1: Add new log type for IOMMU transactions
> > 
> > Patch 2: Adds support in virt.c to create both SMMUv3 device and dts entries
> > 
> > Patch 2: Adds SMMUv3 model to QEMU
> > 	Multiple files, big ones, translate functionality is split across to
> > 	accomodate SMMUv2 model, and to remove when common translation feature
> > 	(if) becomes available.
> > 
> > Patch 3: Adds SMMU build support
> > 
> > Patch 4: Some devicetree function to add support for SMMU's multiple interrupt
> > 	 assignment with names
> > 
> > << optional patches >>
> > Optional patches are posted for completeness or for those who wants to test.
> > 
> > Patch 5: A simple PCI device which does DMA from 'src' to 'dst' given
> > 	 src_addr, dst_addr and size, and is used by unit test, uses
> > 	 pci_dma_read and pci_dma_write in a crude way but serves the purpose.
> > 
> > Patch 6: Current libqos PCI helpers are x86 only, this addes a generic interface
> > 
> > Patch 7: Unit tests for SMMU, 
> > 		- initializes SMMU device 
> > 		- initializes Test device
> > 		- allocates page tables 1:1 mapping va == pa
> > 		- allocates STE/CD accordingly for S1, S2, S1+S2
> > 		- initiates DMA via PCI test device
> > 		- verifies transfered data
> > 
> > Patch 8: Added ACPI IORT tables, was needed for internal project purpose, but 
> > 	 posting here for anyone looking for testing ACPI on ARM platforms.
> > 	 (P.S: Linux side IORT patches are WIP)
> > 
> > Repo:
> > https://github.com/pmallappa/qemu/tree/upstream/smmuv3/v2
> > 
> > To Test:
> > $ make tests/smmuv3-test
> > $ QTEST_QEMU_BINARY=aarch64-softmmu/qemu-system-aarch64 tests/smmuv3-test
> > << expect lot of prints >>
> > 
> > Any comments welcome..
> As Prem was forced to stop his activity on this series, I volunteer to
> pursue his work. Prior to starting the work, I just would like to check
> nobody works on this already or objects.
> 
> If not, I intend to rebase and will do my utmost to align, when sensible
> with what was done on Xilinx vsmmuv2/intel iommu.


That would be awesome! Sorry for the late reply.

I had some comments on the PCI integration that Prem did but I'm not sure
it matters now if you're going to go through the work. We can do a review
of your future patches instead.

Cheers,
Edgar

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

* Re: [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support
  2017-03-27 11:44   ` Edgar E. Iglesias
@ 2017-03-27 12:18     ` Auger Eric
  2017-03-27 12:28       ` Edgar E. Iglesias
  0 siblings, 1 reply; 34+ messages in thread
From: Auger Eric @ 2017-03-27 12:18 UTC (permalink / raw)
  To: Edgar E. Iglesias
  Cc: Peter Maydell, Andrew Jones, qemu-devel, Christoffer Dall

Hi Edgar,

removing Prem's address which is not valid anymore

On 27/03/2017 13:44, Edgar E. Iglesias wrote:
> On Wed, Mar 08, 2017 at 06:46:13PM +0100, Auger Eric wrote:
>> Hi,
>> On 22/08/2016 18:17, Prem Mallappa wrote:
>>> v1 -> v2:
>>> 	- Adopted review comments from Eric Auger
>>> 		- Make SMMU_DPRINTF to internally call qemu_log
>>> 	            (since translation requests are too many, we need control
>>> 		     on the type of log we want)
>>> 		- SMMUTransCfg modified to suite simplicity
>>> 		- Change RegInfo to uint64 register array
>>> 		- Code cleanup
>>> 		- Test cleanups
>>> 	- Reshuffled patches
>>>
>>> RFC -> v1:
>>> 	- As per SMMUv3 spec 16.0 (only is_ste_consistant() is noticeable)
>>> 	- Reworked register access/update logic
>>> 	- Factored out translation code for
>>> 		- single point bug fix
>>> 		- sharing/removal in future
>>> 	- (optional) Unit tests added, with PCI test device
>>> 		- S1 with 4k/64k, S1+S2 with 4k/64k
>>> 		- (S1 or S2) only can be verified by Linux 4.7 driver
>>> 	- (optional) Priliminary ACPI support
>>>
>>> RFC:
>>> 	- Implements SMMUv3 spec 11.0
>>> 	- Supported for PCIe devices, 
>>> 	- Command Queue and Event Queue supported
>>> 	- LPAE only, S1 is supported and Tested, S2 not tested
>>> 	- BE mode Translation not supported
>>> 	- IRQ support (legacy, no MSI)
>>> 	- Tested with DPDK and e1000 
>>>
>>> Patch 1: Add new log type for IOMMU transactions
>>>
>>> Patch 2: Adds support in virt.c to create both SMMUv3 device and dts entries
>>>
>>> Patch 2: Adds SMMUv3 model to QEMU
>>> 	Multiple files, big ones, translate functionality is split across to
>>> 	accomodate SMMUv2 model, and to remove when common translation feature
>>> 	(if) becomes available.
>>>
>>> Patch 3: Adds SMMU build support
>>>
>>> Patch 4: Some devicetree function to add support for SMMU's multiple interrupt
>>> 	 assignment with names
>>>
>>> << optional patches >>
>>> Optional patches are posted for completeness or for those who wants to test.
>>>
>>> Patch 5: A simple PCI device which does DMA from 'src' to 'dst' given
>>> 	 src_addr, dst_addr and size, and is used by unit test, uses
>>> 	 pci_dma_read and pci_dma_write in a crude way but serves the purpose.
>>>
>>> Patch 6: Current libqos PCI helpers are x86 only, this addes a generic interface
>>>
>>> Patch 7: Unit tests for SMMU, 
>>> 		- initializes SMMU device 
>>> 		- initializes Test device
>>> 		- allocates page tables 1:1 mapping va == pa
>>> 		- allocates STE/CD accordingly for S1, S2, S1+S2
>>> 		- initiates DMA via PCI test device
>>> 		- verifies transfered data
>>>
>>> Patch 8: Added ACPI IORT tables, was needed for internal project purpose, but 
>>> 	 posting here for anyone looking for testing ACPI on ARM platforms.
>>> 	 (P.S: Linux side IORT patches are WIP)
>>>
>>> Repo:
>>> https://github.com/pmallappa/qemu/tree/upstream/smmuv3/v2
>>>
>>> To Test:
>>> $ make tests/smmuv3-test
>>> $ QTEST_QEMU_BINARY=aarch64-softmmu/qemu-system-aarch64 tests/smmuv3-test
>>> << expect lot of prints >>
>>>
>>> Any comments welcome..
>> As Prem was forced to stop his activity on this series, I volunteer to
>> pursue his work. Prior to starting the work, I just would like to check
>> nobody works on this already or objects.
>>
>> If not, I intend to rebase and will do my utmost to align, when sensible
>> with what was done on Xilinx vsmmuv2/intel iommu.
> 
> 
> That would be awesome! Sorry for the late reply.
no worries.
> 
> I had some comments on the PCI integration that Prem did but I'm not sure
> it matters now if you're going to go through the work. We can do a review
> of your future patches instead.

I plan to send a respin this week. As I am still getting familiar with
the code, this may be mostly a rebase on 2.9 plus some cleanups. Then I
plan to dig into more details, add missing features, maybe reuse your
table translation code if you allow me to do so, share a base class. So
I will be delighted to share about enhancements and possible reuse of
your code too.

note: at the moment I chose not to use the register API since you said
that maybe for this IP it didn't bring much added-value.

Thanks

Eric
> 
> Cheers,
> Edgar
> 

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

* Re: [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support
  2017-03-27 12:18     ` Auger Eric
@ 2017-03-27 12:28       ` Edgar E. Iglesias
  0 siblings, 0 replies; 34+ messages in thread
From: Edgar E. Iglesias @ 2017-03-27 12:28 UTC (permalink / raw)
  To: Auger Eric; +Cc: Peter Maydell, Andrew Jones, qemu-devel, Christoffer Dall

On Mon, Mar 27, 2017 at 02:18:56PM +0200, Auger Eric wrote:
> Hi Edgar,
> 
> removing Prem's address which is not valid anymore
> 
> On 27/03/2017 13:44, Edgar E. Iglesias wrote:
> > On Wed, Mar 08, 2017 at 06:46:13PM +0100, Auger Eric wrote:
> >> Hi,
> >> On 22/08/2016 18:17, Prem Mallappa wrote:
> >>> v1 -> v2:
> >>> 	- Adopted review comments from Eric Auger
> >>> 		- Make SMMU_DPRINTF to internally call qemu_log
> >>> 	            (since translation requests are too many, we need control
> >>> 		     on the type of log we want)
> >>> 		- SMMUTransCfg modified to suite simplicity
> >>> 		- Change RegInfo to uint64 register array
> >>> 		- Code cleanup
> >>> 		- Test cleanups
> >>> 	- Reshuffled patches
> >>>
> >>> RFC -> v1:
> >>> 	- As per SMMUv3 spec 16.0 (only is_ste_consistant() is noticeable)
> >>> 	- Reworked register access/update logic
> >>> 	- Factored out translation code for
> >>> 		- single point bug fix
> >>> 		- sharing/removal in future
> >>> 	- (optional) Unit tests added, with PCI test device
> >>> 		- S1 with 4k/64k, S1+S2 with 4k/64k
> >>> 		- (S1 or S2) only can be verified by Linux 4.7 driver
> >>> 	- (optional) Priliminary ACPI support
> >>>
> >>> RFC:
> >>> 	- Implements SMMUv3 spec 11.0
> >>> 	- Supported for PCIe devices, 
> >>> 	- Command Queue and Event Queue supported
> >>> 	- LPAE only, S1 is supported and Tested, S2 not tested
> >>> 	- BE mode Translation not supported
> >>> 	- IRQ support (legacy, no MSI)
> >>> 	- Tested with DPDK and e1000 
> >>>
> >>> Patch 1: Add new log type for IOMMU transactions
> >>>
> >>> Patch 2: Adds support in virt.c to create both SMMUv3 device and dts entries
> >>>
> >>> Patch 2: Adds SMMUv3 model to QEMU
> >>> 	Multiple files, big ones, translate functionality is split across to
> >>> 	accomodate SMMUv2 model, and to remove when common translation feature
> >>> 	(if) becomes available.
> >>>
> >>> Patch 3: Adds SMMU build support
> >>>
> >>> Patch 4: Some devicetree function to add support for SMMU's multiple interrupt
> >>> 	 assignment with names
> >>>
> >>> << optional patches >>
> >>> Optional patches are posted for completeness or for those who wants to test.
> >>>
> >>> Patch 5: A simple PCI device which does DMA from 'src' to 'dst' given
> >>> 	 src_addr, dst_addr and size, and is used by unit test, uses
> >>> 	 pci_dma_read and pci_dma_write in a crude way but serves the purpose.
> >>>
> >>> Patch 6: Current libqos PCI helpers are x86 only, this addes a generic interface
> >>>
> >>> Patch 7: Unit tests for SMMU, 
> >>> 		- initializes SMMU device 
> >>> 		- initializes Test device
> >>> 		- allocates page tables 1:1 mapping va == pa
> >>> 		- allocates STE/CD accordingly for S1, S2, S1+S2
> >>> 		- initiates DMA via PCI test device
> >>> 		- verifies transfered data
> >>>
> >>> Patch 8: Added ACPI IORT tables, was needed for internal project purpose, but 
> >>> 	 posting here for anyone looking for testing ACPI on ARM platforms.
> >>> 	 (P.S: Linux side IORT patches are WIP)
> >>>
> >>> Repo:
> >>> https://github.com/pmallappa/qemu/tree/upstream/smmuv3/v2
> >>>
> >>> To Test:
> >>> $ make tests/smmuv3-test
> >>> $ QTEST_QEMU_BINARY=aarch64-softmmu/qemu-system-aarch64 tests/smmuv3-test
> >>> << expect lot of prints >>
> >>>
> >>> Any comments welcome..
> >> As Prem was forced to stop his activity on this series, I volunteer to
> >> pursue his work. Prior to starting the work, I just would like to check
> >> nobody works on this already or objects.
> >>
> >> If not, I intend to rebase and will do my utmost to align, when sensible
> >> with what was done on Xilinx vsmmuv2/intel iommu.
> > 
> > 
> > That would be awesome! Sorry for the late reply.
> no worries.
> > 
> > I had some comments on the PCI integration that Prem did but I'm not sure
> > it matters now if you're going to go through the work. We can do a review
> > of your future patches instead.
> 
> I plan to send a respin this week. As I am still getting familiar with
> the code, this may be mostly a rebase on 2.9 plus some cleanups. Then I
> plan to dig into more details, add missing features, maybe reuse your
> table translation code if you allow me to do so, share a base class. So
> I will be delighted to share about enhancements and possible reuse of
> your code too.

Sounds great. Feel free to re-use anything from Xilinx if needed.

> 
> note: at the moment I chose not to use the register API since you said
> that maybe for this IP it didn't bring much added-value.


Due to the large amounts of registers that share implementation it
may not be the best example to showcase the Register API. But if you
feel it comes handy for some reason, go for it. I don't really mind
either way.

Thanks,
Edgar


> 
> Thanks
> 
> Eric
> > 
> > Cheers,
> > Edgar
> > 

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

* Re: [Qemu-devel] [PATCH v2 6/9] [optional] hw: misc: added testdev for smmu
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 6/9] [optional] hw: misc: added testdev for smmu Prem Mallappa
@ 2017-03-27 15:24   ` Philippe Mathieu-Daudé
  2017-03-27 15:41   ` Andrew Jones
  1 sibling, 0 replies; 34+ messages in thread
From: Philippe Mathieu-Daudé @ 2017-03-27 15:24 UTC (permalink / raw)
  To: Prem Mallappa; +Cc: Peter Maydell, Edgar E . Iglesias, qemu-devel

Hi Prem,

On 08/22/2016 01:17 PM, Prem Mallappa wrote:
> A simple PCI device which does DMA from 'src' to 'dst' given
> src_addr, dst_addr and size, and is used by unit test. uses
> pci_dma_read and pci_dma_write in a crude way but serves the purpose.
>
> Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
> ---
>  hw/misc/Makefile.objs      |   2 +-
>  hw/misc/pci-testdev-smmu.c | 239 +++++++++++++++++++++++++++++++++++++++++++++
>  hw/misc/pci-testdev-smmu.h |  22 +++++
>  3 files changed, 262 insertions(+), 1 deletion(-)
>  create mode 100644 hw/misc/pci-testdev-smmu.c
>  create mode 100644 hw/misc/pci-testdev-smmu.h
>
> diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
> index ffb49c1..fc34c5f 100644
> --- a/hw/misc/Makefile.objs
> +++ b/hw/misc/Makefile.objs
> @@ -29,7 +29,6 @@ obj-$(CONFIG_IMX) += imx_ccm.o
>  obj-$(CONFIG_IMX) += imx31_ccm.o
>  obj-$(CONFIG_IMX) += imx25_ccm.o
>  obj-$(CONFIG_IMX) += imx6_ccm.o
> -obj-$(CONFIG_IMX) += imx6_src.o

this change seems unrelated to your patch.

>  obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
>  obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
>  obj-$(CONFIG_MAINSTONE) += mst_fpga.o
> @@ -52,3 +51,4 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o
>  obj-$(CONFIG_EDU) += edu.o
>  obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
>  obj-$(CONFIG_AUX) += aux.o
> +obj-$(CONFIG_ARM_SMMUV3) += pci-testdev-smmu.o
> diff --git a/hw/misc/pci-testdev-smmu.c b/hw/misc/pci-testdev-smmu.c
> new file mode 100644
> index 0000000..b605912
> --- /dev/null
> +++ b/hw/misc/pci-testdev-smmu.c
> @@ -0,0 +1,239 @@
> +/*
> + * QEMU PCI test device
> + *
> + * Copyright (c) 2012 Red Hat Inc.
> + * Author: Michael S. Tsirkin <mst@redhat.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +#include "qemu/osdep.h"
> +#include "hw/hw.h"
> +#include "hw/pci/pci.h"
> +#include "qemu/event_notifier.h"
> +
> +#include "pci-testdev-smmu.h"
> +
> +/*
> + * pci-testdev-smmu:
> + *          Simple PCIe device, to enable read and write from memory.
> + * Architecture:
> + *          Following registers are supported.
> + *          TST_COMMAND = 0x0
> + *          TST_STATUS  = 0x4
> + *          TST_SRC_ADDRESS = 0x8
> + *          TST_SIZE        = 0x10
> + *          TST_DST_ADDRESS = 0x18
> + */
> +#define PCI_TSTDEV_NREGS 0x10
> +
> +/*
> + *  TST_COMMAND Register bits
> + *      OP[0]
> + *          READ = 0x0
> + *          WRITE = 0x1
> + */
> +
> +struct RegInfo {
> +        uint64_t data;
> +        char *name;
> +};
> +typedef struct RegInfo RegInfo;
> +
> +typedef struct PCITestDevState {
> +    /*< private >*/
> +    PCIDevice dev;
> +    /*< public >*/
> +
> +    MemoryRegion mmio;
> +    RegInfo regs[PCI_TSTDEV_NREGS];
> +} PCITestDevState;
> +
> +#define TYPE_PCI_TEST_DEV "pci-testdev-smmu"
> +
> +#define PCI_TEST_DEV(obj) \
> +    OBJECT_CHECK(PCITestDevState, (obj), TYPE_PCI_TEST_DEV)
> +
> +static void
> +pci_tstdev_reset(PCITestDevState *d)
> +{
> +    memset(d->regs, 0, sizeof(d->regs));
> +}
> +
> +static inline void
> +pci_tstdev_write_reg(PCITestDevState *pdev, hwaddr addr, uint64_t val)
> +{
> +    RegInfo *reg = &pdev->regs[addr >> 2];
> +    reg->data = val;
> +}
> +
> +static inline uint32_t
> +pci_tstdev_read32_reg(PCITestDevState *pdev, hwaddr addr)
> +{
> +    RegInfo *reg = &pdev->regs[addr >> 2];
> +    return (uint32_t) reg->data;
> +}
> +
> +static inline uint64_t
> +pci_tstdev_read64_reg(PCITestDevState *pdev, hwaddr addr)
> +{
> +        RegInfo *reg = &pdev->regs[addr >> 2];
> +        return reg->data;
> +}
> +
> +static void
> +pci_tstdev_handle_cmd(PCITestDevState *pdev, hwaddr addr, uint64_t val,
> +                            unsigned _unused_size)
> +{
> +    uint64_t s = pci_tstdev_read64_reg(pdev, TST_REG_SRC_ADDR);
> +    uint64_t d = pci_tstdev_read64_reg(pdev, TST_REG_DST_ADDR);
> +    uint32_t size = pci_tstdev_read32_reg(pdev, TST_REG_SIZE);
> +    uint8_t buf[128];
> +
> +    printf("+++++++++++++++++++++> src:%lx, dst:%lx size:%d\n",
> +           s, d, size);
> +    while (size) {
> +        int nbytes = (size < sizeof(buf)) ? size: sizeof(buf);
> +        int ret = 0;
> +        printf("nbytes:%d\n", nbytes);
> +        if (val & CMD_READ) {
> +            printf("doing pci_dma_read\n");
> +            ret = pci_dma_read(&pdev->dev, s, (void*)buf, nbytes);
> +        }
> +        if (ret)
> +            return;
> +
> +        if (val & CMD_WRITE) {
> +            printf("doing pci_dma_write\n");
> +            ret = pci_dma_write(&pdev->dev, d, (void*)buf, nbytes);
> +        }
> +        size -= nbytes;
> +        s += nbytes;
> +        d += nbytes;
> +    }
> +}
> +
> +static void
> +pci_tstdev_mmio_write(void *opaque, hwaddr addr,
> +                      uint64_t val, unsigned size)
> +{
> +    PCITestDevState *d = opaque;
> +    uint64_t lo;
> +
> +    printf("=================> addr:%ld act:%d val:%lx reg_addr:%p\n",
> +           addr, TST_REG_COMMAND, val, &d->regs[addr]);
> +    //addr >>= 2;
> +    switch (addr) {
> +    case TST_REG_COMMAND:
> +            printf("Calling command handler.....\n");
> +            pci_tstdev_handle_cmd(d, addr, val, size);
> +    case TST_REG_SRC_ADDR:
> +    case TST_REG_DST_ADDR:
> +    case TST_REG_SIZE:
> +            pci_tstdev_write_reg(d, addr, val);
> +            break;
> +    case TST_REG_SRC_ADDR + 4:
> +    case TST_REG_DST_ADDR + 4:
> +            lo = pci_tstdev_read32_reg(d, addr);
> +            lo &= (0xffffffffULL << 32);
> +            pci_tstdev_write_reg(d, addr, (val << 32) | lo);
> +            break;
> +    case TST_REG_STATUS:        /* Read only reg */
> +    default:
> +        printf("Unkown/RO register write\n");
> +        break;
> +    }
> +}
> +
> +static uint64_t
> +pci_tstdev_mmio_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    PCITestDevState *d = opaque;
> +
> +    switch (addr) {
> +    case TST_REG_SRC_ADDR:
> +    case TST_REG_DST_ADDR:
> +            return pci_tstdev_read64_reg(d, addr);
> +    }
> +
> +    return pci_tstdev_read32_reg(d, addr);
> +}
> +
> +static const MemoryRegionOps pci_testdev_mmio_ops = {
> +    .read = pci_tstdev_mmio_read,
> +    .write = pci_tstdev_mmio_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .impl = {
> +        .min_access_size = 1,
> +        .max_access_size = 8,
> +    },
> +};
> +
> +static void pci_tstdev_realize(PCIDevice *pci_dev, Error **errp)
> +{
> +    PCITestDevState *d = PCI_TEST_DEV(pci_dev);
> +    uint8_t *pci_conf;
> +
> +    pci_conf = pci_dev->config;
> +
> +    pci_conf[PCI_INTERRUPT_PIN] = 0; /* no interrupt pin */
> +
> +    memory_region_init_io(&d->mmio, OBJECT(d), &pci_testdev_mmio_ops, d,
> +                          "pci-testdev-smmu-mmio", 1 << 10);
> +
> +    pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
> +}
> +
> +static void
> +pci_tstdev_uninit(PCIDevice *dev)
> +{
> +    PCITestDevState *d = PCI_TEST_DEV(dev);
> +
> +    pci_tstdev_reset(d);
> +}
> +
> +static void qdev_pci_tstdev_reset(DeviceState *dev)
> +{
> +    PCITestDevState *d = PCI_TEST_DEV(dev);
> +    pci_tstdev_reset(d);
> +}
> +
> +static void pci_tstdev_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> +
> +    k->realize = pci_tstdev_realize;
> +    k->exit = pci_tstdev_uninit;
> +    k->vendor_id = PCI_VENDOR_ID_REDHAT;
> +    k->device_id = PCI_DEVICE_ID_REDHAT_TEST;
> +    k->revision = 0x00;
> +    k->class_id = PCI_CLASS_OTHERS;
> +    dc->desc = "PCI Test Device - for smmu";
> +    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
> +    dc->reset = qdev_pci_tstdev_reset;
> +}
> +
> +static const TypeInfo pci_tstdev_info = {
> +    .name          = TYPE_PCI_TEST_DEV,
> +    .parent        = TYPE_PCI_DEVICE,
> +    .instance_size = sizeof(PCITestDevState),
> +    .class_init    = pci_tstdev_class_init,
> +};
> +
> +static void pci_tstdev_register_types(void)
> +{
> +    type_register_static(&pci_tstdev_info);
> +}
> +
> +type_init(pci_tstdev_register_types)
> diff --git a/hw/misc/pci-testdev-smmu.h b/hw/misc/pci-testdev-smmu.h
> new file mode 100644
> index 0000000..4a2d463
> --- /dev/null
> +++ b/hw/misc/pci-testdev-smmu.h
> @@ -0,0 +1,22 @@
> +#ifndef HW_MISC_PCI_TESTDEV_SMMU
> +#define HW_MISC_PCI_TESTDEV_SMMU
> +
> +enum reg {
> +        TST_REG_COMMAND  = 0x0,
> +        TST_REG_STATUS   = 0x4,
> +        TST_REG_SRC_ADDR = 0x8,
> +        TST_REG_SIZE     = 0x10,
> +        TST_REG_DST_ADDR = 0x18,
> +
> +        TST_REG_LAST     = 0x30,
> +};
> +
> +#define CMD_READ    0x100
> +#define CMD_WRITE   0x200
> +#define CMD_RW      (CMD_READ | CMD_WRITE)
> +
> +#define STATUS_OK   (1 << 0)
> +#define STATUS_CMD_ERROR (1 << 1)
> +#define STATUS_CMD_INVALID (1 << 2)
> +
> +#endif
>

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

* Re: [Qemu-devel] [PATCH v2 6/9] [optional] hw: misc: added testdev for smmu
  2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 6/9] [optional] hw: misc: added testdev for smmu Prem Mallappa
  2017-03-27 15:24   ` Philippe Mathieu-Daudé
@ 2017-03-27 15:41   ` Andrew Jones
  1 sibling, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2017-03-27 15:41 UTC (permalink / raw)
  To: Prem Mallappa; +Cc: Peter Maydell, Edgar E . Iglesias, qemu-devel

On Mon, Aug 22, 2016 at 09:47:37PM +0530, Prem Mallappa wrote:
> A simple PCI device which does DMA from 'src' to 'dst' given
> src_addr, dst_addr and size, and is used by unit test. uses
> pci_dma_read and pci_dma_write in a crude way but serves the purpose.
> 
> Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com>
> ---
>  hw/misc/Makefile.objs      |   2 +-
>  hw/misc/pci-testdev-smmu.c | 239 +++++++++++++++++++++++++++++++++++++++++++++
>  hw/misc/pci-testdev-smmu.h |  22 +++++
>  3 files changed, 262 insertions(+), 1 deletion(-)
>  create mode 100644 hw/misc/pci-testdev-smmu.c
>  create mode 100644 hw/misc/pci-testdev-smmu.h
> 
> diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
> index ffb49c1..fc34c5f 100644
> --- a/hw/misc/Makefile.objs
> +++ b/hw/misc/Makefile.objs
> @@ -29,7 +29,6 @@ obj-$(CONFIG_IMX) += imx_ccm.o
>  obj-$(CONFIG_IMX) += imx31_ccm.o
>  obj-$(CONFIG_IMX) += imx25_ccm.o
>  obj-$(CONFIG_IMX) += imx6_ccm.o
> -obj-$(CONFIG_IMX) += imx6_src.o
>  obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
>  obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
>  obj-$(CONFIG_MAINSTONE) += mst_fpga.o
> @@ -52,3 +51,4 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o
>  obj-$(CONFIG_EDU) += edu.o
>  obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
>  obj-$(CONFIG_AUX) += aux.o
> +obj-$(CONFIG_ARM_SMMUV3) += pci-testdev-smmu.o
> diff --git a/hw/misc/pci-testdev-smmu.c b/hw/misc/pci-testdev-smmu.c
> new file mode 100644
> index 0000000..b605912
> --- /dev/null
> +++ b/hw/misc/pci-testdev-smmu.c
> @@ -0,0 +1,239 @@
> +/*
> + * QEMU PCI test device
> + *
> + * Copyright (c) 2012 Red Hat Inc.
> + * Author: Michael S. Tsirkin <mst@redhat.com>

Header copy+paste mistake.

Anyway, this device appears to be redundant with the hw/misc/edu.c
device.

Thanks,
drew

> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +#include "qemu/osdep.h"
> +#include "hw/hw.h"
> +#include "hw/pci/pci.h"
> +#include "qemu/event_notifier.h"
> +
> +#include "pci-testdev-smmu.h"
> +
> +/*
> + * pci-testdev-smmu:
> + *          Simple PCIe device, to enable read and write from memory.
> + * Architecture:
> + *          Following registers are supported.
> + *          TST_COMMAND = 0x0
> + *          TST_STATUS  = 0x4
> + *          TST_SRC_ADDRESS = 0x8
> + *          TST_SIZE        = 0x10
> + *          TST_DST_ADDRESS = 0x18
> + */
> +#define PCI_TSTDEV_NREGS 0x10
> +
> +/*
> + *  TST_COMMAND Register bits
> + *      OP[0]
> + *          READ = 0x0
> + *          WRITE = 0x1
> + */
> +
> +struct RegInfo {
> +        uint64_t data;
> +        char *name;
> +};
> +typedef struct RegInfo RegInfo;
> +
> +typedef struct PCITestDevState {
> +    /*< private >*/
> +    PCIDevice dev;
> +    /*< public >*/
> +
> +    MemoryRegion mmio;
> +    RegInfo regs[PCI_TSTDEV_NREGS];
> +} PCITestDevState;
> +
> +#define TYPE_PCI_TEST_DEV "pci-testdev-smmu"
> +
> +#define PCI_TEST_DEV(obj) \
> +    OBJECT_CHECK(PCITestDevState, (obj), TYPE_PCI_TEST_DEV)
> +
> +static void
> +pci_tstdev_reset(PCITestDevState *d)
> +{
> +    memset(d->regs, 0, sizeof(d->regs));
> +}
> +
> +static inline void
> +pci_tstdev_write_reg(PCITestDevState *pdev, hwaddr addr, uint64_t val)
> +{
> +    RegInfo *reg = &pdev->regs[addr >> 2];
> +    reg->data = val;
> +}
> +
> +static inline uint32_t
> +pci_tstdev_read32_reg(PCITestDevState *pdev, hwaddr addr)
> +{
> +    RegInfo *reg = &pdev->regs[addr >> 2];
> +    return (uint32_t) reg->data;
> +}
> +
> +static inline uint64_t
> +pci_tstdev_read64_reg(PCITestDevState *pdev, hwaddr addr)
> +{
> +        RegInfo *reg = &pdev->regs[addr >> 2];
> +        return reg->data;
> +}
> +
> +static void
> +pci_tstdev_handle_cmd(PCITestDevState *pdev, hwaddr addr, uint64_t val,
> +                            unsigned _unused_size)
> +{
> +    uint64_t s = pci_tstdev_read64_reg(pdev, TST_REG_SRC_ADDR);
> +    uint64_t d = pci_tstdev_read64_reg(pdev, TST_REG_DST_ADDR);
> +    uint32_t size = pci_tstdev_read32_reg(pdev, TST_REG_SIZE);
> +    uint8_t buf[128];
> +
> +    printf("+++++++++++++++++++++> src:%lx, dst:%lx size:%d\n",
> +           s, d, size);
> +    while (size) {
> +        int nbytes = (size < sizeof(buf)) ? size: sizeof(buf);
> +        int ret = 0;
> +        printf("nbytes:%d\n", nbytes);
> +        if (val & CMD_READ) {
> +            printf("doing pci_dma_read\n");
> +            ret = pci_dma_read(&pdev->dev, s, (void*)buf, nbytes);
> +        }
> +        if (ret)
> +            return;
> +
> +        if (val & CMD_WRITE) {
> +            printf("doing pci_dma_write\n");
> +            ret = pci_dma_write(&pdev->dev, d, (void*)buf, nbytes);
> +        }
> +        size -= nbytes;
> +        s += nbytes;
> +        d += nbytes;
> +    }
> +}
> +
> +static void
> +pci_tstdev_mmio_write(void *opaque, hwaddr addr,
> +                      uint64_t val, unsigned size)
> +{
> +    PCITestDevState *d = opaque;
> +    uint64_t lo;
> +
> +    printf("=================> addr:%ld act:%d val:%lx reg_addr:%p\n",
> +           addr, TST_REG_COMMAND, val, &d->regs[addr]);
> +    //addr >>= 2;
> +    switch (addr) {
> +    case TST_REG_COMMAND:
> +            printf("Calling command handler.....\n");
> +            pci_tstdev_handle_cmd(d, addr, val, size);
> +    case TST_REG_SRC_ADDR:
> +    case TST_REG_DST_ADDR:
> +    case TST_REG_SIZE:
> +            pci_tstdev_write_reg(d, addr, val);
> +            break;
> +    case TST_REG_SRC_ADDR + 4:
> +    case TST_REG_DST_ADDR + 4:
> +            lo = pci_tstdev_read32_reg(d, addr);
> +            lo &= (0xffffffffULL << 32);
> +            pci_tstdev_write_reg(d, addr, (val << 32) | lo);
> +            break;
> +    case TST_REG_STATUS:        /* Read only reg */
> +    default:
> +        printf("Unkown/RO register write\n");
> +        break;
> +    }
> +}
> +
> +static uint64_t
> +pci_tstdev_mmio_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    PCITestDevState *d = opaque;
> +
> +    switch (addr) {
> +    case TST_REG_SRC_ADDR:
> +    case TST_REG_DST_ADDR:
> +            return pci_tstdev_read64_reg(d, addr);
> +    }
> +
> +    return pci_tstdev_read32_reg(d, addr);
> +}
> +
> +static const MemoryRegionOps pci_testdev_mmio_ops = {
> +    .read = pci_tstdev_mmio_read,
> +    .write = pci_tstdev_mmio_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .impl = {
> +        .min_access_size = 1,
> +        .max_access_size = 8,
> +    },
> +};
> +
> +static void pci_tstdev_realize(PCIDevice *pci_dev, Error **errp)
> +{
> +    PCITestDevState *d = PCI_TEST_DEV(pci_dev);
> +    uint8_t *pci_conf;
> +
> +    pci_conf = pci_dev->config;
> +
> +    pci_conf[PCI_INTERRUPT_PIN] = 0; /* no interrupt pin */
> +
> +    memory_region_init_io(&d->mmio, OBJECT(d), &pci_testdev_mmio_ops, d,
> +                          "pci-testdev-smmu-mmio", 1 << 10);
> +
> +    pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
> +}
> +
> +static void
> +pci_tstdev_uninit(PCIDevice *dev)
> +{
> +    PCITestDevState *d = PCI_TEST_DEV(dev);
> +
> +    pci_tstdev_reset(d);
> +}
> +
> +static void qdev_pci_tstdev_reset(DeviceState *dev)
> +{
> +    PCITestDevState *d = PCI_TEST_DEV(dev);
> +    pci_tstdev_reset(d);
> +}
> +
> +static void pci_tstdev_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> +
> +    k->realize = pci_tstdev_realize;
> +    k->exit = pci_tstdev_uninit;
> +    k->vendor_id = PCI_VENDOR_ID_REDHAT;
> +    k->device_id = PCI_DEVICE_ID_REDHAT_TEST;
> +    k->revision = 0x00;
> +    k->class_id = PCI_CLASS_OTHERS;
> +    dc->desc = "PCI Test Device - for smmu";
> +    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
> +    dc->reset = qdev_pci_tstdev_reset;
> +}
> +
> +static const TypeInfo pci_tstdev_info = {
> +    .name          = TYPE_PCI_TEST_DEV,
> +    .parent        = TYPE_PCI_DEVICE,
> +    .instance_size = sizeof(PCITestDevState),
> +    .class_init    = pci_tstdev_class_init,
> +};
> +
> +static void pci_tstdev_register_types(void)
> +{
> +    type_register_static(&pci_tstdev_info);
> +}
> +
> +type_init(pci_tstdev_register_types)
> diff --git a/hw/misc/pci-testdev-smmu.h b/hw/misc/pci-testdev-smmu.h
> new file mode 100644
> index 0000000..4a2d463
> --- /dev/null
> +++ b/hw/misc/pci-testdev-smmu.h
> @@ -0,0 +1,22 @@
> +#ifndef HW_MISC_PCI_TESTDEV_SMMU
> +#define HW_MISC_PCI_TESTDEV_SMMU
> +
> +enum reg {
> +        TST_REG_COMMAND  = 0x0,
> +        TST_REG_STATUS   = 0x4,
> +        TST_REG_SRC_ADDR = 0x8,
> +        TST_REG_SIZE     = 0x10,
> +        TST_REG_DST_ADDR = 0x18,
> +
> +        TST_REG_LAST     = 0x30,
> +};
> +
> +#define CMD_READ    0x100
> +#define CMD_WRITE   0x200
> +#define CMD_RW      (CMD_READ | CMD_WRITE)
> +
> +#define STATUS_OK   (1 << 0)
> +#define STATUS_CMD_ERROR (1 << 1)
> +#define STATUS_CMD_INVALID (1 << 2)
> +
> +#endif
> -- 
> 2.9.3
> 
> 

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

end of thread, other threads:[~2017-03-27 15:42 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-22 16:17 [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Prem Mallappa
2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 1/9] log: Add new IOMMU type Prem Mallappa
2016-09-09 15:36   ` Auger Eric
2016-09-12 20:23     ` Prem Mallappa
2016-09-25 14:58       ` Edgar E. Iglesias
2016-09-26  6:54         ` Auger Eric
2016-09-26 18:30           ` Edgar E. Iglesias
2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 2/9] devicetree: Added new APIs to make use of more fdt functions Prem Mallappa
2016-09-09 16:02   ` Auger Eric
2016-09-12 20:21     ` Prem Mallappa
2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 3/9] hw: arm: SMMUv3 emulation model Prem Mallappa
2016-09-25 16:37   ` Edgar E. Iglesias
2016-09-26  5:27     ` Prem Mallappa
2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 4/9] hw: arm: Added SMMUv3 files for build Prem Mallappa
2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 5/9] hw: arm: Add SMMUv3 to virt platform, create DTS accordingly Prem Mallappa
2016-09-09 16:31   ` Auger Eric
2016-09-12 20:20     ` Prem Mallappa
2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 6/9] [optional] hw: misc: added testdev for smmu Prem Mallappa
2017-03-27 15:24   ` Philippe Mathieu-Daudé
2017-03-27 15:41   ` Andrew Jones
2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 7/9] [optional] tests: libqos: generic pci probing helpers Prem Mallappa
2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 8/9] [optional] tests: SMMUv3 unit tests Prem Mallappa
2016-08-22 16:17 ` [Qemu-devel] [PATCH v2 9/9] [optional] arm: smmu-v3: ACPI IORT initial support Prem Mallappa
2016-09-09 15:24   ` Auger Eric
2016-09-12 20:42     ` Prem Mallappa
2016-09-23 13:10       ` Auger Eric
2016-09-23 14:07         ` Prem Mallappa
2016-09-23 16:38           ` Auger Eric
2016-08-31 21:44 ` [Qemu-devel] [PATCH v2 0/9] SMMUv3 Emulation support Auger Eric
2016-09-01  5:24   ` Prem Mallappa
2017-03-08 17:46 ` Auger Eric
2017-03-27 11:44   ` Edgar E. Iglesias
2017-03-27 12:18     ` Auger Eric
2017-03-27 12:28       ` Edgar E. Iglesias

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.