Home | History | Annotate | Line # | Download | only in powerpc
      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
     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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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