xref: /freebsd/sys/amd64/ia32/ia32_reg.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
162919d78SPeter Wemm /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3c49761ddSPedro F. Giffuni  *
462919d78SPeter Wemm  * Copyright (c) 2005 Peter Wemm
562919d78SPeter Wemm  * All rights reserved.
662919d78SPeter Wemm  *
762919d78SPeter Wemm  * Redistribution and use in source and binary forms, with or without
862919d78SPeter Wemm  * modification, are permitted provided that the following conditions
962919d78SPeter Wemm  * are met:
1062919d78SPeter Wemm  * 1. Redistributions of source code must retain the above copyright
1162919d78SPeter Wemm  *    notice, this list of conditions and the following disclaimer.
1262919d78SPeter Wemm  * 2. Redistributions in binary form must reproduce the above copyright
1362919d78SPeter Wemm  *    notice, this list of conditions and the following disclaimer in the
1462919d78SPeter Wemm  *    documentation and/or other materials provided with the distribution.
1562919d78SPeter Wemm  *
1662919d78SPeter Wemm  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1762919d78SPeter Wemm  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1862919d78SPeter Wemm  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1962919d78SPeter Wemm  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2062919d78SPeter Wemm  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2162919d78SPeter Wemm  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2262919d78SPeter Wemm  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2362919d78SPeter Wemm  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2462919d78SPeter Wemm  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2562919d78SPeter Wemm  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2662919d78SPeter Wemm  * SUCH DAMAGE.
2762919d78SPeter Wemm  */
2862919d78SPeter Wemm 
2962919d78SPeter Wemm #include <sys/param.h>
30931983eeSJohn Baldwin #include <sys/elf.h>
3162919d78SPeter Wemm #include <sys/exec.h>
3262919d78SPeter Wemm #include <sys/fcntl.h>
3362919d78SPeter Wemm #include <sys/imgact.h>
3462919d78SPeter Wemm #include <sys/kernel.h>
3562919d78SPeter Wemm #include <sys/lock.h>
3662919d78SPeter Wemm #include <sys/malloc.h>
3762919d78SPeter Wemm #include <sys/mutex.h>
3862919d78SPeter Wemm #include <sys/mman.h>
3962919d78SPeter Wemm #include <sys/namei.h>
4062919d78SPeter Wemm #include <sys/proc.h>
4162919d78SPeter Wemm #include <sys/procfs.h>
42b7924341SAndrew Turner #include <sys/reg.h>
4362919d78SPeter Wemm #include <sys/resourcevar.h>
4462919d78SPeter Wemm #include <sys/systm.h>
4562919d78SPeter Wemm #include <sys/signalvar.h>
4662919d78SPeter Wemm #include <sys/stat.h>
4762919d78SPeter Wemm #include <sys/sx.h>
4862919d78SPeter Wemm #include <sys/syscall.h>
4962919d78SPeter Wemm #include <sys/sysctl.h>
5062919d78SPeter Wemm #include <sys/sysent.h>
5162919d78SPeter Wemm #include <sys/vnode.h>
5262919d78SPeter Wemm 
5362919d78SPeter Wemm #include <vm/vm.h>
5462919d78SPeter Wemm #include <vm/vm_kern.h>
5562919d78SPeter Wemm #include <vm/vm_param.h>
5662919d78SPeter Wemm #include <vm/pmap.h>
5762919d78SPeter Wemm #include <vm/vm_map.h>
5862919d78SPeter Wemm #include <vm/vm_object.h>
5962919d78SPeter Wemm #include <vm/vm_extern.h>
6062919d78SPeter Wemm 
6162919d78SPeter Wemm #include <compat/freebsd32/freebsd32_util.h>
6262919d78SPeter Wemm #include <compat/freebsd32/freebsd32_proto.h>
6362919d78SPeter Wemm #include <machine/fpu.h>
6462919d78SPeter Wemm #include <machine/psl.h>
6562919d78SPeter Wemm #include <machine/segments.h>
6662919d78SPeter Wemm #include <machine/specialreg.h>
6762919d78SPeter Wemm #include <machine/frame.h>
6862919d78SPeter Wemm #include <machine/md_var.h>
6962919d78SPeter Wemm #include <machine/pcb.h>
7062919d78SPeter Wemm #include <machine/cpufunc.h>
7162919d78SPeter Wemm 
7262919d78SPeter Wemm int
fill_regs32(struct thread * td,struct reg32 * regs)7362919d78SPeter Wemm fill_regs32(struct thread *td, struct reg32 *regs)
7462919d78SPeter Wemm {
7562919d78SPeter Wemm 	struct trapframe *tp;
7662919d78SPeter Wemm 
7762919d78SPeter Wemm 	tp = td->td_frame;
782c66cccaSKonstantin Belousov 	if (tp->tf_flags & TF_HASSEGS) {
792c66cccaSKonstantin Belousov 		regs->r_gs = tp->tf_gs;
802c66cccaSKonstantin Belousov 		regs->r_fs = tp->tf_fs;
812c66cccaSKonstantin Belousov 		regs->r_es = tp->tf_es;
822c66cccaSKonstantin Belousov 		regs->r_ds = tp->tf_ds;
832c66cccaSKonstantin Belousov 	} else {
842c66cccaSKonstantin Belousov 		regs->r_gs = _ugssel;
852c66cccaSKonstantin Belousov 		regs->r_fs = _ufssel;
862c66cccaSKonstantin Belousov 		regs->r_es = _udatasel;
872c66cccaSKonstantin Belousov 		regs->r_ds = _udatasel;
882c66cccaSKonstantin Belousov 	}
8962919d78SPeter Wemm 	regs->r_edi = tp->tf_rdi;
9062919d78SPeter Wemm 	regs->r_esi = tp->tf_rsi;
9162919d78SPeter Wemm 	regs->r_ebp = tp->tf_rbp;
9262919d78SPeter Wemm 	regs->r_ebx = tp->tf_rbx;
9362919d78SPeter Wemm 	regs->r_edx = tp->tf_rdx;
9462919d78SPeter Wemm 	regs->r_ecx = tp->tf_rcx;
9562919d78SPeter Wemm 	regs->r_eax = tp->tf_rax;
9662919d78SPeter Wemm 	regs->r_eip = tp->tf_rip;
9762919d78SPeter Wemm 	regs->r_cs = tp->tf_cs;
9862919d78SPeter Wemm 	regs->r_eflags = tp->tf_rflags;
9962919d78SPeter Wemm 	regs->r_esp = tp->tf_rsp;
10062919d78SPeter Wemm 	regs->r_ss = tp->tf_ss;
101352aaa51SMark Johnston 	regs->r_err = 0;
102352aaa51SMark Johnston 	regs->r_trapno = 0;
10362919d78SPeter Wemm 	return (0);
10462919d78SPeter Wemm }
10562919d78SPeter Wemm 
10662919d78SPeter Wemm int
set_regs32(struct thread * td,struct reg32 * regs)10762919d78SPeter Wemm set_regs32(struct thread *td, struct reg32 *regs)
10862919d78SPeter Wemm {
10962919d78SPeter Wemm 	struct trapframe *tp;
11062919d78SPeter Wemm 
11162919d78SPeter Wemm 	tp = td->td_frame;
11262919d78SPeter Wemm 	if (!EFL_SECURE(regs->r_eflags, tp->tf_rflags) || !CS_SECURE(regs->r_cs))
11362919d78SPeter Wemm 		return (EINVAL);
1142c66cccaSKonstantin Belousov 	tp->tf_gs = regs->r_gs;
1152c66cccaSKonstantin Belousov 	tp->tf_fs = regs->r_fs;
1162c66cccaSKonstantin Belousov 	tp->tf_es = regs->r_es;
1172c66cccaSKonstantin Belousov 	tp->tf_ds = regs->r_ds;
11869baeadcSKonstantin Belousov 	set_pcb_flags(td->td_pcb, PCB_FULL_IRET);
1192c66cccaSKonstantin Belousov 	tp->tf_flags = TF_HASSEGS;
12062919d78SPeter Wemm 	tp->tf_rdi = regs->r_edi;
12162919d78SPeter Wemm 	tp->tf_rsi = regs->r_esi;
12262919d78SPeter Wemm 	tp->tf_rbp = regs->r_ebp;
12362919d78SPeter Wemm 	tp->tf_rbx = regs->r_ebx;
12462919d78SPeter Wemm 	tp->tf_rdx = regs->r_edx;
12562919d78SPeter Wemm 	tp->tf_rcx = regs->r_ecx;
12662919d78SPeter Wemm 	tp->tf_rax = regs->r_eax;
12762919d78SPeter Wemm 	tp->tf_rip = regs->r_eip;
12862919d78SPeter Wemm 	tp->tf_cs = regs->r_cs;
12962919d78SPeter Wemm 	tp->tf_rflags = regs->r_eflags;
13062919d78SPeter Wemm 	tp->tf_rsp = regs->r_esp;
13162919d78SPeter Wemm 	tp->tf_ss = regs->r_ss;
13262919d78SPeter Wemm 	return (0);
13362919d78SPeter Wemm }
13462919d78SPeter Wemm 
13562919d78SPeter Wemm int
fill_fpregs32(struct thread * td,struct fpreg32 * regs)13662919d78SPeter Wemm fill_fpregs32(struct thread *td, struct fpreg32 *regs)
13762919d78SPeter Wemm {
1385c6eb037SKonstantin Belousov 	struct savefpu *sv_fpu;
1395c6eb037SKonstantin Belousov 	struct save87 *sv_87;
1405c6eb037SKonstantin Belousov 	struct env87 *penv_87;
1415c6eb037SKonstantin Belousov 	struct envxmm *penv_xmm;
142c0b5fcf6SKonstantin Belousov 	struct fpacc87 *fx_reg;
143c0b5fcf6SKonstantin Belousov 	int i, st;
144c0b5fcf6SKonstantin Belousov 	uint64_t mantissa;
145c0b5fcf6SKonstantin Belousov 	uint16_t tw, exp;
146c0b5fcf6SKonstantin Belousov 	uint8_t ab_tw;
14762919d78SPeter Wemm 
14862919d78SPeter Wemm 	bzero(regs, sizeof(*regs));
1495c6eb037SKonstantin Belousov 	sv_87 = (struct save87 *)regs;
1505c6eb037SKonstantin Belousov 	penv_87 = &sv_87->sv_env;
1515c6eb037SKonstantin Belousov 	fpugetregs(td);
1528c6f8f3dSKonstantin Belousov 	sv_fpu = get_pcb_user_save_td(td);
1535c6eb037SKonstantin Belousov 	penv_xmm = &sv_fpu->sv_env;
15462919d78SPeter Wemm 
15562919d78SPeter Wemm 	/* FPU control/status */
15662919d78SPeter Wemm 	penv_87->en_cw = penv_xmm->en_cw;
15762919d78SPeter Wemm 	penv_87->en_sw = penv_xmm->en_sw;
158c377ff61SKonstantin Belousov 
15962919d78SPeter Wemm 	/*
16062919d78SPeter Wemm 	 * XXX for en_fip/fcs/foo/fos, check if the fxsave format
16162919d78SPeter Wemm 	 * uses the old-style layout for 32 bit user apps.  If so,
16262919d78SPeter Wemm 	 * read the ip and operand segment registers from there.
16362919d78SPeter Wemm 	 * For now, use the process's %cs/%ds.
16462919d78SPeter Wemm 	 */
16562919d78SPeter Wemm 	penv_87->en_fip = penv_xmm->en_rip;
16662919d78SPeter Wemm 	penv_87->en_fcs = td->td_frame->tf_cs;
16762919d78SPeter Wemm 	penv_87->en_opcode = penv_xmm->en_opcode;
16862919d78SPeter Wemm 	penv_87->en_foo = penv_xmm->en_rdp;
1692c66cccaSKonstantin Belousov 	/* Entry into the kernel always sets TF_HASSEGS */
1702c66cccaSKonstantin Belousov 	penv_87->en_fos = td->td_frame->tf_ds;
17162919d78SPeter Wemm 
172c0b5fcf6SKonstantin Belousov 	/*
173c0b5fcf6SKonstantin Belousov 	 * FPU registers and tags.
174c0b5fcf6SKonstantin Belousov 	 * For ST(i), i = fpu_reg - top; we start with fpu_reg=7.
175c0b5fcf6SKonstantin Belousov 	 */
176c0b5fcf6SKonstantin Belousov 	st = 7 - ((penv_xmm->en_sw >> 11) & 7);
177c0b5fcf6SKonstantin Belousov 	ab_tw = penv_xmm->en_tw;
178c0b5fcf6SKonstantin Belousov 	tw = 0;
179c0b5fcf6SKonstantin Belousov 	for (i = 0x80; i != 0; i >>= 1) {
180c0b5fcf6SKonstantin Belousov 		sv_87->sv_ac[st] = sv_fpu->sv_fp[st].fp_acc;
181c0b5fcf6SKonstantin Belousov 		tw <<= 2;
182c0b5fcf6SKonstantin Belousov 		if ((ab_tw & i) != 0) {
183c0b5fcf6SKonstantin Belousov 			/* Non-empty - we need to check ST(i) */
184c0b5fcf6SKonstantin Belousov 			fx_reg = &sv_fpu->sv_fp[st].fp_acc;
185c0b5fcf6SKonstantin Belousov 			/* The first 64 bits contain the mantissa. */
186c0b5fcf6SKonstantin Belousov 			mantissa = *((uint64_t *)fx_reg->fp_bytes);
187c0b5fcf6SKonstantin Belousov 			/*
188c0b5fcf6SKonstantin Belousov 			 * The final 16 bits contain the sign bit and the exponent.
189c0b5fcf6SKonstantin Belousov 			 * Mask the sign bit since it is of no consequence to these
190c0b5fcf6SKonstantin Belousov 			 * tests.
191c0b5fcf6SKonstantin Belousov 			 */
192c0b5fcf6SKonstantin Belousov 			exp = *((uint16_t *)&fx_reg->fp_bytes[8]) & 0x7fff;
193c0b5fcf6SKonstantin Belousov 			if (exp == 0) {
194c0b5fcf6SKonstantin Belousov 				if (mantissa == 0)
195c0b5fcf6SKonstantin Belousov 					tw |= 1; /* Zero */
196c0b5fcf6SKonstantin Belousov 				else
197c0b5fcf6SKonstantin Belousov 					tw |= 2; /* Denormal */
198c0b5fcf6SKonstantin Belousov 			} else if (exp == 0x7fff)
199c0b5fcf6SKonstantin Belousov 				tw |= 2; /* Infinity or NaN */
200c0b5fcf6SKonstantin Belousov 		} else
201c0b5fcf6SKonstantin Belousov 			tw |= 3; /* Empty */
202c0b5fcf6SKonstantin Belousov 		st = (st - 1) & 7;
203c377ff61SKonstantin Belousov 	}
204c0b5fcf6SKonstantin Belousov 	penv_87->en_tw = tw;
20562919d78SPeter Wemm 
20662919d78SPeter Wemm 	return (0);
20762919d78SPeter Wemm }
20862919d78SPeter Wemm 
20962919d78SPeter Wemm int
set_fpregs32(struct thread * td,struct fpreg32 * regs)21062919d78SPeter Wemm set_fpregs32(struct thread *td, struct fpreg32 *regs)
21162919d78SPeter Wemm {
21262919d78SPeter Wemm 	struct save87 *sv_87 = (struct save87 *)regs;
21362919d78SPeter Wemm 	struct env87 *penv_87 = &sv_87->sv_env;
2148c6f8f3dSKonstantin Belousov 	struct savefpu *sv_fpu = get_pcb_user_save_td(td);
21562919d78SPeter Wemm 	struct envxmm *penv_xmm = &sv_fpu->sv_env;
21662919d78SPeter Wemm 	int i;
21762919d78SPeter Wemm 
21862919d78SPeter Wemm 	/* FPU control/status */
21962919d78SPeter Wemm 	penv_xmm->en_cw = penv_87->en_cw;
22062919d78SPeter Wemm 	penv_xmm->en_sw = penv_87->en_sw;
22162919d78SPeter Wemm 	penv_xmm->en_rip = penv_87->en_fip;
22262919d78SPeter Wemm 	/* penv_87->en_fcs and en_fos ignored, see above */
22362919d78SPeter Wemm 	penv_xmm->en_opcode = penv_87->en_opcode;
22462919d78SPeter Wemm 	penv_xmm->en_rdp = penv_87->en_foo;
22562919d78SPeter Wemm 
226c377ff61SKonstantin Belousov 	/* FPU registers and tags */
227c377ff61SKonstantin Belousov 	penv_xmm->en_tw = 0;
228c377ff61SKonstantin Belousov 	for (i = 0; i < 8; ++i) {
22962919d78SPeter Wemm 		sv_fpu->sv_fp[i].fp_acc = sv_87->sv_ac[i];
230c377ff61SKonstantin Belousov 		if ((penv_87->en_tw & (3 << i * 2)) != (3 << i * 2))
231c377ff61SKonstantin Belousov 			penv_xmm->en_tw |= 1 << i;
232c377ff61SKonstantin Belousov 	}
233c377ff61SKonstantin Belousov 
23462919d78SPeter Wemm 	for (i = 8; i < 16; ++i)
23562919d78SPeter Wemm 		bzero(&sv_fpu->sv_fp[i].fp_acc, sizeof(sv_fpu->sv_fp[i].fp_acc));
2365c6eb037SKonstantin Belousov 	fpuuserinited(td);
23762919d78SPeter Wemm 
23862919d78SPeter Wemm 	return (0);
23962919d78SPeter Wemm }
24062919d78SPeter Wemm 
24162919d78SPeter Wemm int
fill_dbregs32(struct thread * td,struct dbreg32 * regs)24262919d78SPeter Wemm fill_dbregs32(struct thread *td, struct dbreg32 *regs)
24362919d78SPeter Wemm {
24462919d78SPeter Wemm 	struct dbreg dr;
24562919d78SPeter Wemm 	int err, i;
24662919d78SPeter Wemm 
24762919d78SPeter Wemm 	err = fill_dbregs(td, &dr);
24862919d78SPeter Wemm 	for (i = 0; i < 8; i++)
24962919d78SPeter Wemm 		regs->dr[i] = dr.dr[i];
25062919d78SPeter Wemm 	return (err);
25162919d78SPeter Wemm }
25262919d78SPeter Wemm 
25362919d78SPeter Wemm int
set_dbregs32(struct thread * td,struct dbreg32 * regs)25462919d78SPeter Wemm set_dbregs32(struct thread *td, struct dbreg32 *regs)
25562919d78SPeter Wemm {
25662919d78SPeter Wemm 	struct dbreg dr;
25762919d78SPeter Wemm 	int i;
25862919d78SPeter Wemm 
25962919d78SPeter Wemm 	for (i = 0; i < 8; i++)
26062919d78SPeter Wemm 		dr.dr[i] = regs->dr[i];
26162919d78SPeter Wemm 	for (i = 8; i < 16; i++)
26262919d78SPeter Wemm 		dr.dr[i] = 0;
26362919d78SPeter Wemm 	return (set_dbregs(td, &dr));
26462919d78SPeter Wemm }
265931983eeSJohn Baldwin 
266931983eeSJohn Baldwin static bool
get_i386_segbases(struct regset * rs,struct thread * td,void * buf,size_t * sizep)267931983eeSJohn Baldwin get_i386_segbases(struct regset *rs, struct thread *td, void *buf,
268931983eeSJohn Baldwin     size_t *sizep)
269931983eeSJohn Baldwin {
270931983eeSJohn Baldwin 	struct segbasereg32 *reg;
271931983eeSJohn Baldwin 	struct pcb *pcb;
272931983eeSJohn Baldwin 
273931983eeSJohn Baldwin 	if (buf != NULL) {
274931983eeSJohn Baldwin 		KASSERT(*sizep == sizeof(*reg), ("%s: invalid size", __func__));
275931983eeSJohn Baldwin 		reg = buf;
276931983eeSJohn Baldwin 
277931983eeSJohn Baldwin 		pcb = td->td_pcb;
278931983eeSJohn Baldwin 		if (td == curthread)
279931983eeSJohn Baldwin 			update_pcb_bases(pcb);
280931983eeSJohn Baldwin 		reg->r_fsbase = pcb->pcb_fsbase;
281931983eeSJohn Baldwin 		reg->r_gsbase = pcb->pcb_gsbase;
282931983eeSJohn Baldwin 	}
283931983eeSJohn Baldwin 	*sizep = sizeof(*reg);
284931983eeSJohn Baldwin 	return (true);
285931983eeSJohn Baldwin }
286931983eeSJohn Baldwin 
287931983eeSJohn Baldwin static bool
set_i386_segbases(struct regset * rs,struct thread * td,void * buf,size_t size)288931983eeSJohn Baldwin set_i386_segbases(struct regset *rs, struct thread *td, void *buf,
289931983eeSJohn Baldwin     size_t size)
290931983eeSJohn Baldwin {
291931983eeSJohn Baldwin 	struct segbasereg32 *reg;
292931983eeSJohn Baldwin 	struct pcb *pcb;
293931983eeSJohn Baldwin 
294931983eeSJohn Baldwin 	KASSERT(size == sizeof(*reg), ("%s: invalid size", __func__));
295931983eeSJohn Baldwin 	reg = buf;
296931983eeSJohn Baldwin 
297931983eeSJohn Baldwin 	pcb = td->td_pcb;
298931983eeSJohn Baldwin 	set_pcb_flags(pcb, PCB_FULL_IRET);
299931983eeSJohn Baldwin 	pcb->pcb_fsbase = reg->r_fsbase;
300931983eeSJohn Baldwin 	td->td_frame->tf_fs = _ufssel;
301931983eeSJohn Baldwin 	pcb->pcb_gsbase = reg->r_gsbase;
302931983eeSJohn Baldwin 	td->td_frame->tf_gs = _ugssel;
303931983eeSJohn Baldwin 
304931983eeSJohn Baldwin 	return (true);
305931983eeSJohn Baldwin }
306931983eeSJohn Baldwin 
307931983eeSJohn Baldwin static struct regset regset_i386_segbases = {
308931983eeSJohn Baldwin 	.note = NT_X86_SEGBASES,
309931983eeSJohn Baldwin 	.size = sizeof(struct segbasereg),
310931983eeSJohn Baldwin 	.get = get_i386_segbases,
311931983eeSJohn Baldwin 	.set = set_i386_segbases,
312931983eeSJohn Baldwin };
313931983eeSJohn Baldwin ELF32_REGSET(regset_i386_segbases);
314