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