sys/platform/pc64/x86_64/swtch.s
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 | /* * Copyright (c) 2003,2004,2008 The DragonFly Project. All rights reserved. * Copyright (c) 2008 Jordan Gordeev. * * This code is derived from software contributed to The DragonFly Project * by Matthew Dillon <dillon@backplane.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name of The DragonFly Project nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific, prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/sys/i386/i386/swtch.s,v 1.89.2.10 2003/01/23 03:36:24 ps Exp $ */ #include <sys/rtprio.h> #include <machine/asmacros.h> #include <machine/segments.h> #include <machine/pmap.h> #if 0 /* JG */ #include <machine_base/apic/apicreg.h> #endif #include <machine/lock.h> #include "assym.s" #define MPLOCKED lock ; /* * PREEMPT_OPTIMIZE * * This feature allows the preempting (interrupt) kernel thread to borrow * %cr3 from the user process it interrupts, allowing us to do-away with * two %cr3 stores, two atomic ops (pm_active is not modified), and pmap * lock tests (not needed since pm_active is not modified). * * Unfortunately, I couldn't really measure any result so for now the * optimization is disabled. */ #undef PREEMPT_OPTIMIZE /* * LWP_SWITCH_OPTIMIZE * * This optimization attempted to avoid a %cr3 store and atomic op, and * it might have been useful on older cpus but newer cpus (and more * importantly multi-core cpus) generally do not switch between LWPs on * the same cpu. Multiple user threads are more likely to be distributed * across multiple cpus. In cpu-bound situations the scheduler will already * be in batch-mode (meaning relatively few context-switches/sec), and * otherwise the lwp(s) are likely to be blocked waiting for events. * * On the flip side, the conditionals this option uses measurably reduce * performance (just slightly, honestly). So this option is disabled. */ #undef LWP_SWITCH_OPTIMIZE /* * Global Declarations */ .data .globl panic .globl lwkt_switch_return #if defined(SWTCH_OPTIM_STATS) .globl swtch_optim_stats, tlb_flush_count swtch_optim_stats: .long 0 /* number of _swtch_optims */ tlb_flush_count: .long 0 #endif /* * Code */ .text /* * cpu_heavy_switch(struct thread *next_thread) * * Switch from the current thread to a new thread. This entry * is normally called via the thread->td_switch function, and will * only be called when the current thread is a heavy weight process. * * Some instructions have been reordered to reduce pipeline stalls. * * YYY disable interrupts once giant is removed. */ ENTRY(cpu_heavy_switch) /* * Save RIP, RSP and callee-saved registers (RBX, RBP, R12-R15). */ movq PCPU(curthread),%rcx /* On top of the stack is the return adress. */ movq (%rsp),%rax /* (reorder optimization) */ movq TD_PCB(%rcx),%rdx /* RDX = PCB */ movq %rax,PCB_RIP(%rdx) /* return PC may be modified */ movq %rbx,PCB_RBX(%rdx) movq %rsp,PCB_RSP(%rdx) movq %rbp,PCB_RBP(%rdx) movq %r12,PCB_R12(%rdx) movq %r13,PCB_R13(%rdx) movq %r14,PCB_R14(%rdx) movq %r15,PCB_R15(%rdx) /* * Clear the cpu bit in the pmap active mask. The restore * function will set the bit in the pmap active mask. * * If we are switching away due to a preempt, TD_PREEMPTED(%rdi) * will be non-NULL. In this situation we do want to avoid extra * atomic ops and %cr3 reloads (see top of file for reasoning). * * NOTE: Do not try to optimize avoiding the %cr3 reload or pm_active * adjustment. This mattered on uni-processor systems but in * multi-core systems we are highly unlikely to be switching * to another thread belonging to the same process on this cpu. * * (more likely the target thread is still sleeping, or if cpu- * bound the scheduler is in batch mode and the switch rate is * already low). */ movq %rcx,%rbx /* RBX = oldthread */ #ifdef PREEMPT_OPTIMIZE /* * If we are being preempted the target thread borrows our %cr3 * and we leave our pmap bits intact for the duration. */ movq TD_PREEMPTED(%rdi),%r13 testq %r13,%r13 jne 2f #endif movq TD_LWP(%rcx),%rcx /* RCX = oldlwp */ movq LWP_VMSPACE(%rcx), %rcx /* RCX = oldvmspace */ #ifdef LWP_SWITCH_OPTIMIZE movq TD_LWP(%rdi),%r13 /* R13 = newlwp */ testq %r13,%r13 /* might not be a heavy */ jz 1f cmpq LWP_VMSPACE(%r13),%rcx /* same vmspace? */ je 2f 1: #endif movq PCPU(cpumask_simple),%rsi movq PCPU(cpumask_offset),%r12 xorq $-1,%rsi MPLOCKED andq %rsi, VM_PMAP+PM_ACTIVE(%rcx, %r12, 1) 2: /* * Push the LWKT switch restore function, which resumes a heavy * weight process. Note that the LWKT switcher is based on * TD_SP, while the heavy weight process switcher is based on * PCB_RSP. TD_SP is usually two ints pushed relative to * PCB_RSP. We push the flags for later restore by cpu_heavy_restore. */ pushfq cli movq $cpu_heavy_restore, %rax pushq %rax movq %rsp,TD_SP(%rbx) /* * Save debug regs if necessary */ movq PCB_FLAGS(%rdx),%rax andq $PCB_DBREGS,%rax jz 1f /* no, skip over */ movq %dr7,%rax /* yes, do the save */ movq %rax,PCB_DR7(%rdx) /* JG correct value? */ andq $0x0000fc00, %rax /* disable all watchpoints */ movq %rax,%dr7 movq %dr6,%rax movq %rax,PCB_DR6(%rdx) movq %dr3,%rax movq %rax,PCB_DR3(%rdx) movq %dr2,%rax movq %rax,PCB_DR2(%rdx) movq %dr1,%rax movq %rax,PCB_DR1(%rdx) movq %dr0,%rax movq %rax,PCB_DR0(%rdx) 1: /* * Save the FP state if we have used the FP. Note that calling * npxsave will NULL out PCPU(npxthread). */ cmpq %rbx,PCPU(npxthread) jne 1f movq %rdi,%r12 /* save %rdi. %r12 is callee-saved */ movq TD_SAVEFPU(%rbx),%rdi call npxsave /* do it in a big C function */ movq %r12,%rdi /* restore %rdi */ 1: /* * Switch to the next thread, which was passed as an argument * to cpu_heavy_switch(). The argument is in %rdi. * Set the current thread, load the stack pointer, * and 'ret' into the switch-restore function. * * The switch restore function expects the new thread to be in %rax * and the old one to be in %rbx. * * There is a one-instruction window where curthread is the new * thread but %rsp still points to the old thread's stack, but * we are protected by a critical section so it is ok. */ movq %rdi,%rax /* RAX = newtd, RBX = oldtd */ movq %rax,PCPU(curthread) movq TD_SP(%rax),%rsp ret END(cpu_heavy_switch) /* * cpu_exit_switch(struct thread *next) * * The switch function is changed to this when a thread is going away * for good. We have to ensure that the MMU state is not cached, and * we don't bother saving the existing thread state before switching. * * At this point we are in a critical section and this cpu owns the * thread's token, which serves as an interlock until the switchout is * complete. */ ENTRY(cpu_exit_switch) #ifdef PREEMPT_OPTIMIZE /* * If we were preempting we are switching back to the original thread. * In this situation we already have the original thread's %cr3 and * should not replace it! */ testl $TDF_PREEMPT_DONE, TD_FLAGS(%rdi) jne 1f #endif /* * Get us out of the vmspace */ movq KPML4phys,%rcx movq %cr3,%rax cmpq %rcx,%rax je 1f movq %rcx,%cr3 1: movq PCPU(curthread),%rbx /* * If this is a process/lwp, deactivate the pmap after we've * switched it out. */ movq TD_LWP(%rbx),%rcx testq %rcx,%rcx jz 2f movq LWP_VMSPACE(%rcx), %rcx /* RCX = vmspace */ movq PCPU(cpumask_simple),%rax movq PCPU(cpumask_offset),%r12 xorq $-1,%rax MPLOCKED andq %rax, VM_PMAP+PM_ACTIVE(%rcx, %r12, 1) 2: /* * Switch to the next thread. RET into the restore function, which * expects the new thread in RAX and the old in RBX. * * There is a one-instruction window where curthread is the new * thread but %rsp still points to the old thread's stack, but * we are protected by a critical section so it is ok. */ cli movq %rdi,%rax movq %rax,PCPU(curthread) movq TD_SP(%rax),%rsp ret END(cpu_exit_switch) /* * cpu_heavy_restore() (current thread in %rax on entry, old thread in %rbx) * * We immediately move %rax to %r12. %rbx is retained throughout, and * we nominally use %r14 for TD_PCB(%r12) until near the end where we * switch to %rdx for that. * * Restore the thread after an LWKT switch. This entry is normally * called via the LWKT switch restore function, which was pulled * off the thread stack and jumped to. * * This entry is only called if the thread was previously saved * using cpu_heavy_switch() (the heavy weight process thread switcher), * or when a new process is initially scheduled. * * NOTE: The lwp may be in any state, not necessarily LSRUN, because * a preemption switch may interrupt the process and then return via * cpu_heavy_restore. * * YYY theoretically we do not have to restore everything here, a lot * of this junk can wait until we return to usermode. But for now * we restore everything. * * YYY the PCB crap is really crap, it makes startup a bitch because * we can't switch away. * * YYY note: spl check is done in mi_switch when it splx()'s. */ ENTRY(cpu_heavy_restore) movq %rax,%r12 /* R12 = newtd */ movq TD_PCB(%rax),%r14 /* R14 = PCB */ movq %r14, PCPU(trampoline)+TR_PCB_RSP movq PCB_FLAGS(%r14), %rcx movq %rcx, PCPU(trampoline)+TR_PCB_FLAGS movq PCB_CR3_ISO(%r14), %rcx movq %rcx, PCPU(trampoline)+TR_PCB_CR3_ISO movq PCB_CR3(%r14), %rcx movq %rcx, PCPU(trampoline)+TR_PCB_CR3 popfq #if defined(SWTCH_OPTIM_STATS) incl _swtch_optim_stats #endif #ifdef PREEMPT_OPTIMIZE /* * If restoring our thread after a preemption has returned to * us, our %cr3 and pmap were borrowed and are being returned to * us and no further action on those items need be taken. */ testl $TDF_PREEMPT_DONE, TD_FLAGS(%r12) jne 4f #endif /* * Tell the pmap that our cpu is using the VMSPACE now. We cannot * safely test/reload %cr3 until after we have set the bit in the * pmap. * * We must do an interlocked test of the CPULOCK_EXCL at the same * time. If found to be set we will have to wait for it to clear * and then do a forced reload of %cr3 (even if the value matches). * * XXX When switching between two LWPs sharing the same vmspace * the cpu_heavy_switch() code currently avoids clearing the * cpu bit in PM_ACTIVE. So if the bit is already set we can * avoid checking for the interlock via CPULOCK_EXCL. We currently * do not perform this optimization. */ movq TD_LWP(%r12),%rcx movq LWP_VMSPACE(%rcx),%rcx /* RCX = vmspace */ movq PCPU(cpumask_simple),%rsi movq PCPU(cpumask_offset),%r13 MPLOCKED orq %rsi, VM_PMAP+PM_ACTIVE(%rcx, %r13, 1) movl VM_PMAP+PM_ACTIVE_LOCK(%rcx),%esi testl $CPULOCK_EXCL,%esi jz 1f movq %rcx,%rdi /* (found to be set) */ call pmap_interlock_wait /* pmap_interlock_wait(%rdi:vm) */ /* * Need unconditional load cr3 */ movq PCB_CR3(%r14),%rcx /* RCX = desired CR3 */ jmp 2f /* unconditional reload */ 1: /* * Restore the MMU address space. If it is the same as the last * thread we don't have to invalidate the tlb (i.e. reload cr3). * * XXX Temporary cludge, do NOT do this optimization! The problem * is that the pm_active bit for the cpu had dropped for a small * period of time, just a few cycles, but even one cycle is long * enough for some other cpu doing a pmap invalidation to not see * our cpu. * * When that happens, and we don't invltlb (by loading %cr3), we * wind up with a stale TLB. */ movq %cr3,%rsi /* RSI = current CR3 */ movq PCB_CR3(%r14),%rcx /* RCX = desired CR3 */ cmpq %rsi,%rcx /*je 4f*/ 2: #if defined(SWTCH_OPTIM_STATS) decl _swtch_optim_stats incl _tlb_flush_count #endif movq %rcx,%cr3 4: /* * NOTE: %rbx is the previous thread and %r12 is the new thread. * %rbx is retained throughout so we can return it. * * lwkt_switch[_return] is responsible for handling TDF_RUNNING. */ /* * Deal with the PCB extension, restore the private tss */ movq PCB_EXT(%r14),%rdi /* check for a PCB extension */ movq $1,%rcx /* maybe mark use of a private tss */ testq %rdi,%rdi #if 0 /* JG */ jnz 2f #endif #if 0 /* * Going back to the common_tss. (this was already executed at * the top). * * Set the top of the supervisor stack for the new thread * in gd_thread_pcb so the trampoline code can load it into %rsp. */ movq %r14, PCPU(trampoline)+TR_PCB_RSP movq PCB_FLAGS(%r14), %rcx movq %rcx, PCPU(trampoline)+TR_PCB_FLAGS movq PCB_CR3_ISO(%r14), %rcx movq %rcx, PCPU(trampoline)+TR_PCB_CR3_ISO movq PCB_CR3(%r14), %rcx movq %rcx, PCPU(trampoline)+TR_PCB_CR3 #endif #if 0 /* JG */ cmpl $0,PCPU(private_tss) /* don't have to reload if */ je 3f /* already using the common TSS */ /* JG? */ subq %rcx,%rcx /* unmark use of private tss */ /* * Get the address of the common TSS descriptor for the ltr. * There is no way to get the address of a segment-accessed variable * so we store a self-referential pointer at the base of the per-cpu * data area and add the appropriate offset. */ /* JG movl? */ movq $gd_common_tssd, %rdi /* JG name for "%gs:0"? */ addq %gs:0, %rdi /* * Move the correct TSS descriptor into the GDT slot, then reload * ltr. */ 2: /* JG */ movl %rcx,PCPU(private_tss) /* mark/unmark private tss */ movq PCPU(tss_gdt), %rbx /* entry in GDT */ movq 0(%rdi), %rax movq %rax, 0(%rbx) movl $GPROC0_SEL*8, %esi /* GSEL(entry, SEL_KPL) */ ltr %si #endif 3: /* * Restore the user %gs and %fs */ movq PCB_FSBASE(%r14),%r9 cmpq PCPU(user_fs),%r9 je 4f movq %r9,PCPU(user_fs) movl $MSR_FSBASE,%ecx movl PCB_FSBASE(%r14),%eax movl PCB_FSBASE+4(%r14),%edx wrmsr 4: movq PCB_GSBASE(%r14),%r9 cmpq PCPU(user_gs),%r9 je 5f movq %r9,PCPU(user_gs) movl $MSR_KGSBASE,%ecx /* later swapgs moves it to GSBASE */ movl PCB_GSBASE(%r14),%eax movl PCB_GSBASE+4(%r14),%edx wrmsr 5: /* * Actively restore FP state */ movq PCPU(npxthread),%r13 testq %r13,%r13 jnz 6f movl TD_FLAGS(%r12),%r13d andq $TDF_USINGFP,%r13 jz 6f movq %r12,%rdi /* npxdna_quick(newtd) */ call npxdna_quick 6: /* * Restore general registers. %rbx is restored later. * * Switch our PCB register from %r14 to %rdx so we can restore * %r14. */ movq %r14,%rdx movq PCB_RSP(%rdx), %rsp movq PCB_RBP(%rdx), %rbp movq PCB_R12(%rdx), %r12 movq PCB_R13(%rdx), %r13 movq PCB_R14(%rdx), %r14 movq PCB_R15(%rdx), %r15 movq PCB_RIP(%rdx), %rax movq %rax, (%rsp) movw $KDSEL,%ax movw %ax,%es #if 0 /* JG */ /* * Restore the user LDT if we have one */ cmpl $0, PCB_USERLDT(%edx) jnz 1f movl _default_ldt,%eax cmpl PCPU(currentldt),%eax je 2f lldt _default_ldt movl %eax,PCPU(currentldt) jmp 2f 1: pushl %edx call set_user_ldt popl %edx 2: #endif #if 0 /* JG */ /* * Restore the user TLS if we have one */ pushl %edx call set_user_TLS popl %edx #endif /* * Restore the DEBUG register state if necessary. */ movq PCB_FLAGS(%rdx),%rax andq $PCB_DBREGS,%rax jz 1f /* no, skip over */ movq PCB_DR6(%rdx),%rax /* yes, do the restore */ movq %rax,%dr6 movq PCB_DR3(%rdx),%rax movq %rax,%dr3 movq PCB_DR2(%rdx),%rax movq %rax,%dr2 movq PCB_DR1(%rdx),%rax movq %rax,%dr1 movq PCB_DR0(%rdx),%rax movq %rax,%dr0 movq %dr7,%rax /* load dr7 so as not to disturb */ /* JG correct value? */ andq $0x0000fc00,%rax /* reserved bits */ /* JG we've got more registers on x86_64 */ movq PCB_DR7(%rdx),%rcx /* JG correct value? */ andq $~0x0000fc00,%rcx orq %rcx,%rax movq %rax,%dr7 /* * Clear the QUICKRET flag when restoring a user process context * so we don't try to do a quick syscall return. */ 1: andl $~RQF_QUICKRET,PCPU(reqflags) movq %rbx,%rax movq PCB_RBX(%rdx),%rbx ret END(cpu_heavy_restore) /* * savectx(struct pcb *pcb) * * Update pcb, saving current processor state. */ ENTRY(savectx) /* fetch PCB */ /* JG use %rdi instead of %rcx everywhere? */ movq %rdi,%rcx /* caller's return address - child won't execute this routine */ movq (%rsp),%rax movq %rax,PCB_RIP(%rcx) movq %cr3,%rax movq %rax,PCB_CR3(%rcx) movq %rbx,PCB_RBX(%rcx) movq %rsp,PCB_RSP(%rcx) movq %rbp,PCB_RBP(%rcx) movq %r12,PCB_R12(%rcx) movq %r13,PCB_R13(%rcx) movq %r14,PCB_R14(%rcx) movq %r15,PCB_R15(%rcx) #if 1 /* * If npxthread == NULL, then the npx h/w state is irrelevant and the * state had better already be in the pcb. This is true for forks * but not for dumps (the old book-keeping with FP flags in the pcb * always lost for dumps because the dump pcb has 0 flags). * * If npxthread != NULL, then we have to save the npx h/w state to * npxthread's pcb and copy it to the requested pcb, or save to the * requested pcb and reload. Copying is easier because we would * have to handle h/w bugs for reloading. We used to lose the * parent's npx state for forks by forgetting to reload. */ movq PCPU(npxthread),%rax testq %rax,%rax jz 1f pushq %rcx /* target pcb */ movq TD_SAVEFPU(%rax),%rax /* originating savefpu area */ pushq %rax movq %rax,%rdi call npxsave popq %rax popq %rcx movq $PCB_SAVEFPU_SIZE,%rdx leaq PCB_SAVEFPU(%rcx),%rcx movq %rcx,%rsi movq %rax,%rdi call bcopy #endif 1: ret END(savectx) /* * cpu_idle_restore() (current thread in %rax on entry, old thread in %rbx) * (one-time entry) * * Don't bother setting up any regs other than %rbp so backtraces * don't die. This restore function is used to bootstrap into the * cpu_idle() LWKT only, after that cpu_lwkt_*() will be used for * switching. * * Clear TDF_RUNNING in old thread only after we've cleaned up %cr3. * This only occurs during system boot so no special handling is * required for migration. * * If we are an AP we have to call ap_init() before jumping to * cpu_idle(). ap_init() will synchronize with the BP and finish * setting up various ncpu-dependant globaldata fields. This may * happen on UP as well as SMP if we happen to be simulating multiple * cpus. */ ENTRY(cpu_idle_restore) /* cli */ movq KPML4phys,%rcx xorq %rbp,%rbp /* dummy frame pointer */ pushq $0 /* dummy return pc */ /* NOTE: idle thread can never preempt */ movq %rcx,%cr3 cmpl $0,PCPU(cpuid) je 1f andl $~TDF_RUNNING,TD_FLAGS(%rbx) orl $TDF_RUNNING,TD_FLAGS(%rax) /* manual, no switch_return */ call ap_init /* * ap_init can decide to enable interrupts early, but otherwise, or if * we are UP, do it here. */ sti jmp cpu_idle /* * cpu 0's idle thread entry for the first time must use normal * lwkt_switch_return() semantics or a pending cpu migration on * thread0 will deadlock. */ 1: sti pushq %rax movq %rbx,%rdi call lwkt_switch_return popq %rax jmp cpu_idle END(cpu_idle_restore) /* * cpu_kthread_restore() (current thread is %rax on entry, previous is %rbx) * (one-time execution) * * Don't bother setting up any regs other then %rbp so backtraces * don't die. This restore function is used to bootstrap into an * LWKT based kernel thread only. cpu_lwkt_switch() will be used * after this. * * Because this switch target does not 'return' to lwkt_switch() * we have to call lwkt_switch_return(otd) to clean up otd. * otd is in %ebx. * * Since all of our context is on the stack we are reentrant and * we can release our critical section and enable interrupts early. */ ENTRY(cpu_kthread_restore) sti movq KPML4phys,%rcx movq TD_PCB(%rax),%r13 xorq %rbp,%rbp #ifdef PREEMPT_OPTIMIZE /* * If we are preempting someone we borrow their %cr3, do not overwrite * it! */ movq TD_PREEMPTED(%rax),%r14 testq %r14,%r14 jne 1f #endif movq %rcx,%cr3 1: /* * rax and rbx come from the switchout code. Call * lwkt_switch_return(otd). * * NOTE: unlike i386, the %rsi and %rdi are not call-saved regs. */ pushq %rax movq %rbx,%rdi call lwkt_switch_return popq %rax decl TD_CRITCOUNT(%rax) movq PCB_R12(%r13),%rdi /* argument to RBX function */ movq PCB_RBX(%r13),%rax /* thread function */ /* note: top of stack return address inherited by function */ jmp *%rax END(cpu_kthread_restore) /* * cpu_lwkt_switch(struct thread *) * * Standard LWKT switching function. Only non-scratch registers are * saved and we don't bother with the MMU state or anything else. * * This function is always called while in a critical section. * * There is a one-instruction window where curthread is the new * thread but %rsp still points to the old thread's stack, but * we are protected by a critical section so it is ok. */ ENTRY(cpu_lwkt_switch) pushq %rbp /* JG note: GDB hacked to locate ebp rel to td_sp */ pushq %rbx movq PCPU(curthread),%rbx /* becomes old thread in restore */ pushq %r12 pushq %r13 pushq %r14 pushq %r15 pushfq cli #if 1 /* * Save the FP state if we have used the FP. Note that calling * npxsave will NULL out PCPU(npxthread). * * We have to deal with the FP state for LWKT threads in case they * happen to get preempted or block while doing an optimized * bzero/bcopy/memcpy. */ cmpq %rbx,PCPU(npxthread) jne 1f movq %rdi,%r12 /* save %rdi. %r12 is callee-saved */ movq TD_SAVEFPU(%rbx),%rdi call npxsave /* do it in a big C function */ movq %r12,%rdi /* restore %rdi */ 1: #endif movq %rdi,%rax /* switch to this thread */ pushq $cpu_lwkt_restore movq %rsp,TD_SP(%rbx) /* * %rax contains new thread, %rbx contains old thread. */ movq %rax,PCPU(curthread) movq TD_SP(%rax),%rsp ret END(cpu_lwkt_switch) /* * cpu_lwkt_restore() (current thread in %rax on entry) * * Standard LWKT restore function. This function is always called * while in a critical section. * * WARNING! Due to preemption the restore function can be used to 'return' * to the original thread. Interrupt disablement must be * protected through the switch so we cannot run splz here. */ ENTRY(cpu_lwkt_restore) #ifdef PREEMPT_OPTIMIZE /* * If we are preempting someone we borrow their %cr3 and pmap */ movq TD_PREEMPTED(%rax),%r14 /* kernel thread preempting? */ testq %r14,%r14 jne 1f /* yes, borrow %cr3 from old thread */ #endif /* * Don't reload %cr3 if it hasn't changed. Since this is a LWKT * thread (a kernel thread), and the kernel_pmap always permanently * sets all pm_active bits, we don't have the same problem with it * that we do with process pmaps. */ movq KPML4phys,%rcx movq %cr3,%rdx cmpq %rcx,%rdx je 1f movq %rcx,%cr3 1: /* * NOTE: %rbx is the previous thread and %rax is the new thread. * %rbx is retained throughout so we can return it. * * lwkt_switch[_return] is responsible for handling TDF_RUNNING. */ movq %rbx,%rax popfq popq %r15 popq %r14 popq %r13 popq %r12 popq %rbx popq %rbp ret END(cpu_lwkt_restore) |