cpu_switch.S revision 1888
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.8 1994/08/03 02:45:30 davidg Exp $
37 */
38
39#include "npx.h"	/* for NNPX */
40#include "assym.s"	/* for preprocessor defines */
41#include "errno.h"	/* for error codes */
42
43#include "machine/asmacros.h"	/* for miscellaneous assembly macros */
44#define	LOCORE			/* XXX inhibit C declarations */
45#include "machine/spl.h"	/* for SWI_AST_MASK ... */
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
63_curpcb:	.long	0			/* pointer to curproc's PCB area */
64_whichqs:	.long	0			/* which run queues have data */
65
66	.globl	_qs,_cnt,_panic
67	.comm	_noproc,4
68	.comm	_runrun,4
69
70	.globl	_want_resched
71_want_resched:	.long	0			/* we need to re-run the scheduler */
72
73	.text
74/*
75 * setrunqueue(p)
76 *
77 * Call should be made at spl6(), and p->p_stat should be SRUN
78 */
79ENTRY(setrunqueue)
80	movl	4(%esp),%eax
81	cmpl	$0,P_RLINK(%eax)		/* should not be on q already */
82	je	set1
83	pushl	$set2
84	call	_panic
85set1:
86	movzbl	P_PRI(%eax),%edx
87	shrl	$2,%edx
88	btsl	%edx,_whichqs			/* set q full bit */
89	shll	$3,%edx
90	addl	$_qs,%edx			/* locate q hdr */
91	movl	%edx,P_LINK(%eax)		/* link process on tail of q */
92	movl	P_RLINK(%edx),%ecx
93	movl	%ecx,P_RLINK(%eax)
94	movl	%eax,P_RLINK(%edx)
95	movl	%eax,P_LINK(%ecx)
96	ret
97
98set2:	.asciz	"setrunqueue"
99
100/*
101 * Remrq(p)
102 *
103 * Call should be made at spl6().
104 */
105ENTRY(remrq)
106	movl	4(%esp),%eax
107	movzbl	P_PRI(%eax),%edx
108	shrl	$2,%edx
109	btrl	%edx,_whichqs			/* clear full bit, panic if clear already */
110	jb	rem1
111	pushl	$rem3
112	call	_panic
113rem1:
114	pushl	%edx
115	movl	P_LINK(%eax),%ecx		/* unlink process */
116	movl	P_RLINK(%eax),%edx
117	movl	%edx,P_RLINK(%ecx)
118	movl	P_RLINK(%eax),%ecx
119	movl	P_LINK(%eax),%edx
120	movl	%edx,P_LINK(%ecx)
121	popl	%edx
122	movl	$_qs,%ecx
123	shll	$3,%edx
124	addl	%edx,%ecx
125	cmpl	P_LINK(%ecx),%ecx		/* q still has something? */
126	je	rem2
127	shrl	$3,%edx				/* yes, set bit as still full */
128	btsl	%edx,_whichqs
129rem2:
130	movl	$0,P_RLINK(%eax)		/* zap reverse link to indicate off list */
131	ret
132
133rem3:	.asciz	"remrq"
134sw0:	.asciz	"cpu_switch"
135
136/*
137 * When no processes are on the runq, cpu_switch() branches to _idle
138 * to wait for something to come ready.
139 */
140	ALIGN_TEXT
141_idle:
142	MCOUNT
143	movl	_IdlePTD,%ecx
144	movl	%ecx,%cr3
145	movl	$tmpstk,%esp
146	sti
147
148	/*
149	 * XXX callers of cpu_switch() do a bogus splclock().  Locking should
150	 * be left to cpu_switch().
151	 */
152	movl	$SWI_AST_MASK,_cpl
153	testl	$~SWI_AST_MASK,_ipending
154	je	idle_loop
155	call	_splz
156
157	ALIGN_TEXT
158idle_loop:
159	cli
160	cmpl	$0,_whichqs
161	jne	sw1a
162	sti
163	hlt					/* wait for interrupt */
164	jmp	idle_loop
165
166badsw:
167	pushl	$sw0
168	call	_panic
169	/*NOTREACHED*/
170
171/*
172 * cpu_switch()
173 */
174ENTRY(cpu_switch)
175	incl	_cnt+V_SWTCH
176
177	/* switch to new process. first, save context as needed */
178
179	movl	_curproc,%ecx
180
181	/* if no process to save, don't bother */
182	testl	%ecx,%ecx
183	je	sw1
184
185	movl	P_ADDR(%ecx),%ecx
186
187	movl	(%esp),%eax			/* Hardware registers */
188	movl	%eax,PCB_EIP(%ecx)
189	movl	%ebx,PCB_EBX(%ecx)
190	movl	%esp,PCB_ESP(%ecx)
191	movl	%ebp,PCB_EBP(%ecx)
192	movl	%esi,PCB_ESI(%ecx)
193	movl	%edi,PCB_EDI(%ecx)
194
195#if NNPX > 0
196	/* have we used fp, and need a save? */
197	mov	_curproc,%eax
198	cmp	%eax,_npxproc
199	jne	1f
200	pushl	%ecx				/* h/w bugs make saving complicated */
201	leal	PCB_SAVEFPU(%ecx),%eax
202	pushl	%eax
203	call	_npxsave			/* do it in a big C function */
204	popl	%eax
205	popl	%ecx
2061:
207#endif	/* NNPX > 0 */
208
209	movl	$0,_curproc			/*  out of process */
210
211#	movw	_cpl,%ax
212#	movw	%ax,PCB_IML(%ecx)		/* save ipl */
213
214	/* save is done, now choose a new process or idle */
215sw1:
216	cli
217sw1a:
218	movl	_whichqs,%edi
2192:
220	/* XXX - bsf is sloow */
221	bsfl	%edi,%eax			/* find a full q */
222	je	_idle				/* if none, idle */
223
224	/* XX update whichqs? */
225	btrl	%eax,%edi			/* clear q full status */
226	jnb	2b				/* if it was clear, look for another */
227	movl	%eax,%ebx			/* save which one we are using */
228
229	shll	$3,%eax
230	addl	$_qs,%eax			/* select q */
231	movl	%eax,%esi
232
233#ifdef	DIAGNOSTIC
234	cmpl	P_LINK(%eax),%eax 		/* linked to self? (e.g. not on list) */
235	je	badsw				/* not possible */
236#endif
237
238	movl	P_LINK(%eax),%ecx		/* unlink from front of process q */
239	movl	P_LINK(%ecx),%edx
240	movl	%edx,P_LINK(%eax)
241	movl	P_RLINK(%ecx),%eax
242	movl	%eax,P_RLINK(%edx)
243
244	cmpl	P_LINK(%ecx),%esi		/* q empty */
245	je	3f
246	btsl	%ebx,%edi			/* nope, set to indicate full */
2473:
248	movl	%edi,_whichqs			/* update q status */
249
250	movl	$0,%eax
251	movl	%eax,_want_resched
252
253#ifdef	DIAGNOSTIC
254	cmpl	%eax,P_WCHAN(%ecx)
255	jne	badsw
256	cmpb	$SRUN,P_STAT(%ecx)
257	jne	badsw
258#endif
259
260	movl	%eax,P_RLINK(%ecx) 		/* isolate process to run */
261	movl	P_ADDR(%ecx),%edx
262	movl	PCB_CR3(%edx),%ebx
263
264	/* switch address space */
265	movl	%ebx,%cr3
266
267	/* restore context */
268	movl	PCB_EBX(%edx),%ebx
269	movl	PCB_ESP(%edx),%esp
270	movl	PCB_EBP(%edx),%ebp
271	movl	PCB_ESI(%edx),%esi
272	movl	PCB_EDI(%edx),%edi
273	movl	PCB_EIP(%edx),%eax
274	movl	%eax,(%esp)
275
276	movl	%ecx,_curproc			/* into next process */
277	movl	%edx,_curpcb
278
279#ifdef	USER_LDT
280	cmpl	$0, PCB_USERLDT(%edx)
281	jnz	1f
282	movl	__default_ldt,%eax
283	cmpl	_currentldt,%eax
284	je	2f
285	lldt	__default_ldt
286	movl	%eax,_currentldt
287	jmp	2f
2881:	pushl	%edx
289	call	_set_user_ldt
290	popl	%edx
2912:
292#endif
293
294	pushl	%edx				/* save p to return */
295/*
296 * XXX - 0.0 forgot to save it - is that why this was commented out in 0.1?
297 * I think restoring the cpl is unnecessary, but we must turn off the cli
298 * now that spl*() don't do it as a side affect.
299 */
300	pushl	PCB_IML(%edx)
301	sti
302#if 0
303	call	_splx
304#endif
305	addl	$4,%esp
306/*
307 * XXX - 0.0 gets here via swtch_to_inactive().  I think 0.1 gets here in the
308 * same way.  Better return a value.
309 */
310	popl	%eax				/* return(p); */
311	ret
312
313ENTRY(mvesp)
314	movl	%esp,%eax
315	ret
316/*
317 * struct proc *swtch_to_inactive(struct proc *p);
318 *
319 * At exit of a process, move off the address space of the
320 * process and onto a "safe" one. Then, on a temporary stack
321 * return and run code that disposes of the old state.
322 * Since this code requires a parameter from the "old" stack,
323 * pass it back as a return value.
324 */
325ENTRY(swtch_to_inactive)
326	popl	%edx				/* old pc */
327	popl	%eax				/* arg, our return value */
328	movl	_IdlePTD,%ecx
329	movl	%ecx,%cr3			/* good bye address space */
330 #write buffer?
331	movl	$tmpstk-4,%esp			/* temporary stack, compensated for call */
332	MEXITCOUNT
333	jmp	%edx				/* return, execute remainder of cleanup */
334
335/*
336 * savectx(pcb, altreturn)
337 * Update pcb, saving current processor state and arranging
338 * for alternate return ala longjmp in cpu_switch if altreturn is true.
339 */
340ENTRY(savectx)
341	movl	4(%esp),%ecx
342	movw	_cpl,%ax
343	movw	%ax,PCB_IML(%ecx)
344	movl	(%esp),%eax
345	movl	%eax,PCB_EIP(%ecx)
346	movl	%ebx,PCB_EBX(%ecx)
347	movl	%esp,PCB_ESP(%ecx)
348	movl	%ebp,PCB_EBP(%ecx)
349	movl	%esi,PCB_ESI(%ecx)
350	movl	%edi,PCB_EDI(%ecx)
351
352#if NNPX > 0
353	/*
354	 * If npxproc == NULL, then the npx h/w state is irrelevant and the
355	 * state had better already be in the pcb.  This is true for forks
356	 * but not for dumps (the old book-keeping with FP flags in the pcb
357	 * always lost for dumps because the dump pcb has 0 flags).
358	 *
359	 * If npxproc != NULL, then we have to save the npx h/w state to
360	 * npxproc's pcb and copy it to the requested pcb, or save to the
361	 * requested pcb and reload.  Copying is easier because we would
362	 * have to handle h/w bugs for reloading.  We used to lose the
363	 * parent's npx state for forks by forgetting to reload.
364	 */
365	mov	_npxproc,%eax
366	testl	%eax,%eax
367	je	1f
368
369	pushl	%ecx
370	movl	P_ADDR(%eax),%eax
371	leal	PCB_SAVEFPU(%eax),%eax
372	pushl	%eax
373	pushl	%eax
374	call	_npxsave
375	popl	%eax
376	popl	%eax
377	popl	%ecx
378
379	pushl	%ecx
380	pushl	$108+8*2			/* XXX h/w state size + padding */
381	leal	PCB_SAVEFPU(%ecx),%ecx
382	pushl	%ecx
383	pushl	%eax
384	call	_bcopy
385	addl	$12,%esp
386	popl	%ecx
3871:
388#endif	/* NNPX > 0 */
389
390	movl	_CMAP2,%edx			/* save temporary map PTE */
391	movl	%edx,PCB_CMAP2(%ecx)		/* in our context */
392
393	cmpl	$0,8(%esp)
394	je	1f
395	movl	%esp,%edx			/* relocate current sp relative to pcb */
396	subl	$_kstack,%edx			/*   (sp is relative to kstack): */
397	addl	%edx,%ecx			/*   pcb += sp - kstack; */
398	movl	%eax,(%ecx)			/* write return pc at (relocated) sp@ */
399
400/* this mess deals with replicating register state gcc hides */
401	movl	12(%esp),%eax
402	movl	%eax,12(%ecx)
403	movl	16(%esp),%eax
404	movl	%eax,16(%ecx)
405	movl	20(%esp),%eax
406	movl	%eax,20(%ecx)
407	movl	24(%esp),%eax
408	movl	%eax,24(%ecx)
4091:
410	xorl	%eax,%eax			/* return 0 */
411	ret
412
413/*
414 * addupc(int pc, struct uprof *up, int ticks):
415 * update profiling information for the user process.
416 */
417ENTRY(addupc)
418	pushl %ebp
419	movl %esp,%ebp
420	movl 12(%ebp),%edx			/* up */
421	movl 8(%ebp),%eax			/* pc */
422
423	subl PR_OFF(%edx),%eax			/* pc -= up->pr_off */
424	jb L1					/* if (pc was < off) return */
425
426	shrl $1,%eax				/* praddr = pc >> 1 */
427	imull PR_SCALE(%edx),%eax		/* praddr *= up->pr_scale */
428	shrl $15,%eax				/* praddr = praddr << 15 */
429	andl $-2,%eax				/* praddr &= ~1 */
430
431	cmpl PR_SIZE(%edx),%eax			/* if (praddr > up->pr_size) return */
432	ja L1
433
434/*	addl %eax,%eax				/* praddr -> word offset */
435	addl PR_BASE(%edx),%eax			/* praddr += up-> pr_base */
436	movl 16(%ebp),%ecx			/* ticks */
437
438	movl _curpcb,%edx
439	movl $proffault,PCB_ONFAULT(%edx)
440	addl %ecx,(%eax)			/* storage location += ticks */
441	movl $0,PCB_ONFAULT(%edx)
442L1:
443	leave
444	ret
445
446	ALIGN_TEXT
447proffault:
448	/* if we get a fault, then kill profiling all together */
449	movl $0,PCB_ONFAULT(%edx)		/* squish the fault handler */
450	movl 12(%ebp),%ecx
451	movl $0,PR_SCALE(%ecx)			/* up->pr_scale = 0 */
452	leave
453	ret
454