xref: /linux-6.15/arch/parisc/kernel/patch.c (revision 376e5fd7)
1 // SPDX-License-Identifier: GPL-2.0
2  /*
3   * functions to patch RO kernel text during runtime
4   *
5   * Copyright (c) 2019 Sven Schnelle <[email protected]>
6   */
7 
8 #include <linux/kernel.h>
9 #include <linux/spinlock.h>
10 #include <linux/kprobes.h>
11 #include <linux/mm.h>
12 #include <linux/stop_machine.h>
13 
14 #include <asm/cacheflush.h>
15 #include <asm/fixmap.h>
16 #include <asm/patch.h>
17 
18 struct patch {
19 	void *addr;
20 	unsigned int insn;
21 };
22 
23 static void __kprobes *patch_map(void *addr, int fixmap, unsigned long *flags)
24 {
25 	unsigned long uintaddr = (uintptr_t) addr;
26 	bool module = !core_kernel_text(uintaddr);
27 	struct page *page;
28 
29 	if (module && IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
30 		page = vmalloc_to_page(addr);
31 	else if (!module && IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))
32 		page = virt_to_page(addr);
33 	else
34 		return addr;
35 
36 	set_fixmap(fixmap, page_to_phys(page));
37 
38 	return (void *) (__fix_to_virt(fixmap) + (uintaddr & ~PAGE_MASK));
39 }
40 
41 static void __kprobes patch_unmap(int fixmap, unsigned long *flags)
42 {
43 	clear_fixmap(fixmap);
44 }
45 
46 void __kprobes __patch_text(void *addr, unsigned int insn)
47 {
48 	unsigned long flags;
49 	void *waddr = addr;
50 	int size;
51 
52 	waddr = patch_map(addr, FIX_TEXT_POKE0, &flags);
53 	*(u32 *)waddr = insn;
54 	size = sizeof(u32);
55 	flush_kernel_vmap_range(waddr, size);
56 	patch_unmap(FIX_TEXT_POKE0, &flags);
57 	flush_icache_range((uintptr_t)(addr),
58 			   (uintptr_t)(addr) + size);
59 }
60 
61 static int __kprobes patch_text_stop_machine(void *data)
62 {
63 	struct patch *patch = data;
64 
65 	__patch_text(patch->addr, patch->insn);
66 
67 	return 0;
68 }
69 
70 void __kprobes patch_text(void *addr, unsigned int insn)
71 {
72 	struct patch patch = {
73 		.addr = addr,
74 		.insn = insn,
75 	};
76 
77 	stop_machine_cpuslocked(patch_text_stop_machine, &patch, NULL);
78 }
79