Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/compat.h>
0003 #include <linux/errno.h>
0004 #include <linux/kernel.h>
0005 #include <linux/perf_event.h>
0006 #include <linux/bug.h>
0007 #include <linux/sched/task_stack.h>
0008 
0009 #include <asm/perf_regs.h>
0010 #include <asm/ptrace.h>
0011 
0012 u64 perf_reg_value(struct pt_regs *regs, int idx)
0013 {
0014     if (WARN_ON_ONCE((u32)idx >= PERF_REG_ARM64_MAX))
0015         return 0;
0016 
0017     /*
0018      * Our handling of compat tasks (PERF_SAMPLE_REGS_ABI_32) is weird, but
0019      * we're stuck with it for ABI compatibility reasons.
0020      *
0021      * For a 32-bit consumer inspecting a 32-bit task, then it will look at
0022      * the first 16 registers (see arch/arm/include/uapi/asm/perf_regs.h).
0023      * These correspond directly to a prefix of the registers saved in our
0024      * 'struct pt_regs', with the exception of the PC, so we copy that down
0025      * (x15 corresponds to SP_hyp in the architecture).
0026      *
0027      * So far, so good.
0028      *
0029      * The oddity arises when a 64-bit consumer looks at a 32-bit task and
0030      * asks for registers beyond PERF_REG_ARM_MAX. In this case, we return
0031      * SP_usr, LR_usr and PC in the positions where the AArch64 SP, LR and
0032      * PC registers would normally live. The initial idea was to allow a
0033      * 64-bit unwinder to unwind a 32-bit task and, although it's not clear
0034      * how well that works in practice, somebody might be relying on it.
0035      *
0036      * At the time we make a sample, we don't know whether the consumer is
0037      * 32-bit or 64-bit, so we have to cater for both possibilities.
0038      */
0039     if (compat_user_mode(regs)) {
0040         if ((u32)idx == PERF_REG_ARM64_SP)
0041             return regs->compat_sp;
0042         if ((u32)idx == PERF_REG_ARM64_LR)
0043             return regs->compat_lr;
0044         if (idx == 15)
0045             return regs->pc;
0046     }
0047 
0048     if ((u32)idx == PERF_REG_ARM64_SP)
0049         return regs->sp;
0050 
0051     if ((u32)idx == PERF_REG_ARM64_PC)
0052         return regs->pc;
0053 
0054     return regs->regs[idx];
0055 }
0056 
0057 #define REG_RESERVED (~((1ULL << PERF_REG_ARM64_MAX) - 1))
0058 
0059 int perf_reg_validate(u64 mask)
0060 {
0061     if (!mask || mask & REG_RESERVED)
0062         return -EINVAL;
0063 
0064     return 0;
0065 }
0066 
0067 u64 perf_reg_abi(struct task_struct *task)
0068 {
0069     if (is_compat_thread(task_thread_info(task)))
0070         return PERF_SAMPLE_REGS_ABI_32;
0071     else
0072         return PERF_SAMPLE_REGS_ABI_64;
0073 }
0074 
0075 void perf_get_regs_user(struct perf_regs *regs_user,
0076             struct pt_regs *regs)
0077 {
0078     regs_user->regs = task_pt_regs(current);
0079     regs_user->abi = perf_reg_abi(current);
0080 }