xref: /386bsd/usr/src/kernel/kern/i386/cpu.c (revision a2142627)
1 /*
2  * Copyright (c) 1989, 1990, 1991, 1992, 1993, 1994 William F. Jolitz, TeleMuse
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This software is a component of "386BSD" developed by
16  *	William F. Jolitz, TeleMuse.
17  * 4. Neither the name of the developer nor the name "386BSD"
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  * 5. Non-commercial distribution of this complete file in either source and/or
21  *    binary form at no charge to the user (such as from an official Internet
22  *    archive site) is permitted.
23  * 6. Commercial distribution and sale of this complete file in either source
24  *    and/or binary form on any media, including that of floppies, tape, or
25  *    CD-ROM, or through a per-charge download such as that of a BBS, is not
26  *    permitted without specific prior written permission.
27  * 7. Non-commercial and/or commercial distribution of an incomplete, altered,
28  *    or otherwise modified file in either source and/or binary form is not
29  *    permitted.
30  *
31  * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
32  * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
33  * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
34  * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
35  * NOT MAKE USE OF THIS WORK.
36  *
37  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
38  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40  * ARE DISCLAIMED.  IN NO EVENT SHALL THE DEVELOPER BE LIABLE
41  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  *
49  * $Id: cpu.c,v 1.1 94/10/19 17:39:55 bill Exp $
50  *
51  * This file contains functions that implement the machine-dependant
52  * portions of the kernel's internal facilities.
53  */
54 
55 #include "sys/param.h"
56 #include "sys/user.h"
57 #include "signalvar.h"
58 /* #include "malloc.h" */
59 #include "proc.h"
60 #include "kmem.h"
61 #include "prototypes.h"
62 
63 
64 #include "machine/cpu.h"
65 #define IPCREG
66 #include "machine/reg.h"
67 #include "machine/psl.h"
68 
69 #include "segments.h"
70 #include "specialreg.h"
71 #include "sigframe.h"
72 
73 /*
74  * Implement the innermost part of a fork() operation, by building
75  * a new kernel execution thread (consisting of a process control block
76  * and new kernel stack). When the new thread runs, it's kernel context will
77  * appear to be identical to it's copy, except that in the copy's thread
78  * cpu_tfork() will return the value of 0, and in the new thread it will
79  * return the value of 1.
80  */
81 int
82 cpu_tfork(struct proc *p1, register struct proc *p2)
83 {
84  	int diff;
85 	struct user *up;
86 	unsigned *fp, nfp;
87 
88 #ifdef DIAGNOSTICx	/*XXX proc0 not initialized */
89 	if (p2->p_stksz < sizeof(struct pcb))
90 		panic("invalid kernel stack size");
91 #endif
92 	p2->p_addr = up = (struct user *) kmem_alloc(kernel_map, ctob(UPAGES), 0);
93 	p2->p_stksz = ctob(UPAGES);	/* XXX */
94 	p1->p_stksz = ctob(UPAGES);	/* XXX */
95  	diff = ((caddr_t)p2->p_addr + p2->p_stksz)
96 		- ((caddr_t)p1->p_addr + p1->p_stksz);
97 
98 	/* copy the pcb to the new process. */
99 	up->u_pcb = p1->p_addr->u_pcb;
100 
101 	/* update the new pcb to reflect this process current status. */
102 	/* if (curproc == p1) { */
103 		asm volatile ("movl %%esp, %0" : "=m" (up->u_pcb.pcb_tss.tss_esp));
104 		asm volatile ("movl %%ebp, %0" : "=m" (up->u_pcb.pcb_tss.tss_ebp));
105 	/* } */
106 
107 	up->u_pcb.pcb_tss.tss_esp += diff;
108 	up->u_pcb.pcb_tss.tss_ebp += diff;
109 
110 	/* copy the kernel stack */
111 	asm volatile ("  cld ; repe ; movsl " : :
112 	    "D" ((caddr_t)up->u_pcb.pcb_tss.tss_esp /*+ diff*/),
113 	    "S" ((caddr_t)up->u_pcb.pcb_tss.tss_esp -  diff),
114 	    "c" (((unsigned)p1->p_addr + p1->p_stksz
115 		- (up->u_pcb.pcb_tss.tss_esp- diff) ) / sizeof(int)));
116 
117 	/* relocate the stack and frame pointers to the new kernel stack. */
118 	/*up->u_pcb.pcb_tss.tss_esp += diff;
119 	up->u_pcb.pcb_tss.tss_ebp += diff;*/
120 
121 	/* relocate the frame pointers in the new kernel stack */
122 	fp = (unsigned *) up->u_pcb.pcb_tss.tss_ebp;
123 	while ((nfp = *fp) >= (unsigned) fp - (unsigned) diff) {
124 		nfp += diff;
125 		*fp = nfp;
126 		fp = (unsigned *)nfp;
127 	}
128 
129 	/* relocate md_reg pointer. */
130 	(int)p2->p_md.md_regs = (int) p1->p_md.md_regs + diff;
131 	p2->p_md.md_flags = 0;
132 
133 	/* allocate a TSS for this thread. */
134 	alloctss(p2);
135 
136 	/* set the stack's starting location on entry into kernel mode. */
137 	up->u_pcb.pcb_tss.tss_esp0 = (unsigned) up + p2->p_stksz;
138 
139 	/* inherit only the shared kernel address space. */
140 	up->u_pcb.pcb_ptd = KernelPTD;
141 
142 	/* update the new pcb to reflect this process current status. */
143 	/* if (curproc == p1) { */
144 		asm volatile ("movl %%ebx, %0" : "=m" (up->u_pcb.pcb_tss.tss_ebx));
145 		asm volatile ("movl %%edi, %0" : "=m" (up->u_pcb.pcb_tss.tss_edi));
146 		asm volatile ("movl %%esi, %0" : "=m" (up->u_pcb.pcb_tss.tss_esi));
147 	/* } */
148 
149 	/* set location of the new thread's first instruction. */
150 	asm volatile ("movl $1f, %0; 1:" : "=m" (up->u_pcb.pcb_tss.tss_eip));
151 
152 	/* are we the new thread? */
153 	if (curproc == 0) {
154 asm(".globl _tfork_child ; _tfork_child: ");
155 		curproc = p2;
156 		splnone();
157 		return (1);	/* child */
158 	}
159 	return (0);		/* parent */
160 }
161 
162 /*
163  * Release any remaining resources of this thread and pass control
164  * to next process or thread.
165  */
166 volatile void
167 cpu_texit(register struct proc *p)
168 {
169 	extern int Exit_stack;
170 
171 	/* release coprocessor if we have it */
172 	if (p == npxproc)
173 		npxproc = 0;
174 
175 	/* release the tss, and cut over to a temporary static tss(exit_tss) */
176 	freetss((sel_t)p->p_md.md_tsel);
177 
178 	/* change our stack to temporary exit stack. */
179 	asm ("movl $%0, %%esp" : : "m"(Exit_stack));
180 
181 	/* drop pcb and kernel stack. No more automatic references allowed. */
182 	/* if (p->p_stksz >= NBPG) */
183 		kmem_free(kernel_map, (vm_offset_t)p->p_addr, p->p_stksz);
184 	/* else
185 		free(p->p_addr, M_TEMP); */
186 
187 	/* context switch, never to return */
188 	swtch();
189 #ifdef	DIAGNOSTIC
190 	panic("cpu_exit");
191 #endif
192 }
193 
194 /*
195  * Setup processes registers for execve()
196  */
197 void
198 cpu_execsetregs(struct proc *p, caddr_t eip, caddr_t esp)
199 {
200 
201 	p->p_md.md_regs[sESP] = (int) esp;
202 	p->p_md.md_regs[sEBP] = 0;	/* bottom of the fp chain */
203 	p->p_md.md_regs[sEIP] = (int) eip;
204 
205 	p->p_addr->u_pcb.pcb_flags = 0;	/* no fp at all */
206 }
207 
208 /*
209  * Send an POSIX signal to the program in the process indicated.
210  *
211  * [In this version, the process *must* be the current running process. -wfj]
212  *
213  * This function alters the state of the program by adding a special
214  * stack frame to the stack, which simulates a hardware interrupt to
215  * the program. When the program is restarted, it will continue execution
216  * in the signal handler associated with this signal. If the signal
217  * handler returns, it will be compelled by the sigcode(see locore.s)
218  * present in the user program to execute a sigreturn() (see below) to
219  * reload the register state information saved in the special stack frame.
220  *
221  * If frame cannot be created, return an error code.
222  * N.B. no checks are made for copy-on-write "stacks".
223  */
224 int
225 cpu_signal(struct proc *p, int sig, int mask)
226 {
227 	sig_t catcher;
228 	int *regs;
229 	struct sigframe *fp;
230 	struct sigacts *ps = p->p_sigacts;
231 	int oonstack, frmtrap;
232 	extern caddr_t sigcode;
233 	extern int szsigcode;
234 
235 #ifdef	DIAGNOSTIC
236 	if (curproc != p)
237 		panic("cpu_signal(): not current process");
238 #endif
239 	catcher = ps->ps_sigact[sig];
240 	regs = p->p_md.md_regs;
241         oonstack = ps->ps_onstack;
242 	frmtrap = p->p_addr->u_pcb.pcb_flags & FM_TRAP;
243 
244 	/*
245 	 * Find the base of the special signal stack frame. If a
246 	 * signal uses the special signal stack, and it's available,
247 	 * use that. Otherwise, use the program's stack.
248 	 */
249         if (ps->ps_onstack == 0 && (ps->ps_sigonstack & sigmask(sig))) {
250 		fp = (struct sigframe *)
251 		    (ps->ps_sigsp - sizeof(struct sigframe));
252                 ps->ps_onstack++;
253 	} else {
254 		/* could be in one of two places */
255 		if (frmtrap)
256 			fp = (struct sigframe *)(regs[tESP]
257 				- sizeof(struct sigframe));
258 		else
259 			fp = (struct sigframe *)(regs[sESP]
260 				- sizeof(struct sigframe));
261 	}
262 
263 	/* is signal the gauranteed bad signal, or is the new stack
264 	   frame inside the user program address space ? */
265 	if (catcher == BADSIG
266 	   || (unsigned) fp + sizeof(struct sigframe) >  USRSTACK) {
267 		asm("cpu_signal_err:");
268 		p->p_md.md_onfault = 0;
269 		return(EFAULT);
270 	}
271 
272 	/*
273 	 * point the fault vector at the error return, so that any
274 	 * failed user process address space reference will cause
275 	 * a terminal error.
276 	 */
277 	asm ("movl $cpu_signal_err, %0 "
278 		: "=o" (p->p_md.md_onfault));
279 
280 	/* construct the program visable portion of the signal frame. */
281 	fp->sf_signum = sig;
282 	fp->sf_code = ps->ps_code;
283 	fp->sf_scp = &fp->sf_sc;
284 	fp->sf_handler = catcher;
285 
286 	/* save scratch registers in the opaque portion of the signal frame. */
287 	if(frmtrap) {
288 		fp->sf_eax = regs[tEAX];
289 		fp->sf_edx = regs[tEDX];
290 		fp->sf_ecx = regs[tECX];
291 	} else {
292 		fp->sf_eax = regs[sEAX];
293 		fp->sf_edx = regs[sEDX];
294 		fp->sf_ecx = regs[sECX];
295 	}
296 
297 	/* record previous program state so sigreturn() can restore it. */
298 	fp->sf_sc.sc_onstack = oonstack;
299 	fp->sf_sc.sc_mask = mask;
300 	memcpy((caddr_t)fp->sf_sigcode, &sigcode, szsigcode);
301 	if(frmtrap) {
302 		fp->sf_sc.sc_sp = regs[tESP];
303 		fp->sf_sc.sc_fp = regs[tEBP];
304 		fp->sf_sc.sc_pc = regs[tEIP];
305 		fp->sf_sc.sc_ps = regs[tEFLAGS];
306 		regs[tESP] = (int)fp;
307 		regs[tEIP] = (int)fp->sf_sigcode;
308 	} else {
309 		fp->sf_sc.sc_sp = regs[sESP];
310 		fp->sf_sc.sc_fp = regs[sEBP];
311 		fp->sf_sc.sc_pc = regs[sEIP];
312 		fp->sf_sc.sc_ps = regs[sEFLAGS];
313 		regs[sESP] = (int)fp;
314 		regs[sEIP] = (int)fp->sf_sigcode;
315 	}
316 
317 	/* clear fault vector, return success */
318 	p->p_md.md_onfault = 0;
319 	return(0);
320 }
321 
322 /*
323  * Function to restore signal context prior to signal.
324  * This function is just an implementation detail to clean up
325  * the special stack frame installed by cpu_signal(). To avoid
326  * being spoofed, the contents of the frame are "sanity checked"
327  * to insure that the kernel program is not comprimised.
328  * This function is normally called from a hidden system call.
329  */
330 int
331 cpu_signalreturn(struct proc *p)
332 {	struct sigcontext *scp;
333 	struct sigframe *fp;
334 	int *regs = p->p_md.md_regs;
335 
336 	/* signal state is current top of stack contents */
337 	fp = (struct sigframe *) regs[sESP] ;
338 
339 	/* is new stack frame inside the user program address space ? */
340 	if ((unsigned) fp + sizeof(struct sigframe) >  USRSTACK) {
341 		asm("cpu_signalreturn_err:");
342 		p->p_md.md_onfault = 0;
343 		return(EFAULT);
344 	}
345 
346 	/*
347 	 * point the fault vector at the error return, so that any
348 	 * failed user process address space reference will cause
349 	 * a terminal error.
350 	 */
351 	asm ("movl $cpu_signalreturn_err, %0 " : "=o" (p->p_md.md_onfault));
352 
353 	/* check to see if this is a valid context generated by cpu_signal() */
354 	if (fp->sf_scp != &fp->sf_sc) {
355 		p->p_md.md_onfault = 0;
356 		return(EINVAL);
357 	}
358 	scp = fp->sf_scp;
359 	if ((scp->sc_ps & PSL_MBZ) != 0 || (scp->sc_ps & PSL_MBO) != PSL_MBO) {
360 		p->p_md.md_onfault = 0;
361 		return(EINVAL);
362 	}
363 
364 	/* restore previous program state and signal state */
365         p->p_sigacts->ps_onstack = scp->sc_onstack & 1;
366 	p->p_sigmask = scp->sc_mask & ~sigcantmask;
367 	regs[sEBP] = scp->sc_fp;
368 	regs[sESP] = scp->sc_sp;
369 	regs[sEIP] = scp->sc_pc;
370 	regs[sEFLAGS] = (scp->sc_ps | PSL_USERSET) & ~PSL_USERCLR;
371 
372 	/* restore scratch registers */
373 	regs[sEAX] = fp->sf_eax;
374 	regs[sEDX] = fp->sf_edx;
375 	regs[sECX] = fp->sf_ecx;
376 
377 	p->p_md.md_onfault = 0;
378 	return(EJUSTRETURN);
379 }
380 
381 /*
382  * Force reset the processor by invalidating the entire address space!
383  */
384 void
385 cpu_reset(void) {
386 
387 	/* force a shutdown by unmapping entire address space ! */
388 	(void)memset((caddr_t) PTD, 0, NBPG);
389 
390 	/* "good night, sweet prince .... <THUNK!>" */
391 	tlbflush();
392 
393 	asm("	movl	$0, %esp ");
394 
395 	/* NOTREACHED */
396 }
397 
398 static int sipcreg[NIPCREG] =
399   { 0,0,sEDI,sESI,sEBP,sEBX,sEDX,sECX,sEAX,sEIP,sCS,sEFLAGS,sESP,sSS };
400 static int ipcreg[NIPCREG] =
401   { tES,tDS,tEDI,tESI,tEBP,tEBX,tEDX,tECX,tEAX,tEIP,tCS,tEFLAGS,tESP,tSS };
402 
403 /*
404  * Return address of desired register for ptrace() XXX
405  */
406 int *
407 cpu_ptracereg(struct proc *p, int reg) {
408 
409 	/*if (p->p_addr->u_pcb.pcb_flags & FM_TRAP)
410 		return (p->p_md.md_regs + ipcreg[reg]);
411 	else
412 		return (p->p_md.md_regs + sipcreg[reg]); */
413 	return (p->p_md.md_regs + reg);
414 }
415 
416