swtch.s revision 3842
1/*-
2 * Copyright (c) 1990 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * William Jolitz.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 *	$Id: swtch.s,v 1.15 1994/10/02 04:45:35 davidg Exp $
37 */
38
39#include "npx.h"	/* for NNPX */
40#include "assym.s"	/* for preprocessor defines */
41#include <sys/errno.h>	/* for error codes */
42
43#include <machine/asmacros.h>	/* for miscellaneous assembly macros */
44#include <machine/spl.h>	/* for SWI_AST_MASK ... */
45#include <sys/rtprio.h>
46
47
48/*****************************************************************************/
49/* Scheduling                                                                */
50/*****************************************************************************/
51
52/*
53 * The following primitives manipulate the run queues.
54 * _whichqs tells which of the 32 queues _qs
55 * have processes in them.  setrunqueue puts processes into queues, Remrq
56 * removes them from queues.  The running process is on no queue,
57 * other processes are on a queue related to p->p_priority, divided by 4
58 * actually to shrink the 0-127 range of priorities into the 32 available
59 * queues.
60 */
61	.data
62	.globl	_curpcb, _whichqs, _whichrtqs, _whichidqs
63_curpcb:	.long	0			/* pointer to curproc's PCB area */
64_whichqs:	.long	0			/* which run queues have data */
65_whichrtqs:	.long	0			/* which realtime run queues have data */
66_whichidqs:	.long	0			/* which idletime run queues have data */
67
68	.globl	_qs,_cnt,_panic
69	.comm	_noproc,4
70	.comm	_runrun,4
71
72	.globl	_want_resched
73_want_resched:	.long	0			/* we need to re-run the scheduler */
74
75	.text
76/*
77 * setrunqueue(p)
78 *
79 * Call should be made at spl6(), and p->p_stat should be SRUN
80 */
81ENTRY(setrunqueue)
82	movl	4(%esp),%eax
83	cmpl	$0,P_BACK(%eax)			/* should not be on q already */
84	je	set1
85	pushl	$set2
86	call	_panic
87set1:
88	cmpw	$RTP_PRIO_NORMAL,P_RTPRIO_TYPE(%eax) /* normal priority process? */
89	je	set_nort
90
91	movzwl	P_RTPRIO_PRIO(%eax),%edx
92
93	cmpw	$RTP_PRIO_REALTIME,P_RTPRIO_TYPE(%eax) /* realtime priority? */
94	jne	set_id				/* must be idle priority */
95
96set_rt:
97	btsl	%edx,_whichrtqs			/* set q full bit */
98	shll	$3,%edx
99	addl	$_rtqs,%edx			/* locate q hdr */
100	movl	%edx,P_FORW(%eax)		/* link process on tail of q */
101	movl	P_BACK(%edx),%ecx
102	movl	%ecx,P_BACK(%eax)
103	movl	%eax,P_BACK(%edx)
104	movl	%eax,P_FORW(%ecx)
105	ret
106
107set_id:
108	btsl	%edx,_whichidqs			/* set q full bit */
109	shll	$3,%edx
110	addl	$_idqs,%edx			/* locate q hdr */
111	movl	%edx,P_FORW(%eax)		/* link process on tail of q */
112	movl	P_BACK(%edx),%ecx
113	movl	%ecx,P_BACK(%eax)
114	movl	%eax,P_BACK(%edx)
115	movl	%eax,P_FORW(%ecx)
116	ret
117
118set_nort:                    			/*  Normal (RTOFF) code */
119	movzbl	P_PRI(%eax),%edx
120	shrl	$2,%edx
121	btsl	%edx,_whichqs			/* set q full bit */
122	shll	$3,%edx
123	addl	$_qs,%edx			/* locate q hdr */
124	movl	%edx,P_FORW(%eax)		/* link process on tail of q */
125	movl	P_BACK(%edx),%ecx
126	movl	%ecx,P_BACK(%eax)
127	movl	%eax,P_BACK(%edx)
128	movl	%eax,P_FORW(%ecx)
129	ret
130
131set2:	.asciz	"setrunqueue"
132
133/*
134 * Remrq(p)
135 *
136 * Call should be made at spl6().
137 */
138ENTRY(remrq)
139	movl	4(%esp),%eax
140	cmpw	$RTP_PRIO_NORMAL,P_RTPRIO_TYPE(%eax) /* normal priority process? */
141	je	rem_nort
142
143	movzwl	P_RTPRIO_PRIO(%eax),%edx
144
145	cmpw	$RTP_PRIO_REALTIME,P_RTPRIO_TYPE(%eax) /* normal priority process? */
146	jne	rem_id
147
148	btrl	%edx,_whichrtqs			/* clear full bit, panic if clear already */
149	jb	rem1rt
150	pushl	$rem3rt
151	call	_panic
152rem1rt:
153	pushl	%edx
154	movl	P_FORW(%eax),%ecx		/* unlink process */
155	movl	P_BACK(%eax),%edx
156	movl	%edx,P_BACK(%ecx)
157	movl	P_BACK(%eax),%ecx
158	movl	P_FORW(%eax),%edx
159	movl	%edx,P_FORW(%ecx)
160	popl	%edx
161	movl	$_rtqs,%ecx
162	shll	$3,%edx
163	addl	%edx,%ecx
164	cmpl	P_FORW(%ecx),%ecx		/* q still has something? */
165	je	rem2rt
166	shrl	$3,%edx				/* yes, set bit as still full */
167	btsl	%edx,_whichrtqs
168rem2rt:
169	movl	$0,P_BACK(%eax)			/* zap reverse link to indicate off list */
170	ret
171rem_id:
172	btrl	%edx,_whichidqs			/* clear full bit, panic if clear already */
173	jb	rem1id
174	pushl	$rem3id
175	call	_panic
176rem1id:
177	pushl	%edx
178	movl	P_FORW(%eax),%ecx		/* unlink process */
179	movl	P_BACK(%eax),%edx
180	movl	%edx,P_BACK(%ecx)
181	movl	P_BACK(%eax),%ecx
182	movl	P_FORW(%eax),%edx
183	movl	%edx,P_FORW(%ecx)
184	popl	%edx
185	movl	$_idqs,%ecx
186	shll	$3,%edx
187	addl	%edx,%ecx
188	cmpl	P_FORW(%ecx),%ecx		/* q still has something? */
189	je	rem2id
190	shrl	$3,%edx				/* yes, set bit as still full */
191	btsl	%edx,_whichidqs
192rem2id:
193	movl	$0,P_BACK(%eax)			/* zap reverse link to indicate off list */
194	ret
195
196rem_nort:
197	movzbl	P_PRI(%eax),%edx
198	shrl	$2,%edx
199	btrl	%edx,_whichqs			/* clear full bit, panic if clear already */
200	jb	rem1
201	pushl	$rem3
202	call	_panic
203rem1:
204	pushl	%edx
205	movl	P_FORW(%eax),%ecx		/* unlink process */
206	movl	P_BACK(%eax),%edx
207	movl	%edx,P_BACK(%ecx)
208	movl	P_BACK(%eax),%ecx
209	movl	P_FORW(%eax),%edx
210	movl	%edx,P_FORW(%ecx)
211	popl	%edx
212	movl	$_qs,%ecx
213	shll	$3,%edx
214	addl	%edx,%ecx
215	cmpl	P_FORW(%ecx),%ecx		/* q still has something? */
216	je	rem2
217	shrl	$3,%edx				/* yes, set bit as still full */
218	btsl	%edx,_whichqs
219rem2:
220	movl	$0,P_BACK(%eax)			/* zap reverse link to indicate off list */
221	ret
222
223rem3:	.asciz	"remrq"
224rem3rt:	.asciz	"remrq.rt"
225rem3id:	.asciz	"remrq.id"
226sw0:	.asciz	"cpu_switch"
227
228/*
229 * When no processes are on the runq, cpu_switch() branches to _idle
230 * to wait for something to come ready.
231 */
232	ALIGN_TEXT
233_idle:
234	MCOUNT
235	movl	_IdlePTD,%ecx
236	movl	%ecx,%cr3
237	movl	$tmpstk,%esp
238	sti
239
240	/*
241	 * XXX callers of cpu_switch() do a bogus splclock().  Locking should
242	 * be left to cpu_switch().
243	 */
244	movl	$SWI_AST_MASK,_cpl
245	testl	$~SWI_AST_MASK,_ipending
246	je	idle_loop
247	call	_splz
248
249	ALIGN_TEXT
250idle_loop:
251	cli
252	cmpl	$0,_whichrtqs			/* real-time queue */
253	jne	sw1a
254	cmpl	$0,_whichqs			/* normal queue */
255	jne	nortqr
256	cmpl	$0,_whichidqs			/* 'idle' queue */
257	jne	idqr
258#ifdef APM
259	call	_apm_cpu_idle
260	call	_apm_cpu_busy
261#else
262	sti
263	hlt					/* wait for interrupt */
264#endif
265	jmp	idle_loop
266
267badsw:
268	pushl	$sw0
269	call	_panic
270	/*NOTREACHED*/
271
272/*
273 * cpu_switch()
274 */
275ENTRY(cpu_switch)
276	incl	_cnt+V_SWTCH
277
278	/* switch to new process. first, save context as needed */
279
280	movl	_curproc,%ecx
281
282	/* if no process to save, don't bother */
283	testl	%ecx,%ecx
284	je	sw1
285
286	movl	P_ADDR(%ecx),%ecx
287
288	movl	(%esp),%eax			/* Hardware registers */
289	movl	%eax,PCB_EIP(%ecx)
290	movl	%ebx,PCB_EBX(%ecx)
291	movl	%esp,PCB_ESP(%ecx)
292	movl	%ebp,PCB_EBP(%ecx)
293	movl	%esi,PCB_ESI(%ecx)
294	movl	%edi,PCB_EDI(%ecx)
295
296#if NNPX > 0
297	/* have we used fp, and need a save? */
298	mov	_curproc,%eax
299	cmp	%eax,_npxproc
300	jne	1f
301	pushl	%ecx				/* h/w bugs make saving complicated */
302	leal	PCB_SAVEFPU(%ecx),%eax
303	pushl	%eax
304	call	_npxsave			/* do it in a big C function */
305	popl	%eax
306	popl	%ecx
3071:
308#endif	/* NNPX > 0 */
309
310	movl	$0,_curproc			/*  out of process */
311
312#	movw	_cpl,%ax
313#	movw	%ax,PCB_IML(%ecx)		/* save ipl */
314
315	/* save is done, now choose a new process or idle */
316sw1:
317	cli
318sw1a:
319	movl    _whichrtqs,%edi			/* pick next p. from rtqs */
320	testl	%edi,%edi
321	jz	nortqr				/* no realtime procs */
322
323	/* XXX - bsf is sloow */
324	bsfl	%edi,%ebx			/* find a full q */
325	jz	nortqr				/* no proc on rt q - try normal ... */
326
327	/* XX update whichqs? */
328	btrl	%ebx,%edi			/* clear q full status */
329	leal	_rtqs(,%ebx,8),%eax		/* select q */
330	movl	%eax,%esi
331
332#ifdef        DIAGNOSTIC
333	cmpl	P_FORW(%eax),%eax		/* linked to self? (e.g. not on list) */
334	je	badsw				/* not possible */
335#endif
336
337	movl	P_FORW(%eax),%ecx		/* unlink from front of process q */
338	movl	P_FORW(%ecx),%edx
339	movl	%edx,P_FORW(%eax)
340	movl	P_BACK(%ecx),%eax
341	movl	%eax,P_BACK(%edx)
342
343	cmpl	P_FORW(%ecx),%esi		/* q empty */
344	je	rt3
345	btsl	%ebx,%edi			/* nope, set to indicate not empty */
346rt3:
347	movl	%edi,_whichrtqs			/* update q status */
348	jmp	swtch_com
349
350	/* old sw1a */
351/* Normal process priority's */
352nortqr:
353	movl	_whichqs,%edi
3542:
355	/* XXX - bsf is sloow */
356	bsfl	%edi,%ebx			/* find a full q */
357	jz	idqr				/* if none, idle */
358
359	/* XX update whichqs? */
360	btrl	%ebx,%edi			/* clear q full status */
361	leal	_qs(,%ebx,8),%eax		/* select q */
362	movl	%eax,%esi
363
364#ifdef	DIAGNOSTIC
365	cmpl	P_FORW(%eax),%eax 		/* linked to self? (e.g. not on list) */
366	je	badsw				/* not possible */
367#endif
368
369	movl	P_FORW(%eax),%ecx		/* unlink from front of process q */
370	movl	P_FORW(%ecx),%edx
371	movl	%edx,P_FORW(%eax)
372	movl	P_BACK(%ecx),%eax
373	movl	%eax,P_BACK(%edx)
374
375	cmpl	P_FORW(%ecx),%esi		/* q empty */
376	je	3f
377	btsl	%ebx,%edi			/* nope, set to indicate not empty */
3783:
379	movl	%edi,_whichqs			/* update q status */
380	jmp	swtch_com
381
382idqr: /* was sw1a */
383	movl    _whichidqs,%edi			/* pick next p. from idqs */
384
385	/* XXX - bsf is sloow */
386	bsfl	%edi,%ebx			/* find a full q */
387	jz	_idle				/* no proc, idle */
388
389	/* XX update whichqs? */
390	btrl	%ebx,%edi			/* clear q full status */
391	leal	_idqs(,%ebx,8),%eax		/* select q */
392	movl	%eax,%esi
393
394#ifdef        DIAGNOSTIC
395	cmpl	P_FORW(%eax),%eax		/* linked to self? (e.g. not on list) */
396	je	badsw				/* not possible */
397#endif
398
399	movl	P_FORW(%eax),%ecx		/* unlink from front of process q */
400	movl	P_FORW(%ecx),%edx
401	movl	%edx,P_FORW(%eax)
402	movl	P_BACK(%ecx),%eax
403	movl	%eax,P_BACK(%edx)
404
405	cmpl	P_FORW(%ecx),%esi		/* q empty */
406	je	id3
407	btsl	%ebx,%edi			/* nope, set to indicate not empty */
408id3:
409	movl	%edi,_whichidqs			/* update q status */
410
411swtch_com:
412	movl	$0,%eax
413	movl	%eax,_want_resched
414
415#ifdef	DIAGNOSTIC
416	cmpl	%eax,P_WCHAN(%ecx)
417	jne	badsw
418	cmpb	$SRUN,P_STAT(%ecx)
419	jne	badsw
420#endif
421
422	movl	%eax,P_BACK(%ecx) 		/* isolate process to run */
423	movl	P_ADDR(%ecx),%edx
424	movl	PCB_CR3(%edx),%ebx
425
426	/* switch address space */
427	movl	%ebx,%cr3
428
429	/* restore context */
430	movl	PCB_EBX(%edx),%ebx
431	movl	PCB_ESP(%edx),%esp
432	movl	PCB_EBP(%edx),%ebp
433	movl	PCB_ESI(%edx),%esi
434	movl	PCB_EDI(%edx),%edi
435	movl	PCB_EIP(%edx),%eax
436	movl	%eax,(%esp)
437
438	movl	%ecx,_curproc			/* into next process */
439	movl	%edx,_curpcb
440
441#ifdef	USER_LDT
442	cmpl	$0, PCB_USERLDT(%edx)
443	jnz	1f
444	movl	__default_ldt,%eax
445	cmpl	_currentldt,%eax
446	je	2f
447	lldt	__default_ldt
448	movl	%eax,_currentldt
449	jmp	2f
4501:	pushl	%edx
451	call	_set_user_ldt
452	popl	%edx
4532:
454#endif
455
456	pushl	%edx				/* save p to return */
457/*
458 * XXX - 0.0 forgot to save it - is that why this was commented out in 0.1?
459 * I think restoring the cpl is unnecessary, but we must turn off the cli
460 * now that spl*() don't do it as a side affect.
461 */
462	pushl	PCB_IML(%edx)
463	sti
464#if 0
465	call	_splx
466#endif
467	addl	$4,%esp
468/*
469 * XXX - 0.0 gets here via swtch_to_inactive().  I think 0.1 gets here in the
470 * same way.  Better return a value.
471 */
472	popl	%eax				/* return(p); */
473	ret
474
475ENTRY(mvesp)
476	movl	%esp,%eax
477	ret
478/*
479 * struct proc *swtch_to_inactive(struct proc *p);
480 *
481 * At exit of a process, move off the address space of the
482 * process and onto a "safe" one. Then, on a temporary stack
483 * return and run code that disposes of the old state.
484 * Since this code requires a parameter from the "old" stack,
485 * pass it back as a return value.
486 */
487ENTRY(swtch_to_inactive)
488	popl	%edx				/* old pc */
489	popl	%eax				/* arg, our return value */
490	movl	_IdlePTD,%ecx
491	movl	%ecx,%cr3			/* good bye address space */
492 #write buffer?
493	movl	$tmpstk,%esp			/* temporary stack, compensated for call */
494	MEXITCOUNT
495	jmp	%edx				/* return, execute remainder of cleanup */
496
497/*
498 * savectx(pcb, altreturn)
499 * Update pcb, saving current processor state and arranging
500 * for alternate return ala longjmp in cpu_switch if altreturn is true.
501 */
502ENTRY(savectx)
503	movl	4(%esp),%ecx
504	movw	_cpl,%ax
505	movw	%ax,PCB_IML(%ecx)
506	movl	(%esp),%eax
507	movl	%eax,PCB_EIP(%ecx)
508	movl	%ebx,PCB_EBX(%ecx)
509	movl	%esp,PCB_ESP(%ecx)
510	movl	%ebp,PCB_EBP(%ecx)
511	movl	%esi,PCB_ESI(%ecx)
512	movl	%edi,PCB_EDI(%ecx)
513
514#if NNPX > 0
515	/*
516	 * If npxproc == NULL, then the npx h/w state is irrelevant and the
517	 * state had better already be in the pcb.  This is true for forks
518	 * but not for dumps (the old book-keeping with FP flags in the pcb
519	 * always lost for dumps because the dump pcb has 0 flags).
520	 *
521	 * If npxproc != NULL, then we have to save the npx h/w state to
522	 * npxproc's pcb and copy it to the requested pcb, or save to the
523	 * requested pcb and reload.  Copying is easier because we would
524	 * have to handle h/w bugs for reloading.  We used to lose the
525	 * parent's npx state for forks by forgetting to reload.
526	 */
527	mov	_npxproc,%eax
528	testl	%eax,%eax
529	je	1f
530
531	pushl	%ecx
532	movl	P_ADDR(%eax),%eax
533	leal	PCB_SAVEFPU(%eax),%eax
534	pushl	%eax
535	pushl	%eax
536	call	_npxsave
537	popl	%eax
538	popl	%eax
539	popl	%ecx
540
541	pushl	%ecx
542	pushl	$108+8*2			/* XXX h/w state size + padding */
543	leal	PCB_SAVEFPU(%ecx),%ecx
544	pushl	%ecx
545	pushl	%eax
546	call	_bcopy
547	addl	$12,%esp
548	popl	%ecx
5491:
550#endif	/* NNPX > 0 */
551
552	cmpl	$0,8(%esp)
553	je	1f
554	movl	%esp,%edx			/* relocate current sp relative to pcb */
555	subl	$_kstack,%edx			/*   (sp is relative to kstack): */
556	addl	%edx,%ecx			/*   pcb += sp - kstack; */
557	movl	%eax,(%ecx)			/* write return pc at (relocated) sp@ */
558
559/* this mess deals with replicating register state gcc hides */
560	movl	12(%esp),%eax
561	movl	%eax,12(%ecx)
562	movl	16(%esp),%eax
563	movl	%eax,16(%ecx)
564	movl	20(%esp),%eax
565	movl	%eax,20(%ecx)
566	movl	24(%esp),%eax
567	movl	%eax,24(%ecx)
5681:
569	xorl	%eax,%eax			/* return 0 */
570	ret
571
572/*
573 * addupc(int pc, struct uprof *up, int ticks):
574 * update profiling information for the user process.
575 */
576ENTRY(addupc)
577	pushl %ebp
578	movl %esp,%ebp
579	movl 12(%ebp),%edx			/* up */
580	movl 8(%ebp),%eax			/* pc */
581
582	subl PR_OFF(%edx),%eax			/* pc -= up->pr_off */
583	jb L1					/* if (pc was < off) return */
584
585	shrl $1,%eax				/* praddr = pc >> 1 */
586	imull PR_SCALE(%edx),%eax		/* praddr *= up->pr_scale */
587	shrl $15,%eax				/* praddr = praddr << 15 */
588	andl $-2,%eax				/* praddr &= ~1 */
589
590	cmpl PR_SIZE(%edx),%eax			/* if (praddr > up->pr_size) return */
591	ja L1
592
593/*	addl %eax,%eax				/* praddr -> word offset */
594	addl PR_BASE(%edx),%eax			/* praddr += up-> pr_base */
595	movl 16(%ebp),%ecx			/* ticks */
596
597	movl _curpcb,%edx
598	movl $proffault,PCB_ONFAULT(%edx)
599	addl %ecx,(%eax)			/* storage location += ticks */
600	movl $0,PCB_ONFAULT(%edx)
601L1:
602	leave
603	ret
604
605	ALIGN_TEXT
606proffault:
607	/* if we get a fault, then kill profiling all together */
608	movl $0,PCB_ONFAULT(%edx)		/* squish the fault handler */
609	movl 12(%ebp),%ecx
610	movl $0,PR_SCALE(%ecx)			/* up->pr_scale = 0 */
611	leave
612	ret
613