pageattr.c 2.3 KB

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