xref: /netbsd/sys/arch/i386/i386/process_machdep.c (revision c4a72b64)
1 /*	$NetBSD: process_machdep.c,v 1.45 2002/10/01 12:56:59 fvdl 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.45 2002/10/01 12:56:59 fvdl 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 		npxsave_proc(p, 1);
262 #endif
263 	} else {
264 		/*
265 		 * Fake a FNINIT.
266 		 * The initial control word was already set by setregs(), so
267 		 * save it temporarily.
268 		 */
269 		if (i386_use_fxsave) {
270 			uint32_t mxcsr = frame->sv_xmm.sv_env.en_mxcsr;
271 			uint16_t cw = frame->sv_xmm.sv_env.en_cw;
272 
273 			/* XXX Don't zero XMM regs? */
274 			memset(&frame->sv_xmm, 0, sizeof(frame->sv_xmm));
275 			frame->sv_xmm.sv_env.en_cw = cw;
276 			frame->sv_xmm.sv_env.en_mxcsr = mxcsr;
277 			frame->sv_xmm.sv_env.en_sw = 0x0000;
278 			frame->sv_xmm.sv_env.en_tw = 0x00;
279 		} else {
280 			uint16_t cw = frame->sv_87.sv_env.en_cw;
281 
282 			memset(&frame->sv_87, 0, sizeof(frame->sv_87));
283 			frame->sv_87.sv_env.en_cw = cw;
284 			frame->sv_87.sv_env.en_sw = 0x0000;
285 			frame->sv_87.sv_env.en_tw = 0xffff;
286 		}
287 		p->p_md.md_flags |= MDP_USEDFPU;
288 	}
289 
290 	if (i386_use_fxsave) {
291 		struct save87 s87;
292 
293 		/* XXX Yuck */
294 		process_xmm_to_s87(&frame->sv_xmm, &s87);
295 		memcpy(regs, &s87, sizeof(*regs));
296 	} else
297 		memcpy(regs, &frame->sv_87, sizeof(*regs));
298 	return (0);
299 }
300 
301 int
302 process_write_regs(p, regs)
303 	struct proc *p;
304 	struct reg *regs;
305 {
306 	struct trapframe *tf = process_frame(p);
307 
308 #ifdef VM86
309 	if (regs->r_eflags & PSL_VM) {
310 		void syscall_vm86 __P((struct trapframe));
311 
312 		tf->tf_vm86_gs = regs->r_gs;
313 		tf->tf_vm86_fs = regs->r_fs;
314 		tf->tf_vm86_es = regs->r_es;
315 		tf->tf_vm86_ds = regs->r_ds;
316 		set_vflags(p, regs->r_eflags);
317 		/*
318 		 * Make sure that attempts at system calls from vm86
319 		 * mode die horribly.
320 		 */
321 		p->p_md.md_syscall = syscall_vm86;
322 	} else
323 #endif
324 	{
325 		/*
326 		 * Check for security violations.
327 		 */
328 		if (((regs->r_eflags ^ tf->tf_eflags) & PSL_USERSTATIC) != 0 ||
329 		    !USERMODE(regs->r_cs, regs->r_eflags))
330 			return (EINVAL);
331 
332 		tf->tf_gs = regs->r_gs;
333 		tf->tf_fs = regs->r_fs;
334 		tf->tf_es = regs->r_es;
335 		tf->tf_ds = regs->r_ds;
336 #ifdef VM86
337 		/* Restore normal syscall handler */
338 		if (tf->tf_eflags & PSL_VM)
339 			(*p->p_emul->e_syscall_intern)(p);
340 #endif
341 		tf->tf_eflags = regs->r_eflags;
342 	}
343 	tf->tf_edi = regs->r_edi;
344 	tf->tf_esi = regs->r_esi;
345 	tf->tf_ebp = regs->r_ebp;
346 	tf->tf_ebx = regs->r_ebx;
347 	tf->tf_edx = regs->r_edx;
348 	tf->tf_ecx = regs->r_ecx;
349 	tf->tf_eax = regs->r_eax;
350 	tf->tf_eip = regs->r_eip;
351 	tf->tf_cs = regs->r_cs;
352 	tf->tf_esp = regs->r_esp;
353 	tf->tf_ss = regs->r_ss;
354 
355 	return (0);
356 }
357 
358 int
359 process_write_fpregs(p, regs)
360 	struct proc *p;
361 	struct fpreg *regs;
362 {
363 	union savefpu *frame = process_fpframe(p);
364 
365 	if (p->p_md.md_flags & MDP_USEDFPU) {
366 #if NNPX > 0
367 		npxsave_proc(p, 0);
368 #endif
369 	} else {
370 		p->p_md.md_flags |= MDP_USEDFPU;
371 	}
372 
373 	if (i386_use_fxsave) {
374 		struct save87 s87;
375 
376 		/* XXX Yuck. */
377 		memcpy(&s87, regs, sizeof(*regs));
378 		process_s87_to_xmm(&s87, &frame->sv_xmm);
379 	} else
380 		memcpy(&frame->sv_87, regs, sizeof(*regs));
381 	return (0);
382 }
383 
384 int
385 process_sstep(p, sstep)
386 	struct proc *p;
387 {
388 	struct trapframe *tf = process_frame(p);
389 
390 	if (sstep)
391 		tf->tf_eflags |= PSL_T;
392 	else
393 		tf->tf_eflags &= ~PSL_T;
394 
395 	return (0);
396 }
397 
398 int
399 process_set_pc(p, addr)
400 	struct proc *p;
401 	caddr_t addr;
402 {
403 	struct trapframe *tf = process_frame(p);
404 
405 	tf->tf_eip = (int)addr;
406 
407 	return (0);
408 }
409 
410 #ifdef __HAVE_PTRACE_MACHDEP
411 static int
412 process_machdep_read_xmmregs(struct proc *p, struct xmmregs *regs)
413 {
414 	union savefpu *frame = process_fpframe(p);
415 
416 	if (i386_use_fxsave == 0)
417 		return (EINVAL);
418 
419 	if (p->p_md.md_flags & MDP_USEDFPU) {
420 #if NNPX > 0
421 		if (p->p_addr->u_pcb.pcb_fpcpu != NULL)
422 			npxsave_proc(p, 1);
423 #endif
424 	} else {
425 		/*
426 		 * Fake a FNINIT.
427 		 * The initial control word was already set by setregs(),
428 		 * so save it temporarily.
429 		 */
430 		uint32_t mxcsr = frame->sv_xmm.sv_env.en_mxcsr;
431 		uint16_t cw = frame->sv_xmm.sv_env.en_cw;
432 
433 		/* XXX Don't zero XMM regs? */
434 		memset(&frame->sv_xmm, 0, sizeof(frame->sv_xmm));
435 		frame->sv_xmm.sv_env.en_cw = cw;
436 		frame->sv_xmm.sv_env.en_mxcsr = mxcsr;
437 		frame->sv_xmm.sv_env.en_sw = 0x0000;
438 		frame->sv_xmm.sv_env.en_tw = 0x00;
439 
440 		p->p_md.md_flags |= MDP_USEDFPU;
441 	}
442 
443 	memcpy(regs, &frame->sv_xmm, sizeof(*regs));
444 	return (0);
445 }
446 
447 static int
448 process_machdep_write_xmmregs(struct proc *p, struct xmmregs *regs)
449 {
450 	union savefpu *frame = process_fpframe(p);
451 
452 	if (i386_use_fxsave == 0)
453 		return (EINVAL);
454 
455 	if (p->p_md.md_flags & MDP_USEDFPU) {
456 #if NNPX > 0
457 		/* If we were using the FPU, drop it. */
458 		if (p->p_addr->u_pcb.pcb_fpcpu != NULL)
459 			npxsave_proc(p, 0);
460 #endif
461 	} else {
462 		p->p_md.md_flags |= MDP_USEDFPU;
463 	}
464 
465 	memcpy(&frame->sv_xmm, regs, sizeof(*regs));
466 	return (0);
467 }
468 
469 int
470 ptrace_machdep_dorequest(p, t, req, addr, data)
471 	struct proc *p, *t;
472 	int req;
473 	caddr_t addr;
474 	int data;
475 {
476 	struct uio uio;
477 	struct iovec iov;
478 	int write = 0;
479 
480 	switch (req) {
481 	case PT_SETXMMREGS:
482 		write = 1;
483 
484 	case PT_GETXMMREGS:
485 		/* write = 0 done above. */
486 		if (!process_machdep_validxmmregs(t))
487 			return (EINVAL);
488 		else {
489 			iov.iov_base = addr;
490 			iov.iov_len = sizeof(struct xmmregs);
491 			uio.uio_iov = &iov;
492 			uio.uio_iovcnt = 1;
493 			uio.uio_offset = 0;
494 			uio.uio_resid = sizeof(struct xmmregs);
495 			uio.uio_segflg = UIO_USERSPACE;
496 			uio.uio_rw = write ? UIO_WRITE : UIO_READ;
497 			uio.uio_procp = p;
498 			return (process_machdep_doxmmregs(p, t, &uio));
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 
513 int
514 process_machdep_doxmmregs(curp, p, uio)
515 	struct proc *curp;		/* tracer */
516 	struct proc *p;			/* traced */
517 	struct uio *uio;
518 {
519 	int error;
520 	struct xmmregs r;
521 	char *kv;
522 	int kl;
523 
524 	if ((error = process_checkioperm(curp, p)) != 0)
525 		return (error);
526 
527 	kl = sizeof(r);
528 	kv = (char *) &r;
529 
530 	kv += uio->uio_offset;
531 	kl -= uio->uio_offset;
532 	if (kl > uio->uio_resid)
533 		kl = uio->uio_resid;
534 
535 	PHOLD(p);
536 
537 	if (kl < 0)
538 		error = EINVAL;
539 	else
540 		error = process_machdep_read_xmmregs(p, &r);
541 	if (error == 0)
542 		error = uiomove(kv, kl, uio);
543 	if (error == 0 && uio->uio_rw == UIO_WRITE) {
544 		if (p->p_stat != SSTOP)
545 			error = EBUSY;
546 		else
547 			error = process_machdep_write_xmmregs(p, &r);
548 	}
549 
550 	PRELE(p);
551 
552 	uio->uio_offset = 0;
553 	return (error);
554 }
555 
556 int
557 process_machdep_validxmmregs(p)
558 	struct proc *p;
559 {
560 
561 	if (p->p_flag & P_SYSTEM)
562 		return (0);
563 
564 	return (i386_use_fxsave);
565 }
566 #endif /* __HAVE_PTRACE_MACHDEP */
567