2020-03-10 00:55:43 +08:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright ( C ) 2020 SiFive
*/
# include <linux/spinlock.h>
# include <linux/mm.h>
2020-04-21 15:30:01 +08:00
# include <linux/memory.h>
2023-08-31 13:12:28 +00:00
# include <linux/string.h>
2020-03-10 00:55:43 +08:00
# include <linux/uaccess.h>
# include <linux/stop_machine.h>
# include <asm/kprobes.h>
# include <asm/cacheflush.h>
# include <asm/fixmap.h>
2023-03-03 14:37:55 +00:00
# include <asm/ftrace.h>
2020-06-01 15:10:58 +08:00
# include <asm/patch.h>
2020-03-10 00:55:43 +08:00
2020-04-21 15:29:59 +08:00
struct patch_insn {
2020-03-10 00:55:43 +08:00
void * addr ;
2023-02-15 21:52:02 +08:00
u32 * insns ;
int ninsns ;
2020-03-10 00:55:43 +08:00
atomic_t cpu_count ;
} ;
2023-03-03 14:37:55 +00:00
int riscv_patch_in_stop_machine = false ;
2020-03-10 00:55:43 +08:00
# ifdef CONFIG_MMU
riscv: Fixup compile error BUILD_BUG_ON failed
Unfortunately, the current code couldn't be compiled:
CC arch/riscv/kernel/patch.o
In file included from ./include/linux/kernel.h:11,
from ./include/linux/list.h:9,
from ./include/linux/preempt.h:11,
from ./include/linux/spinlock.h:51,
from arch/riscv/kernel/patch.c:6:
In function ‘fix_to_virt’,
inlined from ‘patch_map’ at arch/riscv/kernel/patch.c:37:17:
./include/linux/compiler.h:392:38: error: call to ‘__compiletime_assert_205’ declared with attribute error: BUILD_BUG_ON failed: idx >= __end_of_fixed_addresses
_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
^
./include/linux/compiler.h:373:4: note: in definition of macro ‘__compiletime_assert’
prefix ## suffix(); \
^~~~~~
./include/linux/compiler.h:392:2: note: in expansion of macro ‘_compiletime_assert’
_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
^~~~~~~~~~~~~~~~~~~
./include/linux/build_bug.h:39:37: note: in expansion of macro ‘compiletime_assert’
#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
^~~~~~~~~~~~~~~~~~
./include/linux/build_bug.h:50:2: note: in expansion of macro ‘BUILD_BUG_ON_MSG’
BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition)
^~~~~~~~~~~~~~~~
./include/asm-generic/fixmap.h:32:2: note: in expansion of macro ‘BUILD_BUG_ON’
BUILD_BUG_ON(idx >= __end_of_fixed_addresses);
^~~~~~~~~~~~
Because fix_to_virt(, idx) needs a const value, not a dynamic variable of
reg-a0 or BUILD_BUG_ON failed with "idx >= __end_of_fixed_addresses".
Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Reviewed-by: Masami Hiramatsu <mhiramat@kernel.org>
Reviewed-by: Pekka Enberg <penberg@kernel.org>
Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>
2020-12-17 16:01:38 +00:00
/*
* The fix_to_virt ( , idx ) needs a const value ( not a dynamic variable of
* reg - a0 ) or BUILD_BUG_ON failed with " idx >= __end_of_fixed_addresses " .
* So use ' __always_inline ' and ' const unsigned int fixmap ' here .
*/
static __always_inline void * patch_map ( void * addr , const unsigned int fixmap )
2020-03-10 00:55:43 +08:00
{
uintptr_t uintaddr = ( uintptr_t ) addr ;
struct page * page ;
if ( core_kernel_text ( uintaddr ) )
page = phys_to_page ( __pa_symbol ( addr ) ) ;
else if ( IS_ENABLED ( CONFIG_STRICT_MODULE_RWX ) )
page = vmalloc_to_page ( addr ) ;
else
return addr ;
BUG_ON ( ! page ) ;
return ( void * ) set_fixmap_offset ( fixmap , page_to_phys ( page ) +
( uintaddr & ~ PAGE_MASK ) ) ;
}
2020-04-21 15:30:00 +08:00
static void patch_unmap ( int fixmap )
2020-03-10 00:55:43 +08:00
{
clear_fixmap ( fixmap ) ;
}
2020-04-21 15:30:00 +08:00
NOKPROBE_SYMBOL ( patch_unmap ) ;
2020-03-10 00:55:43 +08:00
2023-08-31 13:12:28 +00:00
static int __patch_insn_set ( void * addr , u8 c , size_t len )
{
void * waddr = addr ;
bool across_pages = ( ( ( uintptr_t ) addr & ~ PAGE_MASK ) + len ) > PAGE_SIZE ;
/*
* Only two pages can be mapped at a time for writing .
*/
if ( len + offset_in_page ( addr ) > 2 * PAGE_SIZE )
return - EINVAL ;
/*
* Before reaching here , it was expected to lock the text_mutex
* already , so we don ' t need to give another lock here and could
* ensure that it was safe between each cores .
*/
lockdep_assert_held ( & text_mutex ) ;
if ( across_pages )
patch_map ( addr + PAGE_SIZE , FIX_TEXT_POKE1 ) ;
waddr = patch_map ( addr , FIX_TEXT_POKE0 ) ;
memset ( waddr , c , len ) ;
patch_unmap ( FIX_TEXT_POKE0 ) ;
if ( across_pages )
patch_unmap ( FIX_TEXT_POKE1 ) ;
return 0 ;
}
NOKPROBE_SYMBOL ( __patch_insn_set ) ;
2023-08-31 13:12:27 +00:00
static int __patch_insn_write ( void * addr , const void * insn , size_t len )
2020-03-10 00:55:43 +08:00
{
void * waddr = addr ;
bool across_pages = ( ( ( uintptr_t ) addr & ~ PAGE_MASK ) + len ) > PAGE_SIZE ;
int ret ;
2023-08-31 13:12:27 +00:00
/*
* Only two pages can be mapped at a time for writing .
*/
if ( len + offset_in_page ( addr ) > 2 * PAGE_SIZE )
return - EINVAL ;
2020-04-21 15:30:01 +08:00
/*
* Before reaching here , it was expected to lock the text_mutex
* already , so we don ' t need to give another lock here and could
* ensure that it was safe between each cores .
2023-03-03 14:37:55 +00:00
*
* We ' re currently using stop_machine ( ) for ftrace & kprobes , and while
* that ensures text_mutex is held before installing the mappings it
* does not ensure text_mutex is held by the calling thread . That ' s
* safe but triggers a lockdep failure , so just elide it for that
* specific case .
2020-04-21 15:30:01 +08:00
*/
2023-03-03 14:37:55 +00:00
if ( ! riscv_patch_in_stop_machine )
lockdep_assert_held ( & text_mutex ) ;
2020-03-10 00:55:43 +08:00
if ( across_pages )
2023-08-31 13:12:27 +00:00
patch_map ( addr + PAGE_SIZE , FIX_TEXT_POKE1 ) ;
2020-03-10 00:55:43 +08:00
waddr = patch_map ( addr , FIX_TEXT_POKE0 ) ;
2020-06-17 09:37:53 +02:00
ret = copy_to_kernel_nofault ( waddr , insn , len ) ;
2020-03-10 00:55:43 +08:00
patch_unmap ( FIX_TEXT_POKE0 ) ;
if ( across_pages )
patch_unmap ( FIX_TEXT_POKE1 ) ;
return ret ;
}
2023-08-31 13:12:27 +00:00
NOKPROBE_SYMBOL ( __patch_insn_write ) ;
2020-03-10 00:55:43 +08:00
# else
2023-08-31 13:12:28 +00:00
static int __patch_insn_set ( void * addr , u8 c , size_t len )
{
memset ( addr , c , len ) ;
return 0 ;
}
NOKPROBE_SYMBOL ( __patch_insn_set ) ;
2023-08-31 13:12:27 +00:00
static int __patch_insn_write ( void * addr , const void * insn , size_t len )
2020-03-10 00:55:43 +08:00
{
2020-06-17 09:37:53 +02:00
return copy_to_kernel_nofault ( addr , insn , len ) ;
2020-03-10 00:55:43 +08:00
}
2023-08-31 13:12:27 +00:00
NOKPROBE_SYMBOL ( __patch_insn_write ) ;
2020-03-10 00:55:43 +08:00
# endif /* CONFIG_MMU */
2023-08-31 13:12:28 +00:00
static int patch_insn_set ( void * addr , u8 c , size_t len )
{
size_t patched = 0 ;
size_t size ;
int ret = 0 ;
/*
* __patch_insn_set ( ) can only work on 2 pages at a time so call it in a
* loop with len < = 2 * PAGE_SIZE .
*/
while ( patched < len & & ! ret ) {
size = min_t ( size_t , PAGE_SIZE * 2 - offset_in_page ( addr + patched ) , len - patched ) ;
ret = __patch_insn_set ( addr + patched , c , size ) ;
patched + = size ;
}
return ret ;
}
NOKPROBE_SYMBOL ( patch_insn_set ) ;
int patch_text_set_nosync ( void * addr , u8 c , size_t len )
{
u32 * tp = addr ;
int ret ;
ret = patch_insn_set ( tp , c , len ) ;
if ( ! ret )
flush_icache_range ( ( uintptr_t ) tp , ( uintptr_t ) tp + len ) ;
return ret ;
}
NOKPROBE_SYMBOL ( patch_text_set_nosync ) ;
2023-08-31 13:12:27 +00:00
static int patch_insn_write ( void * addr , const void * insn , size_t len )
{
size_t patched = 0 ;
size_t size ;
int ret = 0 ;
/*
* Copy the instructions to the destination address , two pages at a time
* because __patch_insn_write ( ) can only handle len < = 2 * PAGE_SIZE .
*/
while ( patched < len & & ! ret ) {
size = min_t ( size_t , PAGE_SIZE * 2 - offset_in_page ( addr + patched ) , len - patched ) ;
ret = __patch_insn_write ( addr + patched , insn + patched , size ) ;
patched + = size ;
}
return ret ;
}
NOKPROBE_SYMBOL ( patch_insn_write ) ;
2020-04-21 15:30:00 +08:00
int patch_text_nosync ( void * addr , const void * insns , size_t len )
2020-03-10 00:55:43 +08:00
{
u32 * tp = addr ;
int ret ;
2020-04-21 15:29:59 +08:00
ret = patch_insn_write ( tp , insns , len ) ;
2020-03-10 00:55:43 +08:00
if ( ! ret )
flush_icache_range ( ( uintptr_t ) tp , ( uintptr_t ) tp + len ) ;
return ret ;
}
2020-04-21 15:30:00 +08:00
NOKPROBE_SYMBOL ( patch_text_nosync ) ;
2020-03-10 00:55:43 +08:00
2020-04-21 15:30:00 +08:00
static int patch_text_cb ( void * data )
2020-03-10 00:55:43 +08:00
{
2020-04-21 15:29:59 +08:00
struct patch_insn * patch = data ;
2023-02-15 21:52:02 +08:00
unsigned long len ;
int i , ret = 0 ;
2020-03-10 00:55:43 +08:00
2022-04-06 22:16:49 +08:00
if ( atomic_inc_return ( & patch - > cpu_count ) = = num_online_cpus ( ) ) {
2023-02-15 21:52:02 +08:00
for ( i = 0 ; ret = = 0 & & i < patch - > ninsns ; i + + ) {
len = GET_INSN_LENGTH ( patch - > insns [ i ] ) ;
ret = patch_text_nosync ( patch - > addr + i * len ,
& patch - > insns [ i ] , len ) ;
}
2020-03-10 00:55:43 +08:00
atomic_inc ( & patch - > cpu_count ) ;
} else {
while ( atomic_read ( & patch - > cpu_count ) < = num_online_cpus ( ) )
cpu_relax ( ) ;
smp_mb ( ) ;
}
return ret ;
}
2020-04-21 15:30:00 +08:00
NOKPROBE_SYMBOL ( patch_text_cb ) ;
2020-03-10 00:55:43 +08:00
2023-02-15 21:52:02 +08:00
int patch_text ( void * addr , u32 * insns , int ninsns )
2020-03-10 00:55:43 +08:00
{
2023-03-03 14:37:55 +00:00
int ret ;
2020-04-21 15:29:59 +08:00
struct patch_insn patch = {
2020-03-10 00:55:43 +08:00
. addr = addr ,
2023-02-15 21:52:02 +08:00
. insns = insns ,
. ninsns = ninsns ,
2020-03-10 00:55:43 +08:00
. cpu_count = ATOMIC_INIT ( 0 ) ,
} ;
2023-03-03 14:37:55 +00:00
/*
* kprobes takes text_mutex , before calling patch_text ( ) , but as we call
* calls stop_machine ( ) , the lockdep assertion in patch_insn_write ( )
* gets confused by the context in which the lock is taken .
* Instead , ensure the lock is held before calling stop_machine ( ) , and
* set riscv_patch_in_stop_machine to skip the check in
* patch_insn_write ( ) .
*/
lockdep_assert_held ( & text_mutex ) ;
riscv_patch_in_stop_machine = true ;
ret = stop_machine_cpuslocked ( patch_text_cb , & patch , cpu_online_mask ) ;
riscv_patch_in_stop_machine = false ;
return ret ;
2020-03-10 00:55:43 +08:00
}
2020-04-21 15:30:00 +08:00
NOKPROBE_SYMBOL ( patch_text ) ;