xref: /linux/arch/openrisc/kernel/patching.c (revision 742adaa16db994ba1748465b95548e2f28aa18ca)
1*4735037bSchenmiao // SPDX-License-Identifier: GPL-2.0-only
2*4735037bSchenmiao /* Copyright (C) 2020 SiFive
3*4735037bSchenmiao  * Copyright (C) 2025 Chen Miao
4*4735037bSchenmiao  */
5*4735037bSchenmiao 
6*4735037bSchenmiao #include <linux/mm.h>
7*4735037bSchenmiao #include <linux/kernel.h>
8*4735037bSchenmiao #include <linux/spinlock.h>
9*4735037bSchenmiao #include <linux/uaccess.h>
10*4735037bSchenmiao 
11*4735037bSchenmiao #include <asm/insn-def.h>
12*4735037bSchenmiao #include <asm/cacheflush.h>
13*4735037bSchenmiao #include <asm/page.h>
14*4735037bSchenmiao #include <asm/fixmap.h>
15*4735037bSchenmiao #include <asm/text-patching.h>
16*4735037bSchenmiao #include <asm/sections.h>
17*4735037bSchenmiao 
18*4735037bSchenmiao static DEFINE_RAW_SPINLOCK(patch_lock);
19*4735037bSchenmiao 
patch_map(void * addr,int fixmap)20*4735037bSchenmiao static __always_inline void *patch_map(void *addr, int fixmap)
21*4735037bSchenmiao {
22*4735037bSchenmiao 	uintptr_t uaddr = (uintptr_t) addr;
23*4735037bSchenmiao 	phys_addr_t phys;
24*4735037bSchenmiao 
25*4735037bSchenmiao 	if (core_kernel_text(uaddr)) {
26*4735037bSchenmiao 		phys = __pa_symbol(addr);
27*4735037bSchenmiao 	} else {
28*4735037bSchenmiao 		struct page *page = vmalloc_to_page(addr);
29*4735037bSchenmiao 		BUG_ON(!page);
30*4735037bSchenmiao 		phys = page_to_phys(page) + offset_in_page(addr);
31*4735037bSchenmiao 	}
32*4735037bSchenmiao 
33*4735037bSchenmiao 	return (void *)set_fixmap_offset(fixmap, phys);
34*4735037bSchenmiao }
35*4735037bSchenmiao 
patch_unmap(int fixmap)36*4735037bSchenmiao static void patch_unmap(int fixmap)
37*4735037bSchenmiao {
38*4735037bSchenmiao 	clear_fixmap(fixmap);
39*4735037bSchenmiao }
40*4735037bSchenmiao 
__patch_insn_write(void * addr,u32 insn)41*4735037bSchenmiao static int __patch_insn_write(void *addr, u32 insn)
42*4735037bSchenmiao {
43*4735037bSchenmiao 	void *waddr = addr;
44*4735037bSchenmiao 	unsigned long flags = 0;
45*4735037bSchenmiao 	int ret;
46*4735037bSchenmiao 
47*4735037bSchenmiao 	raw_spin_lock_irqsave(&patch_lock, flags);
48*4735037bSchenmiao 
49*4735037bSchenmiao 	waddr = patch_map(addr, FIX_TEXT_POKE0);
50*4735037bSchenmiao 
51*4735037bSchenmiao 	ret = copy_to_kernel_nofault(waddr, &insn, OPENRISC_INSN_SIZE);
52*4735037bSchenmiao 	local_icache_range_inv((unsigned long)waddr,
53*4735037bSchenmiao 			       (unsigned long)waddr + OPENRISC_INSN_SIZE);
54*4735037bSchenmiao 
55*4735037bSchenmiao 	patch_unmap(FIX_TEXT_POKE0);
56*4735037bSchenmiao 
57*4735037bSchenmiao 	raw_spin_unlock_irqrestore(&patch_lock, flags);
58*4735037bSchenmiao 
59*4735037bSchenmiao 	return ret;
60*4735037bSchenmiao }
61*4735037bSchenmiao 
62*4735037bSchenmiao /*
63*4735037bSchenmiao  * patch_insn_write - Write a single instruction to a specified memory location
64*4735037bSchenmiao  * This API provides a single-instruction patching, primarily used for runtime
65*4735037bSchenmiao  * code modification.
66*4735037bSchenmiao  * By the way, the insn size must be 4 bytes.
67*4735037bSchenmiao  */
patch_insn_write(void * addr,u32 insn)68*4735037bSchenmiao int patch_insn_write(void *addr, u32 insn)
69*4735037bSchenmiao {
70*4735037bSchenmiao 	u32 *tp = addr;
71*4735037bSchenmiao 	int ret;
72*4735037bSchenmiao 
73*4735037bSchenmiao 	if ((uintptr_t) tp & 0x3)
74*4735037bSchenmiao 		return -EINVAL;
75*4735037bSchenmiao 
76*4735037bSchenmiao 	ret = __patch_insn_write(tp, insn);
77*4735037bSchenmiao 
78*4735037bSchenmiao 	return ret;
79*4735037bSchenmiao }
80