forked from Minki/linux
nds32 patches for 4.19
Here is the nds32 patch set based on 4.19-rc2. Contained in here are the bug fixes, building error fixes and ftrace support for nds32. These are the LTP20170427 testing results. Total Tests: 1902 Total Skipped Tests: 592 Total Failures: 420 Kernel Version: 4.19.0-rc2-00018-g2c9d30cc16f0-dirty Machine Architecture: nds32 Signed-off-by: Greentime Hu <greentime@andestech.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.17 (GNU/Linux) iQIcBAABAgAGBQJbj0lJAAoJEHfB0l0b2JxElb4P/3YWwh0q3kqbUxMmfm0Dp2zv SIFQwD2N1HGs0ZW4vB4hVEYhMz3qWJdkzRRzkdLxAdPVnJ0Prc6jBmQtwvBmYcWG zWGcUkF1fGSQ+2XAqXZeqbUd4/GIApcdOVimquOZFgKnvprhymwqc2jlf3Zed42B EM1sdxmP2ADsNQo+sz/BmhbLtBNzfFxwSo9KmstArVNNkkwOVTnzowzg6PxYSvMu 3cVFk4iYcoAVc8JEbUN/pObq9mdVs0xnDbilgWAdpFBcnk020V8GTB0PbY3PnD9G PRfYV/4zwkVAviqkbBV8LXQD4joR9vSSjp+tk9sT4WUYXK4EjUyHvg0iWiV5pnAB NsFzlH9WWQWnp3VSVBfP2mIE2j2A3iGzUZVfDQbI2lwNI4GI0AKUZtscpFDRz/Pw J0s/FdXilKWDviefHcX+C31dkH0ZPCm4lymGWgv8le158Bo6BOTo6aRAsTnP33IN VOdODET0zf14v+5FooL/5T75EiEoS1MtLC9cMA1U4XZ3p3GrEOiSuNIMetZQ+cHd Z+FPflfAyDaSBFJzRyLohnOBOaWWDNe6CyGMzIKZ4qSRz0BltW12Ig6LfMUZXEMN U0jmS4b8rWYHiOhSOCKsg95GBYGDUocJj1RRmoBLy0+Mq1yf+V/r/GnACouFOTEZ VQgbEAzh2rcLJJi5GoFp =DJCS -----END PGP SIGNATURE----- Merge tag 'nds32-for-linus-4.19-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/greentime/linux Pull nds32 updates from Greentime Hu: "Contained in here are the bug fixes, building error fixes and ftrace support for nds32" * tag 'nds32-for-linus-4.19-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/greentime/linux: nds32: linker script: GCOV kernel may refers data in __exit nds32: fix build error because of wrong semicolon nds32: Fix a kernel panic issue because of wrong frame pointer access. nds32: Only print one page of stack when die to prevent printing too much information. nds32: Add macro definition for offset of lp register on stack nds32: Remove the deprecated ABI implementation nds32/stack: Get real return address by using ftrace_graph_ret_addr nds32/ftrace: Support dynamic function graph tracer nds32/ftrace: Support dynamic function tracer nds32/ftrace: Add RECORD_MCOUNT support nds32/ftrace: Support static function graph tracer nds32/ftrace: Support static function tracer nds32: Extract the checking and getting pointer to a macro nds32: Clean up the coding style nds32: Fix get_user/put_user macro expand pointer problem nds32: Fix empty call trace nds32: add NULL entry to the end of_device_id array nds32: fix logic for module
This commit is contained in:
commit
d0c1db1da4
@ -40,6 +40,10 @@ config NDS32
|
||||
select NO_IOPORT_MAP
|
||||
select RTC_LIB
|
||||
select THREAD_INFO_IN_TASK
|
||||
select HAVE_FUNCTION_TRACER
|
||||
select HAVE_FUNCTION_GRAPH_TRACER
|
||||
select HAVE_FTRACE_MCOUNT_RECORD
|
||||
select HAVE_DYNAMIC_FTRACE
|
||||
help
|
||||
Andes(nds32) Linux support.
|
||||
|
||||
|
@ -5,6 +5,10 @@ KBUILD_DEFCONFIG := defconfig
|
||||
|
||||
comma = ,
|
||||
|
||||
ifdef CONFIG_FUNCTION_TRACER
|
||||
arch-y += -malways-save-lp -mno-relax
|
||||
endif
|
||||
|
||||
KBUILD_CFLAGS += $(call cc-option, -mno-sched-prolog-epilog)
|
||||
KBUILD_CFLAGS += -mcmodel=large
|
||||
|
||||
|
@ -121,9 +121,9 @@ struct elf32_hdr;
|
||||
*/
|
||||
#define ELF_CLASS ELFCLASS32
|
||||
#ifdef __NDS32_EB__
|
||||
#define ELF_DATA ELFDATA2MSB;
|
||||
#define ELF_DATA ELFDATA2MSB
|
||||
#else
|
||||
#define ELF_DATA ELFDATA2LSB;
|
||||
#define ELF_DATA ELFDATA2LSB
|
||||
#endif
|
||||
#define ELF_ARCH EM_NDS32
|
||||
#define USE_ELF_CORE_DUMP
|
||||
|
46
arch/nds32/include/asm/ftrace.h
Normal file
46
arch/nds32/include/asm/ftrace.h
Normal file
@ -0,0 +1,46 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef __ASM_NDS32_FTRACE_H
|
||||
#define __ASM_NDS32_FTRACE_H
|
||||
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
|
||||
#define HAVE_FUNCTION_GRAPH_FP_TEST
|
||||
|
||||
#define MCOUNT_ADDR ((unsigned long)(_mcount))
|
||||
/* mcount call is composed of three instructions:
|
||||
* sethi + ori + jral
|
||||
*/
|
||||
#define MCOUNT_INSN_SIZE 12
|
||||
|
||||
extern void _mcount(unsigned long parent_ip);
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
|
||||
#define FTRACE_ADDR ((unsigned long)_ftrace_caller)
|
||||
|
||||
#ifdef __NDS32_EL__
|
||||
#define INSN_NOP 0x09000040
|
||||
#define INSN_SIZE(insn) (((insn & 0x00000080) == 0) ? 4 : 2)
|
||||
#define IS_SETHI(insn) ((insn & 0x000000fe) == 0x00000046)
|
||||
#define ENDIAN_CONVERT(insn) be32_to_cpu(insn)
|
||||
#else /* __NDS32_EB__ */
|
||||
#define INSN_NOP 0x40000009
|
||||
#define INSN_SIZE(insn) (((insn & 0x80000000) == 0) ? 4 : 2)
|
||||
#define IS_SETHI(insn) ((insn & 0xfe000000) == 0x46000000)
|
||||
#define ENDIAN_CONVERT(insn) (insn)
|
||||
#endif
|
||||
|
||||
extern void _ftrace_caller(unsigned long parent_ip);
|
||||
static inline unsigned long ftrace_call_adjust(unsigned long addr)
|
||||
{
|
||||
return addr;
|
||||
}
|
||||
struct dyn_arch_ftrace {
|
||||
};
|
||||
|
||||
#endif /* CONFIG_DYNAMIC_FTRACE */
|
||||
|
||||
#endif /* CONFIG_FUNCTION_TRACER */
|
||||
|
||||
#endif /* __ASM_NDS32_FTRACE_H */
|
@ -17,6 +17,7 @@
|
||||
#else
|
||||
#define FP_OFFSET (-2)
|
||||
#endif
|
||||
#define LP_OFFSET (-1)
|
||||
|
||||
extern void __init early_trap_init(void);
|
||||
static inline void GIE_ENABLE(void)
|
||||
|
@ -38,7 +38,7 @@ struct exception_table_entry {
|
||||
extern int fixup_exception(struct pt_regs *regs);
|
||||
|
||||
#define KERNEL_DS ((mm_segment_t) { ~0UL })
|
||||
#define USER_DS ((mm_segment_t) {TASK_SIZE - 1})
|
||||
#define USER_DS ((mm_segment_t) {TASK_SIZE - 1})
|
||||
|
||||
#define get_ds() (KERNEL_DS)
|
||||
#define get_fs() (current_thread_info()->addr_limit)
|
||||
@ -49,11 +49,11 @@ static inline void set_fs(mm_segment_t fs)
|
||||
current_thread_info()->addr_limit = fs;
|
||||
}
|
||||
|
||||
#define segment_eq(a, b) ((a) == (b))
|
||||
#define segment_eq(a, b) ((a) == (b))
|
||||
|
||||
#define __range_ok(addr, size) (size <= get_fs() && addr <= (get_fs() -size))
|
||||
|
||||
#define access_ok(type, addr, size) \
|
||||
#define access_ok(type, addr, size) \
|
||||
__range_ok((unsigned long)addr, (unsigned long)size)
|
||||
/*
|
||||
* Single-value transfer routines. They automatically use the right
|
||||
@ -75,70 +75,73 @@ static inline void set_fs(mm_segment_t fs)
|
||||
* versions are void (ie, don't return a value as such).
|
||||
*/
|
||||
|
||||
#define get_user(x,p) \
|
||||
({ \
|
||||
long __e = -EFAULT; \
|
||||
if(likely(access_ok(VERIFY_READ, p, sizeof(*p)))) { \
|
||||
__e = __get_user(x,p); \
|
||||
} else \
|
||||
x = 0; \
|
||||
__e; \
|
||||
})
|
||||
#define __get_user(x,ptr) \
|
||||
#define get_user __get_user \
|
||||
|
||||
#define __get_user(x, ptr) \
|
||||
({ \
|
||||
long __gu_err = 0; \
|
||||
__get_user_err((x),(ptr),__gu_err); \
|
||||
__get_user_check((x), (ptr), __gu_err); \
|
||||
__gu_err; \
|
||||
})
|
||||
|
||||
#define __get_user_error(x,ptr,err) \
|
||||
#define __get_user_error(x, ptr, err) \
|
||||
({ \
|
||||
__get_user_err((x),(ptr),err); \
|
||||
(void) 0; \
|
||||
__get_user_check((x), (ptr), (err)); \
|
||||
(void)0; \
|
||||
})
|
||||
|
||||
#define __get_user_err(x,ptr,err) \
|
||||
#define __get_user_check(x, ptr, err) \
|
||||
({ \
|
||||
const __typeof__(*(ptr)) __user *__p = (ptr); \
|
||||
might_fault(); \
|
||||
if (access_ok(VERIFY_READ, __p, sizeof(*__p))) { \
|
||||
__get_user_err((x), __p, (err)); \
|
||||
} else { \
|
||||
(x) = 0; (err) = -EFAULT; \
|
||||
} \
|
||||
})
|
||||
|
||||
#define __get_user_err(x, ptr, err) \
|
||||
do { \
|
||||
unsigned long __gu_addr = (unsigned long)(ptr); \
|
||||
unsigned long __gu_val; \
|
||||
__chk_user_ptr(ptr); \
|
||||
switch (sizeof(*(ptr))) { \
|
||||
case 1: \
|
||||
__get_user_asm("lbi",__gu_val,__gu_addr,err); \
|
||||
__get_user_asm("lbi", __gu_val, (ptr), (err)); \
|
||||
break; \
|
||||
case 2: \
|
||||
__get_user_asm("lhi",__gu_val,__gu_addr,err); \
|
||||
__get_user_asm("lhi", __gu_val, (ptr), (err)); \
|
||||
break; \
|
||||
case 4: \
|
||||
__get_user_asm("lwi",__gu_val,__gu_addr,err); \
|
||||
__get_user_asm("lwi", __gu_val, (ptr), (err)); \
|
||||
break; \
|
||||
case 8: \
|
||||
__get_user_asm_dword(__gu_val,__gu_addr,err); \
|
||||
__get_user_asm_dword(__gu_val, (ptr), (err)); \
|
||||
break; \
|
||||
default: \
|
||||
BUILD_BUG(); \
|
||||
break; \
|
||||
} \
|
||||
(x) = (__typeof__(*(ptr)))__gu_val; \
|
||||
(x) = (__force __typeof__(*(ptr)))__gu_val; \
|
||||
} while (0)
|
||||
|
||||
#define __get_user_asm(inst,x,addr,err) \
|
||||
asm volatile( \
|
||||
"1: "inst" %1,[%2]\n" \
|
||||
"2:\n" \
|
||||
" .section .fixup,\"ax\"\n" \
|
||||
" .align 2\n" \
|
||||
"3: move %0, %3\n" \
|
||||
" move %1, #0\n" \
|
||||
" b 2b\n" \
|
||||
" .previous\n" \
|
||||
" .section __ex_table,\"a\"\n" \
|
||||
" .align 3\n" \
|
||||
" .long 1b, 3b\n" \
|
||||
" .previous" \
|
||||
: "+r" (err), "=&r" (x) \
|
||||
: "r" (addr), "i" (-EFAULT) \
|
||||
: "cc")
|
||||
#define __get_user_asm(inst, x, addr, err) \
|
||||
__asm__ __volatile__ ( \
|
||||
"1: "inst" %1,[%2]\n" \
|
||||
"2:\n" \
|
||||
" .section .fixup,\"ax\"\n" \
|
||||
" .align 2\n" \
|
||||
"3: move %0, %3\n" \
|
||||
" move %1, #0\n" \
|
||||
" b 2b\n" \
|
||||
" .previous\n" \
|
||||
" .section __ex_table,\"a\"\n" \
|
||||
" .align 3\n" \
|
||||
" .long 1b, 3b\n" \
|
||||
" .previous" \
|
||||
: "+r" (err), "=&r" (x) \
|
||||
: "r" (addr), "i" (-EFAULT) \
|
||||
: "cc")
|
||||
|
||||
#ifdef __NDS32_EB__
|
||||
#define __gu_reg_oper0 "%H1"
|
||||
@ -149,61 +152,66 @@ do { \
|
||||
#endif
|
||||
|
||||
#define __get_user_asm_dword(x, addr, err) \
|
||||
asm volatile( \
|
||||
"\n1:\tlwi " __gu_reg_oper0 ",[%2]\n" \
|
||||
"\n2:\tlwi " __gu_reg_oper1 ",[%2+4]\n" \
|
||||
"3:\n" \
|
||||
" .section .fixup,\"ax\"\n" \
|
||||
" .align 2\n" \
|
||||
"4: move %0, %3\n" \
|
||||
" b 3b\n" \
|
||||
" .previous\n" \
|
||||
" .section __ex_table,\"a\"\n" \
|
||||
" .align 3\n" \
|
||||
" .long 1b, 4b\n" \
|
||||
" .long 2b, 4b\n" \
|
||||
" .previous" \
|
||||
: "+r"(err), "=&r"(x) \
|
||||
: "r"(addr), "i"(-EFAULT) \
|
||||
: "cc")
|
||||
#define put_user(x,p) \
|
||||
({ \
|
||||
long __e = -EFAULT; \
|
||||
if(likely(access_ok(VERIFY_WRITE, p, sizeof(*p)))) { \
|
||||
__e = __put_user(x,p); \
|
||||
} \
|
||||
__e; \
|
||||
})
|
||||
#define __put_user(x,ptr) \
|
||||
__asm__ __volatile__ ( \
|
||||
"\n1:\tlwi " __gu_reg_oper0 ",[%2]\n" \
|
||||
"\n2:\tlwi " __gu_reg_oper1 ",[%2+4]\n" \
|
||||
"3:\n" \
|
||||
" .section .fixup,\"ax\"\n" \
|
||||
" .align 2\n" \
|
||||
"4: move %0, %3\n" \
|
||||
" b 3b\n" \
|
||||
" .previous\n" \
|
||||
" .section __ex_table,\"a\"\n" \
|
||||
" .align 3\n" \
|
||||
" .long 1b, 4b\n" \
|
||||
" .long 2b, 4b\n" \
|
||||
" .previous" \
|
||||
: "+r"(err), "=&r"(x) \
|
||||
: "r"(addr), "i"(-EFAULT) \
|
||||
: "cc")
|
||||
|
||||
#define put_user __put_user \
|
||||
|
||||
#define __put_user(x, ptr) \
|
||||
({ \
|
||||
long __pu_err = 0; \
|
||||
__put_user_err((x),(ptr),__pu_err); \
|
||||
__put_user_err((x), (ptr), __pu_err); \
|
||||
__pu_err; \
|
||||
})
|
||||
|
||||
#define __put_user_error(x,ptr,err) \
|
||||
#define __put_user_error(x, ptr, err) \
|
||||
({ \
|
||||
__put_user_err((x),(ptr),err); \
|
||||
(void) 0; \
|
||||
__put_user_err((x), (ptr), (err)); \
|
||||
(void)0; \
|
||||
})
|
||||
|
||||
#define __put_user_err(x,ptr,err) \
|
||||
#define __put_user_check(x, ptr, err) \
|
||||
({ \
|
||||
__typeof__(*(ptr)) __user *__p = (ptr); \
|
||||
might_fault(); \
|
||||
if (access_ok(VERIFY_WRITE, __p, sizeof(*__p))) { \
|
||||
__put_user_err((x), __p, (err)); \
|
||||
} else { \
|
||||
(err) = -EFAULT; \
|
||||
} \
|
||||
})
|
||||
|
||||
#define __put_user_err(x, ptr, err) \
|
||||
do { \
|
||||
unsigned long __pu_addr = (unsigned long)(ptr); \
|
||||
__typeof__(*(ptr)) __pu_val = (x); \
|
||||
__chk_user_ptr(ptr); \
|
||||
switch (sizeof(*(ptr))) { \
|
||||
case 1: \
|
||||
__put_user_asm("sbi",__pu_val,__pu_addr,err); \
|
||||
__put_user_asm("sbi", __pu_val, (ptr), (err)); \
|
||||
break; \
|
||||
case 2: \
|
||||
__put_user_asm("shi",__pu_val,__pu_addr,err); \
|
||||
__put_user_asm("shi", __pu_val, (ptr), (err)); \
|
||||
break; \
|
||||
case 4: \
|
||||
__put_user_asm("swi",__pu_val,__pu_addr,err); \
|
||||
__put_user_asm("swi", __pu_val, (ptr), (err)); \
|
||||
break; \
|
||||
case 8: \
|
||||
__put_user_asm_dword(__pu_val,__pu_addr,err); \
|
||||
__put_user_asm_dword(__pu_val, (ptr), (err)); \
|
||||
break; \
|
||||
default: \
|
||||
BUILD_BUG(); \
|
||||
@ -211,22 +219,22 @@ do { \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define __put_user_asm(inst,x,addr,err) \
|
||||
asm volatile( \
|
||||
"1: "inst" %1,[%2]\n" \
|
||||
"2:\n" \
|
||||
" .section .fixup,\"ax\"\n" \
|
||||
" .align 2\n" \
|
||||
"3: move %0, %3\n" \
|
||||
" b 2b\n" \
|
||||
" .previous\n" \
|
||||
" .section __ex_table,\"a\"\n" \
|
||||
" .align 3\n" \
|
||||
" .long 1b, 3b\n" \
|
||||
" .previous" \
|
||||
: "+r" (err) \
|
||||
: "r" (x), "r" (addr), "i" (-EFAULT) \
|
||||
: "cc")
|
||||
#define __put_user_asm(inst, x, addr, err) \
|
||||
__asm__ __volatile__ ( \
|
||||
"1: "inst" %1,[%2]\n" \
|
||||
"2:\n" \
|
||||
" .section .fixup,\"ax\"\n" \
|
||||
" .align 2\n" \
|
||||
"3: move %0, %3\n" \
|
||||
" b 2b\n" \
|
||||
" .previous\n" \
|
||||
" .section __ex_table,\"a\"\n" \
|
||||
" .align 3\n" \
|
||||
" .long 1b, 3b\n" \
|
||||
" .previous" \
|
||||
: "+r" (err) \
|
||||
: "r" (x), "r" (addr), "i" (-EFAULT) \
|
||||
: "cc")
|
||||
|
||||
#ifdef __NDS32_EB__
|
||||
#define __pu_reg_oper0 "%H2"
|
||||
@ -237,23 +245,24 @@ do { \
|
||||
#endif
|
||||
|
||||
#define __put_user_asm_dword(x, addr, err) \
|
||||
asm volatile( \
|
||||
"\n1:\tswi " __pu_reg_oper0 ",[%1]\n" \
|
||||
"\n2:\tswi " __pu_reg_oper1 ",[%1+4]\n" \
|
||||
"3:\n" \
|
||||
" .section .fixup,\"ax\"\n" \
|
||||
" .align 2\n" \
|
||||
"4: move %0, %3\n" \
|
||||
" b 3b\n" \
|
||||
" .previous\n" \
|
||||
" .section __ex_table,\"a\"\n" \
|
||||
" .align 3\n" \
|
||||
" .long 1b, 4b\n" \
|
||||
" .long 2b, 4b\n" \
|
||||
" .previous" \
|
||||
: "+r"(err) \
|
||||
: "r"(addr), "r"(x), "i"(-EFAULT) \
|
||||
: "cc")
|
||||
__asm__ __volatile__ ( \
|
||||
"\n1:\tswi " __pu_reg_oper0 ",[%1]\n" \
|
||||
"\n2:\tswi " __pu_reg_oper1 ",[%1+4]\n" \
|
||||
"3:\n" \
|
||||
" .section .fixup,\"ax\"\n" \
|
||||
" .align 2\n" \
|
||||
"4: move %0, %3\n" \
|
||||
" b 3b\n" \
|
||||
" .previous\n" \
|
||||
" .section __ex_table,\"a\"\n" \
|
||||
" .align 3\n" \
|
||||
" .long 1b, 4b\n" \
|
||||
" .long 2b, 4b\n" \
|
||||
" .previous" \
|
||||
: "+r"(err) \
|
||||
: "r"(addr), "r"(x), "i"(-EFAULT) \
|
||||
: "cc")
|
||||
|
||||
extern unsigned long __arch_clear_user(void __user * addr, unsigned long n);
|
||||
extern long strncpy_from_user(char *dest, const char __user * src, long count);
|
||||
extern __must_check long strlen_user(const char __user * str);
|
||||
|
@ -21,3 +21,9 @@ extra-y := head.o vmlinux.lds
|
||||
|
||||
|
||||
obj-y += vdso/
|
||||
|
||||
obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o
|
||||
|
||||
ifdef CONFIG_FUNCTION_TRACER
|
||||
CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE)
|
||||
endif
|
||||
|
@ -9,7 +9,8 @@
|
||||
|
||||
void __iomem *atl2c_base;
|
||||
static const struct of_device_id atl2c_ids[] __initconst = {
|
||||
{.compatible = "andestech,atl2c",}
|
||||
{.compatible = "andestech,atl2c",},
|
||||
{}
|
||||
};
|
||||
|
||||
static int __init atl2c_of_init(void)
|
||||
|
@ -118,7 +118,7 @@ common_exception_handler:
|
||||
/* interrupt */
|
||||
2:
|
||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
||||
jal trace_hardirqs_off
|
||||
jal __trace_hardirqs_off
|
||||
#endif
|
||||
move $r0, $sp
|
||||
sethi $lp, hi20(ret_from_intr)
|
||||
|
@ -138,8 +138,8 @@ no_work_pending:
|
||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
||||
lwi $p0, [$sp+(#IPSW_OFFSET)]
|
||||
andi $p0, $p0, #0x1
|
||||
la $r10, trace_hardirqs_off
|
||||
la $r9, trace_hardirqs_on
|
||||
la $r10, __trace_hardirqs_off
|
||||
la $r9, __trace_hardirqs_on
|
||||
cmovz $r9, $p0, $r10
|
||||
jral $r9
|
||||
#endif
|
||||
|
309
arch/nds32/kernel/ftrace.c
Normal file
309
arch/nds32/kernel/ftrace.c
Normal file
@ -0,0 +1,309 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#ifndef CONFIG_DYNAMIC_FTRACE
|
||||
extern void (*ftrace_trace_function)(unsigned long, unsigned long,
|
||||
struct ftrace_ops*, struct pt_regs*);
|
||||
extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace);
|
||||
extern void ftrace_graph_caller(void);
|
||||
|
||||
noinline void __naked ftrace_stub(unsigned long ip, unsigned long parent_ip,
|
||||
struct ftrace_ops *op, struct pt_regs *regs)
|
||||
{
|
||||
__asm__ (""); /* avoid to optimize as pure function */
|
||||
}
|
||||
|
||||
noinline void _mcount(unsigned long parent_ip)
|
||||
{
|
||||
/* save all state by the compiler prologue */
|
||||
|
||||
unsigned long ip = (unsigned long)__builtin_return_address(0);
|
||||
|
||||
if (ftrace_trace_function != ftrace_stub)
|
||||
ftrace_trace_function(ip - MCOUNT_INSN_SIZE, parent_ip,
|
||||
NULL, NULL);
|
||||
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
if (ftrace_graph_return != (trace_func_graph_ret_t)ftrace_stub
|
||||
|| ftrace_graph_entry != ftrace_graph_entry_stub)
|
||||
ftrace_graph_caller();
|
||||
#endif
|
||||
|
||||
/* restore all state by the compiler epilogue */
|
||||
}
|
||||
EXPORT_SYMBOL(_mcount);
|
||||
|
||||
#else /* CONFIG_DYNAMIC_FTRACE */
|
||||
|
||||
noinline void __naked ftrace_stub(unsigned long ip, unsigned long parent_ip,
|
||||
struct ftrace_ops *op, struct pt_regs *regs)
|
||||
{
|
||||
__asm__ (""); /* avoid to optimize as pure function */
|
||||
}
|
||||
|
||||
noinline void __naked _mcount(unsigned long parent_ip)
|
||||
{
|
||||
__asm__ (""); /* avoid to optimize as pure function */
|
||||
}
|
||||
EXPORT_SYMBOL(_mcount);
|
||||
|
||||
#define XSTR(s) STR(s)
|
||||
#define STR(s) #s
|
||||
void _ftrace_caller(unsigned long parent_ip)
|
||||
{
|
||||
/* save all state needed by the compiler prologue */
|
||||
|
||||
/*
|
||||
* prepare arguments for real tracing function
|
||||
* first arg : __builtin_return_address(0) - MCOUNT_INSN_SIZE
|
||||
* second arg : parent_ip
|
||||
*/
|
||||
__asm__ __volatile__ (
|
||||
"move $r1, %0 \n\t"
|
||||
"addi $r0, %1, #-" XSTR(MCOUNT_INSN_SIZE) "\n\t"
|
||||
:
|
||||
: "r" (parent_ip), "r" (__builtin_return_address(0)));
|
||||
|
||||
/* a placeholder for the call to a real tracing function */
|
||||
__asm__ __volatile__ (
|
||||
"ftrace_call: \n\t"
|
||||
"nop \n\t"
|
||||
"nop \n\t"
|
||||
"nop \n\t");
|
||||
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
/* a placeholder for the call to ftrace_graph_caller */
|
||||
__asm__ __volatile__ (
|
||||
"ftrace_graph_call: \n\t"
|
||||
"nop \n\t"
|
||||
"nop \n\t"
|
||||
"nop \n\t");
|
||||
#endif
|
||||
/* restore all state needed by the compiler epilogue */
|
||||
}
|
||||
|
||||
int __init ftrace_dyn_arch_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ftrace_arch_code_modify_prepare(void)
|
||||
{
|
||||
set_all_modules_text_rw();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ftrace_arch_code_modify_post_process(void)
|
||||
{
|
||||
set_all_modules_text_ro();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long gen_sethi_insn(unsigned long addr)
|
||||
{
|
||||
unsigned long opcode = 0x46000000;
|
||||
unsigned long imm = addr >> 12;
|
||||
unsigned long rt_num = 0xf << 20;
|
||||
|
||||
return ENDIAN_CONVERT(opcode | rt_num | imm);
|
||||
}
|
||||
|
||||
static unsigned long gen_ori_insn(unsigned long addr)
|
||||
{
|
||||
unsigned long opcode = 0x58000000;
|
||||
unsigned long imm = addr & 0x0000fff;
|
||||
unsigned long rt_num = 0xf << 20;
|
||||
unsigned long ra_num = 0xf << 15;
|
||||
|
||||
return ENDIAN_CONVERT(opcode | rt_num | ra_num | imm);
|
||||
}
|
||||
|
||||
static unsigned long gen_jral_insn(unsigned long addr)
|
||||
{
|
||||
unsigned long opcode = 0x4a000001;
|
||||
unsigned long rt_num = 0x1e << 20;
|
||||
unsigned long rb_num = 0xf << 10;
|
||||
|
||||
return ENDIAN_CONVERT(opcode | rt_num | rb_num);
|
||||
}
|
||||
|
||||
static void ftrace_gen_call_insn(unsigned long *call_insns,
|
||||
unsigned long addr)
|
||||
{
|
||||
call_insns[0] = gen_sethi_insn(addr); /* sethi $r15, imm20u */
|
||||
call_insns[1] = gen_ori_insn(addr); /* ori $r15, $r15, imm15u */
|
||||
call_insns[2] = gen_jral_insn(addr); /* jral $lp, $r15 */
|
||||
}
|
||||
|
||||
static int __ftrace_modify_code(unsigned long pc, unsigned long *old_insn,
|
||||
unsigned long *new_insn, bool validate)
|
||||
{
|
||||
unsigned long orig_insn[3];
|
||||
|
||||
if (validate) {
|
||||
if (probe_kernel_read(orig_insn, (void *)pc, MCOUNT_INSN_SIZE))
|
||||
return -EFAULT;
|
||||
if (memcmp(orig_insn, old_insn, MCOUNT_INSN_SIZE))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (probe_kernel_write((void *)pc, new_insn, MCOUNT_INSN_SIZE))
|
||||
return -EPERM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftrace_modify_code(unsigned long pc, unsigned long *old_insn,
|
||||
unsigned long *new_insn, bool validate)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __ftrace_modify_code(pc, old_insn, new_insn, validate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
flush_icache_range(pc, pc + MCOUNT_INSN_SIZE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ftrace_update_ftrace_func(ftrace_func_t func)
|
||||
{
|
||||
unsigned long pc = (unsigned long)&ftrace_call;
|
||||
unsigned long old_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
|
||||
unsigned long new_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
|
||||
|
||||
if (func != ftrace_stub)
|
||||
ftrace_gen_call_insn(new_insn, (unsigned long)func);
|
||||
|
||||
return ftrace_modify_code(pc, old_insn, new_insn, false);
|
||||
}
|
||||
|
||||
int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
|
||||
{
|
||||
unsigned long pc = rec->ip;
|
||||
unsigned long nop_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
|
||||
unsigned long call_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
|
||||
|
||||
ftrace_gen_call_insn(call_insn, addr);
|
||||
|
||||
return ftrace_modify_code(pc, nop_insn, call_insn, true);
|
||||
}
|
||||
|
||||
int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
|
||||
unsigned long addr)
|
||||
{
|
||||
unsigned long pc = rec->ip;
|
||||
unsigned long nop_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
|
||||
unsigned long call_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
|
||||
|
||||
ftrace_gen_call_insn(call_insn, addr);
|
||||
|
||||
return ftrace_modify_code(pc, call_insn, nop_insn, true);
|
||||
}
|
||||
#endif /* CONFIG_DYNAMIC_FTRACE */
|
||||
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
|
||||
unsigned long frame_pointer)
|
||||
{
|
||||
unsigned long return_hooker = (unsigned long)&return_to_handler;
|
||||
struct ftrace_graph_ent trace;
|
||||
unsigned long old;
|
||||
int err;
|
||||
|
||||
if (unlikely(atomic_read(¤t->tracing_graph_pause)))
|
||||
return;
|
||||
|
||||
old = *parent;
|
||||
|
||||
trace.func = self_addr;
|
||||
trace.depth = current->curr_ret_stack + 1;
|
||||
|
||||
/* Only trace if the calling function expects to */
|
||||
if (!ftrace_graph_entry(&trace))
|
||||
return;
|
||||
|
||||
err = ftrace_push_return_trace(old, self_addr, &trace.depth,
|
||||
frame_pointer, NULL);
|
||||
|
||||
if (err == -EBUSY)
|
||||
return;
|
||||
|
||||
*parent = return_hooker;
|
||||
}
|
||||
|
||||
noinline void ftrace_graph_caller(void)
|
||||
{
|
||||
unsigned long *parent_ip =
|
||||
(unsigned long *)(__builtin_frame_address(2) - 4);
|
||||
|
||||
unsigned long selfpc =
|
||||
(unsigned long)(__builtin_return_address(1) - MCOUNT_INSN_SIZE);
|
||||
|
||||
unsigned long frame_pointer =
|
||||
(unsigned long)__builtin_frame_address(3);
|
||||
|
||||
prepare_ftrace_return(parent_ip, selfpc, frame_pointer);
|
||||
}
|
||||
|
||||
extern unsigned long ftrace_return_to_handler(unsigned long frame_pointer);
|
||||
void __naked return_to_handler(void)
|
||||
{
|
||||
__asm__ __volatile__ (
|
||||
/* save state needed by the ABI */
|
||||
"smw.adm $r0,[$sp],$r1,#0x0 \n\t"
|
||||
|
||||
/* get original return address */
|
||||
"move $r0, $fp \n\t"
|
||||
"bal ftrace_return_to_handler\n\t"
|
||||
"move $lp, $r0 \n\t"
|
||||
|
||||
/* restore state nedded by the ABI */
|
||||
"lmw.bim $r0,[$sp],$r1,#0x0 \n\t");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
extern unsigned long ftrace_graph_call;
|
||||
|
||||
static int ftrace_modify_graph_caller(bool enable)
|
||||
{
|
||||
unsigned long pc = (unsigned long)&ftrace_graph_call;
|
||||
unsigned long nop_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
|
||||
unsigned long call_insn[3] = {INSN_NOP, INSN_NOP, INSN_NOP};
|
||||
|
||||
ftrace_gen_call_insn(call_insn, (unsigned long)ftrace_graph_caller);
|
||||
|
||||
if (enable)
|
||||
return ftrace_modify_code(pc, nop_insn, call_insn, true);
|
||||
else
|
||||
return ftrace_modify_code(pc, call_insn, nop_insn, true);
|
||||
}
|
||||
|
||||
int ftrace_enable_ftrace_graph_caller(void)
|
||||
{
|
||||
return ftrace_modify_graph_caller(true);
|
||||
}
|
||||
|
||||
int ftrace_disable_ftrace_graph_caller(void)
|
||||
{
|
||||
return ftrace_modify_graph_caller(false);
|
||||
}
|
||||
#endif /* CONFIG_DYNAMIC_FTRACE */
|
||||
|
||||
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
|
||||
|
||||
|
||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
||||
noinline void __trace_hardirqs_off(void)
|
||||
{
|
||||
trace_hardirqs_off();
|
||||
}
|
||||
noinline void __trace_hardirqs_on(void)
|
||||
{
|
||||
trace_hardirqs_on();
|
||||
}
|
||||
#endif /* CONFIG_TRACE_IRQFLAGS */
|
@ -40,7 +40,7 @@ void do_reloc16(unsigned int val, unsigned int *loc, unsigned int val_mask,
|
||||
|
||||
tmp2 = tmp & loc_mask;
|
||||
if (partial_in_place) {
|
||||
tmp &= (!loc_mask);
|
||||
tmp &= (~loc_mask);
|
||||
tmp =
|
||||
tmp2 | ((tmp + ((val & val_mask) >> val_shift)) & val_mask);
|
||||
} else {
|
||||
@ -70,7 +70,7 @@ void do_reloc32(unsigned int val, unsigned int *loc, unsigned int val_mask,
|
||||
|
||||
tmp2 = tmp & loc_mask;
|
||||
if (partial_in_place) {
|
||||
tmp &= (!loc_mask);
|
||||
tmp &= (~loc_mask);
|
||||
tmp =
|
||||
tmp2 | ((tmp + ((val & val_mask) >> val_shift)) & val_mask);
|
||||
} else {
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <linux/sched/debug.h>
|
||||
#include <linux/sched/task_stack.h>
|
||||
#include <linux/stacktrace.h>
|
||||
#include <linux/ftrace.h>
|
||||
|
||||
void save_stack_trace(struct stack_trace *trace)
|
||||
{
|
||||
@ -16,6 +17,7 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
|
||||
unsigned long *fpn;
|
||||
int skip = trace->skip;
|
||||
int savesched;
|
||||
int graph_idx = 0;
|
||||
|
||||
if (tsk == current) {
|
||||
__asm__ __volatile__("\tori\t%0, $fp, #0\n":"=r"(fpn));
|
||||
@ -29,10 +31,12 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
|
||||
&& (fpn >= (unsigned long *)TASK_SIZE)) {
|
||||
unsigned long lpp, fpp;
|
||||
|
||||
lpp = fpn[-1];
|
||||
lpp = fpn[LP_OFFSET];
|
||||
fpp = fpn[FP_OFFSET];
|
||||
if (!__kernel_text_address(lpp))
|
||||
break;
|
||||
else
|
||||
lpp = ftrace_graph_ret_addr(tsk, &graph_idx, lpp, NULL);
|
||||
|
||||
if (savesched || !in_sched_functions(lpp)) {
|
||||
if (skip) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <linux/kdebug.h>
|
||||
#include <linux/sched/task_stack.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/ftrace.h>
|
||||
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/unistd.h>
|
||||
@ -94,28 +95,6 @@ static void dump_instr(struct pt_regs *regs)
|
||||
set_fs(fs);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
#include <linux/ftrace.h>
|
||||
static void
|
||||
get_real_ret_addr(unsigned long *addr, struct task_struct *tsk, int *graph)
|
||||
{
|
||||
if (*addr == (unsigned long)return_to_handler) {
|
||||
int index = tsk->curr_ret_stack;
|
||||
|
||||
if (tsk->ret_stack && index >= *graph) {
|
||||
index -= *graph;
|
||||
*addr = tsk->ret_stack[index].ret;
|
||||
(*graph)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void
|
||||
get_real_ret_addr(unsigned long *addr, struct task_struct *tsk, int *graph)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#define LOOP_TIMES (100)
|
||||
static void __dump(struct task_struct *tsk, unsigned long *base_reg)
|
||||
{
|
||||
@ -126,7 +105,8 @@ static void __dump(struct task_struct *tsk, unsigned long *base_reg)
|
||||
while (!kstack_end(base_reg)) {
|
||||
ret_addr = *base_reg++;
|
||||
if (__kernel_text_address(ret_addr)) {
|
||||
get_real_ret_addr(&ret_addr, tsk, &graph);
|
||||
ret_addr = ftrace_graph_ret_addr(
|
||||
tsk, &graph, ret_addr, NULL);
|
||||
print_ip_sym(ret_addr);
|
||||
}
|
||||
if (--cnt < 0)
|
||||
@ -137,15 +117,12 @@ static void __dump(struct task_struct *tsk, unsigned long *base_reg)
|
||||
!((unsigned long)base_reg & 0x3) &&
|
||||
((unsigned long)base_reg >= TASK_SIZE)) {
|
||||
unsigned long next_fp;
|
||||
#if !defined(NDS32_ABI_2)
|
||||
ret_addr = base_reg[0];
|
||||
next_fp = base_reg[1];
|
||||
#else
|
||||
ret_addr = base_reg[-1];
|
||||
ret_addr = base_reg[LP_OFFSET];
|
||||
next_fp = base_reg[FP_OFFSET];
|
||||
#endif
|
||||
if (__kernel_text_address(ret_addr)) {
|
||||
get_real_ret_addr(&ret_addr, tsk, &graph);
|
||||
|
||||
ret_addr = ftrace_graph_ret_addr(
|
||||
tsk, &graph, ret_addr, NULL);
|
||||
print_ip_sym(ret_addr);
|
||||
}
|
||||
if (--cnt < 0)
|
||||
@ -196,11 +173,10 @@ void die(const char *str, struct pt_regs *regs, int err)
|
||||
pr_emerg("CPU: %i\n", smp_processor_id());
|
||||
show_regs(regs);
|
||||
pr_emerg("Process %s (pid: %d, stack limit = 0x%p)\n",
|
||||
tsk->comm, tsk->pid, task_thread_info(tsk) + 1);
|
||||
tsk->comm, tsk->pid, end_of_stack(tsk));
|
||||
|
||||
if (!user_mode(regs) || in_interrupt()) {
|
||||
dump_mem("Stack: ", regs->sp,
|
||||
THREAD_SIZE + (unsigned long)task_thread_info(tsk));
|
||||
dump_mem("Stack: ", regs->sp, (regs->sp + PAGE_SIZE) & PAGE_MASK);
|
||||
dump_instr(regs);
|
||||
dump_stack();
|
||||
}
|
||||
|
@ -13,14 +13,26 @@ OUTPUT_ARCH(nds32)
|
||||
ENTRY(_stext_lma)
|
||||
jiffies = jiffies_64;
|
||||
|
||||
#if defined(CONFIG_GCOV_KERNEL)
|
||||
#define NDS32_EXIT_KEEP(x) x
|
||||
#else
|
||||
#define NDS32_EXIT_KEEP(x)
|
||||
#endif
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
_stext_lma = TEXTADDR - LOAD_OFFSET;
|
||||
. = TEXTADDR;
|
||||
__init_begin = .;
|
||||
HEAD_TEXT_SECTION
|
||||
.exit.text : {
|
||||
NDS32_EXIT_KEEP(EXIT_TEXT)
|
||||
}
|
||||
INIT_TEXT_SECTION(PAGE_SIZE)
|
||||
INIT_DATA_SECTION(16)
|
||||
.exit.data : {
|
||||
NDS32_EXIT_KEEP(EXIT_DATA)
|
||||
}
|
||||
PERCPU_SECTION(L1_CACHE_BYTES)
|
||||
__init_end = .;
|
||||
|
||||
|
@ -389,6 +389,9 @@ if ($arch eq "x86_64") {
|
||||
$mcount_regex = "^\\s*([0-9a-fA-F]+):\\sR_RISCV_CALL\\s_mcount\$";
|
||||
$type = ".quad";
|
||||
$alignment = 2;
|
||||
} elsif ($arch eq "nds32") {
|
||||
$mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_NDS32_HI20_RELA\\s+_mcount\$";
|
||||
$alignment = 2;
|
||||
} else {
|
||||
die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD";
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user