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 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 36*4735037bSchenmiao static void patch_unmap(int fixmap) 37*4735037bSchenmiao { 38*4735037bSchenmiao clear_fixmap(fixmap); 39*4735037bSchenmiao } 40*4735037bSchenmiao 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 */ 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