1/*	$NetBSD: process_machdep.c,v 1.71 2009/10/21 21:12:00 rmind Exp $	*/
2
3/*-
4 * Copyright (c) 1998, 2000, 2001, 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Charles M. Hannum; by Jason R. Thorpe of Wasabi Systems, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * This file may seem a bit stylized, but that so that it's easier to port.
34 * Functions to be implemented here are:
35 *
36 * process_read_regs(proc, regs)
37 *	Get the current user-visible register set from the process
38 *	and copy it into the regs structure (<machine/reg.h>).
39 *	The process is stopped at the time read_regs is called.
40 *
41 * process_write_regs(proc, regs)
42 *	Update the current register set from the passed in regs
43 *	structure.  Take care to avoid clobbering special CPU
44 *	registers or privileged bits in the PSL.
45 *	The process is stopped at the time write_regs is called.
46 *
47 * process_sstep(proc)
48 *	Arrange for the process to trap after executing a single instruction.
49 *
50 * process_set_pc(proc)
51 *	Set the process's program counter.
52 */
53
54#include <sys/cdefs.h>
55__KERNEL_RCSID(0, "$NetBSD: process_machdep.c,v 1.71 2009/10/21 21:12:00 rmind Exp $");
56
57#include "opt_vm86.h"
58#include "opt_ptrace.h"
59#include "npx.h"
60
61#include <sys/param.h>
62#include <sys/systm.h>
63#include <sys/time.h>
64#include <sys/kernel.h>
65#include <sys/proc.h>
66#include <sys/vnode.h>
67#include <sys/ptrace.h>
68
69#include <uvm/uvm_extern.h>
70
71#include <machine/psl.h>
72#include <machine/reg.h>
73#include <machine/segments.h>
74
75#ifdef VM86
76#include <machine/vm86.h>
77#endif
78
79static inline struct trapframe *
80process_frame(struct lwp *l)
81{
82
83	return (l->l_md.md_regs);
84}
85
86static inline union savefpu *
87process_fpframe(struct lwp *l)
88{
89	struct pcb *pcb = lwp_getpcb(l);
90
91	return &pcb->pcb_savefpu;
92}
93
94static int
95xmm_to_s87_tag(const uint8_t *fpac, int regno, uint8_t tw)
96{
97	static const uint8_t empty_significand[8] = { 0 };
98	int tag;
99	uint16_t exponent;
100
101	if (tw & (1U << regno)) {
102		exponent = fpac[8] | (fpac[9] << 8);
103		switch (exponent) {
104		case 0x7fff:
105			tag = 2;
106			break;
107
108		case 0x0000:
109			if (memcmp(empty_significand, fpac,
110				   sizeof(empty_significand)) == 0)
111				tag = 1;
112			else
113				tag = 2;
114			break;
115
116		default:
117			if ((fpac[7] & 0x80) == 0)
118				tag = 2;
119			else
120				tag = 0;
121			break;
122		}
123	} else
124		tag = 3;
125
126	return (tag);
127}
128
129void
130process_xmm_to_s87(const struct savexmm *sxmm, struct save87 *s87)
131{
132	int i;
133
134	/* FPU control/status */
135	s87->sv_env.en_cw = sxmm->sv_env.en_cw;
136	s87->sv_env.en_sw = sxmm->sv_env.en_sw;
137	/* tag word handled below */
138	s87->sv_env.en_fip = sxmm->sv_env.en_fip;
139	s87->sv_env.en_fcs = sxmm->sv_env.en_fcs;
140	s87->sv_env.en_opcode = sxmm->sv_env.en_opcode;
141	s87->sv_env.en_foo = sxmm->sv_env.en_foo;
142	s87->sv_env.en_fos = sxmm->sv_env.en_fos;
143
144	/* Tag word and registers. */
145	s87->sv_env.en_tw = 0;
146	s87->sv_ex_tw = 0;
147	for (i = 0; i < 8; i++) {
148		s87->sv_env.en_tw |=
149		    (xmm_to_s87_tag(sxmm->sv_ac[i].fp_bytes, i,
150		     sxmm->sv_env.en_tw) << (i * 2));
151
152		s87->sv_ex_tw |=
153		    (xmm_to_s87_tag(sxmm->sv_ac[i].fp_bytes, i,
154		     sxmm->sv_ex_tw) << (i * 2));
155
156		memcpy(&s87->sv_ac[i].fp_bytes, &sxmm->sv_ac[i].fp_bytes,
157		    sizeof(s87->sv_ac[i].fp_bytes));
158	}
159
160	s87->sv_ex_sw = sxmm->sv_ex_sw;
161}
162
163void
164process_s87_to_xmm(const struct save87 *s87, struct savexmm *sxmm)
165{
166	int i;
167
168	/* FPU control/status */
169	sxmm->sv_env.en_cw = s87->sv_env.en_cw;
170	sxmm->sv_env.en_sw = s87->sv_env.en_sw;
171	/* tag word handled below */
172	sxmm->sv_env.en_fip = s87->sv_env.en_fip;
173	sxmm->sv_env.en_fcs = s87->sv_env.en_fcs;
174	sxmm->sv_env.en_opcode = s87->sv_env.en_opcode;
175	sxmm->sv_env.en_foo = s87->sv_env.en_foo;
176	sxmm->sv_env.en_fos = s87->sv_env.en_fos;
177
178	/* Tag word and registers. */
179	for (i = 0; i < 8; i++) {
180		if (((s87->sv_env.en_tw >> (i * 2)) & 3) == 3)
181			sxmm->sv_env.en_tw &= ~(1U << i);
182		else
183			sxmm->sv_env.en_tw |= (1U << i);
184
185#if 0
186		/*
187		 * Software-only word not provided by the userland fpreg
188		 * structure.
189		 */
190		if (((s87->sv_ex_tw >> (i * 2)) & 3) == 3)
191			sxmm->sv_ex_tw &= ~(1U << i);
192		else
193			sxmm->sv_ex_tw |= (1U << i);
194#endif
195
196		memcpy(&sxmm->sv_ac[i].fp_bytes, &s87->sv_ac[i].fp_bytes,
197		    sizeof(sxmm->sv_ac[i].fp_bytes));
198	}
199#if 0
200	/*
201	 * Software-only word not provided by the userland fpreg
202	 * structure.
203	 */
204	sxmm->sv_ex_sw = s87->sv_ex_sw;
205#endif
206}
207
208int
209process_read_regs(struct lwp *l, struct reg *regs)
210{
211	struct trapframe *tf = process_frame(l);
212
213#ifdef VM86
214	if (tf->tf_eflags & PSL_VM) {
215		regs->r_gs = tf->tf_vm86_gs;
216		regs->r_fs = tf->tf_vm86_fs;
217		regs->r_es = tf->tf_vm86_es;
218		regs->r_ds = tf->tf_vm86_ds;
219		regs->r_eflags = get_vflags(l);
220	} else
221#endif
222	{
223		regs->r_gs = tf->tf_gs & 0xffff;
224		regs->r_fs = tf->tf_fs & 0xffff;
225		regs->r_es = tf->tf_es & 0xffff;
226		regs->r_ds = tf->tf_ds & 0xffff;
227		regs->r_eflags = tf->tf_eflags;
228	}
229	regs->r_edi = tf->tf_edi;
230	regs->r_esi = tf->tf_esi;
231	regs->r_ebp = tf->tf_ebp;
232	regs->r_ebx = tf->tf_ebx;
233	regs->r_edx = tf->tf_edx;
234	regs->r_ecx = tf->tf_ecx;
235	regs->r_eax = tf->tf_eax;
236	regs->r_eip = tf->tf_eip;
237	regs->r_cs = tf->tf_cs & 0xffff;
238	regs->r_esp = tf->tf_esp;
239	regs->r_ss = tf->tf_ss & 0xffff;
240
241	return (0);
242}
243
244int
245process_read_fpregs(struct lwp *l, struct fpreg *regs)
246{
247	union savefpu *frame = process_fpframe(l);
248
249	if (l->l_md.md_flags & MDL_USEDFPU) {
250#if NNPX > 0
251		npxsave_lwp(l, true);
252#endif
253	} else {
254		/*
255		 * Fake a FNINIT.
256		 * The initial control word was already set by setregs(), so
257		 * save it temporarily.
258		 */
259		if (i386_use_fxsave) {
260			uint32_t mxcsr = frame->sv_xmm.sv_env.en_mxcsr;
261			uint16_t cw = frame->sv_xmm.sv_env.en_cw;
262
263			/* XXX Don't zero XMM regs? */
264			memset(&frame->sv_xmm, 0, sizeof(frame->sv_xmm));
265			frame->sv_xmm.sv_env.en_cw = cw;
266			frame->sv_xmm.sv_env.en_mxcsr = mxcsr;
267			frame->sv_xmm.sv_env.en_sw = 0x0000;
268			frame->sv_xmm.sv_env.en_tw = 0x00;
269		} else {
270			uint16_t cw = frame->sv_87.sv_env.en_cw;
271
272			memset(&frame->sv_87, 0, sizeof(frame->sv_87));
273			frame->sv_87.sv_env.en_cw = cw;
274			frame->sv_87.sv_env.en_sw = 0x0000;
275			frame->sv_87.sv_env.en_tw = 0xffff;
276		}
277		l->l_md.md_flags |= MDL_USEDFPU;
278	}
279
280	if (i386_use_fxsave) {
281		struct save87 s87;
282
283		/* XXX Yuck */
284		process_xmm_to_s87(&frame->sv_xmm, &s87);
285		memcpy(regs, &s87, sizeof(*regs));
286	} else
287		memcpy(regs, &frame->sv_87, sizeof(*regs));
288	return (0);
289}
290
291#ifdef PTRACE
292int
293process_write_regs(struct lwp *l, const struct reg *regs)
294{
295	struct trapframe *tf = process_frame(l);
296
297#ifdef VM86
298	if (regs->r_eflags & PSL_VM) {
299		void syscall_vm86(struct trapframe *);
300
301		tf->tf_vm86_gs = regs->r_gs;
302		tf->tf_vm86_fs = regs->r_fs;
303		tf->tf_vm86_es = regs->r_es;
304		tf->tf_vm86_ds = regs->r_ds;
305		set_vflags(l, regs->r_eflags);
306		/*
307		 * Make sure that attempts at system calls from vm86
308		 * mode die horribly.
309		 */
310		l->l_proc->p_md.md_syscall = syscall_vm86;
311	} else
312#endif
313	{
314		/*
315		 * Check for security violations.
316		 */
317		if (((regs->r_eflags ^ tf->tf_eflags) & PSL_USERSTATIC) != 0 ||
318		    !USERMODE(regs->r_cs, regs->r_eflags))
319			return (EINVAL);
320
321		tf->tf_gs = regs->r_gs;
322		tf->tf_fs = regs->r_fs;
323		tf->tf_es = regs->r_es;
324		tf->tf_ds = regs->r_ds;
325#ifdef VM86
326		/* Restore normal syscall handler */
327		if (tf->tf_eflags & PSL_VM)
328			(*l->l_proc->p_emul->e_syscall_intern)(l->l_proc);
329#endif
330		tf->tf_eflags = regs->r_eflags;
331	}
332	tf->tf_edi = regs->r_edi;
333	tf->tf_esi = regs->r_esi;
334	tf->tf_ebp = regs->r_ebp;
335	tf->tf_ebx = regs->r_ebx;
336	tf->tf_edx = regs->r_edx;
337	tf->tf_ecx = regs->r_ecx;
338	tf->tf_eax = regs->r_eax;
339	tf->tf_eip = regs->r_eip;
340	tf->tf_cs = regs->r_cs;
341	tf->tf_esp = regs->r_esp;
342	tf->tf_ss = regs->r_ss;
343
344	return (0);
345}
346
347int
348process_write_fpregs(struct lwp *l, const struct fpreg *regs)
349{
350	union savefpu *frame = process_fpframe(l);
351
352	if (l->l_md.md_flags & MDL_USEDFPU) {
353#if NNPX > 0
354		npxsave_lwp(l, false);
355#endif
356	} else {
357		l->l_md.md_flags |= MDL_USEDFPU;
358	}
359
360	if (i386_use_fxsave) {
361		struct save87 s87;
362
363		/* XXX Yuck. */
364		memcpy(&s87, regs, sizeof(*regs));
365		process_s87_to_xmm(&s87, &frame->sv_xmm);
366	} else
367		memcpy(&frame->sv_87, regs, sizeof(*regs));
368	return (0);
369}
370
371int
372process_sstep(struct lwp *l, int sstep)
373{
374	struct trapframe *tf = process_frame(l);
375
376	if (sstep)
377		tf->tf_eflags |= PSL_T;
378	else
379		tf->tf_eflags &= ~PSL_T;
380
381	return (0);
382}
383
384int
385process_set_pc(struct lwp *l, void *addr)
386{
387	struct trapframe *tf = process_frame(l);
388
389	tf->tf_eip = (int)addr;
390
391	return (0);
392}
393
394#ifdef __HAVE_PTRACE_MACHDEP
395static int
396process_machdep_read_xmmregs(struct lwp *l, struct xmmregs *regs)
397{
398	union savefpu *frame = process_fpframe(l);
399
400	if (i386_use_fxsave == 0)
401		return (EINVAL);
402
403	if (l->l_md.md_flags & MDL_USEDFPU) {
404#if NNPX > 0
405		struct pcb *pcb = lwp_getpcb(l);
406
407		if (pcb->pcb_fpcpu != NULL) {
408			npxsave_lwp(l, true);
409		}
410#endif
411	} else {
412		/*
413		 * Fake a FNINIT.
414		 * The initial control word was already set by setregs(),
415		 * so save it temporarily.
416		 */
417		uint32_t mxcsr = frame->sv_xmm.sv_env.en_mxcsr;
418		uint16_t cw = frame->sv_xmm.sv_env.en_cw;
419
420		/* XXX Don't zero XMM regs? */
421		memset(&frame->sv_xmm, 0, sizeof(frame->sv_xmm));
422		frame->sv_xmm.sv_env.en_cw = cw;
423		frame->sv_xmm.sv_env.en_mxcsr = mxcsr;
424		frame->sv_xmm.sv_env.en_sw = 0x0000;
425		frame->sv_xmm.sv_env.en_tw = 0x00;
426
427		l->l_md.md_flags |= MDL_USEDFPU;
428	}
429
430	memcpy(regs, &frame->sv_xmm, sizeof(*regs));
431	return (0);
432}
433
434static int
435process_machdep_write_xmmregs(struct lwp *l, struct xmmregs *regs)
436{
437	union savefpu *frame = process_fpframe(l);
438
439	if (i386_use_fxsave == 0)
440		return (EINVAL);
441
442	if (l->l_md.md_flags & MDL_USEDFPU) {
443#if NNPX > 0
444		struct pcb *pcb = lwp_getpcb(l);
445
446		/* If we were using the FPU, drop it. */
447		if (pcb->pcb_fpcpu != NULL) {
448			npxsave_lwp(l, false);
449		}
450#endif
451	} else {
452		l->l_md.md_flags |= MDL_USEDFPU;
453	}
454
455	memcpy(&frame->sv_xmm, regs, sizeof(*regs));
456	return (0);
457}
458
459int
460ptrace_machdep_dorequest(
461    struct lwp *l,
462    struct lwp *lt,
463    int req,
464    void *addr,
465    int data
466)
467{
468	struct uio uio;
469	struct iovec iov;
470	int write = 0;
471
472	switch (req) {
473	case PT_SETXMMREGS:
474		write = 1;
475
476	case PT_GETXMMREGS:
477		/* write = 0 done above. */
478		if (!process_machdep_validxmmregs(lt->l_proc))
479			return (EINVAL);
480		else {
481			struct vmspace *vm;
482			int error;
483
484			error = proc_vmspace_getref(l->l_proc, &vm);
485			if (error) {
486				return error;
487			}
488			iov.iov_base = addr;
489			iov.iov_len = sizeof(struct xmmregs);
490			uio.uio_iov = &iov;
491			uio.uio_iovcnt = 1;
492			uio.uio_offset = 0;
493			uio.uio_resid = sizeof(struct xmmregs);
494			uio.uio_rw = write ? UIO_WRITE : UIO_READ;
495			uio.uio_vmspace = vm;
496			error = process_machdep_doxmmregs(l, lt, &uio);
497			uvmspace_free(vm);
498			return error;
499		}
500	}
501
502#ifdef DIAGNOSTIC
503	panic("ptrace_machdep: impossible");
504#endif
505
506	return (0);
507}
508
509/*
510 * The following functions are used by both ptrace(2) and procfs.
511 */
512
513int
514process_machdep_doxmmregs(struct lwp *curl, struct lwp *l, struct uio *uio)
515	/* curl:		 tracer */
516	/* l:			 traced */
517{
518	int error;
519	struct xmmregs r;
520	char *kv;
521	int kl;
522
523	kl = sizeof(r);
524	kv = (char *) &r;
525
526	kv += uio->uio_offset;
527	kl -= uio->uio_offset;
528	if (kl > uio->uio_resid)
529		kl = uio->uio_resid;
530
531	if (kl < 0)
532		error = EINVAL;
533	else
534		error = process_machdep_read_xmmregs(l, &r);
535	if (error == 0)
536		error = uiomove(kv, kl, uio);
537	if (error == 0 && uio->uio_rw == UIO_WRITE) {
538		if (l->l_proc->p_stat != SSTOP)
539			error = EBUSY;
540		else
541			error = process_machdep_write_xmmregs(l, &r);
542	}
543
544	uio->uio_offset = 0;
545	return (error);
546}
547
548int
549process_machdep_validxmmregs(struct proc *p)
550{
551
552	if (p->p_flag & PK_SYSTEM)
553		return (0);
554
555	return (i386_use_fxsave);
556}
557#endif /* __HAVE_PTRACE_MACHDEP */
558#endif /* PTRACE */
559