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