whiterose

linux unikernel
Log | Files | Refs | README | LICENSE | git clone https://git.ne02ptzero.me/git/whiterose

stacktrace.c (5981B)


      1 /*
      2  * Kernel and userspace stack tracing.
      3  *
      4  * This file is subject to the terms and conditions of the GNU General Public
      5  * License.  See the file "COPYING" in the main directory of this archive
      6  * for more details.
      7  *
      8  * Copyright (C) 2001 - 2013 Tensilica Inc.
      9  * Copyright (C) 2015 Cadence Design Systems Inc.
     10  */
     11 #include <linux/export.h>
     12 #include <linux/sched.h>
     13 #include <linux/stacktrace.h>
     14 
     15 #include <asm/stacktrace.h>
     16 #include <asm/traps.h>
     17 #include <linux/uaccess.h>
     18 
     19 #if IS_ENABLED(CONFIG_OPROFILE) || IS_ENABLED(CONFIG_PERF_EVENTS)
     20 
     21 /* Address of common_exception_return, used to check the
     22  * transition from kernel to user space.
     23  */
     24 extern int common_exception_return;
     25 
     26 void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth,
     27 			   int (*ufn)(struct stackframe *frame, void *data),
     28 			   void *data)
     29 {
     30 	unsigned long windowstart = regs->windowstart;
     31 	unsigned long windowbase = regs->windowbase;
     32 	unsigned long a0 = regs->areg[0];
     33 	unsigned long a1 = regs->areg[1];
     34 	unsigned long pc = regs->pc;
     35 	struct stackframe frame;
     36 	int index;
     37 
     38 	if (!depth--)
     39 		return;
     40 
     41 	frame.pc = pc;
     42 	frame.sp = a1;
     43 
     44 	if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
     45 		return;
     46 
     47 	/* Two steps:
     48 	 *
     49 	 * 1. Look through the register window for the
     50 	 * previous PCs in the call trace.
     51 	 *
     52 	 * 2. Look on the stack.
     53 	 */
     54 
     55 	/* Step 1.  */
     56 	/* Rotate WINDOWSTART to move the bit corresponding to
     57 	 * the current window to the bit #0.
     58 	 */
     59 	windowstart = (windowstart << WSBITS | windowstart) >> windowbase;
     60 
     61 	/* Look for bits that are set, they correspond to
     62 	 * valid windows.
     63 	 */
     64 	for (index = WSBITS - 1; (index > 0) && depth; depth--, index--)
     65 		if (windowstart & (1 << index)) {
     66 			/* Get the PC from a0 and a1. */
     67 			pc = MAKE_PC_FROM_RA(a0, pc);
     68 			/* Read a0 and a1 from the
     69 			 * corresponding position in AREGs.
     70 			 */
     71 			a0 = regs->areg[index * 4];
     72 			a1 = regs->areg[index * 4 + 1];
     73 
     74 			frame.pc = pc;
     75 			frame.sp = a1;
     76 
     77 			if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
     78 				return;
     79 		}
     80 
     81 	/* Step 2. */
     82 	/* We are done with the register window, we need to
     83 	 * look through the stack.
     84 	 */
     85 	if (!depth)
     86 		return;
     87 
     88 	/* Start from the a1 register. */
     89 	/* a1 = regs->areg[1]; */
     90 	while (a0 != 0 && depth--) {
     91 		pc = MAKE_PC_FROM_RA(a0, pc);
     92 
     93 		/* Check if the region is OK to access. */
     94 		if (!access_ok(&SPILL_SLOT(a1, 0), 8))
     95 			return;
     96 		/* Copy a1, a0 from user space stack frame. */
     97 		if (__get_user(a0, &SPILL_SLOT(a1, 0)) ||
     98 		    __get_user(a1, &SPILL_SLOT(a1, 1)))
     99 			return;
    100 
    101 		frame.pc = pc;
    102 		frame.sp = a1;
    103 
    104 		if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
    105 			return;
    106 	}
    107 }
    108 EXPORT_SYMBOL(xtensa_backtrace_user);
    109 
    110 void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth,
    111 			     int (*kfn)(struct stackframe *frame, void *data),
    112 			     int (*ufn)(struct stackframe *frame, void *data),
    113 			     void *data)
    114 {
    115 	unsigned long pc = regs->depc > VALID_DOUBLE_EXCEPTION_ADDRESS ?
    116 		regs->depc : regs->pc;
    117 	unsigned long sp_start, sp_end;
    118 	unsigned long a0 = regs->areg[0];
    119 	unsigned long a1 = regs->areg[1];
    120 
    121 	sp_start = a1 & ~(THREAD_SIZE - 1);
    122 	sp_end = sp_start + THREAD_SIZE;
    123 
    124 	/* Spill the register window to the stack first. */
    125 	spill_registers();
    126 
    127 	/* Read the stack frames one by one and create the PC
    128 	 * from the a0 and a1 registers saved there.
    129 	 */
    130 	while (a1 > sp_start && a1 < sp_end && depth--) {
    131 		struct stackframe frame;
    132 
    133 		frame.pc = pc;
    134 		frame.sp = a1;
    135 
    136 		if (kernel_text_address(pc) && kfn(&frame, data))
    137 			return;
    138 
    139 		if (pc == (unsigned long)&common_exception_return) {
    140 			regs = (struct pt_regs *)a1;
    141 			if (user_mode(regs)) {
    142 				if (ufn == NULL)
    143 					return;
    144 				xtensa_backtrace_user(regs, depth, ufn, data);
    145 				return;
    146 			}
    147 			a0 = regs->areg[0];
    148 			a1 = regs->areg[1];
    149 			continue;
    150 		}
    151 
    152 		sp_start = a1;
    153 
    154 		pc = MAKE_PC_FROM_RA(a0, pc);
    155 		a0 = SPILL_SLOT(a1, 0);
    156 		a1 = SPILL_SLOT(a1, 1);
    157 	}
    158 }
    159 EXPORT_SYMBOL(xtensa_backtrace_kernel);
    160 
    161 #endif
    162 
    163 void walk_stackframe(unsigned long *sp,
    164 		int (*fn)(struct stackframe *frame, void *data),
    165 		void *data)
    166 {
    167 	unsigned long a0, a1;
    168 	unsigned long sp_end;
    169 
    170 	a1 = (unsigned long)sp;
    171 	sp_end = ALIGN(a1, THREAD_SIZE);
    172 
    173 	spill_registers();
    174 
    175 	while (a1 < sp_end) {
    176 		struct stackframe frame;
    177 
    178 		sp = (unsigned long *)a1;
    179 
    180 		a0 = SPILL_SLOT(a1, 0);
    181 		a1 = SPILL_SLOT(a1, 1);
    182 
    183 		if (a1 <= (unsigned long)sp)
    184 			break;
    185 
    186 		frame.pc = MAKE_PC_FROM_RA(a0, a1);
    187 		frame.sp = a1;
    188 
    189 		if (fn(&frame, data))
    190 			return;
    191 	}
    192 }
    193 
    194 #ifdef CONFIG_STACKTRACE
    195 
    196 struct stack_trace_data {
    197 	struct stack_trace *trace;
    198 	unsigned skip;
    199 };
    200 
    201 static int stack_trace_cb(struct stackframe *frame, void *data)
    202 {
    203 	struct stack_trace_data *trace_data = data;
    204 	struct stack_trace *trace = trace_data->trace;
    205 
    206 	if (trace_data->skip) {
    207 		--trace_data->skip;
    208 		return 0;
    209 	}
    210 	if (!kernel_text_address(frame->pc))
    211 		return 0;
    212 
    213 	trace->entries[trace->nr_entries++] = frame->pc;
    214 	return trace->nr_entries >= trace->max_entries;
    215 }
    216 
    217 void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace)
    218 {
    219 	struct stack_trace_data trace_data = {
    220 		.trace = trace,
    221 		.skip = trace->skip,
    222 	};
    223 	walk_stackframe(stack_pointer(task), stack_trace_cb, &trace_data);
    224 }
    225 EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
    226 
    227 void save_stack_trace(struct stack_trace *trace)
    228 {
    229 	save_stack_trace_tsk(current, trace);
    230 }
    231 EXPORT_SYMBOL_GPL(save_stack_trace);
    232 
    233 #endif
    234 
    235 #ifdef CONFIG_FRAME_POINTER
    236 
    237 struct return_addr_data {
    238 	unsigned long addr;
    239 	unsigned skip;
    240 };
    241 
    242 static int return_address_cb(struct stackframe *frame, void *data)
    243 {
    244 	struct return_addr_data *r = data;
    245 
    246 	if (r->skip) {
    247 		--r->skip;
    248 		return 0;
    249 	}
    250 	if (!kernel_text_address(frame->pc))
    251 		return 0;
    252 	r->addr = frame->pc;
    253 	return 1;
    254 }
    255 
    256 /*
    257  * level == 0 is for the return address from the caller of this function,
    258  * not from this function itself.
    259  */
    260 unsigned long return_address(unsigned level)
    261 {
    262 	struct return_addr_data r = {
    263 		.skip = level,
    264 	};
    265 	walk_stackframe(stack_pointer(NULL), return_address_cb, &r);
    266 	return r.addr;
    267 }
    268 EXPORT_SYMBOL(return_address);
    269 
    270 #endif