1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2011 Konstantin Belousov <kib@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/elf.h> 36 #include <sys/lock.h> 37 #include <sys/malloc.h> 38 #include <sys/mutex.h> 39 #include <sys/proc.h> 40 #include <sys/ptrace.h> 41 #include <sys/reg.h> 42 #include <sys/sysent.h> 43 #include <vm/vm.h> 44 #include <vm/pmap.h> 45 #include <machine/md_var.h> 46 #include <machine/pcb.h> 47 #include <machine/frame.h> 48 #include <machine/vmparam.h> 49 50 #ifdef COMPAT_FREEBSD32 51 struct ptrace_xstate_info32 { 52 uint32_t xsave_mask1, xsave_mask2; 53 uint32_t xsave_len; 54 }; 55 #endif 56 57 static bool 58 get_segbases(struct regset *rs, struct thread *td, void *buf, 59 size_t *sizep) 60 { 61 struct segbasereg *reg; 62 struct pcb *pcb; 63 64 if (buf != NULL) { 65 KASSERT(*sizep == sizeof(*reg), ("%s: invalid size", __func__)); 66 reg = buf; 67 68 pcb = td->td_pcb; 69 if (td == curthread) 70 update_pcb_bases(pcb); 71 reg->r_fsbase = pcb->pcb_fsbase; 72 reg->r_gsbase = pcb->pcb_gsbase; 73 } 74 *sizep = sizeof(*reg); 75 return (true); 76 } 77 78 static bool 79 set_segbases(struct regset *rs, struct thread *td, void *buf, 80 size_t size) 81 { 82 struct segbasereg *reg; 83 struct pcb *pcb; 84 85 KASSERT(size == sizeof(*reg), ("%s: invalid size", __func__)); 86 reg = buf; 87 88 pcb = td->td_pcb; 89 set_pcb_flags(pcb, PCB_FULL_IRET); 90 pcb->pcb_fsbase = reg->r_fsbase; 91 td->td_frame->tf_fs = _ufssel; 92 pcb->pcb_gsbase = reg->r_gsbase; 93 td->td_frame->tf_gs = _ugssel; 94 95 return (true); 96 } 97 98 static struct regset regset_segbases = { 99 .note = NT_X86_SEGBASES, 100 .size = sizeof(struct segbasereg), 101 .get = get_segbases, 102 .set = set_segbases, 103 }; 104 ELF_REGSET(regset_segbases); 105 106 #ifdef COMPAT_FREEBSD32 107 static bool 108 get_segbases32(struct regset *rs, struct thread *td, void *buf, 109 size_t *sizep) 110 { 111 struct segbasereg32 *reg; 112 struct pcb *pcb; 113 114 if (buf != NULL) { 115 KASSERT(*sizep == sizeof(*reg), ("%s: invalid size", __func__)); 116 reg = buf; 117 118 pcb = td->td_pcb; 119 if (td == curthread) 120 update_pcb_bases(pcb); 121 reg->r_fsbase = (uint32_t)pcb->pcb_fsbase; 122 reg->r_gsbase = (uint32_t)pcb->pcb_gsbase; 123 } 124 *sizep = sizeof(*reg); 125 return (true); 126 } 127 128 static bool 129 set_segbases32(struct regset *rs, struct thread *td, void *buf, 130 size_t size) 131 { 132 struct segbasereg32 *reg; 133 struct pcb *pcb; 134 135 KASSERT(size == sizeof(*reg), ("%s: invalid size", __func__)); 136 reg = buf; 137 138 pcb = td->td_pcb; 139 set_pcb_flags(pcb, PCB_FULL_IRET); 140 pcb->pcb_fsbase = reg->r_fsbase; 141 td->td_frame->tf_fs = _ufssel; 142 pcb->pcb_gsbase = reg->r_gsbase; 143 td->td_frame->tf_gs = _ugssel; 144 145 return (true); 146 } 147 148 static struct regset regset_segbases32 = { 149 .note = NT_X86_SEGBASES, 150 .size = sizeof(struct segbasereg32), 151 .get = get_segbases32, 152 .set = set_segbases32, 153 }; 154 ELF32_REGSET(regset_segbases32); 155 #endif 156 157 static int 158 cpu_ptrace_xstate(struct thread *td, int req, void *addr, int data) 159 { 160 struct ptrace_xstate_info info; 161 #ifdef COMPAT_FREEBSD32 162 struct ptrace_xstate_info32 info32; 163 #endif 164 char *savefpu; 165 int error; 166 167 if (!use_xsave) 168 return (EOPNOTSUPP); 169 170 switch (req) { 171 case PT_GETXSTATE_OLD: 172 fpugetregs(td); 173 savefpu = (char *)(get_pcb_user_save_td(td) + 1); 174 error = copyout(savefpu, addr, 175 cpu_max_ext_state_size - sizeof(struct savefpu)); 176 break; 177 178 case PT_SETXSTATE_OLD: 179 if (data > cpu_max_ext_state_size - sizeof(struct savefpu)) { 180 error = EINVAL; 181 break; 182 } 183 savefpu = malloc(data, M_TEMP, M_WAITOK); 184 error = copyin(addr, savefpu, data); 185 if (error == 0) { 186 fpugetregs(td); 187 error = fpusetxstate(td, savefpu, data); 188 } 189 free(savefpu, M_TEMP); 190 break; 191 192 case PT_GETXSTATE_INFO: 193 #ifdef COMPAT_FREEBSD32 194 if (SV_CURPROC_FLAG(SV_ILP32)) { 195 if (data != sizeof(info32)) { 196 error = EINVAL; 197 } else { 198 info32.xsave_len = cpu_max_ext_state_size; 199 info32.xsave_mask1 = xsave_mask; 200 info32.xsave_mask2 = xsave_mask >> 32; 201 error = copyout(&info32, addr, data); 202 } 203 } else 204 #endif 205 { 206 if (data != sizeof(info)) { 207 error = EINVAL; 208 } else { 209 bzero(&info, sizeof(info)); 210 info.xsave_len = cpu_max_ext_state_size; 211 info.xsave_mask = xsave_mask; 212 error = copyout(&info, addr, data); 213 } 214 } 215 break; 216 217 case PT_GETXSTATE: 218 fpugetregs(td); 219 savefpu = (char *)(get_pcb_user_save_td(td)); 220 error = copyout(savefpu, addr, cpu_max_ext_state_size); 221 break; 222 223 case PT_SETXSTATE: 224 if (data < sizeof(struct savefpu) || 225 data > cpu_max_ext_state_size) { 226 error = EINVAL; 227 break; 228 } 229 savefpu = malloc(data, M_TEMP, M_WAITOK); 230 error = copyin(addr, savefpu, data); 231 if (error == 0) 232 error = fpusetregs(td, (struct savefpu *)savefpu, 233 savefpu + sizeof(struct savefpu), data - 234 sizeof(struct savefpu)); 235 free(savefpu, M_TEMP); 236 break; 237 238 default: 239 error = EINVAL; 240 break; 241 } 242 243 return (error); 244 } 245 246 static void 247 cpu_ptrace_setbase(struct thread *td, int req, register_t r) 248 { 249 struct pcb *pcb; 250 251 pcb = td->td_pcb; 252 set_pcb_flags(pcb, PCB_FULL_IRET); 253 if (req == PT_SETFSBASE) { 254 pcb->pcb_fsbase = r; 255 td->td_frame->tf_fs = _ufssel; 256 } else { 257 pcb->pcb_gsbase = r; 258 td->td_frame->tf_gs = _ugssel; 259 } 260 } 261 262 #ifdef COMPAT_FREEBSD32 263 #define PT_I386_GETXMMREGS (PT_FIRSTMACH + 0) 264 #define PT_I386_SETXMMREGS (PT_FIRSTMACH + 1) 265 266 static int 267 cpu32_ptrace(struct thread *td, int req, void *addr, int data) 268 { 269 struct savefpu *fpstate; 270 struct pcb *pcb; 271 uint32_t r; 272 int error; 273 274 switch (req) { 275 case PT_I386_GETXMMREGS: 276 fpugetregs(td); 277 error = copyout(get_pcb_user_save_td(td), addr, 278 sizeof(*fpstate)); 279 break; 280 281 case PT_I386_SETXMMREGS: 282 fpugetregs(td); 283 fpstate = get_pcb_user_save_td(td); 284 error = copyin(addr, fpstate, sizeof(*fpstate)); 285 fpstate->sv_env.en_mxcsr &= cpu_mxcsr_mask; 286 break; 287 288 case PT_GETXSTATE_OLD: 289 case PT_SETXSTATE_OLD: 290 case PT_GETXSTATE_INFO: 291 case PT_GETXSTATE: 292 case PT_SETXSTATE: 293 error = cpu_ptrace_xstate(td, req, addr, data); 294 break; 295 296 case PT_GETFSBASE: 297 case PT_GETGSBASE: 298 if (!SV_PROC_FLAG(td->td_proc, SV_ILP32)) { 299 error = EINVAL; 300 break; 301 } 302 pcb = td->td_pcb; 303 if (td == curthread) 304 update_pcb_bases(pcb); 305 r = req == PT_GETFSBASE ? pcb->pcb_fsbase : pcb->pcb_gsbase; 306 error = copyout(&r, addr, sizeof(r)); 307 break; 308 309 case PT_SETFSBASE: 310 case PT_SETGSBASE: 311 if (!SV_PROC_FLAG(td->td_proc, SV_ILP32)) { 312 error = EINVAL; 313 break; 314 } 315 error = copyin(addr, &r, sizeof(r)); 316 if (error != 0) 317 break; 318 cpu_ptrace_setbase(td, req, r); 319 break; 320 321 default: 322 error = EINVAL; 323 break; 324 } 325 326 return (error); 327 } 328 #endif 329 330 int 331 cpu_ptrace(struct thread *td, int req, void *addr, int data) 332 { 333 register_t *r, rv; 334 struct pcb *pcb; 335 int error; 336 337 #ifdef COMPAT_FREEBSD32 338 if (SV_CURPROC_FLAG(SV_ILP32)) 339 return (cpu32_ptrace(td, req, addr, data)); 340 #endif 341 342 /* Support old values of PT_GETXSTATE_OLD and PT_SETXSTATE_OLD. */ 343 if (req == PT_FIRSTMACH + 0) 344 req = PT_GETXSTATE_OLD; 345 if (req == PT_FIRSTMACH + 1) 346 req = PT_SETXSTATE_OLD; 347 348 switch (req) { 349 case PT_GETXSTATE_OLD: 350 case PT_SETXSTATE_OLD: 351 case PT_GETXSTATE_INFO: 352 case PT_GETXSTATE: 353 case PT_SETXSTATE: 354 error = cpu_ptrace_xstate(td, req, addr, data); 355 break; 356 357 case PT_GETFSBASE: 358 case PT_GETGSBASE: 359 pcb = td->td_pcb; 360 if (td == curthread) 361 update_pcb_bases(pcb); 362 r = req == PT_GETFSBASE ? &pcb->pcb_fsbase : &pcb->pcb_gsbase; 363 error = copyout(r, addr, sizeof(*r)); 364 break; 365 366 case PT_SETFSBASE: 367 case PT_SETGSBASE: 368 error = copyin(addr, &rv, sizeof(rv)); 369 if (error != 0) 370 break; 371 if (rv >= td->td_proc->p_sysent->sv_maxuser) { 372 error = EINVAL; 373 break; 374 } 375 cpu_ptrace_setbase(td, req, rv); 376 break; 377 378 default: 379 error = EINVAL; 380 break; 381 } 382 383 return (error); 384 } 385 386 int 387 ptrace_set_pc(struct thread *td, unsigned long addr) 388 { 389 390 td->td_frame->tf_rip = addr; 391 set_pcb_flags(td->td_pcb, PCB_FULL_IRET); 392 return (0); 393 } 394 395 int 396 ptrace_single_step(struct thread *td) 397 { 398 399 PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); 400 if ((td->td_frame->tf_rflags & PSL_T) == 0) { 401 td->td_frame->tf_rflags |= PSL_T; 402 td->td_dbgflags |= TDB_STEP; 403 } 404 return (0); 405 } 406 407 int 408 ptrace_clear_single_step(struct thread *td) 409 { 410 411 PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); 412 td->td_frame->tf_rflags &= ~PSL_T; 413 td->td_dbgflags &= ~TDB_STEP; 414 return (0); 415 } 416