1/*- 2 * Copyright (c) 2011 Konstantin Belousov <kib@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: stable/11/sys/amd64/amd64/ptrace_machdep.c 331735 2018-03-29 15:08:40Z kib $"); 30 31#include "opt_compat.h" 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 47struct ptrace_xstate_info32 { 48 uint32_t xsave_mask1, xsave_mask2; 49 uint32_t xsave_len; 50}; 51#endif 52 53static int 54cpu_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 142static void 143cpu_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 162static int 163cpu32_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 226int 227cpu_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