Home | History | Annotate | Line # | Download | only in i386
      1 /*	$NetBSD: process_machdep.c,v 1.98 2026/08/20 16:20:54 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1998, 2000, 2001, 2008 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Charles M. Hannum; by Jason R. Thorpe of Wasabi Systems, Inc.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * This file may seem a bit stylized, but that so that it's easier to port.
     34  * Functions to be implemented here are:
     35  *
     36  * process_read_regs(proc, regs)
     37  *	Get the current user-visible register set from the process
     38  *	and copy it into the regs structure (<machine/reg.h>).
     39  *	The process is stopped at the time read_regs is called.
     40  *
     41  * process_write_regs(proc, regs)
     42  *	Update the current register set from the passed in regs
     43  *	structure.  Take care to avoid clobbering special CPU
     44  *	registers or privileged bits in the PSL.
     45  *	The process is stopped at the time write_regs is called.
     46  *
     47  * process_read_fpregs(proc, regs, sz)
     48  *	Get the current user-visible register set from the process
     49  *	and copy it into the regs structure (<machine/reg.h>).
     50  *	The process is stopped at the time read_fpregs is called.
     51  *
     52  * process_write_fpregs(proc, regs, sz)
     53  *	Update the current register set from the passed in regs
     54  *	structure.  Take care to avoid clobbering special CPU
     55  *	registers or privileged bits in the PSL.
     56  *	The process is stopped at the time write_fpregs is called.
     57  *
     58  * process_alloc_dbregs(proc)
     59  *	Ensure a dbregs buffer is allocated.
     60  *
     61  * process_read_dbregs(proc, regs)
     62  *	Get the current user-visible register set from the process
     63  *	and copy it into the regs structure (<machine/reg.h>).
     64  *	The process is stopped at the time read_dbregs is called.
     65  *
     66  * process_write_dbregs(proc, regs)
     67  *	Update the current register set from the passed in regs
     68  *	structure.  Take care to avoid clobbering special CPU
     69  *	registers or privileged bits in the PSL.
     70  *	The process is stopped at the time write_dbregs is called.
     71  *
     72  * process_sstep(proc)
     73  *	Arrange for the process to trap after executing a single instruction.
     74  *
     75  * process_set_pc(proc)
     76  *	Set the process's program counter.
     77  *
     78  */
     79 
     80 #include <sys/cdefs.h>
     81 __KERNEL_RCSID(0, "$NetBSD: process_machdep.c,v 1.98 2026/08/20 16:20:54 riastradh Exp $");
     82 
     83 #ifdef _KERNEL_OPT
     84 #include "opt_ptrace.h"
     85 #endif
     86 
     87 #include <sys/param.h>
     88 #include <sys/systm.h>
     89 #include <sys/time.h>
     90 #include <sys/kernel.h>
     91 #include <sys/proc.h>
     92 #include <sys/vnode.h>
     93 #include <sys/ptrace.h>
     94 
     95 #include <uvm/uvm_extern.h>
     96 
     97 #include <machine/psl.h>
     98 #include <machine/reg.h>
     99 #include <machine/segments.h>
    100 
    101 #include <x86/dbregs.h>
    102 #include <x86/fpu.h>
    103 
    104 static inline struct trapframe *
    105 process_frame(struct lwp *l)
    106 {
    107 
    108 	return (l->l_md.md_regs);
    109 }
    110 
    111 int
    112 process_read_regs(struct lwp *l, struct reg *regs)
    113 {
    114 	struct trapframe *tf = process_frame(l);
    115 
    116 	regs->r_gs = tf->tf_gs & 0xffff;
    117 	regs->r_fs = tf->tf_fs & 0xffff;
    118 	regs->r_es = tf->tf_es & 0xffff;
    119 	regs->r_ds = tf->tf_ds & 0xffff;
    120 	regs->r_eflags = tf->tf_eflags;
    121 
    122 	regs->r_edi = tf->tf_edi;
    123 	regs->r_esi = tf->tf_esi;
    124 	regs->r_ebp = tf->tf_ebp;
    125 	regs->r_ebx = tf->tf_ebx;
    126 	regs->r_edx = tf->tf_edx;
    127 	regs->r_ecx = tf->tf_ecx;
    128 	regs->r_eax = tf->tf_eax;
    129 	regs->r_eip = tf->tf_eip;
    130 	regs->r_cs = tf->tf_cs & 0xffff;
    131 	regs->r_esp = tf->tf_esp;
    132 	regs->r_ss = tf->tf_ss & 0xffff;
    133 
    134 	return (0);
    135 }
    136 
    137 int
    138 process_read_fpregs(struct lwp *l, struct fpreg *regs, size_t *sz)
    139 {
    140 
    141 	__CTASSERT(sizeof *regs == sizeof (struct save87));
    142 	process_read_fpregs_s87(l, (struct save87 *)regs);
    143 	return 0;
    144 }
    145 
    146 int
    147 process_read_dbregs(struct lwp *l, struct dbreg *regs, size_t *sz)
    148 {
    149 
    150 	x86_dbregs_read(l, regs);
    151 
    152 	return 0;
    153 }
    154 
    155 void
    156 process_alloc_dbregs(struct lwp *l)
    157 {
    158 
    159 	KASSERT(rw_lock_held(&l->l_proc->p_reflock));
    160 	x86_dbregs_alloc(l);
    161 }
    162 
    163 #ifdef PTRACE_HOOKS
    164 int
    165 process_write_regs(struct lwp *l, const struct reg *regs)
    166 {
    167 	struct trapframe *tf = process_frame(l);
    168 
    169 	/*
    170 	 * Check for security violations.
    171 	 */
    172 	if (((regs->r_eflags ^ tf->tf_eflags) & PSL_USERSTATIC) != 0 ||
    173 	    !USERMODE(regs->r_cs))
    174 		return (EINVAL);
    175 
    176 	tf->tf_gs = regs->r_gs;
    177 	tf->tf_fs = regs->r_fs;
    178 	tf->tf_es = regs->r_es;
    179 	tf->tf_ds = regs->r_ds;
    180 	tf->tf_eflags = regs->r_eflags;
    181 
    182 	tf->tf_edi = regs->r_edi;
    183 	tf->tf_esi = regs->r_esi;
    184 	tf->tf_ebp = regs->r_ebp;
    185 	tf->tf_ebx = regs->r_ebx;
    186 	tf->tf_edx = regs->r_edx;
    187 	tf->tf_ecx = regs->r_ecx;
    188 	tf->tf_eax = regs->r_eax;
    189 	tf->tf_eip = regs->r_eip;
    190 	tf->tf_cs = regs->r_cs;
    191 	tf->tf_esp = regs->r_esp;
    192 	tf->tf_ss = regs->r_ss;
    193 
    194 	return (0);
    195 }
    196 
    197 int
    198 process_write_fpregs(struct lwp *l, const struct fpreg *regs, size_t sz)
    199 {
    200 
    201 	__CTASSERT(sizeof *regs == sizeof (struct save87));
    202 	process_write_fpregs_s87(l, (const struct save87 *)regs);
    203 	return 0;
    204 }
    205 
    206 int
    207 process_write_dbregs(struct lwp *l, const struct dbreg *regs, size_t sz)
    208 {
    209 	int error;
    210 
    211 	/*
    212 	 * Check for security violations.
    213 	 */
    214 	error = x86_dbregs_validate(regs);
    215 	if (error != 0)
    216 		return error;
    217 
    218 	x86_dbregs_write(l, regs);
    219 
    220 	return 0;
    221 }
    222 
    223 int
    224 process_sstep(struct lwp *l, int sstep)
    225 {
    226 	struct trapframe *tf = process_frame(l);
    227 
    228 	if (sstep)
    229 		tf->tf_eflags |= PSL_T;
    230 	else
    231 		tf->tf_eflags &= ~PSL_T;
    232 
    233 	return (0);
    234 }
    235 
    236 int
    237 process_set_pc(struct lwp *l, void *addr)
    238 {
    239 	struct trapframe *tf = process_frame(l);
    240 
    241 	tf->tf_eip = (int)addr;
    242 
    243 	return (0);
    244 }
    245 
    246 #ifdef __HAVE_PTRACE_MACHDEP
    247 static int
    248 process_machdep_read_xstate(struct lwp *l, struct xstate *regs)
    249 {
    250 	return process_read_xstate(l, regs);
    251 }
    252 
    253 static int
    254 process_machdep_read_xmmregs(struct lwp *l, struct xmmregs *regs)
    255 {
    256 
    257 	__CTASSERT(sizeof *regs == sizeof (struct fxsave));
    258 	process_read_fpregs_xmm(l, (struct fxsave *)regs);
    259 	return 0;
    260 }
    261 
    262 static int
    263 process_machdep_write_xstate(struct lwp *l, const struct xstate *regs)
    264 {
    265 	int error;
    266 
    267 	/*
    268 	 * Check for security violations.
    269 	 */
    270 	error = process_verify_xstate(regs);
    271 	if (error != 0)
    272 		return error;
    273 
    274 	return process_write_xstate(l, regs);
    275 }
    276 
    277 static int
    278 process_machdep_write_xmmregs(struct lwp *l, struct xmmregs *regs)
    279 {
    280 
    281 	__CTASSERT(sizeof *regs == sizeof (struct fxsave));
    282 	process_write_fpregs_xmm(l, (const struct fxsave *)regs);
    283 	return 0;
    284 }
    285 
    286 int
    287 ptrace_machdep_dorequest(
    288     struct lwp *l,
    289     struct lwp **lt,
    290     int req,
    291     void *addr,
    292     int data
    293 )
    294 {
    295 	struct uio uio;
    296 	struct iovec iov;
    297 	struct iovec user_iov;
    298 	struct vmspace *vm;
    299 	int error;
    300 	int write = 0;
    301 
    302 	switch (req) {
    303 	case PT_SETXMMREGS:
    304 		write = 1;
    305 
    306 		/* FALLTHROUGH */
    307 	case PT_GETXMMREGS:
    308 		/* write = 0 done above. */
    309 		if ((error = ptrace_update_lwp((*lt)->l_proc, lt, data)) != 0)
    310 			return error;
    311 		if (!process_machdep_validxmmregs((*lt)->l_proc))
    312 			return (EINVAL);
    313 		error = proc_vmspace_getref(l->l_proc, &vm);
    314 		if (error) {
    315 			return error;
    316 		}
    317 		iov.iov_base = addr;
    318 		iov.iov_len = sizeof(struct xmmregs);
    319 		uio.uio_iov = &iov;
    320 		uio.uio_iovcnt = 1;
    321 		uio.uio_offset = 0;
    322 		uio.uio_resid = sizeof(struct xmmregs);
    323 		uio.uio_rw = write ? UIO_WRITE : UIO_READ;
    324 		uio.uio_vmspace = vm;
    325 		error = process_machdep_doxmmregs(l, *lt, &uio);
    326 		uvmspace_free(vm);
    327 		return error;
    328 
    329 	case PT_SETXSTATE:
    330 		write = 1;
    331 
    332 		/* FALLTHROUGH */
    333 	case PT_GETXSTATE:
    334 		/* write = 0 done above. */
    335 		if ((error = ptrace_update_lwp((*lt)->l_proc, lt, data)) != 0)
    336 			return error;
    337 		if (!process_machdep_validxstate((*lt)->l_proc))
    338 			return EINVAL;
    339 		if ((error = copyin(addr, &user_iov, sizeof(user_iov))) != 0)
    340 			return error;
    341 		error = proc_vmspace_getref(l->l_proc, &vm);
    342 		if (error) {
    343 			return error;
    344 		}
    345 		iov.iov_base = user_iov.iov_base;
    346 		iov.iov_len = user_iov.iov_len;
    347 		if (iov.iov_len > sizeof(struct xstate))
    348 			iov.iov_len = sizeof(struct xstate);
    349 		uio.uio_iov = &iov;
    350 		uio.uio_iovcnt = 1;
    351 		uio.uio_offset = 0;
    352 		uio.uio_resid = iov.iov_len;
    353 		uio.uio_rw = write ? UIO_WRITE : UIO_READ;
    354 		uio.uio_vmspace = vm;
    355 		error = process_machdep_doxstate(l, *lt, &uio);
    356 		uvmspace_free(vm);
    357 		return error;
    358 	}
    359 
    360 #ifdef DIAGNOSTIC
    361 	panic("ptrace_machdep: impossible");
    362 #endif
    363 
    364 	return 0;
    365 }
    366 
    367 /*
    368  * The following functions are used by both ptrace(2) and procfs.
    369  */
    370 
    371 int
    372 process_machdep_doxmmregs(struct lwp *curl, struct lwp *l, struct uio *uio)
    373 	/* curl:		 tracer */
    374 	/* l:			 traced */
    375 {
    376 	int error;
    377 	struct xmmregs r;
    378 	char *kv;
    379 	int kl;
    380 
    381 	kl = sizeof(r);
    382 	kv = (char *) &r;
    383 
    384 	kv += uio->uio_offset;
    385 	kl -= uio->uio_offset;
    386 	if (kl > uio->uio_resid)
    387 		kl = uio->uio_resid;
    388 
    389 	if (kl < 0)
    390 		error = EINVAL;
    391 	else
    392 		error = process_machdep_read_xmmregs(l, &r);
    393 	if (error == 0)
    394 		error = uiomove(kv, kl, uio);
    395 	if (error == 0 && uio->uio_rw == UIO_WRITE) {
    396 		if (l->l_proc->p_stat != SSTOP)
    397 			error = EBUSY;
    398 		else
    399 			error = process_machdep_write_xmmregs(l, &r);
    400 	}
    401 
    402 	uio->uio_offset = 0;
    403 	return (error);
    404 }
    405 
    406 int
    407 process_machdep_validxmmregs(struct proc *p)
    408 {
    409 
    410 	if (p->p_flag & PK_SYSTEM)
    411 		return (0);
    412 
    413 	return (i386_use_fxsave);
    414 }
    415 
    416 int
    417 process_machdep_doxstate(struct lwp *curl, struct lwp *l, struct uio *uio)
    418 	/* curl:		 tracer */
    419 	/* l:			 traced */
    420 {
    421 	int error;
    422 	struct xstate r;	/* XXX FIXME big stack object */
    423 	char *kv;
    424 	ssize_t kl;
    425 
    426 	memset(&r, 0, sizeof(r));
    427 	kl = MIN(uio->uio_iov->iov_len, sizeof(r));
    428 	kv = (char *) &r;
    429 
    430 	kv += uio->uio_offset;
    431 	kl -= uio->uio_offset;
    432 	if (kl > uio->uio_resid)
    433 		kl = uio->uio_resid;
    434 
    435 	if (kl < 0)
    436 		error = EINVAL;
    437 	else
    438 		error = process_machdep_read_xstate(l, &r);
    439 	if (error == 0)
    440 		error = uiomove(kv, kl, uio);
    441 	if (error == 0 && uio->uio_rw == UIO_WRITE)
    442 		error = process_machdep_write_xstate(l, &r);
    443 
    444 	uio->uio_offset = 0;
    445 	return error;
    446 }
    447 
    448 int
    449 process_machdep_validxstate(struct proc *p)
    450 {
    451 
    452 	if (p->p_flag & PK_SYSTEM)
    453 		return 0;
    454 
    455 	return 1;
    456 }
    457 #endif /* __HAVE_PTRACE_MACHDEP */
    458 #endif /* PTRACE_HOOKS */
    459