2020-03-20 13:20:18 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Performance counter callchain support - powerpc architecture code
*
* Copyright © 2009 Paul Mackerras , IBM Corporation .
*/
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/perf_event.h>
# include <linux/percpu.h>
# include <linux/uaccess.h>
# include <linux/mm.h>
# include <asm/ptrace.h>
# include <asm/sigcontext.h>
# include <asm/ucontext.h>
# include <asm/vdso.h>
# include <asm/pte-walk.h>
# include "callchain.h"
/*
* On 64 - bit we don ' t want to invoke hash_page on user addresses from
* interrupt context , so if the access faults , we read the page tables
2020-11-11 15:01:51 +03:00
* to find which page ( if any ) is mapped and access it directly . Radix
* has no need for this so it doesn ' t use read_user_stack_slow .
2020-03-20 13:20:18 +03:00
*/
2020-04-07 00:00:22 +03:00
int read_user_stack_slow ( const void __user * ptr , void * buf , int nb )
2020-03-20 13:20:18 +03:00
{
2020-05-05 10:17:14 +03:00
2020-03-20 13:20:18 +03:00
unsigned long addr = ( unsigned long ) ptr ;
unsigned long offset ;
2020-05-05 10:17:14 +03:00
struct page * page ;
2020-03-20 13:20:18 +03:00
void * kaddr ;
2020-06-08 07:40:55 +03:00
if ( get_user_page_fast_only ( addr , FOLL_WRITE , & page ) ) {
2020-05-05 10:17:14 +03:00
kaddr = page_address ( page ) ;
2020-03-20 13:20:18 +03:00
2020-05-05 10:17:14 +03:00
/* align address to page boundary */
offset = addr & ~ PAGE_MASK ;
2020-03-20 13:20:18 +03:00
2020-05-05 10:17:14 +03:00
memcpy ( buf , kaddr + offset , nb ) ;
put_page ( page ) ;
return 0 ;
}
return - EFAULT ;
2020-03-20 13:20:18 +03:00
}
2020-04-07 00:00:22 +03:00
static int read_user_stack_64 ( const unsigned long __user * ptr , unsigned long * ret )
2020-03-20 13:20:18 +03:00
{
2020-04-07 00:00:22 +03:00
return __read_user_stack ( ptr , ret , sizeof ( * ret ) ) ;
2020-03-20 13:20:18 +03:00
}
/*
* 64 - bit user processes use the same stack frame for RT and non - RT signals .
*/
struct signal_frame_64 {
char dummy [ __SIGNAL_FRAMESIZE ] ;
struct ucontext uc ;
unsigned long unused [ 2 ] ;
unsigned int tramp [ 6 ] ;
struct siginfo * pinfo ;
void * puc ;
struct siginfo info ;
char abigap [ 288 ] ;
} ;
static int is_sigreturn_64_address ( unsigned long nip , unsigned long fp )
{
if ( nip = = fp + offsetof ( struct signal_frame_64 , tramp ) )
return 1 ;
2020-09-27 12:16:33 +03:00
if ( current - > mm - > context . vdso & &
nip = = VDSO64_SYMBOL ( current - > mm - > context . vdso , sigtramp_rt64 ) )
2020-03-20 13:20:18 +03:00
return 1 ;
return 0 ;
}
/*
* Do some sanity checking on the signal frame pointed to by sp .
* We check the pinfo and puc pointers in the frame .
*/
static int sane_signal_64_frame ( unsigned long sp )
{
struct signal_frame_64 __user * sf ;
unsigned long pinfo , puc ;
sf = ( struct signal_frame_64 __user * ) sp ;
if ( read_user_stack_64 ( ( unsigned long __user * ) & sf - > pinfo , & pinfo ) | |
read_user_stack_64 ( ( unsigned long __user * ) & sf - > puc , & puc ) )
return 0 ;
return pinfo = = ( unsigned long ) & sf - > info & &
puc = = ( unsigned long ) & sf - > uc ;
}
void perf_callchain_user_64 ( struct perf_callchain_entry_ctx * entry ,
struct pt_regs * regs )
{
unsigned long sp , next_sp ;
unsigned long next_ip ;
unsigned long lr ;
long level = 0 ;
struct signal_frame_64 __user * sigframe ;
unsigned long __user * fp , * uregs ;
next_ip = perf_instruction_pointer ( regs ) ;
lr = regs - > link ;
sp = regs - > gpr [ 1 ] ;
perf_callchain_store ( entry , next_ip ) ;
while ( entry - > nr < entry - > max_stack ) {
fp = ( unsigned long __user * ) sp ;
if ( invalid_user_sp ( sp ) | | read_user_stack_64 ( fp , & next_sp ) )
return ;
if ( level > 0 & & read_user_stack_64 ( & fp [ 2 ] , & next_ip ) )
return ;
/*
* Note : the next_sp - sp > = signal frame size check
* is true when next_sp < sp , which can happen when
* transitioning from an alternate signal stack to the
* normal stack .
*/
if ( next_sp - sp > = sizeof ( struct signal_frame_64 ) & &
( is_sigreturn_64_address ( next_ip , sp ) | |
( level < = 1 & & is_sigreturn_64_address ( lr , sp ) ) ) & &
sane_signal_64_frame ( sp ) ) {
/*
* This looks like an signal frame
*/
sigframe = ( struct signal_frame_64 __user * ) sp ;
uregs = sigframe - > uc . uc_mcontext . gp_regs ;
if ( read_user_stack_64 ( & uregs [ PT_NIP ] , & next_ip ) | |
read_user_stack_64 ( & uregs [ PT_LNK ] , & lr ) | |
read_user_stack_64 ( & uregs [ PT_R1 ] , & sp ) )
return ;
level = 0 ;
perf_callchain_store_context ( entry , PERF_CONTEXT_USER ) ;
perf_callchain_store ( entry , next_ip ) ;
continue ;
}
if ( level = = 0 )
next_ip = lr ;
perf_callchain_store ( entry , next_ip ) ;
+ + level ;
sp = next_sp ;
}
}