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/malloc.h> 36 #include <sys/proc.h> 37 #include <sys/ptrace.h> 38 #include <sys/sysent.h> 39 #include <vm/vm.h> 40 #include <vm/pmap.h> 41 #include <machine/md_var.h> 42 #include <machine/pcb.h> 43 #include <machine/frame.h> 44 #include <machine/vmparam.h> 45 46 #ifdef COMPAT_FREEBSD32 47 struct ptrace_xstate_info32 { 48 uint32_t xsave_mask1, xsave_mask2; 49 uint32_t xsave_len; 50 }; 51 #endif 52 53 static int 54 cpu_ptrace_xstate(struct thread *td, int req, void *addr, int data) 55 { 56 struct ptrace_xstate_info info; 57 #ifdef COMPAT_FREEBSD32 58 struct ptrace_xstate_info32 info32; 59 #endif 60 char *savefpu; 61 int error; 62 63 if (!use_xsave) 64 return (EOPNOTSUPP); 65 66 switch (req) { 67 case PT_GETXSTATE_OLD: 68 fpugetregs(td); 69 savefpu = (char *)(get_pcb_user_save_td(td) + 1); 70 error = copyout(savefpu, addr, 71 cpu_max_ext_state_size - sizeof(struct savefpu)); 72 break; 73 74 case PT_SETXSTATE_OLD: 75 if (data > cpu_max_ext_state_size - sizeof(struct savefpu)) { 76 error = EINVAL; 77 break; 78 } 79 savefpu = malloc(data, M_TEMP, M_WAITOK); 80 error = copyin(addr, savefpu, data); 81 if (error == 0) { 82 fpugetregs(td); 83 error = fpusetxstate(td, savefpu, data); 84 } 85 free(savefpu, M_TEMP); 86 break; 87 88 case PT_GETXSTATE_INFO: 89 #ifdef COMPAT_FREEBSD32 90 if (SV_CURPROC_FLAG(SV_ILP32)) { 91 if (data != sizeof(info32)) { 92 error = EINVAL; 93 } else { 94 info32.xsave_len = cpu_max_ext_state_size; 95 info32.xsave_mask1 = xsave_mask; 96 info32.xsave_mask2 = xsave_mask >> 32; 97 error = copyout(&info32, addr, data); 98 } 99 } else 100 #endif 101 { 102 if (data != sizeof(info)) { 103 error = EINVAL; 104 } else { 105 bzero(&info, sizeof(info)); 106 info.xsave_len = cpu_max_ext_state_size; 107 info.xsave_mask = xsave_mask; 108 error = copyout(&info, addr, data); 109 } 110 } 111 break; 112 113 case PT_GETXSTATE: 114 fpugetregs(td); 115 savefpu = (char *)(get_pcb_user_save_td(td)); 116 error = copyout(savefpu, addr, cpu_max_ext_state_size); 117 break; 118 119 case PT_SETXSTATE: 120 if (data < sizeof(struct savefpu) || 121 data > cpu_max_ext_state_size) { 122 error = EINVAL; 123 break; 124 } 125 savefpu = malloc(data, M_TEMP, M_WAITOK); 126 error = copyin(addr, savefpu, data); 127 if (error == 0) 128 error = fpusetregs(td, (struct savefpu *)savefpu, 129 savefpu + sizeof(struct savefpu), data - 130 sizeof(struct savefpu)); 131 free(savefpu, M_TEMP); 132 break; 133 134 default: 135 error = EINVAL; 136 break; 137 } 138 139 return (error); 140 } 141 142 static void 143 cpu_ptrace_setbase(struct thread *td, int req, register_t r) 144 { 145 struct pcb *pcb; 146 147 pcb = td->td_pcb; 148 set_pcb_flags(pcb, PCB_FULL_IRET); 149 if (req == PT_SETFSBASE) { 150 pcb->pcb_fsbase = r; 151 td->td_frame->tf_fs = _ufssel; 152 } else { 153 pcb->pcb_gsbase = r; 154 td->td_frame->tf_gs = _ugssel; 155 } 156 } 157 158 #ifdef COMPAT_FREEBSD32 159 #define PT_I386_GETXMMREGS (PT_FIRSTMACH + 0) 160 #define PT_I386_SETXMMREGS (PT_FIRSTMACH + 1) 161 162 static int 163 cpu32_ptrace(struct thread *td, int req, void *addr, int data) 164 { 165 struct savefpu *fpstate; 166 struct pcb *pcb; 167 uint32_t r; 168 int error; 169 170 switch (req) { 171 case PT_I386_GETXMMREGS: 172 fpugetregs(td); 173 error = copyout(get_pcb_user_save_td(td), addr, 174 sizeof(*fpstate)); 175 break; 176 177 case PT_I386_SETXMMREGS: 178 fpugetregs(td); 179 fpstate = get_pcb_user_save_td(td); 180 error = copyin(addr, fpstate, sizeof(*fpstate)); 181 fpstate->sv_env.en_mxcsr &= cpu_mxcsr_mask; 182 break; 183 184 case PT_GETXSTATE_OLD: 185 case PT_SETXSTATE_OLD: 186 case PT_GETXSTATE_INFO: 187 case PT_GETXSTATE: 188 case PT_SETXSTATE: 189 error = cpu_ptrace_xstate(td, req, addr, data); 190 break; 191 192 case PT_GETFSBASE: 193 case PT_GETGSBASE: 194 if (!SV_PROC_FLAG(td->td_proc, SV_ILP32)) { 195 error = EINVAL; 196 break; 197 } 198 pcb = td->td_pcb; 199 if (td == curthread) 200 update_pcb_bases(pcb); 201 r = req == PT_GETFSBASE ? pcb->pcb_fsbase : pcb->pcb_gsbase; 202 error = copyout(&r, addr, sizeof(r)); 203 break; 204 205 case PT_SETFSBASE: 206 case PT_SETGSBASE: 207 if (!SV_PROC_FLAG(td->td_proc, SV_ILP32)) { 208 error = EINVAL; 209 break; 210 } 211 error = copyin(addr, &r, sizeof(r)); 212 if (error != 0) 213 break; 214 cpu_ptrace_setbase(td, req, r); 215 break; 216 217 default: 218 error = EINVAL; 219 break; 220 } 221 222 return (error); 223 } 224 #endif 225 226 int 227 cpu_ptrace(struct thread *td, int req, void *addr, int data) 228 { 229 register_t *r, rv; 230 struct pcb *pcb; 231 int error; 232 233 #ifdef COMPAT_FREEBSD32 234 if (SV_CURPROC_FLAG(SV_ILP32)) 235 return (cpu32_ptrace(td, req, addr, data)); 236 #endif 237 238 /* Support old values of PT_GETXSTATE_OLD and PT_SETXSTATE_OLD. */ 239 if (req == PT_FIRSTMACH + 0) 240 req = PT_GETXSTATE_OLD; 241 if (req == PT_FIRSTMACH + 1) 242 req = PT_SETXSTATE_OLD; 243 244 switch (req) { 245 case PT_GETXSTATE_OLD: 246 case PT_SETXSTATE_OLD: 247 case PT_GETXSTATE_INFO: 248 case PT_GETXSTATE: 249 case PT_SETXSTATE: 250 error = cpu_ptrace_xstate(td, req, addr, data); 251 break; 252 253 case PT_GETFSBASE: 254 case PT_GETGSBASE: 255 pcb = td->td_pcb; 256 if (td == curthread) 257 update_pcb_bases(pcb); 258 r = req == PT_GETFSBASE ? &pcb->pcb_fsbase : &pcb->pcb_gsbase; 259 error = copyout(r, addr, sizeof(*r)); 260 break; 261 262 case PT_SETFSBASE: 263 case PT_SETGSBASE: 264 error = copyin(addr, &rv, sizeof(rv)); 265 if (error != 0) 266 break; 267 if (rv >= td->td_proc->p_sysent->sv_maxuser) { 268 error = EINVAL; 269 break; 270 } 271 cpu_ptrace_setbase(td, req, rv); 272 break; 273 274 default: 275 error = EINVAL; 276 break; 277 } 278 279 return (error); 280 } 281