Merge branch 'linux-linaro-lsk' into linux-linaro-lsk-android
[firefly-linux-kernel-4.4.55.git] / arch / arm / mm / rodata.c
1 /*
2  *  linux/arch/arm/mm/rodata.c
3  *
4  *  Copyright (C) 2011 Google, Inc.
5  *
6  *  Author: Colin Cross <ccross@android.com>
7  *
8  *  Based on x86 implementation in arch/x86/mm/init_32.c
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13  */
14
15 #include <linux/kernel.h>
16 #include <linux/mm.h>
17 #include <linux/module.h>
18
19 #include <asm/cache.h>
20 #include <asm/pgtable.h>
21 #include <asm/rodata.h>
22 #include <asm/sections.h>
23 #include <asm/tlbflush.h>
24
25 #include "mm.h"
26
27 static int kernel_set_to_readonly __read_mostly;
28
29 #ifdef CONFIG_DEBUG_RODATA_TEST
30 static const int rodata_test_data = 0xC3;
31
32 static noinline void rodata_test(void)
33 {
34         int result;
35
36         pr_info("%s: attempting to write to read-only section:\n", __func__);
37
38         if (*(volatile int *)&rodata_test_data != 0xC3) {
39                 pr_err("read only data changed before test\n");
40                 return;
41         }
42
43         /*
44          * Attempt to to write to rodata_test_data, trapping the expected
45          * data abort.  If the trap executed, result will be 1.  If it didn't,
46          * result will be 0xFF.
47          */
48         asm volatile(
49                 "0:     str     %[zero], [%[rodata_test_data]]\n"
50                 "       mov     %[result], #0xFF\n"
51                 "       b       2f\n"
52                 "1:     mov     %[result], #1\n"
53                 "2:\n"
54
55                 /* Exception fixup - if store at label 0 faults, jumps to 1 */
56                 ".pushsection __ex_table, \"a\"\n"
57                 "       .long   0b, 1b\n"
58                 ".popsection\n"
59
60                 : [result] "=r" (result)
61                 : [rodata_test_data] "r" (&rodata_test_data), [zero] "r" (0)
62                 : "memory"
63         );
64
65         if (result == 1)
66                 pr_info("write to read-only section trapped, success\n");
67         else
68                 pr_err("write to read-only section NOT trapped, test failed\n");
69
70         if (*(volatile int *)&rodata_test_data != 0xC3)
71                 pr_err("read only data changed during write\n");
72 }
73 #else
74 static inline void rodata_test(void) { }
75 #endif
76
77 static int set_page_attributes(unsigned long virt, int numpages,
78         pte_t (*f)(pte_t))
79 {
80         pmd_t *pmd;
81         pte_t *pte;
82         unsigned long start = virt;
83         unsigned long end = virt + (numpages << PAGE_SHIFT);
84         unsigned long pmd_end;
85
86         while (virt < end) {
87                 pmd = pmd_off_k(virt);
88                 pmd_end = min(ALIGN(virt + 1, PMD_SIZE), end);
89
90                 if ((pmd_val(*pmd) & PMD_TYPE_MASK) != PMD_TYPE_TABLE) {
91                         pr_err("%s: pmd %p=%08lx for %08lx not page table\n",
92                                 __func__, pmd, pmd_val(*pmd), virt);
93                         virt = pmd_end;
94                         continue;
95                 }
96
97                 while (virt < pmd_end) {
98                         pte = pte_offset_kernel(pmd, virt);
99                         set_pte_ext(pte, f(*pte), 0);
100                         virt += PAGE_SIZE;
101                 }
102         }
103
104         flush_tlb_kernel_range(start, end);
105
106         return 0;
107 }
108
109 int set_memory_ro(unsigned long virt, int numpages)
110 {
111         return set_page_attributes(virt, numpages, pte_wrprotect);
112 }
113 EXPORT_SYMBOL(set_memory_ro);
114
115 int set_memory_rw(unsigned long virt, int numpages)
116 {
117         return set_page_attributes(virt, numpages, pte_mkwrite);
118 }
119 EXPORT_SYMBOL(set_memory_rw);
120
121 void set_kernel_text_rw(void)
122 {
123         unsigned long start = PAGE_ALIGN((unsigned long)_text);
124         unsigned long size = PAGE_ALIGN((unsigned long)__end_rodata) - start;
125
126         if (!kernel_set_to_readonly)
127                 return;
128
129         pr_debug("Set kernel text: %lx - %lx to read-write\n",
130                  start, start + size);
131
132         set_memory_rw(start, size >> PAGE_SHIFT);
133 }
134
135 void set_kernel_text_ro(void)
136 {
137         unsigned long start = PAGE_ALIGN((unsigned long)_text);
138         unsigned long size = PAGE_ALIGN((unsigned long)__end_rodata) - start;
139
140         if (!kernel_set_to_readonly)
141                 return;
142
143         pr_info_once("Write protecting the kernel text section %lx - %lx\n",
144                 start, start + size);
145
146         pr_debug("Set kernel text: %lx - %lx to read only\n",
147                  start, start + size);
148
149         set_memory_ro(start, size >> PAGE_SHIFT);
150 }
151
152 void mark_rodata_ro(void)
153 {
154         kernel_set_to_readonly = 1;
155
156         set_kernel_text_ro();
157
158         rodata_test();
159 }