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