diff options
author | Will Deacon <will.deacon@arm.com> | 2014-11-14 12:18:23 -0500 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2015-01-19 09:46:44 -0500 |
commit | e1d3c0fd701df831169b116cd5c5d6203ac07f70 (patch) | |
tree | eb2537042682bf45a36148d06bb0bfb10ce3da74 | |
parent | fdb1d7be7c4d452e9735aeb2b60ae8a2fcf0a514 (diff) |
iommu: add ARM LPAE page table allocator
A number of IOMMUs found in ARM SoCs can walk architecture-compatible
page tables.
This patch adds a generic allocator for Stage-1 and Stage-2 v7/v8
long-descriptor page tables. 4k, 16k and 64k pages are supported, with
up to 4-levels of walk to cover a 48-bit address space.
Tested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | drivers/iommu/Kconfig | 9 | ||||
-rw-r--r-- | drivers/iommu/Makefile | 1 | ||||
-rw-r--r-- | drivers/iommu/io-pgtable-arm.c | 781 | ||||
-rw-r--r-- | drivers/iommu/io-pgtable.c | 11 | ||||
-rw-r--r-- | drivers/iommu/io-pgtable.h | 14 |
6 files changed, 817 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 3589d67437f8..00b27863384e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -1589,6 +1589,7 @@ M: Will Deacon <will.deacon@arm.com> | |||
1589 | L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) | 1589 | L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) |
1590 | S: Maintained | 1590 | S: Maintained |
1591 | F: drivers/iommu/arm-smmu.c | 1591 | F: drivers/iommu/arm-smmu.c |
1592 | F: drivers/iommu/io-pgtable-arm.c | ||
1592 | 1593 | ||
1593 | ARM64 PORT (AARCH64 ARCHITECTURE) | 1594 | ARM64 PORT (AARCH64 ARCHITECTURE) |
1594 | M: Catalin Marinas <catalin.marinas@arm.com> | 1595 | M: Catalin Marinas <catalin.marinas@arm.com> |
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 3faaa41db8ff..306454fbc52d 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig | |||
@@ -19,6 +19,15 @@ menu "Generic IOMMU Pagetable Support" | |||
19 | config IOMMU_IO_PGTABLE | 19 | config IOMMU_IO_PGTABLE |
20 | bool | 20 | bool |
21 | 21 | ||
22 | config IOMMU_IO_PGTABLE_LPAE | ||
23 | bool "ARMv7/v8 Long Descriptor Format" | ||
24 | select IOMMU_IO_PGTABLE | ||
25 | help | ||
26 | Enable support for the ARM long descriptor pagetable format. | ||
27 | This allocator supports 4K/2M/1G, 16K/32M and 64K/512M page | ||
28 | sizes at both stage-1 and stage-2, as well as address spaces | ||
29 | up to 48-bits in size. | ||
30 | |||
22 | endmenu | 31 | endmenu |
23 | 32 | ||
24 | config OF_IOMMU | 33 | config OF_IOMMU |
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 701c9516e2ae..d6889b487d55 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile | |||
@@ -2,6 +2,7 @@ obj-$(CONFIG_IOMMU_API) += iommu.o | |||
2 | obj-$(CONFIG_IOMMU_API) += iommu-traces.o | 2 | obj-$(CONFIG_IOMMU_API) += iommu-traces.o |
3 | obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o | 3 | obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o |
4 | obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o | 4 | obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o |
5 | obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o | ||
5 | obj-$(CONFIG_OF_IOMMU) += of_iommu.o | 6 | obj-$(CONFIG_OF_IOMMU) += of_iommu.o |
6 | obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o | 7 | obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o |
7 | obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o | 8 | obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o |
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c new file mode 100644 index 000000000000..dbe6178a53e9 --- /dev/null +++ b/drivers/iommu/io-pgtable-arm.c | |||
@@ -0,0 +1,781 @@ | |||
1 | /* | ||
2 | * CPU-agnostic ARM page table allocator. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | * | ||
16 | * Copyright (C) 2014 ARM Limited | ||
17 | * | ||
18 | * Author: Will Deacon <will.deacon@arm.com> | ||
19 | */ | ||
20 | |||
21 | #define pr_fmt(fmt) "arm-lpae io-pgtable: " fmt | ||
22 | |||
23 | #include <linux/iommu.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/sizes.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/types.h> | ||
28 | |||
29 | #include "io-pgtable.h" | ||
30 | |||
31 | #define ARM_LPAE_MAX_ADDR_BITS 48 | ||
32 | #define ARM_LPAE_S2_MAX_CONCAT_PAGES 16 | ||
33 | #define ARM_LPAE_MAX_LEVELS 4 | ||
34 | |||
35 | /* Struct accessors */ | ||
36 | #define io_pgtable_to_data(x) \ | ||
37 | container_of((x), struct arm_lpae_io_pgtable, iop) | ||
38 | |||
39 | #define io_pgtable_ops_to_pgtable(x) \ | ||
40 | container_of((x), struct io_pgtable, ops) | ||
41 | |||
42 | #define io_pgtable_ops_to_data(x) \ | ||
43 | io_pgtable_to_data(io_pgtable_ops_to_pgtable(x)) | ||
44 | |||
45 | /* | ||
46 | * For consistency with the architecture, we always consider | ||
47 | * ARM_LPAE_MAX_LEVELS levels, with the walk starting at level n >=0 | ||
48 | */ | ||
49 | #define ARM_LPAE_START_LVL(d) (ARM_LPAE_MAX_LEVELS - (d)->levels) | ||
50 | |||
51 | /* | ||
52 | * Calculate the right shift amount to get to the portion describing level l | ||
53 | * in a virtual address mapped by the pagetable in d. | ||
54 | */ | ||
55 | #define ARM_LPAE_LVL_SHIFT(l,d) \ | ||
56 | ((((d)->levels - ((l) - ARM_LPAE_START_LVL(d) + 1)) \ | ||
57 | * (d)->bits_per_level) + (d)->pg_shift) | ||
58 | |||
59 | #define ARM_LPAE_PAGES_PER_PGD(d) ((d)->pgd_size >> (d)->pg_shift) | ||
60 | |||
61 | /* | ||
62 | * Calculate the index at level l used to map virtual address a using the | ||
63 | * pagetable in d. | ||
64 | */ | ||
65 | #define ARM_LPAE_PGD_IDX(l,d) \ | ||
66 | ((l) == ARM_LPAE_START_LVL(d) ? ilog2(ARM_LPAE_PAGES_PER_PGD(d)) : 0) | ||
67 | |||
68 | #define ARM_LPAE_LVL_IDX(a,l,d) \ | ||
69 | (((a) >> ARM_LPAE_LVL_SHIFT(l,d)) & \ | ||
70 | ((1 << ((d)->bits_per_level + ARM_LPAE_PGD_IDX(l,d))) - 1)) | ||
71 | |||
72 | /* Calculate the block/page mapping size at level l for pagetable in d. */ | ||
73 | #define ARM_LPAE_BLOCK_SIZE(l,d) \ | ||
74 | (1 << (ilog2(sizeof(arm_lpae_iopte)) + \ | ||
75 | ((ARM_LPAE_MAX_LEVELS - (l)) * (d)->bits_per_level))) | ||
76 | |||
77 | /* Page table bits */ | ||
78 | #define ARM_LPAE_PTE_TYPE_SHIFT 0 | ||
79 | #define ARM_LPAE_PTE_TYPE_MASK 0x3 | ||
80 | |||
81 | #define ARM_LPAE_PTE_TYPE_BLOCK 1 | ||
82 | #define ARM_LPAE_PTE_TYPE_TABLE 3 | ||
83 | #define ARM_LPAE_PTE_TYPE_PAGE 3 | ||
84 | |||
85 | #define ARM_LPAE_PTE_XN (((arm_lpae_iopte)3) << 53) | ||
86 | #define ARM_LPAE_PTE_AF (((arm_lpae_iopte)1) << 10) | ||
87 | #define ARM_LPAE_PTE_SH_NS (((arm_lpae_iopte)0) << 8) | ||
88 | #define ARM_LPAE_PTE_SH_OS (((arm_lpae_iopte)2) << 8) | ||
89 | #define ARM_LPAE_PTE_SH_IS (((arm_lpae_iopte)3) << 8) | ||
90 | #define ARM_LPAE_PTE_VALID (((arm_lpae_iopte)1) << 0) | ||
91 | |||
92 | #define ARM_LPAE_PTE_ATTR_LO_MASK (((arm_lpae_iopte)0x3ff) << 2) | ||
93 | /* Ignore the contiguous bit for block splitting */ | ||
94 | #define ARM_LPAE_PTE_ATTR_HI_MASK (((arm_lpae_iopte)6) << 52) | ||
95 | #define ARM_LPAE_PTE_ATTR_MASK (ARM_LPAE_PTE_ATTR_LO_MASK | \ | ||
96 | ARM_LPAE_PTE_ATTR_HI_MASK) | ||
97 | |||
98 | /* Stage-1 PTE */ | ||
99 | #define ARM_LPAE_PTE_AP_UNPRIV (((arm_lpae_iopte)1) << 6) | ||
100 | #define ARM_LPAE_PTE_AP_RDONLY (((arm_lpae_iopte)2) << 6) | ||
101 | #define ARM_LPAE_PTE_ATTRINDX_SHIFT 2 | ||
102 | #define ARM_LPAE_PTE_nG (((arm_lpae_iopte)1) << 11) | ||
103 | |||
104 | /* Stage-2 PTE */ | ||
105 | #define ARM_LPAE_PTE_HAP_FAULT (((arm_lpae_iopte)0) << 6) | ||
106 | #define ARM_LPAE_PTE_HAP_READ (((arm_lpae_iopte)1) << 6) | ||
107 | #define ARM_LPAE_PTE_HAP_WRITE (((arm_lpae_iopte)2) << 6) | ||
108 | #define ARM_LPAE_PTE_MEMATTR_OIWB (((arm_lpae_iopte)0xf) << 2) | ||
109 | #define ARM_LPAE_PTE_MEMATTR_NC (((arm_lpae_iopte)0x5) << 2) | ||
110 | #define ARM_LPAE_PTE_MEMATTR_DEV (((arm_lpae_iopte)0x1) << 2) | ||
111 | |||
112 | /* Register bits */ | ||
113 | #define ARM_32_LPAE_TCR_EAE (1 << 31) | ||
114 | #define ARM_64_LPAE_S2_TCR_RES1 (1 << 31) | ||
115 | |||
116 | #define ARM_LPAE_TCR_TG0_4K (0 << 14) | ||
117 | #define ARM_LPAE_TCR_TG0_64K (1 << 14) | ||
118 | #define ARM_LPAE_TCR_TG0_16K (2 << 14) | ||
119 | |||
120 | #define ARM_LPAE_TCR_SH0_SHIFT 12 | ||
121 | #define ARM_LPAE_TCR_SH0_MASK 0x3 | ||
122 | #define ARM_LPAE_TCR_SH_NS 0 | ||
123 | #define ARM_LPAE_TCR_SH_OS 2 | ||
124 | #define ARM_LPAE_TCR_SH_IS 3 | ||
125 | |||
126 | #define ARM_LPAE_TCR_ORGN0_SHIFT 10 | ||
127 | #define ARM_LPAE_TCR_IRGN0_SHIFT 8 | ||
128 | #define ARM_LPAE_TCR_RGN_MASK 0x3 | ||
129 | #define ARM_LPAE_TCR_RGN_NC 0 | ||
130 | #define ARM_LPAE_TCR_RGN_WBWA 1 | ||
131 | #define ARM_LPAE_TCR_RGN_WT 2 | ||
132 | #define ARM_LPAE_TCR_RGN_WB 3 | ||
133 | |||
134 | #define ARM_LPAE_TCR_SL0_SHIFT 6 | ||
135 | #define ARM_LPAE_TCR_SL0_MASK 0x3 | ||
136 | |||
137 | #define ARM_LPAE_TCR_T0SZ_SHIFT 0 | ||
138 | #define ARM_LPAE_TCR_SZ_MASK 0xf | ||
139 | |||
140 | #define ARM_LPAE_TCR_PS_SHIFT 16 | ||
141 | #define ARM_LPAE_TCR_PS_MASK 0x7 | ||
142 | |||
143 | #define ARM_LPAE_TCR_IPS_SHIFT 32 | ||
144 | #define ARM_LPAE_TCR_IPS_MASK 0x7 | ||
145 | |||
146 | #define ARM_LPAE_TCR_PS_32_BIT 0x0ULL | ||
147 | #define ARM_LPAE_TCR_PS_36_BIT 0x1ULL | ||
148 | #define ARM_LPAE_TCR_PS_40_BIT 0x2ULL | ||
149 | #define ARM_LPAE_TCR_PS_42_BIT 0x3ULL | ||
150 | #define ARM_LPAE_TCR_PS_44_BIT 0x4ULL | ||
151 | #define ARM_LPAE_TCR_PS_48_BIT 0x5ULL | ||
152 | |||
153 | #define ARM_LPAE_MAIR_ATTR_SHIFT(n) ((n) << 3) | ||
154 | #define ARM_LPAE_MAIR_ATTR_MASK 0xff | ||
155 | #define ARM_LPAE_MAIR_ATTR_DEVICE 0x04 | ||
156 | #define ARM_LPAE_MAIR_ATTR_NC 0x44 | ||
157 | #define ARM_LPAE_MAIR_ATTR_WBRWA 0xff | ||
158 | #define ARM_LPAE_MAIR_ATTR_IDX_NC 0 | ||
159 | #define ARM_LPAE_MAIR_ATTR_IDX_CACHE 1 | ||
160 | #define ARM_LPAE_MAIR_ATTR_IDX_DEV 2 | ||
161 | |||
162 | /* IOPTE accessors */ | ||
163 | #define iopte_deref(pte,d) \ | ||
164 | (__va((pte) & ((1ULL << ARM_LPAE_MAX_ADDR_BITS) - 1) \ | ||
165 | & ~((1ULL << (d)->pg_shift) - 1))) | ||
166 | |||
167 | #define iopte_type(pte,l) \ | ||
168 | (((pte) >> ARM_LPAE_PTE_TYPE_SHIFT) & ARM_LPAE_PTE_TYPE_MASK) | ||
169 | |||
170 | #define iopte_prot(pte) ((pte) & ARM_LPAE_PTE_ATTR_MASK) | ||
171 | |||
172 | #define iopte_leaf(pte,l) \ | ||
173 | (l == (ARM_LPAE_MAX_LEVELS - 1) ? \ | ||
174 | (iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_PAGE) : \ | ||
175 | (iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_BLOCK)) | ||
176 | |||
177 | #define iopte_to_pfn(pte,d) \ | ||
178 | (((pte) & ((1ULL << ARM_LPAE_MAX_ADDR_BITS) - 1)) >> (d)->pg_shift) | ||
179 | |||
180 | #define pfn_to_iopte(pfn,d) \ | ||
181 | (((pfn) << (d)->pg_shift) & ((1ULL << ARM_LPAE_MAX_ADDR_BITS) - 1)) | ||
182 | |||
183 | struct arm_lpae_io_pgtable { | ||
184 | struct io_pgtable iop; | ||
185 | |||
186 | int levels; | ||
187 | size_t pgd_size; | ||
188 | unsigned long pg_shift; | ||
189 | unsigned long bits_per_level; | ||
190 | |||
191 | void *pgd; | ||
192 | }; | ||
193 | |||
194 | typedef u64 arm_lpae_iopte; | ||
195 | |||
196 | static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, | ||
197 | unsigned long iova, phys_addr_t paddr, | ||
198 | arm_lpae_iopte prot, int lvl, | ||
199 | arm_lpae_iopte *ptep) | ||
200 | { | ||
201 | arm_lpae_iopte pte = prot; | ||
202 | |||
203 | /* We require an unmap first */ | ||
204 | if (WARN_ON(iopte_leaf(*ptep, lvl))) | ||
205 | return -EEXIST; | ||
206 | |||
207 | if (lvl == ARM_LPAE_MAX_LEVELS - 1) | ||
208 | pte |= ARM_LPAE_PTE_TYPE_PAGE; | ||
209 | else | ||
210 | pte |= ARM_LPAE_PTE_TYPE_BLOCK; | ||
211 | |||
212 | pte |= ARM_LPAE_PTE_AF | ARM_LPAE_PTE_SH_IS; | ||
213 | pte |= pfn_to_iopte(paddr >> data->pg_shift, data); | ||
214 | |||
215 | *ptep = pte; | ||
216 | data->iop.cfg.tlb->flush_pgtable(ptep, sizeof(*ptep), data->iop.cookie); | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova, | ||
221 | phys_addr_t paddr, size_t size, arm_lpae_iopte prot, | ||
222 | int lvl, arm_lpae_iopte *ptep) | ||
223 | { | ||
224 | arm_lpae_iopte *cptep, pte; | ||
225 | void *cookie = data->iop.cookie; | ||
226 | size_t block_size = ARM_LPAE_BLOCK_SIZE(lvl, data); | ||
227 | |||
228 | /* Find our entry at the current level */ | ||
229 | ptep += ARM_LPAE_LVL_IDX(iova, lvl, data); | ||
230 | |||
231 | /* If we can install a leaf entry at this level, then do so */ | ||
232 | if (size == block_size && (size & data->iop.cfg.pgsize_bitmap)) | ||
233 | return arm_lpae_init_pte(data, iova, paddr, prot, lvl, ptep); | ||
234 | |||
235 | /* We can't allocate tables at the final level */ | ||
236 | if (WARN_ON(lvl >= ARM_LPAE_MAX_LEVELS - 1)) | ||
237 | return -EINVAL; | ||
238 | |||
239 | /* Grab a pointer to the next level */ | ||
240 | pte = *ptep; | ||
241 | if (!pte) { | ||
242 | cptep = alloc_pages_exact(1UL << data->pg_shift, | ||
243 | GFP_ATOMIC | __GFP_ZERO); | ||
244 | if (!cptep) | ||
245 | return -ENOMEM; | ||
246 | |||
247 | data->iop.cfg.tlb->flush_pgtable(cptep, 1UL << data->pg_shift, | ||
248 | cookie); | ||
249 | pte = __pa(cptep) | ARM_LPAE_PTE_TYPE_TABLE; | ||
250 | *ptep = pte; | ||
251 | data->iop.cfg.tlb->flush_pgtable(ptep, sizeof(*ptep), cookie); | ||
252 | } else { | ||
253 | cptep = iopte_deref(pte, data); | ||
254 | } | ||
255 | |||
256 | /* Rinse, repeat */ | ||
257 | return __arm_lpae_map(data, iova, paddr, size, prot, lvl + 1, cptep); | ||
258 | } | ||
259 | |||
260 | static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data, | ||
261 | int prot) | ||
262 | { | ||
263 | arm_lpae_iopte pte; | ||
264 | |||
265 | if (data->iop.fmt == ARM_64_LPAE_S1 || | ||
266 | data->iop.fmt == ARM_32_LPAE_S1) { | ||
267 | pte = ARM_LPAE_PTE_AP_UNPRIV | ARM_LPAE_PTE_nG; | ||
268 | |||
269 | if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ)) | ||
270 | pte |= ARM_LPAE_PTE_AP_RDONLY; | ||
271 | |||
272 | if (prot & IOMMU_CACHE) | ||
273 | pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE | ||
274 | << ARM_LPAE_PTE_ATTRINDX_SHIFT); | ||
275 | } else { | ||
276 | pte = ARM_LPAE_PTE_HAP_FAULT; | ||
277 | if (prot & IOMMU_READ) | ||
278 | pte |= ARM_LPAE_PTE_HAP_READ; | ||
279 | if (prot & IOMMU_WRITE) | ||
280 | pte |= ARM_LPAE_PTE_HAP_WRITE; | ||
281 | if (prot & IOMMU_CACHE) | ||
282 | pte |= ARM_LPAE_PTE_MEMATTR_OIWB; | ||
283 | else | ||
284 | pte |= ARM_LPAE_PTE_MEMATTR_NC; | ||
285 | } | ||
286 | |||
287 | if (prot & IOMMU_NOEXEC) | ||
288 | pte |= ARM_LPAE_PTE_XN; | ||
289 | |||
290 | return pte; | ||
291 | } | ||
292 | |||
293 | static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova, | ||
294 | phys_addr_t paddr, size_t size, int iommu_prot) | ||
295 | { | ||
296 | struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); | ||
297 | arm_lpae_iopte *ptep = data->pgd; | ||
298 | int lvl = ARM_LPAE_START_LVL(data); | ||
299 | arm_lpae_iopte prot; | ||
300 | |||
301 | /* If no access, then nothing to do */ | ||
302 | if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE))) | ||
303 | return 0; | ||
304 | |||
305 | prot = arm_lpae_prot_to_pte(data, iommu_prot); | ||
306 | return __arm_lpae_map(data, iova, paddr, size, prot, lvl, ptep); | ||
307 | } | ||
308 | |||
309 | static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl, | ||
310 | arm_lpae_iopte *ptep) | ||
311 | { | ||
312 | arm_lpae_iopte *start, *end; | ||
313 | unsigned long table_size; | ||
314 | |||
315 | /* Only leaf entries at the last level */ | ||
316 | if (lvl == ARM_LPAE_MAX_LEVELS - 1) | ||
317 | return; | ||
318 | |||
319 | if (lvl == ARM_LPAE_START_LVL(data)) | ||
320 | table_size = data->pgd_size; | ||
321 | else | ||
322 | table_size = 1UL << data->pg_shift; | ||
323 | |||
324 | start = ptep; | ||
325 | end = (void *)ptep + table_size; | ||
326 | |||
327 | while (ptep != end) { | ||
328 | arm_lpae_iopte pte = *ptep++; | ||
329 | |||
330 | if (!pte || iopte_leaf(pte, lvl)) | ||
331 | continue; | ||
332 | |||
333 | __arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data)); | ||
334 | } | ||
335 | |||
336 | free_pages_exact(start, table_size); | ||
337 | } | ||
338 | |||
339 | static void arm_lpae_free_pgtable(struct io_pgtable *iop) | ||
340 | { | ||
341 | struct arm_lpae_io_pgtable *data = io_pgtable_to_data(iop); | ||
342 | |||
343 | __arm_lpae_free_pgtable(data, ARM_LPAE_START_LVL(data), data->pgd); | ||
344 | kfree(data); | ||
345 | } | ||
346 | |||
347 | static int arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data, | ||
348 | unsigned long iova, size_t size, | ||
349 | arm_lpae_iopte prot, int lvl, | ||
350 | arm_lpae_iopte *ptep, size_t blk_size) | ||
351 | { | ||
352 | unsigned long blk_start, blk_end; | ||
353 | phys_addr_t blk_paddr; | ||
354 | arm_lpae_iopte table = 0; | ||
355 | void *cookie = data->iop.cookie; | ||
356 | const struct iommu_gather_ops *tlb = data->iop.cfg.tlb; | ||
357 | |||
358 | blk_start = iova & ~(blk_size - 1); | ||
359 | blk_end = blk_start + blk_size; | ||
360 | blk_paddr = iopte_to_pfn(*ptep, data) << data->pg_shift; | ||
361 | |||
362 | for (; blk_start < blk_end; blk_start += size, blk_paddr += size) { | ||
363 | arm_lpae_iopte *tablep; | ||
364 | |||
365 | /* Unmap! */ | ||
366 | if (blk_start == iova) | ||
367 | continue; | ||
368 | |||
369 | /* __arm_lpae_map expects a pointer to the start of the table */ | ||
370 | tablep = &table - ARM_LPAE_LVL_IDX(blk_start, lvl, data); | ||
371 | if (__arm_lpae_map(data, blk_start, blk_paddr, size, prot, lvl, | ||
372 | tablep) < 0) { | ||
373 | if (table) { | ||
374 | /* Free the table we allocated */ | ||
375 | tablep = iopte_deref(table, data); | ||
376 | __arm_lpae_free_pgtable(data, lvl + 1, tablep); | ||
377 | } | ||
378 | return 0; /* Bytes unmapped */ | ||
379 | } | ||
380 | } | ||
381 | |||
382 | *ptep = table; | ||
383 | tlb->flush_pgtable(ptep, sizeof(*ptep), cookie); | ||
384 | iova &= ~(blk_size - 1); | ||
385 | tlb->tlb_add_flush(iova, blk_size, true, cookie); | ||
386 | return size; | ||
387 | } | ||
388 | |||
389 | static int __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, | ||
390 | unsigned long iova, size_t size, int lvl, | ||
391 | arm_lpae_iopte *ptep) | ||
392 | { | ||
393 | arm_lpae_iopte pte; | ||
394 | const struct iommu_gather_ops *tlb = data->iop.cfg.tlb; | ||
395 | void *cookie = data->iop.cookie; | ||
396 | size_t blk_size = ARM_LPAE_BLOCK_SIZE(lvl, data); | ||
397 | |||
398 | ptep += ARM_LPAE_LVL_IDX(iova, lvl, data); | ||
399 | pte = *ptep; | ||
400 | |||
401 | /* Something went horribly wrong and we ran out of page table */ | ||
402 | if (WARN_ON(!pte || (lvl == ARM_LPAE_MAX_LEVELS))) | ||
403 | return 0; | ||
404 | |||
405 | /* If the size matches this level, we're in the right place */ | ||
406 | if (size == blk_size) { | ||
407 | *ptep = 0; | ||
408 | tlb->flush_pgtable(ptep, sizeof(*ptep), cookie); | ||
409 | |||
410 | if (!iopte_leaf(pte, lvl)) { | ||
411 | /* Also flush any partial walks */ | ||
412 | tlb->tlb_add_flush(iova, size, false, cookie); | ||
413 | tlb->tlb_sync(data->iop.cookie); | ||
414 | ptep = iopte_deref(pte, data); | ||
415 | __arm_lpae_free_pgtable(data, lvl + 1, ptep); | ||
416 | } else { | ||
417 | tlb->tlb_add_flush(iova, size, true, cookie); | ||
418 | } | ||
419 | |||
420 | return size; | ||
421 | } else if (iopte_leaf(pte, lvl)) { | ||
422 | /* | ||
423 | * Insert a table at the next level to map the old region, | ||
424 | * minus the part we want to unmap | ||
425 | */ | ||
426 | return arm_lpae_split_blk_unmap(data, iova, size, | ||
427 | iopte_prot(pte), lvl, ptep, | ||
428 | blk_size); | ||
429 | } | ||
430 | |||
431 | /* Keep on walkin' */ | ||
432 | ptep = iopte_deref(pte, data); | ||
433 | return __arm_lpae_unmap(data, iova, size, lvl + 1, ptep); | ||
434 | } | ||
435 | |||
436 | static int arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova, | ||
437 | size_t size) | ||
438 | { | ||
439 | size_t unmapped; | ||
440 | struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); | ||
441 | struct io_pgtable *iop = &data->iop; | ||
442 | arm_lpae_iopte *ptep = data->pgd; | ||
443 | int lvl = ARM_LPAE_START_LVL(data); | ||
444 | |||
445 | unmapped = __arm_lpae_unmap(data, iova, size, lvl, ptep); | ||
446 | if (unmapped) | ||
447 | iop->cfg.tlb->tlb_sync(iop->cookie); | ||
448 | |||
449 | return unmapped; | ||
450 | } | ||
451 | |||
452 | static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops, | ||
453 | unsigned long iova) | ||
454 | { | ||
455 | struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); | ||
456 | arm_lpae_iopte pte, *ptep = data->pgd; | ||
457 | int lvl = ARM_LPAE_START_LVL(data); | ||
458 | |||
459 | do { | ||
460 | /* Valid IOPTE pointer? */ | ||
461 | if (!ptep) | ||
462 | return 0; | ||
463 | |||
464 | /* Grab the IOPTE we're interested in */ | ||
465 | pte = *(ptep + ARM_LPAE_LVL_IDX(iova, lvl, data)); | ||
466 | |||
467 | /* Valid entry? */ | ||
468 | if (!pte) | ||
469 | return 0; | ||
470 | |||
471 | /* Leaf entry? */ | ||
472 | if (iopte_leaf(pte,lvl)) | ||
473 | goto found_translation; | ||
474 | |||
475 | /* Take it to the next level */ | ||
476 | ptep = iopte_deref(pte, data); | ||
477 | } while (++lvl < ARM_LPAE_MAX_LEVELS); | ||
478 | |||
479 | /* Ran out of page tables to walk */ | ||
480 | return 0; | ||
481 | |||
482 | found_translation: | ||
483 | iova &= ((1 << data->pg_shift) - 1); | ||
484 | return ((phys_addr_t)iopte_to_pfn(pte,data) << data->pg_shift) | iova; | ||
485 | } | ||
486 | |||
487 | static void arm_lpae_restrict_pgsizes(struct io_pgtable_cfg *cfg) | ||
488 | { | ||
489 | unsigned long granule; | ||
490 | |||
491 | /* | ||
492 | * We need to restrict the supported page sizes to match the | ||
493 | * translation regime for a particular granule. Aim to match | ||
494 | * the CPU page size if possible, otherwise prefer smaller sizes. | ||
495 | * While we're at it, restrict the block sizes to match the | ||
496 | * chosen granule. | ||
497 | */ | ||
498 | if (cfg->pgsize_bitmap & PAGE_SIZE) | ||
499 | granule = PAGE_SIZE; | ||
500 | else if (cfg->pgsize_bitmap & ~PAGE_MASK) | ||
501 | granule = 1UL << __fls(cfg->pgsize_bitmap & ~PAGE_MASK); | ||
502 | else if (cfg->pgsize_bitmap & PAGE_MASK) | ||
503 | granule = 1UL << __ffs(cfg->pgsize_bitmap & PAGE_MASK); | ||
504 | else | ||
505 | granule = 0; | ||
506 | |||
507 | switch (granule) { | ||
508 | case SZ_4K: | ||
509 | cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G); | ||
510 | break; | ||
511 | case SZ_16K: | ||
512 | cfg->pgsize_bitmap &= (SZ_16K | SZ_32M); | ||
513 | break; | ||
514 | case SZ_64K: | ||
515 | cfg->pgsize_bitmap &= (SZ_64K | SZ_512M); | ||
516 | break; | ||
517 | default: | ||
518 | cfg->pgsize_bitmap = 0; | ||
519 | } | ||
520 | } | ||
521 | |||
522 | static struct arm_lpae_io_pgtable * | ||
523 | arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg) | ||
524 | { | ||
525 | unsigned long va_bits, pgd_bits; | ||
526 | struct arm_lpae_io_pgtable *data; | ||
527 | |||
528 | arm_lpae_restrict_pgsizes(cfg); | ||
529 | |||
530 | if (!(cfg->pgsize_bitmap & (SZ_4K | SZ_16K | SZ_64K))) | ||
531 | return NULL; | ||
532 | |||
533 | if (cfg->ias > ARM_LPAE_MAX_ADDR_BITS) | ||
534 | return NULL; | ||
535 | |||
536 | if (cfg->oas > ARM_LPAE_MAX_ADDR_BITS) | ||
537 | return NULL; | ||
538 | |||
539 | data = kmalloc(sizeof(*data), GFP_KERNEL); | ||
540 | if (!data) | ||
541 | return NULL; | ||
542 | |||
543 | data->pg_shift = __ffs(cfg->pgsize_bitmap); | ||
544 | data->bits_per_level = data->pg_shift - ilog2(sizeof(arm_lpae_iopte)); | ||
545 | |||
546 | va_bits = cfg->ias - data->pg_shift; | ||
547 | data->levels = DIV_ROUND_UP(va_bits, data->bits_per_level); | ||
548 | |||
549 | /* Calculate the actual size of our pgd (without concatenation) */ | ||
550 | pgd_bits = va_bits - (data->bits_per_level * (data->levels - 1)); | ||
551 | data->pgd_size = 1UL << (pgd_bits + ilog2(sizeof(arm_lpae_iopte))); | ||
552 | |||
553 | data->iop.ops = (struct io_pgtable_ops) { | ||
554 | .map = arm_lpae_map, | ||
555 | .unmap = arm_lpae_unmap, | ||
556 | .iova_to_phys = arm_lpae_iova_to_phys, | ||
557 | }; | ||
558 | |||
559 | return data; | ||
560 | } | ||
561 | |||
562 | static struct io_pgtable * | ||
563 | arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) | ||
564 | { | ||
565 | u64 reg; | ||
566 | struct arm_lpae_io_pgtable *data = arm_lpae_alloc_pgtable(cfg); | ||
567 | |||
568 | if (!data) | ||
569 | return NULL; | ||
570 | |||
571 | /* TCR */ | ||
572 | reg = (ARM_LPAE_TCR_SH_IS << ARM_LPAE_TCR_SH0_SHIFT) | | ||
573 | (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_IRGN0_SHIFT) | | ||
574 | (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT); | ||
575 | |||
576 | switch (1 << data->pg_shift) { | ||
577 | case SZ_4K: | ||
578 | reg |= ARM_LPAE_TCR_TG0_4K; | ||
579 | break; | ||
580 | case SZ_16K: | ||
581 | reg |= ARM_LPAE_TCR_TG0_16K; | ||
582 | break; | ||
583 | case SZ_64K: | ||
584 | reg |= ARM_LPAE_TCR_TG0_64K; | ||
585 | break; | ||
586 | } | ||
587 | |||
588 | switch (cfg->oas) { | ||
589 | case 32: | ||
590 | reg |= (ARM_LPAE_TCR_PS_32_BIT << ARM_LPAE_TCR_IPS_SHIFT); | ||
591 | break; | ||
592 | case 36: | ||
593 | reg |= (ARM_LPAE_TCR_PS_36_BIT << ARM_LPAE_TCR_IPS_SHIFT); | ||
594 | break; | ||
595 | case 40: | ||
596 | reg |= (ARM_LPAE_TCR_PS_40_BIT << ARM_LPAE_TCR_IPS_SHIFT); | ||
597 | break; | ||
598 | case 42: | ||
599 | reg |= (ARM_LPAE_TCR_PS_42_BIT << ARM_LPAE_TCR_IPS_SHIFT); | ||
600 | break; | ||
601 | case 44: | ||
602 | reg |= (ARM_LPAE_TCR_PS_44_BIT << ARM_LPAE_TCR_IPS_SHIFT); | ||
603 | break; | ||
604 | case 48: | ||
605 | reg |= (ARM_LPAE_TCR_PS_48_BIT << ARM_LPAE_TCR_IPS_SHIFT); | ||
606 | break; | ||
607 | default: | ||
608 | goto out_free_data; | ||
609 | } | ||
610 | |||
611 | reg |= (64ULL - cfg->ias) << ARM_LPAE_TCR_T0SZ_SHIFT; | ||
612 | cfg->arm_lpae_s1_cfg.tcr = reg; | ||
613 | |||
614 | /* MAIRs */ | ||
615 | reg = (ARM_LPAE_MAIR_ATTR_NC | ||
616 | << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_NC)) | | ||
617 | (ARM_LPAE_MAIR_ATTR_WBRWA | ||
618 | << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_CACHE)) | | ||
619 | (ARM_LPAE_MAIR_ATTR_DEVICE | ||
620 | << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_DEV)); | ||
621 | |||
622 | cfg->arm_lpae_s1_cfg.mair[0] = reg; | ||
623 | cfg->arm_lpae_s1_cfg.mair[1] = 0; | ||
624 | |||
625 | /* Looking good; allocate a pgd */ | ||
626 | data->pgd = alloc_pages_exact(data->pgd_size, GFP_KERNEL | __GFP_ZERO); | ||
627 | if (!data->pgd) | ||
628 | goto out_free_data; | ||
629 | |||
630 | cfg->tlb->flush_pgtable(data->pgd, data->pgd_size, cookie); | ||
631 | |||
632 | /* TTBRs */ | ||
633 | cfg->arm_lpae_s1_cfg.ttbr[0] = virt_to_phys(data->pgd); | ||
634 | cfg->arm_lpae_s1_cfg.ttbr[1] = 0; | ||
635 | return &data->iop; | ||
636 | |||
637 | out_free_data: | ||
638 | kfree(data); | ||
639 | return NULL; | ||
640 | } | ||
641 | |||
642 | static struct io_pgtable * | ||
643 | arm_64_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie) | ||
644 | { | ||
645 | u64 reg, sl; | ||
646 | struct arm_lpae_io_pgtable *data = arm_lpae_alloc_pgtable(cfg); | ||
647 | |||
648 | if (!data) | ||
649 | return NULL; | ||
650 | |||
651 | /* | ||
652 | * Concatenate PGDs at level 1 if possible in order to reduce | ||
653 | * the depth of the stage-2 walk. | ||
654 | */ | ||
655 | if (data->levels == ARM_LPAE_MAX_LEVELS) { | ||
656 | unsigned long pgd_pages; | ||
657 | |||
658 | pgd_pages = data->pgd_size >> ilog2(sizeof(arm_lpae_iopte)); | ||
659 | if (pgd_pages <= ARM_LPAE_S2_MAX_CONCAT_PAGES) { | ||
660 | data->pgd_size = pgd_pages << data->pg_shift; | ||
661 | data->levels--; | ||
662 | } | ||
663 | } | ||
664 | |||
665 | /* VTCR */ | ||
666 | reg = ARM_64_LPAE_S2_TCR_RES1 | | ||
667 | (ARM_LPAE_TCR_SH_IS << ARM_LPAE_TCR_SH0_SHIFT) | | ||
668 | (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_IRGN0_SHIFT) | | ||
669 | (ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT); | ||
670 | |||
671 | sl = ARM_LPAE_START_LVL(data); | ||
672 | |||
673 | switch (1 << data->pg_shift) { | ||
674 | case SZ_4K: | ||
675 | reg |= ARM_LPAE_TCR_TG0_4K; | ||
676 | sl++; /* SL0 format is different for 4K granule size */ | ||
677 | break; | ||
678 | case SZ_16K: | ||
679 | reg |= ARM_LPAE_TCR_TG0_16K; | ||
680 | break; | ||
681 | case SZ_64K: | ||
682 | reg |= ARM_LPAE_TCR_TG0_64K; | ||
683 | break; | ||
684 | } | ||
685 | |||
686 | switch (cfg->oas) { | ||
687 | case 32: | ||
688 | reg |= (ARM_LPAE_TCR_PS_32_BIT << ARM_LPAE_TCR_PS_SHIFT); | ||
689 | break; | ||
690 | case 36: | ||
691 | reg |= (ARM_LPAE_TCR_PS_36_BIT << ARM_LPAE_TCR_PS_SHIFT); | ||
692 | break; | ||
693 | case 40: | ||
694 | reg |= (ARM_LPAE_TCR_PS_40_BIT << ARM_LPAE_TCR_PS_SHIFT); | ||
695 | break; | ||
696 | case 42: | ||
697 | reg |= (ARM_LPAE_TCR_PS_42_BIT << ARM_LPAE_TCR_PS_SHIFT); | ||
698 | break; | ||
699 | case 44: | ||
700 | reg |= (ARM_LPAE_TCR_PS_44_BIT << ARM_LPAE_TCR_PS_SHIFT); | ||
701 | break; | ||
702 | case 48: | ||
703 | reg |= (ARM_LPAE_TCR_PS_48_BIT << ARM_LPAE_TCR_PS_SHIFT); | ||
704 | break; | ||
705 | default: | ||
706 | goto out_free_data; | ||
707 | } | ||
708 | |||
709 | reg |= (64ULL - cfg->ias) << ARM_LPAE_TCR_T0SZ_SHIFT; | ||
710 | reg |= (~sl & ARM_LPAE_TCR_SL0_MASK) << ARM_LPAE_TCR_SL0_SHIFT; | ||
711 | cfg->arm_lpae_s2_cfg.vtcr = reg; | ||
712 | |||
713 | /* Allocate pgd pages */ | ||
714 | data->pgd = alloc_pages_exact(data->pgd_size, GFP_KERNEL | __GFP_ZERO); | ||
715 | if (!data->pgd) | ||
716 | goto out_free_data; | ||
717 | |||
718 | cfg->tlb->flush_pgtable(data->pgd, data->pgd_size, cookie); | ||
719 | |||
720 | /* VTTBR */ | ||
721 | cfg->arm_lpae_s2_cfg.vttbr = virt_to_phys(data->pgd); | ||
722 | return &data->iop; | ||
723 | |||
724 | out_free_data: | ||
725 | kfree(data); | ||
726 | return NULL; | ||
727 | } | ||
728 | |||
729 | static struct io_pgtable * | ||
730 | arm_32_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) | ||
731 | { | ||
732 | struct io_pgtable *iop; | ||
733 | |||
734 | if (cfg->ias > 32 || cfg->oas > 40) | ||
735 | return NULL; | ||
736 | |||
737 | cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G); | ||
738 | iop = arm_64_lpae_alloc_pgtable_s1(cfg, cookie); | ||
739 | if (iop) { | ||
740 | cfg->arm_lpae_s1_cfg.tcr |= ARM_32_LPAE_TCR_EAE; | ||
741 | cfg->arm_lpae_s1_cfg.tcr &= 0xffffffff; | ||
742 | } | ||
743 | |||
744 | return iop; | ||
745 | } | ||
746 | |||
747 | static struct io_pgtable * | ||
748 | arm_32_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie) | ||
749 | { | ||
750 | struct io_pgtable *iop; | ||
751 | |||
752 | if (cfg->ias > 40 || cfg->oas > 40) | ||
753 | return NULL; | ||
754 | |||
755 | cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G); | ||
756 | iop = arm_64_lpae_alloc_pgtable_s2(cfg, cookie); | ||
757 | if (iop) | ||
758 | cfg->arm_lpae_s2_cfg.vtcr &= 0xffffffff; | ||
759 | |||
760 | return iop; | ||
761 | } | ||
762 | |||
763 | struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = { | ||
764 | .alloc = arm_64_lpae_alloc_pgtable_s1, | ||
765 | .free = arm_lpae_free_pgtable, | ||
766 | }; | ||
767 | |||
768 | struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns = { | ||
769 | .alloc = arm_64_lpae_alloc_pgtable_s2, | ||
770 | .free = arm_lpae_free_pgtable, | ||
771 | }; | ||
772 | |||
773 | struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s1_init_fns = { | ||
774 | .alloc = arm_32_lpae_alloc_pgtable_s1, | ||
775 | .free = arm_lpae_free_pgtable, | ||
776 | }; | ||
777 | |||
778 | struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns = { | ||
779 | .alloc = arm_32_lpae_alloc_pgtable_s2, | ||
780 | .free = arm_lpae_free_pgtable, | ||
781 | }; | ||
diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c index f664a1ca49cf..6436fe24bc2f 100644 --- a/drivers/iommu/io-pgtable.c +++ b/drivers/iommu/io-pgtable.c | |||
@@ -24,9 +24,20 @@ | |||
24 | 24 | ||
25 | #include "io-pgtable.h" | 25 | #include "io-pgtable.h" |
26 | 26 | ||
27 | extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s1_init_fns; | ||
28 | extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns; | ||
29 | extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns; | ||
30 | extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns; | ||
31 | |||
27 | static const struct io_pgtable_init_fns * | 32 | static const struct io_pgtable_init_fns * |
28 | io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = | 33 | io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = |
29 | { | 34 | { |
35 | #ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE | ||
36 | [ARM_32_LPAE_S1] = &io_pgtable_arm_32_lpae_s1_init_fns, | ||
37 | [ARM_32_LPAE_S2] = &io_pgtable_arm_32_lpae_s2_init_fns, | ||
38 | [ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns, | ||
39 | [ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns, | ||
40 | #endif | ||
30 | }; | 41 | }; |
31 | 42 | ||
32 | struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt, | 43 | struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt, |
diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h index fdd792c37698..05c4e593a25f 100644 --- a/drivers/iommu/io-pgtable.h +++ b/drivers/iommu/io-pgtable.h | |||
@@ -5,6 +5,10 @@ | |||
5 | * Public API for use by IOMMU drivers | 5 | * Public API for use by IOMMU drivers |
6 | */ | 6 | */ |
7 | enum io_pgtable_fmt { | 7 | enum io_pgtable_fmt { |
8 | ARM_32_LPAE_S1, | ||
9 | ARM_32_LPAE_S2, | ||
10 | ARM_64_LPAE_S1, | ||
11 | ARM_64_LPAE_S2, | ||
8 | IO_PGTABLE_NUM_FMTS, | 12 | IO_PGTABLE_NUM_FMTS, |
9 | }; | 13 | }; |
10 | 14 | ||
@@ -47,6 +51,16 @@ struct io_pgtable_cfg { | |||
47 | 51 | ||
48 | /* Low-level data specific to the table format */ | 52 | /* Low-level data specific to the table format */ |
49 | union { | 53 | union { |
54 | struct { | ||
55 | u64 ttbr[2]; | ||
56 | u64 tcr; | ||
57 | u64 mair[2]; | ||
58 | } arm_lpae_s1_cfg; | ||
59 | |||
60 | struct { | ||
61 | u64 vttbr; | ||
62 | u64 vtcr; | ||
63 | } arm_lpae_s2_cfg; | ||
50 | }; | 64 | }; |
51 | }; | 65 | }; |
52 | 66 | ||