1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * OpenRISC ptrace.c |
4 | * |
5 | * Linux architectural port borrowing liberally from similar works of |
6 | * others. All original copyrights apply as per the original source |
7 | * declaration. |
8 | * |
9 | * Modifications for the OpenRISC architecture: |
10 | * Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com> |
11 | * Copyright (C) 2005 Gyorgy Jeney <nog@bsemi.com> |
12 | * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se> |
13 | */ |
14 | |
15 | #include <linux/kernel.h> |
16 | #include <linux/sched.h> |
17 | #include <linux/sched/task_stack.h> |
18 | #include <linux/string.h> |
19 | |
20 | #include <linux/mm.h> |
21 | #include <linux/errno.h> |
22 | #include <linux/ptrace.h> |
23 | #include <linux/audit.h> |
24 | #include <linux/regset.h> |
25 | #include <linux/elf.h> |
26 | |
27 | #include <asm/thread_info.h> |
28 | #include <asm/page.h> |
29 | |
30 | asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); |
31 | |
32 | asmlinkage void do_syscall_trace_leave(struct pt_regs *regs); |
33 | |
34 | /* |
35 | * Copy the thread state to a regset that can be interpreted by userspace. |
36 | * |
37 | * It doesn't matter what our internal pt_regs structure looks like. The |
38 | * important thing is that we export a consistent view of the thread state |
39 | * to userspace. As such, we need to make sure that the regset remains |
40 | * ABI compatible as defined by the struct user_regs_struct: |
41 | * |
42 | * (Each item is a 32-bit word) |
43 | * r0 = 0 (exported for clarity) |
44 | * 31 GPRS r1-r31 |
45 | * PC (Program counter) |
46 | * SR (Supervision register) |
47 | */ |
48 | static int genregs_get(struct task_struct *target, |
49 | const struct user_regset *regset, |
50 | struct membuf to) |
51 | { |
52 | const struct pt_regs *regs = task_pt_regs(target); |
53 | |
54 | /* r0 */ |
55 | membuf_zero(s: &to, size: 4); |
56 | membuf_write(s: &to, v: regs->gpr + 1, size: 31 * 4); |
57 | membuf_store(&to, regs->pc); |
58 | return membuf_store(&to, regs->sr); |
59 | } |
60 | |
61 | /* |
62 | * Set the thread state from a regset passed in via ptrace |
63 | */ |
64 | static int genregs_set(struct task_struct *target, |
65 | const struct user_regset *regset, |
66 | unsigned int pos, unsigned int count, |
67 | const void *kbuf, const void __user * ubuf) |
68 | { |
69 | struct pt_regs *regs = task_pt_regs(target); |
70 | int ret; |
71 | |
72 | /* ignore r0 */ |
73 | user_regset_copyin_ignore(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, start_pos: 0, end_pos: 4); |
74 | /* r1 - r31 */ |
75 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
76 | data: regs->gpr+1, start_pos: 4, end_pos: 4*32); |
77 | /* PC */ |
78 | if (!ret) |
79 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
80 | data: ®s->pc, start_pos: 4*32, end_pos: 4*33); |
81 | /* |
82 | * Skip SR and padding... userspace isn't allowed to changes bits in |
83 | * the Supervision register |
84 | */ |
85 | if (!ret) |
86 | user_regset_copyin_ignore(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, start_pos: 4*33, end_pos: -1); |
87 | |
88 | return ret; |
89 | } |
90 | |
91 | /* |
92 | * As OpenRISC shares GPRs and floating point registers we don't need to export |
93 | * the floating point registers again. So here we only export the fpcsr special |
94 | * purpose register. |
95 | */ |
96 | static int fpregs_get(struct task_struct *target, |
97 | const struct user_regset *regset, |
98 | struct membuf to) |
99 | { |
100 | const struct pt_regs *regs = task_pt_regs(target); |
101 | |
102 | return membuf_store(&to, regs->fpcsr); |
103 | } |
104 | |
105 | static int fpregs_set(struct task_struct *target, |
106 | const struct user_regset *regset, |
107 | unsigned int pos, unsigned int count, |
108 | const void *kbuf, const void __user *ubuf) |
109 | { |
110 | struct pt_regs *regs = task_pt_regs(target); |
111 | int ret; |
112 | |
113 | /* FPCSR */ |
114 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
115 | data: ®s->fpcsr, start_pos: 0, end_pos: 4); |
116 | return ret; |
117 | } |
118 | |
119 | /* |
120 | * Define the register sets available on OpenRISC under Linux |
121 | */ |
122 | enum or1k_regset { |
123 | REGSET_GENERAL, |
124 | REGSET_FPU, |
125 | }; |
126 | |
127 | static const struct user_regset or1k_regsets[] = { |
128 | [REGSET_GENERAL] = { |
129 | .core_note_type = NT_PRSTATUS, |
130 | .n = ELF_NGREG, |
131 | .size = sizeof(long), |
132 | .align = sizeof(long), |
133 | .regset_get = genregs_get, |
134 | .set = genregs_set, |
135 | }, |
136 | [REGSET_FPU] = { |
137 | .core_note_type = NT_PRFPREG, |
138 | .n = sizeof(struct __or1k_fpu_state) / sizeof(long), |
139 | .size = sizeof(long), |
140 | .align = sizeof(long), |
141 | .regset_get = fpregs_get, |
142 | .set = fpregs_set, |
143 | }, |
144 | }; |
145 | |
146 | static const struct user_regset_view user_or1k_native_view = { |
147 | .name = "or1k" , |
148 | .e_machine = EM_OPENRISC, |
149 | .regsets = or1k_regsets, |
150 | .n = ARRAY_SIZE(or1k_regsets), |
151 | }; |
152 | |
153 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
154 | { |
155 | return &user_or1k_native_view; |
156 | } |
157 | |
158 | /* |
159 | * does not yet catch signals sent when the child dies. |
160 | * in exit.c or in signal.c. |
161 | */ |
162 | |
163 | |
164 | /* |
165 | * Called by kernel/ptrace.c when detaching.. |
166 | * |
167 | * Make sure the single step bit is not set. |
168 | */ |
169 | void ptrace_disable(struct task_struct *child) |
170 | { |
171 | pr_debug("ptrace_disable(): TODO\n" ); |
172 | |
173 | user_disable_single_step(child); |
174 | clear_tsk_thread_flag(tsk: child, flag: TIF_SYSCALL_TRACE); |
175 | } |
176 | |
177 | long arch_ptrace(struct task_struct *child, long request, unsigned long addr, |
178 | unsigned long data) |
179 | { |
180 | int ret; |
181 | |
182 | switch (request) { |
183 | default: |
184 | ret = ptrace_request(child, request, addr, data); |
185 | break; |
186 | } |
187 | |
188 | return ret; |
189 | } |
190 | |
191 | /* |
192 | * Notification of system call entry/exit |
193 | * - triggered by current->work.syscall_trace |
194 | */ |
195 | asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) |
196 | { |
197 | long ret = 0; |
198 | |
199 | if (test_thread_flag(TIF_SYSCALL_TRACE) && |
200 | ptrace_report_syscall_entry(regs)) |
201 | /* |
202 | * Tracing decided this syscall should not happen. |
203 | * We'll return a bogus call number to get an ENOSYS |
204 | * error, but leave the original number in <something>. |
205 | */ |
206 | ret = -1L; |
207 | |
208 | audit_syscall_entry(major: regs->gpr[11], a0: regs->gpr[3], a1: regs->gpr[4], |
209 | a2: regs->gpr[5], a3: regs->gpr[6]); |
210 | |
211 | return ret ? : regs->gpr[11]; |
212 | } |
213 | |
214 | asmlinkage void do_syscall_trace_leave(struct pt_regs *regs) |
215 | { |
216 | int step; |
217 | |
218 | audit_syscall_exit(pt_regs: regs); |
219 | |
220 | step = test_thread_flag(TIF_SINGLESTEP); |
221 | if (step || test_thread_flag(TIF_SYSCALL_TRACE)) |
222 | ptrace_report_syscall_exit(regs, step); |
223 | } |
224 | |