1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/kernel.h>
3 #include <linux/spinlock.h>
4 #include <linux/kprobes.h>
6 #include <linux/stop_machine.h>
8 #include <asm/cacheflush.h>
9 #include <asm/fixmap.h>
10 #include <asm/smp_plat.h>
11 #include <asm/opcodes.h>
12 #include <asm/patch.h>
20 static DEFINE_RAW_SPINLOCK(patch_lock
);
22 static void __kprobes
*patch_map(void *addr
, int fixmap
, unsigned long *flags
)
24 unsigned int uintaddr
= (uintptr_t) addr
;
25 bool module
= !core_kernel_text(uintaddr
);
28 if (module
&& IS_ENABLED(CONFIG_STRICT_MODULE_RWX
))
29 page
= vmalloc_to_page(addr
);
30 else if (!module
&& IS_ENABLED(CONFIG_STRICT_KERNEL_RWX
))
31 page
= virt_to_page(addr
);
36 raw_spin_lock_irqsave(&patch_lock
, *flags
);
38 set_fixmap(fixmap
, page_to_phys(page
));
40 return (void *) (__fix_to_virt(fixmap
) + (uintaddr
& ~PAGE_MASK
));
43 static void __kprobes
patch_unmap(int fixmap
, unsigned long *flags
)
48 raw_spin_unlock_irqrestore(&patch_lock
, *flags
);
51 static void __kprobes
*patch_map(void *addr
, int fixmap
, unsigned long *flags
)
55 static void __kprobes
patch_unmap(int fixmap
, unsigned long *flags
) { }
58 void __kprobes
__patch_text_real(void *addr
, unsigned int insn
, bool remap
)
60 bool thumb2
= IS_ENABLED(CONFIG_THUMB2_KERNEL
);
61 unsigned int uintaddr
= (uintptr_t) addr
;
68 waddr
= patch_map(addr
, FIX_TEXT_POKE0
, &flags
);
70 if (thumb2
&& __opcode_is_thumb16(insn
)) {
71 *(u16
*)waddr
= __opcode_to_mem_thumb16(insn
);
73 } else if (thumb2
&& (uintaddr
& 2)) {
74 u16 first
= __opcode_thumb32_first(insn
);
75 u16 second
= __opcode_thumb32_second(insn
);
77 u16
*addrh1
= waddr
+ 2;
79 twopage
= (uintaddr
& ~PAGE_MASK
) == PAGE_SIZE
- 2;
81 addrh1
= patch_map(addr
+ 2, FIX_TEXT_POKE1
, NULL
);
83 *addrh0
= __opcode_to_mem_thumb16(first
);
84 *addrh1
= __opcode_to_mem_thumb16(second
);
86 if (twopage
&& addrh1
!= addr
+ 2) {
87 flush_kernel_vmap_range(addrh1
, 2);
88 patch_unmap(FIX_TEXT_POKE1
, NULL
);
94 insn
= __opcode_to_mem_thumb32(insn
);
96 insn
= __opcode_to_mem_arm(insn
);
103 flush_kernel_vmap_range(waddr
, twopage
? size
/ 2 : size
);
104 patch_unmap(FIX_TEXT_POKE0
, &flags
);
107 flush_icache_range((uintptr_t)(addr
),
108 (uintptr_t)(addr
) + size
);
111 static int __kprobes
patch_text_stop_machine(void *data
)
113 struct patch
*patch
= data
;
115 __patch_text(patch
->addr
, patch
->insn
);
120 void __kprobes
patch_text(void *addr
, unsigned int insn
)
122 struct patch patch
= {
127 stop_machine_cpuslocked(patch_text_stop_machine
, &patch
, NULL
);