mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
Merge remote-tracking branch 'lsk/v3.10/topic/arm64-perf' into linux-linaro-lsk
Conflicts: arch/arm64/kernel/Makefile
This commit is contained in:
@@ -39,6 +39,8 @@ config ARM64
|
||||
select HAVE_MEMBLOCK
|
||||
select HAVE_PATA_PLATFORM
|
||||
select HAVE_PERF_EVENTS
|
||||
select HAVE_PERF_REGS
|
||||
select HAVE_PERF_USER_STACK_DUMP
|
||||
select IRQ_DOMAIN
|
||||
select MODULES_USE_ELF_RELA
|
||||
select NO_BOOTMEM
|
||||
|
||||
@@ -228,7 +228,7 @@ static inline compat_uptr_t ptr_to_compat(void __user *uptr)
|
||||
return (u32)(unsigned long)uptr;
|
||||
}
|
||||
|
||||
#define compat_user_stack_pointer() (current_pt_regs()->compat_sp)
|
||||
#define compat_user_stack_pointer() (user_stack_pointer(current_pt_regs()))
|
||||
|
||||
static inline void __user *arch_compat_alloc_user_space(long len)
|
||||
{
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
|
||||
/* Architecturally defined mapping between AArch32 and AArch64 registers */
|
||||
#define compat_usr(x) regs[(x)]
|
||||
#define compat_fp regs[11]
|
||||
#define compat_sp regs[13]
|
||||
#define compat_lr regs[14]
|
||||
#define compat_sp_hyp regs[15]
|
||||
@@ -132,7 +133,7 @@ struct pt_regs {
|
||||
(!((regs)->pstate & PSR_F_BIT))
|
||||
|
||||
#define user_stack_pointer(regs) \
|
||||
((regs)->sp)
|
||||
(!compat_user_mode(regs)) ? ((regs)->sp) : ((regs)->compat_sp)
|
||||
|
||||
/*
|
||||
* Are the current registers suitable for user mode? (used to maintain
|
||||
|
||||
@@ -9,6 +9,7 @@ header-y += byteorder.h
|
||||
header-y += fcntl.h
|
||||
header-y += hwcap.h
|
||||
header-y += kvm_para.h
|
||||
header-y += perf_regs.h
|
||||
header-y += param.h
|
||||
header-y += ptrace.h
|
||||
header-y += setup.h
|
||||
|
||||
40
arch/arm64/include/uapi/asm/perf_regs.h
Normal file
40
arch/arm64/include/uapi/asm/perf_regs.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef _ASM_ARM64_PERF_REGS_H
|
||||
#define _ASM_ARM64_PERF_REGS_H
|
||||
|
||||
enum perf_event_arm_regs {
|
||||
PERF_REG_ARM64_X0,
|
||||
PERF_REG_ARM64_X1,
|
||||
PERF_REG_ARM64_X2,
|
||||
PERF_REG_ARM64_X3,
|
||||
PERF_REG_ARM64_X4,
|
||||
PERF_REG_ARM64_X5,
|
||||
PERF_REG_ARM64_X6,
|
||||
PERF_REG_ARM64_X7,
|
||||
PERF_REG_ARM64_X8,
|
||||
PERF_REG_ARM64_X9,
|
||||
PERF_REG_ARM64_X10,
|
||||
PERF_REG_ARM64_X11,
|
||||
PERF_REG_ARM64_X12,
|
||||
PERF_REG_ARM64_X13,
|
||||
PERF_REG_ARM64_X14,
|
||||
PERF_REG_ARM64_X15,
|
||||
PERF_REG_ARM64_X16,
|
||||
PERF_REG_ARM64_X17,
|
||||
PERF_REG_ARM64_X18,
|
||||
PERF_REG_ARM64_X19,
|
||||
PERF_REG_ARM64_X20,
|
||||
PERF_REG_ARM64_X21,
|
||||
PERF_REG_ARM64_X22,
|
||||
PERF_REG_ARM64_X23,
|
||||
PERF_REG_ARM64_X24,
|
||||
PERF_REG_ARM64_X25,
|
||||
PERF_REG_ARM64_X26,
|
||||
PERF_REG_ARM64_X27,
|
||||
PERF_REG_ARM64_X28,
|
||||
PERF_REG_ARM64_X29,
|
||||
PERF_REG_ARM64_LR,
|
||||
PERF_REG_ARM64_SP,
|
||||
PERF_REG_ARM64_PC,
|
||||
PERF_REG_ARM64_MAX,
|
||||
};
|
||||
#endif /* _ASM_ARM64_PERF_REGS_H */
|
||||
@@ -16,8 +16,9 @@ arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
|
||||
arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
|
||||
arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o
|
||||
arm64-obj-$(CONFIG_SMP) += topology.o
|
||||
arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
|
||||
arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
|
||||
arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
|
||||
arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
|
||||
arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
|
||||
arm64-obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
|
||||
arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o
|
||||
|
||||
@@ -1347,8 +1347,8 @@ early_initcall(init_hw_perf_events);
|
||||
* Callchain handling code.
|
||||
*/
|
||||
struct frame_tail {
|
||||
struct frame_tail __user *fp;
|
||||
unsigned long lr;
|
||||
struct frame_tail __user *fp;
|
||||
unsigned long lr;
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
@@ -1385,22 +1385,84 @@ user_backtrace(struct frame_tail __user *tail,
|
||||
return buftail.fp;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
/*
|
||||
* The registers we're interested in are at the end of the variable
|
||||
* length saved register structure. The fp points at the end of this
|
||||
* structure so the address of this struct is:
|
||||
* (struct compat_frame_tail *)(xxx->fp)-1
|
||||
*
|
||||
* This code has been adapted from the ARM OProfile support.
|
||||
*/
|
||||
struct compat_frame_tail {
|
||||
compat_uptr_t fp; /* a (struct compat_frame_tail *) in compat mode */
|
||||
u32 sp;
|
||||
u32 lr;
|
||||
} __attribute__((packed));
|
||||
|
||||
static struct compat_frame_tail __user *
|
||||
compat_user_backtrace(struct compat_frame_tail __user *tail,
|
||||
struct perf_callchain_entry *entry)
|
||||
{
|
||||
struct compat_frame_tail buftail;
|
||||
unsigned long err;
|
||||
|
||||
/* Also check accessibility of one struct frame_tail beyond */
|
||||
if (!access_ok(VERIFY_READ, tail, sizeof(buftail)))
|
||||
return NULL;
|
||||
|
||||
pagefault_disable();
|
||||
err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
|
||||
pagefault_enable();
|
||||
|
||||
if (err)
|
||||
return NULL;
|
||||
|
||||
perf_callchain_store(entry, buftail.lr);
|
||||
|
||||
/*
|
||||
* Frame pointers should strictly progress back up the stack
|
||||
* (towards higher addresses).
|
||||
*/
|
||||
if (tail + 1 >= (struct compat_frame_tail __user *)
|
||||
compat_ptr(buftail.fp))
|
||||
return NULL;
|
||||
|
||||
return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1;
|
||||
}
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
void perf_callchain_user(struct perf_callchain_entry *entry,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct frame_tail __user *tail;
|
||||
|
||||
if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
|
||||
/* We don't support guest os callchain now */
|
||||
return;
|
||||
}
|
||||
|
||||
perf_callchain_store(entry, regs->pc);
|
||||
tail = (struct frame_tail __user *)regs->regs[29];
|
||||
|
||||
while (entry->nr < PERF_MAX_STACK_DEPTH &&
|
||||
tail && !((unsigned long)tail & 0xf))
|
||||
tail = user_backtrace(tail, entry);
|
||||
if (!compat_user_mode(regs)) {
|
||||
/* AARCH64 mode */
|
||||
struct frame_tail __user *tail;
|
||||
|
||||
tail = (struct frame_tail __user *)regs->regs[29];
|
||||
|
||||
while (entry->nr < PERF_MAX_STACK_DEPTH &&
|
||||
tail && !((unsigned long)tail & 0xf))
|
||||
tail = user_backtrace(tail, entry);
|
||||
} else {
|
||||
#ifdef CONFIG_COMPAT
|
||||
/* AARCH32 compat mode */
|
||||
struct compat_frame_tail __user *tail;
|
||||
|
||||
tail = (struct compat_frame_tail __user *)regs->compat_fp - 1;
|
||||
|
||||
while ((entry->nr < PERF_MAX_STACK_DEPTH) &&
|
||||
tail && !((unsigned long)tail & 0x3))
|
||||
tail = compat_user_backtrace(tail, entry);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1428,6 +1490,7 @@ void perf_callchain_kernel(struct perf_callchain_entry *entry,
|
||||
frame.fp = regs->regs[29];
|
||||
frame.sp = regs->sp;
|
||||
frame.pc = regs->pc;
|
||||
|
||||
walk_stackframe(&frame, callchain_trace, entry);
|
||||
}
|
||||
|
||||
|
||||
46
arch/arm64/kernel/perf_regs.c
Normal file
46
arch/arm64/kernel/perf_regs.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/bug.h>
|
||||
|
||||
#include <asm/compat.h>
|
||||
#include <asm/perf_regs.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
u64 perf_reg_value(struct pt_regs *regs, int idx)
|
||||
{
|
||||
if (WARN_ON_ONCE((u32)idx >= PERF_REG_ARM64_MAX))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Compat (i.e. 32 bit) mode:
|
||||
* - PC has been set in the pt_regs struct in kernel_entry,
|
||||
* - Handle SP and LR here.
|
||||
*/
|
||||
if (compat_user_mode(regs)) {
|
||||
if ((u32)idx == PERF_REG_ARM64_SP)
|
||||
return regs->compat_sp;
|
||||
if ((u32)idx == PERF_REG_ARM64_LR)
|
||||
return regs->compat_lr;
|
||||
}
|
||||
|
||||
return regs->regs[idx];
|
||||
}
|
||||
|
||||
#define REG_RESERVED (~((1ULL << PERF_REG_ARM64_MAX) - 1))
|
||||
|
||||
int perf_reg_validate(u64 mask)
|
||||
{
|
||||
if (!mask || mask & REG_RESERVED)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 perf_reg_abi(struct task_struct *task)
|
||||
{
|
||||
if (is_compat_thread(task_thread_info(task)))
|
||||
return PERF_SAMPLE_REGS_ABI_32;
|
||||
else
|
||||
return PERF_SAMPLE_REGS_ABI_64;
|
||||
}
|
||||
Reference in New Issue
Block a user