Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * arm64 callchain support
0004  *
0005  * Copyright (C) 2015 ARM Limited
0006  */
0007 #include <linux/perf_event.h>
0008 #include <linux/stacktrace.h>
0009 #include <linux/uaccess.h>
0010 
0011 #include <asm/pointer_auth.h>
0012 
0013 struct frame_tail {
0014     struct frame_tail   __user *fp;
0015     unsigned long       lr;
0016 } __attribute__((packed));
0017 
0018 /*
0019  * Get the return address for a single stackframe and return a pointer to the
0020  * next frame tail.
0021  */
0022 static struct frame_tail __user *
0023 user_backtrace(struct frame_tail __user *tail,
0024            struct perf_callchain_entry_ctx *entry)
0025 {
0026     struct frame_tail buftail;
0027     unsigned long err;
0028     unsigned long lr;
0029 
0030     /* Also check accessibility of one struct frame_tail beyond */
0031     if (!access_ok(tail, sizeof(buftail)))
0032         return NULL;
0033 
0034     pagefault_disable();
0035     err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
0036     pagefault_enable();
0037 
0038     if (err)
0039         return NULL;
0040 
0041     lr = ptrauth_strip_insn_pac(buftail.lr);
0042 
0043     perf_callchain_store(entry, lr);
0044 
0045     /*
0046      * Frame pointers should strictly progress back up the stack
0047      * (towards higher addresses).
0048      */
0049     if (tail >= buftail.fp)
0050         return NULL;
0051 
0052     return buftail.fp;
0053 }
0054 
0055 #ifdef CONFIG_COMPAT
0056 /*
0057  * The registers we're interested in are at the end of the variable
0058  * length saved register structure. The fp points at the end of this
0059  * structure so the address of this struct is:
0060  * (struct compat_frame_tail *)(xxx->fp)-1
0061  *
0062  * This code has been adapted from the ARM OProfile support.
0063  */
0064 struct compat_frame_tail {
0065     compat_uptr_t   fp; /* a (struct compat_frame_tail *) in compat mode */
0066     u32     sp;
0067     u32     lr;
0068 } __attribute__((packed));
0069 
0070 static struct compat_frame_tail __user *
0071 compat_user_backtrace(struct compat_frame_tail __user *tail,
0072               struct perf_callchain_entry_ctx *entry)
0073 {
0074     struct compat_frame_tail buftail;
0075     unsigned long err;
0076 
0077     /* Also check accessibility of one struct frame_tail beyond */
0078     if (!access_ok(tail, sizeof(buftail)))
0079         return NULL;
0080 
0081     pagefault_disable();
0082     err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
0083     pagefault_enable();
0084 
0085     if (err)
0086         return NULL;
0087 
0088     perf_callchain_store(entry, buftail.lr);
0089 
0090     /*
0091      * Frame pointers should strictly progress back up the stack
0092      * (towards higher addresses).
0093      */
0094     if (tail + 1 >= (struct compat_frame_tail __user *)
0095             compat_ptr(buftail.fp))
0096         return NULL;
0097 
0098     return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1;
0099 }
0100 #endif /* CONFIG_COMPAT */
0101 
0102 void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
0103              struct pt_regs *regs)
0104 {
0105     if (perf_guest_state()) {
0106         /* We don't support guest os callchain now */
0107         return;
0108     }
0109 
0110     perf_callchain_store(entry, regs->pc);
0111 
0112     if (!compat_user_mode(regs)) {
0113         /* AARCH64 mode */
0114         struct frame_tail __user *tail;
0115 
0116         tail = (struct frame_tail __user *)regs->regs[29];
0117 
0118         while (entry->nr < entry->max_stack &&
0119                tail && !((unsigned long)tail & 0x7))
0120             tail = user_backtrace(tail, entry);
0121     } else {
0122 #ifdef CONFIG_COMPAT
0123         /* AARCH32 compat mode */
0124         struct compat_frame_tail __user *tail;
0125 
0126         tail = (struct compat_frame_tail __user *)regs->compat_fp - 1;
0127 
0128         while ((entry->nr < entry->max_stack) &&
0129             tail && !((unsigned long)tail & 0x3))
0130             tail = compat_user_backtrace(tail, entry);
0131 #endif
0132     }
0133 }
0134 
0135 static bool callchain_trace(void *data, unsigned long pc)
0136 {
0137     struct perf_callchain_entry_ctx *entry = data;
0138     return perf_callchain_store(entry, pc) == 0;
0139 }
0140 
0141 void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
0142                struct pt_regs *regs)
0143 {
0144     if (perf_guest_state()) {
0145         /* We don't support guest os callchain now */
0146         return;
0147     }
0148 
0149     arch_stack_walk(callchain_trace, entry, current, regs);
0150 }
0151 
0152 unsigned long perf_instruction_pointer(struct pt_regs *regs)
0153 {
0154     if (perf_guest_state())
0155         return perf_guest_get_ip();
0156 
0157     return instruction_pointer(regs);
0158 }
0159 
0160 unsigned long perf_misc_flags(struct pt_regs *regs)
0161 {
0162     unsigned int guest_state = perf_guest_state();
0163     int misc = 0;
0164 
0165     if (guest_state) {
0166         if (guest_state & PERF_GUEST_USER)
0167             misc |= PERF_RECORD_MISC_GUEST_USER;
0168         else
0169             misc |= PERF_RECORD_MISC_GUEST_KERNEL;
0170     } else {
0171         if (user_mode(regs))
0172             misc |= PERF_RECORD_MISC_USER;
0173         else
0174             misc |= PERF_RECORD_MISC_KERNEL;
0175     }
0176 
0177     return misc;
0178 }