cpu_switch.S revision 3291
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.14 1994/10/01 02:56:03 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-4,%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