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 register unsigned long current_sp asm ("sp"); 434bf1fa5aSUwe Kleine-König 4401223f36SKeun-O Park data.level = level + 2; 4501223f36SKeun-O Park data.addr = NULL; 464bf1fa5aSUwe Kleine-König 474bf1fa5aSUwe Kleine-König frame.fp = (unsigned long)__builtin_frame_address(0); 484bf1fa5aSUwe Kleine-König frame.sp = current_sp; 494bf1fa5aSUwe Kleine-König frame.lr = (unsigned long)__builtin_return_address(0); 504bf1fa5aSUwe Kleine-König frame.pc = (unsigned long)return_address; 514bf1fa5aSUwe Kleine-König 524bf1fa5aSUwe Kleine-König walk_stackframe(&frame, save_return_addr, &data); 534bf1fa5aSUwe Kleine-König 544bf1fa5aSUwe Kleine-König if (!data.level) 554bf1fa5aSUwe Kleine-König return data.addr; 564bf1fa5aSUwe Kleine-König else 574bf1fa5aSUwe Kleine-König return NULL; 584bf1fa5aSUwe Kleine-König } 594bf1fa5aSUwe Kleine-König 604bf1fa5aSUwe Kleine-König #else /* if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) */ 614bf1fa5aSUwe Kleine-König 624bf1fa5aSUwe Kleine-König #if defined(CONFIG_ARM_UNWIND) 634bf1fa5aSUwe Kleine-König #warning "TODO: return_address should use unwind tables" 644bf1fa5aSUwe Kleine-König #endif 654bf1fa5aSUwe Kleine-König 664bf1fa5aSUwe Kleine-König void *return_address(unsigned int level) 674bf1fa5aSUwe Kleine-König { 684bf1fa5aSUwe Kleine-König return NULL; 694bf1fa5aSUwe Kleine-König } 704bf1fa5aSUwe Kleine-König 714bf1fa5aSUwe Kleine-König #endif /* if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) / else */ 724bf1fa5aSUwe Kleine-König 734bf1fa5aSUwe Kleine-König EXPORT_SYMBOL_GPL(return_address); 74