cpu_switch.S revision 58764
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 * $FreeBSD: head/sys/amd64/amd64/cpu_switch.S 58764 2000-03-29 06:15:43Z dillon $ 37 */ 38 39#include "npx.h" 40#include "opt_user_ldt.h" 41 42#include <sys/rtprio.h> 43 44#include <machine/asmacros.h> 45 46#ifdef SMP 47#include <machine/pmap.h> 48#include <machine/apic.h> 49#include <machine/smptests.h> /** GRAB_LOPRIO */ 50#include <machine/ipl.h> 51#include <machine/lock.h> 52#endif /* SMP */ 53 54#include "assym.s" 55 56 57/*****************************************************************************/ 58/* Scheduling */ 59/*****************************************************************************/ 60 61 .data 62 63 .globl _hlt_vector 64_hlt_vector: .long _default_halt /* pointer to halt routine */ 65 66 .globl _panic 67 68#if defined(SWTCH_OPTIM_STATS) 69 .globl _swtch_optim_stats, _tlb_flush_count 70_swtch_optim_stats: .long 0 /* number of _swtch_optims */ 71_tlb_flush_count: .long 0 72#endif 73 74 .text 75 76/* 77 * When no processes are on the runq, cpu_switch() branches to _idle 78 * to wait for something to come ready. 79 */ 80 ALIGN_TEXT 81 .type _idle,@function 82_idle: 83 xorl %ebp,%ebp 84 movl %ebp,_switchtime 85 86#ifdef SMP 87 88 /* when called, we have the mplock, intr disabled */ 89 /* use our idleproc's "context" */ 90 movl _IdlePTD, %ecx 91 movl %cr3, %eax 92 cmpl %ecx, %eax 93 je 2f 94#if defined(SWTCH_OPTIM_STATS) 95 decl _swtch_optim_stats 96 incl _tlb_flush_count 97#endif 98 movl %ecx, %cr3 992: 100 /* Keep space for nonexisting return addr, or profiling bombs */ 101 movl $gd_idlestack_top-4, %ecx 102 addl %fs:0, %ecx 103 movl %ecx, %esp 104 105 /* update common_tss.tss_esp0 pointer */ 106 movl %ecx, _common_tss + TSS_ESP0 107 108 movl _cpuid, %esi 109 btrl %esi, _private_tss 110 jae 1f 111 112 movl $gd_common_tssd, %edi 113 addl %fs:0, %edi 114 115 /* move correct tss descriptor into GDT slot, then reload tr */ 116 movl _tss_gdt, %ebx /* entry in GDT */ 117 movl 0(%edi), %eax 118 movl %eax, 0(%ebx) 119 movl 4(%edi), %eax 120 movl %eax, 4(%ebx) 121 movl $GPROC0_SEL*8, %esi /* GSEL(entry, SEL_KPL) */ 122 ltr %si 1231: 124 125 sti 126 127 /* 128 * XXX callers of cpu_switch() do a bogus splclock(). Locking should 129 * be left to cpu_switch(). 130 * 131 * NOTE: spl*() may only be called while we hold the MP lock (which 132 * we do). 133 */ 134 call _spl0 135 136 cli 137 138 /* 139 * _REALLY_ free the lock, no matter how deep the prior nesting. 140 * We will recover the nesting on the way out when we have a new 141 * proc to load. 142 * 143 * XXX: we had damn well better be sure we had it before doing this! 144 */ 145 movl $FREE_LOCK, %eax 146 movl %eax, _mp_lock 147 148 /* do NOT have lock, intrs disabled */ 149 .globl idle_loop 150idle_loop: 151 152 cmpl $0,_smp_active 153 jne 1f 154 cmpl $0,_cpuid 155 je 1f 156 jmp 2f 157 1581: 159 call _procrunnable 160 testl %eax,%eax 161 jnz 3f 162 163 /* 164 * Handle page-zeroing in the idle loop. Called with interrupts 165 * disabled and the MP lock released. Inside vm_page_zero_idle 166 * we enable interrupts and grab the mplock as required. 167 */ 168 cmpl $0,_do_page_zero_idle 169 je 2f 170 171 call _vm_page_zero_idle /* internal locking */ 172 testl %eax, %eax 173 jnz idle_loop 1742: 175 176 /* enable intrs for a halt */ 177 movl $0, lapic_tpr /* 1st candidate for an INT */ 178 call *_hlt_vector /* wait for interrupt */ 179 cli 180 jmp idle_loop 181 182 /* 183 * Note that interrupts must be enabled while obtaining the MP lock 184 * in order to be able to take IPI's while blocked. 185 */ 1863: 187 movl $LOPRIO_LEVEL, lapic_tpr /* arbitrate for INTs */ 188 sti 189 call _get_mplock 190 cli 191 call _procrunnable 192 testl %eax,%eax 193 CROSSJUMP(jnz, sw1a, jz) 194 call _rel_mplock 195 jmp idle_loop 196 197#else /* !SMP */ 198 199 movl $HIDENAME(tmpstk),%esp 200#if defined(OVERLY_CONSERVATIVE_PTD_MGMT) 201#if defined(SWTCH_OPTIM_STATS) 202 incl _swtch_optim_stats 203#endif 204 movl _IdlePTD, %ecx 205 movl %cr3, %eax 206 cmpl %ecx, %eax 207 je 2f 208#if defined(SWTCH_OPTIM_STATS) 209 decl _swtch_optim_stats 210 incl _tlb_flush_count 211#endif 212 movl %ecx, %cr3 2132: 214#endif 215 216 /* update common_tss.tss_esp0 pointer */ 217 movl %esp, _common_tss + TSS_ESP0 218 219 movl $0, %esi 220 btrl %esi, _private_tss 221 jae 1f 222 223 movl $_common_tssd, %edi 224 225 /* move correct tss descriptor into GDT slot, then reload tr */ 226 movl _tss_gdt, %ebx /* entry in GDT */ 227 movl 0(%edi), %eax 228 movl %eax, 0(%ebx) 229 movl 4(%edi), %eax 230 movl %eax, 4(%ebx) 231 movl $GPROC0_SEL*8, %esi /* GSEL(entry, SEL_KPL) */ 232 ltr %si 2331: 234 235 sti 236 237 /* 238 * XXX callers of cpu_switch() do a bogus splclock(). Locking should 239 * be left to cpu_switch(). 240 */ 241 call _spl0 242 243 ALIGN_TEXT 244idle_loop: 245 cli 246 call _procrunnable 247 testl %eax,%eax 248 CROSSJUMP(jnz, sw1a, jz) 249 call _vm_page_zero_idle 250 testl %eax, %eax 251 jnz idle_loop 252 call *_hlt_vector /* wait for interrupt */ 253 jmp idle_loop 254 255#endif /* SMP */ 256 257CROSSJUMPTARGET(_idle) 258 259ENTRY(default_halt) 260 sti 261#ifndef SMP 262 hlt /* XXX: until a wakeup IPI */ 263#endif 264 ret 265 266/* 267 * cpu_switch() 268 */ 269ENTRY(cpu_switch) 270 271 /* switch to new process. first, save context as needed */ 272 movl _curproc,%ecx 273 274 /* if no process to save, don't bother */ 275 testl %ecx,%ecx 276 je sw1 277 278#ifdef SMP 279 movb P_ONCPU(%ecx), %al /* save "last" cpu */ 280 movb %al, P_LASTCPU(%ecx) 281 movb $0xff, P_ONCPU(%ecx) /* "leave" the cpu */ 282#endif /* SMP */ 283 movl P_VMSPACE(%ecx), %edx 284#ifdef SMP 285 movl _cpuid, %eax 286#else 287 xorl %eax, %eax 288#endif /* SMP */ 289 btrl %eax, VM_PMAP+PM_ACTIVE(%edx) 290 291 movl P_ADDR(%ecx),%edx 292 293 movl (%esp),%eax /* Hardware registers */ 294 movl %eax,PCB_EIP(%edx) 295 movl %ebx,PCB_EBX(%edx) 296 movl %esp,PCB_ESP(%edx) 297 movl %ebp,PCB_EBP(%edx) 298 movl %esi,PCB_ESI(%edx) 299 movl %edi,PCB_EDI(%edx) 300 movl %gs,PCB_GS(%edx) 301 302 /* test if debug regisers should be saved */ 303 movb PCB_FLAGS(%edx),%al 304 andb $PCB_DBREGS,%al 305 jz 1f /* no, skip over */ 306 movl %dr7,%eax /* yes, do the save */ 307 movl %eax,PCB_DR7(%edx) 308 andl $0x0000ff00, %eax /* disable all watchpoints */ 309 movl %eax,%dr7 310 movl %dr6,%eax 311 movl %eax,PCB_DR6(%edx) 312 movl %dr3,%eax 313 movl %eax,PCB_DR3(%edx) 314 movl %dr2,%eax 315 movl %eax,PCB_DR2(%edx) 316 movl %dr1,%eax 317 movl %eax,PCB_DR1(%edx) 318 movl %dr0,%eax 319 movl %eax,PCB_DR0(%edx) 3201: 321 322#ifdef SMP 323 movl _mp_lock, %eax 324 /* XXX FIXME: we should be saving the local APIC TPR */ 325#ifdef DIAGNOSTIC 326 cmpl $FREE_LOCK, %eax /* is it free? */ 327 je badsw4 /* yes, bad medicine! */ 328#endif /* DIAGNOSTIC */ 329 andl $COUNT_FIELD, %eax /* clear CPU portion */ 330 movl %eax, PCB_MPNEST(%edx) /* store it */ 331#endif /* SMP */ 332 333#if NNPX > 0 334 /* have we used fp, and need a save? */ 335 cmpl %ecx,_npxproc 336 jne 1f 337 addl $PCB_SAVEFPU,%edx /* h/w bugs make saving complicated */ 338 pushl %edx 339 call _npxsave /* do it in a big C function */ 340 popl %eax 3411: 342#endif /* NNPX > 0 */ 343 344 movl $0,_curproc /* out of process */ 345 346 /* save is done, now choose a new process or idle */ 347sw1: 348 cli 349 350#ifdef SMP 351 /* Stop scheduling if smp_active goes zero and we are not BSP */ 352 cmpl $0,_smp_active 353 jne 1f 354 cmpl $0,_cpuid 355 CROSSJUMP(je, _idle, jne) /* wind down */ 3561: 357#endif 358 359sw1a: 360 call _chooseproc /* trash ecx, edx, ret eax*/ 361 testl %eax,%eax 362 CROSSJUMP(je, _idle, jne) /* if no proc, idle */ 363 movl %eax,%ecx 364 365 xorl %eax,%eax 366 andl $~AST_RESCHED,_astpending 367 368#ifdef DIAGNOSTIC 369 cmpl %eax,P_WCHAN(%ecx) 370 jne badsw1 371 cmpb $SRUN,P_STAT(%ecx) 372 jne badsw2 373#endif 374 375 movl P_ADDR(%ecx),%edx 376 377#if defined(SWTCH_OPTIM_STATS) 378 incl _swtch_optim_stats 379#endif 380 /* switch address space */ 381 movl %cr3,%ebx 382 cmpl PCB_CR3(%edx),%ebx 383 je 4f 384#if defined(SWTCH_OPTIM_STATS) 385 decl _swtch_optim_stats 386 incl _tlb_flush_count 387#endif 388 movl PCB_CR3(%edx),%ebx 389 movl %ebx,%cr3 3904: 391 392#ifdef SMP 393 movl _cpuid, %esi 394#else 395 xorl %esi, %esi 396#endif 397 cmpl $0, PCB_EXT(%edx) /* has pcb extension? */ 398 je 1f 399 btsl %esi, _private_tss /* mark use of private tss */ 400 movl PCB_EXT(%edx), %edi /* new tss descriptor */ 401 jmp 2f 4021: 403 404 /* update common_tss.tss_esp0 pointer */ 405 movl %edx, %ebx /* pcb */ 406 addl $(UPAGES * PAGE_SIZE - 16), %ebx 407 movl %ebx, _common_tss + TSS_ESP0 408 409 btrl %esi, _private_tss 410 jae 3f 411#ifdef SMP 412 movl $gd_common_tssd, %edi 413 addl %fs:0, %edi 414#else 415 movl $_common_tssd, %edi 416#endif 4172: 418 /* move correct tss descriptor into GDT slot, then reload tr */ 419 movl _tss_gdt, %ebx /* entry in GDT */ 420 movl 0(%edi), %eax 421 movl %eax, 0(%ebx) 422 movl 4(%edi), %eax 423 movl %eax, 4(%ebx) 424 movl $GPROC0_SEL*8, %esi /* GSEL(entry, SEL_KPL) */ 425 ltr %si 4263: 427 movl P_VMSPACE(%ecx), %ebx 428#ifdef SMP 429 movl _cpuid, %eax 430#else 431 xorl %eax, %eax 432#endif 433 btsl %eax, VM_PMAP+PM_ACTIVE(%ebx) 434 435 /* restore context */ 436 movl PCB_EBX(%edx),%ebx 437 movl PCB_ESP(%edx),%esp 438 movl PCB_EBP(%edx),%ebp 439 movl PCB_ESI(%edx),%esi 440 movl PCB_EDI(%edx),%edi 441 movl PCB_EIP(%edx),%eax 442 movl %eax,(%esp) 443 444#ifdef SMP 445#ifdef GRAB_LOPRIO /* hold LOPRIO for INTs */ 446#ifdef CHEAP_TPR 447 movl $0, lapic_tpr 448#else 449 andl $~APIC_TPR_PRIO, lapic_tpr 450#endif /** CHEAP_TPR */ 451#endif /** GRAB_LOPRIO */ 452 movl _cpuid,%eax 453 movb %al, P_ONCPU(%ecx) 454#endif /* SMP */ 455 movl %edx, _curpcb 456 movl %ecx, _curproc /* into next process */ 457 458#ifdef SMP 459 movl _cpu_lockid, %eax 460 orl PCB_MPNEST(%edx), %eax /* add next count from PROC */ 461 movl %eax, _mp_lock /* load the mp_lock */ 462 /* XXX FIXME: we should be restoring the local APIC TPR */ 463#endif /* SMP */ 464 465#ifdef USER_LDT 466 cmpl $0, PCB_USERLDT(%edx) 467 jnz 1f 468 movl __default_ldt,%eax 469 cmpl _currentldt,%eax 470 je 2f 471 lldt __default_ldt 472 movl %eax,_currentldt 473 jmp 2f 4741: pushl %edx 475 call _set_user_ldt 476 popl %edx 4772: 478#endif 479 480 /* This must be done after loading the user LDT. */ 481 .globl cpu_switch_load_gs 482cpu_switch_load_gs: 483 movl PCB_GS(%edx),%gs 484 485 /* test if debug regisers should be restored */ 486 movb PCB_FLAGS(%edx),%al 487 andb $PCB_DBREGS,%al 488 jz 1f /* no, skip over */ 489 movl PCB_DR6(%edx),%eax /* yes, do the restore */ 490 movl %eax,%dr6 491 movl PCB_DR3(%edx),%eax 492 movl %eax,%dr3 493 movl PCB_DR2(%edx),%eax 494 movl %eax,%dr2 495 movl PCB_DR1(%edx),%eax 496 movl %eax,%dr1 497 movl PCB_DR0(%edx),%eax 498 movl %eax,%dr0 499 movl PCB_DR7(%edx),%eax 500 movl %eax,%dr7 5011: 502 503 sti 504 ret 505 506CROSSJUMPTARGET(sw1a) 507 508#ifdef DIAGNOSTIC 509badsw1: 510 pushl $sw0_1 511 call _panic 512 513sw0_1: .asciz "cpu_switch: has wchan" 514 515badsw2: 516 pushl $sw0_2 517 call _panic 518 519sw0_2: .asciz "cpu_switch: not SRUN" 520#endif 521 522#if defined(SMP) && defined(DIAGNOSTIC) 523badsw4: 524 pushl $sw0_4 525 call _panic 526 527sw0_4: .asciz "cpu_switch: do not have lock" 528#endif /* SMP && DIAGNOSTIC */ 529 530/* 531 * savectx(pcb) 532 * Update pcb, saving current processor state. 533 */ 534ENTRY(savectx) 535 /* fetch PCB */ 536 movl 4(%esp),%ecx 537 538 /* caller's return address - child won't execute this routine */ 539 movl (%esp),%eax 540 movl %eax,PCB_EIP(%ecx) 541 542 movl %ebx,PCB_EBX(%ecx) 543 movl %esp,PCB_ESP(%ecx) 544 movl %ebp,PCB_EBP(%ecx) 545 movl %esi,PCB_ESI(%ecx) 546 movl %edi,PCB_EDI(%ecx) 547 movl %gs,PCB_GS(%ecx) 548 549#if NNPX > 0 550 /* 551 * If npxproc == NULL, then the npx h/w state is irrelevant and the 552 * state had better already be in the pcb. This is true for forks 553 * but not for dumps (the old book-keeping with FP flags in the pcb 554 * always lost for dumps because the dump pcb has 0 flags). 555 * 556 * If npxproc != NULL, then we have to save the npx h/w state to 557 * npxproc's pcb and copy it to the requested pcb, or save to the 558 * requested pcb and reload. Copying is easier because we would 559 * have to handle h/w bugs for reloading. We used to lose the 560 * parent's npx state for forks by forgetting to reload. 561 */ 562 movl _npxproc,%eax 563 testl %eax,%eax 564 je 1f 565 566 pushl %ecx 567 movl P_ADDR(%eax),%eax 568 leal PCB_SAVEFPU(%eax),%eax 569 pushl %eax 570 pushl %eax 571 call _npxsave 572 addl $4,%esp 573 popl %eax 574 popl %ecx 575 576 pushl $PCB_SAVEFPU_SIZE 577 leal PCB_SAVEFPU(%ecx),%ecx 578 pushl %ecx 579 pushl %eax 580 call _bcopy 581 addl $12,%esp 582#endif /* NNPX > 0 */ 583 5841: 585 ret 586