pageattr.c 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. /*
  2. * Copyright (c) 2014, The Linux Foundation. All rights reserved.
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License version 2 and
  6. * only version 2 as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. */
  13. #include <linux/mm.h>
  14. #include <linux/module.h>
  15. #include <asm/pgtable.h>
  16. #include <asm/tlbflush.h>
  17. #include <asm/set_memory.h>
  18. struct page_change_data {
  19. pgprot_t set_mask;
  20. pgprot_t clear_mask;
  21. };
  22. static int change_page_range(pte_t *ptep, pgtable_t token, unsigned long addr,
  23. void *data)
  24. {
  25. struct page_change_data *cdata = data;
  26. pte_t pte = *ptep;
  27. pte = clear_pte_bit(pte, cdata->clear_mask);
  28. pte = set_pte_bit(pte, cdata->set_mask);
  29. set_pte_ext(ptep, pte, 0);
  30. return 0;
  31. }
  32. static bool in_range(unsigned long start, unsigned long size,
  33. unsigned long range_start, unsigned long range_end)
  34. {
  35. return start >= range_start && start < range_end &&
  36. size <= range_end - start;
  37. }
  38. static int change_memory_common(unsigned long addr, int numpages,
  39. pgprot_t set_mask, pgprot_t clear_mask)
  40. {
  41. unsigned long start = addr & PAGE_MASK;
  42. unsigned long end = PAGE_ALIGN(addr) + numpages * PAGE_SIZE;
  43. unsigned long size = end - start;
  44. int ret;
  45. struct page_change_data data;
  46. WARN_ON_ONCE(start != addr);
  47. if (!size)
  48. return 0;
  49. if (!in_range(start, size, MODULES_VADDR, MODULES_END) &&
  50. !in_range(start, size, VMALLOC_START, VMALLOC_END))
  51. return -EINVAL;
  52. data.set_mask = set_mask;
  53. data.clear_mask = clear_mask;
  54. ret = apply_to_page_range(&init_mm, start, size, change_page_range,
  55. &data);
  56. flush_tlb_kernel_range(start, end);
  57. return ret;
  58. }
  59. int set_memory_ro(unsigned long addr, int numpages)
  60. {
  61. return change_memory_common(addr, numpages,
  62. __pgprot(L_PTE_RDONLY),
  63. __pgprot(0));
  64. }
  65. int set_memory_rw(unsigned long addr, int numpages)
  66. {
  67. return change_memory_common(addr, numpages,
  68. __pgprot(0),
  69. __pgprot(L_PTE_RDONLY));
  70. }
  71. int set_memory_nx(unsigned long addr, int numpages)
  72. {
  73. return change_memory_common(addr, numpages,
  74. __pgprot(L_PTE_XN),
  75. __pgprot(0));
  76. }
  77. int set_memory_x(unsigned long addr, int numpages)
  78. {
  79. return change_memory_common(addr, numpages,
  80. __pgprot(0),
  81. __pgprot(L_PTE_XN));
  82. }