14bf1fa5aSUwe Kleine-König /* 24bf1fa5aSUwe Kleine-König * arch/arm/kernel/return_address.c 34bf1fa5aSUwe Kleine-König * 44bf1fa5aSUwe Kleine-König * Copyright (C) 2009 Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de> 54bf1fa5aSUwe Kleine-König * for Pengutronix 64bf1fa5aSUwe Kleine-König * 74bf1fa5aSUwe Kleine-König * This program is free software; you can redistribute it and/or modify it 84bf1fa5aSUwe Kleine-König * under the terms of the GNU General Public License version 2 as published by 94bf1fa5aSUwe Kleine-König * the Free Software Foundation. 104bf1fa5aSUwe Kleine-König */ 11ecea4ab6SPaul Gortmaker #include <linux/export.h> 122bbd7e9bSRussell King #include <linux/ftrace.h> 134bf1fa5aSUwe Kleine-König 144bf1fa5aSUwe Kleine-König #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) 154bf1fa5aSUwe Kleine-König #include <linux/sched.h> 164bf1fa5aSUwe Kleine-König 174bf1fa5aSUwe Kleine-König #include <asm/stacktrace.h> 184bf1fa5aSUwe Kleine-König 194bf1fa5aSUwe Kleine-König struct return_address_data { 204bf1fa5aSUwe Kleine-König unsigned int level; 214bf1fa5aSUwe Kleine-König void *addr; 224bf1fa5aSUwe Kleine-König }; 234bf1fa5aSUwe Kleine-König 244bf1fa5aSUwe Kleine-König static int save_return_addr(struct stackframe *frame, void *d) 254bf1fa5aSUwe Kleine-König { 264bf1fa5aSUwe Kleine-König struct return_address_data *data = d; 274bf1fa5aSUwe Kleine-König 284bf1fa5aSUwe Kleine-König if (!data->level) { 2901223f36SKeun-O Park data->addr = (void *)frame->pc; 304bf1fa5aSUwe Kleine-König 314bf1fa5aSUwe Kleine-König return 1; 324bf1fa5aSUwe Kleine-König } else { 334bf1fa5aSUwe Kleine-König --data->level; 344bf1fa5aSUwe Kleine-König return 0; 354bf1fa5aSUwe Kleine-König } 364bf1fa5aSUwe Kleine-König } 374bf1fa5aSUwe Kleine-König 384bf1fa5aSUwe Kleine-König void *return_address(unsigned int level) 394bf1fa5aSUwe Kleine-König { 404bf1fa5aSUwe Kleine-König struct return_address_data data; 414bf1fa5aSUwe Kleine-König struct stackframe frame; 424bf1fa5aSUwe Kleine-König 4301223f36SKeun-O Park data.level = level + 2; 4401223f36SKeun-O Park data.addr = NULL; 454bf1fa5aSUwe Kleine-König 464bf1fa5aSUwe Kleine-König frame.fp = (unsigned long)__builtin_frame_address(0); 47a556ee12SBehan Webster frame.sp = current_stack_pointer; 484bf1fa5aSUwe Kleine-König frame.lr = (unsigned long)__builtin_return_address(0); 494bf1fa5aSUwe Kleine-König frame.pc = (unsigned long)return_address; 504bf1fa5aSUwe Kleine-König 514bf1fa5aSUwe Kleine-König walk_stackframe(&frame, save_return_addr, &data); 524bf1fa5aSUwe Kleine-König 534bf1fa5aSUwe Kleine-König if (!data.level) 544bf1fa5aSUwe Kleine-König return data.addr; 554bf1fa5aSUwe Kleine-König else 564bf1fa5aSUwe Kleine-König return NULL; 574bf1fa5aSUwe Kleine-König } 584bf1fa5aSUwe Kleine-König 594bf1fa5aSUwe Kleine-König #else /* if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) */ 604bf1fa5aSUwe Kleine-König 614bf1fa5aSUwe Kleine-König #endif /* if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) / else */ 624bf1fa5aSUwe Kleine-König 634bf1fa5aSUwe Kleine-König EXPORT_SYMBOL_GPL(return_address); 64