blob: b1c1aac0c039248652bac4b0e8145877b8a2e0f7 [file] [log] [blame]
David Hendricksf9be7562013-03-21 21:58:50 -07001/*
2 * This file is part of the coreboot project.
3 *
Deepa Dinamanie1977482015-01-28 14:15:56 -08004 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
David Hendricksf9be7562013-03-21 21:58:50 -07005 * Copyright 2013 Google Inc.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
Daisuke Nojirif574a322014-02-27 14:56:39 -080031#include <assert.h>
Gabe Blackee4bfbf2013-08-13 21:05:43 -070032#include <config.h>
David Hendricksf9be7562013-03-21 21:58:50 -070033#include <stdlib.h>
David Hendricksfa244a62013-03-28 18:07:30 -070034#include <stdint.h>
Julius Wernerec5e5e02014-08-20 15:29:56 -070035#include <symbols.h>
David Hendricksf9be7562013-03-21 21:58:50 -070036
37#include <cbmem.h>
38#include <console/console.h>
39
40#include <arch/cache.h>
Gabe Black800790d2013-05-18 22:45:54 -070041#include <arch/io.h>
David Hendricksf9be7562013-03-21 21:58:50 -070042
Daisuke Nojirif574a322014-02-27 14:56:39 -080043#if CONFIG_ARM_LPAE
44/* See B3.6.2 of ARMv7 Architecture Reference Manual */
45/* TODO: Utilize the contiguous hint flag */
Julius Werner108548a2014-10-09 17:31:45 -070046#define ATTR_BLOCK (\
Jimmy Zhangc1f7cbe2014-06-06 17:00:10 -070047 0ULL << 54 | /* XN. 0:Not restricted */ \
Daisuke Nojirif574a322014-02-27 14:56:39 -080048 0ULL << 53 | /* PXN. 0:Not restricted */ \
49 1 << 10 | /* AF. 1:Accessed. This is to prevent access \
50 * fault when accessed for the first time */ \
51 0 << 6 | /* AP[2:1]. 0b00:full access from PL1 */ \
52 0 << 5 | /* NS. 0:Output address is in Secure space */ \
53 0 << 1 | /* block/table. 0:block entry */ \
54 1 << 0 /* validity. 1:valid */ \
55 )
Julius Werner108548a2014-10-09 17:31:45 -070056#define ATTR_PAGE (ATTR_BLOCK | 1 << 1)
57#define ATTR_NEXTLEVEL (0x3)
58#define ATTR_NC ((MAIR_INDX_NC << 2) | (1ULL << 53) | (1ULL << 54))
59#define ATTR_WT (MAIR_INDX_WT << 2)
60#define ATTR_WB (MAIR_INDX_WB << 2)
David Hendricksf9be7562013-03-21 21:58:50 -070061
Julius Werner108548a2014-10-09 17:31:45 -070062#define PAGE_MASK 0x000ffffffffff000ULL
63#define BLOCK_MASK 0x000fffffffe00000ULL
64#define NEXTLEVEL_MASK PAGE_MASK
Daisuke Nojirif574a322014-02-27 14:56:39 -080065#define BLOCK_SHIFT 21
Gabe Black800790d2013-05-18 22:45:54 -070066
Julius Werner108548a2014-10-09 17:31:45 -070067typedef uint64_t pte_t;
Daisuke Nojirif574a322014-02-27 14:56:39 -080068#else /* CONFIG_ARM_LPAE */
David Hendricksf9be7562013-03-21 21:58:50 -070069 /*
70 * Section entry bits:
71 * 31:20 - section base address
72 * 18 - 0 to indicate normal section (versus supersection)
73 * 17 - nG, 0 to indicate page is global
74 * 16 - S, 0 for non-shareable (?)
75 * 15 - APX, 0 for full access
76 * 14:12 - TEX, 0b000 for outer and inner write-back
77 * 11:10 - AP, 0b11 for full access
78 * 9 - P, ? (FIXME: not described or possibly obsolete?)
79 * 8: 5 - Domain
80 * 4 - XN, 1 to set execute-never (and also avoid prefetches)
81 * 3 - C, 1 for cacheable
82 * 2 - B, 1 for bufferable
83 * 1: 0 - 0b10 to indicate section entry
84 */
Julius Werner108548a2014-10-09 17:31:45 -070085#define ATTR_BLOCK ((3 << 10) | 0x2)
86#define ATTR_PAGE ((3 << 4) | 0x2)
87#define ATTR_NEXTLEVEL (0x1)
88#define ATTR_NC (1 << 4)
89#define ATTR_WT (1 << 3)
90#define ATTR_WB ((1 << 3) | (1 << 2))
Daisuke Nojirif574a322014-02-27 14:56:39 -080091
Julius Werner108548a2014-10-09 17:31:45 -070092#define PAGE_MASK 0xfffff000UL
93#define BLOCK_MASK 0xfff00000UL
94#define NEXTLEVEL_MASK 0xfffffc00UL
Daisuke Nojirif574a322014-02-27 14:56:39 -080095#define BLOCK_SHIFT 20
96
Julius Werner108548a2014-10-09 17:31:45 -070097typedef uint32_t pte_t;
Daisuke Nojirif574a322014-02-27 14:56:39 -080098#endif /* CONFIG_ARM_LPAE */
99
Julius Werner03a0a652015-09-29 17:28:15 -0700100/* We set the first PTE to a sentinel value that cannot occur naturally (has
101 * attributes set but bits [1:0] are 0 -> unmapped) to mark unused subtables. */
102#define ATTR_UNUSED 0xBADbA6E0
103#define SUBTABLE_PTES (1 << (BLOCK_SHIFT - PAGE_SHIFT))
104
Daisuke Nojirif574a322014-02-27 14:56:39 -0800105/*
106 * mask/shift/size for pages and blocks
107 */
108#define PAGE_SHIFT 12
109#define PAGE_SIZE (1UL << PAGE_SHIFT)
Daisuke Nojirif574a322014-02-27 14:56:39 -0800110#define BLOCK_SIZE (1UL << BLOCK_SHIFT)
111
112/*
113 * MAIR Index
114 */
115#define MAIR_INDX_NC 0
116#define MAIR_INDX_WT 1
117#define MAIR_INDX_WB 2
118
Julius Werner108548a2014-10-09 17:31:45 -0700119static pte_t *const ttb_buff = (void *)_ttb;
Julius Werner108548a2014-10-09 17:31:45 -0700120
Julius Werner8c093772016-02-09 16:09:15 -0800121/* Not all boards want to use subtables and declare them in memlayout.ld. */
122DECLARE_OPTIONAL_REGION(ttb_subtables);
Julius Werner108548a2014-10-09 17:31:45 -0700123
124static struct {
125 pte_t value;
126 const char *name;
127} attrs[] = {
128 [DCACHE_OFF] = {.value = ATTR_NC, .name = "uncached"},
129 [DCACHE_WRITEBACK] = {.value = ATTR_WB, .name = "writeback"},
130 [DCACHE_WRITETHROUGH] = {.value = ATTR_WT, .name = "writethrough"},
131};
132
133/* Fills page table entries in |table| from |start_idx| to |end_idx| with |attr|
134 * and performs necessary invalidations. |offset| is the start address of the
135 * area described by |table|, and |shift| is the size-shift of each frame. */
136static void mmu_fill_table(pte_t *table, u32 start_idx, u32 end_idx,
137 uintptr_t offset, u32 shift, pte_t attr)
Daisuke Nojirif574a322014-02-27 14:56:39 -0800138{
139 int i;
140
Julius Werner108548a2014-10-09 17:31:45 -0700141 /* Write out page table entries. */
142 for (i = start_idx; i < end_idx; i++)
143 table[i] = (offset + (i << shift)) | attr;
144
Daisuke Nojirif574a322014-02-27 14:56:39 -0800145 /* Flush the page table entries from the dcache. */
Julius Werner108548a2014-10-09 17:31:45 -0700146 for (i = start_idx; i < end_idx; i++)
147 dccmvac((uintptr_t)&table[i]);
Daisuke Nojirif574a322014-02-27 14:56:39 -0800148 dsb();
Julius Werner108548a2014-10-09 17:31:45 -0700149
Daisuke Nojirif574a322014-02-27 14:56:39 -0800150 /* Invalidate the TLB entries. */
Julius Werner108548a2014-10-09 17:31:45 -0700151 for (i = start_idx; i < end_idx; i++)
152 tlbimvaa(offset + (i << shift));
Daisuke Nojirif574a322014-02-27 14:56:39 -0800153 dsb();
154 isb();
155}
156
Julius Werner108548a2014-10-09 17:31:45 -0700157static pte_t *mmu_create_subtable(pte_t *pgd_entry)
Daisuke Nojirif574a322014-02-27 14:56:39 -0800158{
Julius Werner03a0a652015-09-29 17:28:15 -0700159 pte_t *table = (pte_t *)_ttb_subtables;
160
161 /* Find unused subtable (first PTE == ATTR_UNUSED). */
162 while (table[0] != ATTR_UNUSED) {
163 table += SUBTABLE_PTES;
164 if ((pte_t *)_ettb_subtables - table <= 0)
165 die("Not enough room for another sub-pagetable!");
166 }
Daisuke Nojirif574a322014-02-27 14:56:39 -0800167
Julius Werner108548a2014-10-09 17:31:45 -0700168 /* We assume that *pgd_entry must already be a valid block mapping. */
169 uintptr_t start_addr = (uintptr_t)(*pgd_entry & BLOCK_MASK);
Julius Werner108548a2014-10-09 17:31:45 -0700170 printk(BIOS_DEBUG, "Creating new subtable @%p for [%#.8x:%#.8lx)\n",
171 table, start_addr, start_addr + BLOCK_SIZE);
Daisuke Nojirif574a322014-02-27 14:56:39 -0800172
Julius Werner108548a2014-10-09 17:31:45 -0700173 /* Initialize the new subtable with entries of the same attributes
174 * (XN bit moves from 4 to 0, set PAGE unless block was unmapped). */
175 pte_t attr = *pgd_entry & ~(BLOCK_MASK);
176 if (!IS_ENABLED(CONFIG_ARM_LPAE) && (attr & (1 << 4)))
177 attr = ((attr & ~(1 << 4)) | (1 << 0));
178 if (attr & ATTR_BLOCK)
179 attr = (attr & ~ATTR_BLOCK) | ATTR_PAGE;
Julius Werner03a0a652015-09-29 17:28:15 -0700180 mmu_fill_table(table, 0, SUBTABLE_PTES, start_addr, PAGE_SHIFT, attr);
Daisuke Nojirif574a322014-02-27 14:56:39 -0800181
Julius Werner108548a2014-10-09 17:31:45 -0700182 /* Replace old entry in upper level table to point at subtable. */
183 *pgd_entry = (pte_t)(uintptr_t)table | ATTR_NEXTLEVEL;
184 dccmvac((uintptr_t)pgd_entry);
185 dsb();
186 tlbimvaa(start_addr);
187 dsb();
188 isb();
189
190 return table;
Daisuke Nojirif574a322014-02-27 14:56:39 -0800191}
192
Deepa Dinamanie1977482015-01-28 14:15:56 -0800193static pte_t *mmu_validate_create_sub_table(u32 start_kb, u32 size_kb)
Daisuke Nojirif574a322014-02-27 14:56:39 -0800194{
Julius Werner108548a2014-10-09 17:31:45 -0700195 pte_t *pgd_entry = &ttb_buff[start_kb / (BLOCK_SIZE/KiB)];
196 pte_t *table = (void *)(uintptr_t)(*pgd_entry & NEXTLEVEL_MASK);
David Hendricksf9be7562013-03-21 21:58:50 -0700197
Julius Werner108548a2014-10-09 17:31:45 -0700198 /* Make sure the range is contained within a single superpage. */
199 assert(((start_kb + size_kb - 1) & (BLOCK_MASK/KiB))
200 == (start_kb & (BLOCK_MASK/KiB)) && start_kb < 4 * (GiB/KiB));
David Hendricksf9be7562013-03-21 21:58:50 -0700201
Julius Werner108548a2014-10-09 17:31:45 -0700202 if ((*pgd_entry & ~NEXTLEVEL_MASK) != ATTR_NEXTLEVEL)
203 table = mmu_create_subtable(pgd_entry);
David Hendricksf9be7562013-03-21 21:58:50 -0700204
Deepa Dinamanie1977482015-01-28 14:15:56 -0800205 return table;
206}
207
208void mmu_config_range_kb(u32 start_kb, u32 size_kb, enum dcache_policy policy)
209{
210 pte_t *table = mmu_validate_create_sub_table(start_kb, size_kb);
211
Julius Werner108548a2014-10-09 17:31:45 -0700212 /* Always _one_ _damn_ bit that won't fit... (XN moves from 4 to 0) */
213 pte_t attr = attrs[policy].value;
214 if (!IS_ENABLED(CONFIG_ARM_LPAE) && (attr & (1 << 4)))
215 attr = ((attr & ~(1 << 4)) | (1 << 0));
Gabe Black800790d2013-05-18 22:45:54 -0700216
Julius Werner108548a2014-10-09 17:31:45 -0700217 /* Mask away high address bits that are handled by upper level table. */
218 u32 mask = BLOCK_SIZE/KiB - 1;
219 printk(BIOS_DEBUG, "Mapping address range [%#.8x:%#.8x) as %s\n",
220 start_kb * KiB, (start_kb + size_kb) * KiB, attrs[policy].name);
Varadarajan Narayanan0067a422016-03-03 15:14:46 +0530221
222 u32 end_kb = ALIGN_UP((start_kb + size_kb), PAGE_SIZE/KiB) -
223 (start_kb & ~mask);
224
225 assert(end_kb <= BLOCK_SIZE/KiB);
226
Julius Werner108548a2014-10-09 17:31:45 -0700227 mmu_fill_table(table, (start_kb & mask) / (PAGE_SIZE/KiB),
Varadarajan Narayanan0067a422016-03-03 15:14:46 +0530228 end_kb / (PAGE_SIZE/KiB),
Julius Werner108548a2014-10-09 17:31:45 -0700229 (start_kb & ~mask) * KiB, PAGE_SHIFT, ATTR_PAGE | attr);
230}
231
Deepa Dinamanie1977482015-01-28 14:15:56 -0800232void mmu_disable_range_kb(u32 start_kb, u32 size_kb)
233{
234 pte_t *table = mmu_validate_create_sub_table(start_kb, size_kb);
235
236 /* Mask away high address bits that are handled by upper level table. */
237 u32 mask = BLOCK_SIZE/KiB - 1;
238 printk(BIOS_DEBUG, "Setting address range [%#.8x:%#.8x) as unmapped\n",
239 start_kb * KiB, (start_kb + size_kb) * KiB);
240 mmu_fill_table(table, (start_kb & mask) / (PAGE_SIZE/KiB),
241 div_round_up((start_kb + size_kb) & mask, PAGE_SIZE/KiB),
242 (start_kb & ~mask) * KiB, PAGE_SHIFT, 0);
243}
244
Julius Werner108548a2014-10-09 17:31:45 -0700245void mmu_disable_range(u32 start_mb, u32 size_mb)
246{
247 printk(BIOS_DEBUG, "Setting address range [%#.8x:%#.8x) as unmapped\n",
248 start_mb * MiB, (start_mb + size_mb) * MiB);
249 assert(start_mb + size_mb <= 4 * (GiB/MiB));
250 mmu_fill_table(ttb_buff, start_mb / (BLOCK_SIZE/MiB),
251 div_round_up(start_mb + size_mb, BLOCK_SIZE/MiB),
252 0, BLOCK_SHIFT, 0);
253}
254
255void mmu_config_range(u32 start_mb, u32 size_mb, enum dcache_policy policy)
256{
257 printk(BIOS_DEBUG, "Mapping address range [%#.8x:%#.8x) as %s\n",
258 start_mb * MiB, (start_mb + size_mb) * MiB, attrs[policy].name);
259 assert(start_mb + size_mb <= 4 * (GiB/MiB));
260 mmu_fill_table(ttb_buff, start_mb / (BLOCK_SIZE/MiB),
261 div_round_up(start_mb + size_mb, BLOCK_SIZE/MiB),
262 0, BLOCK_SHIFT, ATTR_BLOCK | attrs[policy].value);
David Hendricksf9be7562013-03-21 21:58:50 -0700263}
264
Daisuke Nojirif574a322014-02-27 14:56:39 -0800265/*
266 * For coreboot's purposes, we will create a simple identity map.
267 *
268 * If LPAE is disabled, we will create a L1 page
269 * table in RAM with 1MB section translation entries over the 4GB address space.
270 * (ref: section 10.2 and example 15-4 in Cortex-A series programmer's guide)
271 *
272 * If LPAE is enabled, we do two level translation with one L1 table with 4
273 * entries, each covering a 1GB space, and four L2 tables with 512 entries, each
274 * covering a 2MB space.
275 */
David Hendricksf9be7562013-03-21 21:58:50 -0700276void mmu_init(void)
277{
Julius Werner03a0a652015-09-29 17:28:15 -0700278 /* Initially mark all subtables as unused (first PTE == ATTR_UNUSED). */
279 pte_t *table = (pte_t *)_ttb_subtables;
280 for (; (pte_t *)_ettb_subtables - table > 0; table += SUBTABLE_PTES)
281 table[0] = ATTR_UNUSED;
282
Daisuke Nojirif574a322014-02-27 14:56:39 -0800283 if (CONFIG_ARM_LPAE) {
Julius Werner108548a2014-10-09 17:31:45 -0700284 pte_t *const pgd_buff = (pte_t*)(_ttb + 16*KiB);
285 pte_t *pmd = ttb_buff;
Daisuke Nojirif574a322014-02-27 14:56:39 -0800286 int i;
287
288 printk(BIOS_DEBUG, "LPAE Translation tables are @ %p\n",
289 ttb_buff);
290 ASSERT((read_mmfr0() & 0xf) >= 5);
291
292 /*
293 * Set MAIR
294 * See B4.1.104 of ARMv7 Architecture Reference Manual
295 */
296 write_mair0(
297 0x00 << (MAIR_INDX_NC*8) | /* Strongly-ordered,
298 * Non-Cacheable */
299 0xaa << (MAIR_INDX_WT*8) | /* Write-Thru,
300 * Read-Allocate */
301 0xff << (MAIR_INDX_WB*8) /* Write-Back,
302 * Read/Write-Allocate */
303 );
304
305 /*
306 * Set up L1 table
307 * Once set here, L1 table won't be modified by coreboot.
308 * See B3.6.1 of ARMv7 Architecture Reference Manual
309 */
310 for (i = 0; i < 4; i++) {
Julius Werner108548a2014-10-09 17:31:45 -0700311 pgd_buff[i] = ((uint32_t)pmd & NEXTLEVEL_MASK) |
312 ATTR_NEXTLEVEL;
Daisuke Nojirif574a322014-02-27 14:56:39 -0800313 pmd += BLOCK_SIZE / PAGE_SIZE;
314 }
315
316 /*
317 * Set TTBR0
318 */
319 write_ttbr0((uintptr_t)pgd_buff);
320 } else {
321 printk(BIOS_DEBUG, "Translation table is @ %p\n", ttb_buff);
322
323 /*
324 * Translation table base 0 address is in bits 31:14-N, where N
325 * is given by bits 2:0 in TTBCR (which we set to 0). All lower
326 * bits in this register should be zero for coreboot.
327 */
328 write_ttbr0((uintptr_t)ttb_buff);
329 }
David Hendricksf9be7562013-03-21 21:58:50 -0700330
331 /*
Daisuke Nojirif574a322014-02-27 14:56:39 -0800332 * Set TTBCR
333 * See B4.1.153 of ARMv7 Architecture Reference Manual
334 * See B3.5.4 and B3.6.4 for how TTBR0 or TTBR1 is selected.
David Hendricksf9be7562013-03-21 21:58:50 -0700335 */
Daisuke Nojirif574a322014-02-27 14:56:39 -0800336 write_ttbcr(
337 CONFIG_ARM_LPAE << 31 | /* EAE. 1:Enable LPAE */
338 0 << 16 | 0 << 0 /* Use TTBR0 for all addresses */
339 );
David Hendricksf9be7562013-03-21 21:58:50 -0700340
Julius Wernerba11d6f2014-10-16 09:56:27 -0700341 /* Set domain 0 to Client so XN bit works (to prevent prefetches) */
342 write_dacr(0x5);
David Hendricksf9be7562013-03-21 21:58:50 -0700343}