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