1803b0fc5SHuacai Chen // SPDX-License-Identifier: GPL-2.0
2803b0fc5SHuacai Chen /*
3803b0fc5SHuacai Chen * Author: Hanlu Li <lihanlu@loongson.cn>
4803b0fc5SHuacai Chen * Huacai Chen <chenhuacai@loongson.cn>
5803b0fc5SHuacai Chen *
6803b0fc5SHuacai Chen * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
7803b0fc5SHuacai Chen *
8803b0fc5SHuacai Chen * Derived from MIPS:
9803b0fc5SHuacai Chen * Copyright (C) 1992 Ross Biro
10803b0fc5SHuacai Chen * Copyright (C) Linus Torvalds
11803b0fc5SHuacai Chen * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle
12803b0fc5SHuacai Chen * Copyright (C) 1996 David S. Miller
13803b0fc5SHuacai Chen * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
14803b0fc5SHuacai Chen * Copyright (C) 1999 MIPS Technologies, Inc.
15803b0fc5SHuacai Chen * Copyright (C) 2000 Ulf Carlsson
16803b0fc5SHuacai Chen */
17803b0fc5SHuacai Chen #include <linux/kernel.h>
18803b0fc5SHuacai Chen #include <linux/audit.h>
19803b0fc5SHuacai Chen #include <linux/compiler.h>
20803b0fc5SHuacai Chen #include <linux/context_tracking.h>
21803b0fc5SHuacai Chen #include <linux/elf.h>
22803b0fc5SHuacai Chen #include <linux/errno.h>
231a69f7a1SQing Zhang #include <linux/hw_breakpoint.h>
24803b0fc5SHuacai Chen #include <linux/mm.h>
251a69f7a1SQing Zhang #include <linux/nospec.h>
26803b0fc5SHuacai Chen #include <linux/ptrace.h>
27803b0fc5SHuacai Chen #include <linux/regset.h>
28803b0fc5SHuacai Chen #include <linux/sched.h>
29803b0fc5SHuacai Chen #include <linux/sched/task_stack.h>
30803b0fc5SHuacai Chen #include <linux/security.h>
31803b0fc5SHuacai Chen #include <linux/smp.h>
32803b0fc5SHuacai Chen #include <linux/stddef.h>
33803b0fc5SHuacai Chen #include <linux/seccomp.h>
34424421a7SQing Zhang #include <linux/thread_info.h>
35803b0fc5SHuacai Chen #include <linux/uaccess.h>
36803b0fc5SHuacai Chen
37803b0fc5SHuacai Chen #include <asm/byteorder.h>
38803b0fc5SHuacai Chen #include <asm/cpu.h>
39803b0fc5SHuacai Chen #include <asm/cpu-info.h>
40803b0fc5SHuacai Chen #include <asm/fpu.h>
41bd3c5798SQi Hu #include <asm/lbt.h>
42803b0fc5SHuacai Chen #include <asm/loongarch.h>
43803b0fc5SHuacai Chen #include <asm/page.h>
44803b0fc5SHuacai Chen #include <asm/pgtable.h>
45803b0fc5SHuacai Chen #include <asm/processor.h>
46424421a7SQing Zhang #include <asm/ptrace.h>
47803b0fc5SHuacai Chen #include <asm/reg.h>
48803b0fc5SHuacai Chen #include <asm/syscall.h>
49803b0fc5SHuacai Chen
init_fp_ctx(struct task_struct * target)50803b0fc5SHuacai Chen static void init_fp_ctx(struct task_struct *target)
51803b0fc5SHuacai Chen {
52803b0fc5SHuacai Chen /* The target already has context */
53803b0fc5SHuacai Chen if (tsk_used_math(target))
54803b0fc5SHuacai Chen return;
55803b0fc5SHuacai Chen
56803b0fc5SHuacai Chen /* Begin with data registers set to all 1s... */
57803b0fc5SHuacai Chen memset(&target->thread.fpu.fpr, ~0, sizeof(target->thread.fpu.fpr));
58803b0fc5SHuacai Chen set_stopped_child_used_math(target);
59803b0fc5SHuacai Chen }
60803b0fc5SHuacai Chen
61803b0fc5SHuacai Chen /*
62803b0fc5SHuacai Chen * Called by kernel/ptrace.c when detaching..
63803b0fc5SHuacai Chen *
64803b0fc5SHuacai Chen * Make sure single step bits etc are not set.
65803b0fc5SHuacai Chen */
ptrace_disable(struct task_struct * child)66803b0fc5SHuacai Chen void ptrace_disable(struct task_struct *child)
67803b0fc5SHuacai Chen {
68803b0fc5SHuacai Chen /* Don't load the watchpoint registers for the ex-child. */
69803b0fc5SHuacai Chen clear_tsk_thread_flag(child, TIF_LOAD_WATCH);
70803b0fc5SHuacai Chen clear_tsk_thread_flag(child, TIF_SINGLESTEP);
71803b0fc5SHuacai Chen }
72803b0fc5SHuacai Chen
73803b0fc5SHuacai Chen /* regset get/set implementations */
74803b0fc5SHuacai Chen
gpr_get(struct task_struct * target,const struct user_regset * regset,struct membuf to)75803b0fc5SHuacai Chen static int gpr_get(struct task_struct *target,
76803b0fc5SHuacai Chen const struct user_regset *regset,
77803b0fc5SHuacai Chen struct membuf to)
78803b0fc5SHuacai Chen {
79803b0fc5SHuacai Chen int r;
80803b0fc5SHuacai Chen struct pt_regs *regs = task_pt_regs(target);
81803b0fc5SHuacai Chen
82803b0fc5SHuacai Chen r = membuf_write(&to, ®s->regs, sizeof(u64) * GPR_NUM);
83803b0fc5SHuacai Chen r = membuf_write(&to, ®s->orig_a0, sizeof(u64));
84803b0fc5SHuacai Chen r = membuf_write(&to, ®s->csr_era, sizeof(u64));
85803b0fc5SHuacai Chen r = membuf_write(&to, ®s->csr_badvaddr, sizeof(u64));
86803b0fc5SHuacai Chen
87803b0fc5SHuacai Chen return r;
88803b0fc5SHuacai Chen }
89803b0fc5SHuacai Chen
gpr_set(struct task_struct * target,const struct user_regset * regset,unsigned int pos,unsigned int count,const void * kbuf,const void __user * ubuf)90803b0fc5SHuacai Chen static int gpr_set(struct task_struct *target,
91803b0fc5SHuacai Chen const struct user_regset *regset,
92803b0fc5SHuacai Chen unsigned int pos, unsigned int count,
93803b0fc5SHuacai Chen const void *kbuf, const void __user *ubuf)
94803b0fc5SHuacai Chen {
95803b0fc5SHuacai Chen int err;
96803b0fc5SHuacai Chen int a0_start = sizeof(u64) * GPR_NUM;
97803b0fc5SHuacai Chen int era_start = a0_start + sizeof(u64);
98803b0fc5SHuacai Chen int badvaddr_start = era_start + sizeof(u64);
99803b0fc5SHuacai Chen struct pt_regs *regs = task_pt_regs(target);
100803b0fc5SHuacai Chen
101803b0fc5SHuacai Chen err = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
102803b0fc5SHuacai Chen ®s->regs,
103803b0fc5SHuacai Chen 0, a0_start);
104803b0fc5SHuacai Chen err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
105803b0fc5SHuacai Chen ®s->orig_a0,
106803b0fc5SHuacai Chen a0_start, a0_start + sizeof(u64));
107803b0fc5SHuacai Chen err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
108803b0fc5SHuacai Chen ®s->csr_era,
109803b0fc5SHuacai Chen era_start, era_start + sizeof(u64));
110803b0fc5SHuacai Chen err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
111803b0fc5SHuacai Chen ®s->csr_badvaddr,
112803b0fc5SHuacai Chen badvaddr_start, badvaddr_start + sizeof(u64));
113803b0fc5SHuacai Chen
114803b0fc5SHuacai Chen return err;
115803b0fc5SHuacai Chen }
116803b0fc5SHuacai Chen
117803b0fc5SHuacai Chen
118803b0fc5SHuacai Chen /*
119803b0fc5SHuacai Chen * Get the general floating-point registers.
120803b0fc5SHuacai Chen */
gfpr_get(struct task_struct * target,struct membuf * to)121803b0fc5SHuacai Chen static int gfpr_get(struct task_struct *target, struct membuf *to)
122803b0fc5SHuacai Chen {
123803b0fc5SHuacai Chen return membuf_write(to, &target->thread.fpu.fpr,
124803b0fc5SHuacai Chen sizeof(elf_fpreg_t) * NUM_FPU_REGS);
125803b0fc5SHuacai Chen }
126803b0fc5SHuacai Chen
gfpr_get_simd(struct task_struct * target,struct membuf * to)127803b0fc5SHuacai Chen static int gfpr_get_simd(struct task_struct *target, struct membuf *to)
128803b0fc5SHuacai Chen {
129803b0fc5SHuacai Chen int i, r;
130803b0fc5SHuacai Chen u64 fpr_val;
131803b0fc5SHuacai Chen
132803b0fc5SHuacai Chen BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t));
133803b0fc5SHuacai Chen for (i = 0; i < NUM_FPU_REGS; i++) {
134803b0fc5SHuacai Chen fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0);
135803b0fc5SHuacai Chen r = membuf_write(to, &fpr_val, sizeof(elf_fpreg_t));
136803b0fc5SHuacai Chen }
137803b0fc5SHuacai Chen
138803b0fc5SHuacai Chen return r;
139803b0fc5SHuacai Chen }
140803b0fc5SHuacai Chen
141803b0fc5SHuacai Chen /*
142803b0fc5SHuacai Chen * Choose the appropriate helper for general registers, and then copy
143803b0fc5SHuacai Chen * the FCC and FCSR registers separately.
144803b0fc5SHuacai Chen */
fpr_get(struct task_struct * target,const struct user_regset * regset,struct membuf to)145803b0fc5SHuacai Chen static int fpr_get(struct task_struct *target,
146803b0fc5SHuacai Chen const struct user_regset *regset,
147803b0fc5SHuacai Chen struct membuf to)
148803b0fc5SHuacai Chen {
149803b0fc5SHuacai Chen int r;
150803b0fc5SHuacai Chen
151656f9aecSHuacai Chen save_fpu_regs(target);
152656f9aecSHuacai Chen
153803b0fc5SHuacai Chen if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t))
154803b0fc5SHuacai Chen r = gfpr_get(target, &to);
155803b0fc5SHuacai Chen else
156803b0fc5SHuacai Chen r = gfpr_get_simd(target, &to);
157803b0fc5SHuacai Chen
158803b0fc5SHuacai Chen r = membuf_write(&to, &target->thread.fpu.fcc, sizeof(target->thread.fpu.fcc));
159803b0fc5SHuacai Chen r = membuf_write(&to, &target->thread.fpu.fcsr, sizeof(target->thread.fpu.fcsr));
160803b0fc5SHuacai Chen
161803b0fc5SHuacai Chen return r;
162803b0fc5SHuacai Chen }
163803b0fc5SHuacai Chen
gfpr_set(struct task_struct * target,unsigned int * pos,unsigned int * count,const void ** kbuf,const void __user ** ubuf)164803b0fc5SHuacai Chen static int gfpr_set(struct task_struct *target,
165803b0fc5SHuacai Chen unsigned int *pos, unsigned int *count,
166803b0fc5SHuacai Chen const void **kbuf, const void __user **ubuf)
167803b0fc5SHuacai Chen {
168803b0fc5SHuacai Chen return user_regset_copyin(pos, count, kbuf, ubuf,
169803b0fc5SHuacai Chen &target->thread.fpu.fpr,
170803b0fc5SHuacai Chen 0, NUM_FPU_REGS * sizeof(elf_fpreg_t));
171803b0fc5SHuacai Chen }
172803b0fc5SHuacai Chen
gfpr_set_simd(struct task_struct * target,unsigned int * pos,unsigned int * count,const void ** kbuf,const void __user ** ubuf)173803b0fc5SHuacai Chen static int gfpr_set_simd(struct task_struct *target,
174803b0fc5SHuacai Chen unsigned int *pos, unsigned int *count,
175803b0fc5SHuacai Chen const void **kbuf, const void __user **ubuf)
176803b0fc5SHuacai Chen {
177803b0fc5SHuacai Chen int i, err;
178803b0fc5SHuacai Chen u64 fpr_val;
179803b0fc5SHuacai Chen
180803b0fc5SHuacai Chen BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t));
181803b0fc5SHuacai Chen for (i = 0; i < NUM_FPU_REGS && *count > 0; i++) {
182803b0fc5SHuacai Chen err = user_regset_copyin(pos, count, kbuf, ubuf,
183803b0fc5SHuacai Chen &fpr_val, i * sizeof(elf_fpreg_t),
184803b0fc5SHuacai Chen (i + 1) * sizeof(elf_fpreg_t));
185803b0fc5SHuacai Chen if (err)
186803b0fc5SHuacai Chen return err;
187803b0fc5SHuacai Chen set_fpr64(&target->thread.fpu.fpr[i], 0, fpr_val);
188803b0fc5SHuacai Chen }
189803b0fc5SHuacai Chen
190803b0fc5SHuacai Chen return 0;
191803b0fc5SHuacai Chen }
192803b0fc5SHuacai Chen
193803b0fc5SHuacai Chen /*
194803b0fc5SHuacai Chen * Choose the appropriate helper for general registers, and then copy
195803b0fc5SHuacai Chen * the FCC register separately.
196803b0fc5SHuacai Chen */
fpr_set(struct task_struct * target,const struct user_regset * regset,unsigned int pos,unsigned int count,const void * kbuf,const void __user * ubuf)197803b0fc5SHuacai Chen static int fpr_set(struct task_struct *target,
198803b0fc5SHuacai Chen const struct user_regset *regset,
199803b0fc5SHuacai Chen unsigned int pos, unsigned int count,
200803b0fc5SHuacai Chen const void *kbuf, const void __user *ubuf)
201803b0fc5SHuacai Chen {
202803b0fc5SHuacai Chen const int fcc_start = NUM_FPU_REGS * sizeof(elf_fpreg_t);
203b0f3bdc0SQi Hu const int fcsr_start = fcc_start + sizeof(u64);
204803b0fc5SHuacai Chen int err;
205803b0fc5SHuacai Chen
206803b0fc5SHuacai Chen BUG_ON(count % sizeof(elf_fpreg_t));
207803b0fc5SHuacai Chen if (pos + count > sizeof(elf_fpregset_t))
208803b0fc5SHuacai Chen return -EIO;
209803b0fc5SHuacai Chen
210803b0fc5SHuacai Chen init_fp_ctx(target);
211803b0fc5SHuacai Chen
212803b0fc5SHuacai Chen if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t))
213803b0fc5SHuacai Chen err = gfpr_set(target, &pos, &count, &kbuf, &ubuf);
214803b0fc5SHuacai Chen else
215803b0fc5SHuacai Chen err = gfpr_set_simd(target, &pos, &count, &kbuf, &ubuf);
216803b0fc5SHuacai Chen if (err)
217803b0fc5SHuacai Chen return err;
218803b0fc5SHuacai Chen
219803b0fc5SHuacai Chen err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
220b0f3bdc0SQi Hu &target->thread.fpu.fcc, fcc_start,
221b0f3bdc0SQi Hu fcc_start + sizeof(u64));
222b0f3bdc0SQi Hu err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
223b0f3bdc0SQi Hu &target->thread.fpu.fcsr, fcsr_start,
224b0f3bdc0SQi Hu fcsr_start + sizeof(u32));
225803b0fc5SHuacai Chen
226803b0fc5SHuacai Chen return err;
227803b0fc5SHuacai Chen }
228803b0fc5SHuacai Chen
cfg_get(struct task_struct * target,const struct user_regset * regset,struct membuf to)229803b0fc5SHuacai Chen static int cfg_get(struct task_struct *target,
230803b0fc5SHuacai Chen const struct user_regset *regset,
231803b0fc5SHuacai Chen struct membuf to)
232803b0fc5SHuacai Chen {
233803b0fc5SHuacai Chen int i, r;
234803b0fc5SHuacai Chen u32 cfg_val;
235803b0fc5SHuacai Chen
236803b0fc5SHuacai Chen i = 0;
237803b0fc5SHuacai Chen while (to.left > 0) {
238803b0fc5SHuacai Chen cfg_val = read_cpucfg(i++);
239803b0fc5SHuacai Chen r = membuf_write(&to, &cfg_val, sizeof(u32));
240803b0fc5SHuacai Chen }
241803b0fc5SHuacai Chen
242803b0fc5SHuacai Chen return r;
243803b0fc5SHuacai Chen }
244803b0fc5SHuacai Chen
245803b0fc5SHuacai Chen /*
246803b0fc5SHuacai Chen * CFG registers are read-only.
247803b0fc5SHuacai Chen */
cfg_set(struct task_struct * target,const struct user_regset * regset,unsigned int pos,unsigned int count,const void * kbuf,const void __user * ubuf)248803b0fc5SHuacai Chen static int cfg_set(struct task_struct *target,
249803b0fc5SHuacai Chen const struct user_regset *regset,
250803b0fc5SHuacai Chen unsigned int pos, unsigned int count,
251803b0fc5SHuacai Chen const void *kbuf, const void __user *ubuf)
252803b0fc5SHuacai Chen {
253803b0fc5SHuacai Chen return 0;
254803b0fc5SHuacai Chen }
255803b0fc5SHuacai Chen
25661650023SHuacai Chen #ifdef CONFIG_CPU_HAS_LSX
25761650023SHuacai Chen
copy_pad_fprs(struct task_struct * target,const struct user_regset * regset,struct membuf * to,unsigned int live_sz)25861650023SHuacai Chen static void copy_pad_fprs(struct task_struct *target,
25961650023SHuacai Chen const struct user_regset *regset,
26061650023SHuacai Chen struct membuf *to, unsigned int live_sz)
26161650023SHuacai Chen {
26261650023SHuacai Chen int i, j;
26361650023SHuacai Chen unsigned long long fill = ~0ull;
26461650023SHuacai Chen unsigned int cp_sz, pad_sz;
26561650023SHuacai Chen
26661650023SHuacai Chen cp_sz = min(regset->size, live_sz);
26761650023SHuacai Chen pad_sz = regset->size - cp_sz;
26861650023SHuacai Chen WARN_ON(pad_sz % sizeof(fill));
26961650023SHuacai Chen
27061650023SHuacai Chen for (i = 0; i < NUM_FPU_REGS; i++) {
27161650023SHuacai Chen membuf_write(to, &target->thread.fpu.fpr[i], cp_sz);
27261650023SHuacai Chen for (j = 0; j < (pad_sz / sizeof(fill)); j++) {
27361650023SHuacai Chen membuf_store(to, fill);
27461650023SHuacai Chen }
27561650023SHuacai Chen }
27661650023SHuacai Chen }
27761650023SHuacai Chen
simd_get(struct task_struct * target,const struct user_regset * regset,struct membuf to)27861650023SHuacai Chen static int simd_get(struct task_struct *target,
27961650023SHuacai Chen const struct user_regset *regset,
28061650023SHuacai Chen struct membuf to)
28161650023SHuacai Chen {
28261650023SHuacai Chen const unsigned int wr_size = NUM_FPU_REGS * regset->size;
28361650023SHuacai Chen
284656f9aecSHuacai Chen save_fpu_regs(target);
285656f9aecSHuacai Chen
28661650023SHuacai Chen if (!tsk_used_math(target)) {
28761650023SHuacai Chen /* The task hasn't used FP or LSX, fill with 0xff */
28861650023SHuacai Chen copy_pad_fprs(target, regset, &to, 0);
28961650023SHuacai Chen } else if (!test_tsk_thread_flag(target, TIF_LSX_CTX_LIVE)) {
29061650023SHuacai Chen /* Copy scalar FP context, fill the rest with 0xff */
29161650023SHuacai Chen copy_pad_fprs(target, regset, &to, 8);
29261650023SHuacai Chen #ifdef CONFIG_CPU_HAS_LASX
29361650023SHuacai Chen } else if (!test_tsk_thread_flag(target, TIF_LASX_CTX_LIVE)) {
29461650023SHuacai Chen /* Copy LSX 128 Bit context, fill the rest with 0xff */
29561650023SHuacai Chen copy_pad_fprs(target, regset, &to, 16);
29661650023SHuacai Chen #endif
29761650023SHuacai Chen } else if (sizeof(target->thread.fpu.fpr[0]) == regset->size) {
29861650023SHuacai Chen /* Trivially copy the vector registers */
29961650023SHuacai Chen membuf_write(&to, &target->thread.fpu.fpr, wr_size);
30061650023SHuacai Chen } else {
30161650023SHuacai Chen /* Copy as much context as possible, fill the rest with 0xff */
30261650023SHuacai Chen copy_pad_fprs(target, regset, &to, sizeof(target->thread.fpu.fpr[0]));
30361650023SHuacai Chen }
30461650023SHuacai Chen
30561650023SHuacai Chen return 0;
30661650023SHuacai Chen }
30761650023SHuacai Chen
simd_set(struct task_struct * target,const struct user_regset * regset,unsigned int pos,unsigned int count,const void * kbuf,const void __user * ubuf)30861650023SHuacai Chen static int simd_set(struct task_struct *target,
30961650023SHuacai Chen const struct user_regset *regset,
31061650023SHuacai Chen unsigned int pos, unsigned int count,
31161650023SHuacai Chen const void *kbuf, const void __user *ubuf)
31261650023SHuacai Chen {
31361650023SHuacai Chen const unsigned int wr_size = NUM_FPU_REGS * regset->size;
31461650023SHuacai Chen unsigned int cp_sz;
31561650023SHuacai Chen int i, err, start;
31661650023SHuacai Chen
31761650023SHuacai Chen init_fp_ctx(target);
31861650023SHuacai Chen
31961650023SHuacai Chen if (sizeof(target->thread.fpu.fpr[0]) == regset->size) {
32061650023SHuacai Chen /* Trivially copy the vector registers */
32161650023SHuacai Chen err = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
32261650023SHuacai Chen &target->thread.fpu.fpr,
32361650023SHuacai Chen 0, wr_size);
32461650023SHuacai Chen } else {
32561650023SHuacai Chen /* Copy as much context as possible */
32661650023SHuacai Chen cp_sz = min_t(unsigned int, regset->size,
32761650023SHuacai Chen sizeof(target->thread.fpu.fpr[0]));
32861650023SHuacai Chen
32961650023SHuacai Chen i = start = err = 0;
33061650023SHuacai Chen for (; i < NUM_FPU_REGS; i++, start += regset->size) {
33161650023SHuacai Chen err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
33261650023SHuacai Chen &target->thread.fpu.fpr[i],
33361650023SHuacai Chen start, start + cp_sz);
33461650023SHuacai Chen }
33561650023SHuacai Chen }
33661650023SHuacai Chen
33761650023SHuacai Chen return err;
33861650023SHuacai Chen }
33961650023SHuacai Chen
34061650023SHuacai Chen #endif /* CONFIG_CPU_HAS_LSX */
34161650023SHuacai Chen
342bd3c5798SQi Hu #ifdef CONFIG_CPU_HAS_LBT
lbt_get(struct task_struct * target,const struct user_regset * regset,struct membuf to)343bd3c5798SQi Hu static int lbt_get(struct task_struct *target,
344bd3c5798SQi Hu const struct user_regset *regset,
345bd3c5798SQi Hu struct membuf to)
346bd3c5798SQi Hu {
347bd3c5798SQi Hu int r;
348bd3c5798SQi Hu
349bd3c5798SQi Hu r = membuf_write(&to, &target->thread.lbt.scr0, sizeof(target->thread.lbt.scr0));
350bd3c5798SQi Hu r = membuf_write(&to, &target->thread.lbt.scr1, sizeof(target->thread.lbt.scr1));
351bd3c5798SQi Hu r = membuf_write(&to, &target->thread.lbt.scr2, sizeof(target->thread.lbt.scr2));
352bd3c5798SQi Hu r = membuf_write(&to, &target->thread.lbt.scr3, sizeof(target->thread.lbt.scr3));
353bd3c5798SQi Hu r = membuf_write(&to, &target->thread.lbt.eflags, sizeof(u32));
354bd3c5798SQi Hu r = membuf_write(&to, &target->thread.fpu.ftop, sizeof(u32));
355bd3c5798SQi Hu
356bd3c5798SQi Hu return r;
357bd3c5798SQi Hu }
358bd3c5798SQi Hu
lbt_set(struct task_struct * target,const struct user_regset * regset,unsigned int pos,unsigned int count,const void * kbuf,const void __user * ubuf)359bd3c5798SQi Hu static int lbt_set(struct task_struct *target,
360bd3c5798SQi Hu const struct user_regset *regset,
361bd3c5798SQi Hu unsigned int pos, unsigned int count,
362bd3c5798SQi Hu const void *kbuf, const void __user *ubuf)
363bd3c5798SQi Hu {
364bd3c5798SQi Hu int err = 0;
365bd3c5798SQi Hu const int eflags_start = 4 * sizeof(target->thread.lbt.scr0);
366bd3c5798SQi Hu const int ftop_start = eflags_start + sizeof(u32);
367bd3c5798SQi Hu
368bd3c5798SQi Hu err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
369bd3c5798SQi Hu &target->thread.lbt.scr0,
370bd3c5798SQi Hu 0, 4 * sizeof(target->thread.lbt.scr0));
371bd3c5798SQi Hu err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
372bd3c5798SQi Hu &target->thread.lbt.eflags,
373bd3c5798SQi Hu eflags_start, ftop_start);
374bd3c5798SQi Hu err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
375bd3c5798SQi Hu &target->thread.fpu.ftop,
376bd3c5798SQi Hu ftop_start, ftop_start + sizeof(u32));
377bd3c5798SQi Hu
378bd3c5798SQi Hu return err;
379bd3c5798SQi Hu }
380bd3c5798SQi Hu #endif /* CONFIG_CPU_HAS_LBT */
381bd3c5798SQi Hu
3821a69f7a1SQing Zhang #ifdef CONFIG_HAVE_HW_BREAKPOINT
3831a69f7a1SQing Zhang
3841a69f7a1SQing Zhang /*
3851a69f7a1SQing Zhang * Handle hitting a HW-breakpoint.
3861a69f7a1SQing Zhang */
ptrace_hbptriggered(struct perf_event * bp,struct perf_sample_data * data,struct pt_regs * regs)3871a69f7a1SQing Zhang static void ptrace_hbptriggered(struct perf_event *bp,
3881a69f7a1SQing Zhang struct perf_sample_data *data,
3891a69f7a1SQing Zhang struct pt_regs *regs)
3901a69f7a1SQing Zhang {
3911a69f7a1SQing Zhang int i;
3921a69f7a1SQing Zhang struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp);
3931a69f7a1SQing Zhang
3941a69f7a1SQing Zhang for (i = 0; i < LOONGARCH_MAX_BRP; ++i)
3951a69f7a1SQing Zhang if (current->thread.hbp_break[i] == bp)
3961a69f7a1SQing Zhang break;
3971a69f7a1SQing Zhang
3981a69f7a1SQing Zhang for (i = 0; i < LOONGARCH_MAX_WRP; ++i)
3991a69f7a1SQing Zhang if (current->thread.hbp_watch[i] == bp)
4001a69f7a1SQing Zhang break;
4011a69f7a1SQing Zhang
4021a69f7a1SQing Zhang force_sig_ptrace_errno_trap(i, (void __user *)bkpt->address);
4031a69f7a1SQing Zhang }
4041a69f7a1SQing Zhang
ptrace_hbp_get_event(unsigned int note_type,struct task_struct * tsk,unsigned long idx)4051a69f7a1SQing Zhang static struct perf_event *ptrace_hbp_get_event(unsigned int note_type,
4061a69f7a1SQing Zhang struct task_struct *tsk,
4071a69f7a1SQing Zhang unsigned long idx)
4081a69f7a1SQing Zhang {
4091a69f7a1SQing Zhang struct perf_event *bp;
4101a69f7a1SQing Zhang
4111a69f7a1SQing Zhang switch (note_type) {
4121a69f7a1SQing Zhang case NT_LOONGARCH_HW_BREAK:
4131a69f7a1SQing Zhang if (idx >= LOONGARCH_MAX_BRP)
4141a69f7a1SQing Zhang return ERR_PTR(-EINVAL);
4151a69f7a1SQing Zhang idx = array_index_nospec(idx, LOONGARCH_MAX_BRP);
4161a69f7a1SQing Zhang bp = tsk->thread.hbp_break[idx];
4171a69f7a1SQing Zhang break;
4181a69f7a1SQing Zhang case NT_LOONGARCH_HW_WATCH:
4191a69f7a1SQing Zhang if (idx >= LOONGARCH_MAX_WRP)
4201a69f7a1SQing Zhang return ERR_PTR(-EINVAL);
4211a69f7a1SQing Zhang idx = array_index_nospec(idx, LOONGARCH_MAX_WRP);
4221a69f7a1SQing Zhang bp = tsk->thread.hbp_watch[idx];
4231a69f7a1SQing Zhang break;
4241a69f7a1SQing Zhang }
4251a69f7a1SQing Zhang
4261a69f7a1SQing Zhang return bp;
4271a69f7a1SQing Zhang }
4281a69f7a1SQing Zhang
ptrace_hbp_set_event(unsigned int note_type,struct task_struct * tsk,unsigned long idx,struct perf_event * bp)4291a69f7a1SQing Zhang static int ptrace_hbp_set_event(unsigned int note_type,
4301a69f7a1SQing Zhang struct task_struct *tsk,
4311a69f7a1SQing Zhang unsigned long idx,
4321a69f7a1SQing Zhang struct perf_event *bp)
4331a69f7a1SQing Zhang {
4341a69f7a1SQing Zhang switch (note_type) {
4351a69f7a1SQing Zhang case NT_LOONGARCH_HW_BREAK:
4361a69f7a1SQing Zhang if (idx >= LOONGARCH_MAX_BRP)
4371a69f7a1SQing Zhang return -EINVAL;
4381a69f7a1SQing Zhang idx = array_index_nospec(idx, LOONGARCH_MAX_BRP);
4391a69f7a1SQing Zhang tsk->thread.hbp_break[idx] = bp;
4401a69f7a1SQing Zhang break;
4411a69f7a1SQing Zhang case NT_LOONGARCH_HW_WATCH:
4421a69f7a1SQing Zhang if (idx >= LOONGARCH_MAX_WRP)
4431a69f7a1SQing Zhang return -EINVAL;
4441a69f7a1SQing Zhang idx = array_index_nospec(idx, LOONGARCH_MAX_WRP);
4451a69f7a1SQing Zhang tsk->thread.hbp_watch[idx] = bp;
4461a69f7a1SQing Zhang break;
4471a69f7a1SQing Zhang }
4481a69f7a1SQing Zhang
4491a69f7a1SQing Zhang return 0;
4501a69f7a1SQing Zhang }
4511a69f7a1SQing Zhang
ptrace_hbp_create(unsigned int note_type,struct task_struct * tsk,unsigned long idx)4521a69f7a1SQing Zhang static struct perf_event *ptrace_hbp_create(unsigned int note_type,
4531a69f7a1SQing Zhang struct task_struct *tsk,
4541a69f7a1SQing Zhang unsigned long idx)
4551a69f7a1SQing Zhang {
4561a69f7a1SQing Zhang int err, type;
4571a69f7a1SQing Zhang struct perf_event *bp;
4581a69f7a1SQing Zhang struct perf_event_attr attr;
4591a69f7a1SQing Zhang
4601a69f7a1SQing Zhang switch (note_type) {
4611a69f7a1SQing Zhang case NT_LOONGARCH_HW_BREAK:
4621a69f7a1SQing Zhang type = HW_BREAKPOINT_X;
4631a69f7a1SQing Zhang break;
4641a69f7a1SQing Zhang case NT_LOONGARCH_HW_WATCH:
4651a69f7a1SQing Zhang type = HW_BREAKPOINT_RW;
4661a69f7a1SQing Zhang break;
4671a69f7a1SQing Zhang default:
4681a69f7a1SQing Zhang return ERR_PTR(-EINVAL);
4691a69f7a1SQing Zhang }
4701a69f7a1SQing Zhang
4711a69f7a1SQing Zhang ptrace_breakpoint_init(&attr);
4721a69f7a1SQing Zhang
4731a69f7a1SQing Zhang /*
4741a69f7a1SQing Zhang * Initialise fields to sane defaults
4751a69f7a1SQing Zhang * (i.e. values that will pass validation).
4761a69f7a1SQing Zhang */
4771a69f7a1SQing Zhang attr.bp_addr = 0;
4781a69f7a1SQing Zhang attr.bp_len = HW_BREAKPOINT_LEN_4;
4791a69f7a1SQing Zhang attr.bp_type = type;
4801a69f7a1SQing Zhang attr.disabled = 1;
4811a69f7a1SQing Zhang
4821a69f7a1SQing Zhang bp = register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL, tsk);
4831a69f7a1SQing Zhang if (IS_ERR(bp))
4841a69f7a1SQing Zhang return bp;
4851a69f7a1SQing Zhang
4861a69f7a1SQing Zhang err = ptrace_hbp_set_event(note_type, tsk, idx, bp);
4871a69f7a1SQing Zhang if (err)
4881a69f7a1SQing Zhang return ERR_PTR(err);
4891a69f7a1SQing Zhang
4901a69f7a1SQing Zhang return bp;
4911a69f7a1SQing Zhang }
4921a69f7a1SQing Zhang
ptrace_hbp_fill_attr_ctrl(unsigned int note_type,struct arch_hw_breakpoint_ctrl ctrl,struct perf_event_attr * attr)4931a69f7a1SQing Zhang static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type,
4941a69f7a1SQing Zhang struct arch_hw_breakpoint_ctrl ctrl,
4951a69f7a1SQing Zhang struct perf_event_attr *attr)
4961a69f7a1SQing Zhang {
4974b26f9acSHui Li int err, len, type;
4981a69f7a1SQing Zhang
4994b26f9acSHui Li err = arch_bp_generic_fields(ctrl, &len, &type);
5001a69f7a1SQing Zhang if (err)
5011a69f7a1SQing Zhang return err;
5021a69f7a1SQing Zhang
5031a69f7a1SQing Zhang attr->bp_len = len;
5041a69f7a1SQing Zhang attr->bp_type = type;
5051a69f7a1SQing Zhang
5061a69f7a1SQing Zhang return 0;
5071a69f7a1SQing Zhang }
5081a69f7a1SQing Zhang
ptrace_hbp_get_resource_info(unsigned int note_type,u64 * info)509ff9f3d7aSQing Zhang static int ptrace_hbp_get_resource_info(unsigned int note_type, u64 *info)
5101a69f7a1SQing Zhang {
5111a69f7a1SQing Zhang u8 num;
512ff9f3d7aSQing Zhang u64 reg = 0;
5131a69f7a1SQing Zhang
5141a69f7a1SQing Zhang switch (note_type) {
5151a69f7a1SQing Zhang case NT_LOONGARCH_HW_BREAK:
5161a69f7a1SQing Zhang num = hw_breakpoint_slots(TYPE_INST);
5171a69f7a1SQing Zhang break;
5181a69f7a1SQing Zhang case NT_LOONGARCH_HW_WATCH:
5191a69f7a1SQing Zhang num = hw_breakpoint_slots(TYPE_DATA);
5201a69f7a1SQing Zhang break;
5211a69f7a1SQing Zhang default:
5221a69f7a1SQing Zhang return -EINVAL;
5231a69f7a1SQing Zhang }
5241a69f7a1SQing Zhang
5251a69f7a1SQing Zhang *info = reg | num;
5261a69f7a1SQing Zhang
5271a69f7a1SQing Zhang return 0;
5281a69f7a1SQing Zhang }
5291a69f7a1SQing Zhang
ptrace_hbp_get_initialised_bp(unsigned int note_type,struct task_struct * tsk,unsigned long idx)5301a69f7a1SQing Zhang static struct perf_event *ptrace_hbp_get_initialised_bp(unsigned int note_type,
5311a69f7a1SQing Zhang struct task_struct *tsk,
5321a69f7a1SQing Zhang unsigned long idx)
5331a69f7a1SQing Zhang {
5341a69f7a1SQing Zhang struct perf_event *bp = ptrace_hbp_get_event(note_type, tsk, idx);
5351a69f7a1SQing Zhang
5361a69f7a1SQing Zhang if (!bp)
5371a69f7a1SQing Zhang bp = ptrace_hbp_create(note_type, tsk, idx);
5381a69f7a1SQing Zhang
5391a69f7a1SQing Zhang return bp;
5401a69f7a1SQing Zhang }
5411a69f7a1SQing Zhang
ptrace_hbp_get_ctrl(unsigned int note_type,struct task_struct * tsk,unsigned long idx,u32 * ctrl)5421a69f7a1SQing Zhang static int ptrace_hbp_get_ctrl(unsigned int note_type,
5431a69f7a1SQing Zhang struct task_struct *tsk,
5441a69f7a1SQing Zhang unsigned long idx, u32 *ctrl)
5451a69f7a1SQing Zhang {
5461a69f7a1SQing Zhang struct perf_event *bp = ptrace_hbp_get_event(note_type, tsk, idx);
5471a69f7a1SQing Zhang
5481a69f7a1SQing Zhang if (IS_ERR(bp))
5491a69f7a1SQing Zhang return PTR_ERR(bp);
5501a69f7a1SQing Zhang
5511a69f7a1SQing Zhang *ctrl = bp ? encode_ctrl_reg(counter_arch_bp(bp)->ctrl) : 0;
5521a69f7a1SQing Zhang
5531a69f7a1SQing Zhang return 0;
5541a69f7a1SQing Zhang }
5551a69f7a1SQing Zhang
ptrace_hbp_get_mask(unsigned int note_type,struct task_struct * tsk,unsigned long idx,u64 * mask)5561a69f7a1SQing Zhang static int ptrace_hbp_get_mask(unsigned int note_type,
5571a69f7a1SQing Zhang struct task_struct *tsk,
5581a69f7a1SQing Zhang unsigned long idx, u64 *mask)
5591a69f7a1SQing Zhang {
5601a69f7a1SQing Zhang struct perf_event *bp = ptrace_hbp_get_event(note_type, tsk, idx);
5611a69f7a1SQing Zhang
5621a69f7a1SQing Zhang if (IS_ERR(bp))
5631a69f7a1SQing Zhang return PTR_ERR(bp);
5641a69f7a1SQing Zhang
5651a69f7a1SQing Zhang *mask = bp ? counter_arch_bp(bp)->mask : 0;
5661a69f7a1SQing Zhang
5671a69f7a1SQing Zhang return 0;
5681a69f7a1SQing Zhang }
5691a69f7a1SQing Zhang
ptrace_hbp_get_addr(unsigned int note_type,struct task_struct * tsk,unsigned long idx,u64 * addr)5701a69f7a1SQing Zhang static int ptrace_hbp_get_addr(unsigned int note_type,
5711a69f7a1SQing Zhang struct task_struct *tsk,
5721a69f7a1SQing Zhang unsigned long idx, u64 *addr)
5731a69f7a1SQing Zhang {
5741a69f7a1SQing Zhang struct perf_event *bp = ptrace_hbp_get_event(note_type, tsk, idx);
5751a69f7a1SQing Zhang
5761a69f7a1SQing Zhang if (IS_ERR(bp))
5771a69f7a1SQing Zhang return PTR_ERR(bp);
5781a69f7a1SQing Zhang
5791a69f7a1SQing Zhang *addr = bp ? counter_arch_bp(bp)->address : 0;
5801a69f7a1SQing Zhang
5811a69f7a1SQing Zhang return 0;
5821a69f7a1SQing Zhang }
5831a69f7a1SQing Zhang
ptrace_hbp_set_ctrl(unsigned int note_type,struct task_struct * tsk,unsigned long idx,u32 uctrl)5841a69f7a1SQing Zhang static int ptrace_hbp_set_ctrl(unsigned int note_type,
5851a69f7a1SQing Zhang struct task_struct *tsk,
5861a69f7a1SQing Zhang unsigned long idx, u32 uctrl)
5871a69f7a1SQing Zhang {
5881a69f7a1SQing Zhang int err;
5891a69f7a1SQing Zhang struct perf_event *bp;
5901a69f7a1SQing Zhang struct perf_event_attr attr;
5911a69f7a1SQing Zhang struct arch_hw_breakpoint_ctrl ctrl;
592cc336806STiezhu Yang struct thread_info *ti = task_thread_info(tsk);
5931a69f7a1SQing Zhang
5941a69f7a1SQing Zhang bp = ptrace_hbp_get_initialised_bp(note_type, tsk, idx);
5951a69f7a1SQing Zhang if (IS_ERR(bp))
5961a69f7a1SQing Zhang return PTR_ERR(bp);
5971a69f7a1SQing Zhang
5981a69f7a1SQing Zhang attr = bp->attr;
5994b26f9acSHui Li
6004b26f9acSHui Li switch (note_type) {
6014b26f9acSHui Li case NT_LOONGARCH_HW_BREAK:
6024b26f9acSHui Li ctrl.type = LOONGARCH_BREAKPOINT_EXECUTE;
6034b26f9acSHui Li ctrl.len = LOONGARCH_BREAKPOINT_LEN_4;
6044b26f9acSHui Li break;
6054b26f9acSHui Li case NT_LOONGARCH_HW_WATCH:
6061a69f7a1SQing Zhang decode_ctrl_reg(uctrl, &ctrl);
6074b26f9acSHui Li break;
6084b26f9acSHui Li default:
6094b26f9acSHui Li return -EINVAL;
6104b26f9acSHui Li }
6114b26f9acSHui Li
61275ecfab9SHui Li if (uctrl & CTRL_PLV_ENABLE) {
6131a69f7a1SQing Zhang err = ptrace_hbp_fill_attr_ctrl(note_type, ctrl, &attr);
6141a69f7a1SQing Zhang if (err)
6151a69f7a1SQing Zhang return err;
61675ecfab9SHui Li attr.disabled = 0;
617cc336806STiezhu Yang set_ti_thread_flag(ti, TIF_LOAD_WATCH);
61875ecfab9SHui Li } else {
61975ecfab9SHui Li attr.disabled = 1;
620cc336806STiezhu Yang clear_ti_thread_flag(ti, TIF_LOAD_WATCH);
62175ecfab9SHui Li }
6221a69f7a1SQing Zhang
6231a69f7a1SQing Zhang return modify_user_hw_breakpoint(bp, &attr);
6241a69f7a1SQing Zhang }
6251a69f7a1SQing Zhang
ptrace_hbp_set_mask(unsigned int note_type,struct task_struct * tsk,unsigned long idx,u64 mask)6261a69f7a1SQing Zhang static int ptrace_hbp_set_mask(unsigned int note_type,
6271a69f7a1SQing Zhang struct task_struct *tsk,
6281a69f7a1SQing Zhang unsigned long idx, u64 mask)
6291a69f7a1SQing Zhang {
6301a69f7a1SQing Zhang struct perf_event *bp;
6311a69f7a1SQing Zhang struct perf_event_attr attr;
6321a69f7a1SQing Zhang struct arch_hw_breakpoint *info;
6331a69f7a1SQing Zhang
6341a69f7a1SQing Zhang bp = ptrace_hbp_get_initialised_bp(note_type, tsk, idx);
6351a69f7a1SQing Zhang if (IS_ERR(bp))
6361a69f7a1SQing Zhang return PTR_ERR(bp);
6371a69f7a1SQing Zhang
6381a69f7a1SQing Zhang attr = bp->attr;
6391a69f7a1SQing Zhang info = counter_arch_bp(bp);
6401a69f7a1SQing Zhang info->mask = mask;
6411a69f7a1SQing Zhang
6421a69f7a1SQing Zhang return modify_user_hw_breakpoint(bp, &attr);
6431a69f7a1SQing Zhang }
6441a69f7a1SQing Zhang
ptrace_hbp_set_addr(unsigned int note_type,struct task_struct * tsk,unsigned long idx,u64 addr)6451a69f7a1SQing Zhang static int ptrace_hbp_set_addr(unsigned int note_type,
6461a69f7a1SQing Zhang struct task_struct *tsk,
6471a69f7a1SQing Zhang unsigned long idx, u64 addr)
6481a69f7a1SQing Zhang {
6491a69f7a1SQing Zhang struct perf_event *bp;
6501a69f7a1SQing Zhang struct perf_event_attr attr;
6511a69f7a1SQing Zhang
65275ecfab9SHui Li /* Kernel-space address cannot be monitored by user-space */
65375ecfab9SHui Li if ((unsigned long)addr >= XKPRANGE)
65475ecfab9SHui Li return -EINVAL;
65575ecfab9SHui Li
6561a69f7a1SQing Zhang bp = ptrace_hbp_get_initialised_bp(note_type, tsk, idx);
6571a69f7a1SQing Zhang if (IS_ERR(bp))
6581a69f7a1SQing Zhang return PTR_ERR(bp);
6591a69f7a1SQing Zhang
6601a69f7a1SQing Zhang attr = bp->attr;
6611a69f7a1SQing Zhang attr.bp_addr = addr;
6621a69f7a1SQing Zhang
6631a69f7a1SQing Zhang return modify_user_hw_breakpoint(bp, &attr);
6641a69f7a1SQing Zhang }
6651a69f7a1SQing Zhang
6661a69f7a1SQing Zhang #define PTRACE_HBP_ADDR_SZ sizeof(u64)
6671a69f7a1SQing Zhang #define PTRACE_HBP_MASK_SZ sizeof(u64)
668ff9f3d7aSQing Zhang #define PTRACE_HBP_CTRL_SZ sizeof(u32)
669ff9f3d7aSQing Zhang #define PTRACE_HBP_PAD_SZ sizeof(u32)
6701a69f7a1SQing Zhang
hw_break_get(struct task_struct * target,const struct user_regset * regset,struct membuf to)6711a69f7a1SQing Zhang static int hw_break_get(struct task_struct *target,
6721a69f7a1SQing Zhang const struct user_regset *regset,
6731a69f7a1SQing Zhang struct membuf to)
6741a69f7a1SQing Zhang {
675ff9f3d7aSQing Zhang u64 info;
6761a69f7a1SQing Zhang u32 ctrl;
6771a69f7a1SQing Zhang u64 addr, mask;
6781a69f7a1SQing Zhang int ret, idx = 0;
6791a69f7a1SQing Zhang unsigned int note_type = regset->core_note_type;
6801a69f7a1SQing Zhang
6811a69f7a1SQing Zhang /* Resource info */
6821a69f7a1SQing Zhang ret = ptrace_hbp_get_resource_info(note_type, &info);
6831a69f7a1SQing Zhang if (ret)
6841a69f7a1SQing Zhang return ret;
6851a69f7a1SQing Zhang
6861a69f7a1SQing Zhang membuf_write(&to, &info, sizeof(info));
6871a69f7a1SQing Zhang
688e32b3b82SQing Zhang /* (address, mask, ctrl) registers */
6891a69f7a1SQing Zhang while (to.left) {
6901a69f7a1SQing Zhang ret = ptrace_hbp_get_addr(note_type, target, idx, &addr);
6911a69f7a1SQing Zhang if (ret)
6921a69f7a1SQing Zhang return ret;
6931a69f7a1SQing Zhang
6941a69f7a1SQing Zhang ret = ptrace_hbp_get_mask(note_type, target, idx, &mask);
6951a69f7a1SQing Zhang if (ret)
6961a69f7a1SQing Zhang return ret;
6971a69f7a1SQing Zhang
6981a69f7a1SQing Zhang ret = ptrace_hbp_get_ctrl(note_type, target, idx, &ctrl);
6991a69f7a1SQing Zhang if (ret)
7001a69f7a1SQing Zhang return ret;
7011a69f7a1SQing Zhang
7021a69f7a1SQing Zhang membuf_store(&to, addr);
7031a69f7a1SQing Zhang membuf_store(&to, mask);
7041a69f7a1SQing Zhang membuf_store(&to, ctrl);
705ff9f3d7aSQing Zhang membuf_zero(&to, sizeof(u32));
7061a69f7a1SQing Zhang idx++;
7071a69f7a1SQing Zhang }
7081a69f7a1SQing Zhang
7091a69f7a1SQing Zhang return 0;
7101a69f7a1SQing Zhang }
7111a69f7a1SQing Zhang
hw_break_set(struct task_struct * target,const struct user_regset * regset,unsigned int pos,unsigned int count,const void * kbuf,const void __user * ubuf)7121a69f7a1SQing Zhang static int hw_break_set(struct task_struct *target,
7131a69f7a1SQing Zhang const struct user_regset *regset,
7141a69f7a1SQing Zhang unsigned int pos, unsigned int count,
7151a69f7a1SQing Zhang const void *kbuf, const void __user *ubuf)
7161a69f7a1SQing Zhang {
7171a69f7a1SQing Zhang u32 ctrl;
7181a69f7a1SQing Zhang u64 addr, mask;
7191a69f7a1SQing Zhang int ret, idx = 0, offset, limit;
7201a69f7a1SQing Zhang unsigned int note_type = regset->core_note_type;
7211a69f7a1SQing Zhang
7221a69f7a1SQing Zhang /* Resource info */
723*cfa6d942STiezhu Yang offset = offsetof(struct user_watch_state_v2, dbg_regs);
7241a69f7a1SQing Zhang user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 0, offset);
7251a69f7a1SQing Zhang
726e32b3b82SQing Zhang /* (address, mask, ctrl) registers */
7271a69f7a1SQing Zhang limit = regset->n * regset->size;
7281a69f7a1SQing Zhang while (count && offset < limit) {
7291a69f7a1SQing Zhang if (count < PTRACE_HBP_ADDR_SZ)
7301a69f7a1SQing Zhang return -EINVAL;
7311a69f7a1SQing Zhang
7321a69f7a1SQing Zhang ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &addr,
7331a69f7a1SQing Zhang offset, offset + PTRACE_HBP_ADDR_SZ);
7341a69f7a1SQing Zhang if (ret)
7351a69f7a1SQing Zhang return ret;
7361a69f7a1SQing Zhang
7371a69f7a1SQing Zhang ret = ptrace_hbp_set_addr(note_type, target, idx, addr);
7381a69f7a1SQing Zhang if (ret)
7391a69f7a1SQing Zhang return ret;
7401a69f7a1SQing Zhang offset += PTRACE_HBP_ADDR_SZ;
7411a69f7a1SQing Zhang
7421a69f7a1SQing Zhang if (!count)
7431a69f7a1SQing Zhang break;
7441a69f7a1SQing Zhang
7451a69f7a1SQing Zhang ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &mask,
746e32b3b82SQing Zhang offset, offset + PTRACE_HBP_MASK_SZ);
7471a69f7a1SQing Zhang if (ret)
7481a69f7a1SQing Zhang return ret;
7491a69f7a1SQing Zhang
7501a69f7a1SQing Zhang ret = ptrace_hbp_set_mask(note_type, target, idx, mask);
7511a69f7a1SQing Zhang if (ret)
7521a69f7a1SQing Zhang return ret;
7531a69f7a1SQing Zhang offset += PTRACE_HBP_MASK_SZ;
7541a69f7a1SQing Zhang
755e32b3b82SQing Zhang ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl,
756e32b3b82SQing Zhang offset, offset + PTRACE_HBP_CTRL_SZ);
7571a69f7a1SQing Zhang if (ret)
7581a69f7a1SQing Zhang return ret;
7591a69f7a1SQing Zhang
7601a69f7a1SQing Zhang ret = ptrace_hbp_set_ctrl(note_type, target, idx, ctrl);
7611a69f7a1SQing Zhang if (ret)
7621a69f7a1SQing Zhang return ret;
7631a69f7a1SQing Zhang offset += PTRACE_HBP_CTRL_SZ;
764ff9f3d7aSQing Zhang
765ff9f3d7aSQing Zhang user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
766ff9f3d7aSQing Zhang offset, offset + PTRACE_HBP_PAD_SZ);
767ff9f3d7aSQing Zhang offset += PTRACE_HBP_PAD_SZ;
768ff9f3d7aSQing Zhang
7691a69f7a1SQing Zhang idx++;
7701a69f7a1SQing Zhang }
7711a69f7a1SQing Zhang
7721a69f7a1SQing Zhang return 0;
7731a69f7a1SQing Zhang }
7741a69f7a1SQing Zhang
7751a69f7a1SQing Zhang #endif
7761a69f7a1SQing Zhang
777803b0fc5SHuacai Chen struct pt_regs_offset {
778803b0fc5SHuacai Chen const char *name;
779803b0fc5SHuacai Chen int offset;
780803b0fc5SHuacai Chen };
781803b0fc5SHuacai Chen
782803b0fc5SHuacai Chen #define REG_OFFSET_NAME(n, r) {.name = #n, .offset = offsetof(struct pt_regs, r)}
783803b0fc5SHuacai Chen #define REG_OFFSET_END {.name = NULL, .offset = 0}
784803b0fc5SHuacai Chen
785803b0fc5SHuacai Chen static const struct pt_regs_offset regoffset_table[] = {
786803b0fc5SHuacai Chen REG_OFFSET_NAME(r0, regs[0]),
787803b0fc5SHuacai Chen REG_OFFSET_NAME(r1, regs[1]),
788803b0fc5SHuacai Chen REG_OFFSET_NAME(r2, regs[2]),
789803b0fc5SHuacai Chen REG_OFFSET_NAME(r3, regs[3]),
790803b0fc5SHuacai Chen REG_OFFSET_NAME(r4, regs[4]),
791803b0fc5SHuacai Chen REG_OFFSET_NAME(r5, regs[5]),
792803b0fc5SHuacai Chen REG_OFFSET_NAME(r6, regs[6]),
793803b0fc5SHuacai Chen REG_OFFSET_NAME(r7, regs[7]),
794803b0fc5SHuacai Chen REG_OFFSET_NAME(r8, regs[8]),
795803b0fc5SHuacai Chen REG_OFFSET_NAME(r9, regs[9]),
796803b0fc5SHuacai Chen REG_OFFSET_NAME(r10, regs[10]),
797803b0fc5SHuacai Chen REG_OFFSET_NAME(r11, regs[11]),
798803b0fc5SHuacai Chen REG_OFFSET_NAME(r12, regs[12]),
799803b0fc5SHuacai Chen REG_OFFSET_NAME(r13, regs[13]),
800803b0fc5SHuacai Chen REG_OFFSET_NAME(r14, regs[14]),
801803b0fc5SHuacai Chen REG_OFFSET_NAME(r15, regs[15]),
802803b0fc5SHuacai Chen REG_OFFSET_NAME(r16, regs[16]),
803803b0fc5SHuacai Chen REG_OFFSET_NAME(r17, regs[17]),
804803b0fc5SHuacai Chen REG_OFFSET_NAME(r18, regs[18]),
805803b0fc5SHuacai Chen REG_OFFSET_NAME(r19, regs[19]),
806803b0fc5SHuacai Chen REG_OFFSET_NAME(r20, regs[20]),
807803b0fc5SHuacai Chen REG_OFFSET_NAME(r21, regs[21]),
808803b0fc5SHuacai Chen REG_OFFSET_NAME(r22, regs[22]),
809803b0fc5SHuacai Chen REG_OFFSET_NAME(r23, regs[23]),
810803b0fc5SHuacai Chen REG_OFFSET_NAME(r24, regs[24]),
811803b0fc5SHuacai Chen REG_OFFSET_NAME(r25, regs[25]),
812803b0fc5SHuacai Chen REG_OFFSET_NAME(r26, regs[26]),
813803b0fc5SHuacai Chen REG_OFFSET_NAME(r27, regs[27]),
814803b0fc5SHuacai Chen REG_OFFSET_NAME(r28, regs[28]),
815803b0fc5SHuacai Chen REG_OFFSET_NAME(r29, regs[29]),
816803b0fc5SHuacai Chen REG_OFFSET_NAME(r30, regs[30]),
817803b0fc5SHuacai Chen REG_OFFSET_NAME(r31, regs[31]),
818803b0fc5SHuacai Chen REG_OFFSET_NAME(orig_a0, orig_a0),
819803b0fc5SHuacai Chen REG_OFFSET_NAME(csr_era, csr_era),
820803b0fc5SHuacai Chen REG_OFFSET_NAME(csr_badvaddr, csr_badvaddr),
821803b0fc5SHuacai Chen REG_OFFSET_NAME(csr_crmd, csr_crmd),
822803b0fc5SHuacai Chen REG_OFFSET_NAME(csr_prmd, csr_prmd),
823803b0fc5SHuacai Chen REG_OFFSET_NAME(csr_euen, csr_euen),
824803b0fc5SHuacai Chen REG_OFFSET_NAME(csr_ecfg, csr_ecfg),
825803b0fc5SHuacai Chen REG_OFFSET_NAME(csr_estat, csr_estat),
826803b0fc5SHuacai Chen REG_OFFSET_END,
827803b0fc5SHuacai Chen };
828803b0fc5SHuacai Chen
829803b0fc5SHuacai Chen /**
830803b0fc5SHuacai Chen * regs_query_register_offset() - query register offset from its name
831803b0fc5SHuacai Chen * @name: the name of a register
832803b0fc5SHuacai Chen *
833803b0fc5SHuacai Chen * regs_query_register_offset() returns the offset of a register in struct
834803b0fc5SHuacai Chen * pt_regs from its name. If the name is invalid, this returns -EINVAL;
835803b0fc5SHuacai Chen */
regs_query_register_offset(const char * name)836803b0fc5SHuacai Chen int regs_query_register_offset(const char *name)
837803b0fc5SHuacai Chen {
838803b0fc5SHuacai Chen const struct pt_regs_offset *roff;
839803b0fc5SHuacai Chen
840803b0fc5SHuacai Chen for (roff = regoffset_table; roff->name != NULL; roff++)
841803b0fc5SHuacai Chen if (!strcmp(roff->name, name))
842803b0fc5SHuacai Chen return roff->offset;
843803b0fc5SHuacai Chen return -EINVAL;
844803b0fc5SHuacai Chen }
845803b0fc5SHuacai Chen
846803b0fc5SHuacai Chen enum loongarch_regset {
847803b0fc5SHuacai Chen REGSET_GPR,
848803b0fc5SHuacai Chen REGSET_FPR,
849803b0fc5SHuacai Chen REGSET_CPUCFG,
85061650023SHuacai Chen #ifdef CONFIG_CPU_HAS_LSX
85161650023SHuacai Chen REGSET_LSX,
85261650023SHuacai Chen #endif
85361650023SHuacai Chen #ifdef CONFIG_CPU_HAS_LASX
85461650023SHuacai Chen REGSET_LASX,
85561650023SHuacai Chen #endif
856bd3c5798SQi Hu #ifdef CONFIG_CPU_HAS_LBT
857bd3c5798SQi Hu REGSET_LBT,
858bd3c5798SQi Hu #endif
8591a69f7a1SQing Zhang #ifdef CONFIG_HAVE_HW_BREAKPOINT
8601a69f7a1SQing Zhang REGSET_HW_BREAK,
8611a69f7a1SQing Zhang REGSET_HW_WATCH,
8621a69f7a1SQing Zhang #endif
863803b0fc5SHuacai Chen };
864803b0fc5SHuacai Chen
865803b0fc5SHuacai Chen static const struct user_regset loongarch64_regsets[] = {
866803b0fc5SHuacai Chen [REGSET_GPR] = {
867803b0fc5SHuacai Chen .core_note_type = NT_PRSTATUS,
868803b0fc5SHuacai Chen .n = ELF_NGREG,
869803b0fc5SHuacai Chen .size = sizeof(elf_greg_t),
870803b0fc5SHuacai Chen .align = sizeof(elf_greg_t),
871803b0fc5SHuacai Chen .regset_get = gpr_get,
872803b0fc5SHuacai Chen .set = gpr_set,
873803b0fc5SHuacai Chen },
874803b0fc5SHuacai Chen [REGSET_FPR] = {
875803b0fc5SHuacai Chen .core_note_type = NT_PRFPREG,
876803b0fc5SHuacai Chen .n = ELF_NFPREG,
877803b0fc5SHuacai Chen .size = sizeof(elf_fpreg_t),
878803b0fc5SHuacai Chen .align = sizeof(elf_fpreg_t),
879803b0fc5SHuacai Chen .regset_get = fpr_get,
880803b0fc5SHuacai Chen .set = fpr_set,
881803b0fc5SHuacai Chen },
882803b0fc5SHuacai Chen [REGSET_CPUCFG] = {
883803b0fc5SHuacai Chen .core_note_type = NT_LOONGARCH_CPUCFG,
884803b0fc5SHuacai Chen .n = 64,
885803b0fc5SHuacai Chen .size = sizeof(u32),
886803b0fc5SHuacai Chen .align = sizeof(u32),
887803b0fc5SHuacai Chen .regset_get = cfg_get,
888803b0fc5SHuacai Chen .set = cfg_set,
889803b0fc5SHuacai Chen },
89061650023SHuacai Chen #ifdef CONFIG_CPU_HAS_LSX
89161650023SHuacai Chen [REGSET_LSX] = {
89261650023SHuacai Chen .core_note_type = NT_LOONGARCH_LSX,
89361650023SHuacai Chen .n = NUM_FPU_REGS,
89461650023SHuacai Chen .size = 16,
89561650023SHuacai Chen .align = 16,
89661650023SHuacai Chen .regset_get = simd_get,
89761650023SHuacai Chen .set = simd_set,
89861650023SHuacai Chen },
89961650023SHuacai Chen #endif
90061650023SHuacai Chen #ifdef CONFIG_CPU_HAS_LASX
90161650023SHuacai Chen [REGSET_LASX] = {
90261650023SHuacai Chen .core_note_type = NT_LOONGARCH_LASX,
90361650023SHuacai Chen .n = NUM_FPU_REGS,
90461650023SHuacai Chen .size = 32,
90561650023SHuacai Chen .align = 32,
90661650023SHuacai Chen .regset_get = simd_get,
90761650023SHuacai Chen .set = simd_set,
90861650023SHuacai Chen },
90961650023SHuacai Chen #endif
910bd3c5798SQi Hu #ifdef CONFIG_CPU_HAS_LBT
911bd3c5798SQi Hu [REGSET_LBT] = {
912bd3c5798SQi Hu .core_note_type = NT_LOONGARCH_LBT,
913bd3c5798SQi Hu .n = 5,
914bd3c5798SQi Hu .size = sizeof(u64),
915bd3c5798SQi Hu .align = sizeof(u64),
916bd3c5798SQi Hu .regset_get = lbt_get,
917bd3c5798SQi Hu .set = lbt_set,
918bd3c5798SQi Hu },
919bd3c5798SQi Hu #endif
9201a69f7a1SQing Zhang #ifdef CONFIG_HAVE_HW_BREAKPOINT
9211a69f7a1SQing Zhang [REGSET_HW_BREAK] = {
9221a69f7a1SQing Zhang .core_note_type = NT_LOONGARCH_HW_BREAK,
923*cfa6d942STiezhu Yang .n = sizeof(struct user_watch_state_v2) / sizeof(u32),
9241a69f7a1SQing Zhang .size = sizeof(u32),
9251a69f7a1SQing Zhang .align = sizeof(u32),
9261a69f7a1SQing Zhang .regset_get = hw_break_get,
9271a69f7a1SQing Zhang .set = hw_break_set,
9281a69f7a1SQing Zhang },
9291a69f7a1SQing Zhang [REGSET_HW_WATCH] = {
9301a69f7a1SQing Zhang .core_note_type = NT_LOONGARCH_HW_WATCH,
931*cfa6d942STiezhu Yang .n = sizeof(struct user_watch_state_v2) / sizeof(u32),
9321a69f7a1SQing Zhang .size = sizeof(u32),
9331a69f7a1SQing Zhang .align = sizeof(u32),
9341a69f7a1SQing Zhang .regset_get = hw_break_get,
9351a69f7a1SQing Zhang .set = hw_break_set,
9361a69f7a1SQing Zhang },
9371a69f7a1SQing Zhang #endif
938803b0fc5SHuacai Chen };
939803b0fc5SHuacai Chen
940803b0fc5SHuacai Chen static const struct user_regset_view user_loongarch64_view = {
941803b0fc5SHuacai Chen .name = "loongarch64",
942803b0fc5SHuacai Chen .e_machine = ELF_ARCH,
943803b0fc5SHuacai Chen .regsets = loongarch64_regsets,
944803b0fc5SHuacai Chen .n = ARRAY_SIZE(loongarch64_regsets),
945803b0fc5SHuacai Chen };
946803b0fc5SHuacai Chen
947803b0fc5SHuacai Chen
task_user_regset_view(struct task_struct * task)948803b0fc5SHuacai Chen const struct user_regset_view *task_user_regset_view(struct task_struct *task)
949803b0fc5SHuacai Chen {
950803b0fc5SHuacai Chen return &user_loongarch64_view;
951803b0fc5SHuacai Chen }
952803b0fc5SHuacai Chen
read_user(struct task_struct * target,unsigned long addr,unsigned long __user * data)953803b0fc5SHuacai Chen static inline int read_user(struct task_struct *target, unsigned long addr,
954803b0fc5SHuacai Chen unsigned long __user *data)
955803b0fc5SHuacai Chen {
956803b0fc5SHuacai Chen unsigned long tmp = 0;
957803b0fc5SHuacai Chen
958803b0fc5SHuacai Chen switch (addr) {
959803b0fc5SHuacai Chen case 0 ... 31:
960803b0fc5SHuacai Chen tmp = task_pt_regs(target)->regs[addr];
961803b0fc5SHuacai Chen break;
962803b0fc5SHuacai Chen case ARG0:
963803b0fc5SHuacai Chen tmp = task_pt_regs(target)->orig_a0;
964803b0fc5SHuacai Chen break;
965803b0fc5SHuacai Chen case PC:
966803b0fc5SHuacai Chen tmp = task_pt_regs(target)->csr_era;
967803b0fc5SHuacai Chen break;
968803b0fc5SHuacai Chen case BADVADDR:
969803b0fc5SHuacai Chen tmp = task_pt_regs(target)->csr_badvaddr;
970803b0fc5SHuacai Chen break;
971803b0fc5SHuacai Chen default:
972803b0fc5SHuacai Chen return -EIO;
973803b0fc5SHuacai Chen }
974803b0fc5SHuacai Chen
975803b0fc5SHuacai Chen return put_user(tmp, data);
976803b0fc5SHuacai Chen }
977803b0fc5SHuacai Chen
write_user(struct task_struct * target,unsigned long addr,unsigned long data)978803b0fc5SHuacai Chen static inline int write_user(struct task_struct *target, unsigned long addr,
979803b0fc5SHuacai Chen unsigned long data)
980803b0fc5SHuacai Chen {
981803b0fc5SHuacai Chen switch (addr) {
982803b0fc5SHuacai Chen case 0 ... 31:
983803b0fc5SHuacai Chen task_pt_regs(target)->regs[addr] = data;
984803b0fc5SHuacai Chen break;
985803b0fc5SHuacai Chen case ARG0:
986803b0fc5SHuacai Chen task_pt_regs(target)->orig_a0 = data;
987803b0fc5SHuacai Chen break;
988803b0fc5SHuacai Chen case PC:
989803b0fc5SHuacai Chen task_pt_regs(target)->csr_era = data;
990803b0fc5SHuacai Chen break;
991803b0fc5SHuacai Chen case BADVADDR:
992803b0fc5SHuacai Chen task_pt_regs(target)->csr_badvaddr = data;
993803b0fc5SHuacai Chen break;
994803b0fc5SHuacai Chen default:
995803b0fc5SHuacai Chen return -EIO;
996803b0fc5SHuacai Chen }
997803b0fc5SHuacai Chen
998803b0fc5SHuacai Chen return 0;
999803b0fc5SHuacai Chen }
1000803b0fc5SHuacai Chen
arch_ptrace(struct task_struct * child,long request,unsigned long addr,unsigned long data)1001803b0fc5SHuacai Chen long arch_ptrace(struct task_struct *child, long request,
1002803b0fc5SHuacai Chen unsigned long addr, unsigned long data)
1003803b0fc5SHuacai Chen {
1004803b0fc5SHuacai Chen int ret;
1005803b0fc5SHuacai Chen unsigned long __user *datap = (void __user *) data;
1006803b0fc5SHuacai Chen
1007803b0fc5SHuacai Chen switch (request) {
1008803b0fc5SHuacai Chen case PTRACE_PEEKUSR:
1009803b0fc5SHuacai Chen ret = read_user(child, addr, datap);
1010803b0fc5SHuacai Chen break;
1011803b0fc5SHuacai Chen
1012803b0fc5SHuacai Chen case PTRACE_POKEUSR:
1013803b0fc5SHuacai Chen ret = write_user(child, addr, data);
1014803b0fc5SHuacai Chen break;
1015803b0fc5SHuacai Chen
1016803b0fc5SHuacai Chen default:
1017803b0fc5SHuacai Chen ret = ptrace_request(child, request, addr, data);
1018803b0fc5SHuacai Chen break;
1019803b0fc5SHuacai Chen }
1020803b0fc5SHuacai Chen
1021803b0fc5SHuacai Chen return ret;
1022803b0fc5SHuacai Chen }
1023424421a7SQing Zhang
1024424421a7SQing Zhang #ifdef CONFIG_HAVE_HW_BREAKPOINT
ptrace_triggered(struct perf_event * bp,struct perf_sample_data * data,struct pt_regs * regs)1025424421a7SQing Zhang static void ptrace_triggered(struct perf_event *bp,
1026424421a7SQing Zhang struct perf_sample_data *data, struct pt_regs *regs)
1027424421a7SQing Zhang {
1028424421a7SQing Zhang struct perf_event_attr attr;
1029424421a7SQing Zhang
1030424421a7SQing Zhang attr = bp->attr;
1031424421a7SQing Zhang attr.disabled = true;
1032424421a7SQing Zhang modify_user_hw_breakpoint(bp, &attr);
1033424421a7SQing Zhang }
1034424421a7SQing Zhang
set_single_step(struct task_struct * tsk,unsigned long addr)1035424421a7SQing Zhang static int set_single_step(struct task_struct *tsk, unsigned long addr)
1036424421a7SQing Zhang {
1037424421a7SQing Zhang struct perf_event *bp;
1038424421a7SQing Zhang struct perf_event_attr attr;
1039424421a7SQing Zhang struct arch_hw_breakpoint *info;
1040424421a7SQing Zhang struct thread_struct *thread = &tsk->thread;
1041424421a7SQing Zhang
1042424421a7SQing Zhang bp = thread->hbp_break[0];
1043424421a7SQing Zhang if (!bp) {
1044424421a7SQing Zhang ptrace_breakpoint_init(&attr);
1045424421a7SQing Zhang
1046424421a7SQing Zhang attr.bp_addr = addr;
1047424421a7SQing Zhang attr.bp_len = HW_BREAKPOINT_LEN_8;
1048424421a7SQing Zhang attr.bp_type = HW_BREAKPOINT_X;
1049424421a7SQing Zhang
1050424421a7SQing Zhang bp = register_user_hw_breakpoint(&attr, ptrace_triggered,
1051424421a7SQing Zhang NULL, tsk);
1052424421a7SQing Zhang if (IS_ERR(bp))
1053424421a7SQing Zhang return PTR_ERR(bp);
1054424421a7SQing Zhang
1055424421a7SQing Zhang thread->hbp_break[0] = bp;
1056424421a7SQing Zhang } else {
1057424421a7SQing Zhang int err;
1058424421a7SQing Zhang
1059424421a7SQing Zhang attr = bp->attr;
1060424421a7SQing Zhang attr.bp_addr = addr;
1061424421a7SQing Zhang
1062424421a7SQing Zhang /* Reenable breakpoint */
1063424421a7SQing Zhang attr.disabled = false;
1064424421a7SQing Zhang err = modify_user_hw_breakpoint(bp, &attr);
1065424421a7SQing Zhang if (unlikely(err))
1066424421a7SQing Zhang return err;
1067424421a7SQing Zhang
1068424421a7SQing Zhang csr_write64(attr.bp_addr, LOONGARCH_CSR_IB0ADDR);
1069424421a7SQing Zhang }
1070424421a7SQing Zhang info = counter_arch_bp(bp);
1071424421a7SQing Zhang info->mask = TASK_SIZE - 1;
1072424421a7SQing Zhang
1073424421a7SQing Zhang return 0;
1074424421a7SQing Zhang }
1075424421a7SQing Zhang
1076424421a7SQing Zhang /* ptrace API */
user_enable_single_step(struct task_struct * task)1077424421a7SQing Zhang void user_enable_single_step(struct task_struct *task)
1078424421a7SQing Zhang {
1079424421a7SQing Zhang struct thread_info *ti = task_thread_info(task);
1080424421a7SQing Zhang
1081424421a7SQing Zhang set_single_step(task, task_pt_regs(task)->csr_era);
1082424421a7SQing Zhang task->thread.single_step = task_pt_regs(task)->csr_era;
1083424421a7SQing Zhang set_ti_thread_flag(ti, TIF_SINGLESTEP);
1084424421a7SQing Zhang }
1085424421a7SQing Zhang
user_disable_single_step(struct task_struct * task)1086424421a7SQing Zhang void user_disable_single_step(struct task_struct *task)
1087424421a7SQing Zhang {
1088424421a7SQing Zhang clear_tsk_thread_flag(task, TIF_SINGLESTEP);
1089424421a7SQing Zhang }
1090424421a7SQing Zhang #endif
1091