/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (C) 2021 MediaTek Inc.
 */
#ifndef _HANG_UNWIND_H
#define _HANG_UNWIND_H

#include <linux/sched.h>
#include <linux/sched/task.h>
#include <linux/sched/task_stack.h>
#include <linux/interrupt.h>
#include <asm/sections.h>
#include <asm/ptrace.h>
#include <asm/bitops.h>

enum stack_type {
        STACK_TYPE_UNKNOWN,
        STACK_TYPE_TASK,
        STACK_TYPE_IRQ,
        STACK_TYPE_OVERFLOW,
        STACK_TYPE_SDEI_NORMAL,
        STACK_TYPE_SDEI_CRITICAL,
        __NR_STACK_TYPES
};

struct stack_info {
        unsigned long low;
        unsigned long high;
        enum stack_type type;
};

static inline bool on_stack(unsigned long sp, unsigned long size,
                            unsigned long low, unsigned long high,
                            enum stack_type type, struct stack_info *info)
{
        if (!low)
                return false;

        if (sp < low || sp + size < sp || sp + size > high)
                return false;

        if (info) {
                info->low = low;
                info->high = high;
                info->type = type;
        }
        return true;
}

static inline bool on_task_stack(const struct task_struct *tsk,
                                 unsigned long sp, unsigned long size,
                                 struct stack_info *info)
{
        unsigned long low = (unsigned long)task_stack_page(tsk);
        unsigned long high = low + THREAD_SIZE;

        return on_stack(sp, size, low, high, STACK_TYPE_TASK, info);
}

extern unsigned int hang_kernel_trace(struct task_struct *tsk,
					unsigned long *store, unsigned int size);
extern const char *hang_arch_vma_name(struct vm_area_struct *vma);
#endif
