ARC: enable HAVE_REGS_AND_STACK_ACCESS_API feature

Enable HAVE_REGS_AND_STACK_ACCESS_API feature for ARC architecture,
including ARCcompact and ARCv2 flavors. Add supporting functions
and defines.

Signed-off-by: Sergey Matyukevich <sergey.matyukevich@synopsys.com>
Signed-off-by: Vineet Gupta <vgupta@kernel.org>
This commit is contained in:
Sergey Matyukevich 2022-04-14 11:17:21 +03:00 committed by Vineet Gupta
parent af2d861d4c
commit b3bbf6a70b
3 changed files with 147 additions and 0 deletions

View File

@ -36,6 +36,7 @@ config ARC
select HAVE_KERNEL_LZMA
select HAVE_KPROBES
select HAVE_KRETPROBES
select HAVE_REGS_AND_STACK_ACCESS_API
select HAVE_MOD_ARCH_SPECIFIC
select HAVE_PERF_EVENTS
select IRQ_DOMAIN

View File

@ -8,6 +8,7 @@
#define __ASM_ARC_PTRACE_H
#include <uapi/asm/ptrace.h>
#include <linux/compiler.h>
#ifndef __ASSEMBLY__
@ -54,6 +55,9 @@ struct pt_regs {
unsigned long user_r25;
};
#define MAX_REG_OFFSET offsetof(struct pt_regs, user_r25)
#else
struct pt_regs {
@ -102,6 +106,8 @@ struct pt_regs {
unsigned long status32;
};
#define MAX_REG_OFFSET offsetof(struct pt_regs, status32)
#endif
/* Callee saved registers - need to be saved only when you are scheduled out */
@ -154,6 +160,27 @@ static inline void instruction_pointer_set(struct pt_regs *regs,
{
instruction_pointer(regs) = val;
}
static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
{
return regs->sp;
}
extern int regs_query_register_offset(const char *name);
extern const char *regs_query_register_name(unsigned int offset);
extern bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr);
extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
unsigned int n);
static inline unsigned long regs_get_register(struct pt_regs *regs,
unsigned int offset)
{
if (unlikely(offset > MAX_REG_OFFSET))
return 0;
return *(unsigned long *)((unsigned long)regs + offset);
}
#endif /* !__ASSEMBLY__ */
#endif /* __ASM_PTRACE_H */

View File

@ -9,6 +9,89 @@
#include <linux/unistd.h>
#include <linux/elf.h>
struct pt_regs_offset {
const char *name;
int offset;
};
#define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)}
#define REG_OFFSET_END {.name = NULL, .offset = 0}
#ifdef CONFIG_ISA_ARCOMPACT
static const struct pt_regs_offset regoffset_table[] = {
REG_OFFSET_NAME(bta),
REG_OFFSET_NAME(lp_start),
REG_OFFSET_NAME(lp_end),
REG_OFFSET_NAME(lp_count),
REG_OFFSET_NAME(status32),
REG_OFFSET_NAME(ret),
REG_OFFSET_NAME(blink),
REG_OFFSET_NAME(fp),
REG_OFFSET_NAME(r26),
REG_OFFSET_NAME(r12),
REG_OFFSET_NAME(r11),
REG_OFFSET_NAME(r10),
REG_OFFSET_NAME(r9),
REG_OFFSET_NAME(r8),
REG_OFFSET_NAME(r7),
REG_OFFSET_NAME(r6),
REG_OFFSET_NAME(r5),
REG_OFFSET_NAME(r4),
REG_OFFSET_NAME(r3),
REG_OFFSET_NAME(r2),
REG_OFFSET_NAME(r1),
REG_OFFSET_NAME(r0),
REG_OFFSET_NAME(sp),
REG_OFFSET_NAME(orig_r0),
REG_OFFSET_NAME(event),
REG_OFFSET_NAME(user_r25),
REG_OFFSET_END,
};
#else
static const struct pt_regs_offset regoffset_table[] = {
REG_OFFSET_NAME(orig_r0),
REG_OFFSET_NAME(event),
REG_OFFSET_NAME(bta),
REG_OFFSET_NAME(user_r25),
REG_OFFSET_NAME(r26),
REG_OFFSET_NAME(fp),
REG_OFFSET_NAME(sp),
REG_OFFSET_NAME(r12),
REG_OFFSET_NAME(r30),
#ifdef CONFIG_ARC_HAS_ACCL_REGS
REG_OFFSET_NAME(r58),
REG_OFFSET_NAME(r59),
#endif
#ifdef CONFIG_ARC_DSP_SAVE_RESTORE_REGS
REG_OFFSET_NAME(DSP_CTRL),
#endif
REG_OFFSET_NAME(r0),
REG_OFFSET_NAME(r1),
REG_OFFSET_NAME(r2),
REG_OFFSET_NAME(r3),
REG_OFFSET_NAME(r4),
REG_OFFSET_NAME(r5),
REG_OFFSET_NAME(r6),
REG_OFFSET_NAME(r7),
REG_OFFSET_NAME(r8),
REG_OFFSET_NAME(r9),
REG_OFFSET_NAME(r10),
REG_OFFSET_NAME(r11),
REG_OFFSET_NAME(blink),
REG_OFFSET_NAME(lp_end),
REG_OFFSET_NAME(lp_start),
REG_OFFSET_NAME(lp_count),
REG_OFFSET_NAME(ei),
REG_OFFSET_NAME(ldi),
REG_OFFSET_NAME(jli),
REG_OFFSET_NAME(ret),
REG_OFFSET_NAME(status32),
REG_OFFSET_END,
};
#endif
static struct callee_regs *task_callee_regs(struct task_struct *tsk)
{
struct callee_regs *tmp = (struct callee_regs *)tsk->thread.callee_reg;
@ -267,3 +350,39 @@ asmlinkage void syscall_trace_exit(struct pt_regs *regs)
{
ptrace_report_syscall_exit(regs, 0);
}
int regs_query_register_offset(const char *name)
{
const struct pt_regs_offset *roff;
for (roff = regoffset_table; roff->name != NULL; roff++)
if (!strcmp(roff->name, name))
return roff->offset;
return -EINVAL;
}
const char *regs_query_register_name(unsigned int offset)
{
const struct pt_regs_offset *roff;
for (roff = regoffset_table; roff->name != NULL; roff++)
if (roff->offset == offset)
return roff->name;
return NULL;
}
bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
{
return (addr & ~(THREAD_SIZE - 1)) ==
(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1));
}
unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
{
unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
addr += n;
if (regs_within_kernel_stack(regs, (unsigned long)addr))
return *addr;
else
return 0;
}