2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2008-06-24 05:32:21 +04:00
/*
* Copyright 2008 Michael Ellerman , IBM Corporation .
*/
# include <linux/kernel.h>
2017-04-12 14:18:51 +03:00
# include <linux/kprobes.h>
2008-06-24 05:32:32 +04:00
# include <linux/vmalloc.h>
# include <linux/init.h>
2008-07-24 08:28:13 +04:00
# include <linux/mm.h>
2017-06-28 20:04:05 +03:00
# include <linux/cpuhotplug.h>
# include <linux/slab.h>
2016-12-24 22:46:01 +03:00
# include <linux/uaccess.h>
2008-06-24 05:32:21 +04:00
2017-06-28 20:04:05 +03:00
# include <asm/tlbflush.h>
# include <asm/page.h>
# include <asm/code-patching.h>
2017-11-21 17:28:20 +03:00
# include <asm/setup.h>
2020-05-06 06:40:26 +03:00
# include <asm/inst.h>
2008-06-24 05:32:21 +04:00
2020-05-06 06:40:31 +03:00
static int __patch_instruction ( struct ppc_inst * exec_addr , struct ppc_inst instr ,
struct ppc_inst * patch_addr )
2008-06-24 05:32:21 +04:00
{
2021-03-10 20:57:04 +03:00
if ( ! ppc_inst_prefixed ( instr ) ) {
u32 val = ppc_inst_val ( instr ) ;
__put_kernel_nofault ( patch_addr , & val , u32 , failed ) ;
} else {
2021-04-20 17:02:06 +03:00
u64 val = ppc_inst_as_ulong ( instr ) ;
2021-03-10 20:57:04 +03:00
__put_kernel_nofault ( patch_addr , & val , u64 , failed ) ;
}
2017-06-28 20:04:05 +03:00
2017-11-24 10:31:07 +03:00
asm ( " dcbst 0, %0; sync; icbi 0,%1; sync; isync " : : " r " ( patch_addr ) ,
" r " ( exec_addr ) ) ;
2017-06-28 20:04:05 +03:00
return 0 ;
2020-09-04 14:01:31 +03:00
failed :
return - EFAULT ;
2017-06-28 20:04:05 +03:00
}
2020-05-06 06:40:31 +03:00
int raw_patch_instruction ( struct ppc_inst * addr , struct ppc_inst instr )
2017-11-24 10:31:07 +03:00
{
return __patch_instruction ( addr , instr , addr ) ;
}
2017-06-28 20:04:05 +03:00
# ifdef CONFIG_STRICT_KERNEL_RWX
static DEFINE_PER_CPU ( struct vm_struct * , text_poke_area ) ;
static int text_area_cpu_up ( unsigned int cpu )
{
struct vm_struct * area ;
area = get_vm_area ( PAGE_SIZE , VM_ALLOC ) ;
if ( ! area ) {
WARN_ONCE ( 1 , " Failed to create text area for cpu %d \n " ,
cpu ) ;
return - 1 ;
}
this_cpu_write ( text_poke_area , area ) ;
return 0 ;
}
static int text_area_cpu_down ( unsigned int cpu )
{
free_vm_area ( this_cpu_read ( text_poke_area ) ) ;
return 0 ;
}
/*
* Run as a late init call . This allows all the boot time patching to be done
* simply by patching the code , and then we ' re called here prior to
* mark_rodata_ro ( ) , which happens after all init calls are run . Although
* BUG_ON ( ) is rude , in this case it should only happen if ENOMEM , and we judge
* it as being preferable to a kernel that will crash later when someone tries
* to use patch_instruction ( ) .
*/
static int __init setup_text_poke_area ( void )
{
BUG_ON ( ! cpuhp_setup_state ( CPUHP_AP_ONLINE_DYN ,
" powerpc/text_poke:online " , text_area_cpu_up ,
text_area_cpu_down ) ) ;
return 0 ;
}
late_initcall ( setup_text_poke_area ) ;
/*
* This can be called for kernel text or a module .
*/
static int map_patch_area ( void * addr , unsigned long text_poke_addr )
{
unsigned long pfn ;
int err ;
2020-06-29 14:15:20 +03:00
if ( is_vmalloc_or_module_addr ( addr ) )
2017-06-28 20:04:05 +03:00
pfn = vmalloc_to_pfn ( addr ) ;
else
pfn = __pa_symbol ( addr ) > > PAGE_SHIFT ;
2018-10-09 16:51:45 +03:00
err = map_kernel_page ( text_poke_addr , ( pfn < < PAGE_SHIFT ) , PAGE_KERNEL ) ;
2017-06-28 20:04:05 +03:00
pr_devel ( " Mapped addr %lx with pfn %lx:%d \n " , text_poke_addr , pfn , err ) ;
if ( err )
return - 1 ;
return 0 ;
}
static inline int unmap_patch_area ( unsigned long addr )
{
pte_t * ptep ;
pmd_t * pmdp ;
pud_t * pudp ;
2020-06-05 02:46:44 +03:00
p4d_t * p4dp ;
2017-06-28 20:04:05 +03:00
pgd_t * pgdp ;
pgdp = pgd_offset_k ( addr ) ;
if ( unlikely ( ! pgdp ) )
return - EINVAL ;
2020-06-05 02:46:44 +03:00
p4dp = p4d_offset ( pgdp , addr ) ;
if ( unlikely ( ! p4dp ) )
return - EINVAL ;
pudp = pud_offset ( p4dp , addr ) ;
2017-06-28 20:04:05 +03:00
if ( unlikely ( ! pudp ) )
return - EINVAL ;
pmdp = pmd_offset ( pudp , addr ) ;
if ( unlikely ( ! pmdp ) )
return - EINVAL ;
ptep = pte_offset_kernel ( pmdp , addr ) ;
if ( unlikely ( ! ptep ) )
return - EINVAL ;
pr_devel ( " clearing mm %p, pte %p, addr %lx \n " , & init_mm , ptep , addr ) ;
/*
* In hash , pte_clear flushes the tlb , in radix , we have to
*/
pte_clear ( & init_mm , addr , ptep ) ;
flush_tlb_kernel_range ( addr , addr + PAGE_SIZE ) ;
2012-04-26 12:31:18 +04:00
return 0 ;
2008-06-24 05:32:21 +04:00
}
2020-05-06 06:40:31 +03:00
static int do_patch_instruction ( struct ppc_inst * addr , struct ppc_inst instr )
2017-06-28 20:04:05 +03:00
{
int err ;
2020-05-06 06:40:31 +03:00
struct ppc_inst * patch_addr = NULL ;
2017-06-28 20:04:05 +03:00
unsigned long flags ;
unsigned long text_poke_addr ;
unsigned long kaddr = ( unsigned long ) addr ;
/*
* During early early boot patch_instruction is called
* when text_poke_area is not ready , but we still need
* to allow patching . We just do the plain old patching
*/
2017-11-24 10:31:09 +03:00
if ( ! this_cpu_read ( text_poke_area ) )
2017-11-24 10:31:07 +03:00
return raw_patch_instruction ( addr , instr ) ;
2017-06-28 20:04:05 +03:00
local_irq_save ( flags ) ;
text_poke_addr = ( unsigned long ) __this_cpu_read ( text_poke_area ) - > addr ;
if ( map_patch_area ( addr , text_poke_addr ) ) {
err = - 1 ;
goto out ;
}
2020-05-06 06:40:31 +03:00
patch_addr = ( struct ppc_inst * ) ( text_poke_addr + ( kaddr & ~ PAGE_MASK ) ) ;
2017-06-28 20:04:05 +03:00
2017-11-24 10:31:07 +03:00
__patch_instruction ( addr , instr , patch_addr ) ;
2017-06-28 20:04:05 +03:00
err = unmap_patch_area ( text_poke_addr ) ;
if ( err )
pr_warn ( " failed to unmap %lx \n " , text_poke_addr ) ;
out :
local_irq_restore ( flags ) ;
return err ;
}
# else /* !CONFIG_STRICT_KERNEL_RWX */
2020-05-06 06:40:31 +03:00
static int do_patch_instruction ( struct ppc_inst * addr , struct ppc_inst instr )
2017-06-28 20:04:05 +03:00
{
2017-11-24 10:31:07 +03:00
return raw_patch_instruction ( addr , instr ) ;
2017-06-28 20:04:05 +03:00
}
# endif /* CONFIG_STRICT_KERNEL_RWX */
2018-10-01 15:21:10 +03:00
2020-05-06 06:40:31 +03:00
int patch_instruction ( struct ppc_inst * addr , struct ppc_inst instr )
2018-10-01 15:21:10 +03:00
{
/* Make sure we aren't patching a freed init section */
if ( init_mem_is_free & & init_section_contains ( addr , 4 ) ) {
pr_debug ( " Skipping init section patching addr: 0x%px \n " , addr ) ;
return 0 ;
}
return do_patch_instruction ( addr , instr ) ;
}
2017-06-28 20:04:05 +03:00
NOKPROBE_SYMBOL ( patch_instruction ) ;
2020-05-06 06:40:31 +03:00
int patch_branch ( struct ppc_inst * addr , unsigned long target , int flags )
2008-06-24 05:32:22 +04:00
{
2020-05-06 06:40:31 +03:00
struct ppc_inst instr ;
2020-05-06 06:40:25 +03:00
create_branch ( & instr , addr , target , flags ) ;
return patch_instruction ( addr , instr ) ;
2008-06-24 05:32:22 +04:00
}
2017-02-08 11:57:30 +03:00
bool is_offset_in_branch_range ( long offset )
{
/*
* Powerpc branch instruction is :
*
* 0 6 30 31
* + - - - - - - - - - + - - - - - - - - - - - - - - - - + - - - + - - - +
* | opcode | LI | AA | LK |
* + - - - - - - - - - + - - - - - - - - - - - - - - - - + - - - + - - - +
* Where AA = 0 and LK = 0
*
* LI is a signed 24 bits integer . The real branch offset is computed
* by : imm32 = SignExtend ( LI : ' 0 b00 ' , 32 ) ;
*
* So the maximum forward branch should be :
* ( 0x007fffff < < 2 ) = 0x01fffffc = 0x1fffffc
* The maximum backward branch should be :
* ( 0xff800000 < < 2 ) = 0xfe000000 = - 0x2000000
*/
return ( offset > = - 0x2000000 & & offset < = 0x1fffffc & & ! ( offset & 0x3 ) ) ;
}
2017-02-08 12:50:51 +03:00
/*
* Helper to check if a given instruction is a conditional branch
* Derived from the conditional checks in analyse_instr ( )
*/
2020-05-06 06:40:31 +03:00
bool is_conditional_branch ( struct ppc_inst instr )
2017-02-08 12:50:51 +03:00
{
2020-05-06 06:40:28 +03:00
unsigned int opcode = ppc_inst_primary_opcode ( instr ) ;
2017-02-08 12:50:51 +03:00
if ( opcode = = 16 ) /* bc, bca, bcl, bcla */
return true ;
if ( opcode = = 19 ) {
2020-05-06 06:40:27 +03:00
switch ( ( ppc_inst_val ( instr ) > > 1 ) & 0x3ff ) {
2017-02-08 12:50:51 +03:00
case 16 : /* bclr, bclrl */
case 528 : /* bcctr, bcctrl */
case 560 : /* bctar, bctarl */
return true ;
}
}
return false ;
}
2017-04-12 14:18:51 +03:00
NOKPROBE_SYMBOL ( is_conditional_branch ) ;
2017-02-08 12:50:51 +03:00
2020-05-06 06:40:31 +03:00
int create_branch ( struct ppc_inst * instr ,
const struct ppc_inst * addr ,
2020-05-06 06:40:25 +03:00
unsigned long target , int flags )
2008-06-24 05:32:21 +04:00
{
2008-06-24 05:32:24 +04:00
long offset ;
2008-06-24 05:32:21 +04:00
2020-05-06 06:40:31 +03:00
* instr = ppc_inst ( 0 ) ;
2008-06-24 05:32:24 +04:00
offset = target ;
2008-06-24 05:32:21 +04:00
if ( ! ( flags & BRANCH_ABSOLUTE ) )
2008-06-24 05:32:24 +04:00
offset = offset - ( unsigned long ) addr ;
/* Check we can represent the target in the instruction format */
2017-02-08 11:57:30 +03:00
if ( ! is_offset_in_branch_range ( offset ) )
2020-05-06 06:40:25 +03:00
return 1 ;
2008-06-24 05:32:21 +04:00
/* Mask out the flags and target, so they don't step on each other. */
2020-05-06 06:40:31 +03:00
* instr = ppc_inst ( 0x48000000 | ( flags & 0x3 ) | ( offset & 0x03FFFFFC ) ) ;
2008-06-24 05:32:21 +04:00
2020-05-06 06:40:25 +03:00
return 0 ;
2008-06-24 05:32:21 +04:00
}
2008-06-24 05:32:29 +04:00
2020-05-06 06:40:31 +03:00
int create_cond_branch ( struct ppc_inst * instr , const struct ppc_inst * addr ,
2020-05-06 06:40:25 +03:00
unsigned long target , int flags )
2008-06-24 05:32:29 +04:00
{
long offset ;
offset = target ;
if ( ! ( flags & BRANCH_ABSOLUTE ) )
offset = offset - ( unsigned long ) addr ;
/* Check we can represent the target in the instruction format */
if ( offset < - 0x8000 | | offset > 0x7FFF | | offset & 0x3 )
2020-05-06 06:40:25 +03:00
return 1 ;
2008-06-24 05:32:29 +04:00
/* Mask out the flags and target, so they don't step on each other. */
2020-05-06 06:40:31 +03:00
* instr = ppc_inst ( 0x40000000 | ( flags & 0x3FF0003 ) | ( offset & 0xFFFC ) ) ;
2008-06-24 05:32:29 +04:00
2020-05-06 06:40:25 +03:00
return 0 ;
2008-06-24 05:32:29 +04:00
}
2020-05-06 06:40:31 +03:00
static unsigned int branch_opcode ( struct ppc_inst instr )
2008-06-24 05:32:29 +04:00
{
2020-05-06 06:40:28 +03:00
return ppc_inst_primary_opcode ( instr ) & 0x3F ;
2008-06-24 05:32:29 +04:00
}
2020-05-06 06:40:31 +03:00
static int instr_is_branch_iform ( struct ppc_inst instr )
2008-06-24 05:32:29 +04:00
{
return branch_opcode ( instr ) = = 18 ;
}
2020-05-06 06:40:31 +03:00
static int instr_is_branch_bform ( struct ppc_inst instr )
2008-06-24 05:32:29 +04:00
{
return branch_opcode ( instr ) = = 16 ;
}
2020-05-06 06:40:31 +03:00
int instr_is_relative_branch ( struct ppc_inst instr )
2008-06-24 05:32:29 +04:00
{
2020-05-06 06:40:27 +03:00
if ( ppc_inst_val ( instr ) & BRANCH_ABSOLUTE )
2008-06-24 05:32:29 +04:00
return 0 ;
return instr_is_branch_iform ( instr ) | | instr_is_branch_bform ( instr ) ;
}
2020-05-06 06:40:31 +03:00
int instr_is_relative_link_branch ( struct ppc_inst instr )
2017-11-16 20:45:37 +03:00
{
2020-05-06 06:40:27 +03:00
return instr_is_relative_branch ( instr ) & & ( ppc_inst_val ( instr ) & BRANCH_SET_LINK ) ;
2017-11-16 20:45:37 +03:00
}
2020-05-06 06:40:31 +03:00
static unsigned long branch_iform_target ( const struct ppc_inst * instr )
2008-06-24 05:32:29 +04:00
{
signed long imm ;
2020-05-06 06:40:27 +03:00
imm = ppc_inst_val ( * instr ) & 0x3FFFFFC ;
2008-06-24 05:32:29 +04:00
/* If the top bit of the immediate value is set this is negative */
if ( imm & 0x2000000 )
imm - = 0x4000000 ;
2020-05-06 06:40:27 +03:00
if ( ( ppc_inst_val ( * instr ) & BRANCH_ABSOLUTE ) = = 0 )
2008-06-24 05:32:29 +04:00
imm + = ( unsigned long ) instr ;
return ( unsigned long ) imm ;
}
2020-05-06 06:40:31 +03:00
static unsigned long branch_bform_target ( const struct ppc_inst * instr )
2008-06-24 05:32:29 +04:00
{
signed long imm ;
2020-05-06 06:40:27 +03:00
imm = ppc_inst_val ( * instr ) & 0xFFFC ;
2008-06-24 05:32:29 +04:00
/* If the top bit of the immediate value is set this is negative */
if ( imm & 0x8000 )
imm - = 0x10000 ;
2020-05-06 06:40:27 +03:00
if ( ( ppc_inst_val ( * instr ) & BRANCH_ABSOLUTE ) = = 0 )
2008-06-24 05:32:29 +04:00
imm + = ( unsigned long ) instr ;
return ( unsigned long ) imm ;
}
2020-05-06 06:40:31 +03:00
unsigned long branch_target ( const struct ppc_inst * instr )
2008-06-24 05:32:29 +04:00
{
2020-05-06 06:40:32 +03:00
if ( instr_is_branch_iform ( ppc_inst_read ( instr ) ) )
2008-06-24 05:32:29 +04:00
return branch_iform_target ( instr ) ;
2020-05-06 06:40:32 +03:00
else if ( instr_is_branch_bform ( ppc_inst_read ( instr ) ) )
2008-06-24 05:32:29 +04:00
return branch_bform_target ( instr ) ;
return 0 ;
}
2020-05-06 06:40:31 +03:00
int instr_is_branch_to_addr ( const struct ppc_inst * instr , unsigned long addr )
2008-06-24 05:32:29 +04:00
{
2020-05-06 06:40:32 +03:00
if ( instr_is_branch_iform ( ppc_inst_read ( instr ) ) | |
instr_is_branch_bform ( ppc_inst_read ( instr ) ) )
2008-06-24 05:32:29 +04:00
return branch_target ( instr ) = = addr ;
return 0 ;
}
2020-05-06 06:40:31 +03:00
int translate_branch ( struct ppc_inst * instr , const struct ppc_inst * dest ,
const struct ppc_inst * src )
2008-06-24 05:32:29 +04:00
{
unsigned long target ;
target = branch_target ( src ) ;
2020-05-06 06:40:32 +03:00
if ( instr_is_branch_iform ( ppc_inst_read ( src ) ) )
return create_branch ( instr , dest , target ,
ppc_inst_val ( ppc_inst_read ( src ) ) ) ;
else if ( instr_is_branch_bform ( ppc_inst_read ( src ) ) )
return create_cond_branch ( instr , dest , target ,
ppc_inst_val ( ppc_inst_read ( src ) ) ) ;
2008-06-24 05:32:29 +04:00
2020-05-06 06:40:25 +03:00
return 1 ;
2008-06-24 05:32:29 +04:00
}
2008-06-24 05:32:32 +04:00
2013-05-12 03:26:22 +04:00
# ifdef CONFIG_PPC_BOOK3E_64
void __patch_exception ( int exc , unsigned long addr )
{
extern unsigned int interrupt_base_book3e ;
unsigned int * ibase = & interrupt_base_book3e ;
/* Our exceptions vectors start with a NOP and -then- a branch
* to deal with single stepping from userspace which stops on
* the second instruction . Thus we need to patch the second
* instruction of the exception , not the first one
*/
2020-05-06 06:40:31 +03:00
patch_branch ( ( struct ppc_inst * ) ( ibase + ( exc / 4 ) + 1 ) , addr , 0 ) ;
2013-05-12 03:26:22 +04:00
}
# endif
2008-06-24 05:32:32 +04:00
# ifdef CONFIG_CODE_PATCHING_SELFTEST
static void __init test_trampoline ( void )
{
asm ( " nop; \n " ) ;
}
# define check(x) \
if ( ! ( x ) ) printk ( " code-patching: test failed at line %d \n " , __LINE__ ) ;
static void __init test_branch_iform ( void )
{
2020-05-06 06:40:25 +03:00
int err ;
2020-05-06 06:40:31 +03:00
struct ppc_inst instr ;
2008-06-24 05:32:32 +04:00
unsigned long addr ;
addr = ( unsigned long ) & instr ;
/* The simplest case, branch to self, no flags */
2020-05-06 06:40:26 +03:00
check ( instr_is_branch_iform ( ppc_inst ( 0x48000000 ) ) ) ;
2008-06-24 05:32:32 +04:00
/* All bits of target set, and flags */
2020-05-06 06:40:26 +03:00
check ( instr_is_branch_iform ( ppc_inst ( 0x4bffffff ) ) ) ;
2008-06-24 05:32:32 +04:00
/* High bit of opcode set, which is wrong */
2020-05-06 06:40:26 +03:00
check ( ! instr_is_branch_iform ( ppc_inst ( 0xcbffffff ) ) ) ;
2008-06-24 05:32:32 +04:00
/* Middle bits of opcode set, which is wrong */
2020-05-06 06:40:26 +03:00
check ( ! instr_is_branch_iform ( ppc_inst ( 0x7bffffff ) ) ) ;
2008-06-24 05:32:32 +04:00
/* Simplest case, branch to self with link */
2020-05-06 06:40:26 +03:00
check ( instr_is_branch_iform ( ppc_inst ( 0x48000001 ) ) ) ;
2008-06-24 05:32:32 +04:00
/* All bits of targets set */
2020-05-06 06:40:26 +03:00
check ( instr_is_branch_iform ( ppc_inst ( 0x4bfffffd ) ) ) ;
2008-06-24 05:32:32 +04:00
/* Some bits of targets set */
2020-05-06 06:40:26 +03:00
check ( instr_is_branch_iform ( ppc_inst ( 0x4bff00fd ) ) ) ;
2008-06-24 05:32:32 +04:00
/* Must be a valid branch to start with */
2020-05-06 06:40:26 +03:00
check ( ! instr_is_branch_iform ( ppc_inst ( 0x7bfffffd ) ) ) ;
2008-06-24 05:32:32 +04:00
/* Absolute branch to 0x100 */
2020-05-06 06:40:26 +03:00
instr = ppc_inst ( 0x48000103 ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , 0x100 ) ) ;
/* Absolute branch to 0x420fc */
2020-05-06 06:40:26 +03:00
instr = ppc_inst ( 0x480420ff ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , 0x420fc ) ) ;
/* Maximum positive relative branch, + 20MB - 4B */
2020-05-06 06:40:26 +03:00
instr = ppc_inst ( 0x49fffffc ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr + 0x1FFFFFC ) ) ;
/* Smallest negative relative branch, - 4B */
2020-05-06 06:40:26 +03:00
instr = ppc_inst ( 0x4bfffffc ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr - 4 ) ) ;
/* Largest negative relative branch, - 32 MB */
2020-05-06 06:40:26 +03:00
instr = ppc_inst ( 0x4a000000 ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr - 0x2000000 ) ) ;
/* Branch to self, with link */
2020-05-06 06:40:25 +03:00
err = create_branch ( & instr , & instr , addr , BRANCH_SET_LINK ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr ) ) ;
/* Branch to self - 0x100, with link */
2020-05-06 06:40:25 +03:00
err = create_branch ( & instr , & instr , addr - 0x100 , BRANCH_SET_LINK ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr - 0x100 ) ) ;
/* Branch to self + 0x100, no link */
2020-05-06 06:40:25 +03:00
err = create_branch ( & instr , & instr , addr + 0x100 , 0 ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr + 0x100 ) ) ;
/* Maximum relative negative offset, - 32 MB */
2020-05-06 06:40:25 +03:00
err = create_branch ( & instr , & instr , addr - 0x2000000 , BRANCH_SET_LINK ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr - 0x2000000 ) ) ;
/* Out of range relative negative offset, - 32 MB + 4*/
2020-05-06 06:40:25 +03:00
err = create_branch ( & instr , & instr , addr - 0x2000004 , BRANCH_SET_LINK ) ;
check ( err ) ;
2008-06-24 05:32:32 +04:00
/* Out of range relative positive offset, + 32 MB */
2020-05-06 06:40:25 +03:00
err = create_branch ( & instr , & instr , addr + 0x2000000 , BRANCH_SET_LINK ) ;
check ( err ) ;
2008-06-24 05:32:32 +04:00
/* Unaligned target */
2020-05-06 06:40:25 +03:00
err = create_branch ( & instr , & instr , addr + 3 , BRANCH_SET_LINK ) ;
check ( err ) ;
2008-06-24 05:32:32 +04:00
/* Check flags are masked correctly */
2020-05-06 06:40:25 +03:00
err = create_branch ( & instr , & instr , addr , 0xFFFFFFFC ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr ) ) ;
2020-05-06 06:40:30 +03:00
check ( ppc_inst_equal ( instr , ppc_inst ( 0x48000000 ) ) ) ;
2008-06-24 05:32:32 +04:00
}
static void __init test_create_function_call ( void )
{
2020-05-06 06:40:31 +03:00
struct ppc_inst * iptr ;
2008-06-24 05:32:32 +04:00
unsigned long dest ;
2020-05-06 06:40:31 +03:00
struct ppc_inst instr ;
2008-06-24 05:32:32 +04:00
/* Check we can create a function call */
2020-05-06 06:40:31 +03:00
iptr = ( struct ppc_inst * ) ppc_function_entry ( test_trampoline ) ;
2008-06-24 05:32:32 +04:00
dest = ppc_function_entry ( test_create_function_call ) ;
2020-05-06 06:40:25 +03:00
create_branch ( & instr , iptr , dest , BRANCH_SET_LINK ) ;
patch_instruction ( iptr , instr ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( iptr , dest ) ) ;
}
static void __init test_branch_bform ( void )
{
2020-05-06 06:40:25 +03:00
int err ;
2008-06-24 05:32:32 +04:00
unsigned long addr ;
2020-05-06 06:40:31 +03:00
struct ppc_inst * iptr , instr ;
unsigned int flags ;
2008-06-24 05:32:32 +04:00
iptr = & instr ;
addr = ( unsigned long ) iptr ;
/* The simplest case, branch to self, no flags */
2020-05-06 06:40:26 +03:00
check ( instr_is_branch_bform ( ppc_inst ( 0x40000000 ) ) ) ;
2008-06-24 05:32:32 +04:00
/* All bits of target set, and flags */
2020-05-06 06:40:26 +03:00
check ( instr_is_branch_bform ( ppc_inst ( 0x43ffffff ) ) ) ;
2008-06-24 05:32:32 +04:00
/* High bit of opcode set, which is wrong */
2020-05-06 06:40:26 +03:00
check ( ! instr_is_branch_bform ( ppc_inst ( 0xc3ffffff ) ) ) ;
2008-06-24 05:32:32 +04:00
/* Middle bits of opcode set, which is wrong */
2020-05-06 06:40:26 +03:00
check ( ! instr_is_branch_bform ( ppc_inst ( 0x7bffffff ) ) ) ;
2008-06-24 05:32:32 +04:00
/* Absolute conditional branch to 0x100 */
2020-05-06 06:40:26 +03:00
instr = ppc_inst ( 0x43ff0103 ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , 0x100 ) ) ;
/* Absolute conditional branch to 0x20fc */
2020-05-06 06:40:26 +03:00
instr = ppc_inst ( 0x43ff20ff ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , 0x20fc ) ) ;
/* Maximum positive relative conditional branch, + 32 KB - 4B */
2020-05-06 06:40:26 +03:00
instr = ppc_inst ( 0x43ff7ffc ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr + 0x7FFC ) ) ;
/* Smallest negative relative conditional branch, - 4B */
2020-05-06 06:40:26 +03:00
instr = ppc_inst ( 0x43fffffc ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr - 4 ) ) ;
/* Largest negative relative conditional branch, - 32 KB */
2020-05-06 06:40:26 +03:00
instr = ppc_inst ( 0x43ff8000 ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr - 0x8000 ) ) ;
/* All condition code bits set & link */
flags = 0x3ff000 | BRANCH_SET_LINK ;
/* Branch to self */
2020-05-06 06:40:25 +03:00
err = create_cond_branch ( & instr , iptr , addr , flags ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr ) ) ;
/* Branch to self - 0x100 */
2020-05-06 06:40:25 +03:00
err = create_cond_branch ( & instr , iptr , addr - 0x100 , flags ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr - 0x100 ) ) ;
/* Branch to self + 0x100 */
2020-05-06 06:40:25 +03:00
err = create_cond_branch ( & instr , iptr , addr + 0x100 , flags ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr + 0x100 ) ) ;
/* Maximum relative negative offset, - 32 KB */
2020-05-06 06:40:25 +03:00
err = create_cond_branch ( & instr , iptr , addr - 0x8000 , flags ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr - 0x8000 ) ) ;
/* Out of range relative negative offset, - 32 KB + 4*/
2020-05-06 06:40:25 +03:00
err = create_cond_branch ( & instr , iptr , addr - 0x8004 , flags ) ;
check ( err ) ;
2008-06-24 05:32:32 +04:00
/* Out of range relative positive offset, + 32 KB */
2020-05-06 06:40:25 +03:00
err = create_cond_branch ( & instr , iptr , addr + 0x8000 , flags ) ;
check ( err ) ;
2008-06-24 05:32:32 +04:00
/* Unaligned target */
2020-05-06 06:40:25 +03:00
err = create_cond_branch ( & instr , iptr , addr + 3 , flags ) ;
check ( err ) ;
2008-06-24 05:32:32 +04:00
/* Check flags are masked correctly */
2020-05-06 06:40:25 +03:00
err = create_cond_branch ( & instr , iptr , addr , 0xFFFFFFFC ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( & instr , addr ) ) ;
2020-05-06 06:40:30 +03:00
check ( ppc_inst_equal ( instr , ppc_inst ( 0x43FF0000 ) ) ) ;
2008-06-24 05:32:32 +04:00
}
static void __init test_translate_branch ( void )
{
unsigned long addr ;
2020-05-06 06:40:40 +03:00
void * p , * q ;
2020-05-06 06:40:31 +03:00
struct ppc_inst instr ;
2008-06-24 05:32:32 +04:00
void * buf ;
buf = vmalloc ( PAGE_ALIGN ( 0x2000000 + 1 ) ) ;
check ( buf ) ;
if ( ! buf )
return ;
/* Simple case, branch to self moved a little */
p = buf ;
addr = ( unsigned long ) p ;
patch_branch ( p , addr , 0 ) ;
check ( instr_is_branch_to_addr ( p , addr ) ) ;
2020-05-06 06:40:40 +03:00
q = p + 4 ;
2020-05-06 06:40:25 +03:00
translate_branch ( & instr , q , p ) ;
patch_instruction ( q , instr ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( q , addr ) ) ;
/* Maximum negative case, move b . to addr + 32 MB */
p = buf ;
addr = ( unsigned long ) p ;
patch_branch ( p , addr , 0 ) ;
q = buf + 0x2000000 ;
2020-05-06 06:40:25 +03:00
translate_branch ( & instr , q , p ) ;
patch_instruction ( q , instr ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( p , addr ) ) ;
check ( instr_is_branch_to_addr ( q , addr ) ) ;
2020-05-06 06:40:32 +03:00
check ( ppc_inst_equal ( ppc_inst_read ( q ) , ppc_inst ( 0x4a000000 ) ) ) ;
2008-06-24 05:32:32 +04:00
/* Maximum positive case, move x to x - 32 MB + 4 */
p = buf + 0x2000000 ;
addr = ( unsigned long ) p ;
patch_branch ( p , addr , 0 ) ;
q = buf + 4 ;
2020-05-06 06:40:25 +03:00
translate_branch ( & instr , q , p ) ;
patch_instruction ( q , instr ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( p , addr ) ) ;
check ( instr_is_branch_to_addr ( q , addr ) ) ;
2020-05-06 06:40:32 +03:00
check ( ppc_inst_equal ( ppc_inst_read ( q ) , ppc_inst ( 0x49fffffc ) ) ) ;
2008-06-24 05:32:32 +04:00
/* Jump to x + 16 MB moved to x + 20 MB */
p = buf ;
addr = 0x1000000 + ( unsigned long ) buf ;
patch_branch ( p , addr , BRANCH_SET_LINK ) ;
q = buf + 0x1400000 ;
2020-05-06 06:40:25 +03:00
translate_branch ( & instr , q , p ) ;
patch_instruction ( q , instr ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( p , addr ) ) ;
check ( instr_is_branch_to_addr ( q , addr ) ) ;
/* Jump to x + 16 MB moved to x - 16 MB + 4 */
p = buf + 0x1000000 ;
addr = 0x2000000 + ( unsigned long ) buf ;
patch_branch ( p , addr , 0 ) ;
q = buf + 4 ;
2020-05-06 06:40:25 +03:00
translate_branch ( & instr , q , p ) ;
patch_instruction ( q , instr ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( p , addr ) ) ;
check ( instr_is_branch_to_addr ( q , addr ) ) ;
/* Conditional branch tests */
/* Simple case, branch to self moved a little */
p = buf ;
addr = ( unsigned long ) p ;
2020-05-06 06:40:25 +03:00
create_cond_branch ( & instr , p , addr , 0 ) ;
patch_instruction ( p , instr ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( p , addr ) ) ;
2020-05-06 06:40:40 +03:00
q = buf + 4 ;
2020-05-06 06:40:25 +03:00
translate_branch ( & instr , q , p ) ;
patch_instruction ( q , instr ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( q , addr ) ) ;
/* Maximum negative case, move b . to addr + 32 KB */
p = buf ;
addr = ( unsigned long ) p ;
2020-05-06 06:40:25 +03:00
create_cond_branch ( & instr , p , addr , 0xFFFFFFFC ) ;
patch_instruction ( p , instr ) ;
2008-06-24 05:32:32 +04:00
q = buf + 0x8000 ;
2020-05-06 06:40:25 +03:00
translate_branch ( & instr , q , p ) ;
patch_instruction ( q , instr ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( p , addr ) ) ;
check ( instr_is_branch_to_addr ( q , addr ) ) ;
2020-05-06 06:40:32 +03:00
check ( ppc_inst_equal ( ppc_inst_read ( q ) , ppc_inst ( 0x43ff8000 ) ) ) ;
2008-06-24 05:32:32 +04:00
/* Maximum positive case, move x to x - 32 KB + 4 */
p = buf + 0x8000 ;
addr = ( unsigned long ) p ;
2020-05-06 06:40:25 +03:00
create_cond_branch ( & instr , p , addr , 0xFFFFFFFC ) ;
patch_instruction ( p , instr ) ;
2008-06-24 05:32:32 +04:00
q = buf + 4 ;
2020-05-06 06:40:25 +03:00
translate_branch ( & instr , q , p ) ;
patch_instruction ( q , instr ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( p , addr ) ) ;
check ( instr_is_branch_to_addr ( q , addr ) ) ;
2020-05-06 06:40:32 +03:00
check ( ppc_inst_equal ( ppc_inst_read ( q ) , ppc_inst ( 0x43ff7ffc ) ) ) ;
2008-06-24 05:32:32 +04:00
/* Jump to x + 12 KB moved to x + 20 KB */
p = buf ;
addr = 0x3000 + ( unsigned long ) buf ;
2020-05-06 06:40:25 +03:00
create_cond_branch ( & instr , p , addr , BRANCH_SET_LINK ) ;
patch_instruction ( p , instr ) ;
2008-06-24 05:32:32 +04:00
q = buf + 0x5000 ;
2020-05-06 06:40:25 +03:00
translate_branch ( & instr , q , p ) ;
patch_instruction ( q , instr ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( p , addr ) ) ;
check ( instr_is_branch_to_addr ( q , addr ) ) ;
/* Jump to x + 8 KB moved to x - 8 KB + 4 */
p = buf + 0x2000 ;
addr = 0x4000 + ( unsigned long ) buf ;
2020-05-06 06:40:25 +03:00
create_cond_branch ( & instr , p , addr , 0 ) ;
patch_instruction ( p , instr ) ;
2008-06-24 05:32:32 +04:00
q = buf + 4 ;
2020-05-06 06:40:25 +03:00
translate_branch ( & instr , q , p ) ;
patch_instruction ( q , instr ) ;
2008-06-24 05:32:32 +04:00
check ( instr_is_branch_to_addr ( p , addr ) ) ;
check ( instr_is_branch_to_addr ( q , addr ) ) ;
/* Free the buffer we were using */
vfree ( buf ) ;
}
2020-05-06 06:40:44 +03:00
# ifdef CONFIG_PPC64
static void __init test_prefixed_patching ( void )
{
extern unsigned int code_patching_test1 [ ] ;
extern unsigned int code_patching_test1_expected [ ] ;
extern unsigned int end_code_patching_test1 [ ] ;
__patch_instruction ( ( struct ppc_inst * ) code_patching_test1 ,
ppc_inst_prefix ( OP_PREFIX < < 26 , 0x00000000 ) ,
( struct ppc_inst * ) code_patching_test1 ) ;
check ( ! memcmp ( code_patching_test1 ,
code_patching_test1_expected ,
sizeof ( unsigned int ) *
( end_code_patching_test1 - code_patching_test1 ) ) ) ;
}
# else
static inline void test_prefixed_patching ( void ) { }
# endif
2008-06-24 05:32:32 +04:00
static int __init test_code_patching ( void )
{
printk ( KERN_DEBUG " Running code patching self-tests ... \n " ) ;
test_branch_iform ( ) ;
test_branch_bform ( ) ;
test_create_function_call ( ) ;
test_translate_branch ( ) ;
2020-05-06 06:40:44 +03:00
test_prefixed_patching ( ) ;
2008-06-24 05:32:32 +04:00
return 0 ;
}
late_initcall ( test_code_patching ) ;
# endif /* CONFIG_CODE_PATCHING_SELFTEST */