1803b0fc5SHuacai Chen /* SPDX-License-Identifier: GPL-2.0 */
2803b0fc5SHuacai Chen /*
3803b0fc5SHuacai Chen * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
4803b0fc5SHuacai Chen */
5803b0fc5SHuacai Chen #ifndef _ASM_PTRACE_H
6803b0fc5SHuacai Chen #define _ASM_PTRACE_H
7803b0fc5SHuacai Chen
8803b0fc5SHuacai Chen #include <asm/page.h>
99b3441a6STiezhu Yang #include <asm/irqflags.h>
10803b0fc5SHuacai Chen #include <asm/thread_info.h>
11803b0fc5SHuacai Chen #include <uapi/asm/ptrace.h>
12803b0fc5SHuacai Chen
13803b0fc5SHuacai Chen /*
14803b0fc5SHuacai Chen * This struct defines the way the registers are stored on the stack during
15803b0fc5SHuacai Chen * a system call/exception. If you add a register here, please also add it to
16803b0fc5SHuacai Chen * regoffset_table[] in arch/loongarch/kernel/ptrace.c.
17803b0fc5SHuacai Chen */
18803b0fc5SHuacai Chen struct pt_regs {
19803b0fc5SHuacai Chen /* Main processor registers. */
20803b0fc5SHuacai Chen unsigned long regs[32];
21803b0fc5SHuacai Chen
22803b0fc5SHuacai Chen /* Original syscall arg0. */
23803b0fc5SHuacai Chen unsigned long orig_a0;
24803b0fc5SHuacai Chen
25803b0fc5SHuacai Chen /* Special CSR registers. */
26803b0fc5SHuacai Chen unsigned long csr_era;
27803b0fc5SHuacai Chen unsigned long csr_badvaddr;
28803b0fc5SHuacai Chen unsigned long csr_crmd;
29803b0fc5SHuacai Chen unsigned long csr_prmd;
30803b0fc5SHuacai Chen unsigned long csr_euen;
31803b0fc5SHuacai Chen unsigned long csr_ecfg;
32803b0fc5SHuacai Chen unsigned long csr_estat;
334805a13dSYushan Zhou unsigned long __last[];
34803b0fc5SHuacai Chen } __aligned(8);
35803b0fc5SHuacai Chen
regs_irqs_disabled(struct pt_regs * regs)36803b0fc5SHuacai Chen static inline int regs_irqs_disabled(struct pt_regs *regs)
37803b0fc5SHuacai Chen {
38803b0fc5SHuacai Chen return arch_irqs_disabled_flags(regs->csr_prmd);
39803b0fc5SHuacai Chen }
40803b0fc5SHuacai Chen
kernel_stack_pointer(struct pt_regs * regs)41803b0fc5SHuacai Chen static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
42803b0fc5SHuacai Chen {
43803b0fc5SHuacai Chen return regs->regs[3];
44803b0fc5SHuacai Chen }
45803b0fc5SHuacai Chen
46803b0fc5SHuacai Chen /*
47803b0fc5SHuacai Chen * Don't use asm-generic/ptrace.h it defines FP accessors that don't make
48803b0fc5SHuacai Chen * sense on LoongArch. We rather want an error if they get invoked.
49803b0fc5SHuacai Chen */
50803b0fc5SHuacai Chen
instruction_pointer_set(struct pt_regs * regs,unsigned long val)51803b0fc5SHuacai Chen static inline void instruction_pointer_set(struct pt_regs *regs, unsigned long val)
52803b0fc5SHuacai Chen {
53803b0fc5SHuacai Chen regs->csr_era = val;
54803b0fc5SHuacai Chen }
55803b0fc5SHuacai Chen
56803b0fc5SHuacai Chen /* Query offset/name of register from its name/offset */
57803b0fc5SHuacai Chen extern int regs_query_register_offset(const char *name);
58803b0fc5SHuacai Chen #define MAX_REG_OFFSET (offsetof(struct pt_regs, __last))
59803b0fc5SHuacai Chen
60803b0fc5SHuacai Chen /**
61803b0fc5SHuacai Chen * regs_get_register() - get register value from its offset
62803b0fc5SHuacai Chen * @regs: pt_regs from which register value is gotten.
63803b0fc5SHuacai Chen * @offset: offset number of the register.
64803b0fc5SHuacai Chen *
65803b0fc5SHuacai Chen * regs_get_register returns the value of a register. The @offset is the
66803b0fc5SHuacai Chen * offset of the register in struct pt_regs address which specified by @regs.
67803b0fc5SHuacai Chen * If @offset is bigger than MAX_REG_OFFSET, this returns 0.
68803b0fc5SHuacai Chen */
regs_get_register(struct pt_regs * regs,unsigned int offset)69803b0fc5SHuacai Chen static inline unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset)
70803b0fc5SHuacai Chen {
71803b0fc5SHuacai Chen if (unlikely(offset > MAX_REG_OFFSET))
72803b0fc5SHuacai Chen return 0;
73803b0fc5SHuacai Chen
74803b0fc5SHuacai Chen return *(unsigned long *)((unsigned long)regs + offset);
75803b0fc5SHuacai Chen }
76803b0fc5SHuacai Chen
77803b0fc5SHuacai Chen /**
78803b0fc5SHuacai Chen * regs_within_kernel_stack() - check the address in the stack
79803b0fc5SHuacai Chen * @regs: pt_regs which contains kernel stack pointer.
80803b0fc5SHuacai Chen * @addr: address which is checked.
81803b0fc5SHuacai Chen *
82803b0fc5SHuacai Chen * regs_within_kernel_stack() checks @addr is within the kernel stack page(s).
83803b0fc5SHuacai Chen * If @addr is within the kernel stack, it returns true. If not, returns false.
84803b0fc5SHuacai Chen */
regs_within_kernel_stack(struct pt_regs * regs,unsigned long addr)85803b0fc5SHuacai Chen static inline int regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
86803b0fc5SHuacai Chen {
87803b0fc5SHuacai Chen return ((addr & ~(THREAD_SIZE - 1)) ==
88803b0fc5SHuacai Chen (kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1)));
89803b0fc5SHuacai Chen }
90803b0fc5SHuacai Chen
91803b0fc5SHuacai Chen /**
92803b0fc5SHuacai Chen * regs_get_kernel_stack_nth() - get Nth entry of the stack
93803b0fc5SHuacai Chen * @regs: pt_regs which contains kernel stack pointer.
94803b0fc5SHuacai Chen * @n: stack entry number.
95803b0fc5SHuacai Chen *
96803b0fc5SHuacai Chen * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
97803b0fc5SHuacai Chen * is specified by @regs. If the @n th entry is NOT in the kernel stack,
98803b0fc5SHuacai Chen * this returns 0.
99803b0fc5SHuacai Chen */
regs_get_kernel_stack_nth(struct pt_regs * regs,unsigned int n)100803b0fc5SHuacai Chen static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
101803b0fc5SHuacai Chen {
102803b0fc5SHuacai Chen unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
103803b0fc5SHuacai Chen
104803b0fc5SHuacai Chen addr += n;
105803b0fc5SHuacai Chen if (regs_within_kernel_stack(regs, (unsigned long)addr))
106803b0fc5SHuacai Chen return *addr;
107803b0fc5SHuacai Chen else
108803b0fc5SHuacai Chen return 0;
109803b0fc5SHuacai Chen }
110803b0fc5SHuacai Chen
111803b0fc5SHuacai Chen struct task_struct;
112803b0fc5SHuacai Chen
113356bd6f2SQing Zhang /**
114356bd6f2SQing Zhang * regs_get_kernel_argument() - get Nth function argument in kernel
115356bd6f2SQing Zhang * @regs: pt_regs of that context
116356bd6f2SQing Zhang * @n: function argument number (start from 0)
117356bd6f2SQing Zhang *
118356bd6f2SQing Zhang * regs_get_argument() returns @n th argument of the function call.
119356bd6f2SQing Zhang * Note that this chooses most probably assignment, in some case
120356bd6f2SQing Zhang * it can be incorrect.
121356bd6f2SQing Zhang * This is expected to be called from kprobes or ftrace with regs
122356bd6f2SQing Zhang * where the top of stack is the return address.
123356bd6f2SQing Zhang */
regs_get_kernel_argument(struct pt_regs * regs,unsigned int n)124356bd6f2SQing Zhang static inline unsigned long regs_get_kernel_argument(struct pt_regs *regs,
125356bd6f2SQing Zhang unsigned int n)
126356bd6f2SQing Zhang {
127356bd6f2SQing Zhang #define NR_REG_ARGUMENTS 8
128356bd6f2SQing Zhang static const unsigned int args[] = {
129356bd6f2SQing Zhang offsetof(struct pt_regs, regs[4]),
130356bd6f2SQing Zhang offsetof(struct pt_regs, regs[5]),
131356bd6f2SQing Zhang offsetof(struct pt_regs, regs[6]),
132356bd6f2SQing Zhang offsetof(struct pt_regs, regs[7]),
133356bd6f2SQing Zhang offsetof(struct pt_regs, regs[8]),
134356bd6f2SQing Zhang offsetof(struct pt_regs, regs[9]),
135356bd6f2SQing Zhang offsetof(struct pt_regs, regs[10]),
136356bd6f2SQing Zhang offsetof(struct pt_regs, regs[11]),
137356bd6f2SQing Zhang };
138356bd6f2SQing Zhang
139356bd6f2SQing Zhang if (n < NR_REG_ARGUMENTS)
140356bd6f2SQing Zhang return regs_get_register(regs, args[n]);
141356bd6f2SQing Zhang else {
142356bd6f2SQing Zhang n -= NR_REG_ARGUMENTS;
143356bd6f2SQing Zhang return regs_get_kernel_stack_nth(regs, n);
144356bd6f2SQing Zhang }
145356bd6f2SQing Zhang }
146356bd6f2SQing Zhang
147803b0fc5SHuacai Chen /*
148803b0fc5SHuacai Chen * Does the process account for user or for system time?
149803b0fc5SHuacai Chen */
150803b0fc5SHuacai Chen #define user_mode(regs) (((regs)->csr_prmd & PLV_MASK) == PLV_USER)
151803b0fc5SHuacai Chen
regs_return_value(struct pt_regs * regs)152803b0fc5SHuacai Chen static inline long regs_return_value(struct pt_regs *regs)
153803b0fc5SHuacai Chen {
154803b0fc5SHuacai Chen return regs->regs[4];
155803b0fc5SHuacai Chen }
156803b0fc5SHuacai Chen
regs_set_return_value(struct pt_regs * regs,unsigned long val)1578b5ee2c6STiezhu Yang static inline void regs_set_return_value(struct pt_regs *regs, unsigned long val)
1588b5ee2c6STiezhu Yang {
1598b5ee2c6STiezhu Yang regs->regs[4] = val;
1608b5ee2c6STiezhu Yang }
1618b5ee2c6STiezhu Yang
162803b0fc5SHuacai Chen #define instruction_pointer(regs) ((regs)->csr_era)
163803b0fc5SHuacai Chen #define profile_pc(regs) instruction_pointer(regs)
164803b0fc5SHuacai Chen
165*8879515eSTiezhu Yang extern void die(const char *str, struct pt_regs *regs);
166803b0fc5SHuacai Chen
die_if_kernel(const char * str,struct pt_regs * regs)167803b0fc5SHuacai Chen static inline void die_if_kernel(const char *str, struct pt_regs *regs)
168803b0fc5SHuacai Chen {
169803b0fc5SHuacai Chen if (unlikely(!user_mode(regs)))
170803b0fc5SHuacai Chen die(str, regs);
171803b0fc5SHuacai Chen }
172803b0fc5SHuacai Chen
173803b0fc5SHuacai Chen #define current_pt_regs() \
174803b0fc5SHuacai Chen ({ \
175803b0fc5SHuacai Chen unsigned long sp = (unsigned long)__builtin_frame_address(0); \
176b40fa75eSJinyang He (struct pt_regs *)((sp | (THREAD_SIZE - 1)) + 1) - 1; \
177803b0fc5SHuacai Chen })
178803b0fc5SHuacai Chen
179803b0fc5SHuacai Chen /* Helpers for working with the user stack pointer */
180803b0fc5SHuacai Chen
user_stack_pointer(struct pt_regs * regs)181803b0fc5SHuacai Chen static inline unsigned long user_stack_pointer(struct pt_regs *regs)
182803b0fc5SHuacai Chen {
183803b0fc5SHuacai Chen return regs->regs[3];
184803b0fc5SHuacai Chen }
185803b0fc5SHuacai Chen
user_stack_pointer_set(struct pt_regs * regs,unsigned long val)186803b0fc5SHuacai Chen static inline void user_stack_pointer_set(struct pt_regs *regs,
187803b0fc5SHuacai Chen unsigned long val)
188803b0fc5SHuacai Chen {
189803b0fc5SHuacai Chen regs->regs[3] = val;
190803b0fc5SHuacai Chen }
191803b0fc5SHuacai Chen
192424421a7SQing Zhang #ifdef CONFIG_HAVE_HW_BREAKPOINT
193424421a7SQing Zhang #define arch_has_single_step() (1)
194424421a7SQing Zhang #endif
195424421a7SQing Zhang
196803b0fc5SHuacai Chen #endif /* _ASM_PTRACE_H */
197