xref: /linux-6.15/arch/parisc/kernel/patch.c (revision 4e87ace9)
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 	u32 *insn;
21 	unsigned int len;
22 };
23 
24 static DEFINE_RAW_SPINLOCK(patch_lock);
25 
26 static void __kprobes *patch_map(void *addr, int fixmap, int *need_unmap)
27 	unsigned long uintaddr = (uintptr_t) addr;
28 	bool module = !core_kernel_text(uintaddr);
29 	struct page *page;
30 
31 	*need_unmap = 0;
32 	if (module && IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
33 		page = vmalloc_to_page(addr);
34 	else if (!module && IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))
35 		page = virt_to_page(addr);
36 	else
37 		return addr;
38 
39 	*need_unmap = 1;
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)
46 {
47 	clear_fixmap(fixmap);
48 }
49 
50 void __kprobes __patch_text_multiple(void *addr, u32 *insn, unsigned int len)
51 {
52 	unsigned long start = (unsigned long)addr;
53 	unsigned long end = (unsigned long)addr + len;
54 	u32 *p, *fixmap;
55 	int mapped;
56 
57 	/* Make sure we don't have any aliases in cache */
58 	flush_kernel_vmap_range(addr, len);
59 	flush_icache_range(start, end);
60 
61 	p = fixmap = patch_map(addr, FIX_TEXT_POKE0, &mapped);
62 
63 	while (len >= 4) {
64 		*p++ = *insn++;
65 		addr += sizeof(u32);
66 		len -= sizeof(u32);
67 		if (len && offset_in_page(addr) == 0) {
68 			/*
69 			 * We're crossing a page boundary, so
70 			 * need to remap
71 			 */
72 			flush_kernel_vmap_range((void *)fixmap,
73 						(p-fixmap) * sizeof(*p));
74 			if (mapped)
75 				patch_unmap(FIX_TEXT_POKE0);
76 			p = fixmap = patch_map(addr, FIX_TEXT_POKE0, &mapped);
77 		}
78 	}
79 
80 	flush_kernel_vmap_range((void *)fixmap, (p-fixmap) * sizeof(*p));
81 	if (mapped)
82 		patch_unmap(FIX_TEXT_POKE0);
83 	flush_icache_range(start, end);
84 }
85 
86 void __kprobes __patch_text(void *addr, u32 insn)
87 {
88 	__patch_text_multiple(addr, &insn, sizeof(insn));
89 }
90 
91 static int __kprobes patch_text_stop_machine(void *data)
92 {
93 	struct patch *patch = data;
94 
95 	__patch_text_multiple(patch->addr, patch->insn, patch->len);
96 	return 0;
97 }
98 
99 void __kprobes patch_text(void *addr, unsigned int insn)
100 {
101 	struct patch patch = {
102 		.addr = addr,
103 		.insn = &insn,
104 		.len = sizeof(insn),
105 	};
106 
107 	stop_machine_cpuslocked(patch_text_stop_machine, &patch, NULL);
108 }
109 
110 void __kprobes patch_text_multiple(void *addr, u32 *insn, unsigned int len)
111 {
112 
113 	struct patch patch = {
114 		.addr = addr,
115 		.insn = insn,
116 		.len = len
117 	};
118 
119 	stop_machine_cpuslocked(patch_text_stop_machine, &patch, NULL);
120 }
121