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