First version
[3rdparty/ote_partner/tlk.git] / arch / arm / arm / mmu_ldesc.c
1 /*
2  * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23
24 #include <debug.h>
25 #include <assert.h>
26 #include <sys/types.h>
27 #include <err.h>
28 #include <compiler.h>
29 #include <malloc.h>
30 #include <string.h>
31 #include <arch.h>
32 #include <arch/arm.h>
33 #include <arch/arm/mmu.h>
34 #include <arch/arm/mmu_ldesc.h>
35 #include <platform.h>
36
37 #if ARM_WITH_MMU
38
39 #define MB (1024*1024)
40
41 /* the location of the table may be brought in from outside */
42 #if WITH_EXTERNAL_TRANSLATION_TABLE
43 #if !defined(MMU_TRANSLATION_TABLE_ADDR)
44 #error must set MMU_TRANSLATION_TABLE_ADDR in the make configuration
45 #endif
46 uint32_t *tt = (void *)MMU_TRANSLATION_TABLE_ADDR;
47 #else
48 /* the main translation table */
49 uint64_t tt[2048] __ALIGNED(16384) __SECTION(".bss.prebss.translation_table");
50 /* first level page table base (with TTBCR.T1SZ = 0, only need 32 byte alignment) */
51 uint64_t tt_level1[4] __ALIGNED(32) __SECTION(".bss.prebss.translation_table");
52 #endif
53
54 typedef enum {
55         NONE = 0,
56         LEVEL_2_USER,
57         LEVEL_2_PRIV,
58         LEVEL_3,
59 } pgtbl_lvl_t;
60
61 vaddr_t arm_mmu_desc_get_max_user_space()
62 {
63         return (1 << (32 - MMU_MEMORY_TTBCR_T0SZ));
64 }
65
66 void arm_mmu_desc_set_default_kernel(arm_phys_attrs_t *attrs)
67 {
68         attrs->inner = MMU_MEMORY_WRITE_BACK_ALLOCATE;
69         attrs->outer = MMU_MEMORY_WRITE_BACK_NO_ALLOCATE;
70         attrs->is_lpae = true;
71 }
72
73 void arm_mmu_desc_set_default_task(arm_phys_attrs_t *attrs)
74 {
75         attrs->inner = MMU_MEMORY_WRITE_BACK_ALLOCATE;
76         attrs->outer = MMU_MEMORY_WRITE_BACK_NO_ALLOCATE;
77         attrs->is_lpae = true;
78 }
79
80 static uint64_t *arm_mmu_desc_alloc_pgtbl(pgtbl_lvl_t type)
81 {
82         uint32_t size;
83         uint64_t *pgtable = NULL;
84
85         switch (type) {
86         case LEVEL_2_USER:
87         case LEVEL_2_PRIV:
88                 size = MMU_MEMORY_TTBR_L2_SIZE;
89                 break;
90         case LEVEL_3:
91                 size = MMU_MEMORY_TTBR_L3_SIZE;
92                 break;
93         default:
94                 dprintf(CRITICAL, "unrecognized pgtbl_type %d\n", type);
95                 return pgtable;
96         }
97
98         pgtable = memalign(size, MMU_MEMORY_TABLE_ADDR_ALIGN);
99         if (pgtable)
100                 memset(pgtable, 0, size);
101
102         return pgtable;
103 }
104
105 static int32_t arm_mmu_desc_find_mair_index(uint32_t attr)
106 {
107         uint32_t index;
108         uint32_t free_index = 0;
109         uint32_t mem_attrs;
110
111         /* read MAIR0 */
112         __asm__ volatile("mrc p15, 0, %0, c10, c2, 0": "=r" (mem_attrs));
113
114         for (index = 0; index < 4; index++) {
115                 if ((attr & 0xFF) == (mem_attrs & 0xFF))
116                         return index;   /* match */
117                 /* don't replace SO index (first index and holds 0) */
118                 if (index && !(mem_attrs & 0xFF)) {
119                         free_index = index;
120                         break;
121                 }
122                 mem_attrs >>= 8;
123         }
124
125         if (free_index) {
126                 /* insert in MAIR0 */
127                 __asm__ volatile("mrc p15, 0, %0, c10, c2, 0" : "=r" (mem_attrs));
128                 mem_attrs |= ((attr & 0xFF) << (free_index * 8));
129                 __asm__ volatile("mcr p15, 0, %0, c10, c2, 0" :: "r" (mem_attrs));
130                 return free_index;
131         }
132
133         /* read MAIR1 (no match or free index in the first set) */
134         __asm__ volatile("mrc p15, 0, %0, c10, c2, 1": "=r" (mem_attrs));
135
136         for (index = 4; index < 8; index++) {
137                 if ((attr & 0xFF) == (mem_attrs & 0xFF))
138                         return index;   /* match */
139                 if (!(mem_attrs & 0xFF)) {
140                         free_index = index;
141                         break;
142                 }
143                 mem_attrs >>= 8;
144         }
145
146         if (free_index) {
147                 /* insert in MAIR1 */
148                 __asm__ volatile("mrc p15, 0, %0, c10, c2, 1" : "=r" (mem_attrs));
149                 mem_attrs |= ((attr & 0xFF) << ((free_index - 4) * 8));
150                 __asm__ volatile("mcr p15, 0, %0, c10, c2, 1" :: "r" (mem_attrs));
151                 return free_index;
152         }
153
154         dprintf(CRITICAL, "%s: unable to map attr (0x%02x) to MAIR0/1 index\n",
155                 __func__, attr);
156         halt();
157
158         return free_index;
159 }
160
161 static uint64_t arm_mmu_desc_l2_cache_attrs(arm_phys_attrs_t *attrs)
162 {
163         uint64_t mem_attrs = 0;
164         uint32_t attr_val;
165         uint32_t attr_indx;
166
167         if (!attrs->is_lpae) {
168                 dprintf(CRITICAL,
169                         "%s: mapping non-LPAE attribs to LPAE is unsupported\n",
170                         __func__);
171                 halt();
172         }
173
174         if (attrs->shareable)
175                 mem_attrs |= MMU_MEMORY_SH_INNER_SHAREABLE;
176
177         /* find mem attr index */
178         attr_val = (attrs->outer << 4) | (attrs->inner);
179         attr_indx = arm_mmu_desc_find_mair_index(attr_val);
180
181         mem_attrs |= MMU_MEMORY_SET_ATTR_IDX(attr_indx);
182         return mem_attrs;
183 }
184
185 void arm_mmu_desc_map_page(vaddr_t vaddr, paddr_t paddr, paddr_t *pgtbl,
186                            tmflags_t flags, arm_phys_attrs_t *attrs)
187 {
188         uint64_t *page_table;
189         uint64_t l3_flags = 0;
190         uint64_t *level_3;
191         uint32_t idx;
192         bool is_kmap;
193
194         is_kmap = (flags & TM_KERN_VA);
195         if (is_kmap) {
196                 /* always start with second level */
197                 page_table = tt;
198         } else {
199                 /* task mapping */
200                 if (*pgtbl == NULL) {
201                         page_table = arm_mmu_desc_alloc_pgtbl(LEVEL_2_USER);
202                         if (page_table == NULL) {
203                                 dprintf(CRITICAL,
204                                         "unable to allocate LEVEL_2 page table\n");
205                                 halt();
206                         }
207                         *pgtbl = virtual_to_physical((vaddr_t)page_table);
208                 } else {
209                         /* task page tables are saved as its physaddr */
210                         page_table = (uint64_t *)physical_to_virtual(*pgtbl);
211                 }
212         }
213
214         ASSERT(page_table);
215
216         idx = vaddr >> MMU_MEMORY_TTBR_L2_VADDR_SHIFT;
217
218         level_3 = (uint64_t *)(addr_t)(page_table[idx] & MMU_MEMORY_TABLE_ADDR_MASK);
219         if (!level_3) {
220                 /* alloc level 3 page table */
221                 level_3 = arm_mmu_desc_alloc_pgtbl(LEVEL_3);
222                 if (level_3 == NULL) {
223                         dprintf(CRITICAL, "unable to allocate LEVEL_2 page table\n");
224                         halt();
225                 }
226
227                 /* install in level_2 */
228                 page_table[idx]  = (uint64_t)(virtual_to_physical((vaddr_t)level_3));
229                 page_table[idx] |= 0x3;
230         } else {
231                 /* convert from a level 2 block to page table desc */
232                 if ((page_table[idx] & 0x3) == 0x1) {
233                         uint32_t i;
234                         ASSERT(is_kmap);
235
236                         level_3 = arm_mmu_desc_alloc_pgtbl(LEVEL_3);
237                         if (level_3 == NULL) {
238                                 dprintf(CRITICAL,
239                                         "unable to allocate LEVEL_3 page table\n");
240                                 halt();
241                         }
242
243                         /* initialize level 3 to map what level 2 had */
244                         for (i = 0; i < (1 << MMU_MEMORY_TTBR_L3_INDEX_BITS); i++)
245                                 level_3[i] = page_table[idx] | (i << MMU_L3_SIZE_SHIFT) | 0x3;
246
247                         /* install in level_2 */
248                         page_table[idx]  = (uint64_t)(virtual_to_physical((vaddr_t)level_3));
249                         page_table[idx] |= 0x3;
250                 } else {
251                         level_3 = (uint64_t *)physical_to_virtual((addr_t)level_3);
252                 }
253         }
254
255         idx = vaddr >> MMU_MEMORY_TTBR_L3_VADDR_SHIFT;
256         idx &= MMU_MEMORY_TTBR_L3_INDEX_MASK;
257
258         /* setup level 3 flags */
259         if (is_kmap) {
260                 l3_flags = MMU_MEMORY_AP_P_RW_U_NA;
261         } else {
262                 l3_flags = (flags & TM_UW) ?
263                         MMU_MEMORY_AP_P_RW_U_RW : MMU_MEMORY_AP_P_RO_U_RO;
264                 l3_flags |= MMU_MEMORY_NON_GLOBAL;
265         }
266         l3_flags |= (flags & (TM_NS_MEM | TM_NS_MEM_PRIV)) ? MMU_MEMORY_NON_SECURE : 0;
267         l3_flags |= MMU_MEMORY_ACCESS_FLAG;     /* avoid an access fault */
268
269         /* set cache attribs */
270         if (flags & TM_IO)
271                 l3_flags |= MMU_MEMORY_SET_ATTR_IDX(MMU_MEMORY_STRONGLY_ORDERED);
272         else
273                 l3_flags |= arm_mmu_desc_l2_cache_attrs(attrs);
274
275         /* install level_3 4K entry */
276         level_3[idx]  = paddr & ~PAGE_MASK;
277         level_3[idx] |= (l3_flags | 0x3);
278 }
279
280 void arm_mmu_desc_unmap_page(paddr_t pgtbl, vaddr_t vaddr, uint32_t asid)
281 {
282         uint64_t *page_table;
283         uint64_t *level_3;
284         u_int idx;
285
286         ASSERT(pgtbl);
287         page_table = (uint64_t *)physical_to_virtual(pgtbl);
288
289         idx = vaddr >> MMU_MEMORY_TTBR_L2_VADDR_SHIFT;
290
291         level_3 = (uint64_t *)(addr_t)(page_table[idx] & MMU_MEMORY_TABLE_ADDR_MASK);
292         ASSERT(level_3);
293         level_3 = (uint64_t *)physical_to_virtual((addr_t)level_3);
294
295         idx = vaddr >> MMU_MEMORY_TTBR_L3_VADDR_SHIFT;
296         idx &= MMU_MEMORY_TTBR_L3_INDEX_MASK;
297
298         level_3[idx] = 0;       /* invalid entry */
299         arm_invalidate_tlb_byaddr_asid(vaddr, asid);
300 }
301
302 #if !defined(ARM_USE_MMU_RELOC)
303 static void arm_mmu_desc_map_block(addr_t paddr, addr_t vaddr, uint flags)
304 {
305         int index;
306
307         /* Get the index into the translation table */
308         index = vaddr / (2 * MB);
309
310         /* Set the entry value:
311          * (1<<0): Block entry
312          *  flags: TEX, CB and AP bit settings provided by the caller.
313          */
314         tt[index] = (paddr & ~(2*MB-1)) | (1<<0) | flags;
315
316         arm_invalidate_tlb();
317 }
318
319 void arm_mmu_desc_config_mmu()
320 {
321         int i;
322         uint32_t reg;
323
324         /* set some mmu specific control bits:
325          * access flag disabled, TEX remap disabled, mmu disabled
326          */
327         arm_write_sctlr(arm_read_sctlr() & ~((1<<29)|(1<<28)|(1<<0)));
328
329         /* set up an identity-mapped translation table with
330          * strongly ordered memory type and read/write access.
331          */
332         for (i=0; i < 2048; i++) {
333                 arm_mmu_desc_map_block((i * (2 * MB)),
334                                          (i * (2 * MB)),
335                                          MMU_MEMORY_SET_ATTR_IDX(MMU_MEMORY_STRONGLY_ORDERED) |
336                                          MMU_MEMORY_AP_P_RW_U_NA);
337         }
338
339         platform_init_mmu_mappings();
340
341         /* configure N=7, (kernel uses TTBR1 / user uses TTBR0) */
342         arm_write_ttbcr(MMU_TTBCR_FLAGS);
343
344         /* set up the translation table base */
345         arm_write_ttbr1((uint32_t)tt_level1);
346
347         /* turn on the mmu (EAE and enable) */
348         reg = arm_read_sctlr();
349         reg |= ((1 << 31) | 1);
350         arm_write_sctlr(reg);
351 }
352 #endif // ARM_USE_MMU_RELOC
353
354 #endif // ARM_WITH_MMU