pageattr.c 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  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/kernel.h>
  14. #include <linux/mm.h>
  15. #include <linux/module.h>
  16. #include <linux/sched.h>
  17. #include <asm/pgtable.h>
  18. #include <asm/tlbflush.h>
  19. struct page_change_data {
  20. pgprot_t set_mask;
  21. pgprot_t clear_mask;
  22. };
  23. static int change_page_range(pte_t *ptep, pgtable_t token, unsigned long addr,
  24. void *data)
  25. {
  26. struct page_change_data *cdata = data;
  27. pte_t pte = *ptep;
  28. pte = clear_pte_bit(pte, cdata->clear_mask);
  29. pte = set_pte_bit(pte, cdata->set_mask);
  30. set_pte(ptep, pte);
  31. return 0;
  32. }
  33. static int change_memory_common(unsigned long addr, int numpages,
  34. pgprot_t set_mask, pgprot_t clear_mask)
  35. {
  36. unsigned long start = addr;
  37. unsigned long size = PAGE_SIZE*numpages;
  38. unsigned long end = start + size;
  39. int ret;
  40. struct page_change_data data;
  41. if (!IS_ALIGNED(addr, PAGE_SIZE)) {
  42. start &= PAGE_MASK;
  43. end = start + size;
  44. WARN_ON_ONCE(1);
  45. }
  46. if (!is_module_address(start) || !is_module_address(end - 1))
  47. return -EINVAL;
  48. data.set_mask = set_mask;
  49. data.clear_mask = clear_mask;
  50. ret = apply_to_page_range(&init_mm, start, size, change_page_range,
  51. &data);
  52. flush_tlb_kernel_range(start, end);
  53. return ret;
  54. }
  55. int set_memory_ro(unsigned long addr, int numpages)
  56. {
  57. return change_memory_common(addr, numpages,
  58. __pgprot(PTE_RDONLY),
  59. __pgprot(PTE_WRITE));
  60. }
  61. EXPORT_SYMBOL_GPL(set_memory_ro);
  62. int set_memory_rw(unsigned long addr, int numpages)
  63. {
  64. return change_memory_common(addr, numpages,
  65. __pgprot(PTE_WRITE),
  66. __pgprot(PTE_RDONLY));
  67. }
  68. EXPORT_SYMBOL_GPL(set_memory_rw);
  69. int set_memory_nx(unsigned long addr, int numpages)
  70. {
  71. return change_memory_common(addr, numpages,
  72. __pgprot(PTE_PXN),
  73. __pgprot(0));
  74. }
  75. EXPORT_SYMBOL_GPL(set_memory_nx);
  76. int set_memory_x(unsigned long addr, int numpages)
  77. {
  78. return change_memory_common(addr, numpages,
  79. __pgprot(0),
  80. __pgprot(PTE_PXN));
  81. }
  82. EXPORT_SYMBOL_GPL(set_memory_x);