Merge tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64...
[linux-2.6-block.git] / arch / arm64 / kernel / head.S
index e3cb9fbf96b66c3ba2d4327d4c1a4b3ca734ef1f..ba3ab04788dc98952398f4bcf4bb549e241d1412 100644 (file)
@@ -147,6 +147,26 @@ preserve_boot_args:
        b       __inval_dcache_area             // tail call
 ENDPROC(preserve_boot_args)
 
+/*
+ * Macro to arrange a physical address in a page table entry, taking care of
+ * 52-bit addresses.
+ *
+ * Preserves:  phys
+ * Returns:    pte
+ */
+       .macro  phys_to_pte, phys, pte
+#ifdef CONFIG_ARM64_PA_BITS_52
+       /*
+        * We assume \phys is 64K aligned and this is guaranteed by only
+        * supporting this configuration with 64K pages.
+        */
+       orr     \pte, \phys, \phys, lsr #36
+       and     \pte, \pte, #PTE_ADDR_MASK
+#else
+       mov     \pte, \phys
+#endif
+       .endm
+
 /*
  * Macro to create a table entry to the next page.
  *
@@ -156,54 +176,124 @@ ENDPROC(preserve_boot_args)
  *     ptrs:   #imm pointers per table page
  *
  * Preserves:  virt
- * Corrupts:   tmp1, tmp2
+ * Corrupts:   ptrs, tmp1, tmp2
  * Returns:    tbl -> next level table page address
  */
        .macro  create_table_entry, tbl, virt, shift, ptrs, tmp1, tmp2
-       lsr     \tmp1, \virt, #\shift
-       and     \tmp1, \tmp1, #\ptrs - 1        // table index
-       add     \tmp2, \tbl, #PAGE_SIZE
+       add     \tmp1, \tbl, #PAGE_SIZE
+       phys_to_pte \tmp1, \tmp2
        orr     \tmp2, \tmp2, #PMD_TYPE_TABLE   // address of next table and entry type
+       lsr     \tmp1, \virt, #\shift
+       sub     \ptrs, \ptrs, #1
+       and     \tmp1, \tmp1, \ptrs             // table index
        str     \tmp2, [\tbl, \tmp1, lsl #3]
        add     \tbl, \tbl, #PAGE_SIZE          // next level table page
        .endm
 
 /*
- * Macro to populate the PGD (and possibily PUD) for the corresponding
- * block entry in the next level (tbl) for the given virtual address.
+ * Macro to populate page table entries, these entries can be pointers to the next level
+ * or last level entries pointing to physical memory.
+ *
+ *     tbl:    page table address
+ *     rtbl:   pointer to page table or physical memory
+ *     index:  start index to write
+ *     eindex: end index to write - [index, eindex] written to
+ *     flags:  flags for pagetable entry to or in
+ *     inc:    increment to rtbl between each entry
+ *     tmp1:   temporary variable
  *
- * Preserves:  tbl, next, virt
- * Corrupts:   tmp1, tmp2
+ * Preserves:  tbl, eindex, flags, inc
+ * Corrupts:   index, tmp1
+ * Returns:    rtbl
  */
-       .macro  create_pgd_entry, tbl, virt, tmp1, tmp2
-       create_table_entry \tbl, \virt, PGDIR_SHIFT, PTRS_PER_PGD, \tmp1, \tmp2
-#if SWAPPER_PGTABLE_LEVELS > 3
-       create_table_entry \tbl, \virt, PUD_SHIFT, PTRS_PER_PUD, \tmp1, \tmp2
-#endif
-#if SWAPPER_PGTABLE_LEVELS > 2
-       create_table_entry \tbl, \virt, SWAPPER_TABLE_SHIFT, PTRS_PER_PTE, \tmp1, \tmp2
-#endif
+       .macro populate_entries, tbl, rtbl, index, eindex, flags, inc, tmp1
+.Lpe\@:        phys_to_pte \rtbl, \tmp1
+       orr     \tmp1, \tmp1, \flags    // tmp1 = table entry
+       str     \tmp1, [\tbl, \index, lsl #3]
+       add     \rtbl, \rtbl, \inc      // rtbl = pa next level
+       add     \index, \index, #1
+       cmp     \index, \eindex
+       b.ls    .Lpe\@
+       .endm
+
+/*
+ * Compute indices of table entries from virtual address range. If multiple entries
+ * were needed in the previous page table level then the next page table level is assumed
+ * to be composed of multiple pages. (This effectively scales the end index).
+ *
+ *     vstart: virtual address of start of range
+ *     vend:   virtual address of end of range
+ *     shift:  shift used to transform virtual address into index
+ *     ptrs:   number of entries in page table
+ *     istart: index in table corresponding to vstart
+ *     iend:   index in table corresponding to vend
+ *     count:  On entry: how many extra entries were required in previous level, scales
+ *                       our end index.
+ *             On exit: returns how many extra entries required for next page table level
+ *
+ * Preserves:  vstart, vend, shift, ptrs
+ * Returns:    istart, iend, count
+ */
+       .macro compute_indices, vstart, vend, shift, ptrs, istart, iend, count
+       lsr     \iend, \vend, \shift
+       mov     \istart, \ptrs
+       sub     \istart, \istart, #1
+       and     \iend, \iend, \istart   // iend = (vend >> shift) & (ptrs - 1)
+       mov     \istart, \ptrs
+       mul     \istart, \istart, \count
+       add     \iend, \iend, \istart   // iend += (count - 1) * ptrs
+                                       // our entries span multiple tables
+
+       lsr     \istart, \vstart, \shift
+       mov     \count, \ptrs
+       sub     \count, \count, #1
+       and     \istart, \istart, \count
+
+       sub     \count, \iend, \istart
        .endm
 
 /*
- * Macro to populate block entries in the page table for the start..end
- * virtual range (inclusive).
+ * Map memory for specified virtual address range. Each level of page table needed supports
+ * multiple entries. If a level requires n entries the next page table level is assumed to be
+ * formed from n pages.
+ *
+ *     tbl:    location of page table
+ *     rtbl:   address to be used for first level page table entry (typically tbl + PAGE_SIZE)
+ *     vstart: start address to map
+ *     vend:   end address to map - we map [vstart, vend]
+ *     flags:  flags to use to map last level entries
+ *     phys:   physical address corresponding to vstart - physical memory is contiguous
+ *     pgds:   the number of pgd entries
  *
- * Preserves:  tbl, flags
- * Corrupts:   phys, start, end, pstate
+ * Temporaries:        istart, iend, tmp, count, sv - these need to be different registers
+ * Preserves:  vstart, vend, flags
+ * Corrupts:   tbl, rtbl, istart, iend, tmp, count, sv
  */
-       .macro  create_block_map, tbl, flags, phys, start, end
-       lsr     \phys, \phys, #SWAPPER_BLOCK_SHIFT
-       lsr     \start, \start, #SWAPPER_BLOCK_SHIFT
-       and     \start, \start, #PTRS_PER_PTE - 1       // table index
-       orr     \phys, \flags, \phys, lsl #SWAPPER_BLOCK_SHIFT  // table entry
-       lsr     \end, \end, #SWAPPER_BLOCK_SHIFT
-       and     \end, \end, #PTRS_PER_PTE - 1           // table end index
-9999:  str     \phys, [\tbl, \start, lsl #3]           // store the entry
-       add     \start, \start, #1                      // next entry
-       add     \phys, \phys, #SWAPPER_BLOCK_SIZE               // next block
-       cmp     \start, \end
-       b.ls    9999b
+       .macro map_memory, tbl, rtbl, vstart, vend, flags, phys, pgds, istart, iend, tmp, count, sv
+       add \rtbl, \tbl, #PAGE_SIZE
+       mov \sv, \rtbl
+       mov \count, #0
+       compute_indices \vstart, \vend, #PGDIR_SHIFT, \pgds, \istart, \iend, \count
+       populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
+       mov \tbl, \sv
+       mov \sv, \rtbl
+
+#if SWAPPER_PGTABLE_LEVELS > 3
+       compute_indices \vstart, \vend, #PUD_SHIFT, #PTRS_PER_PUD, \istart, \iend, \count
+       populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
+       mov \tbl, \sv
+       mov \sv, \rtbl
+#endif
+
+#if SWAPPER_PGTABLE_LEVELS > 2
+       compute_indices \vstart, \vend, #SWAPPER_TABLE_SHIFT, #PTRS_PER_PMD, \istart, \iend, \count
+       populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
+       mov \tbl, \sv
+#endif
+
+       compute_indices \vstart, \vend, #SWAPPER_BLOCK_SHIFT, #PTRS_PER_PTE, \istart, \iend, \count
+       bic \count, \phys, #SWAPPER_BLOCK_SIZE - 1
+       populate_entries \tbl, \count, \istart, \iend, \flags, #SWAPPER_BLOCK_SIZE, \tmp
        .endm
 
 /*
@@ -221,14 +311,16 @@ __create_page_tables:
         * dirty cache lines being evicted.
         */
        adrp    x0, idmap_pg_dir
-       ldr     x1, =(IDMAP_DIR_SIZE + SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE)
+       adrp    x1, swapper_pg_end
+       sub     x1, x1, x0
        bl      __inval_dcache_area
 
        /*
         * Clear the idmap and swapper page tables.
         */
        adrp    x0, idmap_pg_dir
-       ldr     x1, =(IDMAP_DIR_SIZE + SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE)
+       adrp    x1, swapper_pg_end
+       sub     x1, x1, x0
 1:     stp     xzr, xzr, [x0], #16
        stp     xzr, xzr, [x0], #16
        stp     xzr, xzr, [x0], #16
@@ -244,26 +336,13 @@ __create_page_tables:
        adrp    x0, idmap_pg_dir
        adrp    x3, __idmap_text_start          // __pa(__idmap_text_start)
 
-#ifndef CONFIG_ARM64_VA_BITS_48
-#define EXTRA_SHIFT    (PGDIR_SHIFT + PAGE_SHIFT - 3)
-#define EXTRA_PTRS     (1 << (48 - EXTRA_SHIFT))
-
-       /*
-        * If VA_BITS < 48, it may be too small to allow for an ID mapping to be
-        * created that covers system RAM if that is located sufficiently high
-        * in the physical address space. So for the ID map, use an extended
-        * virtual range in that case, by configuring an additional translation
-        * level.
-        * First, we have to verify our assumption that the current value of
-        * VA_BITS was chosen such that all translation levels are fully
-        * utilised, and that lowering T0SZ will always result in an additional
-        * translation level to be configured.
-        */
-#if VA_BITS != EXTRA_SHIFT
-#error "Mismatch between VA_BITS and page size/number of translation levels"
-#endif
-
        /*
+        * VA_BITS may be too small to allow for an ID mapping to be created
+        * that covers system RAM if that is located sufficiently high in the
+        * physical address space. So for the ID map, use an extended virtual
+        * range in that case, and configure an additional translation level
+        * if needed.
+        *
         * Calculate the maximum allowed value for TCR_EL1.T0SZ so that the
         * entire ID map region can be mapped. As T0SZ == (64 - #bits used),
         * this number conveniently equals the number of leading zeroes in
@@ -272,21 +351,44 @@ __create_page_tables:
        adrp    x5, __idmap_text_end
        clz     x5, x5
        cmp     x5, TCR_T0SZ(VA_BITS)   // default T0SZ small enough?
-       b.ge    1f                      // .. then skip additional level
+       b.ge    1f                      // .. then skip VA range extension
 
        adr_l   x6, idmap_t0sz
        str     x5, [x6]
        dmb     sy
        dc      ivac, x6                // Invalidate potentially stale cache line
 
-       create_table_entry x0, x3, EXTRA_SHIFT, EXTRA_PTRS, x5, x6
-1:
+#if (VA_BITS < 48)
+#define EXTRA_SHIFT    (PGDIR_SHIFT + PAGE_SHIFT - 3)
+#define EXTRA_PTRS     (1 << (PHYS_MASK_SHIFT - EXTRA_SHIFT))
+
+       /*
+        * If VA_BITS < 48, we have to configure an additional table level.
+        * First, we have to verify our assumption that the current value of
+        * VA_BITS was chosen such that all translation levels are fully
+        * utilised, and that lowering T0SZ will always result in an additional
+        * translation level to be configured.
+        */
+#if VA_BITS != EXTRA_SHIFT
+#error "Mismatch between VA_BITS and page size/number of translation levels"
 #endif
 
-       create_pgd_entry x0, x3, x5, x6
+       mov     x4, EXTRA_PTRS
+       create_table_entry x0, x3, EXTRA_SHIFT, x4, x5, x6
+#else
+       /*
+        * If VA_BITS == 48, we don't have to configure an additional
+        * translation level, but the top-level table has more entries.
+        */
+       mov     x4, #1 << (PHYS_MASK_SHIFT - PGDIR_SHIFT)
+       str_l   x4, idmap_ptrs_per_pgd, x5
+#endif
+1:
+       ldr_l   x4, idmap_ptrs_per_pgd
        mov     x5, x3                          // __pa(__idmap_text_start)
        adr_l   x6, __idmap_text_end            // __pa(__idmap_text_end)
-       create_block_map x0, x7, x3, x5, x6
+
+       map_memory x0, x1, x3, x6, x7, x3, x4, x10, x11, x12, x13, x14
 
        /*
         * Map the kernel image (starting with PHYS_OFFSET).
@@ -294,12 +396,13 @@ __create_page_tables:
        adrp    x0, swapper_pg_dir
        mov_q   x5, KIMAGE_VADDR + TEXT_OFFSET  // compile time __va(_text)
        add     x5, x5, x23                     // add KASLR displacement
-       create_pgd_entry x0, x5, x3, x6
+       mov     x4, PTRS_PER_PGD
        adrp    x6, _end                        // runtime __pa(_end)
        adrp    x3, _text                       // runtime __pa(_text)
        sub     x6, x6, x3                      // _end - _text
        add     x6, x6, x5                      // runtime __va(_end)
-       create_block_map x0, x7, x3, x5, x6
+
+       map_memory x0, x1, x5, x6, x7, x3, x4, x10, x11, x12, x13, x14
 
        /*
         * Since the page tables have been populated with non-cacheable
@@ -307,7 +410,8 @@ __create_page_tables:
         * tables again to remove any speculatively loaded cache lines.
         */
        adrp    x0, idmap_pg_dir
-       ldr     x1, =(IDMAP_DIR_SIZE + SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE)
+       adrp    x1, swapper_pg_end
+       sub     x1, x1, x0
        dmb     sy
        bl      __inval_dcache_area
 
@@ -388,17 +492,13 @@ ENTRY(el2_setup)
        mrs     x0, CurrentEL
        cmp     x0, #CurrentEL_EL2
        b.eq    1f
-       mrs     x0, sctlr_el1
-CPU_BE(        orr     x0, x0, #(3 << 24)      )       // Set the EE and E0E bits for EL1
-CPU_LE(        bic     x0, x0, #(3 << 24)      )       // Clear the EE and E0E bits for EL1
+       mov_q   x0, (SCTLR_EL1_RES1 | ENDIAN_SET_EL1)
        msr     sctlr_el1, x0
        mov     w0, #BOOT_CPU_MODE_EL1          // This cpu booted in EL1
        isb
        ret
 
-1:     mrs     x0, sctlr_el2
-CPU_BE(        orr     x0, x0, #(1 << 25)      )       // Set the EE bit for EL2
-CPU_LE(        bic     x0, x0, #(1 << 25)      )       // Clear the EE bit for EL2
+1:     mov_q   x0, (SCTLR_EL2_RES1 | ENDIAN_SET_EL2)
        msr     sctlr_el2, x0
 
 #ifdef CONFIG_ARM64_VHE
@@ -514,10 +614,7 @@ install_el2_stub:
         * requires no configuration, and all non-hyp-specific EL2 setup
         * will be done via the _EL1 system register aliases in __cpu_setup.
         */
-       /* sctlr_el1 */
-       mov     x0, #0x0800                     // Set/clear RES{1,0} bits
-CPU_BE(        movk    x0, #0x33d0, lsl #16    )       // Set EE and E0E on BE systems
-CPU_LE(        movk    x0, #0x30d0, lsl #16    )       // Clear EE and E0E on LE systems
+       mov_q   x0, (SCTLR_EL1_RES1 | ENDIAN_SET_EL1)
        msr     sctlr_el1, x0
 
        /* Coprocessor traps. */
@@ -679,8 +776,10 @@ ENTRY(__enable_mmu)
        update_early_cpu_boot_status 0, x1, x2
        adrp    x1, idmap_pg_dir
        adrp    x2, swapper_pg_dir
-       msr     ttbr0_el1, x1                   // load TTBR0
-       msr     ttbr1_el1, x2                   // load TTBR1
+       phys_to_ttbr x1, x3
+       phys_to_ttbr x2, x4
+       msr     ttbr0_el1, x3                   // load TTBR0
+       msr     ttbr1_el1, x4                   // load TTBR1
        isb
        msr     sctlr_el1, x0
        isb