* [PATCH v8 0/4] IOMMU support for ARM
@ 2014-05-19 16:23 Julien Grall
2014-05-19 16:23 ` [PATCH v8 1/4] xen/arm: p2m: Clean cache PT when the IOMMU doesn't support coherent walk Julien Grall
` (4 more replies)
0 siblings, 5 replies; 17+ messages in thread
From: Julien Grall @ 2014-05-19 16:23 UTC (permalink / raw)
To: xen-devel; +Cc: stefano.stabellini, Julien Grall, tim, ian.campbell
Hello,
This is the eight version of this patch series to add support for IOMMU on
ARM. It also adds driver to support ARM SMMU which is used for instance on
Midway.
Major changes see v7:
- Rework patch #4 to handle correctly multiple grant mapping with
the same MFN
For all changes see in each patch.
This series also depends on the interrupt reworking series [1]
A working tree can be found here:
git://xenbits.xen.org/people/julieng/xen-unstable.git branch smmu-v8
Sincerely yours,
[1] http://lists.xen.org/archives/html/xen-devel/2014-05/msg02172.html
Julien Grall (4):
xen/arm: p2m: Clean cache PT when the IOMMU doesn't support coherent
walk
xen: iommu: Define PAGE_{SHIFT,SIZE,ALIGN,MASK)_64K
drivers/passthrough: arm: Add support for SMMU drivers
xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m
xen/arch/arm/mm.c | 10 +
xen/arch/arm/p2m.c | 48 +-
xen/common/grant_table.c | 4 +-
xen/drivers/passthrough/arm/Makefile | 1 +
xen/drivers/passthrough/arm/smmu.c | 1786 ++++++++++++++++++++++++++++++++++
xen/drivers/passthrough/iommu.c | 10 +
xen/include/asm-arm/grant_table.h | 2 +
xen/include/asm-arm/mm.h | 3 +
xen/include/asm-arm/p2m.h | 2 +
xen/include/asm-x86/grant_table.h | 2 +
xen/include/xen/hvm/iommu.h | 6 +
xen/include/xen/iommu.h | 24 +-
12 files changed, 1877 insertions(+), 21 deletions(-)
create mode 100644 xen/drivers/passthrough/arm/smmu.c
--
1.7.10.4
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v8 1/4] xen/arm: p2m: Clean cache PT when the IOMMU doesn't support coherent walk
2014-05-19 16:23 [PATCH v8 0/4] IOMMU support for ARM Julien Grall
@ 2014-05-19 16:23 ` Julien Grall
2014-05-19 16:23 ` [PATCH v8 2/4] xen: iommu: Define PAGE_{SHIFT, SIZE, ALIGN, MASK)_64K Julien Grall
` (3 subsequent siblings)
4 siblings, 0 replies; 17+ messages in thread
From: Julien Grall @ 2014-05-19 16:23 UTC (permalink / raw)
To: xen-devel
Cc: stefano.stabellini, Julien Grall, tim, ian.campbell, Jan Beulich
Some IOMMU don't suppport coherent PT walk. When the p2m is shared with
the CPU, Xen has to make sure the PT changes have reached the memory.
Introduce new IOMMU function that will check if the IOMMU feature is enabled
for a specified domain.
On ARM, the platform can contain multiple IOMMUs. Each of them may not
have the same set of feature. The domain parameter will be used to get the
set of features for IOMMUs used by this domain.
Signed-off-by: Julien Grall <julien.grall@linaro.org>
Acked-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
---
Changes in v8:
- Drop final comma in the enum
- Add function clear_and_clean_page
Changes in v7:
- Add IOMMU_FEAT_count
- Use DECLARE_BITMAP
Changes in v6:
- Rework the condition to flush cache for PT
- Use {set,clear,test}_bit
- Store features in hvm_iommu structure and add accessor
- Don't specificed value in the enum
Changes in v5:
- Flush on every write_pte instead of unmap page. This will
avoid to flush a whole page when only few bytes are modified
- Only get iommu feature once.
- Add bits to flush cache when a new table is created
- Fix typoes in commit message and comment
- Use an enum to describe the feature. Each items are a bit
position
Changes in v4:
- Patch added
---
xen/arch/arm/mm.c | 10 +++++++++
xen/arch/arm/p2m.c | 46 +++++++++++++++++++++++++--------------
xen/drivers/passthrough/iommu.c | 10 +++++++++
xen/include/asm-arm/mm.h | 3 +++
xen/include/xen/hvm/iommu.h | 6 +++++
xen/include/xen/iommu.h | 9 ++++++++
6 files changed, 68 insertions(+), 16 deletions(-)
diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c
index eac228c..7e8e06a 100644
--- a/xen/arch/arm/mm.c
+++ b/xen/arch/arm/mm.c
@@ -1235,6 +1235,16 @@ int is_iomem_page(unsigned long mfn)
return 1;
return 0;
}
+
+void clear_and_clean_page(struct page_info *page)
+{
+ void *p = __map_domain_page(page);
+
+ clear_page(p);
+ clean_xen_dcache_va_range(p, PAGE_SIZE);
+ unmap_domain_page(p);
+}
+
/*
* Local variables:
* mode: C
diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index b85143b..96bc0ef 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -253,9 +253,15 @@ static lpae_t mfn_to_p2m_entry(unsigned long mfn, unsigned int mattr,
return e;
}
+static inline void p2m_write_pte(lpae_t *p, lpae_t pte, bool_t flush_cache)
+{
+ write_pte(p, pte);
+ if ( flush_cache )
+ clean_xen_dcache(*p);
+}
+
/* Allocate a new page table page and hook it in via the given entry */
-static int p2m_create_table(struct domain *d,
- lpae_t *entry)
+static int p2m_create_table(struct domain *d, lpae_t *entry, bool_t flush_cache)
{
struct p2m_domain *p2m = &d->arch.p2m;
struct page_info *page;
@@ -272,11 +278,13 @@ static int p2m_create_table(struct domain *d,
p = __map_domain_page(page);
clear_page(p);
+ if ( flush_cache )
+ clean_xen_dcache_va_range(p, PAGE_SIZE);
unmap_domain_page(p);
pte = mfn_to_p2m_entry(page_to_mfn(page), MATTR_MEM, p2m_invalid);
- write_pte(entry, pte);
+ p2m_write_pte(entry, pte, flush_cache);
return 0;
}
@@ -308,6 +316,13 @@ static int apply_p2m_changes(struct domain *d,
unsigned int flush = 0;
bool_t populate = (op == INSERT || op == ALLOCATE);
lpae_t pte;
+ bool_t flush_pt;
+
+ /* Some IOMMU don't support coherent PT walk. When the p2m is
+ * shared with the CPU, Xen has to make sure that the PT changes have
+ * reached the memory
+ */
+ flush_pt = iommu_enabled && !iommu_has_feature(d, IOMMU_FEAT_COHERENT_WALK);
spin_lock(&p2m->lock);
@@ -334,7 +349,8 @@ static int apply_p2m_changes(struct domain *d,
continue;
}
- rc = p2m_create_table(d, &first[first_table_offset(addr)]);
+ rc = p2m_create_table(d, &first[first_table_offset(addr)],
+ flush_pt);
if ( rc < 0 )
{
printk("p2m_populate_ram: L1 failed\n");
@@ -360,7 +376,8 @@ static int apply_p2m_changes(struct domain *d,
continue;
}
- rc = p2m_create_table(d, &second[second_table_offset(addr)]);
+ rc = p2m_create_table(d, &second[second_table_offset(addr)],
+ flush_pt);
if ( rc < 0 ) {
printk("p2m_populate_ram: L2 failed\n");
goto out;
@@ -411,13 +428,15 @@ static int apply_p2m_changes(struct domain *d,
pte = mfn_to_p2m_entry(page_to_mfn(page), mattr, t);
- write_pte(&third[third_table_offset(addr)], pte);
+ p2m_write_pte(&third[third_table_offset(addr)],
+ pte, flush_pt);
}
break;
case INSERT:
{
pte = mfn_to_p2m_entry(maddr >> PAGE_SHIFT, mattr, t);
- write_pte(&third[third_table_offset(addr)], pte);
+ p2m_write_pte(&third[third_table_offset(addr)],
+ pte, flush_pt);
maddr += PAGE_SIZE;
}
break;
@@ -433,7 +452,8 @@ static int apply_p2m_changes(struct domain *d,
count += 0x10;
memset(&pte, 0x00, sizeof(pte));
- write_pte(&third[third_table_offset(addr)], pte);
+ p2m_write_pte(&third[third_table_offset(addr)],
+ pte, flush_pt);
count++;
}
break;
@@ -537,7 +557,6 @@ int p2m_alloc_table(struct domain *d)
{
struct p2m_domain *p2m = &d->arch.p2m;
struct page_info *page;
- void *p;
page = alloc_domheap_pages(NULL, P2M_FIRST_ORDER, 0);
if ( page == NULL )
@@ -546,13 +565,8 @@ int p2m_alloc_table(struct domain *d)
spin_lock(&p2m->lock);
/* Clear both first level pages */
- p = __map_domain_page(page);
- clear_page(p);
- unmap_domain_page(p);
-
- p = __map_domain_page(page + 1);
- clear_page(p);
- unmap_domain_page(p);
+ clear_and_clean_page(page);
+ clear_and_clean_page(page + 1);
p2m->first_level = page;
diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c
index 59f1c3e..cc12735 100644
--- a/xen/drivers/passthrough/iommu.c
+++ b/xen/drivers/passthrough/iommu.c
@@ -344,6 +344,16 @@ void iommu_crash_shutdown(void)
iommu_enabled = iommu_intremap = 0;
}
+bool_t iommu_has_feature(struct domain *d, enum iommu_feature feature)
+{
+ const struct hvm_iommu *hd = domain_hvm_iommu(d);
+
+ if ( !iommu_enabled )
+ return 0;
+
+ return test_bit(feature, hd->features);
+}
+
static void iommu_dump_p2m_table(unsigned char key)
{
struct domain *d;
diff --git a/xen/include/asm-arm/mm.h b/xen/include/asm-arm/mm.h
index b8d4e7d..3bef93f 100644
--- a/xen/include/asm-arm/mm.h
+++ b/xen/include/asm-arm/mm.h
@@ -5,6 +5,7 @@
#include <xen/kernel.h>
#include <asm/page.h>
#include <public/xen.h>
+#include <xen/domain_page.h>
/* Align Xen to a 2 MiB boundary. */
#define XEN_PADDR_ALIGN (1 << 21)
@@ -341,6 +342,8 @@ static inline void put_page_and_type(struct page_info *page)
put_page(page);
}
+void clear_and_clean_page(struct page_info *page);
+
#endif /* __ARCH_ARM_MM__ */
/*
* Local variables:
diff --git a/xen/include/xen/hvm/iommu.h b/xen/include/xen/hvm/iommu.h
index 1259e16..693346c 100644
--- a/xen/include/xen/hvm/iommu.h
+++ b/xen/include/xen/hvm/iommu.h
@@ -34,6 +34,12 @@ struct hvm_iommu {
/* List of DT devices assigned to this domain */
struct list_head dt_devices;
#endif
+
+ /* Features supported by the IOMMU */
+ DECLARE_BITMAP(features, IOMMU_FEAT_count);
};
+#define iommu_set_feature(d, f) set_bit((f), domain_hvm_iommu(d)->features)
+#define iommu_clear_feature(d, f) clear_bit((f), domain_hvm_iommu(d)->features)
+
#endif /* __XEN_HVM_IOMMU_H__ */
diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h
index b7481dac..2ec7834 100644
--- a/xen/include/xen/iommu.h
+++ b/xen/include/xen/iommu.h
@@ -67,6 +67,15 @@ int iommu_map_page(struct domain *d, unsigned long gfn, unsigned long mfn,
unsigned int flags);
int iommu_unmap_page(struct domain *d, unsigned long gfn);
+enum iommu_feature
+{
+ IOMMU_FEAT_COHERENT_WALK,
+ IOMMU_FEAT_count
+};
+
+bool_t iommu_has_feature(struct domain *d, enum iommu_feature feature);
+
+
#ifdef HAS_PCI
void pt_pci_init(void);
--
1.7.10.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v8 2/4] xen: iommu: Define PAGE_{SHIFT, SIZE, ALIGN, MASK)_64K
2014-05-19 16:23 [PATCH v8 0/4] IOMMU support for ARM Julien Grall
2014-05-19 16:23 ` [PATCH v8 1/4] xen/arm: p2m: Clean cache PT when the IOMMU doesn't support coherent walk Julien Grall
@ 2014-05-19 16:23 ` Julien Grall
2014-05-19 16:23 ` [PATCH v8 3/4] drivers/passthrough: arm: Add support for SMMU drivers Julien Grall
` (2 subsequent siblings)
4 siblings, 0 replies; 17+ messages in thread
From: Julien Grall @ 2014-05-19 16:23 UTC (permalink / raw)
To: xen-devel
Cc: stefano.stabellini, Julien Grall, tim, ian.campbell, Jan Beulich
Also add IOMMU_PAGE_* helper macros to help creating PAGE_* defines.
Signed-off-by: Julien Grall <julien.grall@linaro.org>
Acked-by: Jan Beulich <jbeulich@suse.com>
---
Changes in v7:
- Drop pointless parentheses
Changes in v6:
- Patch added
---
xen/include/xen/iommu.h | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h
index 2ec7834..8eb764a 100644
--- a/xen/include/xen/iommu.h
+++ b/xen/include/xen/iommu.h
@@ -35,10 +35,19 @@ extern bool_t iommu_hap_pt_share;
extern bool_t iommu_debug;
extern bool_t amd_iommu_perdev_intremap;
+#define IOMMU_PAGE_SIZE(sz) (1UL << PAGE_SHIFT_##sz)
+#define IOMMU_PAGE_MASK(sz) (~(u64)0 << PAGE_SHIFT_##sz)
+#define IOMMU_PAGE_ALIGN(sz, addr) (((addr) + ~PAGE_MASK_##sz) & PAGE_MASK_##sz)
+
#define PAGE_SHIFT_4K (12)
-#define PAGE_SIZE_4K (1UL << PAGE_SHIFT_4K)
-#define PAGE_MASK_4K (((u64)-1) << PAGE_SHIFT_4K)
-#define PAGE_ALIGN_4K(addr) (((addr) + PAGE_SIZE_4K - 1) & PAGE_MASK_4K)
+#define PAGE_SIZE_4K IOMMU_PAGE_SIZE(4K)
+#define PAGE_MASK_4K IOMMU_PAGE_MASK(4K)
+#define PAGE_ALIGN_4K(addr) IOMMU_PAGE_ALIGN(4K, addr)
+
+#define PAGE_SHIFT_64K (16)
+#define PAGE_SIZE_64K IOMMU_PAGE_SIZE(64K)
+#define PAGE_MASK_64K IOMMU_PAGE_MASK(64K)
+#define PAGE_ALIGN_64K(addr) IOMMU_PAGE_ALIGN(64K, addr)
int iommu_setup(void);
--
1.7.10.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v8 3/4] drivers/passthrough: arm: Add support for SMMU drivers
2014-05-19 16:23 [PATCH v8 0/4] IOMMU support for ARM Julien Grall
2014-05-19 16:23 ` [PATCH v8 1/4] xen/arm: p2m: Clean cache PT when the IOMMU doesn't support coherent walk Julien Grall
2014-05-19 16:23 ` [PATCH v8 2/4] xen: iommu: Define PAGE_{SHIFT, SIZE, ALIGN, MASK)_64K Julien Grall
@ 2014-05-19 16:23 ` Julien Grall
2014-05-19 16:24 ` [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m Julien Grall
2014-05-21 13:27 ` [PATCH v8 0/4] IOMMU support for ARM Ian Campbell
4 siblings, 0 replies; 17+ messages in thread
From: Julien Grall @ 2014-05-19 16:23 UTC (permalink / raw)
To: xen-devel; +Cc: stefano.stabellini, Julien Grall, tim, ian.campbell
This patch add support for ARM architected SMMU driver. It's based on the
linux drivers (drivers/iommu/arm-smmu) commit 89ac23cd.
The major differences with the Linux driver are:
- Fault by default if the SMMU is enabled to translate an
address (Linux is bypassing the SMMU)
- Using P2M page table instead of creating new one
- Dropped stage-1 support
- Dropped chained SMMUs support for now
- Reworking device assignment and the different structures
Xen is programming each IOMMU by:
- Using stage-2 mode translation
- Sharing the page table with the processor
- Injecting a fault if the device has made a wrong translation
Signed-off-by: Julien Grall<julien.grall@linaro.org>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
---
Changes in v6:
- Move the definition page PAGE_*_64K in another patch. This
patch only contains ARM specific code now.
- Use the new way to set feature
Changes in v5:
- Fix arm_smmu_domain_remove_master by using num_s2crs instead
of num_streamids
- IOMMU_FEATURE_COHERENT_WALK is became a bit position
Changes in v4:
- Use the new IRQ API e.g function without _dt_ in the name
- Use IRQF_SHARED to notify that the IRQ can be shared (i.e
handle multiple actions)
- Update comment in smmu_init
- Update comment for upgrade barrier in smmu_device_reset
- Update CB_TCR to reflect change in VTCR by commit 8200dae
- Drop gr0_base which is not unused in arm_smmu_init_context_bank
- Rebase on the latest Xen (hwdom series was pushed)
- On 32 bit IPA is encoded on 40-bits. Read CB_FAR_HI on all arch
- Add verbosity in CB fault handler
- Handle 40 bits address space on ARM{32,64}.
- Implement new callback features
- platform_get_irq now returns -1 if an error occured
Changes in v3:
- Missing some static
Changes in v2:
- Update commit message
- Update some comments in the code
- Add new callbacks to assign/reassign DT device
- Rework init_dom0 and domain_teardown. The
assignment/deassignement is now made in the generic code
- Set protected field in dt_device_node when the device is under
an IOMMU
- Use SZ_64K and SZ_4K by the global PAGE_SIZE_{64,4}K in
xen/iommu.h. The first one was not defined.
---
xen/drivers/passthrough/arm/Makefile | 1 +
xen/drivers/passthrough/arm/smmu.c | 1742 ++++++++++++++++++++++++++++++++++
2 files changed, 1743 insertions(+)
create mode 100644 xen/drivers/passthrough/arm/smmu.c
diff --git a/xen/drivers/passthrough/arm/Makefile b/xen/drivers/passthrough/arm/Makefile
index 0484b79..f4cd26e 100644
--- a/xen/drivers/passthrough/arm/Makefile
+++ b/xen/drivers/passthrough/arm/Makefile
@@ -1 +1,2 @@
obj-y += iommu.o
+obj-y += smmu.o
diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
new file mode 100644
index 0000000..21b4572
--- /dev/null
+++ b/xen/drivers/passthrough/arm/smmu.c
@@ -0,0 +1,1742 @@
+/*
+ * IOMMU API for ARM architected SMMU implementations.
+ *
+ * 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.
+ *
+ * Based on Linux drivers/iommu/arm-smmu.c (commit 89a23cd)
+ * Copyright (C) 2013 ARM Limited
+ *
+ * Author: Will Deacon <will.deacon@arm.com>
+ *
+ * Xen modification:
+ * Julien Grall <julien.grall@linaro.org>
+ * Copyright (C) 2014 Linaro Limited.
+ *
+ * This driver currently supports:
+ * - SMMUv1 and v2 implementations (didn't try v2 SMMU)
+ * - Stream-matching and stream-indexing
+ * - v7/v8 long-descriptor format
+ * - Non-secure access to the SMMU
+ * - 4k pages, p2m shared with the processor
+ * - Up to 40-bit addressing
+ * - Context fault reporting
+ */
+
+#include <xen/config.h>
+#include <xen/delay.h>
+#include <xen/errno.h>
+#include <xen/irq.h>
+#include <xen/lib.h>
+#include <xen/list.h>
+#include <xen/mm.h>
+#include <xen/vmap.h>
+#include <xen/rbtree.h>
+#include <xen/sched.h>
+#include <asm/atomic.h>
+#include <asm/device.h>
+#include <asm/io.h>
+#include <asm/platform.h>
+
+/* Driver options */
+#define SMMU_OPT_SECURE_CONFIG_ACCESS (1 << 0)
+
+/* Maximum number of stream IDs assigned to a single device */
+#define MAX_MASTER_STREAMIDS MAX_PHANDLE_ARGS
+
+/* Maximum stream ID */
+#define SMMU_MAX_STREAMIDS (PAGE_SIZE_64K - 1)
+
+/* Maximum number of context banks per SMMU */
+#define SMMU_MAX_CBS 128
+
+/* Maximum number of mapping groups per SMMU */
+#define SMMU_MAX_SMRS 128
+
+/* SMMU global address space */
+#define SMMU_GR0(smmu) ((smmu)->base)
+#define SMMU_GR1(smmu) ((smmu)->base + (smmu)->pagesize)
+
+/*
+ * SMMU global address space with conditional offset to access secure aliases of
+ * non-secure registers (e.g. nsCR0: 0x400, nsGFSR: 0x448, nsGFSYNR0: 0x450)
+ */
+#define SMMU_GR0_NS(smmu) \
+ ((smmu)->base + \
+ ((smmu->options & SMMU_OPT_SECURE_CONFIG_ACCESS) \
+ ? 0x400 : 0))
+
+/* Page table bits */
+#define SMMU_PTE_PAGE (((pteval_t)3) << 0)
+#define SMMU_PTE_CONT (((pteval_t)1) << 52)
+#define SMMU_PTE_AF (((pteval_t)1) << 10)
+#define SMMU_PTE_SH_NS (((pteval_t)0) << 8)
+#define SMMU_PTE_SH_OS (((pteval_t)2) << 8)
+#define SMMU_PTE_SH_IS (((pteval_t)3) << 8)
+
+#if PAGE_SIZE == PAGE_SIZE_4K
+#define SMMU_PTE_CONT_ENTRIES 16
+#elif PAGE_SIZE == PAGE_SIZE_64K
+#define SMMU_PTE_CONT_ENTRIES 32
+#else
+#define SMMU_PTE_CONT_ENTRIES 1
+#endif
+
+#define SMMU_PTE_CONT_SIZE (PAGE_SIZE * SMMU_PTE_CONT_ENTRIES)
+#define SMMU_PTE_CONT_MASK (~(SMMU_PTE_CONT_SIZE - 1))
+#define SMMU_PTE_HWTABLE_SIZE (PTRS_PER_PTE * sizeof(pte_t))
+
+/* Stage-1 PTE */
+#define SMMU_PTE_AP_UNPRIV (((pteval_t)1) << 6)
+#define SMMU_PTE_AP_RDONLY (((pteval_t)2) << 6)
+#define SMMU_PTE_ATTRINDX_SHIFT 2
+#define SMMU_PTE_nG (((pteval_t)1) << 11)
+
+/* Stage-2 PTE */
+#define SMMU_PTE_HAP_FAULT (((pteval_t)0) << 6)
+#define SMMU_PTE_HAP_READ (((pteval_t)1) << 6)
+#define SMMU_PTE_HAP_WRITE (((pteval_t)2) << 6)
+#define SMMU_PTE_MEMATTR_OIWB (((pteval_t)0xf) << 2)
+#define SMMU_PTE_MEMATTR_NC (((pteval_t)0x5) << 2)
+#define SMMU_PTE_MEMATTR_DEV (((pteval_t)0x1) << 2)
+
+/* Configuration registers */
+#define SMMU_GR0_sCR0 0x0
+#define SMMU_sCR0_CLIENTPD (1 << 0)
+#define SMMU_sCR0_GFRE (1 << 1)
+#define SMMU_sCR0_GFIE (1 << 2)
+#define SMMU_sCR0_GCFGFRE (1 << 4)
+#define SMMU_sCR0_GCFGFIE (1 << 5)
+#define SMMU_sCR0_USFCFG (1 << 10)
+#define SMMU_sCR0_VMIDPNE (1 << 11)
+#define SMMU_sCR0_PTM (1 << 12)
+#define SMMU_sCR0_FB (1 << 13)
+#define SMMU_sCR0_BSU_SHIFT 14
+#define SMMU_sCR0_BSU_MASK 0x3
+
+/* Identification registers */
+#define SMMU_GR0_ID0 0x20
+#define SMMU_GR0_ID1 0x24
+#define SMMU_GR0_ID2 0x28
+#define SMMU_GR0_ID3 0x2c
+#define SMMU_GR0_ID4 0x30
+#define SMMU_GR0_ID5 0x34
+#define SMMU_GR0_ID6 0x38
+#define SMMU_GR0_ID7 0x3c
+#define SMMU_GR0_sGFSR 0x48
+#define SMMU_GR0_sGFSYNR0 0x50
+#define SMMU_GR0_sGFSYNR1 0x54
+#define SMMU_GR0_sGFSYNR2 0x58
+#define SMMU_GR0_PIDR0 0xfe0
+#define SMMU_GR0_PIDR1 0xfe4
+#define SMMU_GR0_PIDR2 0xfe8
+
+#define SMMU_ID0_S1TS (1 << 30)
+#define SMMU_ID0_S2TS (1 << 29)
+#define SMMU_ID0_NTS (1 << 28)
+#define SMMU_ID0_SMS (1 << 27)
+#define SMMU_ID0_PTFS_SHIFT 24
+#define SMMU_ID0_PTFS_MASK 0x2
+#define SMMU_ID0_PTFS_V8_ONLY 0x2
+#define SMMU_ID0_CTTW (1 << 14)
+#define SMMU_ID0_NUMIRPT_SHIFT 16
+#define SMMU_ID0_NUMIRPT_MASK 0xff
+#define SMMU_ID0_NUMSMRG_SHIFT 0
+#define SMMU_ID0_NUMSMRG_MASK 0xff
+
+#define SMMU_ID1_PAGESIZE (1 << 31)
+#define SMMU_ID1_NUMPAGENDXB_SHIFT 28
+#define SMMU_ID1_NUMPAGENDXB_MASK 7
+#define SMMU_ID1_NUMS2CB_SHIFT 16
+#define SMMU_ID1_NUMS2CB_MASK 0xff
+#define SMMU_ID1_NUMCB_SHIFT 0
+#define SMMU_ID1_NUMCB_MASK 0xff
+
+#define SMMU_ID2_OAS_SHIFT 4
+#define SMMU_ID2_OAS_MASK 0xf
+#define SMMU_ID2_IAS_SHIFT 0
+#define SMMU_ID2_IAS_MASK 0xf
+#define SMMU_ID2_UBS_SHIFT 8
+#define SMMU_ID2_UBS_MASK 0xf
+#define SMMU_ID2_PTFS_4K (1 << 12)
+#define SMMU_ID2_PTFS_16K (1 << 13)
+#define SMMU_ID2_PTFS_64K (1 << 14)
+
+#define SMMU_PIDR2_ARCH_SHIFT 4
+#define SMMU_PIDR2_ARCH_MASK 0xf
+
+/* Global TLB invalidation */
+#define SMMU_GR0_STLBIALL 0x60
+#define SMMU_GR0_TLBIVMID 0x64
+#define SMMU_GR0_TLBIALLNSNH 0x68
+#define SMMU_GR0_TLBIALLH 0x6c
+#define SMMU_GR0_sTLBGSYNC 0x70
+#define SMMU_GR0_sTLBGSTATUS 0x74
+#define SMMU_sTLBGSTATUS_GSACTIVE (1 << 0)
+#define SMMU_TLB_LOOP_TIMEOUT 1000000 /* 1s! */
+
+/* Stream mapping registers */
+#define SMMU_GR0_SMR(n) (0x800 + ((n) << 2))
+#define SMMU_SMR_VALID (1 << 31)
+#define SMMU_SMR_MASK_SHIFT 16
+#define SMMU_SMR_MASK_MASK 0x7fff
+#define SMMU_SMR_ID_SHIFT 0
+#define SMMU_SMR_ID_MASK 0x7fff
+
+#define SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2))
+#define SMMU_S2CR_CBNDX_SHIFT 0
+#define SMMU_S2CR_CBNDX_MASK 0xff
+#define SMMU_S2CR_TYPE_SHIFT 16
+#define SMMU_S2CR_TYPE_MASK 0x3
+#define SMMU_S2CR_TYPE_TRANS (0 << SMMU_S2CR_TYPE_SHIFT)
+#define SMMU_S2CR_TYPE_BYPASS (1 << SMMU_S2CR_TYPE_SHIFT)
+#define SMMU_S2CR_TYPE_FAULT (2 << SMMU_S2CR_TYPE_SHIFT)
+
+/* Context bank attribute registers */
+#define SMMU_GR1_CBAR(n) (0x0 + ((n) << 2))
+#define SMMU_CBAR_VMID_SHIFT 0
+#define SMMU_CBAR_VMID_MASK 0xff
+#define SMMU_CBAR_S1_MEMATTR_SHIFT 12
+#define SMMU_CBAR_S1_MEMATTR_MASK 0xf
+#define SMMU_CBAR_S1_MEMATTR_WB 0xf
+#define SMMU_CBAR_TYPE_SHIFT 16
+#define SMMU_CBAR_TYPE_MASK 0x3
+#define SMMU_CBAR_TYPE_S2_TRANS (0 << SMMU_CBAR_TYPE_SHIFT)
+#define SMMU_CBAR_TYPE_S1_TRANS_S2_BYPASS (1 << SMMU_CBAR_TYPE_SHIFT)
+#define SMMU_CBAR_TYPE_S1_TRANS_S2_FAULT (2 << SMMU_CBAR_TYPE_SHIFT)
+#define SMMU_CBAR_TYPE_S1_TRANS_S2_TRANS (3 << SMMU_CBAR_TYPE_SHIFT)
+#define SMMU_CBAR_IRPTNDX_SHIFT 24
+#define SMMU_CBAR_IRPTNDX_MASK 0xff
+
+#define SMMU_GR1_CBA2R(n) (0x800 + ((n) << 2))
+#define SMMU_CBA2R_RW64_32BIT (0 << 0)
+#define SMMU_CBA2R_RW64_64BIT (1 << 0)
+
+/* Translation context bank */
+#define SMMU_CB_BASE(smmu) ((smmu)->base + ((smmu)->size >> 1))
+#define SMMU_CB(smmu, n) ((n) * (smmu)->pagesize)
+
+#define SMMU_CB_SCTLR 0x0
+#define SMMU_CB_RESUME 0x8
+#define SMMU_CB_TCR2 0x10
+#define SMMU_CB_TTBR0_LO 0x20
+#define SMMU_CB_TTBR0_HI 0x24
+#define SMMU_CB_TCR 0x30
+#define SMMU_CB_S1_MAIR0 0x38
+#define SMMU_CB_FSR 0x58
+#define SMMU_CB_FAR_LO 0x60
+#define SMMU_CB_FAR_HI 0x64
+#define SMMU_CB_FSYNR0 0x68
+#define SMMU_CB_S1_TLBIASID 0x610
+
+#define SMMU_SCTLR_S1_ASIDPNE (1 << 12)
+#define SMMU_SCTLR_CFCFG (1 << 7)
+#define SMMU_SCTLR_CFIE (1 << 6)
+#define SMMU_SCTLR_CFRE (1 << 5)
+#define SMMU_SCTLR_E (1 << 4)
+#define SMMU_SCTLR_AFE (1 << 2)
+#define SMMU_SCTLR_TRE (1 << 1)
+#define SMMU_SCTLR_M (1 << 0)
+#define SMMU_SCTLR_EAE_SBOP (SMMU_SCTLR_AFE | SMMU_SCTLR_TRE)
+
+#define SMMU_RESUME_RETRY (0 << 0)
+#define SMMU_RESUME_TERMINATE (1 << 0)
+
+#define SMMU_TCR_EAE (1 << 31)
+
+#define SMMU_TCR_PASIZE_SHIFT 16
+#define SMMU_TCR_PASIZE_MASK 0x7
+
+#define SMMU_TCR_TG0_4K (0 << 14)
+#define SMMU_TCR_TG0_64K (1 << 14)
+
+#define SMMU_TCR_SH0_SHIFT 12
+#define SMMU_TCR_SH0_MASK 0x3
+#define SMMU_TCR_SH_NS 0
+#define SMMU_TCR_SH_OS 2
+#define SMMU_TCR_SH_IS 3
+
+#define SMMU_TCR_ORGN0_SHIFT 10
+#define SMMU_TCR_IRGN0_SHIFT 8
+#define SMMU_TCR_RGN_MASK 0x3
+#define SMMU_TCR_RGN_NC 0
+#define SMMU_TCR_RGN_WBWA 1
+#define SMMU_TCR_RGN_WT 2
+#define SMMU_TCR_RGN_WB 3
+
+#define SMMU_TCR_SL0_SHIFT 6
+#define SMMU_TCR_SL0_MASK 0x3
+#define SMMU_TCR_SL0_LVL_2 0
+#define SMMU_TCR_SL0_LVL_1 1
+
+#define SMMU_TCR_T1SZ_SHIFT 16
+#define SMMU_TCR_T0SZ_SHIFT 0
+#define SMMU_TCR_SZ_MASK 0xf
+
+#define SMMU_TCR2_SEP_SHIFT 15
+#define SMMU_TCR2_SEP_MASK 0x7
+
+#define SMMU_TCR2_PASIZE_SHIFT 0
+#define SMMU_TCR2_PASIZE_MASK 0x7
+
+/* Common definitions for PASize and SEP fields */
+#define SMMU_TCR2_ADDR_32 0
+#define SMMU_TCR2_ADDR_36 1
+#define SMMU_TCR2_ADDR_40 2
+#define SMMU_TCR2_ADDR_42 3
+#define SMMU_TCR2_ADDR_44 4
+#define SMMU_TCR2_ADDR_48 5
+
+#define SMMU_TTBRn_HI_ASID_SHIFT 16
+
+#define SMMU_MAIR_ATTR_SHIFT(n) ((n) << 3)
+#define SMMU_MAIR_ATTR_MASK 0xff
+#define SMMU_MAIR_ATTR_DEVICE 0x04
+#define SMMU_MAIR_ATTR_NC 0x44
+#define SMMU_MAIR_ATTR_WBRWA 0xff
+#define SMMU_MAIR_ATTR_IDX_NC 0
+#define SMMU_MAIR_ATTR_IDX_CACHE 1
+#define SMMU_MAIR_ATTR_IDX_DEV 2
+
+#define SMMU_FSR_MULTI (1 << 31)
+#define SMMU_FSR_SS (1 << 30)
+#define SMMU_FSR_UUT (1 << 8)
+#define SMMU_FSR_ASF (1 << 7)
+#define SMMU_FSR_TLBLKF (1 << 6)
+#define SMMU_FSR_TLBMCF (1 << 5)
+#define SMMU_FSR_EF (1 << 4)
+#define SMMU_FSR_PF (1 << 3)
+#define SMMU_FSR_AFF (1 << 2)
+#define SMMU_FSR_TF (1 << 1)
+
+#define SMMU_FSR_IGN (SMMU_FSR_AFF | SMMU_FSR_ASF | \
+ SMMU_FSR_TLBMCF | SMMU_FSR_TLBLKF)
+#define SMMU_FSR_FAULT (SMMU_FSR_MULTI | SMMU_FSR_SS | \
+ SMMU_FSR_UUT | SMMU_FSR_EF | \
+ SMMU_FSR_PF | SMMU_FSR_TF | \
+ SMMU_FSR_IGN)
+
+#define SMMU_FSYNR0_WNR (1 << 4)
+
+#define smmu_print(dev, lvl, fmt, ...) \
+ printk(lvl "smmu: %s: " fmt, dt_node_full_name(dev->node), ## __VA_ARGS__)
+
+#define smmu_err(dev, fmt, ...) smmu_print(dev, XENLOG_ERR, fmt, ## __VA_ARGS__)
+
+#define smmu_dbg(dev, fmt, ...) \
+ smmu_print(dev, XENLOG_DEBUG, fmt, ## __VA_ARGS__)
+
+#define smmu_info(dev, fmt, ...) \
+ smmu_print(dev, XENLOG_INFO, fmt, ## __VA_ARGS__)
+
+#define smmu_warn(dev, fmt, ...) \
+ smmu_print(dev, XENLOG_WARNING, fmt, ## __VA_ARGS__)
+
+struct arm_smmu_device {
+ const struct dt_device_node *node;
+
+ void __iomem *base;
+ unsigned long size;
+ unsigned long pagesize;
+
+#define SMMU_FEAT_COHERENT_WALK (1 << 0)
+#define SMMU_FEAT_STREAM_MATCH (1 << 1)
+#define SMMU_FEAT_TRANS_S1 (1 << 2)
+#define SMMU_FEAT_TRANS_S2 (1 << 3)
+#define SMMU_FEAT_TRANS_NESTED (1 << 4)
+ u32 features;
+ u32 options;
+ int version;
+
+ u32 num_context_banks;
+ u32 num_s2_context_banks;
+ DECLARE_BITMAP(context_map, SMMU_MAX_CBS);
+ atomic_t irptndx;
+
+ u32 num_mapping_groups;
+ DECLARE_BITMAP(smr_map, SMMU_MAX_SMRS);
+
+ unsigned long input_size;
+ unsigned long s1_output_size;
+ unsigned long s2_output_size;
+
+ u32 num_global_irqs;
+ u32 num_context_irqs;
+ unsigned int *irqs;
+
+ u32 smr_mask_mask;
+ u32 smr_id_mask;
+
+ unsigned long *sids;
+
+ struct list_head list;
+ struct rb_root masters;
+};
+
+struct arm_smmu_smr {
+ u8 idx;
+ u16 mask;
+ u16 id;
+};
+
+#define INVALID_IRPTNDX 0xff
+
+#define SMMU_CB_ASID(cfg) ((cfg)->cbndx)
+#define SMMU_CB_VMID(cfg) ((cfg)->cbndx + 1)
+
+struct arm_smmu_domain_cfg {
+ struct arm_smmu_device *smmu;
+ u8 cbndx;
+ u8 irptndx;
+ u32 cbar;
+ /* Domain associated to this device */
+ struct domain *domain;
+ /* List of master which use this structure */
+ struct list_head masters;
+
+ /* Used to link domain context for a same domain */
+ struct list_head list;
+};
+
+struct arm_smmu_master {
+ const struct dt_device_node *dt_node;
+
+ /*
+ * The following is specific to the master's position in the
+ * SMMU chain.
+ */
+ struct rb_node node;
+ u32 num_streamids;
+ u16 streamids[MAX_MASTER_STREAMIDS];
+ int num_s2crs;
+
+ struct arm_smmu_smr *smrs;
+ struct arm_smmu_domain_cfg *cfg;
+
+ /* Used to link masters in a same domain context */
+ struct list_head list;
+};
+
+static LIST_HEAD(arm_smmu_devices);
+
+struct arm_smmu_domain {
+ spinlock_t lock;
+ struct list_head contexts;
+};
+
+struct arm_smmu_option_prop {
+ u32 opt;
+ const char *prop;
+};
+
+static const struct arm_smmu_option_prop arm_smmu_options [] __initconst =
+{
+ { SMMU_OPT_SECURE_CONFIG_ACCESS, "calxeda,smmu-secure-config-access" },
+ { 0, NULL},
+};
+
+static void __init check_driver_options(struct arm_smmu_device *smmu)
+{
+ int i = 0;
+
+ do {
+ if ( dt_property_read_bool(smmu->node, arm_smmu_options[i].prop) )
+ {
+ smmu->options |= arm_smmu_options[i].opt;
+ smmu_dbg(smmu, "option %s\n", arm_smmu_options[i].prop);
+ }
+ } while ( arm_smmu_options[++i].opt );
+}
+
+static void arm_smmu_context_fault(int irq, void *data,
+ struct cpu_user_regs *regs)
+{
+ u32 fsr, far, fsynr;
+ uint64_t iova;
+ struct arm_smmu_domain_cfg *cfg = data;
+ struct arm_smmu_device *smmu = cfg->smmu;
+ void __iomem *cb_base;
+
+ cb_base = SMMU_CB_BASE(smmu) + SMMU_CB(smmu, cfg->cbndx);
+ fsr = readl_relaxed(cb_base + SMMU_CB_FSR);
+
+ if ( !(fsr & SMMU_FSR_FAULT) )
+ return;
+
+ if ( fsr & SMMU_FSR_IGN )
+ smmu_err(smmu, "Unexpected context fault (fsr 0x%u)\n", fsr);
+
+ fsynr = readl_relaxed(cb_base + SMMU_CB_FSYNR0);
+ far = readl_relaxed(cb_base + SMMU_CB_FAR_LO);
+ iova = far;
+ far = readl_relaxed(cb_base + SMMU_CB_FAR_HI);
+ iova |= ((uint64_t)far << 32);
+
+ smmu_err(smmu, "Unhandled context fault for domain %u\n",
+ cfg->domain->domain_id);
+ smmu_err(smmu, "\tFSR 0x%x, IOVA 0x%"PRIx64", FSYNR 0x%x, CB %d\n",
+ fsr, iova, fsynr, cfg->cbndx);
+
+ /* Clear the faulting FSR */
+ writel(fsr, cb_base + SMMU_CB_FSR);
+
+ /* Terminate any stalled transactions */
+ if ( fsr & SMMU_FSR_SS )
+ writel_relaxed(SMMU_RESUME_TERMINATE, cb_base + SMMU_CB_RESUME);
+}
+
+static void arm_smmu_global_fault(int irq, void *data,
+ struct cpu_user_regs *regs)
+{
+ u32 gfsr, gfsynr0, gfsynr1, gfsynr2;
+ struct arm_smmu_device *smmu = data;
+ void __iomem *gr0_base = SMMU_GR0_NS(smmu);
+
+ gfsr = readl_relaxed(gr0_base + SMMU_GR0_sGFSR);
+ gfsynr0 = readl_relaxed(gr0_base + SMMU_GR0_sGFSYNR0);
+ gfsynr1 = readl_relaxed(gr0_base + SMMU_GR0_sGFSYNR1);
+ gfsynr2 = readl_relaxed(gr0_base + SMMU_GR0_sGFSYNR2);
+
+ if ( !gfsr )
+ return;
+
+ smmu_err(smmu, "Unexpected global fault, this could be serious\n");
+ smmu_err(smmu,
+ "\tGFSR 0x%08x, GFSYNR0 0x%08x, GFSYNR1 0x%08x, GFSYNR2 0x%08x\n",
+ gfsr, gfsynr0, gfsynr1, gfsynr2);
+ writel(gfsr, gr0_base + SMMU_GR0_sGFSR);
+}
+
+static struct arm_smmu_master *
+find_smmu_master(struct arm_smmu_device *smmu,
+ const struct dt_device_node *dev_node)
+{
+ struct rb_node *node = smmu->masters.rb_node;
+
+ while ( node )
+ {
+ struct arm_smmu_master *master;
+
+ master = container_of(node, struct arm_smmu_master, node);
+
+ if ( dev_node < master->dt_node )
+ node = node->rb_left;
+ else if ( dev_node > master->dt_node )
+ node = node->rb_right;
+ else
+ return master;
+ }
+
+ return NULL;
+}
+
+static __init int insert_smmu_master(struct arm_smmu_device *smmu,
+ struct arm_smmu_master *master)
+{
+ struct rb_node **new, *parent;
+
+ new = &smmu->masters.rb_node;
+ parent = NULL;
+ while ( *new )
+ {
+ struct arm_smmu_master *this;
+
+ this = container_of(*new, struct arm_smmu_master, node);
+
+ parent = *new;
+ if ( master->dt_node < this->dt_node )
+ new = &((*new)->rb_left);
+ else if (master->dt_node > this->dt_node)
+ new = &((*new)->rb_right);
+ else
+ return -EEXIST;
+ }
+
+ rb_link_node(&master->node, parent, new);
+ rb_insert_color(&master->node, &smmu->masters);
+ return 0;
+}
+
+static __init int register_smmu_master(struct arm_smmu_device *smmu,
+ struct dt_phandle_args *masterspec)
+{
+ int i, sid;
+ struct arm_smmu_master *master;
+ int rc = 0;
+
+ smmu_dbg(smmu, "Try to add master %s\n", masterspec->np->name);
+
+ master = find_smmu_master(smmu, masterspec->np);
+ if ( master )
+ {
+ smmu_err(smmu,
+ "rejecting multiple registrations for master device %s\n",
+ masterspec->np->name);
+ return -EBUSY;
+ }
+
+ if ( masterspec->args_count > MAX_MASTER_STREAMIDS )
+ {
+ smmu_err(smmu,
+ "reached maximum number (%d) of stream IDs for master device %s\n",
+ MAX_MASTER_STREAMIDS, masterspec->np->name);
+ return -ENOSPC;
+ }
+
+ master = xzalloc(struct arm_smmu_master);
+ if ( !master )
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&master->list);
+ master->dt_node = masterspec->np;
+ master->num_streamids = masterspec->args_count;
+
+ dt_device_set_protected(masterspec->np);
+
+ for ( i = 0; i < master->num_streamids; ++i )
+ {
+ sid = masterspec->args[i];
+ if ( test_and_set_bit(sid, smmu->sids) )
+ {
+ smmu_err(smmu, "duplicate stream ID (%d)\n", sid);
+ xfree(master);
+ return -EEXIST;
+ }
+ master->streamids[i] = masterspec->args[i];
+ }
+
+ rc = insert_smmu_master(smmu, master);
+ /* Insertion should never fail */
+ ASSERT(rc == 0);
+
+ return 0;
+}
+
+static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
+{
+ int idx;
+
+ do
+ {
+ idx = find_next_zero_bit(map, end, start);
+ if ( idx == end )
+ return -ENOSPC;
+ } while ( test_and_set_bit(idx, map) );
+
+ return idx;
+}
+
+static void __arm_smmu_free_bitmap(unsigned long *map, int idx)
+{
+ clear_bit(idx, map);
+}
+
+static void arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
+{
+ int count = 0;
+ void __iomem *gr0_base = SMMU_GR0(smmu);
+
+ writel_relaxed(0, gr0_base + SMMU_GR0_sTLBGSYNC);
+ while ( readl_relaxed(gr0_base + SMMU_GR0_sTLBGSTATUS) &
+ SMMU_sTLBGSTATUS_GSACTIVE )
+ {
+ cpu_relax();
+ if ( ++count == SMMU_TLB_LOOP_TIMEOUT )
+ {
+ smmu_err(smmu, "TLB sync timed out -- SMMU may be deadlocked\n");
+ return;
+ }
+ udelay(1);
+ }
+}
+
+static void arm_smmu_tlb_inv_context(struct arm_smmu_domain_cfg *cfg)
+{
+ struct arm_smmu_device *smmu = cfg->smmu;
+ void __iomem *base = SMMU_GR0(smmu);
+
+ writel_relaxed(SMMU_CB_VMID(cfg),
+ base + SMMU_GR0_TLBIVMID);
+
+ arm_smmu_tlb_sync(smmu);
+}
+
+static void arm_smmu_iotlb_flush_all(struct domain *d)
+{
+ struct arm_smmu_domain *smmu_domain = domain_hvm_iommu(d)->arch.priv;
+ struct arm_smmu_domain_cfg *cfg;
+
+ spin_lock(&smmu_domain->lock);
+ list_for_each_entry(cfg, &smmu_domain->contexts, list)
+ arm_smmu_tlb_inv_context(cfg);
+ spin_unlock(&smmu_domain->lock);
+}
+
+static void arm_smmu_iotlb_flush(struct domain *d, unsigned long gfn,
+ unsigned int page_count)
+{
+ /* ARM SMMU v1 doesn't have flush by VMA and VMID */
+ arm_smmu_iotlb_flush_all(d);
+}
+
+static int determine_smr_mask(struct arm_smmu_device *smmu,
+ struct arm_smmu_master *master,
+ struct arm_smmu_smr *smr, int start, int order)
+{
+ u16 i, zero_bits_mask, one_bits_mask, const_mask;
+ int nr;
+
+ nr = 1 << order;
+
+ if ( nr == 1 )
+ {
+ /* no mask, use streamid to match and be done with it */
+ smr->mask = 0;
+ smr->id = master->streamids[start];
+ return 0;
+ }
+
+ zero_bits_mask = 0;
+ one_bits_mask = 0xffff;
+ for ( i = start; i < start + nr; i++)
+ {
+ zero_bits_mask |= master->streamids[i]; /* const 0 bits */
+ one_bits_mask &= master->streamids[i]; /* const 1 bits */
+ }
+ zero_bits_mask = ~zero_bits_mask;
+
+ /* bits having constant values (either 0 or 1) */
+ const_mask = zero_bits_mask | one_bits_mask;
+
+ i = hweight16(~const_mask);
+ if ( (1 << i) == nr )
+ {
+ smr->mask = ~const_mask;
+ smr->id = one_bits_mask;
+ }
+ else
+ /* no usable mask for this set of streamids */
+ return 1;
+
+ if ( ((smr->mask & smmu->smr_mask_mask) != smr->mask) ||
+ ((smr->id & smmu->smr_id_mask) != smr->id) )
+ /* insufficient number of mask/id bits */
+ return 1;
+
+ return 0;
+}
+
+static int determine_smr_mapping(struct arm_smmu_device *smmu,
+ struct arm_smmu_master *master,
+ struct arm_smmu_smr *smrs, int max_smrs)
+{
+ int nr_sid, nr, i, bit, start;
+
+ /*
+ * This function is called only once -- when a master is added
+ * to a domain. If master->num_s2crs != 0 then this master
+ * was already added to a domain.
+ */
+ BUG_ON(master->num_s2crs);
+
+ start = nr = 0;
+ nr_sid = master->num_streamids;
+ do
+ {
+ /*
+ * largest power-of-2 number of streamids for which to
+ * determine a usable mask/id pair for stream matching
+ */
+ bit = fls(nr_sid);
+ if (!bit)
+ return 0;
+
+ /*
+ * iterate over power-of-2 numbers to determine
+ * largest possible mask/id pair for stream matching
+ * of next 2**i streamids
+ */
+ for ( i = bit - 1; i >= 0; i-- )
+ {
+ if( !determine_smr_mask(smmu, master,
+ &smrs[master->num_s2crs],
+ start, i))
+ break;
+ }
+
+ if ( i < 0 )
+ goto out;
+
+ nr = 1 << i;
+ nr_sid -= nr;
+ start += nr;
+ master->num_s2crs++;
+ } while ( master->num_s2crs <= max_smrs );
+
+out:
+ if ( nr_sid )
+ {
+ /* not enough mapping groups available */
+ master->num_s2crs = 0;
+ return -ENOSPC;
+ }
+
+ return 0;
+}
+
+static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
+ struct arm_smmu_master *master)
+{
+ int i, max_smrs, ret;
+ struct arm_smmu_smr *smrs;
+ void __iomem *gr0_base = SMMU_GR0(smmu);
+
+ if ( !(smmu->features & SMMU_FEAT_STREAM_MATCH) )
+ return 0;
+
+ if ( master->smrs )
+ return -EEXIST;
+
+ max_smrs = min(smmu->num_mapping_groups, master->num_streamids);
+ smrs = xmalloc_array(struct arm_smmu_smr, max_smrs);
+ if ( !smrs )
+ {
+ smmu_err(smmu, "failed to allocated %d SMRs for master %s\n",
+ max_smrs, dt_node_name(master->dt_node));
+ return -ENOMEM;
+ }
+
+ ret = determine_smr_mapping(smmu, master, smrs, max_smrs);
+ if ( ret )
+ goto err_free_smrs;
+
+ /* Allocate the SMRs on the root SMMU */
+ for ( i = 0; i < master->num_s2crs; ++i )
+ {
+ int idx = __arm_smmu_alloc_bitmap(smmu->smr_map, 0,
+ smmu->num_mapping_groups);
+ if ( idx < 0 )
+ {
+ smmu_err(smmu, "failed to allocate free SMR\n");
+ goto err_free_bitmap;
+ }
+ smrs[i].idx = idx;
+ }
+
+ /* It worked! Now, poke the actual hardware */
+ for ( i = 0; i < master->num_s2crs; ++i )
+ {
+ u32 reg = SMMU_SMR_VALID | smrs[i].id << SMMU_SMR_ID_SHIFT |
+ smrs[i].mask << SMMU_SMR_MASK_SHIFT;
+ smmu_dbg(smmu, "SMR%d: 0x%x\n", smrs[i].idx, reg);
+ writel_relaxed(reg, gr0_base + SMMU_GR0_SMR(smrs[i].idx));
+ }
+
+ master->smrs = smrs;
+ return 0;
+
+err_free_bitmap:
+ while (--i >= 0)
+ __arm_smmu_free_bitmap(smmu->smr_map, smrs[i].idx);
+ master->num_s2crs = 0;
+err_free_smrs:
+ xfree(smrs);
+ return -ENOSPC;
+}
+
+/* Forward declaration */
+static void arm_smmu_destroy_domain_context(struct arm_smmu_domain_cfg *cfg);
+
+static int arm_smmu_domain_add_master(struct domain *d,
+ struct arm_smmu_domain_cfg *cfg,
+ struct arm_smmu_master *master)
+{
+ int i, ret;
+ struct arm_smmu_device *smmu = cfg->smmu;
+ void __iomem *gr0_base = SMMU_GR0(smmu);
+ struct arm_smmu_smr *smrs = master->smrs;
+
+ if ( master->cfg )
+ return -EBUSY;
+
+ ret = arm_smmu_master_configure_smrs(smmu, master);
+ if ( ret )
+ return ret;
+
+ /* Now we're at the root, time to point at our context bank */
+ if ( !master->num_s2crs )
+ master->num_s2crs = master->num_streamids;
+
+ for ( i = 0; i < master->num_s2crs; ++i )
+ {
+ u32 idx, s2cr;
+
+ idx = smrs ? smrs[i].idx : master->streamids[i];
+ s2cr = (SMMU_S2CR_TYPE_TRANS << SMMU_S2CR_TYPE_SHIFT) |
+ (cfg->cbndx << SMMU_S2CR_CBNDX_SHIFT);
+ smmu_dbg(smmu, "S2CR%d: 0x%x\n", idx, s2cr);
+ writel_relaxed(s2cr, gr0_base + SMMU_GR0_S2CR(idx));
+ }
+
+ master->cfg = cfg;
+ list_add(&master->list, &cfg->masters);
+
+ return 0;
+}
+
+static void arm_smmu_domain_remove_master(struct arm_smmu_master *master)
+{
+ int i;
+ struct arm_smmu_domain_cfg *cfg = master->cfg;
+ struct arm_smmu_device *smmu = cfg->smmu;
+ void __iomem *gr0_base = SMMU_GR0(smmu);
+ struct arm_smmu_smr *smrs = master->smrs;
+
+ /*
+ * We *must* clear the S2CR first, because freeing the SMR means
+ * that it can be reallocated immediately
+ */
+ for ( i = 0; i < master->num_streamids; ++i )
+ {
+ u16 sid = master->streamids[i];
+ writel_relaxed(SMMU_S2CR_TYPE_FAULT,
+ gr0_base + SMMU_GR0_S2CR(sid));
+ }
+
+ /* Invalidate the SMRs before freeing back to the allocator */
+ for (i = 0; i < master->num_s2crs; ++i) {
+ u8 idx = smrs[i].idx;
+ writel_relaxed(~SMMU_SMR_VALID, gr0_base + SMMU_GR0_SMR(idx));
+ __arm_smmu_free_bitmap(smmu->smr_map, idx);
+ }
+
+ master->smrs = NULL;
+ master->num_s2crs = 0;
+ xfree(smrs);
+
+ master->cfg = NULL;
+ list_del(&master->list);
+ INIT_LIST_HEAD(&master->list);
+}
+
+static void arm_smmu_init_context_bank(struct arm_smmu_domain_cfg *cfg)
+{
+ u32 reg;
+ struct arm_smmu_device *smmu = cfg->smmu;
+ void __iomem *cb_base, *gr1_base;
+ paddr_t p2maddr;
+
+ ASSERT(cfg->domain != NULL);
+ p2maddr = page_to_maddr(cfg->domain->arch.p2m.first_level);
+
+ gr1_base = SMMU_GR1(smmu);
+ cb_base = SMMU_CB_BASE(smmu) + SMMU_CB(smmu, cfg->cbndx);
+
+ /* CBAR */
+ reg = cfg->cbar;
+ if ( smmu->version == 1 )
+ reg |= cfg->irptndx << SMMU_CBAR_IRPTNDX_SHIFT;
+
+ reg |= SMMU_CB_VMID(cfg) << SMMU_CBAR_VMID_SHIFT;
+ writel_relaxed(reg, gr1_base + SMMU_GR1_CBAR(cfg->cbndx));
+
+ if ( smmu->version > 1 )
+ {
+ /* CBA2R */
+#ifdef CONFIG_ARM_64
+ reg = SMMU_CBA2R_RW64_64BIT;
+#else
+ reg = SMMU_CBA2R_RW64_32BIT;
+#endif
+ writel_relaxed(reg, gr1_base + SMMU_GR1_CBA2R(cfg->cbndx));
+ }
+
+ /* TTBR0 */
+ reg = (p2maddr & ((1ULL << 32) - 1));
+ writel_relaxed(reg, cb_base + SMMU_CB_TTBR0_LO);
+ reg = (p2maddr >> 32);
+ writel_relaxed(reg, cb_base + SMMU_CB_TTBR0_HI);
+
+ /*
+ * TCR
+ * We use long descriptor, with inner-shareable WBWA tables in TTBR0.
+ */
+ if ( smmu->version > 1 )
+ {
+ /* 4K Page Table */
+ if ( PAGE_SIZE == PAGE_SIZE_4K )
+ reg = SMMU_TCR_TG0_4K;
+ else
+ reg = SMMU_TCR_TG0_64K;
+
+ switch ( smmu->s2_output_size ) {
+ case 32:
+ reg |= (SMMU_TCR2_ADDR_32 << SMMU_TCR_PASIZE_SHIFT);
+ break;
+ case 36:
+ reg |= (SMMU_TCR2_ADDR_36 << SMMU_TCR_PASIZE_SHIFT);
+ break;
+ case 40:
+ reg |= (SMMU_TCR2_ADDR_40 << SMMU_TCR_PASIZE_SHIFT);
+ break;
+ case 42:
+ reg |= (SMMU_TCR2_ADDR_42 << SMMU_TCR_PASIZE_SHIFT);
+ break;
+ case 44:
+ reg |= (SMMU_TCR2_ADDR_44 << SMMU_TCR_PASIZE_SHIFT);
+ break;
+ case 48:
+ reg |= (SMMU_TCR2_ADDR_48 << SMMU_TCR_PASIZE_SHIFT);
+ break;
+ }
+ }
+ else
+ reg = 0;
+
+ /* The attribute to walk the page table should be the same as VTCR_EL2 */
+ reg |= SMMU_TCR_EAE |
+ (SMMU_TCR_SH_IS << SMMU_TCR_SH0_SHIFT) |
+ (SMMU_TCR_RGN_WBWA << SMMU_TCR_ORGN0_SHIFT) |
+ (SMMU_TCR_RGN_WBWA << SMMU_TCR_IRGN0_SHIFT) |
+ (SMMU_TCR_SL0_LVL_1 << SMMU_TCR_SL0_SHIFT) |
+ /* T0SZ=(1)100 = -8 ( 32 -(-8) = 40 bit physical addresses ) */
+ (0x18 << SMMU_TCR_T0SZ_SHIFT);
+ writel_relaxed(reg, cb_base + SMMU_CB_TCR);
+
+ /* SCTLR */
+ reg = SMMU_SCTLR_CFCFG |
+ SMMU_SCTLR_CFIE |
+ SMMU_SCTLR_CFRE |
+ SMMU_SCTLR_M |
+ SMMU_SCTLR_EAE_SBOP;
+
+ writel_relaxed(reg, cb_base + SMMU_CB_SCTLR);
+}
+
+static struct arm_smmu_domain_cfg *
+arm_smmu_alloc_domain_context(struct domain *d,
+ struct arm_smmu_device *smmu)
+{
+ unsigned int irq;
+ int ret, start;
+ struct arm_smmu_domain_cfg *cfg;
+ struct arm_smmu_domain *smmu_domain = domain_hvm_iommu(d)->arch.priv;
+
+ ASSERT(spin_is_locked(&smmu_domain->lock));
+
+ cfg = xzalloc(struct arm_smmu_domain_cfg);
+ if ( !cfg )
+ return NULL;
+
+ /* Master already initialized to another domain ... */
+ if ( cfg->domain != NULL )
+ goto out_free_mem;
+
+ cfg->cbar = SMMU_CBAR_TYPE_S2_TRANS;
+ start = 0;
+
+ ret = __arm_smmu_alloc_bitmap(smmu->context_map, start,
+ smmu->num_context_banks);
+ if ( ret < 0 )
+ goto out_free_mem;
+
+ cfg->cbndx = ret;
+ if ( smmu->version == 1 )
+ {
+ cfg->irptndx = atomic_inc_return(&smmu->irptndx);
+ cfg->irptndx %= smmu->num_context_irqs;
+ }
+ else
+ cfg->irptndx = cfg->cbndx;
+
+ irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx];
+ ret = request_irq(irq, IRQF_SHARED, arm_smmu_context_fault,
+ "arm-smmu-context-fault", cfg);
+ if ( ret )
+ {
+ smmu_err(smmu, "failed to request context IRQ %d (%u)\n",
+ cfg->irptndx, irq);
+ cfg->irptndx = INVALID_IRPTNDX;
+ goto out_free_context;
+ }
+
+ cfg->domain = d;
+ cfg->smmu = smmu;
+ if ( smmu->features & SMMU_FEAT_COHERENT_WALK )
+ iommu_set_feature(d, IOMMU_FEAT_COHERENT_WALK);
+
+ arm_smmu_init_context_bank(cfg);
+ list_add(&cfg->list, &smmu_domain->contexts);
+ INIT_LIST_HEAD(&cfg->masters);
+
+ return cfg;
+
+out_free_context:
+ __arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx);
+out_free_mem:
+ xfree(cfg);
+
+ return NULL;
+}
+
+static void arm_smmu_destroy_domain_context(struct arm_smmu_domain_cfg *cfg)
+{
+ struct domain *d = cfg->domain;
+ struct arm_smmu_domain *smmu_domain = domain_hvm_iommu(d)->arch.priv;
+ struct arm_smmu_device *smmu = cfg->smmu;
+ void __iomem *cb_base;
+ unsigned int irq;
+
+ ASSERT(spin_is_locked(&smmu_domain->lock));
+ BUG_ON(!list_empty(&cfg->masters));
+
+ /* Disable the context bank and nuke the TLB before freeing it */
+ cb_base = SMMU_CB_BASE(smmu) + SMMU_CB(smmu, cfg->cbndx);
+ writel_relaxed(0, cb_base + SMMU_CB_SCTLR);
+ arm_smmu_tlb_inv_context(cfg);
+
+ if ( cfg->irptndx != INVALID_IRPTNDX )
+ {
+ irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx];
+ release_irq(irq, cfg);
+ }
+
+ __arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx);
+ list_del(&cfg->list);
+ xfree(cfg);
+}
+
+static struct arm_smmu_device *
+arm_smmu_find_smmu_by_dev(const struct dt_device_node *dev)
+{
+ struct arm_smmu_device *smmu;
+ struct arm_smmu_master *master = NULL;
+
+ list_for_each_entry( smmu, &arm_smmu_devices, list )
+ {
+ master = find_smmu_master(smmu, dev);
+ if ( master )
+ break;
+ }
+
+ if ( !master )
+ return NULL;
+
+ return smmu;
+}
+
+static int arm_smmu_attach_dev(struct domain *d,
+ const struct dt_device_node *dev)
+{
+ struct arm_smmu_device *smmu = arm_smmu_find_smmu_by_dev(dev);
+ struct arm_smmu_master *master;
+ struct arm_smmu_domain *smmu_domain = domain_hvm_iommu(d)->arch.priv;
+ struct arm_smmu_domain_cfg *cfg = NULL;
+ struct arm_smmu_domain_cfg *curr;
+ int ret;
+
+ printk(XENLOG_DEBUG "arm-smmu: attach %s to domain %d\n",
+ dt_node_full_name(dev), d->domain_id);
+
+ if ( !smmu )
+ {
+ printk(XENLOG_ERR "%s: cannot attach to SMMU, is it on the same bus?\n",
+ dt_node_full_name(dev));
+ return -ENODEV;
+ }
+
+ master = find_smmu_master(smmu, dev);
+ BUG_ON(master == NULL);
+
+ /* Check if the device is already assigned to someone */
+ if ( master->cfg )
+ return -EBUSY;
+
+ spin_lock(&smmu_domain->lock);
+ list_for_each_entry( curr, &smmu_domain->contexts, list )
+ {
+ if ( curr->smmu == smmu )
+ {
+ cfg = curr;
+ break;
+ }
+ }
+
+ if ( !cfg )
+ {
+ cfg = arm_smmu_alloc_domain_context(d, smmu);
+ if ( !cfg )
+ {
+ smmu_err(smmu, "unable to allocate context for domain %u\n",
+ d->domain_id);
+ spin_unlock(&smmu_domain->lock);
+ return -ENOMEM;
+ }
+ }
+ spin_unlock(&smmu_domain->lock);
+
+ ret = arm_smmu_domain_add_master(d, cfg, master);
+ if ( ret )
+ {
+ spin_lock(&smmu_domain->lock);
+ if ( list_empty(&cfg->masters) )
+ arm_smmu_destroy_domain_context(cfg);
+ spin_unlock(&smmu_domain->lock);
+ }
+
+ return ret;
+}
+
+static int arm_smmu_detach_dev(struct domain *d,
+ const struct dt_device_node *dev)
+{
+ struct arm_smmu_domain *smmu_domain = domain_hvm_iommu(d)->arch.priv;
+ struct arm_smmu_master *master;
+ struct arm_smmu_device *smmu = arm_smmu_find_smmu_by_dev(dev);
+ struct arm_smmu_domain_cfg *cfg;
+
+ printk(XENLOG_DEBUG "arm-smmu: detach %s to domain %d\n",
+ dt_node_full_name(dev), d->domain_id);
+
+ if ( !smmu )
+ {
+ printk(XENLOG_ERR "%s: cannot find the SMMU, is it on the same bus?\n",
+ dt_node_full_name(dev));
+ return -ENODEV;
+ }
+
+ master = find_smmu_master(smmu, dev);
+ BUG_ON(master == NULL);
+
+ cfg = master->cfg;
+
+ /* Sanity check to avoid removing a device that doesn't belong to
+ * the domain
+ */
+ if ( !cfg || cfg->domain != d )
+ {
+ printk(XENLOG_ERR "%s: was not attach to domain %d\n",
+ dt_node_full_name(dev), d->domain_id);
+ return -ESRCH;
+ }
+
+ arm_smmu_domain_remove_master(master);
+
+ spin_lock(&smmu_domain->lock);
+ if ( list_empty(&cfg->masters) )
+ arm_smmu_destroy_domain_context(cfg);
+ spin_unlock(&smmu_domain->lock);
+
+ return 0;
+}
+
+static int arm_smmu_reassign_dt_dev(struct domain *s, struct domain *t,
+ const struct dt_device_node *dev)
+{
+ int ret = 0;
+
+ /* Don't allow remapping on other domain than hwdom */
+ if ( t != hardware_domain )
+ return -EPERM;
+
+ if ( t == s )
+ return 0;
+
+ ret = arm_smmu_detach_dev(s, dev);
+ if ( ret )
+ return ret;
+
+ ret = arm_smmu_attach_dev(t, dev);
+
+ return ret;
+}
+
+static __init int arm_smmu_id_size_to_bits(int size)
+{
+ switch ( size )
+ {
+ case 0:
+ return 32;
+ case 1:
+ return 36;
+ case 2:
+ return 40;
+ case 3:
+ return 42;
+ case 4:
+ return 44;
+ case 5:
+ default:
+ return 48;
+ }
+}
+
+static __init int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
+{
+ unsigned long size;
+ void __iomem *gr0_base = SMMU_GR0(smmu);
+ u32 id;
+
+ smmu_info(smmu, "probing hardware configuration...\n");
+
+ /*
+ * Primecell ID
+ */
+ id = readl_relaxed(gr0_base + SMMU_GR0_PIDR2);
+ smmu->version = ((id >> SMMU_PIDR2_ARCH_SHIFT) & SMMU_PIDR2_ARCH_MASK) + 1;
+ smmu_info(smmu, "SMMUv%d with:\n", smmu->version);
+
+ /* ID0 */
+ id = readl_relaxed(gr0_base + SMMU_GR0_ID0);
+#ifndef CONFIG_ARM_64
+ if ( ((id >> SMMU_ID0_PTFS_SHIFT) & SMMU_ID0_PTFS_MASK) ==
+ SMMU_ID0_PTFS_V8_ONLY )
+ {
+ smmu_err(smmu, "\tno v7 descriptor support!\n");
+ return -ENODEV;
+ }
+#endif
+ if ( id & SMMU_ID0_S1TS )
+ {
+ smmu->features |= SMMU_FEAT_TRANS_S1;
+ smmu_info(smmu, "\tstage 1 translation\n");
+ }
+
+ if ( id & SMMU_ID0_S2TS )
+ {
+ smmu->features |= SMMU_FEAT_TRANS_S2;
+ smmu_info(smmu, "\tstage 2 translation\n");
+ }
+
+ if ( id & SMMU_ID0_NTS )
+ {
+ smmu->features |= SMMU_FEAT_TRANS_NESTED;
+ smmu_info(smmu, "\tnested translation\n");
+ }
+
+ if ( !(smmu->features &
+ (SMMU_FEAT_TRANS_S1 | SMMU_FEAT_TRANS_S2 |
+ SMMU_FEAT_TRANS_NESTED)) )
+ {
+ smmu_err(smmu, "\tno translation support!\n");
+ return -ENODEV;
+ }
+
+ /* We need at least support for Stage 2 */
+ if ( !(smmu->features & SMMU_FEAT_TRANS_S2) )
+ {
+ smmu_err(smmu, "\tno stage 2 translation!\n");
+ return -ENODEV;
+ }
+
+ if ( id & SMMU_ID0_CTTW )
+ {
+ smmu->features |= SMMU_FEAT_COHERENT_WALK;
+ smmu_info(smmu, "\tcoherent table walk\n");
+ }
+
+ if ( id & SMMU_ID0_SMS )
+ {
+ u32 smr, sid, mask;
+
+ smmu->features |= SMMU_FEAT_STREAM_MATCH;
+ smmu->num_mapping_groups = (id >> SMMU_ID0_NUMSMRG_SHIFT) &
+ SMMU_ID0_NUMSMRG_MASK;
+ if ( smmu->num_mapping_groups == 0 )
+ {
+ smmu_err(smmu,
+ "stream-matching supported, but no SMRs present!\n");
+ return -ENODEV;
+ }
+
+ smr = SMMU_SMR_MASK_MASK << SMMU_SMR_MASK_SHIFT;
+ smr |= (SMMU_SMR_ID_MASK << SMMU_SMR_ID_SHIFT);
+ writel_relaxed(smr, gr0_base + SMMU_GR0_SMR(0));
+ smr = readl_relaxed(gr0_base + SMMU_GR0_SMR(0));
+
+ mask = (smr >> SMMU_SMR_MASK_SHIFT) & SMMU_SMR_MASK_MASK;
+ sid = (smr >> SMMU_SMR_ID_SHIFT) & SMMU_SMR_ID_MASK;
+ if ( (mask & sid) != sid )
+ {
+ smmu_err(smmu,
+ "SMR mask bits (0x%x) insufficient for ID field (0x%x)\n",
+ mask, sid);
+ return -ENODEV;
+ }
+ smmu->smr_mask_mask = mask;
+ smmu->smr_id_mask = sid;
+
+ smmu_info(smmu,
+ "\tstream matching with %u register groups, mask 0x%x\n",
+ smmu->num_mapping_groups, mask);
+ }
+
+ /* ID1 */
+ id = readl_relaxed(gr0_base + SMMU_GR0_ID1);
+ smmu->pagesize = (id & SMMU_ID1_PAGESIZE) ? PAGE_SIZE_64K : PAGE_SIZE_4K;
+
+ /* Check for size mismatch of SMMU address space from mapped region */
+ size = 1 << (((id >> SMMU_ID1_NUMPAGENDXB_SHIFT) &
+ SMMU_ID1_NUMPAGENDXB_MASK) + 1);
+ size *= (smmu->pagesize << 1);
+ if ( smmu->size != size )
+ smmu_warn(smmu, "SMMU address space size (0x%lx) differs "
+ "from mapped region size (0x%lx)!\n", size, smmu->size);
+
+ smmu->num_s2_context_banks = (id >> SMMU_ID1_NUMS2CB_SHIFT) &
+ SMMU_ID1_NUMS2CB_MASK;
+ smmu->num_context_banks = (id >> SMMU_ID1_NUMCB_SHIFT) &
+ SMMU_ID1_NUMCB_MASK;
+ if ( smmu->num_s2_context_banks > smmu->num_context_banks )
+ {
+ smmu_err(smmu, "impossible number of S2 context banks!\n");
+ return -ENODEV;
+ }
+ smmu_info(smmu, "\t%u context banks (%u stage-2 only)\n",
+ smmu->num_context_banks, smmu->num_s2_context_banks);
+
+ /* ID2 */
+ id = readl_relaxed(gr0_base + SMMU_GR0_ID2);
+ size = arm_smmu_id_size_to_bits((id >> SMMU_ID2_IAS_SHIFT) &
+ SMMU_ID2_IAS_MASK);
+
+ /*
+ * Stage-1 output limited by stage-2 input size due to VTCR_EL2
+ * setup (see setup_virt_paging)
+ */
+ /* Current maximum output size of 40 bits */
+ smmu->s1_output_size = min(40UL, size);
+
+ /* The stage-2 output mask is also applied for bypass */
+ size = arm_smmu_id_size_to_bits((id >> SMMU_ID2_OAS_SHIFT) &
+ SMMU_ID2_OAS_MASK);
+ smmu->s2_output_size = min((unsigned long)PADDR_BITS, size);
+
+ if ( smmu->version == 1 )
+ smmu->input_size = 32;
+ else
+ {
+#ifdef CONFIG_ARM_64
+ size = (id >> SMMU_ID2_UBS_SHIFT) & SMMU_ID2_UBS_MASK;
+ size = min(39, arm_smmu_id_size_to_bits(size));
+#else
+ size = 32;
+#endif
+ smmu->input_size = size;
+
+ if ( (PAGE_SIZE == PAGE_SIZE_4K && !(id & SMMU_ID2_PTFS_4K) ) ||
+ (PAGE_SIZE == PAGE_SIZE_64K && !(id & SMMU_ID2_PTFS_64K)) ||
+ (PAGE_SIZE != PAGE_SIZE_4K && PAGE_SIZE != PAGE_SIZE_64K) )
+ {
+ smmu_err(smmu, "CPU page size 0x%lx unsupported\n",
+ PAGE_SIZE);
+ return -ENODEV;
+ }
+ }
+
+ smmu_info(smmu, "\t%lu-bit VA, %lu-bit IPA, %lu-bit PA\n",
+ smmu->input_size, smmu->s1_output_size, smmu->s2_output_size);
+ return 0;
+}
+
+static __init void arm_smmu_device_reset(struct arm_smmu_device *smmu)
+{
+ void __iomem *gr0_base = SMMU_GR0(smmu);
+ void __iomem *cb_base;
+ int i = 0;
+ u32 reg;
+
+ smmu_dbg(smmu, "device reset\n");
+
+ /* Clear Global FSR */
+ reg = readl_relaxed(SMMU_GR0_NS(smmu) + SMMU_GR0_sGFSR);
+ writel(reg, SMMU_GR0_NS(smmu) + SMMU_GR0_sGFSR);
+
+ /* Mark all SMRn as invalid and all S2CRn as fault */
+ for ( i = 0; i < smmu->num_mapping_groups; ++i )
+ {
+ writel_relaxed(~SMMU_SMR_VALID, gr0_base + SMMU_GR0_SMR(i));
+ writel_relaxed(SMMU_S2CR_TYPE_FAULT, gr0_base + SMMU_GR0_S2CR(i));
+ }
+
+ /* Make sure all context banks are disabled and clear CB_FSR */
+ for ( i = 0; i < smmu->num_context_banks; ++i )
+ {
+ cb_base = SMMU_CB_BASE(smmu) + SMMU_CB(smmu, i);
+ writel_relaxed(0, cb_base + SMMU_CB_SCTLR);
+ writel_relaxed(SMMU_FSR_FAULT, cb_base + SMMU_CB_FSR);
+ }
+
+ /* Invalidate the TLB, just in case */
+ writel_relaxed(0, gr0_base + SMMU_GR0_STLBIALL);
+ writel_relaxed(0, gr0_base + SMMU_GR0_TLBIALLH);
+ writel_relaxed(0, gr0_base + SMMU_GR0_TLBIALLNSNH);
+
+ reg = readl_relaxed(SMMU_GR0_NS(smmu) + SMMU_GR0_sCR0);
+
+ /* Enable fault reporting */
+ reg |= (SMMU_sCR0_GFRE | SMMU_sCR0_GFIE |
+ SMMU_sCR0_GCFGFRE | SMMU_sCR0_GCFGFIE);
+
+ /* Disable TLB broadcasting. */
+ reg |= (SMMU_sCR0_VMIDPNE | SMMU_sCR0_PTM);
+
+ /* Enable client access, generate a fault if no mapping is found */
+ reg &= ~(SMMU_sCR0_CLIENTPD);
+ reg |= SMMU_sCR0_USFCFG;
+
+ /* Disable forced broadcasting */
+ reg &= ~SMMU_sCR0_FB;
+
+ /* Don't upgrade barriers when client devices are not mapped to
+ * a translation context banks (just here for clarity as Xen policy
+ * is to deny invalid transaction). */
+ reg &= ~(SMMU_sCR0_BSU_MASK << SMMU_sCR0_BSU_SHIFT);
+
+ /* Push the button */
+ arm_smmu_tlb_sync(smmu);
+ writel_relaxed(reg, SMMU_GR0_NS(smmu) + SMMU_GR0_sCR0);
+}
+
+static int arm_smmu_iommu_domain_init(struct domain *d)
+{
+ struct arm_smmu_domain *smmu_domain;
+
+ smmu_domain = xzalloc(struct arm_smmu_domain);
+ if ( !smmu_domain )
+ return -ENOMEM;
+
+ spin_lock_init(&smmu_domain->lock);
+ INIT_LIST_HEAD(&smmu_domain->contexts);
+
+ domain_hvm_iommu(d)->arch.priv = smmu_domain;
+
+ return 0;
+}
+
+static void __hwdom_init arm_smmu_iommu_hwdom_init(struct domain *d)
+{
+}
+
+static void arm_smmu_iommu_domain_teardown(struct domain *d)
+{
+ struct arm_smmu_domain *smmu_domain = domain_hvm_iommu(d)->arch.priv;
+
+ ASSERT(list_empty(&smmu_domain->contexts));
+ xfree(smmu_domain);
+}
+
+static const struct iommu_ops arm_smmu_iommu_ops = {
+ .init = arm_smmu_iommu_domain_init,
+ .hwdom_init = arm_smmu_iommu_hwdom_init,
+ .teardown = arm_smmu_iommu_domain_teardown,
+ .iotlb_flush = arm_smmu_iotlb_flush,
+ .iotlb_flush_all = arm_smmu_iotlb_flush_all,
+ .assign_dt_device = arm_smmu_attach_dev,
+ .reassign_dt_device = arm_smmu_reassign_dt_dev,
+};
+
+static int __init smmu_init(struct dt_device_node *dev,
+ const void *data)
+{
+ struct arm_smmu_device *smmu;
+ int res;
+ u64 addr, size;
+ unsigned int num_irqs, i;
+ struct dt_phandle_args masterspec;
+ struct rb_node *node;
+
+ /* Even if the device can't be initialized, we don't want to give
+ * the smmu device to dom0.
+ */
+ dt_device_set_used_by(dev, DOMID_XEN);
+
+ smmu = xzalloc(struct arm_smmu_device);
+ if ( !smmu )
+ {
+ printk(XENLOG_ERR "%s: failed to allocate arm_smmu_device\n",
+ dt_node_full_name(dev));
+ return -ENOMEM;
+ }
+
+ smmu->node = dev;
+ check_driver_options(smmu);
+
+ res = dt_device_get_address(smmu->node, 0, &addr, &size);
+ if ( res )
+ {
+ smmu_err(smmu, "unable to retrieve the base address of the SMMU\n");
+ goto out_err;
+ }
+
+ smmu->base = ioremap_nocache(addr, size);
+ if ( !smmu->base )
+ {
+ smmu_err(smmu, "unable to map the SMMU memory\n");
+ goto out_err;
+ }
+
+ smmu->size = size;
+
+ if ( !dt_property_read_u32(smmu->node, "#global-interrupts",
+ &smmu->num_global_irqs) )
+ {
+ smmu_err(smmu, "missing #global-interrupts\n");
+ goto out_unmap;
+ }
+
+ num_irqs = dt_number_of_irq(smmu->node);
+ if ( num_irqs > smmu->num_global_irqs )
+ smmu->num_context_irqs = num_irqs - smmu->num_global_irqs;
+
+ if ( !smmu->num_context_irqs )
+ {
+ smmu_err(smmu, "found %d interrupts but expected at least %d\n",
+ num_irqs, smmu->num_global_irqs + 1);
+ goto out_unmap;
+ }
+
+ smmu->irqs = xzalloc_array(unsigned int, num_irqs);
+ if ( !smmu->irqs )
+ {
+ smmu_err(smmu, "failed to allocated %d irqs\n", num_irqs);
+ goto out_unmap;
+ }
+
+ for ( i = 0; i < num_irqs; i++ )
+ {
+ res = platform_get_irq(smmu->node, i);
+ if ( res < 0 )
+ {
+ smmu_err(smmu, "failed to get irq index %d\n", i);
+ goto out_free_irqs;
+ }
+ smmu->irqs[i] = res;
+ }
+
+ smmu->sids = xzalloc_array(unsigned long,
+ BITS_TO_LONGS(SMMU_MAX_STREAMIDS));
+ if ( !smmu->sids )
+ {
+ smmu_err(smmu, "failed to allocated bitmap for stream ID tracking\n");
+ goto out_free_masters;
+ }
+
+
+ i = 0;
+ smmu->masters = RB_ROOT;
+ while ( !dt_parse_phandle_with_args(smmu->node, "mmu-masters",
+ "#stream-id-cells", i, &masterspec) )
+ {
+ res = register_smmu_master(smmu, &masterspec);
+ if ( res )
+ {
+ smmu_err(smmu, "failed to add master %s\n",
+ masterspec.np->name);
+ goto out_free_masters;
+ }
+ i++;
+ }
+
+ smmu_info(smmu, "registered %d master devices\n", i);
+
+ res = arm_smmu_device_cfg_probe(smmu);
+ if ( res )
+ {
+ smmu_err(smmu, "failed to probe the SMMU\n");
+ goto out_free_masters;
+ }
+
+ if ( smmu->version > 1 &&
+ smmu->num_context_banks != smmu->num_context_irqs )
+ {
+ smmu_err(smmu,
+ "found only %d context interrupt(s) but %d required\n",
+ smmu->num_context_irqs, smmu->num_context_banks);
+ goto out_free_masters;
+ }
+
+ smmu_dbg(smmu, "register global IRQs handler\n");
+
+ for ( i = 0; i < smmu->num_global_irqs; ++i )
+ {
+ smmu_dbg(smmu, "\t- global IRQ %u\n", smmu->irqs[i]);
+ res = request_irq(smmu->irqs[i], IRQF_SHARED, arm_smmu_global_fault,
+ "arm-smmu global fault", smmu);
+ if ( res )
+ {
+ smmu_err(smmu, "failed to request global IRQ %d (%u)\n",
+ i, smmu->irqs[i]);
+ goto out_release_irqs;
+ }
+ }
+
+ INIT_LIST_HEAD(&smmu->list);
+ list_add(&smmu->list, &arm_smmu_devices);
+
+ arm_smmu_device_reset(smmu);
+
+ iommu_set_ops(&arm_smmu_iommu_ops);
+
+ /* sids field can be freed... */
+ xfree(smmu->sids);
+ smmu->sids = NULL;
+
+ return 0;
+
+out_release_irqs:
+ while (i--)
+ release_irq(smmu->irqs[i], smmu);
+
+out_free_masters:
+ for ( node = rb_first(&smmu->masters); node; node = rb_next(node) )
+ {
+ struct arm_smmu_master *master;
+
+ master = container_of(node, struct arm_smmu_master, node);
+ xfree(master);
+ }
+
+ xfree(smmu->sids);
+
+out_free_irqs:
+ xfree(smmu->irqs);
+
+out_unmap:
+ iounmap(smmu->base);
+
+out_err:
+ xfree(smmu);
+
+ return -ENODEV;
+}
+
+static const char * const smmu_dt_compat[] __initconst =
+{
+ "arm,mmu-400",
+ NULL
+};
+
+DT_DEVICE_START(smmu, "ARM SMMU", DEVICE_IOMMU)
+ .compatible = smmu_dt_compat,
+ .init = smmu_init,
+DT_DEVICE_END
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--
1.7.10.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m
2014-05-19 16:23 [PATCH v8 0/4] IOMMU support for ARM Julien Grall
` (2 preceding siblings ...)
2014-05-19 16:23 ` [PATCH v8 3/4] drivers/passthrough: arm: Add support for SMMU drivers Julien Grall
@ 2014-05-19 16:24 ` Julien Grall
2014-05-20 7:46 ` Jan Beulich
2014-05-21 13:27 ` Ian Campbell
2014-05-21 13:27 ` [PATCH v8 0/4] IOMMU support for ARM Ian Campbell
4 siblings, 2 replies; 17+ messages in thread
From: Julien Grall @ 2014-05-19 16:24 UTC (permalink / raw)
To: xen-devel
Cc: stefano.stabellini, Julien Grall, tim, ian.campbell, Jan Beulich
Grant mapping can be used for DMA request. The dev_bus_addr returned by the
hypercall is the MFN (not the IPA). Currently Linux is using this address (via
swiotlb) to program the DMA.
When the device is protected by IOMMU the request will fail. We have to
add 1:1 mapping in the domain p2m to allow DMA request working.
This is valid because DOM0 has its memory mapped 1:1 and therefore we know
that RAM and devices cannot clash.
The grant mapping code already handle this case for x86 PV guests. Reuse the
same code path for ARM guest.
Signed-off-by: Julien Grall <julien.grall@linaro.org>
Cc: Jan Beulich <jbeulich@suse.com>
---
The patch has been heavily rework to use iommu_{,un}map_page. I dropped
all the acks.
Changes in v8:
- Rework differently the 1:1 mapping by using iommu_{,un}map_page
helpers.
Changes in v5:
- Update commit message
Changes in v4:
- Patch added
---
xen/arch/arm/p2m.c | 2 ++
xen/common/grant_table.c | 4 ++--
xen/drivers/passthrough/arm/smmu.c | 44 ++++++++++++++++++++++++++++++++++++
xen/include/asm-arm/grant_table.h | 2 ++
xen/include/asm-arm/p2m.h | 2 ++
xen/include/asm-x86/grant_table.h | 2 ++
6 files changed, 54 insertions(+), 2 deletions(-)
diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index 96bc0ef..810459a 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -227,6 +227,7 @@ static lpae_t mfn_to_p2m_entry(unsigned long mfn, unsigned int mattr,
e.p2m.write = 0;
break;
+ case p2m_iommu_map_rw:
case p2m_map_foreign:
case p2m_grant_map_rw:
case p2m_mmio_direct:
@@ -234,6 +235,7 @@ static lpae_t mfn_to_p2m_entry(unsigned long mfn, unsigned int mattr,
e.p2m.write = 1;
break;
+ case p2m_iommu_map_ro:
case p2m_grant_map_ro:
case p2m_invalid:
e.p2m.xn = 1;
diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c
index 2c93d9c..7e549f2 100644
--- a/xen/common/grant_table.c
+++ b/xen/common/grant_table.c
@@ -727,7 +727,7 @@ __gnttab_map_grant_ref(
double_gt_lock(lgt, rgt);
- if ( !paging_mode_translate(ld) && need_iommu(ld) )
+ if ( gnttab_need_iommu_mapping(ld) && need_iommu(ld) )
{
unsigned int wrc, rdc;
int err = 0;
@@ -935,7 +935,7 @@ __gnttab_unmap_common(
act->pin -= GNTPIN_hstw_inc;
}
- if ( !paging_mode_translate(ld) && need_iommu(ld) )
+ if ( gnttab_need_iommu_mapping(ld) && need_iommu(ld) )
{
unsigned int wrc, rdc;
int err = 0;
diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
index 21b4572..9f85800 100644
--- a/xen/drivers/passthrough/arm/smmu.c
+++ b/xen/drivers/passthrough/arm/smmu.c
@@ -1536,6 +1536,48 @@ static void arm_smmu_iommu_domain_teardown(struct domain *d)
xfree(smmu_domain);
}
+static int arm_smmu_map_page(struct domain *d, unsigned long gfn,
+ unsigned long mfn, unsigned int flags)
+{
+ p2m_type_t t;
+
+ /* This function should only be used by gnttab code when the domain
+ * is direct mapped and gfn == mfn.
+ */
+ if ( !is_domain_direct_mapped(d) || gfn != mfn )
+ return -EINVAL;
+
+ /* We only support readable and writable flags */
+ if ( !(flags & (IOMMUF_readable | IOMMUF_writable)) )
+ return -EINVAL;
+
+ /* The function guest_physmap_add_entry replace the current mapping
+ * if there is already one...
+ */
+ t = (flags & IOMMUF_writable)? p2m_iommu_map_rw : p2m_iommu_map_ro;
+
+ /* Grant mapping can be used for DMA request. The dev_bus_addr returned by
+ * the hypercall is the MFN (not the IPA). For device protected by
+ * an IOMMU, Xen needs to add a 1:1 mapping in the domain p2m to
+ * allow DMA request working.
+ * This is only valid when the domain is directed mapped
+ */
+ return guest_physmap_add_entry(d, gfn, mfn, 0, t);
+}
+
+static int arm_smmu_unmap_page(struct domain *d, unsigned long gfn)
+{
+ /* This function should only be used by gnttab code when the domain
+ * is direct mapped
+ */
+ if ( !is_domain_direct_mapped(d) )
+ return -EINVAL;
+
+ guest_physmap_remove_page(d, gfn, gfn, 0);
+
+ return 0;
+}
+
static const struct iommu_ops arm_smmu_iommu_ops = {
.init = arm_smmu_iommu_domain_init,
.hwdom_init = arm_smmu_iommu_hwdom_init,
@@ -1544,6 +1586,8 @@ static const struct iommu_ops arm_smmu_iommu_ops = {
.iotlb_flush_all = arm_smmu_iotlb_flush_all,
.assign_dt_device = arm_smmu_attach_dev,
.reassign_dt_device = arm_smmu_reassign_dt_dev,
+ .map_page = arm_smmu_map_page,
+ .unmap_page = arm_smmu_unmap_page,
};
static int __init smmu_init(struct dt_device_node *dev,
diff --git a/xen/include/asm-arm/grant_table.h b/xen/include/asm-arm/grant_table.h
index 6e0cc59..673bcdd 100644
--- a/xen/include/asm-arm/grant_table.h
+++ b/xen/include/asm-arm/grant_table.h
@@ -33,6 +33,8 @@ static inline int replace_grant_supported(void)
( ((i >= nr_grant_frames(d->grant_table)) && \
(i < max_nr_grant_frames)) ? 0 : (d->arch.grant_table_gpfn[i]))
+#define gnttab_need_iommu_mapping(d) is_domain_direct_mapped(d)
+
#endif /* __ASM_GRANT_TABLE_H__ */
/*
* Local variables:
diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h
index bd71abe..b68d5b8 100644
--- a/xen/include/asm-arm/p2m.h
+++ b/xen/include/asm-arm/p2m.h
@@ -45,6 +45,8 @@ typedef enum {
p2m_map_foreign, /* Ram pages from foreign domain */
p2m_grant_map_rw, /* Read/write grant mapping */
p2m_grant_map_ro, /* Read-only grant mapping */
+ p2m_iommu_map_rw, /* Read/write iommu mapping */
+ p2m_iommu_map_ro, /* Read-only iommu mapping */
p2m_max_real_type, /* Types after this won't be store in the p2m */
} p2m_type_t;
diff --git a/xen/include/asm-x86/grant_table.h b/xen/include/asm-x86/grant_table.h
index 3013869..e5ccf2b 100644
--- a/xen/include/asm-x86/grant_table.h
+++ b/xen/include/asm-x86/grant_table.h
@@ -65,6 +65,8 @@ static inline void gnttab_clear_flag(unsigned int nr, uint16_t *st)
/* Done implicitly when page tables are destroyed. */
#define gnttab_release_host_mappings(domain) ( paging_mode_external(domain) )
+#define gnttab_need_iommu_mapping(d) !paging_mode_translate(d)
+
static inline int replace_grant_supported(void)
{
return 1;
--
1.7.10.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m
2014-05-19 16:24 ` [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m Julien Grall
@ 2014-05-20 7:46 ` Jan Beulich
2014-05-20 9:21 ` Ian Campbell
2014-05-20 10:48 ` Julien Grall
2014-05-21 13:27 ` Ian Campbell
1 sibling, 2 replies; 17+ messages in thread
From: Jan Beulich @ 2014-05-20 7:46 UTC (permalink / raw)
To: Julien Grall; +Cc: xen-devel, stefano.stabellini, ian.campbell, tim
>>> On 19.05.14 at 18:24, <julien.grall@linaro.org> wrote:
> Grant mapping can be used for DMA request. The dev_bus_addr returned by the
> hypercall is the MFN (not the IPA). Currently Linux is using this address (via
> swiotlb) to program the DMA.
> When the device is protected by IOMMU the request will fail. We have to
> add 1:1 mapping in the domain p2m to allow DMA request working.
>
> This is valid because DOM0 has its memory mapped 1:1 and therefore we know
> that RAM and devices cannot clash.
Wasn't this 1:1 mapping meant as a temporary workaround? If so,
does it really make sense to base further code on it?
> --- a/xen/common/grant_table.c
> +++ b/xen/common/grant_table.c
> @@ -727,7 +727,7 @@ __gnttab_map_grant_ref(
>
> double_gt_lock(lgt, rgt);
>
> - if ( !paging_mode_translate(ld) && need_iommu(ld) )
> + if ( gnttab_need_iommu_mapping(ld) && need_iommu(ld) )
I suppose that you want to keep the common bits of this condition
common, but the two "need_iommu" in here look sort of redundant:
With the name chosen, I think it would make more sense for the
second condition to be moved into gnttab_need_iommu_mapping().
> --- a/xen/include/asm-arm/grant_table.h
> +++ b/xen/include/asm-arm/grant_table.h
> @@ -33,6 +33,8 @@ static inline int replace_grant_supported(void)
> ( ((i >= nr_grant_frames(d->grant_table)) && \
> (i < max_nr_grant_frames)) ? 0 : (d->arch.grant_table_gpfn[i]))
>
> +#define gnttab_need_iommu_mapping(d) is_domain_direct_mapped(d)
While this one indeed doesn't need extra parentheses, ...
> --- a/xen/include/asm-x86/grant_table.h
> +++ b/xen/include/asm-x86/grant_table.h
> @@ -65,6 +65,8 @@ static inline void gnttab_clear_flag(unsigned int nr, uint16_t *st)
> /* Done implicitly when page tables are destroyed. */
> #define gnttab_release_host_mappings(domain) ( paging_mode_external(domain) )
>
> +#define gnttab_need_iommu_mapping(d) !paging_mode_translate(d)
... this one does.
Also, with all of this I don't see how other than Dom0 is being (or
going to be) handled in this regard.
Jan
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m
2014-05-20 7:46 ` Jan Beulich
@ 2014-05-20 9:21 ` Ian Campbell
2014-05-20 10:48 ` Julien Grall
1 sibling, 0 replies; 17+ messages in thread
From: Ian Campbell @ 2014-05-20 9:21 UTC (permalink / raw)
To: Jan Beulich; +Cc: xen-devel, Julien Grall, stefano.stabellini, tim
On Tue, 2014-05-20 at 08:46 +0100, Jan Beulich wrote:
> >>> On 19.05.14 at 18:24, <julien.grall@linaro.org> wrote:
> > Grant mapping can be used for DMA request. The dev_bus_addr returned by the
> > hypercall is the MFN (not the IPA). Currently Linux is using this address (via
> > swiotlb) to program the DMA.
> > When the device is protected by IOMMU the request will fail. We have to
> > add 1:1 mapping in the domain p2m to allow DMA request working.
> >
> > This is valid because DOM0 has its memory mapped 1:1 and therefore we know
> > that RAM and devices cannot clash.
>
> Wasn't this 1:1 mapping meant as a temporary workaround? If so,
> does it really make sense to base further code on it?
IOMMU on ARM has not become so prevalent (yet?) that we can get rid of
it. I think we are mostly resigned to keeping it around for the time
being. (sadly)
Ian.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m
2014-05-20 7:46 ` Jan Beulich
2014-05-20 9:21 ` Ian Campbell
@ 2014-05-20 10:48 ` Julien Grall
1 sibling, 0 replies; 17+ messages in thread
From: Julien Grall @ 2014-05-20 10:48 UTC (permalink / raw)
To: Jan Beulich; +Cc: xen-devel, stefano.stabellini, ian.campbell, tim
On 05/20/2014 08:46 AM, Jan Beulich wrote:
>> --- a/xen/common/grant_table.c
>> +++ b/xen/common/grant_table.c
>> @@ -727,7 +727,7 @@ __gnttab_map_grant_ref(
>>
>> double_gt_lock(lgt, rgt);
>>
>> - if ( !paging_mode_translate(ld) && need_iommu(ld) )
>> + if ( gnttab_need_iommu_mapping(ld) && need_iommu(ld) )
>
> I suppose that you want to keep the common bits of this condition
> common, but the two "need_iommu" in here look sort of redundant:
> With the name chosen, I think it would make more sense for the
> second condition to be moved into gnttab_need_iommu_mapping().
Ok. I will move these bits in the macro.
>> --- a/xen/include/asm-arm/grant_table.h
>> +++ b/xen/include/asm-arm/grant_table.h
>> @@ -33,6 +33,8 @@ static inline int replace_grant_supported(void)
>> ( ((i >= nr_grant_frames(d->grant_table)) && \
>> (i < max_nr_grant_frames)) ? 0 : (d->arch.grant_table_gpfn[i]))
>>
>> +#define gnttab_need_iommu_mapping(d) is_domain_direct_mapped(d)
>
> While this one indeed doesn't need extra parentheses, ...
>
>> --- a/xen/include/asm-x86/grant_table.h
>> +++ b/xen/include/asm-x86/grant_table.h
>> @@ -65,6 +65,8 @@ static inline void gnttab_clear_flag(unsigned int nr, uint16_t *st)
>> /* Done implicitly when page tables are destroyed. */
>> #define gnttab_release_host_mappings(domain) ( paging_mode_external(domain) )
>>
>> +#define gnttab_need_iommu_mapping(d) !paging_mode_translate(d)
>
> ... this one does.
Oh right, I will fix it in the next version.
> Also, with all of this I don't see how other than Dom0 is being (or
> going to be) handled in this regard.
Only DOM0 uses direct mapping (i.e 1:1 mapping). The other guests will
use their own mapping. As the P2M is shared, we can't insert this 1:1 entry.
Only protected devices (i.e the IOMMU has been programmed by Xen) will
be passtrough to the guest. So we will be able to modify dev_bus_addr to
return an IPA (guest address) rather than an MFN.
For now, passthrough is not supported, therefore guest doesn't have
DMA-capable device. I will take care of this solution with my non-PCI
passthrough patch series.
Regards,
--
Julien Grall
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v8 0/4] IOMMU support for ARM
2014-05-19 16:23 [PATCH v8 0/4] IOMMU support for ARM Julien Grall
` (3 preceding siblings ...)
2014-05-19 16:24 ` [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m Julien Grall
@ 2014-05-21 13:27 ` Ian Campbell
4 siblings, 0 replies; 17+ messages in thread
From: Ian Campbell @ 2014-05-21 13:27 UTC (permalink / raw)
To: Julien Grall; +Cc: xen-devel, tim, stefano.stabellini
On Mon, 2014-05-19 at 17:23 +0100, Julien Grall wrote:
> xen/arm: p2m: Clean cache PT when the IOMMU doesn't support coherent
> walk
> xen: iommu: Define PAGE_{SHIFT,SIZE,ALIGN,MASK)_64K
I've committed these.
> drivers/passthrough: arm: Add support for SMMU drivers
> xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m
But not these. The last one has outstanding comments and the penultimate
one cannot go in without.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m
2014-05-19 16:24 ` [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m Julien Grall
2014-05-20 7:46 ` Jan Beulich
@ 2014-05-21 13:27 ` Ian Campbell
2014-05-21 13:42 ` Julien Grall
1 sibling, 1 reply; 17+ messages in thread
From: Ian Campbell @ 2014-05-21 13:27 UTC (permalink / raw)
To: Julien Grall; +Cc: xen-devel, tim, Jan Beulich, stefano.stabellini
On Mon, 2014-05-19 at 17:24 +0100, Julien Grall wrote:
> Grant mapping can be used for DMA request. The dev_bus_addr returned by the
^Currently (?)
> hypercall is the MFN (not the IPA). Currently Linux is using this address (via
> swiotlb) to program the DMA.
Rather than talking specifically about Linux and swiotlb I think it is
correct to say "Guests expect to be able to use the returned address for
DMA".
> When the device is protected by IOMMU the request will fail. We have to
> add 1:1 mapping in the domain p2m to allow DMA request working.
>
> This is valid because DOM0 has its memory mapped 1:1 and therefore we know
> that RAM and devices cannot clash.
Is it worth mentioning now that in the future when a domain only has
access to protected I/O devices we would instead return
dev_bus_addr==IPA and intend to drop this extra 1:1 mapping?
> The grant mapping code already handle this case for x86 PV guests. Reuse the
> same code path for ARM guest.
In particular do you mean that iommu_{,un}map_page handles the reference
counting needed when an mfn is mapped via multiple grant mapping? I
think it must be the callers of those functions. Could you say that here
please?
> diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
> index 21b4572..9f85800 100644
> --- a/xen/drivers/passthrough/arm/smmu.c
> +++ b/xen/drivers/passthrough/arm/smmu.c
> @@ -1536,6 +1536,48 @@ static void arm_smmu_iommu_domain_teardown(struct domain *d)
> xfree(smmu_domain);
> }
>
> +static int arm_smmu_map_page(struct domain *d, unsigned long gfn,
> + unsigned long mfn, unsigned int flags)
> +{
> + p2m_type_t t;
> +
> + /* This function should only be used by gnttab code when the domain
> + * is direct mapped and gfn == mfn.
Is gfn !+ mfn an ASSERT-worthy condition?
Is gnttab the only possible user?
> + */
> + if ( !is_domain_direct_mapped(d) || gfn != mfn )
> + return -EINVAL;
> +
> + /* We only support readable and writable flags */
> + if ( !(flags & (IOMMUF_readable | IOMMUF_writable)) )
> + return -EINVAL;
> +
> + /* The function guest_physmap_add_entry replace the current mapping
"replaces"
> + * if there is already one...
... I feel like you intended to describe a consequence of that here. I
can't see the relationship between the comment and the selection of rw
vs ro mappings.
> + */
> + t = (flags & IOMMUF_writable)? p2m_iommu_map_rw : p2m_iommu_map_ro;
> +
> + /* Grant mapping can be used for DMA request. The dev_bus_addr returned by
> + * the hypercall is the MFN (not the IPA). For device protected by
"Grant mappings... DMA requests... For devices" (all plural)
> + * an IOMMU, Xen needs to add a 1:1 mapping in the domain p2m to
> + * allow DMA request working.
"to allow DMA requests to work"
> + * This is only valid when the domain is directed mapped
> + */
> + return guest_physmap_add_entry(d, gfn, mfn, 0, t);
> +}
> +
> +static int arm_smmu_unmap_page(struct domain *d, unsigned long gfn)
> +{
> + /* This function should only be used by gnttab code when the domain
> + * is direct mapped
> + */
> + if ( !is_domain_direct_mapped(d) )
> + return -EINVAL;
> +
> + guest_physmap_remove_page(d, gfn, gfn, 0);
I think 0 here is really PAGE_ORDER_4K, is it? (other callers of this
function seem to be inconsistent about this)
> diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h
> index bd71abe..b68d5b8 100644
> --- a/xen/include/asm-arm/p2m.h
> +++ b/xen/include/asm-arm/p2m.h
> @@ -45,6 +45,8 @@ typedef enum {
> p2m_map_foreign, /* Ram pages from foreign domain */
> p2m_grant_map_rw, /* Read/write grant mapping */
> p2m_grant_map_ro, /* Read-only grant mapping */
> + p2m_iommu_map_rw, /* Read/write iommu mapping */
> + p2m_iommu_map_ro, /* Read-only iommu mapping */
Why do we need new p2m types, rather than using e.g. the grant map type
(which I suppose is what the non-1:1 map uses)?
Could you explain the reason in the commit log too please.
Ian.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m
2014-05-21 13:27 ` Ian Campbell
@ 2014-05-21 13:42 ` Julien Grall
2014-05-21 13:50 ` Ian Campbell
2014-05-21 13:51 ` Ian Campbell
0 siblings, 2 replies; 17+ messages in thread
From: Julien Grall @ 2014-05-21 13:42 UTC (permalink / raw)
To: Ian Campbell; +Cc: xen-devel, tim, Jan Beulich, stefano.stabellini
On 05/21/2014 02:27 PM, Ian Campbell wrote:
> On Mon, 2014-05-19 at 17:24 +0100, Julien Grall wrote:
>> Grant mapping can be used for DMA request. The dev_bus_addr returned by the
>
> ^Currently (?)
>
>> hypercall is the MFN (not the IPA). Currently Linux is using this address (via
>> swiotlb) to program the DMA.
>
> Rather than talking specifically about Linux and swiotlb I think it is
> correct to say "Guests expect to be able to use the returned address for
> DMA".
>
>> When the device is protected by IOMMU the request will fail. We have to
>> add 1:1 mapping in the domain p2m to allow DMA request working.
>>
>> This is valid because DOM0 has its memory mapped 1:1 and therefore we know
>> that RAM and devices cannot clash.
>
> Is it worth mentioning now that in the future when a domain only has
> access to protected I/O devices we would instead return
> dev_bus_addr==IPA and intend to drop this extra 1:1 mapping?
I plan to modify dev_bus_addr with my non-PCI passthrough to return the IPA.
The code has been written to remove easily the 1:1 workaround (see macro
is_domain_direct_mapped.
I can make a mention about it in the commit message.
>> The grant mapping code already handle this case for x86 PV guests. Reuse the
>> same code path for ARM guest.
>
> In particular do you mean that iommu_{,un}map_page handles the reference
> counting needed when an mfn is mapped via multiple grant mapping? I
> think it must be the callers of those functions. Could you say that here
> please?
The reference counting is done in common/grant_table (see the wrc/rdc
logic before calling iommu_{,un}map_page).
>> diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
>> index 21b4572..9f85800 100644
>> --- a/xen/drivers/passthrough/arm/smmu.c
>> +++ b/xen/drivers/passthrough/arm/smmu.c
>> @@ -1536,6 +1536,48 @@ static void arm_smmu_iommu_domain_teardown(struct domain *d)
>> xfree(smmu_domain);
>> }
>>
>> +static int arm_smmu_map_page(struct domain *d, unsigned long gfn,
>> + unsigned long mfn, unsigned int flags)
>> +{
>> + p2m_type_t t;
>> +
>> + /* This function should only be used by gnttab code when the domain
>> + * is direct mapped and gfn == mfn.
>
> Is gfn !+ mfn an ASSERT-worthy condition?
The ASSERT would only be for debug build. I'd like to have a safe guard
for non-debug build just in case.
>
> Is gnttab the only possible user?
For ARM yes.
>> + */
>> + if ( !is_domain_direct_mapped(d) || gfn != mfn )
>> + return -EINVAL;
>> +
>> + /* We only support readable and writable flags */
>> + if ( !(flags & (IOMMUF_readable | IOMMUF_writable)) )
>> + return -EINVAL;
>> +
>> + /* The function guest_physmap_add_entry replace the current mapping
>
> "replaces"
>
>> + * if there is already one...
>
> ... I feel like you intended to describe a consequence of that here. I
> can't see the relationship between the comment and the selection of rw
> vs ro mappings.
This was intend to be just above guest_physmap_add_entry. I will move
the comment.
>> + */
>> + t = (flags & IOMMUF_writable)? p2m_iommu_map_rw : p2m_iommu_map_ro;
>> +
>> + /* Grant mapping can be used for DMA request. The dev_bus_addr returned by
>> + * the hypercall is the MFN (not the IPA). For device protected by
>
> "Grant mappings... DMA requests... For devices" (all plural)
>
>> + * an IOMMU, Xen needs to add a 1:1 mapping in the domain p2m to
>> + * allow DMA request working.
>
> "to allow DMA requests to work"
>
>> + * This is only valid when the domain is directed mapped
>> + */
>> + return guest_physmap_add_entry(d, gfn, mfn, 0, t);
>> +}
>> +
>> +static int arm_smmu_unmap_page(struct domain *d, unsigned long gfn)
>> +{
>> + /* This function should only be used by gnttab code when the domain
>> + * is direct mapped
>> + */
>> + if ( !is_domain_direct_mapped(d) )
>> + return -EINVAL;
>> +
>> + guest_physmap_remove_page(d, gfn, gfn, 0);
>
> I think 0 here is really PAGE_ORDER_4K, is it? (other callers of this
> function seem to be inconsistent about this)
Yes, assuming the guest page will always be 4K.
>
>> diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h
>> index bd71abe..b68d5b8 100644
>> --- a/xen/include/asm-arm/p2m.h
>> +++ b/xen/include/asm-arm/p2m.h
>> @@ -45,6 +45,8 @@ typedef enum {
>> p2m_map_foreign, /* Ram pages from foreign domain */
>> p2m_grant_map_rw, /* Read/write grant mapping */
>> p2m_grant_map_ro, /* Read-only grant mapping */
>> + p2m_iommu_map_rw, /* Read/write iommu mapping */
>> + p2m_iommu_map_ro, /* Read-only iommu mapping */
>
> Why do we need new p2m types, rather than using e.g. the grant map type
> (which I suppose is what the non-1:1 map uses)?
> Could you explain the reason in the commit log too please.
The iommu_map_page could be reuse more generically in long-term. Using
p2m_grant_map_{ro,rw} would be confusing here.
What about introducing "dummy type" such as p2m_notype_{ro,rw} which
could be use in such case?
Regards,
--
Julien Grall
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m
2014-05-21 13:42 ` Julien Grall
@ 2014-05-21 13:50 ` Ian Campbell
2014-05-21 14:01 ` Julien Grall
2014-05-21 13:51 ` Ian Campbell
1 sibling, 1 reply; 17+ messages in thread
From: Ian Campbell @ 2014-05-21 13:50 UTC (permalink / raw)
To: Julien Grall; +Cc: xen-devel, tim, Jan Beulich, stefano.stabellini
On Wed, 2014-05-21 at 14:42 +0100, Julien Grall wrote:
> On 05/21/2014 02:27 PM, Ian Campbell wrote:
> > On Mon, 2014-05-19 at 17:24 +0100, Julien Grall wrote:
> >> Grant mapping can be used for DMA request. The dev_bus_addr returned by the
> >
> > ^Currently (?)
> >
> >> hypercall is the MFN (not the IPA). Currently Linux is using this address (via
> >> swiotlb) to program the DMA.
> >
> > Rather than talking specifically about Linux and swiotlb I think it is
> > correct to say "Guests expect to be able to use the returned address for
> > DMA".
> >
> >> When the device is protected by IOMMU the request will fail. We have to
> >> add 1:1 mapping in the domain p2m to allow DMA request working.
> >>
> >> This is valid because DOM0 has its memory mapped 1:1 and therefore we know
> >> that RAM and devices cannot clash.
> >
> > Is it worth mentioning now that in the future when a domain only has
> > access to protected I/O devices we would instead return
> > dev_bus_addr==IPA and intend to drop this extra 1:1 mapping?
>
> I plan to modify dev_bus_addr with my non-PCI passthrough to return the IPA.
>
> The code has been written to remove easily the 1:1 workaround (see macro
> is_domain_direct_mapped.
>
> I can make a mention about it in the commit message.
Thanks.
> >> The grant mapping code already handle this case for x86 PV guests. Reuse the
> >> same code path for ARM guest.
> >
> > In particular do you mean that iommu_{,un}map_page handles the reference
> > counting needed when an mfn is mapped via multiple grant mapping? I
> > think it must be the callers of those functions. Could you say that here
> > please?
>
> The reference counting is done in common/grant_table (see the wrc/rdc
> logic before calling iommu_{,un}map_page).
OK. I take it you plan to insert this into the commit message too.
> >> diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
> >> index 21b4572..9f85800 100644
> >> --- a/xen/drivers/passthrough/arm/smmu.c
> >> +++ b/xen/drivers/passthrough/arm/smmu.c
> >> @@ -1536,6 +1536,48 @@ static void arm_smmu_iommu_domain_teardown(struct domain *d)
> >> xfree(smmu_domain);
> >> }
> >>
> >> +static int arm_smmu_map_page(struct domain *d, unsigned long gfn,
> >> + unsigned long mfn, unsigned int flags)
> >> +{
> >> + p2m_type_t t;
> >> +
> >> + /* This function should only be used by gnttab code when the domain
> >> + * is direct mapped and gfn == mfn.
> >
> > Is gfn !+ mfn an ASSERT-worthy condition?
>
> The ASSERT would only be for debug build. I'd like to have a safe guard
> for non-debug build just in case.
That's a BUG_ON then I think, assuming it would be a coding error in the
hypervisor (rather than e.g. a guest trying to exploit the issue
somehow).
> > Is gnttab the only possible user?
>
> For ARM yes.
OK
(out of curiosity what are the other users on x86?)
> >> + * This is only valid when the domain is directed mapped
> >> + */
> >> + return guest_physmap_add_entry(d, gfn, mfn, 0, t);
> >> +}
> >> +
> >> +static int arm_smmu_unmap_page(struct domain *d, unsigned long gfn)
> >> +{
> >> + /* This function should only be used by gnttab code when the domain
> >> + * is direct mapped
> >> + */
> >> + if ( !is_domain_direct_mapped(d) )
> >> + return -EINVAL;
> >> +
> >> + guest_physmap_remove_page(d, gfn, gfn, 0);
> >
> > I think 0 here is really PAGE_ORDER_4K, is it? (other callers of this
> > function seem to be inconsistent about this)
>
> Yes, assuming the guest page will always be 4K.
Even if not then PAGE_ORDER_4K will make good fodder for grep...
> >> diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h
> >> index bd71abe..b68d5b8 100644
> >> --- a/xen/include/asm-arm/p2m.h
> >> +++ b/xen/include/asm-arm/p2m.h
> >> @@ -45,6 +45,8 @@ typedef enum {
> >> p2m_map_foreign, /* Ram pages from foreign domain */
> >> p2m_grant_map_rw, /* Read/write grant mapping */
> >> p2m_grant_map_ro, /* Read-only grant mapping */
> >> + p2m_iommu_map_rw, /* Read/write iommu mapping */
> >> + p2m_iommu_map_ro, /* Read-only iommu mapping */
> >
> > Why do we need new p2m types, rather than using e.g. the grant map type
> > (which I suppose is what the non-1:1 map uses)?
> > Could you explain the reason in the commit log too please.
>
> The iommu_map_page could be reuse more generically in long-term. Using
> p2m_grant_map_{ro,rw} would be confusing here.
>
>
> What about introducing "dummy type" such as p2m_notype_{ro,rw} which
> could be use in such case?
notype is effectively "ram" I think, but that doesn't seem quite right
either.
I'm just worried that p2m type bits are in limited supply so I want to
be sure using new ones is justified.
Ian.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m
2014-05-21 13:42 ` Julien Grall
2014-05-21 13:50 ` Ian Campbell
@ 2014-05-21 13:51 ` Ian Campbell
2014-05-21 13:54 ` Julien Grall
1 sibling, 1 reply; 17+ messages in thread
From: Ian Campbell @ 2014-05-21 13:51 UTC (permalink / raw)
To: Julien Grall; +Cc: xen-devel, tim, Jan Beulich, stefano.stabellini
On Wed, 2014-05-21 at 14:42 +0100, Julien Grall wrote:
> > Is gnttab the only possible user?
>
> For ARM yes.
[...]
> The iommu_map_page could be reuse more generically in long-term. Using
> p2m_grant_map_{ro,rw} would be confusing here.
BTW, those statements contradict each other.
Ian.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m
2014-05-21 13:51 ` Ian Campbell
@ 2014-05-21 13:54 ` Julien Grall
2014-05-21 14:04 ` Ian Campbell
0 siblings, 1 reply; 17+ messages in thread
From: Julien Grall @ 2014-05-21 13:54 UTC (permalink / raw)
To: Ian Campbell; +Cc: xen-devel, tim, Jan Beulich, stefano.stabellini
On 05/21/2014 02:51 PM, Ian Campbell wrote:
> On Wed, 2014-05-21 at 14:42 +0100, Julien Grall wrote:
>
>>> Is gnttab the only possible user?
>>
>> For ARM yes.
> [...]
>> The iommu_map_page could be reuse more generically in long-term. Using
>> p2m_grant_map_{ro,rw} would be confusing here.
>
> BTW, those statements contradict each other.
Right. This function could also be used if we want to support per-IOMMU
p2m rather than sharing with the CPU.
--
Julien Grall
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m
2014-05-21 13:50 ` Ian Campbell
@ 2014-05-21 14:01 ` Julien Grall
2014-05-21 14:40 ` Ian Campbell
0 siblings, 1 reply; 17+ messages in thread
From: Julien Grall @ 2014-05-21 14:01 UTC (permalink / raw)
To: Ian Campbell; +Cc: xen-devel, tim, Jan Beulich, stefano.stabellini
On 05/21/2014 02:50 PM, Ian Campbell wrote:
>>>> diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
>>>> index 21b4572..9f85800 100644
>>>> --- a/xen/drivers/passthrough/arm/smmu.c
>>>> +++ b/xen/drivers/passthrough/arm/smmu.c
>>>> @@ -1536,6 +1536,48 @@ static void arm_smmu_iommu_domain_teardown(struct domain *d)
>>>> xfree(smmu_domain);
>>>> }
>>>>
>>>> +static int arm_smmu_map_page(struct domain *d, unsigned long gfn,
>>>> + unsigned long mfn, unsigned int flags)
>>>> +{
>>>> + p2m_type_t t;
>>>> +
>>>> + /* This function should only be used by gnttab code when the domain
>>>> + * is direct mapped and gfn == mfn.
>>>
>>> Is gfn !+ mfn an ASSERT-worthy condition?
>>
>> The ASSERT would only be for debug build. I'd like to have a safe guard
>> for non-debug build just in case.
>
> That's a BUG_ON then I think, assuming it would be a coding error in the
> hypervisor (rather than e.g. a guest trying to exploit the issue
> somehow).
The guest should not be able to exploit this issue. I will add a BUG_ON.
>>> Is gnttab the only possible user?
>>
>> For ARM yes.
>
> OK
>
> (out of curiosity what are the other users on x86?)
It's used for create the IOMMU PT.
>>>> + * This is only valid when the domain is directed mapped
>>>> + */
>>>> + return guest_physmap_add_entry(d, gfn, mfn, 0, t);
>>>> +}
>>>> +
>>>> +static int arm_smmu_unmap_page(struct domain *d, unsigned long gfn)
>>>> +{
>>>> + /* This function should only be used by gnttab code when the domain
>>>> + * is direct mapped
>>>> + */
>>>> + if ( !is_domain_direct_mapped(d) )
>>>> + return -EINVAL;
>>>> +
>>>> + guest_physmap_remove_page(d, gfn, gfn, 0);
>>>
>>> I think 0 here is really PAGE_ORDER_4K, is it? (other callers of this
>>> function seem to be inconsistent about this)
>>
>> Yes, assuming the guest page will always be 4K.
>
> Even if not then PAGE_ORDER_4K will make good fodder for grep...
I will use it in the next version.
>> What about introducing "dummy type" such as p2m_notype_{ro,rw} which
>> could be use in such case?
>
> notype is effectively "ram" I think, but that doesn't seem quite right
> either.
>
> I'm just worried that p2m type bits are in limited supply so I want to
> be sure using new ones is justified.
We don't really need to store those type in the P2M. We only need them
to choose the page attributes.
We could introduce a virtual type (i.e value > p2m_max_real_type) and
store p2m_invalid in the P2M.
Regards,
--
Julien Grall
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m
2014-05-21 13:54 ` Julien Grall
@ 2014-05-21 14:04 ` Ian Campbell
0 siblings, 0 replies; 17+ messages in thread
From: Ian Campbell @ 2014-05-21 14:04 UTC (permalink / raw)
To: Julien Grall; +Cc: xen-devel, tim, Jan Beulich, stefano.stabellini
On Wed, 2014-05-21 at 14:54 +0100, Julien Grall wrote:
> On 05/21/2014 02:51 PM, Ian Campbell wrote:
> > On Wed, 2014-05-21 at 14:42 +0100, Julien Grall wrote:
> >
> >>> Is gnttab the only possible user?
> >>
> >> For ARM yes.
> > [...]
> >> The iommu_map_page could be reuse more generically in long-term. Using
> >> p2m_grant_map_{ro,rw} would be confusing here.
> >
> > BTW, those statements contradict each other.
>
> Right. This function could also be used if we want to support per-IOMMU
> p2m rather than sharing with the CPU.
I suspected as much, thanks.
I don't know how likely that is to be a thing which happens.
(I'm not sure what the implications of that are for either the comment
or the p2m type name...)
Ian.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m
2014-05-21 14:01 ` Julien Grall
@ 2014-05-21 14:40 ` Ian Campbell
0 siblings, 0 replies; 17+ messages in thread
From: Ian Campbell @ 2014-05-21 14:40 UTC (permalink / raw)
To: Julien Grall; +Cc: xen-devel, tim, Jan Beulich, stefano.stabellini
On Wed, 2014-05-21 at 15:01 +0100, Julien Grall wrote:
> > I'm just worried that p2m type bits are in limited supply so I want to
> > be sure using new ones is justified.
>
> We don't really need to store those type in the P2M. We only need them
> to choose the page attributes.
>
> We could introduce a virtual type (i.e value > p2m_max_real_type) and
> store p2m_invalid in the P2M.
Will finding p2m_invalid under various circumstances not confuse
something somewhere?
How about a single real p2m_special and the virtual types you suggest?
Or, TBH, lets just use the two types you propose and do all this when we
run out... A comment to the affect that the types are only used for the
rw bit might serve as a hint to us in a couple of years that we can
easily get rid of them...
Ian.
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2014-05-21 14:40 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-05-19 16:23 [PATCH v8 0/4] IOMMU support for ARM Julien Grall
2014-05-19 16:23 ` [PATCH v8 1/4] xen/arm: p2m: Clean cache PT when the IOMMU doesn't support coherent walk Julien Grall
2014-05-19 16:23 ` [PATCH v8 2/4] xen: iommu: Define PAGE_{SHIFT, SIZE, ALIGN, MASK)_64K Julien Grall
2014-05-19 16:23 ` [PATCH v8 3/4] drivers/passthrough: arm: Add support for SMMU drivers Julien Grall
2014-05-19 16:24 ` [PATCH v8 4/4] xen/arm: grant: Add another entry to map MFN 1:1 in dom0 p2m Julien Grall
2014-05-20 7:46 ` Jan Beulich
2014-05-20 9:21 ` Ian Campbell
2014-05-20 10:48 ` Julien Grall
2014-05-21 13:27 ` Ian Campbell
2014-05-21 13:42 ` Julien Grall
2014-05-21 13:50 ` Ian Campbell
2014-05-21 14:01 ` Julien Grall
2014-05-21 14:40 ` Ian Campbell
2014-05-21 13:51 ` Ian Campbell
2014-05-21 13:54 ` Julien Grall
2014-05-21 14:04 ` Ian Campbell
2014-05-21 13:27 ` [PATCH v8 0/4] IOMMU support for ARM Ian Campbell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).