root/arch/openrisc/kernel/unwinder.c
/*
 * OpenRISC unwinder.c
 *
 * Reusable arch specific api for unwinding stacks.
 *
 * Copyright (C) 2017 Stafford Horne <shorne@gmail.com>
 *
 * This file is licensed under the terms of the GNU General Public License
 * version 2.  This program is licensed "as is" without any warranty of any
 * kind, whether express or implied.
 */

#include <linux/sched/task_stack.h>
#include <linux/kernel.h>

#include <asm/unwinder.h>

#ifdef CONFIG_FRAME_POINTER
struct or1k_frameinfo {
        unsigned long *fp;
        unsigned long ra;
        unsigned long top;
};

/*
 * Verify a frameinfo structure.  The return address should be a valid text
 * address.  The frame pointer may be null if its the last frame, otherwise
 * the frame pointer should point to a location in the stack after the
 * top of the next frame up.
 */
static inline int or1k_frameinfo_valid(struct or1k_frameinfo *frameinfo)
{
        return (frameinfo->fp == NULL ||
                (!kstack_end(frameinfo->fp) &&
                 frameinfo->fp > &frameinfo->top)) &&
               __kernel_text_address(frameinfo->ra);
}

/*
 * Create a stack trace doing scanning which is frame pointer aware. We can
 * get reliable stack traces by matching the previously found frame
 * pointer with the top of the stack address every time we find a valid
 * or1k_frameinfo.
 *
 * Ideally the stack parameter will be passed as FP, but it can not be
 * guaranteed.  Therefore we scan each address looking for the first sign
 * of a return address.
 *
 * The OpenRISC stack frame looks something like the following.  The
 * location SP is held in r1 and location FP is held in r2 when frame pointers
 * enabled.
 *
 * SP   -> (top of stack)
 *      -  (callee saved registers)
 *      -  (local variables)
 * FP-8 -> previous FP             \
 * FP-4 -> return address          |- or1k_frameinfo
 * FP   -> (previous top of stack) /
 */
void unwind_stack(void *data, unsigned long *stack,
                  void (*trace)(void *data, unsigned long addr, int reliable))
{
        unsigned long *next_fp = NULL;
        struct or1k_frameinfo *frameinfo = NULL;
        int reliable = 0;

        while (!kstack_end(stack)) {
                frameinfo = container_of(stack,
                                         struct or1k_frameinfo,
                                         top);

                if (__kernel_text_address(frameinfo->ra)) {
                        if (or1k_frameinfo_valid(frameinfo) &&
                            (next_fp == NULL ||
                             next_fp == &frameinfo->top)) {
                                reliable = 1;
                                next_fp = frameinfo->fp;
                        } else
                                reliable = 0;

                        trace(data, frameinfo->ra, reliable);
                }
                stack++;
        }
}

#else /* CONFIG_FRAME_POINTER */

/*
 * Create a stack trace by doing a simple scan treating all text addresses
 * as return addresses.
 */
void unwind_stack(void *data, unsigned long *stack,
                   void (*trace)(void *data, unsigned long addr, int reliable))
{
        unsigned long addr;

        while (!kstack_end(stack)) {
                addr = *stack++;
                if (__kernel_text_address(addr))
                        trace(data, addr, 0);
        }
}
#endif /* CONFIG_FRAME_POINTER */