1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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/param.h> 31 #include <sys/systm.h> 32 #include <sys/elf.h> 33 #include <sys/lock.h> 34 #include <sys/malloc.h> 35 #include <sys/mutex.h> 36 #include <sys/proc.h> 37 #include <sys/ptrace.h> 38 #include <sys/reg.h> 39 #include <sys/sysent.h> 40 #include <vm/vm.h> 41 #include <vm/pmap.h> 42 #include <machine/md_var.h> 43 #include <machine/pcb.h> 44 #include <machine/frame.h> 45 #include <machine/vmparam.h> 46 47 #ifdef COMPAT_FREEBSD32 48 struct ptrace_xstate_info32 { 49 uint32_t xsave_mask1, xsave_mask2; 50 uint32_t xsave_len; 51 }; 52 #endif 53 54 static bool 55 get_segbases(struct regset *rs, struct thread *td, void *buf, 56 size_t *sizep) 57 { 58 struct segbasereg *reg; 59 struct pcb *pcb; 60 61 if (buf != NULL) { 62 KASSERT(*sizep == sizeof(*reg), ("%s: invalid size", __func__)); 63 reg = buf; 64 65 pcb = td->td_pcb; 66 if (td == curthread) 67 update_pcb_bases(pcb); 68 reg->r_fsbase = pcb->pcb_fsbase; 69 reg->r_gsbase = pcb->pcb_gsbase; 70 } 71 *sizep = sizeof(*reg); 72 return (true); 73 } 74 75 static bool 76 set_segbases(struct regset *rs, struct thread *td, void *buf, 77 size_t size) 78 { 79 struct segbasereg *reg; 80 struct pcb *pcb; 81 82 KASSERT(size == sizeof(*reg), ("%s: invalid size", __func__)); 83 reg = buf; 84 85 pcb = td->td_pcb; 86 set_pcb_flags(pcb, PCB_FULL_IRET); 87 pcb->pcb_fsbase = reg->r_fsbase; 88 td->td_frame->tf_fs = _ufssel; 89 pcb->pcb_gsbase = reg->r_gsbase; 90 td->td_frame->tf_gs = _ugssel; 91 92 return (true); 93 } 94 95 static struct regset regset_segbases = { 96 .note = NT_X86_SEGBASES, 97 .size = sizeof(struct segbasereg), 98 .get = get_segbases, 99 .set = set_segbases, 100 }; 101 ELF_REGSET(regset_segbases); 102 103 #ifdef COMPAT_FREEBSD32 104 static bool 105 get_segbases32(struct regset *rs, struct thread *td, void *buf, 106 size_t *sizep) 107 { 108 struct segbasereg32 *reg; 109 struct pcb *pcb; 110 111 if (buf != NULL) { 112 KASSERT(*sizep == sizeof(*reg), ("%s: invalid size", __func__)); 113 reg = buf; 114 115 pcb = td->td_pcb; 116 if (td == curthread) 117 update_pcb_bases(pcb); 118 reg->r_fsbase = (uint32_t)pcb->pcb_fsbase; 119 reg->r_gsbase = (uint32_t)pcb->pcb_gsbase; 120 } 121 *sizep = sizeof(*reg); 122 return (true); 123 } 124 125 static bool 126 set_segbases32(struct regset *rs, struct thread *td, void *buf, 127 size_t size) 128 { 129 struct segbasereg32 *reg; 130 struct pcb *pcb; 131 132 KASSERT(size == sizeof(*reg), ("%s: invalid size", __func__)); 133 reg = buf; 134 135 pcb = td->td_pcb; 136 set_pcb_flags(pcb, PCB_FULL_IRET); 137 pcb->pcb_fsbase = reg->r_fsbase; 138 td->td_frame->tf_fs = _ufssel; 139 pcb->pcb_gsbase = reg->r_gsbase; 140 td->td_frame->tf_gs = _ugssel; 141 142 return (true); 143 } 144 145 static struct regset regset_segbases32 = { 146 .note = NT_X86_SEGBASES, 147 .size = sizeof(struct segbasereg32), 148 .get = get_segbases32, 149 .set = set_segbases32, 150 }; 151 ELF32_REGSET(regset_segbases32); 152 #endif 153 154 static int 155 cpu_ptrace_xstate(struct thread *td, int req, void *addr, int data) 156 { 157 struct ptrace_xstate_info info; 158 #ifdef COMPAT_FREEBSD32 159 struct ptrace_xstate_info32 info32; 160 #endif 161 char *savefpu; 162 int error; 163 164 if (!use_xsave) 165 return (EOPNOTSUPP); 166 167 switch (req) { 168 case PT_GETXSTATE_OLD: 169 fpugetregs(td); 170 savefpu = (char *)(get_pcb_user_save_td(td) + 1); 171 error = copyout(savefpu, addr, 172 cpu_max_ext_state_size - sizeof(struct savefpu)); 173 break; 174 175 case PT_SETXSTATE_OLD: 176 if (data > cpu_max_ext_state_size - sizeof(struct savefpu)) { 177 error = EINVAL; 178 break; 179 } 180 savefpu = malloc(data, M_TEMP, M_WAITOK); 181 error = copyin(addr, savefpu, data); 182 if (error == 0) { 183 fpugetregs(td); 184 error = fpusetxstate(td, savefpu, data); 185 } 186 free(savefpu, M_TEMP); 187 break; 188 189 case PT_GETXSTATE_INFO: 190 #ifdef COMPAT_FREEBSD32 191 if (SV_CURPROC_FLAG(SV_ILP32)) { 192 if (data != sizeof(info32)) { 193 error = EINVAL; 194 } else { 195 info32.xsave_len = cpu_max_ext_state_size; 196 info32.xsave_mask1 = xsave_mask; 197 info32.xsave_mask2 = xsave_mask >> 32; 198 error = copyout(&info32, addr, data); 199 } 200 } else 201 #endif 202 { 203 if (data != sizeof(info)) { 204 error = EINVAL; 205 } else { 206 bzero(&info, sizeof(info)); 207 info.xsave_len = cpu_max_ext_state_size; 208 info.xsave_mask = xsave_mask; 209 error = copyout(&info, addr, data); 210 } 211 } 212 break; 213 214 case PT_GETXSTATE: 215 fpugetregs(td); 216 savefpu = (char *)(get_pcb_user_save_td(td)); 217 error = copyout(savefpu, addr, cpu_max_ext_state_size); 218 break; 219 220 case PT_SETXSTATE: 221 if (data < sizeof(struct savefpu) || 222 data > cpu_max_ext_state_size) { 223 error = EINVAL; 224 break; 225 } 226 savefpu = malloc(data, M_TEMP, M_WAITOK); 227 error = copyin(addr, savefpu, data); 228 if (error == 0) 229 error = fpusetregs(td, (struct savefpu *)savefpu, 230 savefpu + sizeof(struct savefpu), data - 231 sizeof(struct savefpu)); 232 free(savefpu, M_TEMP); 233 break; 234 235 default: 236 error = EINVAL; 237 break; 238 } 239 240 return (error); 241 } 242 243 static void 244 cpu_ptrace_setbase(struct thread *td, int req, register_t r) 245 { 246 struct pcb *pcb; 247 248 pcb = td->td_pcb; 249 set_pcb_flags(pcb, PCB_FULL_IRET); 250 if (req == PT_SETFSBASE) { 251 pcb->pcb_fsbase = r; 252 td->td_frame->tf_fs = _ufssel; 253 } else { 254 pcb->pcb_gsbase = r; 255 td->td_frame->tf_gs = _ugssel; 256 } 257 } 258 259 #ifdef COMPAT_FREEBSD32 260 #define PT_I386_GETXMMREGS (PT_FIRSTMACH + 0) 261 #define PT_I386_SETXMMREGS (PT_FIRSTMACH + 1) 262 263 static int 264 cpu32_ptrace(struct thread *td, int req, void *addr, int data) 265 { 266 struct savefpu *fpstate; 267 struct pcb *pcb; 268 uint32_t r; 269 int error; 270 271 switch (req) { 272 case PT_I386_GETXMMREGS: 273 fpugetregs(td); 274 error = copyout(get_pcb_user_save_td(td), addr, 275 sizeof(*fpstate)); 276 break; 277 278 case PT_I386_SETXMMREGS: 279 fpugetregs(td); 280 fpstate = get_pcb_user_save_td(td); 281 error = copyin(addr, fpstate, sizeof(*fpstate)); 282 fpstate->sv_env.en_mxcsr &= cpu_mxcsr_mask; 283 break; 284 285 case PT_GETXSTATE_OLD: 286 case PT_SETXSTATE_OLD: 287 case PT_GETXSTATE_INFO: 288 case PT_GETXSTATE: 289 case PT_SETXSTATE: 290 error = cpu_ptrace_xstate(td, req, addr, data); 291 break; 292 293 case PT_GETFSBASE: 294 case PT_GETGSBASE: 295 if (!SV_PROC_FLAG(td->td_proc, SV_ILP32)) { 296 error = EINVAL; 297 break; 298 } 299 pcb = td->td_pcb; 300 if (td == curthread) 301 update_pcb_bases(pcb); 302 r = req == PT_GETFSBASE ? pcb->pcb_fsbase : pcb->pcb_gsbase; 303 error = copyout(&r, addr, sizeof(r)); 304 break; 305 306 case PT_SETFSBASE: 307 case PT_SETGSBASE: 308 if (!SV_PROC_FLAG(td->td_proc, SV_ILP32)) { 309 error = EINVAL; 310 break; 311 } 312 error = copyin(addr, &r, sizeof(r)); 313 if (error != 0) 314 break; 315 cpu_ptrace_setbase(td, req, r); 316 break; 317 318 default: 319 error = EINVAL; 320 break; 321 } 322 323 return (error); 324 } 325 #endif 326 327 int 328 cpu_ptrace(struct thread *td, int req, void *addr, int data) 329 { 330 register_t *r, rv; 331 struct pcb *pcb; 332 int error; 333 334 #ifdef COMPAT_FREEBSD32 335 if (SV_CURPROC_FLAG(SV_ILP32)) 336 return (cpu32_ptrace(td, req, addr, data)); 337 #endif 338 339 /* Support old values of PT_GETXSTATE_OLD and PT_SETXSTATE_OLD. */ 340 if (req == PT_FIRSTMACH + 0) 341 req = PT_GETXSTATE_OLD; 342 if (req == PT_FIRSTMACH + 1) 343 req = PT_SETXSTATE_OLD; 344 345 switch (req) { 346 case PT_GETXSTATE_OLD: 347 case PT_SETXSTATE_OLD: 348 case PT_GETXSTATE_INFO: 349 case PT_GETXSTATE: 350 case PT_SETXSTATE: 351 error = cpu_ptrace_xstate(td, req, addr, data); 352 break; 353 354 case PT_GETFSBASE: 355 case PT_GETGSBASE: 356 pcb = td->td_pcb; 357 if (td == curthread) 358 update_pcb_bases(pcb); 359 r = req == PT_GETFSBASE ? &pcb->pcb_fsbase : &pcb->pcb_gsbase; 360 error = copyout(r, addr, sizeof(*r)); 361 break; 362 363 case PT_SETFSBASE: 364 case PT_SETGSBASE: 365 error = copyin(addr, &rv, sizeof(rv)); 366 if (error != 0) 367 break; 368 if (rv >= td->td_proc->p_sysent->sv_maxuser) { 369 error = EINVAL; 370 break; 371 } 372 cpu_ptrace_setbase(td, req, rv); 373 break; 374 375 default: 376 error = EINVAL; 377 break; 378 } 379 380 return (error); 381 } 382 383 int 384 ptrace_set_pc(struct thread *td, unsigned long addr) 385 { 386 387 td->td_frame->tf_rip = addr; 388 set_pcb_flags(td->td_pcb, PCB_FULL_IRET); 389 return (0); 390 } 391 392 int 393 ptrace_single_step(struct thread *td) 394 { 395 396 PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); 397 if ((td->td_frame->tf_rflags & PSL_T) == 0) { 398 td->td_frame->tf_rflags |= PSL_T; 399 td->td_dbgflags |= TDB_STEP; 400 } 401 return (0); 402 } 403 404 int 405 ptrace_clear_single_step(struct thread *td) 406 { 407 408 PROC_LOCK_ASSERT(td->td_proc, MA_OWNED); 409 td->td_frame->tf_rflags &= ~PSL_T; 410 td->td_dbgflags &= ~TDB_STEP; 411 return (0); 412 } 413