map_range.c 3.1 KB

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