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 */ 114bf1fa5aSUwe Kleine-König #include <linux/module.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) { 294bf1fa5aSUwe Kleine-König data->addr = (void *)frame->lr; 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 register unsigned long current_sp asm ("sp"); 434bf1fa5aSUwe Kleine-König 444bf1fa5aSUwe Kleine-König data.level = level + 1; 454bf1fa5aSUwe Kleine-König 464bf1fa5aSUwe Kleine-König frame.fp = (unsigned long)__builtin_frame_address(0); 474bf1fa5aSUwe Kleine-König frame.sp = current_sp; 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 #if defined(CONFIG_ARM_UNWIND) 624bf1fa5aSUwe Kleine-König #warning "TODO: return_address should use unwind tables" 634bf1fa5aSUwe Kleine-König #endif 644bf1fa5aSUwe Kleine-König 654bf1fa5aSUwe Kleine-König void *return_address(unsigned int level) 664bf1fa5aSUwe Kleine-König { 674bf1fa5aSUwe Kleine-König return NULL; 684bf1fa5aSUwe Kleine-König } 694bf1fa5aSUwe Kleine-König 704bf1fa5aSUwe Kleine-König #endif /* if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) / else */ 714bf1fa5aSUwe Kleine-König 724bf1fa5aSUwe Kleine-König EXPORT_SYMBOL_GPL(return_address); 73