| 1 | |
| 2 | |
| 3 | |
| 4 |
|
| 5 | #include <linux/types.h>
|
| 6 | #include <linux/sizes.h>
|
| 7 |
|
| 8 | #include <asm/memory.h>
|
| 9 | #include <asm/pgalloc.h>
|
| 10 | #include <asm/pgtable.h>
|
| 11 |
|
| 12 | #include "pi.h"
|
| 13 |
|
| 14 | |
| 15 | * map_range - Map a contiguous range of physical pages into virtual memory
|
| 16 | *
|
| 17 | * @pte: Address of physical pointer to array of pages to
|
| 18 | * allocate page tables from
|
| 19 | * @start: Virtual address of the start of the range
|
| 20 | * @end: Virtual address of the end of the range (exclusive)
|
| 21 | * @pa: Physical address of the start of the range
|
| 22 | * @prot: Access permissions of the range
|
| 23 | * @level: Translation level for the mapping
|
| 24 | * @tbl: The level @level page table to create the mappings in
|
| 25 | * @may_use_cont: Whether the use of the contiguous attribute is allowed
|
| 26 | * @va_offset: Offset between a physical page and its current mapping
|
| 27 | * in the VA space
|
| 28 |
|
| 29 | void __init map_range(phys_addr_t *pte, u64 start, u64 end, phys_addr_t pa,
|
| 30 | pgprot_t prot, int level, pte_t *tbl, bool may_use_cont,
|
| 31 | u64 va_offset)
|
| 32 | {
|
| 33 | u64 cmask = (level == 3) ? CONT_PTE_SIZE - 1 : U64_MAX;
|
| 34 | ptval_t protval = pgprot_val(prot) & ~PTE_TYPE_MASK;
|
| 35 | int lshift = (3 - level) * PTDESC_TABLE_SHIFT;
|
| 36 | u64 lmask = (PAGE_SIZE << lshift) - 1;
|
| 37 |
|
| 38 | start &= PAGE_MASK;
|
| 39 | pa &= PAGE_MASK;
|
| 40 |
|
| 41 |
|
| 42 | tbl += (start >> (lshift + PAGE_SHIFT)) % PTRS_PER_PTE;
|
| 43 |
|
| 44 | |
| 45 | * Set the right block/page bits for this level unless we are
|
| 46 | * clearing the mapping
|
| 47 |
|
| 48 | if (protval)
|
| 49 | protval |= (level == 2) ? PMD_TYPE_SECT : PTE_TYPE_PAGE;
|
| 50 |
|
| 51 | while (start < end) {
|
| 52 | u64 next = min((start | lmask) + 1, PAGE_ALIGN(end));
|
| 53 |
|
| 54 | if (level < 2 || (level == 2 && (start | next | pa) & lmask)) {
|
| 55 | |
| 56 | * This chunk needs a finer grained mapping. Create a
|
| 57 | * table mapping if necessary and recurse.
|
| 58 |
|
| 59 | if (pte_none(*tbl)) {
|
| 60 | *tbl = __pte(__phys_to_pte_val(*pte) |
|
| 61 | PMD_TYPE_TABLE | PMD_TABLE_UXN);
|
| 62 | *pte += PTRS_PER_PTE * sizeof(pte_t);
|
| 63 | }
|
| 64 | map_range(pte, start, next, pa, prot, level + 1,
|
| 65 | (pte_t *)(__pte_to_phys(*tbl) + va_offset),
|
| 66 | may_use_cont, va_offset);
|
| 67 | } else {
|
| 68 | |
| 69 | * Start a contiguous range if start and pa are
|
| 70 | * suitably aligned
|
| 71 |
|
| 72 | if (((start | pa) & cmask) == 0 && may_use_cont)
|
| 73 | protval |= PTE_CONT;
|
| 74 |
|
| 75 | |
| 76 | * Clear the contiguous attribute if the remaining
|
| 77 | * range does not cover a contiguous block
|
| 78 |
|
| 79 | if ((end & ~cmask) <= start)
|
| 80 | protval &= ~PTE_CONT;
|
| 81 |
|
| 82 |
|
| 83 | *tbl = __pte(__phys_to_pte_val(pa) | protval);
|
| 84 | }
|
| 85 | pa += next - start;
|
| 86 | start = next;
|
| 87 | tbl++;
|
| 88 | }
|
| 89 | }
|
| 90 |
|
| 91 | asmlinkage phys_addr_t __init create_init_idmap(pgd_t *pg_dir, ptval_t clrmask)
|
| 92 | {
|
| 93 | phys_addr_t ptep = (phys_addr_t)pg_dir + PAGE_SIZE;
|
| 94 | pgprot_t text_prot = PAGE_KERNEL_ROX;
|
| 95 | pgprot_t data_prot = PAGE_KERNEL;
|
| 96 |
|
| 97 | pgprot_val(text_prot) &= ~clrmask;
|
| 98 | pgprot_val(data_prot) &= ~clrmask;
|
| 99 |
|
| 100 |
|
| 101 | map_range(&ptep, (u64)_stext, (u64)__initdata_begin,
|
| 102 | (phys_addr_t)_stext, text_prot, IDMAP_ROOT_LEVEL,
|
| 103 | (pte_t *)pg_dir, false, 0);
|
| 104 | map_range(&ptep, (u64)__initdata_begin, (u64)_end,
|
| 105 | (phys_addr_t)__initdata_begin, data_prot, IDMAP_ROOT_LEVEL,
|
| 106 | (pte_t *)pg_dir, false, 0);
|
| 107 |
|
| 108 | return ptep;
|
| 109 | }
|