Skip to content
Snippets Groups Projects
Select Git revision
  • 9338203c4f03ffe323b67f0b2fa17b9811fa9bb6
  • vme-testing default
  • ci-test
  • master
  • remoteproc
  • am625-sk-ov5640
  • pcal6534-upstreaming
  • lps22df-upstreaming
  • msc-upstreaming
  • imx8mp
  • iio/noa1305
  • vme-next
  • vme-next-4.14-rc4
  • v4.14-rc4
  • v4.14-rc3
  • v4.14-rc2
  • v4.14-rc1
  • v4.13
  • vme-next-4.13-rc7
  • v4.13-rc7
  • v4.13-rc6
  • v4.13-rc5
  • v4.13-rc4
  • v4.13-rc3
  • v4.13-rc2
  • v4.13-rc1
  • v4.12
  • v4.12-rc7
  • v4.12-rc6
  • v4.12-rc5
  • v4.12-rc4
  • v4.12-rc3
32 results

amdgpu_mode.h

Blame
  • stacktrace.c 5.84 KiB
    /*
     * Kernel and userspace stack tracing.
     *
     * This file is subject to the terms and conditions of the GNU General Public
     * License.  See the file "COPYING" in the main directory of this archive
     * for more details.
     *
     * Copyright (C) 2001 - 2013 Tensilica Inc.
     * Copyright (C) 2015 Cadence Design Systems Inc.
     */
    #include <linux/export.h>
    #include <linux/sched.h>
    #include <linux/stacktrace.h>
    
    #include <asm/stacktrace.h>
    #include <asm/traps.h>
    #include <linux/uaccess.h>
    
    #if IS_ENABLED(CONFIG_OPROFILE) || IS_ENABLED(CONFIG_PERF_EVENTS)
    
    /* Address of common_exception_return, used to check the
     * transition from kernel to user space.
     */
    extern int common_exception_return;
    
    void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth,
    			   int (*ufn)(struct stackframe *frame, void *data),
    			   void *data)
    {
    	unsigned long windowstart = regs->windowstart;
    	unsigned long windowbase = regs->windowbase;
    	unsigned long a0 = regs->areg[0];
    	unsigned long a1 = regs->areg[1];
    	unsigned long pc = regs->pc;
    	struct stackframe frame;
    	int index;
    
    	if (!depth--)
    		return;
    
    	frame.pc = pc;
    	frame.sp = a1;
    
    	if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
    		return;
    
    	/* Two steps:
    	 *
    	 * 1. Look through the register window for the
    	 * previous PCs in the call trace.
    	 *
    	 * 2. Look on the stack.
    	 */
    
    	/* Step 1.  */
    	/* Rotate WINDOWSTART to move the bit corresponding to
    	 * the current window to the bit #0.
    	 */
    	windowstart = (windowstart << WSBITS | windowstart) >> windowbase;
    
    	/* Look for bits that are set, they correspond to
    	 * valid windows.
    	 */
    	for (index = WSBITS - 1; (index > 0) && depth; depth--, index--)
    		if (windowstart & (1 << index)) {
    			/* Get the PC from a0 and a1. */
    			pc = MAKE_PC_FROM_RA(a0, pc);
    			/* Read a0 and a1 from the
    			 * corresponding position in AREGs.
    			 */
    			a0 = regs->areg[index * 4];
    			a1 = regs->areg[index * 4 + 1];
    
    			frame.pc = pc;
    			frame.sp = a1;
    
    			if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
    				return;
    		}
    
    	/* Step 2. */
    	/* We are done with the register window, we need to
    	 * look through the stack.
    	 */
    	if (!depth)
    		return;
    
    	/* Start from the a1 register. */
    	/* a1 = regs->areg[1]; */
    	while (a0 != 0 && depth--) {
    		pc = MAKE_PC_FROM_RA(a0, pc);
    
    		/* Check if the region is OK to access. */
    		if (!access_ok(&SPILL_SLOT(a1, 0), 8))
    			return;
    		/* Copy a1, a0 from user space stack frame. */
    		if (__get_user(a0, &SPILL_SLOT(a1, 0)) ||
    		    __get_user(a1, &SPILL_SLOT(a1, 1)))
    			return;
    
    		frame.pc = pc;
    		frame.sp = a1;
    
    		if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
    			return;
    	}
    }
    EXPORT_SYMBOL(xtensa_backtrace_user);
    
    void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth,
    			     int (*kfn)(struct stackframe *frame, void *data),
    			     int (*ufn)(struct stackframe *frame, void *data),
    			     void *data)
    {
    	unsigned long pc = regs->depc > VALID_DOUBLE_EXCEPTION_ADDRESS ?
    		regs->depc : regs->pc;
    	unsigned long sp_start, sp_end;
    	unsigned long a0 = regs->areg[0];
    	unsigned long a1 = regs->areg[1];
    
    	sp_start = a1 & ~(THREAD_SIZE - 1);
    	sp_end = sp_start + THREAD_SIZE;
    
    	/* Spill the register window to the stack first. */
    	spill_registers();
    
    	/* Read the stack frames one by one and create the PC
    	 * from the a0 and a1 registers saved there.
    	 */
    	while (a1 > sp_start && a1 < sp_end && depth--) {
    		struct stackframe frame;
    
    		frame.pc = pc;
    		frame.sp = a1;
    
    		if (kernel_text_address(pc) && kfn(&frame, data))
    			return;
    
    		if (pc == (unsigned long)&common_exception_return) {
    			regs = (struct pt_regs *)a1;
    			if (user_mode(regs)) {
    				if (ufn == NULL)
    					return;
    				xtensa_backtrace_user(regs, depth, ufn, data);
    				return;
    			}
    			a0 = regs->areg[0];
    			a1 = regs->areg[1];
    			continue;
    		}
    
    		sp_start = a1;
    
    		pc = MAKE_PC_FROM_RA(a0, pc);
    		a0 = SPILL_SLOT(a1, 0);
    		a1 = SPILL_SLOT(a1, 1);
    	}
    }
    EXPORT_SYMBOL(xtensa_backtrace_kernel);
    
    #endif
    
    void walk_stackframe(unsigned long *sp,
    		int (*fn)(struct stackframe *frame, void *data),
    		void *data)
    {
    	unsigned long a0, a1;
    	unsigned long sp_end;
    
    	a1 = (unsigned long)sp;
    	sp_end = ALIGN(a1, THREAD_SIZE);
    
    	spill_registers();
    
    	while (a1 < sp_end) {
    		struct stackframe frame;
    
    		sp = (unsigned long *)a1;
    
    		a0 = SPILL_SLOT(a1, 0);
    		a1 = SPILL_SLOT(a1, 1);
    
    		if (a1 <= (unsigned long)sp)
    			break;
    
    		frame.pc = MAKE_PC_FROM_RA(a0, a1);
    		frame.sp = a1;
    
    		if (fn(&frame, data))
    			return;
    	}
    }
    
    #ifdef CONFIG_STACKTRACE
    
    struct stack_trace_data {
    	struct stack_trace *trace;
    	unsigned skip;
    };
    
    static int stack_trace_cb(struct stackframe *frame, void *data)
    {
    	struct stack_trace_data *trace_data = data;
    	struct stack_trace *trace = trace_data->trace;
    
    	if (trace_data->skip) {
    		--trace_data->skip;
    		return 0;
    	}
    	if (!kernel_text_address(frame->pc))
    		return 0;
    
    	trace->entries[trace->nr_entries++] = frame->pc;
    	return trace->nr_entries >= trace->max_entries;
    }
    
    void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace)
    {
    	struct stack_trace_data trace_data = {
    		.trace = trace,
    		.skip = trace->skip,
    	};
    	walk_stackframe(stack_pointer(task), stack_trace_cb, &trace_data);
    }
    EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
    
    void save_stack_trace(struct stack_trace *trace)
    {
    	save_stack_trace_tsk(current, trace);
    }
    EXPORT_SYMBOL_GPL(save_stack_trace);
    
    #endif
    
    #ifdef CONFIG_FRAME_POINTER
    
    struct return_addr_data {
    	unsigned long addr;
    	unsigned skip;
    };
    
    static int return_address_cb(struct stackframe *frame, void *data)
    {
    	struct return_addr_data *r = data;
    
    	if (r->skip) {
    		--r->skip;
    		return 0;
    	}
    	if (!kernel_text_address(frame->pc))
    		return 0;
    	r->addr = frame->pc;
    	return 1;
    }
    
    /*
     * level == 0 is for the return address from the caller of this function,
     * not from this function itself.
     */
    unsigned long return_address(unsigned level)
    {
    	struct return_addr_data r = {
    		.skip = level,
    	};
    	walk_stackframe(stack_pointer(NULL), return_address_cb, &r);
    	return r.addr;
    }
    EXPORT_SYMBOL(return_address);
    
    #endif