2020-03-09 19:55:43 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright ( C ) 2020 SiFive
*/
# include <linux/spinlock.h>
# include <linux/mm.h>
2020-04-21 10:30:01 +03:00
# include <linux/memory.h>
2020-03-09 19:55:43 +03:00
# include <linux/uaccess.h>
# include <linux/stop_machine.h>
# include <asm/kprobes.h>
# include <asm/cacheflush.h>
# include <asm/fixmap.h>
2020-06-01 10:10:58 +03:00
# include <asm/patch.h>
2020-03-09 19:55:43 +03:00
2020-04-21 10:29:59 +03:00
struct patch_insn {
2020-03-09 19:55:43 +03:00
void * addr ;
u32 insn ;
atomic_t cpu_count ;
} ;
# 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 19:01:38 +03: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-09 19:55:43 +03: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 10:30:00 +03:00
static void patch_unmap ( int fixmap )
2020-03-09 19:55:43 +03:00
{
clear_fixmap ( fixmap ) ;
}
2020-04-21 10:30:00 +03:00
NOKPROBE_SYMBOL ( patch_unmap ) ;
2020-03-09 19:55:43 +03:00
2020-04-21 10:30:00 +03:00
static int patch_insn_write ( void * addr , const void * insn , size_t len )
2020-03-09 19:55:43 +03:00
{
void * waddr = addr ;
bool across_pages = ( ( ( uintptr_t ) addr & ~ PAGE_MASK ) + len ) > PAGE_SIZE ;
int ret ;
2020-04-21 10:30:01 +03: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 .
*/
lockdep_assert_held ( & text_mutex ) ;
2020-03-09 19:55:43 +03:00
if ( across_pages )
patch_map ( addr + len , FIX_TEXT_POKE1 ) ;
waddr = patch_map ( addr , FIX_TEXT_POKE0 ) ;
2020-06-17 10:37:53 +03:00
ret = copy_to_kernel_nofault ( waddr , insn , len ) ;
2020-03-09 19:55:43 +03:00
patch_unmap ( FIX_TEXT_POKE0 ) ;
if ( across_pages )
patch_unmap ( FIX_TEXT_POKE1 ) ;
return ret ;
}
2020-04-21 10:30:00 +03:00
NOKPROBE_SYMBOL ( patch_insn_write ) ;
2020-03-09 19:55:43 +03:00
# else
2020-04-21 10:30:00 +03:00
static int patch_insn_write ( void * addr , const void * insn , size_t len )
2020-03-09 19:55:43 +03:00
{
2020-06-17 10:37:53 +03:00
return copy_to_kernel_nofault ( addr , insn , len ) ;
2020-03-09 19:55:43 +03:00
}
2020-04-21 10:30:00 +03:00
NOKPROBE_SYMBOL ( patch_insn_write ) ;
2020-03-09 19:55:43 +03:00
# endif /* CONFIG_MMU */
2020-04-21 10:30:00 +03:00
int patch_text_nosync ( void * addr , const void * insns , size_t len )
2020-03-09 19:55:43 +03:00
{
u32 * tp = addr ;
int ret ;
2020-04-21 10:29:59 +03:00
ret = patch_insn_write ( tp , insns , len ) ;
2020-03-09 19:55:43 +03:00
if ( ! ret )
flush_icache_range ( ( uintptr_t ) tp , ( uintptr_t ) tp + len ) ;
return ret ;
}
2020-04-21 10:30:00 +03:00
NOKPROBE_SYMBOL ( patch_text_nosync ) ;
2020-03-09 19:55:43 +03:00
2020-04-21 10:30:00 +03:00
static int patch_text_cb ( void * data )
2020-03-09 19:55:43 +03:00
{
2020-04-21 10:29:59 +03:00
struct patch_insn * patch = data ;
2020-03-09 19:55:43 +03:00
int ret = 0 ;
2022-04-06 17:16:49 +03:00
if ( atomic_inc_return ( & patch - > cpu_count ) = = num_online_cpus ( ) ) {
2020-03-09 19:55:43 +03:00
ret =
2020-04-21 10:29:59 +03:00
patch_text_nosync ( patch - > addr , & patch - > insn ,
2020-03-09 19:55:43 +03:00
GET_INSN_LENGTH ( patch - > insn ) ) ;
atomic_inc ( & patch - > cpu_count ) ;
} else {
while ( atomic_read ( & patch - > cpu_count ) < = num_online_cpus ( ) )
cpu_relax ( ) ;
smp_mb ( ) ;
}
return ret ;
}
2020-04-21 10:30:00 +03:00
NOKPROBE_SYMBOL ( patch_text_cb ) ;
2020-03-09 19:55:43 +03:00
2020-04-21 10:30:00 +03:00
int patch_text ( void * addr , u32 insn )
2020-03-09 19:55:43 +03:00
{
2020-04-21 10:29:59 +03:00
struct patch_insn patch = {
2020-03-09 19:55:43 +03:00
. addr = addr ,
. insn = insn ,
. cpu_count = ATOMIC_INIT ( 0 ) ,
} ;
2020-04-21 10:29:59 +03:00
return stop_machine_cpuslocked ( patch_text_cb ,
2020-03-09 19:55:43 +03:00
& patch , cpu_online_mask ) ;
}
2020-04-21 10:30:00 +03:00
NOKPROBE_SYMBOL ( patch_text ) ;