1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2020 SiFive
6 #include <linux/spinlock.h>
8 #include <linux/uaccess.h>
9 #include <linux/stop_machine.h>
10 #include <asm/kprobes.h>
11 #include <asm/cacheflush.h>
12 #include <asm/fixmap.h>
14 struct riscv_insn_patch
{
21 static DEFINE_RAW_SPINLOCK(patch_lock
);
23 static void __kprobes
*patch_map(void *addr
, int fixmap
)
25 uintptr_t uintaddr
= (uintptr_t) addr
;
28 if (core_kernel_text(uintaddr
))
29 page
= phys_to_page(__pa_symbol(addr
));
30 else if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX
))
31 page
= vmalloc_to_page(addr
);
37 return (void *)set_fixmap_offset(fixmap
, page_to_phys(page
) +
38 (uintaddr
& ~PAGE_MASK
));
41 static void __kprobes
patch_unmap(int fixmap
)
46 static int __kprobes
riscv_insn_write(void *addr
, const void *insn
, size_t len
)
49 bool across_pages
= (((uintptr_t) addr
& ~PAGE_MASK
) + len
) > PAGE_SIZE
;
50 unsigned long flags
= 0;
53 raw_spin_lock_irqsave(&patch_lock
, flags
);
56 patch_map(addr
+ len
, FIX_TEXT_POKE1
);
58 waddr
= patch_map(addr
, FIX_TEXT_POKE0
);
60 ret
= probe_kernel_write(waddr
, insn
, len
);
62 patch_unmap(FIX_TEXT_POKE0
);
65 patch_unmap(FIX_TEXT_POKE1
);
67 raw_spin_unlock_irqrestore(&patch_lock
, flags
);
72 static int __kprobes
riscv_insn_write(void *addr
, const void *insn
, size_t len
)
74 return probe_kernel_write(addr
, insn
, len
);
76 #endif /* CONFIG_MMU */
78 int __kprobes
riscv_patch_text_nosync(void *addr
, const void *insns
, size_t len
)
83 ret
= riscv_insn_write(tp
, insns
, len
);
86 flush_icache_range((uintptr_t) tp
, (uintptr_t) tp
+ len
);
91 static int __kprobes
riscv_patch_text_cb(void *data
)
93 struct riscv_insn_patch
*patch
= data
;
96 if (atomic_inc_return(&patch
->cpu_count
) == 1) {
98 riscv_patch_text_nosync(patch
->addr
, &patch
->insn
,
99 GET_INSN_LENGTH(patch
->insn
));
100 atomic_inc(&patch
->cpu_count
);
102 while (atomic_read(&patch
->cpu_count
) <= num_online_cpus())
110 int __kprobes
riscv_patch_text(void *addr
, u32 insn
)
112 struct riscv_insn_patch patch
= {
115 .cpu_count
= ATOMIC_INIT(0),
118 return stop_machine_cpuslocked(riscv_patch_text_cb
,
119 &patch
, cpu_online_mask
);