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