ptrace_machdep.c revision 331735
162053Smarkm/*-
262765Smarkm * Copyright (c) 2011 Konstantin Belousov <kib@FreeBSD.org>
362053Smarkm * All rights reserved.
462053Smarkm *
562053Smarkm * Redistribution and use in source and binary forms, with or without
662053Smarkm * modification, are permitted provided that the following conditions
762053Smarkm * are met:
862053Smarkm * 1. Redistributions of source code must retain the above copyright
962053Smarkm *    notice, this list of conditions and the following disclaimer.
1062053Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1162053Smarkm *    notice, this list of conditions and the following disclaimer in the
1262053Smarkm *    documentation and/or other materials provided with the distribution.
1362053Smarkm *
1462053Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1562053Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1662053Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1762053Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1862053Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1962053Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2062053Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2162053Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2262053Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2362053Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2462053Smarkm * SUCH DAMAGE.
2562053Smarkm *
2662053Smarkm */
2762053Smarkm
2862053Smarkm#include <sys/cdefs.h>
2962053Smarkm__FBSDID("$FreeBSD: stable/11/sys/amd64/amd64/ptrace_machdep.c 331735 2018-03-29 15:08:40Z kib $");
3062053Smarkm
3176166Smarkm#include "opt_compat.h"
3262053Smarkm
3376166Smarkm#include <sys/param.h>
3474771Smarkm#include <sys/systm.h>
3562053Smarkm#include <sys/malloc.h>
3674072Smarkm#include <sys/proc.h>
3776166Smarkm#include <sys/ptrace.h>
3862053Smarkm#include <sys/sysent.h>
3962053Smarkm#include <vm/vm.h>
4076166Smarkm#include <vm/pmap.h>
4167112Smarkm#include <machine/md_var.h>
4276166Smarkm#include <machine/pcb.h>
4376166Smarkm#include <machine/frame.h>
4470834Swollman#include <machine/vmparam.h>
4571037Smarkm
4676166Smarkm#ifdef COMPAT_FREEBSD32
4776166Smarkmstruct ptrace_xstate_info32 {
4867112Smarkm	uint32_t	xsave_mask1, xsave_mask2;
4974072Smarkm	uint32_t	xsave_len;
5062053Smarkm};
5174072Smarkm#endif
5262053Smarkm
5362053Smarkmstatic int
5474072Smarkmcpu_ptrace_xstate(struct thread *td, int req, void *addr, int data)
5562053Smarkm{
5674072Smarkm	struct ptrace_xstate_info info;
5774072Smarkm#ifdef COMPAT_FREEBSD32
5874072Smarkm	struct ptrace_xstate_info32 info32;
5974072Smarkm#endif
6074072Smarkm	char *savefpu;
6174072Smarkm	int error;
6262053Smarkm
6362053Smarkm	if (!use_xsave)
6462053Smarkm		return (EOPNOTSUPP);
6562053Smarkm
6662053Smarkm	switch (req) {
6763855Smarkm	case PT_GETXSTATE_OLD:
6869172Smarkm		fpugetregs(td);
6962765Smarkm		savefpu = (char *)(get_pcb_user_save_td(td) + 1);
7062765Smarkm		error = copyout(savefpu, addr,
7165686Smarkm		    cpu_max_ext_state_size - sizeof(struct savefpu));
7267112Smarkm		break;
7362053Smarkm
7462053Smarkm	case PT_SETXSTATE_OLD:
7562053Smarkm		if (data > cpu_max_ext_state_size - sizeof(struct savefpu)) {
7662053Smarkm			error = EINVAL;
7762053Smarkm			break;
7862053Smarkm		}
7962053Smarkm		savefpu = malloc(data, M_TEMP, M_WAITOK);
8062053Smarkm		error = copyin(addr, savefpu, data);
8162053Smarkm		if (error == 0) {
8274072Smarkm			fpugetregs(td);
8374072Smarkm			error = fpusetxstate(td, savefpu, data);
8474072Smarkm		}
8574072Smarkm		free(savefpu, M_TEMP);
8674072Smarkm		break;
8774072Smarkm
8874072Smarkm	case PT_GETXSTATE_INFO:
8974072Smarkm#ifdef COMPAT_FREEBSD32
9074072Smarkm		if (SV_CURPROC_FLAG(SV_ILP32)) {
9174072Smarkm			if (data != sizeof(info32)) {
9274072Smarkm				error = EINVAL;
9374072Smarkm			} else {
9474072Smarkm				info32.xsave_len = cpu_max_ext_state_size;
9574072Smarkm				info32.xsave_mask1 = xsave_mask;
9674072Smarkm				info32.xsave_mask2 = xsave_mask >> 32;
9774072Smarkm				error = copyout(&info32, addr, data);
9874072Smarkm			}
9974072Smarkm		} else
10074072Smarkm#endif
10174072Smarkm		{
10274072Smarkm			if (data != sizeof(info)) {
10374072Smarkm				error  = EINVAL;
10462053Smarkm			} else {
10574072Smarkm				bzero(&info, sizeof(info));
10674072Smarkm				info.xsave_len = cpu_max_ext_state_size;
10762053Smarkm				info.xsave_mask = xsave_mask;
10874072Smarkm				error = copyout(&info, addr, data);
10974072Smarkm			}
11074072Smarkm		}
11174072Smarkm		break;
11274072Smarkm
11374072Smarkm	case PT_GETXSTATE:
11474072Smarkm		fpugetregs(td);
11562053Smarkm		savefpu = (char *)(get_pcb_user_save_td(td));
11674072Smarkm		error = copyout(savefpu, addr, cpu_max_ext_state_size);
11774072Smarkm		break;
11874072Smarkm
11974072Smarkm	case PT_SETXSTATE:
12074072Smarkm		if (data < sizeof(struct savefpu) ||
12174072Smarkm		    data > cpu_max_ext_state_size) {
12274072Smarkm			error = EINVAL;
12374072Smarkm			break;
12474072Smarkm		}
12574072Smarkm		savefpu = malloc(data, M_TEMP, M_WAITOK);
12674072Smarkm		error = copyin(addr, savefpu, data);
12774072Smarkm		if (error == 0)
12874072Smarkm			error = fpusetregs(td, (struct savefpu *)savefpu,
12974072Smarkm			    savefpu + sizeof(struct savefpu), data -
13074072Smarkm			    sizeof(struct savefpu));
13174072Smarkm		free(savefpu, M_TEMP);
13274072Smarkm		break;
13374072Smarkm
13474072Smarkm	default:
13574072Smarkm		error = EINVAL;
13674072Smarkm		break;
13774072Smarkm	}
13874072Smarkm
13974072Smarkm	return (error);
14062053Smarkm}
14163855Smarkm
14263855Smarkmstatic void
14363855Smarkmcpu_ptrace_setbase(struct thread *td, int req, register_t r)
14463855Smarkm{
14563855Smarkm	struct pcb *pcb;
14663855Smarkm
14763855Smarkm	pcb = td->td_pcb;
14863855Smarkm	set_pcb_flags(pcb, PCB_FULL_IRET);
14963855Smarkm	if (req == PT_SETFSBASE) {
15069172Smarkm		pcb->pcb_fsbase = r;
15169172Smarkm		td->td_frame->tf_fs = _ufssel;
15269174Smarkm	} else {
15369172Smarkm		pcb->pcb_gsbase = r;
15469172Smarkm		td->td_frame->tf_gs = _ugssel;
15569172Smarkm	}
15669172Smarkm}
15769172Smarkm
15862765Smarkm#ifdef COMPAT_FREEBSD32
15962053Smarkm#define PT_I386_GETXMMREGS	(PT_FIRSTMACH + 0)
16074072Smarkm#define PT_I386_SETXMMREGS	(PT_FIRSTMACH + 1)
16174072Smarkm
16274072Smarkmstatic int
16362053Smarkmcpu32_ptrace(struct thread *td, int req, void *addr, int data)
16474072Smarkm{
16567286Speter	struct savefpu *fpstate;
16667286Speter	struct pcb *pcb;
16767112Smarkm	uint32_t r;
16874072Smarkm	int error;
16974072Smarkm
17067286Speter	switch (req) {
17167286Speter	case PT_I386_GETXMMREGS:
17267112Smarkm		fpugetregs(td);
17367286Speter		error = copyout(get_pcb_user_save_td(td), addr,
17467286Speter		    sizeof(*fpstate));
17567286Speter		break;
17667286Speter
17767286Speter	case PT_I386_SETXMMREGS:
17867286Speter		fpugetregs(td);
17967286Speter		fpstate = get_pcb_user_save_td(td);
18062053Smarkm		error = copyin(addr, fpstate, sizeof(*fpstate));
18162053Smarkm		fpstate->sv_env.en_mxcsr &= cpu_mxcsr_mask;
18262053Smarkm		break;
18362053Smarkm
18462765Smarkm	case PT_GETXSTATE_OLD:
18562053Smarkm	case PT_SETXSTATE_OLD:
18674072Smarkm	case PT_GETXSTATE_INFO:
18774072Smarkm	case PT_GETXSTATE:
18874072Smarkm	case PT_SETXSTATE:
18962053Smarkm		error = cpu_ptrace_xstate(td, req, addr, data);
19074072Smarkm		break;
19162765Smarkm
19262053Smarkm	case PT_GETFSBASE:
19362053Smarkm	case PT_GETGSBASE:
19462765Smarkm		if (!SV_PROC_FLAG(td->td_proc, SV_ILP32)) {
19562053Smarkm			error = EINVAL;
19662053Smarkm			break;
19774072Smarkm		}
19862053Smarkm		pcb = td->td_pcb;
19962765Smarkm		if (td == curthread)
20062053Smarkm			update_pcb_bases(pcb);
20162053Smarkm		r = req == PT_GETFSBASE ? pcb->pcb_fsbase : pcb->pcb_gsbase;
20262053Smarkm		error = copyout(&r, addr, sizeof(r));
20362053Smarkm		break;
20465686Smarkm
20565686Smarkm	case PT_SETFSBASE:
20674771Smarkm	case PT_SETGSBASE:
20774771Smarkm		if (!SV_PROC_FLAG(td->td_proc, SV_ILP32)) {
20874771Smarkm			error = EINVAL;
20974771Smarkm			break;
21074771Smarkm		}
21174771Smarkm		error = copyin(addr, &r, sizeof(r));
21274771Smarkm		if (error != 0)
21374771Smarkm			break;
21465686Smarkm		cpu_ptrace_setbase(td, req, r);
21565686Smarkm		break;
21665686Smarkm
21767112Smarkm	default:
21867112Smarkm		error = EINVAL;
21974072Smarkm		break;
22067112Smarkm	}
22167112Smarkm
22267112Smarkm	return (error);
22374072Smarkm}
22467112Smarkm#endif
22567112Smarkm
22674072Smarkmint
22767112Smarkmcpu_ptrace(struct thread *td, int req, void *addr, int data)
22867112Smarkm{
22967112Smarkm	register_t *r, rv;
23067112Smarkm	struct pcb *pcb;
23167112Smarkm	int error;
23262053Smarkm
23362053Smarkm#ifdef COMPAT_FREEBSD32
23474072Smarkm	if (SV_CURPROC_FLAG(SV_ILP32))
23565686Smarkm		return (cpu32_ptrace(td, req, addr, data));
23662053Smarkm#endif
23762053Smarkm
23874072Smarkm	/* Support old values of PT_GETXSTATE_OLD and PT_SETXSTATE_OLD. */
23971037Smarkm	if (req == PT_FIRSTMACH + 0)
24074072Smarkm		req = PT_GETXSTATE_OLD;
24174072Smarkm	if (req == PT_FIRSTMACH + 1)
24274072Smarkm		req = PT_SETXSTATE_OLD;
24374072Smarkm
24471037Smarkm	switch (req) {
24574072Smarkm	case PT_GETXSTATE_OLD:
24674072Smarkm	case PT_SETXSTATE_OLD:
24774072Smarkm	case PT_GETXSTATE_INFO:
24874072Smarkm	case PT_GETXSTATE:
24974072Smarkm	case PT_SETXSTATE:
25074072Smarkm		error = cpu_ptrace_xstate(td, req, addr, data);
25174072Smarkm		break;
25274072Smarkm
25374072Smarkm	case PT_GETFSBASE:
25462053Smarkm	case PT_GETGSBASE:
25562053Smarkm		pcb = td->td_pcb;
25662765Smarkm		if (td == curthread)
25762149Smarkm			update_pcb_bases(pcb);
25874072Smarkm		r = req == PT_GETFSBASE ? &pcb->pcb_fsbase : &pcb->pcb_gsbase;
25974072Smarkm		error = copyout(r, addr, sizeof(*r));
26074072Smarkm		break;
26174072Smarkm
26274072Smarkm	case PT_SETFSBASE:
26374072Smarkm	case PT_SETGSBASE:
26474072Smarkm		error = copyin(addr, &rv, sizeof(rv));
26574072Smarkm		if (error != 0)
26674072Smarkm			break;
26774072Smarkm		if (rv >= td->td_proc->p_sysent->sv_maxuser) {
26874072Smarkm			error = EINVAL;
26974072Smarkm			break;
27062053Smarkm		}
27162053Smarkm		cpu_ptrace_setbase(td, req, rv);
27262053Smarkm		break;
27374072Smarkm
27474072Smarkm	default:
27574072Smarkm		error = EINVAL;
27674072Smarkm		break;
27774072Smarkm	}
27874072Smarkm
27974072Smarkm	return (error);
28074072Smarkm}
28174072Smarkm