diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2014-12-05 11:30:54 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2014-12-05 11:30:54 -0500 |
commit | e9f2d6d66037cdf97487491e04053f411abc5d16 (patch) | |
tree | 123cec080d17fb74a2531d8cc7ad1cf44bbad9ec /arch | |
parent | fbe4dd088f449cbae586aa8af51d271297c75f9f (diff) | |
parent | 06e944b8e5fc4bec83f102f98c1ee4f972f5f072 (diff) |
Merge branch 'devel-stable' into for-next
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/include/asm/cacheflush.h | 10 | ||||
-rw-r--r-- | arch/arm/include/asm/fixmap.h | 31 | ||||
-rw-r--r-- | arch/arm/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/kernel/ftrace.c | 19 | ||||
-rw-r--r-- | arch/arm/kernel/jump_label.c | 2 | ||||
-rw-r--r-- | arch/arm/kernel/kgdb.c | 29 | ||||
-rw-r--r-- | arch/arm/kernel/machine_kexec.c | 9 | ||||
-rw-r--r-- | arch/arm/kernel/patch.c | 92 | ||||
-rw-r--r-- | arch/arm/kernel/patch.h | 12 | ||||
-rw-r--r-- | arch/arm/kernel/vmlinux.lds.S | 19 | ||||
-rw-r--r-- | arch/arm/mm/Kconfig | 21 | ||||
-rw-r--r-- | arch/arm/mm/highmem.c | 15 | ||||
-rw-r--r-- | arch/arm/mm/init.c | 149 | ||||
-rw-r--r-- | arch/arm/mm/mmu.c | 39 |
14 files changed, 393 insertions, 56 deletions
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h index 10e78d00a0bb..2d46862e7bef 100644 --- a/arch/arm/include/asm/cacheflush.h +++ b/arch/arm/include/asm/cacheflush.h | |||
@@ -487,6 +487,16 @@ int set_memory_rw(unsigned long addr, int numpages); | |||
487 | int set_memory_x(unsigned long addr, int numpages); | 487 | int set_memory_x(unsigned long addr, int numpages); |
488 | int set_memory_nx(unsigned long addr, int numpages); | 488 | int set_memory_nx(unsigned long addr, int numpages); |
489 | 489 | ||
490 | #ifdef CONFIG_DEBUG_RODATA | ||
491 | void mark_rodata_ro(void); | ||
492 | void set_kernel_text_rw(void); | ||
493 | void set_kernel_text_ro(void); | ||
494 | #else | ||
495 | static inline void set_kernel_text_rw(void) { } | ||
496 | static inline void set_kernel_text_ro(void) { } | ||
497 | #endif | ||
498 | |||
490 | void flush_uprobe_xol_access(struct page *page, unsigned long uaddr, | 499 | void flush_uprobe_xol_access(struct page *page, unsigned long uaddr, |
491 | void *kaddr, unsigned long len); | 500 | void *kaddr, unsigned long len); |
501 | |||
492 | #endif | 502 | #endif |
diff --git a/arch/arm/include/asm/fixmap.h b/arch/arm/include/asm/fixmap.h index 74124b0d0d79..0415eae1df27 100644 --- a/arch/arm/include/asm/fixmap.h +++ b/arch/arm/include/asm/fixmap.h | |||
@@ -2,27 +2,24 @@ | |||
2 | #define _ASM_FIXMAP_H | 2 | #define _ASM_FIXMAP_H |
3 | 3 | ||
4 | #define FIXADDR_START 0xffc00000UL | 4 | #define FIXADDR_START 0xffc00000UL |
5 | #define FIXADDR_TOP 0xffe00000UL | 5 | #define FIXADDR_END 0xfff00000UL |
6 | #define FIXADDR_SIZE (FIXADDR_TOP - FIXADDR_START) | 6 | #define FIXADDR_TOP (FIXADDR_END - PAGE_SIZE) |
7 | 7 | ||
8 | #define FIX_KMAP_NR_PTES (FIXADDR_SIZE >> PAGE_SHIFT) | 8 | #include <asm/kmap_types.h> |
9 | 9 | ||
10 | #define __fix_to_virt(x) (FIXADDR_START + ((x) << PAGE_SHIFT)) | 10 | enum fixed_addresses { |
11 | #define __virt_to_fix(x) (((x) - FIXADDR_START) >> PAGE_SHIFT) | 11 | FIX_KMAP_BEGIN, |
12 | FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1, | ||
12 | 13 | ||
13 | extern void __this_fixmap_does_not_exist(void); | 14 | /* Support writing RO kernel text via kprobes, jump labels, etc. */ |
15 | FIX_TEXT_POKE0, | ||
16 | FIX_TEXT_POKE1, | ||
14 | 17 | ||
15 | static inline unsigned long fix_to_virt(const unsigned int idx) | 18 | __end_of_fixed_addresses |
16 | { | 19 | }; |
17 | if (idx >= FIX_KMAP_NR_PTES) | ||
18 | __this_fixmap_does_not_exist(); | ||
19 | return __fix_to_virt(idx); | ||
20 | } | ||
21 | 20 | ||
22 | static inline unsigned int virt_to_fix(const unsigned long vaddr) | 21 | void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot); |
23 | { | 22 | |
24 | BUG_ON(vaddr >= FIXADDR_TOP || vaddr < FIXADDR_START); | 23 | #include <asm-generic/fixmap.h> |
25 | return __virt_to_fix(vaddr); | ||
26 | } | ||
27 | 24 | ||
28 | #endif | 25 | #endif |
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 64886387c0bf..2ac3920e1793 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
@@ -68,7 +68,7 @@ test-kprobes-objs += kprobes-test-arm.o | |||
68 | endif | 68 | endif |
69 | obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o | 69 | obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o |
70 | obj-$(CONFIG_ARM_THUMBEE) += thumbee.o | 70 | obj-$(CONFIG_ARM_THUMBEE) += thumbee.o |
71 | obj-$(CONFIG_KGDB) += kgdb.o | 71 | obj-$(CONFIG_KGDB) += kgdb.o patch.o |
72 | obj-$(CONFIG_ARM_UNWIND) += unwind.o | 72 | obj-$(CONFIG_ARM_UNWIND) += unwind.o |
73 | obj-$(CONFIG_HAVE_TCM) += tcm.o | 73 | obj-$(CONFIG_HAVE_TCM) += tcm.o |
74 | obj-$(CONFIG_OF) += devtree.o | 74 | obj-$(CONFIG_OF) += devtree.o |
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index af9a8a927a4e..b8c75e45a950 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/ftrace.h> | 15 | #include <linux/ftrace.h> |
16 | #include <linux/uaccess.h> | 16 | #include <linux/uaccess.h> |
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/stop_machine.h> | ||
18 | 19 | ||
19 | #include <asm/cacheflush.h> | 20 | #include <asm/cacheflush.h> |
20 | #include <asm/opcodes.h> | 21 | #include <asm/opcodes.h> |
@@ -35,6 +36,22 @@ | |||
35 | 36 | ||
36 | #define OLD_NOP 0xe1a00000 /* mov r0, r0 */ | 37 | #define OLD_NOP 0xe1a00000 /* mov r0, r0 */ |
37 | 38 | ||
39 | static int __ftrace_modify_code(void *data) | ||
40 | { | ||
41 | int *command = data; | ||
42 | |||
43 | set_kernel_text_rw(); | ||
44 | ftrace_modify_all_code(*command); | ||
45 | set_kernel_text_ro(); | ||
46 | |||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | void arch_ftrace_update_code(int command) | ||
51 | { | ||
52 | stop_machine(__ftrace_modify_code, &command, NULL); | ||
53 | } | ||
54 | |||
38 | static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec) | 55 | static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec) |
39 | { | 56 | { |
40 | return rec->arch.old_mcount ? OLD_NOP : NOP; | 57 | return rec->arch.old_mcount ? OLD_NOP : NOP; |
@@ -73,6 +90,8 @@ int ftrace_arch_code_modify_prepare(void) | |||
73 | int ftrace_arch_code_modify_post_process(void) | 90 | int ftrace_arch_code_modify_post_process(void) |
74 | { | 91 | { |
75 | set_all_modules_text_ro(); | 92 | set_all_modules_text_ro(); |
93 | /* Make sure any TLB misses during machine stop are cleared. */ | ||
94 | flush_tlb_all(); | ||
76 | return 0; | 95 | return 0; |
77 | } | 96 | } |
78 | 97 | ||
diff --git a/arch/arm/kernel/jump_label.c b/arch/arm/kernel/jump_label.c index 4ce4f789446d..afeeb9ea6f43 100644 --- a/arch/arm/kernel/jump_label.c +++ b/arch/arm/kernel/jump_label.c | |||
@@ -19,7 +19,7 @@ static void __arch_jump_label_transform(struct jump_entry *entry, | |||
19 | insn = arm_gen_nop(); | 19 | insn = arm_gen_nop(); |
20 | 20 | ||
21 | if (is_static) | 21 | if (is_static) |
22 | __patch_text(addr, insn); | 22 | __patch_text_early(addr, insn); |
23 | else | 23 | else |
24 | patch_text(addr, insn); | 24 | patch_text(addr, insn); |
25 | } | 25 | } |
diff --git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c index a74b53c1b7df..07db2f8a1b45 100644 --- a/arch/arm/kernel/kgdb.c +++ b/arch/arm/kernel/kgdb.c | |||
@@ -12,8 +12,12 @@ | |||
12 | #include <linux/irq.h> | 12 | #include <linux/irq.h> |
13 | #include <linux/kdebug.h> | 13 | #include <linux/kdebug.h> |
14 | #include <linux/kgdb.h> | 14 | #include <linux/kgdb.h> |
15 | #include <linux/uaccess.h> | ||
16 | |||
15 | #include <asm/traps.h> | 17 | #include <asm/traps.h> |
16 | 18 | ||
19 | #include "patch.h" | ||
20 | |||
17 | struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = | 21 | struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = |
18 | { | 22 | { |
19 | { "r0", 4, offsetof(struct pt_regs, ARM_r0)}, | 23 | { "r0", 4, offsetof(struct pt_regs, ARM_r0)}, |
@@ -244,6 +248,31 @@ void kgdb_arch_exit(void) | |||
244 | unregister_die_notifier(&kgdb_notifier); | 248 | unregister_die_notifier(&kgdb_notifier); |
245 | } | 249 | } |
246 | 250 | ||
251 | int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt) | ||
252 | { | ||
253 | int err; | ||
254 | |||
255 | /* patch_text() only supports int-sized breakpoints */ | ||
256 | BUILD_BUG_ON(sizeof(int) != BREAK_INSTR_SIZE); | ||
257 | |||
258 | err = probe_kernel_read(bpt->saved_instr, (char *)bpt->bpt_addr, | ||
259 | BREAK_INSTR_SIZE); | ||
260 | if (err) | ||
261 | return err; | ||
262 | |||
263 | patch_text((void *)bpt->bpt_addr, | ||
264 | *(unsigned int *)arch_kgdb_ops.gdb_bpt_instr); | ||
265 | |||
266 | return err; | ||
267 | } | ||
268 | |||
269 | int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt) | ||
270 | { | ||
271 | patch_text((void *)bpt->bpt_addr, *(unsigned int *)bpt->saved_instr); | ||
272 | |||
273 | return 0; | ||
274 | } | ||
275 | |||
247 | /* | 276 | /* |
248 | * Register our undef instruction hooks with ARM undef core. | 277 | * Register our undef instruction hooks with ARM undef core. |
249 | * We regsiter a hook specifically looking for the KGB break inst | 278 | * We regsiter a hook specifically looking for the KGB break inst |
diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index 4f75192f2ef9..de2b085ad753 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c | |||
@@ -29,6 +29,7 @@ extern unsigned long kexec_boot_atags; | |||
29 | 29 | ||
30 | static atomic_t waiting_for_crash_ipi; | 30 | static atomic_t waiting_for_crash_ipi; |
31 | 31 | ||
32 | static unsigned long dt_mem; | ||
32 | /* | 33 | /* |
33 | * Provide a dummy crash_notes definition while crash dump arrives to arm. | 34 | * Provide a dummy crash_notes definition while crash dump arrives to arm. |
34 | * This prevents breakage of crash_notes attribute in kernel/ksysfs.c. | 35 | * This prevents breakage of crash_notes attribute in kernel/ksysfs.c. |
@@ -64,7 +65,7 @@ int machine_kexec_prepare(struct kimage *image) | |||
64 | return err; | 65 | return err; |
65 | 66 | ||
66 | if (be32_to_cpu(header) == OF_DT_HEADER) | 67 | if (be32_to_cpu(header) == OF_DT_HEADER) |
67 | kexec_boot_atags = current_segment->mem; | 68 | dt_mem = current_segment->mem; |
68 | } | 69 | } |
69 | return 0; | 70 | return 0; |
70 | } | 71 | } |
@@ -163,12 +164,12 @@ void machine_kexec(struct kimage *image) | |||
163 | reboot_code_buffer = page_address(image->control_code_page); | 164 | reboot_code_buffer = page_address(image->control_code_page); |
164 | 165 | ||
165 | /* Prepare parameters for reboot_code_buffer*/ | 166 | /* Prepare parameters for reboot_code_buffer*/ |
167 | set_kernel_text_rw(); | ||
166 | kexec_start_address = image->start; | 168 | kexec_start_address = image->start; |
167 | kexec_indirection_page = page_list; | 169 | kexec_indirection_page = page_list; |
168 | kexec_mach_type = machine_arch_type; | 170 | kexec_mach_type = machine_arch_type; |
169 | if (!kexec_boot_atags) | 171 | kexec_boot_atags = dt_mem ?: image->start - KEXEC_ARM_ZIMAGE_OFFSET |
170 | kexec_boot_atags = image->start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET; | 172 | + KEXEC_ARM_ATAGS_OFFSET; |
171 | |||
172 | 173 | ||
173 | /* copy our kernel relocation code to the control code page */ | 174 | /* copy our kernel relocation code to the control code page */ |
174 | reboot_entry = fncpy(reboot_code_buffer, | 175 | reboot_entry = fncpy(reboot_code_buffer, |
diff --git a/arch/arm/kernel/patch.c b/arch/arm/kernel/patch.c index 07314af47733..5038960e3c55 100644 --- a/arch/arm/kernel/patch.c +++ b/arch/arm/kernel/patch.c | |||
@@ -1,8 +1,11 @@ | |||
1 | #include <linux/kernel.h> | 1 | #include <linux/kernel.h> |
2 | #include <linux/spinlock.h> | ||
2 | #include <linux/kprobes.h> | 3 | #include <linux/kprobes.h> |
4 | #include <linux/mm.h> | ||
3 | #include <linux/stop_machine.h> | 5 | #include <linux/stop_machine.h> |
4 | 6 | ||
5 | #include <asm/cacheflush.h> | 7 | #include <asm/cacheflush.h> |
8 | #include <asm/fixmap.h> | ||
6 | #include <asm/smp_plat.h> | 9 | #include <asm/smp_plat.h> |
7 | #include <asm/opcodes.h> | 10 | #include <asm/opcodes.h> |
8 | 11 | ||
@@ -13,21 +16,77 @@ struct patch { | |||
13 | unsigned int insn; | 16 | unsigned int insn; |
14 | }; | 17 | }; |
15 | 18 | ||
16 | void __kprobes __patch_text(void *addr, unsigned int insn) | 19 | static DEFINE_SPINLOCK(patch_lock); |
20 | |||
21 | static void __kprobes *patch_map(void *addr, int fixmap, unsigned long *flags) | ||
22 | __acquires(&patch_lock) | ||
23 | { | ||
24 | unsigned int uintaddr = (uintptr_t) addr; | ||
25 | bool module = !core_kernel_text(uintaddr); | ||
26 | struct page *page; | ||
27 | |||
28 | if (module && IS_ENABLED(CONFIG_DEBUG_SET_MODULE_RONX)) | ||
29 | page = vmalloc_to_page(addr); | ||
30 | else if (!module && IS_ENABLED(CONFIG_DEBUG_RODATA)) | ||
31 | page = virt_to_page(addr); | ||
32 | else | ||
33 | return addr; | ||
34 | |||
35 | if (flags) | ||
36 | spin_lock_irqsave(&patch_lock, *flags); | ||
37 | else | ||
38 | __acquire(&patch_lock); | ||
39 | |||
40 | set_fixmap(fixmap, page_to_phys(page)); | ||
41 | |||
42 | return (void *) (__fix_to_virt(fixmap) + (uintaddr & ~PAGE_MASK)); | ||
43 | } | ||
44 | |||
45 | static void __kprobes patch_unmap(int fixmap, unsigned long *flags) | ||
46 | __releases(&patch_lock) | ||
47 | { | ||
48 | clear_fixmap(fixmap); | ||
49 | |||
50 | if (flags) | ||
51 | spin_unlock_irqrestore(&patch_lock, *flags); | ||
52 | else | ||
53 | __release(&patch_lock); | ||
54 | } | ||
55 | |||
56 | void __kprobes __patch_text_real(void *addr, unsigned int insn, bool remap) | ||
17 | { | 57 | { |
18 | bool thumb2 = IS_ENABLED(CONFIG_THUMB2_KERNEL); | 58 | bool thumb2 = IS_ENABLED(CONFIG_THUMB2_KERNEL); |
59 | unsigned int uintaddr = (uintptr_t) addr; | ||
60 | bool twopage = false; | ||
61 | unsigned long flags; | ||
62 | void *waddr = addr; | ||
19 | int size; | 63 | int size; |
20 | 64 | ||
65 | if (remap) | ||
66 | waddr = patch_map(addr, FIX_TEXT_POKE0, &flags); | ||
67 | else | ||
68 | __acquire(&patch_lock); | ||
69 | |||
21 | if (thumb2 && __opcode_is_thumb16(insn)) { | 70 | if (thumb2 && __opcode_is_thumb16(insn)) { |
22 | *(u16 *)addr = __opcode_to_mem_thumb16(insn); | 71 | *(u16 *)waddr = __opcode_to_mem_thumb16(insn); |
23 | size = sizeof(u16); | 72 | size = sizeof(u16); |
24 | } else if (thumb2 && ((uintptr_t)addr & 2)) { | 73 | } else if (thumb2 && (uintaddr & 2)) { |
25 | u16 first = __opcode_thumb32_first(insn); | 74 | u16 first = __opcode_thumb32_first(insn); |
26 | u16 second = __opcode_thumb32_second(insn); | 75 | u16 second = __opcode_thumb32_second(insn); |
27 | u16 *addrh = addr; | 76 | u16 *addrh0 = waddr; |
77 | u16 *addrh1 = waddr + 2; | ||
78 | |||
79 | twopage = (uintaddr & ~PAGE_MASK) == PAGE_SIZE - 2; | ||
80 | if (twopage && remap) | ||
81 | addrh1 = patch_map(addr + 2, FIX_TEXT_POKE1, NULL); | ||
82 | |||
83 | *addrh0 = __opcode_to_mem_thumb16(first); | ||
84 | *addrh1 = __opcode_to_mem_thumb16(second); | ||
28 | 85 | ||
29 | addrh[0] = __opcode_to_mem_thumb16(first); | 86 | if (twopage && addrh1 != addr + 2) { |
30 | addrh[1] = __opcode_to_mem_thumb16(second); | 87 | flush_kernel_vmap_range(addrh1, 2); |
88 | patch_unmap(FIX_TEXT_POKE1, NULL); | ||
89 | } | ||
31 | 90 | ||
32 | size = sizeof(u32); | 91 | size = sizeof(u32); |
33 | } else { | 92 | } else { |
@@ -36,10 +95,16 @@ void __kprobes __patch_text(void *addr, unsigned int insn) | |||
36 | else | 95 | else |
37 | insn = __opcode_to_mem_arm(insn); | 96 | insn = __opcode_to_mem_arm(insn); |
38 | 97 | ||
39 | *(u32 *)addr = insn; | 98 | *(u32 *)waddr = insn; |
40 | size = sizeof(u32); | 99 | size = sizeof(u32); |
41 | } | 100 | } |
42 | 101 | ||
102 | if (waddr != addr) { | ||
103 | flush_kernel_vmap_range(waddr, twopage ? size / 2 : size); | ||
104 | patch_unmap(FIX_TEXT_POKE0, &flags); | ||
105 | } else | ||
106 | __release(&patch_lock); | ||
107 | |||
43 | flush_icache_range((uintptr_t)(addr), | 108 | flush_icache_range((uintptr_t)(addr), |
44 | (uintptr_t)(addr) + size); | 109 | (uintptr_t)(addr) + size); |
45 | } | 110 | } |
@@ -60,16 +125,5 @@ void __kprobes patch_text(void *addr, unsigned int insn) | |||
60 | .insn = insn, | 125 | .insn = insn, |
61 | }; | 126 | }; |
62 | 127 | ||
63 | if (cache_ops_need_broadcast()) { | 128 | stop_machine(patch_text_stop_machine, &patch, NULL); |
64 | stop_machine(patch_text_stop_machine, &patch, cpu_online_mask); | ||
65 | } else { | ||
66 | bool straddles_word = IS_ENABLED(CONFIG_THUMB2_KERNEL) | ||
67 | && __opcode_is_thumb32(insn) | ||
68 | && ((uintptr_t)addr & 2); | ||
69 | |||
70 | if (straddles_word) | ||
71 | stop_machine(patch_text_stop_machine, &patch, NULL); | ||
72 | else | ||
73 | __patch_text(addr, insn); | ||
74 | } | ||
75 | } | 129 | } |
diff --git a/arch/arm/kernel/patch.h b/arch/arm/kernel/patch.h index b4731f2dac38..77e054c2f6cd 100644 --- a/arch/arm/kernel/patch.h +++ b/arch/arm/kernel/patch.h | |||
@@ -2,6 +2,16 @@ | |||
2 | #define _ARM_KERNEL_PATCH_H | 2 | #define _ARM_KERNEL_PATCH_H |
3 | 3 | ||
4 | void patch_text(void *addr, unsigned int insn); | 4 | void patch_text(void *addr, unsigned int insn); |
5 | void __patch_text(void *addr, unsigned int insn); | 5 | void __patch_text_real(void *addr, unsigned int insn, bool remap); |
6 | |||
7 | static inline void __patch_text(void *addr, unsigned int insn) | ||
8 | { | ||
9 | __patch_text_real(addr, insn, true); | ||
10 | } | ||
11 | |||
12 | static inline void __patch_text_early(void *addr, unsigned int insn) | ||
13 | { | ||
14 | __patch_text_real(addr, insn, false); | ||
15 | } | ||
6 | 16 | ||
7 | #endif | 17 | #endif |
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 8e95aa47457a..b31aa73e8076 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S | |||
@@ -8,6 +8,9 @@ | |||
8 | #include <asm/thread_info.h> | 8 | #include <asm/thread_info.h> |
9 | #include <asm/memory.h> | 9 | #include <asm/memory.h> |
10 | #include <asm/page.h> | 10 | #include <asm/page.h> |
11 | #ifdef CONFIG_ARM_KERNMEM_PERMS | ||
12 | #include <asm/pgtable.h> | ||
13 | #endif | ||
11 | 14 | ||
12 | #define PROC_INFO \ | 15 | #define PROC_INFO \ |
13 | . = ALIGN(4); \ | 16 | . = ALIGN(4); \ |
@@ -90,6 +93,11 @@ SECTIONS | |||
90 | _text = .; | 93 | _text = .; |
91 | HEAD_TEXT | 94 | HEAD_TEXT |
92 | } | 95 | } |
96 | |||
97 | #ifdef CONFIG_ARM_KERNMEM_PERMS | ||
98 | . = ALIGN(1<<SECTION_SHIFT); | ||
99 | #endif | ||
100 | |||
93 | .text : { /* Real text segment */ | 101 | .text : { /* Real text segment */ |
94 | _stext = .; /* Text and read-only data */ | 102 | _stext = .; /* Text and read-only data */ |
95 | __exception_text_start = .; | 103 | __exception_text_start = .; |
@@ -112,6 +120,9 @@ SECTIONS | |||
112 | ARM_CPU_KEEP(PROC_INFO) | 120 | ARM_CPU_KEEP(PROC_INFO) |
113 | } | 121 | } |
114 | 122 | ||
123 | #ifdef CONFIG_DEBUG_RODATA | ||
124 | . = ALIGN(1<<SECTION_SHIFT); | ||
125 | #endif | ||
115 | RO_DATA(PAGE_SIZE) | 126 | RO_DATA(PAGE_SIZE) |
116 | 127 | ||
117 | . = ALIGN(4); | 128 | . = ALIGN(4); |
@@ -145,7 +156,11 @@ SECTIONS | |||
145 | _etext = .; /* End of text and rodata section */ | 156 | _etext = .; /* End of text and rodata section */ |
146 | 157 | ||
147 | #ifndef CONFIG_XIP_KERNEL | 158 | #ifndef CONFIG_XIP_KERNEL |
159 | # ifdef CONFIG_ARM_KERNMEM_PERMS | ||
160 | . = ALIGN(1<<SECTION_SHIFT); | ||
161 | # else | ||
148 | . = ALIGN(PAGE_SIZE); | 162 | . = ALIGN(PAGE_SIZE); |
163 | # endif | ||
149 | __init_begin = .; | 164 | __init_begin = .; |
150 | #endif | 165 | #endif |
151 | /* | 166 | /* |
@@ -219,7 +234,11 @@ SECTIONS | |||
219 | __data_loc = ALIGN(4); /* location in binary */ | 234 | __data_loc = ALIGN(4); /* location in binary */ |
220 | . = PAGE_OFFSET + TEXT_OFFSET; | 235 | . = PAGE_OFFSET + TEXT_OFFSET; |
221 | #else | 236 | #else |
237 | #ifdef CONFIG_ARM_KERNMEM_PERMS | ||
238 | . = ALIGN(1<<SECTION_SHIFT); | ||
239 | #else | ||
222 | . = ALIGN(THREAD_SIZE); | 240 | . = ALIGN(THREAD_SIZE); |
241 | #endif | ||
223 | __init_end = .; | 242 | __init_end = .; |
224 | __data_loc = .; | 243 | __data_loc = .; |
225 | #endif | 244 | #endif |
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 7eb94e6fc376..bc219b303bc7 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig | |||
@@ -1009,3 +1009,24 @@ config ARCH_SUPPORTS_BIG_ENDIAN | |||
1009 | help | 1009 | help |
1010 | This option specifies the architecture can support big endian | 1010 | This option specifies the architecture can support big endian |
1011 | operation. | 1011 | operation. |
1012 | |||
1013 | config ARM_KERNMEM_PERMS | ||
1014 | bool "Restrict kernel memory permissions" | ||
1015 | help | ||
1016 | If this is set, kernel memory other than kernel text (and rodata) | ||
1017 | will be made non-executable. The tradeoff is that each region is | ||
1018 | padded to section-size (1MiB) boundaries (because their permissions | ||
1019 | are different and splitting the 1M pages into 4K ones causes TLB | ||
1020 | performance problems), wasting memory. | ||
1021 | |||
1022 | config DEBUG_RODATA | ||
1023 | bool "Make kernel text and rodata read-only" | ||
1024 | depends on ARM_KERNMEM_PERMS | ||
1025 | default y | ||
1026 | help | ||
1027 | If this is set, kernel text and rodata will be made read-only. This | ||
1028 | is to help catch accidental or malicious attempts to change the | ||
1029 | kernel's executable code. Additionally splits rodata from kernel | ||
1030 | text so it can be made explicitly non-executable. This creates | ||
1031 | another section-size padded region, so it can waste more memory | ||
1032 | space while gaining the read-only protections. | ||
diff --git a/arch/arm/mm/highmem.c b/arch/arm/mm/highmem.c index e17ed00828d7..b98895d9fe57 100644 --- a/arch/arm/mm/highmem.c +++ b/arch/arm/mm/highmem.c | |||
@@ -18,19 +18,20 @@ | |||
18 | #include <asm/tlbflush.h> | 18 | #include <asm/tlbflush.h> |
19 | #include "mm.h" | 19 | #include "mm.h" |
20 | 20 | ||
21 | pte_t *fixmap_page_table; | ||
22 | |||
23 | static inline void set_fixmap_pte(int idx, pte_t pte) | 21 | static inline void set_fixmap_pte(int idx, pte_t pte) |
24 | { | 22 | { |
25 | unsigned long vaddr = __fix_to_virt(idx); | 23 | unsigned long vaddr = __fix_to_virt(idx); |
26 | set_pte_ext(fixmap_page_table + idx, pte, 0); | 24 | pte_t *ptep = pte_offset_kernel(pmd_off_k(vaddr), vaddr); |
25 | |||
26 | set_pte_ext(ptep, pte, 0); | ||
27 | local_flush_tlb_kernel_page(vaddr); | 27 | local_flush_tlb_kernel_page(vaddr); |
28 | } | 28 | } |
29 | 29 | ||
30 | static inline pte_t get_fixmap_pte(unsigned long vaddr) | 30 | static inline pte_t get_fixmap_pte(unsigned long vaddr) |
31 | { | 31 | { |
32 | unsigned long idx = __virt_to_fix(vaddr); | 32 | pte_t *ptep = pte_offset_kernel(pmd_off_k(vaddr), vaddr); |
33 | return *(fixmap_page_table + idx); | 33 | |
34 | return *ptep; | ||
34 | } | 35 | } |
35 | 36 | ||
36 | void *kmap(struct page *page) | 37 | void *kmap(struct page *page) |
@@ -84,7 +85,7 @@ void *kmap_atomic(struct page *page) | |||
84 | * With debugging enabled, kunmap_atomic forces that entry to 0. | 85 | * With debugging enabled, kunmap_atomic forces that entry to 0. |
85 | * Make sure it was indeed properly unmapped. | 86 | * Make sure it was indeed properly unmapped. |
86 | */ | 87 | */ |
87 | BUG_ON(!pte_none(*(fixmap_page_table + idx))); | 88 | BUG_ON(!pte_none(get_fixmap_pte(vaddr))); |
88 | #endif | 89 | #endif |
89 | /* | 90 | /* |
90 | * When debugging is off, kunmap_atomic leaves the previous mapping | 91 | * When debugging is off, kunmap_atomic leaves the previous mapping |
@@ -137,7 +138,7 @@ void *kmap_atomic_pfn(unsigned long pfn) | |||
137 | idx = type + KM_TYPE_NR * smp_processor_id(); | 138 | idx = type + KM_TYPE_NR * smp_processor_id(); |
138 | vaddr = __fix_to_virt(idx); | 139 | vaddr = __fix_to_virt(idx); |
139 | #ifdef CONFIG_DEBUG_HIGHMEM | 140 | #ifdef CONFIG_DEBUG_HIGHMEM |
140 | BUG_ON(!pte_none(*(fixmap_page_table + idx))); | 141 | BUG_ON(!pte_none(get_fixmap_pte(vaddr))); |
141 | #endif | 142 | #endif |
142 | set_fixmap_pte(idx, pfn_pte(pfn, kmap_prot)); | 143 | set_fixmap_pte(idx, pfn_pte(pfn, kmap_prot)); |
143 | 144 | ||
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 6ca53c338519..ba87b1b3565f 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <asm/prom.h> | 29 | #include <asm/prom.h> |
30 | #include <asm/sections.h> | 30 | #include <asm/sections.h> |
31 | #include <asm/setup.h> | 31 | #include <asm/setup.h> |
32 | #include <asm/system_info.h> | ||
32 | #include <asm/tlb.h> | 33 | #include <asm/tlb.h> |
33 | #include <asm/fixmap.h> | 34 | #include <asm/fixmap.h> |
34 | 35 | ||
@@ -570,7 +571,7 @@ void __init mem_init(void) | |||
570 | MLK(DTCM_OFFSET, (unsigned long) dtcm_end), | 571 | MLK(DTCM_OFFSET, (unsigned long) dtcm_end), |
571 | MLK(ITCM_OFFSET, (unsigned long) itcm_end), | 572 | MLK(ITCM_OFFSET, (unsigned long) itcm_end), |
572 | #endif | 573 | #endif |
573 | MLK(FIXADDR_START, FIXADDR_TOP), | 574 | MLK(FIXADDR_START, FIXADDR_END), |
574 | MLM(VMALLOC_START, VMALLOC_END), | 575 | MLM(VMALLOC_START, VMALLOC_END), |
575 | MLM(PAGE_OFFSET, (unsigned long)high_memory), | 576 | MLM(PAGE_OFFSET, (unsigned long)high_memory), |
576 | #ifdef CONFIG_HIGHMEM | 577 | #ifdef CONFIG_HIGHMEM |
@@ -615,7 +616,145 @@ void __init mem_init(void) | |||
615 | } | 616 | } |
616 | } | 617 | } |
617 | 618 | ||
618 | void free_initmem(void) | 619 | #ifdef CONFIG_ARM_KERNMEM_PERMS |
620 | struct section_perm { | ||
621 | unsigned long start; | ||
622 | unsigned long end; | ||
623 | pmdval_t mask; | ||
624 | pmdval_t prot; | ||
625 | pmdval_t clear; | ||
626 | }; | ||
627 | |||
628 | static struct section_perm nx_perms[] = { | ||
629 | /* Make pages tables, etc before _stext RW (set NX). */ | ||
630 | { | ||
631 | .start = PAGE_OFFSET, | ||
632 | .end = (unsigned long)_stext, | ||
633 | .mask = ~PMD_SECT_XN, | ||
634 | .prot = PMD_SECT_XN, | ||
635 | }, | ||
636 | /* Make init RW (set NX). */ | ||
637 | { | ||
638 | .start = (unsigned long)__init_begin, | ||
639 | .end = (unsigned long)_sdata, | ||
640 | .mask = ~PMD_SECT_XN, | ||
641 | .prot = PMD_SECT_XN, | ||
642 | }, | ||
643 | #ifdef CONFIG_DEBUG_RODATA | ||
644 | /* Make rodata NX (set RO in ro_perms below). */ | ||
645 | { | ||
646 | .start = (unsigned long)__start_rodata, | ||
647 | .end = (unsigned long)__init_begin, | ||
648 | .mask = ~PMD_SECT_XN, | ||
649 | .prot = PMD_SECT_XN, | ||
650 | }, | ||
651 | #endif | ||
652 | }; | ||
653 | |||
654 | #ifdef CONFIG_DEBUG_RODATA | ||
655 | static struct section_perm ro_perms[] = { | ||
656 | /* Make kernel code and rodata RX (set RO). */ | ||
657 | { | ||
658 | .start = (unsigned long)_stext, | ||
659 | .end = (unsigned long)__init_begin, | ||
660 | #ifdef CONFIG_ARM_LPAE | ||
661 | .mask = ~PMD_SECT_RDONLY, | ||
662 | .prot = PMD_SECT_RDONLY, | ||
663 | #else | ||
664 | .mask = ~(PMD_SECT_APX | PMD_SECT_AP_WRITE), | ||
665 | .prot = PMD_SECT_APX | PMD_SECT_AP_WRITE, | ||
666 | .clear = PMD_SECT_AP_WRITE, | ||
667 | #endif | ||
668 | }, | ||
669 | }; | ||
670 | #endif | ||
671 | |||
672 | /* | ||
673 | * Updates section permissions only for the current mm (sections are | ||
674 | * copied into each mm). During startup, this is the init_mm. Is only | ||
675 | * safe to be called with preemption disabled, as under stop_machine(). | ||
676 | */ | ||
677 | static inline void section_update(unsigned long addr, pmdval_t mask, | ||
678 | pmdval_t prot) | ||
679 | { | ||
680 | struct mm_struct *mm; | ||
681 | pmd_t *pmd; | ||
682 | |||
683 | mm = current->active_mm; | ||
684 | pmd = pmd_offset(pud_offset(pgd_offset(mm, addr), addr), addr); | ||
685 | |||
686 | #ifdef CONFIG_ARM_LPAE | ||
687 | pmd[0] = __pmd((pmd_val(pmd[0]) & mask) | prot); | ||
688 | #else | ||
689 | if (addr & SECTION_SIZE) | ||
690 | pmd[1] = __pmd((pmd_val(pmd[1]) & mask) | prot); | ||
691 | else | ||
692 | pmd[0] = __pmd((pmd_val(pmd[0]) & mask) | prot); | ||
693 | #endif | ||
694 | flush_pmd_entry(pmd); | ||
695 | local_flush_tlb_kernel_range(addr, addr + SECTION_SIZE); | ||
696 | } | ||
697 | |||
698 | /* Make sure extended page tables are in use. */ | ||
699 | static inline bool arch_has_strict_perms(void) | ||
700 | { | ||
701 | if (cpu_architecture() < CPU_ARCH_ARMv6) | ||
702 | return false; | ||
703 | |||
704 | return !!(get_cr() & CR_XP); | ||
705 | } | ||
706 | |||
707 | #define set_section_perms(perms, field) { \ | ||
708 | size_t i; \ | ||
709 | unsigned long addr; \ | ||
710 | \ | ||
711 | if (!arch_has_strict_perms()) \ | ||
712 | return; \ | ||
713 | \ | ||
714 | for (i = 0; i < ARRAY_SIZE(perms); i++) { \ | ||
715 | if (!IS_ALIGNED(perms[i].start, SECTION_SIZE) || \ | ||
716 | !IS_ALIGNED(perms[i].end, SECTION_SIZE)) { \ | ||
717 | pr_err("BUG: section %lx-%lx not aligned to %lx\n", \ | ||
718 | perms[i].start, perms[i].end, \ | ||
719 | SECTION_SIZE); \ | ||
720 | continue; \ | ||
721 | } \ | ||
722 | \ | ||
723 | for (addr = perms[i].start; \ | ||
724 | addr < perms[i].end; \ | ||
725 | addr += SECTION_SIZE) \ | ||
726 | section_update(addr, perms[i].mask, \ | ||
727 | perms[i].field); \ | ||
728 | } \ | ||
729 | } | ||
730 | |||
731 | static inline void fix_kernmem_perms(void) | ||
732 | { | ||
733 | set_section_perms(nx_perms, prot); | ||
734 | } | ||
735 | |||
736 | #ifdef CONFIG_DEBUG_RODATA | ||
737 | void mark_rodata_ro(void) | ||
738 | { | ||
739 | set_section_perms(ro_perms, prot); | ||
740 | } | ||
741 | |||
742 | void set_kernel_text_rw(void) | ||
743 | { | ||
744 | set_section_perms(ro_perms, clear); | ||
745 | } | ||
746 | |||
747 | void set_kernel_text_ro(void) | ||
748 | { | ||
749 | set_section_perms(ro_perms, prot); | ||
750 | } | ||
751 | #endif /* CONFIG_DEBUG_RODATA */ | ||
752 | |||
753 | #else | ||
754 | static inline void fix_kernmem_perms(void) { } | ||
755 | #endif /* CONFIG_ARM_KERNMEM_PERMS */ | ||
756 | |||
757 | void free_tcmmem(void) | ||
619 | { | 758 | { |
620 | #ifdef CONFIG_HAVE_TCM | 759 | #ifdef CONFIG_HAVE_TCM |
621 | extern char __tcm_start, __tcm_end; | 760 | extern char __tcm_start, __tcm_end; |
@@ -623,6 +762,12 @@ void free_initmem(void) | |||
623 | poison_init_mem(&__tcm_start, &__tcm_end - &__tcm_start); | 762 | poison_init_mem(&__tcm_start, &__tcm_end - &__tcm_start); |
624 | free_reserved_area(&__tcm_start, &__tcm_end, -1, "TCM link"); | 763 | free_reserved_area(&__tcm_start, &__tcm_end, -1, "TCM link"); |
625 | #endif | 764 | #endif |
765 | } | ||
766 | |||
767 | void free_initmem(void) | ||
768 | { | ||
769 | fix_kernmem_perms(); | ||
770 | free_tcmmem(); | ||
626 | 771 | ||
627 | poison_init_mem(__init_begin, __init_end - __init_begin); | 772 | poison_init_mem(__init_begin, __init_end - __init_begin); |
628 | if (!machine_is_integrator() && !machine_is_cintegrator()) | 773 | if (!machine_is_integrator() && !machine_is_cintegrator()) |
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index f86ce1a9f525..cda7c40999b6 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <asm/cputype.h> | 22 | #include <asm/cputype.h> |
23 | #include <asm/sections.h> | 23 | #include <asm/sections.h> |
24 | #include <asm/cachetype.h> | 24 | #include <asm/cachetype.h> |
25 | #include <asm/fixmap.h> | ||
25 | #include <asm/sections.h> | 26 | #include <asm/sections.h> |
26 | #include <asm/setup.h> | 27 | #include <asm/setup.h> |
27 | #include <asm/smp_plat.h> | 28 | #include <asm/smp_plat.h> |
@@ -357,6 +358,29 @@ const struct mem_type *get_mem_type(unsigned int type) | |||
357 | EXPORT_SYMBOL(get_mem_type); | 358 | EXPORT_SYMBOL(get_mem_type); |
358 | 359 | ||
359 | /* | 360 | /* |
361 | * To avoid TLB flush broadcasts, this uses local_flush_tlb_kernel_range(). | ||
362 | * As a result, this can only be called with preemption disabled, as under | ||
363 | * stop_machine(). | ||
364 | */ | ||
365 | void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot) | ||
366 | { | ||
367 | unsigned long vaddr = __fix_to_virt(idx); | ||
368 | pte_t *pte = pte_offset_kernel(pmd_off_k(vaddr), vaddr); | ||
369 | |||
370 | /* Make sure fixmap region does not exceed available allocation. */ | ||
371 | BUILD_BUG_ON(FIXADDR_START + (__end_of_fixed_addresses * PAGE_SIZE) > | ||
372 | FIXADDR_END); | ||
373 | BUG_ON(idx >= __end_of_fixed_addresses); | ||
374 | |||
375 | if (pgprot_val(prot)) | ||
376 | set_pte_at(NULL, vaddr, pte, | ||
377 | pfn_pte(phys >> PAGE_SHIFT, prot)); | ||
378 | else | ||
379 | pte_clear(NULL, vaddr, pte); | ||
380 | local_flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE); | ||
381 | } | ||
382 | |||
383 | /* | ||
360 | * Adjust the PMD section entries according to the CPU in use. | 384 | * Adjust the PMD section entries according to the CPU in use. |
361 | */ | 385 | */ |
362 | static void __init build_mem_type_table(void) | 386 | static void __init build_mem_type_table(void) |
@@ -1296,10 +1320,10 @@ static void __init kmap_init(void) | |||
1296 | #ifdef CONFIG_HIGHMEM | 1320 | #ifdef CONFIG_HIGHMEM |
1297 | pkmap_page_table = early_pte_alloc(pmd_off_k(PKMAP_BASE), | 1321 | pkmap_page_table = early_pte_alloc(pmd_off_k(PKMAP_BASE), |
1298 | PKMAP_BASE, _PAGE_KERNEL_TABLE); | 1322 | PKMAP_BASE, _PAGE_KERNEL_TABLE); |
1299 | |||
1300 | fixmap_page_table = early_pte_alloc(pmd_off_k(FIXADDR_START), | ||
1301 | FIXADDR_START, _PAGE_KERNEL_TABLE); | ||
1302 | #endif | 1323 | #endif |
1324 | |||
1325 | early_pte_alloc(pmd_off_k(FIXADDR_START), FIXADDR_START, | ||
1326 | _PAGE_KERNEL_TABLE); | ||
1303 | } | 1327 | } |
1304 | 1328 | ||
1305 | static void __init map_lowmem(void) | 1329 | static void __init map_lowmem(void) |
@@ -1319,13 +1343,20 @@ static void __init map_lowmem(void) | |||
1319 | if (start >= end) | 1343 | if (start >= end) |
1320 | break; | 1344 | break; |
1321 | 1345 | ||
1322 | if (end < kernel_x_start || start >= kernel_x_end) { | 1346 | if (end < kernel_x_start) { |
1323 | map.pfn = __phys_to_pfn(start); | 1347 | map.pfn = __phys_to_pfn(start); |
1324 | map.virtual = __phys_to_virt(start); | 1348 | map.virtual = __phys_to_virt(start); |
1325 | map.length = end - start; | 1349 | map.length = end - start; |
1326 | map.type = MT_MEMORY_RWX; | 1350 | map.type = MT_MEMORY_RWX; |
1327 | 1351 | ||
1328 | create_mapping(&map); | 1352 | create_mapping(&map); |
1353 | } else if (start >= kernel_x_end) { | ||
1354 | map.pfn = __phys_to_pfn(start); | ||
1355 | map.virtual = __phys_to_virt(start); | ||
1356 | map.length = end - start; | ||
1357 | map.type = MT_MEMORY_RW; | ||
1358 | |||
1359 | create_mapping(&map); | ||
1329 | } else { | 1360 | } else { |
1330 | /* This better cover the entire kernel */ | 1361 | /* This better cover the entire kernel */ |
1331 | if (start < kernel_x_start) { | 1362 | if (start < kernel_x_start) { |