blob: 9a8eb841c428828942bca4d45ed1f33372a5345a [file] [log] [blame]
Colin Crosse5e483d2011-08-11 17:15:24 -07001/*
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
27static int kernel_set_to_readonly __read_mostly;
28
29#ifdef CONFIG_DEBUG_RODATA_TEST
30static const int rodata_test_data = 0xC3;
31
32static 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
74static inline void rodata_test(void) { }
75#endif
76
77static 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
109int set_memory_ro(unsigned long virt, int numpages)
110{
111 return set_page_attributes(virt, numpages, pte_wrprotect);
112}
113EXPORT_SYMBOL(set_memory_ro);
114
115int set_memory_rw(unsigned long virt, int numpages)
116{
117 return set_page_attributes(virt, numpages, pte_mkwrite);
118}
119EXPORT_SYMBOL(set_memory_rw);
120
121void 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
135void 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
152void mark_rodata_ro(void)
153{
154 kernel_set_to_readonly = 1;
155
156 set_kernel_text_ro();
157
158 rodata_test();
159}