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