xref: /openbmc/linux/arch/parisc/kernel/patch.c (revision ff148d8a)
1 // SPDX-License-Identifier: GPL-2.0
2  /*
3   * functions to patch RO kernel text during runtime
4   *
5   * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org>
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)
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)
42 {
43 	clear_fixmap(fixmap);
44 }
45 
46 void __kprobes __patch_text(void *addr, unsigned int insn)
47 {
48 	void *waddr = addr;
49 	int size;
50 
51 	waddr = patch_map(addr, FIX_TEXT_POKE0);
52 	*(u32 *)waddr = insn;
53 	size = sizeof(u32);
54 	flush_kernel_vmap_range(waddr, size);
55 	patch_unmap(FIX_TEXT_POKE0);
56 	flush_icache_range((uintptr_t)(addr),
57 			   (uintptr_t)(addr) + size);
58 }
59 
60 static int __kprobes patch_text_stop_machine(void *data)
61 {
62 	struct patch *patch = data;
63 
64 	__patch_text(patch->addr, patch->insn);
65 
66 	return 0;
67 }
68 
69 void __kprobes patch_text(void *addr, unsigned int insn)
70 {
71 	struct patch patch = {
72 		.addr = addr,
73 		.insn = insn,
74 	};
75 
76 	stop_machine_cpuslocked(patch_text_stop_machine, &patch, NULL);
77 }
78