cpu_switch.S revision 16723
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.35 1996/05/01 03:46:15 bde Exp $
37 */
38
39#include "apm.h"
40#include "npx.h"
41#include "opt_user_ldt.h"
42
43#include <sys/errno.h>
44#include <sys/rtprio.h>
45
46#include <machine/asmacros.h>
47#include <machine/spl.h>
48
49#include "assym.s"
50
51
52/*****************************************************************************/
53/* Scheduling                                                                */
54/*****************************************************************************/
55
56/*
57 * The following primitives manipulate the run queues.
58 * _whichqs tells which of the 32 queues _qs
59 * have processes in them.  setrunqueue puts processes into queues, Remrq
60 * removes them from queues.  The running process is on no queue,
61 * other processes are on a queue related to p->p_priority, divided by 4
62 * actually to shrink the 0-127 range of priorities into the 32 available
63 * queues.
64 */
65	.data
66	.globl	_curpcb
67_curpcb:	.long	0			/* pointer to curproc's PCB area */
68_whichqs:	.long	0			/* which run queues have data */
69_whichrtqs:	.long	0			/* which realtime run queues have data */
70_whichidqs:	.long	0			/* which idletime run queues have data */
71
72	.globl	_qs,_cnt,_panic
73
74	.globl	_want_resched
75_want_resched:	.long	0			/* we need to re-run the scheduler */
76
77	.text
78/*
79 * setrunqueue(p)
80 *
81 * Call should be made at spl6(), and p->p_stat should be SRUN
82 */
83ENTRY(setrunqueue)
84	movl	4(%esp),%eax
85	cmpl	$0,P_BACK(%eax)			/* should not be on q already */
86	je	set1
87	pushl	$set2
88	call	_panic
89set1:
90	cmpw	$RTP_PRIO_NORMAL,P_RTPRIO_TYPE(%eax) /* normal priority process? */
91	je	set_nort
92
93	movzwl	P_RTPRIO_PRIO(%eax),%edx
94
95	cmpw	$RTP_PRIO_REALTIME,P_RTPRIO_TYPE(%eax) /* realtime priority? */
96	jne	set_id				/* must be idle priority */
97
98set_rt:
99	btsl	%edx,_whichrtqs			/* set q full bit */
100	shll	$3,%edx
101	addl	$_rtqs,%edx			/* locate q hdr */
102	movl	%edx,P_FORW(%eax)		/* link process on tail of q */
103	movl	P_BACK(%edx),%ecx
104	movl	%ecx,P_BACK(%eax)
105	movl	%eax,P_BACK(%edx)
106	movl	%eax,P_FORW(%ecx)
107	ret
108
109set_id:
110	btsl	%edx,_whichidqs			/* set q full bit */
111	shll	$3,%edx
112	addl	$_idqs,%edx			/* locate q hdr */
113	movl	%edx,P_FORW(%eax)		/* link process on tail of q */
114	movl	P_BACK(%edx),%ecx
115	movl	%ecx,P_BACK(%eax)
116	movl	%eax,P_BACK(%edx)
117	movl	%eax,P_FORW(%ecx)
118	ret
119
120set_nort:                    			/*  Normal (RTOFF) code */
121	movzbl	P_PRI(%eax),%edx
122	shrl	$2,%edx
123	btsl	%edx,_whichqs			/* set q full bit */
124	shll	$3,%edx
125	addl	$_qs,%edx			/* locate q hdr */
126	movl	%edx,P_FORW(%eax)		/* link process on tail of q */
127	movl	P_BACK(%edx),%ecx
128	movl	%ecx,P_BACK(%eax)
129	movl	%eax,P_BACK(%edx)
130	movl	%eax,P_FORW(%ecx)
131	ret
132
133set2:	.asciz	"setrunqueue"
134
135/*
136 * Remrq(p)
137 *
138 * Call should be made at spl6().
139 */
140ENTRY(remrq)
141	movl	4(%esp),%eax
142	cmpw	$RTP_PRIO_NORMAL,P_RTPRIO_TYPE(%eax) /* normal priority process? */
143	je	rem_nort
144
145	movzwl	P_RTPRIO_PRIO(%eax),%edx
146
147	cmpw	$RTP_PRIO_REALTIME,P_RTPRIO_TYPE(%eax) /* normal priority process? */
148	jne	rem_id
149
150	btrl	%edx,_whichrtqs			/* clear full bit, panic if clear already */
151	jb	rem1rt
152	pushl	$rem3rt
153	call	_panic
154rem1rt:
155	pushl	%edx
156	movl	P_FORW(%eax),%ecx		/* unlink process */
157	movl	P_BACK(%eax),%edx
158	movl	%edx,P_BACK(%ecx)
159	movl	P_BACK(%eax),%ecx
160	movl	P_FORW(%eax),%edx
161	movl	%edx,P_FORW(%ecx)
162	popl	%edx
163	movl	$_rtqs,%ecx
164	shll	$3,%edx
165	addl	%edx,%ecx
166	cmpl	P_FORW(%ecx),%ecx		/* q still has something? */
167	je	rem2rt
168	shrl	$3,%edx				/* yes, set bit as still full */
169	btsl	%edx,_whichrtqs
170rem2rt:
171	movl	$0,P_BACK(%eax)			/* zap reverse link to indicate off list */
172	ret
173rem_id:
174	btrl	%edx,_whichidqs			/* clear full bit, panic if clear already */
175	jb	rem1id
176	pushl	$rem3id
177	call	_panic
178rem1id:
179	pushl	%edx
180	movl	P_FORW(%eax),%ecx		/* unlink process */
181	movl	P_BACK(%eax),%edx
182	movl	%edx,P_BACK(%ecx)
183	movl	P_BACK(%eax),%ecx
184	movl	P_FORW(%eax),%edx
185	movl	%edx,P_FORW(%ecx)
186	popl	%edx
187	movl	$_idqs,%ecx
188	shll	$3,%edx
189	addl	%edx,%ecx
190	cmpl	P_FORW(%ecx),%ecx		/* q still has something? */
191	je	rem2id
192	shrl	$3,%edx				/* yes, set bit as still full */
193	btsl	%edx,_whichidqs
194rem2id:
195	movl	$0,P_BACK(%eax)			/* zap reverse link to indicate off list */
196	ret
197
198rem_nort:
199	movzbl	P_PRI(%eax),%edx
200	shrl	$2,%edx
201	btrl	%edx,_whichqs			/* clear full bit, panic if clear already */
202	jb	rem1
203	pushl	$rem3
204	call	_panic
205rem1:
206	pushl	%edx
207	movl	P_FORW(%eax),%ecx		/* unlink process */
208	movl	P_BACK(%eax),%edx
209	movl	%edx,P_BACK(%ecx)
210	movl	P_BACK(%eax),%ecx
211	movl	P_FORW(%eax),%edx
212	movl	%edx,P_FORW(%ecx)
213	popl	%edx
214	movl	$_qs,%ecx
215	shll	$3,%edx
216	addl	%edx,%ecx
217	cmpl	P_FORW(%ecx),%ecx		/* q still has something? */
218	je	rem2
219	shrl	$3,%edx				/* yes, set bit as still full */
220	btsl	%edx,_whichqs
221rem2:
222	movl	$0,P_BACK(%eax)			/* zap reverse link to indicate off list */
223	ret
224
225rem3:	.asciz	"remrq"
226rem3rt:	.asciz	"remrq.rt"
227rem3id:	.asciz	"remrq.id"
228sw0:	.asciz	"cpu_switch"
229
230/*
231 * When no processes are on the runq, cpu_switch() branches to _idle
232 * to wait for something to come ready.
233 */
234	ALIGN_TEXT
235_idle:
236	MCOUNT
237	xorl	%ebp,%ebp
238	movl	$tmpstk,%esp
239	movl	_IdlePTD,%ecx
240	movl	%ecx,%cr3
241	sti
242
243	/*
244	 * XXX callers of cpu_switch() do a bogus splclock().  Locking should
245	 * be left to cpu_switch().
246	 */
247	movl	$SWI_AST_MASK,_cpl
248	testl	$~SWI_AST_MASK,_ipending
249	je	idle_loop
250	call	_splz
251
252	ALIGN_TEXT
253idle_loop:
254	cli
255	movb	$1,_intr_nesting_level		/* charge Intr if we leave */
256	cmpl	$0,_whichrtqs			/* real-time queue */
257	jne	sw1a
258	cmpl	$0,_whichqs			/* normal queue */
259	jne	nortqr
260	cmpl	$0,_whichidqs			/* 'idle' queue */
261	jne	idqr
262	movb	$0,_intr_nesting_level		/* charge Idle for this loop */
263	call	_vm_page_zero_idle
264	testl	%eax, %eax
265	jnz	idle_loop
266	sti
267#if NAPM > 0
268	call    _apm_cpu_idle
269	call    _apm_cpu_busy
270#else
271	hlt					/* wait for interrupt */
272#endif
273	jmp	idle_loop
274
275badsw:
276	pushl	$sw0
277	call	_panic
278	/*NOTREACHED*/
279
280/*
281 * cpu_switch()
282 */
283ENTRY(cpu_switch)
284	/* switch to new process. first, save context as needed */
285	movl	_curproc,%ecx
286
287	/* if no process to save, don't bother */
288	testl	%ecx,%ecx
289	je	sw1
290
291	movl	P_ADDR(%ecx),%ecx
292
293	movl	(%esp),%eax			/* Hardware registers */
294	movl	%eax,PCB_EIP(%ecx)
295	movl	%ebx,PCB_EBX(%ecx)
296	movl	%esp,PCB_ESP(%ecx)
297	movl	%ebp,PCB_EBP(%ecx)
298	movl	%esi,PCB_ESI(%ecx)
299	movl	%edi,PCB_EDI(%ecx)
300
301	movb	_intr_nesting_level,%al
302	movb	%al,PCB_INL(%ecx)
303
304#if NNPX > 0
305	/* have we used fp, and need a save? */
306	mov	_curproc,%eax
307	cmp	%eax,_npxproc
308	jne	1f
309	addl	$PCB_SAVEFPU,%ecx		/* h/w bugs make saving complicated */
310	pushl	%ecx
311	call	_npxsave			/* do it in a big C function */
312	popl	%eax
3131:
314#endif	/* NNPX > 0 */
315
316	movb	$1,_intr_nesting_level		/* charge Intr, not Sys/Idle */
317
318	movl	$0,_curproc			/* out of process */
319
320	/* save is done, now choose a new process or idle */
321sw1:
322	cli
323sw1a:
324	movl    _whichrtqs,%edi			/* pick next p. from rtqs */
325	testl	%edi,%edi
326	jz	nortqr				/* no realtime procs */
327
328	/* XXX - bsf is sloow */
329	bsfl	%edi,%ebx			/* find a full q */
330	jz	nortqr				/* no proc on rt q - try normal ... */
331
332	/* XX update whichqs? */
333	btrl	%ebx,%edi			/* clear q full status */
334	leal	_rtqs(,%ebx,8),%eax		/* select q */
335	movl	%eax,%esi
336
337#ifdef        DIAGNOSTIC
338	cmpl	P_FORW(%eax),%eax		/* linked to self? (e.g. not on list) */
339	je	badsw				/* not possible */
340#endif
341
342	movl	P_FORW(%eax),%ecx		/* unlink from front of process q */
343	movl	P_FORW(%ecx),%edx
344	movl	%edx,P_FORW(%eax)
345	movl	P_BACK(%ecx),%eax
346	movl	%eax,P_BACK(%edx)
347
348	cmpl	P_FORW(%ecx),%esi		/* q empty */
349	je	rt3
350	btsl	%ebx,%edi			/* nope, set to indicate not empty */
351rt3:
352	movl	%edi,_whichrtqs			/* update q status */
353	jmp	swtch_com
354
355	/* old sw1a */
356/* Normal process priority's */
357nortqr:
358	movl	_whichqs,%edi
3592:
360	/* XXX - bsf is sloow */
361	bsfl	%edi,%ebx			/* find a full q */
362	jz	idqr				/* if none, idle */
363
364	/* XX update whichqs? */
365	btrl	%ebx,%edi			/* clear q full status */
366	leal	_qs(,%ebx,8),%eax		/* select q */
367	movl	%eax,%esi
368
369#ifdef	DIAGNOSTIC
370	cmpl	P_FORW(%eax),%eax 		/* linked to self? (e.g. not on list) */
371	je	badsw				/* not possible */
372#endif
373
374	movl	P_FORW(%eax),%ecx		/* unlink from front of process q */
375	movl	P_FORW(%ecx),%edx
376	movl	%edx,P_FORW(%eax)
377	movl	P_BACK(%ecx),%eax
378	movl	%eax,P_BACK(%edx)
379
380	cmpl	P_FORW(%ecx),%esi		/* q empty */
381	je	3f
382	btsl	%ebx,%edi			/* nope, set to indicate not empty */
3833:
384	movl	%edi,_whichqs			/* update q status */
385	jmp	swtch_com
386
387idqr: /* was sw1a */
388	movl    _whichidqs,%edi			/* pick next p. from idqs */
389
390	/* XXX - bsf is sloow */
391	bsfl	%edi,%ebx			/* find a full q */
392	jz	_idle				/* no proc, idle */
393
394	/* XX update whichqs? */
395	btrl	%ebx,%edi			/* clear q full status */
396	leal	_idqs(,%ebx,8),%eax		/* select q */
397	movl	%eax,%esi
398
399#ifdef        DIAGNOSTIC
400	cmpl	P_FORW(%eax),%eax		/* linked to self? (e.g. not on list) */
401	je	badsw				/* not possible */
402#endif
403
404	movl	P_FORW(%eax),%ecx		/* unlink from front of process q */
405	movl	P_FORW(%ecx),%edx
406	movl	%edx,P_FORW(%eax)
407	movl	P_BACK(%ecx),%eax
408	movl	%eax,P_BACK(%edx)
409
410	cmpl	P_FORW(%ecx),%esi		/* q empty */
411	je	id3
412	btsl	%ebx,%edi			/* nope, set to indicate not empty */
413id3:
414	movl	%edi,_whichidqs			/* update q status */
415
416swtch_com:
417	movl	$0,%eax
418	movl	%eax,_want_resched
419
420#ifdef	DIAGNOSTIC
421	cmpl	%eax,P_WCHAN(%ecx)
422	jne	badsw
423	cmpb	$SRUN,P_STAT(%ecx)
424	jne	badsw
425#endif
426
427	movl	%eax,P_BACK(%ecx) 		/* isolate process to run */
428	movl	P_ADDR(%ecx),%edx
429	movl	PCB_CR3(%edx),%ebx
430
431	/* switch address space */
432	movl	%ebx,%cr3
433
434	/* restore context */
435	movl	PCB_EBX(%edx),%ebx
436	movl	PCB_ESP(%edx),%esp
437	movl	PCB_EBP(%edx),%ebp
438	movl	PCB_ESI(%edx),%esi
439	movl	PCB_EDI(%edx),%edi
440	movl	PCB_EIP(%edx),%eax
441	movl	%eax,(%esp)
442
443	movl	%edx,_curpcb
444	movl	%ecx,_curproc			/* into next process */
445
446	movb	PCB_INL(%edx),%al
447	movb	%al,_intr_nesting_level
448
449#ifdef	USER_LDT
450	cmpl	$0, PCB_USERLDT(%edx)
451	jnz	1f
452	movl	__default_ldt,%eax
453	cmpl	_currentldt,%eax
454	je	2f
455	lldt	__default_ldt
456	movl	%eax,_currentldt
457	jmp	2f
4581:	pushl	%edx
459	call	_set_user_ldt
460	popl	%edx
4612:
462#endif
463
464	sti
465	ret
466
467/*
468 * savectx(pcb)
469 * Update pcb, saving current processor state.
470 */
471ENTRY(savectx)
472	/* fetch PCB */
473	movl	4(%esp),%ecx
474
475	/* caller's return address - child won't execute this routine */
476	movl	(%esp),%eax
477	movl	%eax,PCB_EIP(%ecx)
478
479	movl	%ebx,PCB_EBX(%ecx)
480	movl	%esp,PCB_ESP(%ecx)
481	movl	%ebp,PCB_EBP(%ecx)
482	movl	%esi,PCB_ESI(%ecx)
483	movl	%edi,PCB_EDI(%ecx)
484
485#if NNPX > 0
486	/*
487	 * If npxproc == NULL, then the npx h/w state is irrelevant and the
488	 * state had better already be in the pcb.  This is true for forks
489	 * but not for dumps (the old book-keeping with FP flags in the pcb
490	 * always lost for dumps because the dump pcb has 0 flags).
491	 *
492	 * If npxproc != NULL, then we have to save the npx h/w state to
493	 * npxproc's pcb and copy it to the requested pcb, or save to the
494	 * requested pcb and reload.  Copying is easier because we would
495	 * have to handle h/w bugs for reloading.  We used to lose the
496	 * parent's npx state for forks by forgetting to reload.
497	 */
498	mov	_npxproc,%eax
499	testl	%eax,%eax
500	je	1f
501
502	pushl	%ecx
503	movl	P_ADDR(%eax),%eax
504	leal	PCB_SAVEFPU(%eax),%eax
505	pushl	%eax
506	pushl	%eax
507	call	_npxsave
508	addl	$4,%esp
509	popl	%eax
510	popl	%ecx
511
512	pushl	$PCB_SAVEFPU_SIZE
513	leal	PCB_SAVEFPU(%ecx),%ecx
514	pushl	%ecx
515	pushl	%eax
516	call	_bcopy
517	addl	$12,%esp
518#endif	/* NNPX > 0 */
519
5201:
521	ret
522
523/*
524 * addupc(int pc, struct uprof *up, int ticks):
525 * update profiling information for the user process.
526 */
527ENTRY(addupc)
528	pushl %ebp
529	movl %esp,%ebp
530	movl 12(%ebp),%edx			/* up */
531	movl 8(%ebp),%eax			/* pc */
532
533	subl PR_OFF(%edx),%eax			/* pc -= up->pr_off */
534	jb L1					/* if (pc was < off) return */
535
536	pushl %edx
537	mull PR_SCALE(%edx)			/* praddr = pc * up->pr_scale */
538	shrdl $16,%edx,%eax			/* praddr >>= 16 */
539	popl %edx
540	andl $-2,%eax				/* praddr &= ~1 */
541
542	cmpl PR_SIZE(%edx),%eax			/* if (praddr > up->pr_size) return */
543	ja L1
544
545/*	addl %eax,%eax				/* praddr -> word offset */
546	addl PR_BASE(%edx),%eax			/* praddr += up-> pr_base */
547	movl 16(%ebp),%ecx			/* ticks */
548
549	movl _curpcb,%edx
550	movl $proffault,PCB_ONFAULT(%edx)
551	addl %ecx,(%eax)			/* storage location += ticks */
552	movl $0,PCB_ONFAULT(%edx)
553L1:
554	leave
555	ret
556
557	ALIGN_TEXT
558proffault:
559	/* if we get a fault, then kill profiling all together */
560	movl $0,PCB_ONFAULT(%edx)		/* squish the fault handler */
561	movl 12(%ebp),%ecx
562	movl $0,PR_SCALE(%ecx)			/* up->pr_scale = 0 */
563	leave
564	ret
565