2022-05-31 18:04:11 +08:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2020 - 2022 Loongson Technology Corporation Limited
*/
2022-12-10 22:40:15 +08:00
# include <linux/sizes.h>
# include <linux/uaccess.h>
# include <asm/cacheflush.h>
2022-05-31 18:04:11 +08:00
# include <asm/inst.h>
2022-12-10 22:40:15 +08:00
static DEFINE_RAW_SPINLOCK ( patch_lock ) ;
int larch_insn_read ( void * addr , u32 * insnp )
{
int ret ;
u32 val ;
ret = copy_from_kernel_nofault ( & val , addr , LOONGARCH_INSN_SIZE ) ;
if ( ! ret )
* insnp = val ;
return ret ;
}
int larch_insn_write ( void * addr , u32 insn )
{
int ret ;
unsigned long flags = 0 ;
raw_spin_lock_irqsave ( & patch_lock , flags ) ;
ret = copy_to_kernel_nofault ( addr , & insn , LOONGARCH_INSN_SIZE ) ;
raw_spin_unlock_irqrestore ( & patch_lock , flags ) ;
return ret ;
}
int larch_insn_patch_text ( void * addr , u32 insn )
{
int ret ;
u32 * tp = addr ;
if ( ( unsigned long ) tp & 3 )
return - EINVAL ;
ret = larch_insn_write ( tp , insn ) ;
if ( ! ret )
flush_icache_range ( ( unsigned long ) tp ,
( unsigned long ) tp + LOONGARCH_INSN_SIZE ) ;
return ret ;
}
u32 larch_insn_gen_nop ( void )
{
return INSN_NOP ;
}
2022-12-10 22:40:15 +08:00
u32 larch_insn_gen_b ( unsigned long pc , unsigned long dest )
{
long offset = dest - pc ;
union loongarch_instruction insn ;
if ( ( offset & 3 ) | | offset < - SZ_128M | | offset > = SZ_128M ) {
pr_warn ( " The generated b instruction is out of range. \n " ) ;
return INSN_BREAK ;
}
2023-01-17 11:42:16 +08:00
emit_b ( & insn , offset > > 2 ) ;
2022-12-10 22:40:15 +08:00
return insn . word ;
}
2022-12-10 22:40:15 +08:00
u32 larch_insn_gen_bl ( unsigned long pc , unsigned long dest )
{
long offset = dest - pc ;
union loongarch_instruction insn ;
if ( ( offset & 3 ) | | offset < - SZ_128M | | offset > = SZ_128M ) {
pr_warn ( " The generated bl instruction is out of range. \n " ) ;
return INSN_BREAK ;
}
2023-01-17 11:42:16 +08:00
emit_bl ( & insn , offset > > 2 ) ;
2022-12-10 22:40:15 +08:00
return insn . word ;
}
u32 larch_insn_gen_or ( enum loongarch_gpr rd , enum loongarch_gpr rj , enum loongarch_gpr rk )
{
union loongarch_instruction insn ;
2023-01-17 11:42:16 +08:00
emit_or ( & insn , rd , rj , rk ) ;
2022-12-10 22:40:15 +08:00
return insn . word ;
}
u32 larch_insn_gen_move ( enum loongarch_gpr rd , enum loongarch_gpr rj )
{
return larch_insn_gen_or ( rd , rj , 0 ) ;
}
2022-12-10 22:40:21 +08:00
u32 larch_insn_gen_lu12iw ( enum loongarch_gpr rd , int imm )
{
union loongarch_instruction insn ;
2023-01-17 11:42:16 +08:00
emit_lu12iw ( & insn , rd , imm ) ;
2022-12-10 22:40:21 +08:00
return insn . word ;
}
2022-05-31 18:04:11 +08:00
u32 larch_insn_gen_lu32id ( enum loongarch_gpr rd , int imm )
{
union loongarch_instruction insn ;
2023-01-17 11:42:16 +08:00
emit_lu32id ( & insn , rd , imm ) ;
2022-05-31 18:04:11 +08:00
return insn . word ;
}
u32 larch_insn_gen_lu52id ( enum loongarch_gpr rd , enum loongarch_gpr rj , int imm )
{
union loongarch_instruction insn ;
2023-01-17 11:42:16 +08:00
emit_lu52id ( & insn , rd , rj , imm ) ;
2022-05-31 18:04:11 +08:00
return insn . word ;
}
u32 larch_insn_gen_jirl ( enum loongarch_gpr rd , enum loongarch_gpr rj , unsigned long pc , unsigned long dest )
{
union loongarch_instruction insn ;
2023-01-17 11:42:16 +08:00
emit_jirl ( & insn , rj , rd , ( dest - pc ) > > 2 ) ;
2022-05-31 18:04:11 +08:00
return insn . word ;
}