xref: /netbsd-src/sys/arch/powerpc/powerpc/process_machdep.c (revision f2f01d3ff21b72b8b4abaa1c723fc154b152102c)
1 /*	$NetBSD: process_machdep.c,v 1.43 2022/12/05 16:03:50 martin Exp $	*/
2 
3 /*
4  * Copyright (C) 1995, 1996 Wolfgang Solfrank.
5  * Copyright (C) 1995, 1996 TooLs GmbH.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by TooLs GmbH.
19  * 4. The name of TooLs GmbH may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: process_machdep.c,v 1.43 2022/12/05 16:03:50 martin Exp $");
36 
37 #ifdef _KERNEL_OPT
38 #include "opt_altivec.h"
39 #include "opt_ppcarch.h"
40 #endif
41 
42 #include <sys/param.h>
43 #include <sys/cpu.h>
44 #include <sys/proc.h>
45 #include <sys/ptrace.h>
46 #include <sys/systm.h>
47 
48 #include <uvm/uvm_extern.h>
49 
50 #include <powerpc/fpu.h>
51 #include <powerpc/pcb.h>
52 #include <powerpc/psl.h>
53 #include <powerpc/reg.h>
54 
55 #include <powerpc/altivec.h>	/* also for e500 SPE */
56 
57 int
process_read_regs(struct lwp * l,struct reg * regs)58 process_read_regs(struct lwp *l, struct reg *regs)
59 {
60 	struct trapframe * const tf = l->l_md.md_utf;
61 
62 	memcpy(regs->fixreg, tf->tf_fixreg, sizeof(regs->fixreg));
63 	regs->lr = tf->tf_lr;
64 	regs->cr = tf->tf_cr;
65 	regs->xer = tf->tf_xer;
66 	regs->ctr = tf->tf_ctr;
67 	regs->pc = tf->tf_srr0;
68 
69 	return 0;
70 }
71 
72 int
process_write_regs(struct lwp * l,const struct reg * regs)73 process_write_regs(struct lwp *l, const struct reg *regs)
74 {
75 	struct trapframe * const tf = l->l_md.md_utf;
76 
77 	memcpy(tf->tf_fixreg, regs->fixreg, sizeof(regs->fixreg));
78 	tf->tf_lr = regs->lr;
79 	tf->tf_cr = regs->cr;
80 	tf->tf_xer = regs->xer;
81 	tf->tf_ctr = regs->ctr;
82 	tf->tf_srr0 = regs->pc;
83 
84 	return 0;
85 }
86 
87 int
process_read_fpregs(struct lwp * l,struct fpreg * fpregs,size_t * sz)88 process_read_fpregs(struct lwp *l, struct fpreg *fpregs, size_t *sz)
89 {
90 	struct pcb * const pcb = lwp_getpcb(l);
91 
92 	/* Is the process using the fpu? */
93 	if (!fpu_used_p(l)) {
94 		memset(fpregs, 0, sizeof (*fpregs));
95 #ifdef PPC_HAVE_FPU
96 	} else {
97 		fpu_save(l);
98 #endif
99 	}
100 	*fpregs = pcb->pcb_fpu;
101 	fpu_mark_used(l);
102 
103 	return 0;
104 }
105 
106 int
process_write_fpregs(struct lwp * l,const struct fpreg * fpregs,size_t sz)107 process_write_fpregs(struct lwp *l, const struct fpreg *fpregs, size_t sz)
108 {
109 	struct pcb * const pcb = lwp_getpcb(l);
110 
111 #ifdef PPC_HAVE_FPU
112 	fpu_discard(l);
113 #endif
114 	pcb->pcb_fpu = *fpregs;
115 	fpu_mark_used(l);		/* pcb_fpu is initialized now. */
116 
117 	return 0;
118 }
119 
120 /*
121  * Set the process's program counter.
122  */
123 int
process_set_pc(struct lwp * l,void * addr)124 process_set_pc(struct lwp *l, void *addr)
125 {
126 	struct trapframe * const tf = l->l_md.md_utf;
127 
128 	tf->tf_srr0 = (register_t)addr;
129 
130 	return 0;
131 }
132 
133 int
process_sstep(struct lwp * l,int sstep)134 process_sstep(struct lwp *l, int sstep)
135 {
136 #if !defined(PPC_BOOKE) && !defined(PPC_IBM4XX)
137 	struct trapframe * const tf = l->l_md.md_utf;
138 
139 	if (sstep) {
140 		tf->tf_srr1 |= PSL_SE;
141 	} else {
142 		tf->tf_srr1 &= ~PSL_SE;
143 	}
144 	return 0;
145 #else
146 /*
147  * We use the software single-stepping for booke/ibm4xx.
148  */
149 	return ppc_sstep(l, sstep);
150 #endif
151 }
152 
153 
154 #ifdef __HAVE_PTRACE_MACHDEP
155 static int
process_machdep_read_vecregs(struct lwp * l,struct vreg * vregs)156 process_machdep_read_vecregs(struct lwp *l, struct vreg *vregs)
157 {
158 	struct pcb * const pcb = lwp_getpcb(l);
159 
160 #ifdef ALTIVEC
161 	if (cpu_altivec == 0)
162 		return EINVAL;
163 #endif
164 
165 	/* Is the process using AltiVEC? */
166 	if (!vec_used_p(l)) {
167 		memset(vregs, 0, sizeof (*vregs));
168 	} else {
169 		vec_save(l);
170 		*vregs = pcb->pcb_vr;
171 	}
172 	vec_mark_used(l);
173 
174 	return 0;
175 }
176 
177 static int
process_machdep_write_vecregs(struct lwp * l,struct vreg * vregs)178 process_machdep_write_vecregs(struct lwp *l, struct vreg *vregs)
179 {
180 	struct pcb * const pcb = lwp_getpcb(l);
181 
182 #ifdef ALTIVEC
183 	if (cpu_altivec == 0)
184 		return (EINVAL);
185 #endif
186 
187 #if defined(ALTIVEC) || defined(PPC_HAVE_SPE)
188 	vec_discard(l);
189 #endif
190 	pcb->pcb_vr = *vregs;		/* pcb_vr is initialized now. */
191 	vec_mark_used(l);
192 
193 	return (0);
194 }
195 
196 int
ptrace_machdep_dorequest(struct lwp * l,struct lwp ** lt,int req,void * addr,int data)197 ptrace_machdep_dorequest(struct lwp *l, struct lwp **lt,
198 	int req, void *addr, int data)
199 {
200 	struct uio uio;
201 	struct iovec iov;
202 	int write = 0, error;
203 
204 	switch (req) {
205 	case PT_SETVECREGS:
206 		write = 1;
207 
208 	case PT_GETVECREGS:
209 		/* write = 0 done above. */
210 		if ((error = ptrace_update_lwp((*lt)->l_proc, lt, data)) != 0)
211 			return error;
212 		if (!process_machdep_validvecregs((*lt)->l_proc))
213 			return (EINVAL);
214 		iov.iov_base = addr;
215 		iov.iov_len = sizeof(struct vreg);
216 		uio.uio_iov = &iov;
217 		uio.uio_iovcnt = 1;
218 		uio.uio_offset = 0;
219 		uio.uio_resid = sizeof(struct vreg);
220 		uio.uio_rw = write ? UIO_WRITE : UIO_READ;
221 		uio.uio_vmspace = l->l_proc->p_vmspace;
222 		return process_machdep_dovecregs(l, *lt, &uio);
223 	}
224 
225 #ifdef DIAGNOSTIC
226 	panic("ptrace_machdep: impossible");
227 #endif
228 
229 	return (0);
230 }
231 
232 /*
233  * The following functions are used by both ptrace(2) and procfs.
234  */
235 
236 int
process_machdep_dovecregs(struct lwp * curl,struct lwp * l,struct uio * uio)237 process_machdep_dovecregs(struct lwp *curl, struct lwp *l, struct uio *uio)
238 {
239 	struct vreg r;
240 	int error;
241 	char *kv;
242 	int kl;
243 
244 	kl = sizeof(r);
245 	kv = (char *) &r;
246 
247 	kv += uio->uio_offset;
248 	kl -= uio->uio_offset;
249 	if (kl > uio->uio_resid)
250 		kl = uio->uio_resid;
251 
252 	if (kl < 0)
253 		error = EINVAL;
254 	else
255 		error = process_machdep_read_vecregs(l, &r);
256 	if (error == 0)
257 		error = uiomove(kv, kl, uio);
258 	if (error == 0 && uio->uio_rw == UIO_WRITE) {
259 		if (l->l_proc->p_stat != SSTOP)
260 			error = EBUSY;
261 		else
262 			error = process_machdep_write_vecregs(l, &r);
263 	}
264 
265 	uio->uio_offset = 0;
266 	return (error);
267 }
268 
269 int
process_machdep_validvecregs(struct proc * p)270 process_machdep_validvecregs(struct proc *p)
271 {
272 	if (p->p_flag & PK_SYSTEM)
273 		return (0);
274 
275 #ifdef ALTIVEC
276 	return (cpu_altivec);
277 #endif
278 #ifdef PPC_HAVE_SPE
279 	return 1;
280 #endif
281 }
282 #endif /* __HAVE_PTRACE_MACHDEP */
283 
284 #if defined(PPC_BOOKE) || defined(PPC_IBM4XX)
285 /*
286  * ppc_ifetch and ppc_istore:
287  * fetch/store instructions from/to given process (therefore, we cannot use
288  * ufetch/ustore(9) here).
289  */
290 
291 static int
ppc_ifetch(struct lwp * l,vaddr_t va,uint32_t * insn)292 ppc_ifetch(struct lwp *l, vaddr_t va, uint32_t *insn)
293 {
294 	struct uio uio;
295 	struct iovec iov;
296 
297 	iov.iov_base = insn;
298 	iov.iov_len = sizeof(*insn);
299 	uio.uio_iov = &iov;
300 	uio.uio_iovcnt = 1;
301 	uio.uio_offset = (off_t)va;
302 	uio.uio_resid = sizeof(*insn);
303 	uio.uio_rw = UIO_READ;
304 	UIO_SETUP_SYSSPACE(&uio);
305 
306 	return process_domem(curlwp, l, &uio);
307 }
308 
309 static int
ppc_istore(struct lwp * l,vaddr_t va,uint32_t insn)310 ppc_istore(struct lwp *l, vaddr_t va, uint32_t insn)
311 {
312 	struct uio uio;
313 	struct iovec iov;
314 
315 	iov.iov_base = &insn;
316 	iov.iov_len = sizeof(insn);
317 	uio.uio_iov = &iov;
318 	uio.uio_iovcnt = 1;
319 	uio.uio_offset = (off_t)va;
320 	uio.uio_resid = sizeof(insn);
321 	uio.uio_rw = UIO_WRITE;
322 	UIO_SETUP_SYSSPACE(&uio);
323 
324 	return process_domem(curlwp, l, &uio);
325 }
326 
327 /*
328  * Insert or remove single-step breakpoints:
329  * We need two breakpoints, in general, at (SRR0 + 4) and the address to
330  * which the process can branch into.
331  */
332 
333 int
ppc_sstep(struct lwp * l,int step)334 ppc_sstep(struct lwp *l, int step)
335 {
336 	struct trapframe * const tf = l->l_md.md_utf;
337 	struct proc * const p = l->l_proc;
338 	const uint32_t trap = 0x7d821008;	/* twge %r2, %r2 */
339 	uint32_t insn;
340 	vaddr_t va[2];
341 	int i, rv;
342 
343 	if (step) {
344 		if (p->p_md.md_ss_addr[0] != 0)
345 			return 0; /* XXX Should we reset breakpoints? */
346 
347 		va[0] = (vaddr_t)tf->tf_srr0;
348 		va[1] = 0;
349 
350 		/*
351 		 * Find the address to which the process can branch into.
352 		 */
353 		if ((rv = ppc_ifetch(l, va[0], &insn)) != 0)
354 			return rv;
355 		if ((insn >> 28) == 4) {
356 			if ((insn >> 26) == 0x12) {
357 				const int32_t off =
358 				    ((int32_t)(insn << 6) >> 6) & ~3;
359 				va[1] = ((insn & 2) ? 0 : va[0]) + off;
360 			} else if ((insn >> 26) == 0x10) {
361 				const int16_t off = (int16_t)insn & ~3;
362 				va[1] = ((insn & 2) ? 0 : va[0]) + off;
363 			} else if ((insn & 0xfc00fffe) == 0x4c000420)
364 				va[1] = tf->tf_ctr;
365 			  else if ((insn & 0xfc00fffe) == 0x4c000020)
366 				va[1] = tf->tf_lr;
367 		}
368 		va[0] += sizeof(insn);
369 		if (va[1] == va[0])
370 			va[1] = 0;
371 
372 		for (i = 0; i < 2; i++) {
373 			if (va[i] == 0)
374 				return 0;
375 			if ((rv = ppc_ifetch(l, va[i], &insn)) != 0)
376 				goto error;
377 			p->p_md.md_ss_insn[i] = insn;
378 			if ((rv = ppc_istore(l, va[i], trap)) != 0) {
379 error:				/* Recover as far as possible. */
380 				if (i == 1 && ppc_istore(l, va[0],
381 				    p->p_md.md_ss_insn[0]) == 0)
382 					p->p_md.md_ss_addr[0] = 0;
383 				return rv;
384 			}
385 			p->p_md.md_ss_addr[i] = va[i];
386 		}
387 	} else {
388 		for (i = 0; i < 2; i++) {
389 			va[i] = p->p_md.md_ss_addr[i];
390 			if (va[i] == 0)
391 				return 0;
392 			if ((rv = ppc_ifetch(l, va[i], &insn)) != 0)
393 				return rv;
394 			if (insn != trap) {
395 				panic("%s: ss_insn[%d] = 0x%x != trap",
396 				    __func__, i, insn);
397 			}
398 			if ((rv = ppc_istore(l, va[i], p->p_md.md_ss_insn[i]))
399 			    != 0)
400 				return rv;
401 			p->p_md.md_ss_addr[i] = 0;
402 		}
403 	}
404 	return 0;
405 }
406 #endif /* PPC_BOOKE || PPC_IBM4XX */
407