]>
Commit | Line | Data |
---|---|---|
b21d55e9 | 1 | #include <linux/kernel.h> |
ab0615e2 | 2 | #include <linux/spinlock.h> |
b21d55e9 | 3 | #include <linux/kprobes.h> |
ab0615e2 | 4 | #include <linux/mm.h> |
b21d55e9 RV |
5 | #include <linux/stop_machine.h> |
6 | ||
7 | #include <asm/cacheflush.h> | |
ab0615e2 | 8 | #include <asm/fixmap.h> |
b21d55e9 RV |
9 | #include <asm/smp_plat.h> |
10 | #include <asm/opcodes.h> | |
fca08f32 | 11 | #include <asm/patch.h> |
b21d55e9 RV |
12 | |
13 | struct patch { | |
14 | void *addr; | |
15 | unsigned int insn; | |
16 | }; | |
17 | ||
a0594831 | 18 | static DEFINE_RAW_SPINLOCK(patch_lock); |
ab0615e2 RV |
19 | |
20 | static void __kprobes *patch_map(void *addr, int fixmap, unsigned long *flags) | |
21 | __acquires(&patch_lock) | |
22 | { | |
23 | unsigned int uintaddr = (uintptr_t) addr; | |
24 | bool module = !core_kernel_text(uintaddr); | |
25 | struct page *page; | |
26 | ||
27 | if (module && IS_ENABLED(CONFIG_DEBUG_SET_MODULE_RONX)) | |
28 | page = vmalloc_to_page(addr); | |
29 | else if (!module && IS_ENABLED(CONFIG_DEBUG_RODATA)) | |
30 | page = virt_to_page(addr); | |
31 | else | |
32 | return addr; | |
33 | ||
34 | if (flags) | |
a0594831 | 35 | raw_spin_lock_irqsave(&patch_lock, *flags); |
ab0615e2 RV |
36 | else |
37 | __acquire(&patch_lock); | |
38 | ||
39 | set_fixmap(fixmap, page_to_phys(page)); | |
40 | ||
41 | return (void *) (__fix_to_virt(fixmap) + (uintaddr & ~PAGE_MASK)); | |
42 | } | |
43 | ||
44 | static void __kprobes patch_unmap(int fixmap, unsigned long *flags) | |
45 | __releases(&patch_lock) | |
46 | { | |
47 | clear_fixmap(fixmap); | |
48 | ||
49 | if (flags) | |
a0594831 | 50 | raw_spin_unlock_irqrestore(&patch_lock, *flags); |
ab0615e2 RV |
51 | else |
52 | __release(&patch_lock); | |
53 | } | |
54 | ||
55 | void __kprobes __patch_text_real(void *addr, unsigned int insn, bool remap) | |
b21d55e9 RV |
56 | { |
57 | bool thumb2 = IS_ENABLED(CONFIG_THUMB2_KERNEL); | |
ab0615e2 RV |
58 | unsigned int uintaddr = (uintptr_t) addr; |
59 | bool twopage = false; | |
60 | unsigned long flags; | |
61 | void *waddr = addr; | |
b21d55e9 RV |
62 | int size; |
63 | ||
ab0615e2 RV |
64 | if (remap) |
65 | waddr = patch_map(addr, FIX_TEXT_POKE0, &flags); | |
66 | else | |
67 | __acquire(&patch_lock); | |
68 | ||
b21d55e9 | 69 | if (thumb2 && __opcode_is_thumb16(insn)) { |
ab0615e2 | 70 | *(u16 *)waddr = __opcode_to_mem_thumb16(insn); |
b21d55e9 | 71 | size = sizeof(u16); |
ab0615e2 | 72 | } else if (thumb2 && (uintaddr & 2)) { |
b21d55e9 RV |
73 | u16 first = __opcode_thumb32_first(insn); |
74 | u16 second = __opcode_thumb32_second(insn); | |
ab0615e2 RV |
75 | u16 *addrh0 = waddr; |
76 | u16 *addrh1 = waddr + 2; | |
77 | ||
78 | twopage = (uintaddr & ~PAGE_MASK) == PAGE_SIZE - 2; | |
79 | if (twopage && remap) | |
80 | addrh1 = patch_map(addr + 2, FIX_TEXT_POKE1, NULL); | |
81 | ||
82 | *addrh0 = __opcode_to_mem_thumb16(first); | |
83 | *addrh1 = __opcode_to_mem_thumb16(second); | |
b21d55e9 | 84 | |
ab0615e2 RV |
85 | if (twopage && addrh1 != addr + 2) { |
86 | flush_kernel_vmap_range(addrh1, 2); | |
87 | patch_unmap(FIX_TEXT_POKE1, NULL); | |
88 | } | |
b21d55e9 RV |
89 | |
90 | size = sizeof(u32); | |
91 | } else { | |
92 | if (thumb2) | |
93 | insn = __opcode_to_mem_thumb32(insn); | |
94 | else | |
95 | insn = __opcode_to_mem_arm(insn); | |
96 | ||
ab0615e2 | 97 | *(u32 *)waddr = insn; |
b21d55e9 RV |
98 | size = sizeof(u32); |
99 | } | |
100 | ||
ab0615e2 RV |
101 | if (waddr != addr) { |
102 | flush_kernel_vmap_range(waddr, twopage ? size / 2 : size); | |
103 | patch_unmap(FIX_TEXT_POKE0, &flags); | |
104 | } else | |
105 | __release(&patch_lock); | |
106 | ||
b21d55e9 RV |
107 | flush_icache_range((uintptr_t)(addr), |
108 | (uintptr_t)(addr) + size); | |
109 | } | |
110 | ||
111 | static int __kprobes patch_text_stop_machine(void *data) | |
112 | { | |
113 | struct patch *patch = data; | |
114 | ||
115 | __patch_text(patch->addr, patch->insn); | |
116 | ||
117 | return 0; | |
118 | } | |
119 | ||
120 | void __kprobes patch_text(void *addr, unsigned int insn) | |
121 | { | |
122 | struct patch patch = { | |
123 | .addr = addr, | |
124 | .insn = insn, | |
125 | }; | |
126 | ||
ab0615e2 | 127 | stop_machine(patch_text_stop_machine, &patch, NULL); |
b21d55e9 | 128 | } |