cpu_switch.S revision 112993
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 112993 2003-04-02 23:53:30Z peter $ 37 */ 38 39#include "opt_npx.h" 40#include "opt_swtch.h" 41 42#include <machine/asmacros.h> 43 44#include "assym.s" 45 46/*****************************************************************************/ 47/* Scheduling */ 48/*****************************************************************************/ 49 50 .text 51 52/* 53 * cpu_throw() 54 * 55 * This is the second half of cpu_swtch(). It is used when the current 56 * thread is either a dummy or slated to die, and we no longer care 57 * about its state. This is only a slight optimization and is probably 58 * not worth it anymore. Note that we need to clear the pm_active bits so 59 * we do need the old proc if it still exists. 60 * 0(%esp) = ret 61 * 4(%esp) = oldtd 62 * 8(%esp) = newtd 63 */ 64ENTRY(cpu_throw) 65 movl PCPU(CPUID), %esi 66 movl 4(%esp),%ecx /* Old thread */ 67 testl %ecx,%ecx /* no thread? */ 68 jz 1f 69 /* release bit from old pm_active */ 70 movl TD_PROC(%ecx), %eax /* thread->td_proc */ 71 movl P_VMSPACE(%eax), %ebx /* proc->p_vmspace */ 72#ifdef SMP 73 lock 74#endif 75 btrl %esi, VM_PMAP+PM_ACTIVE(%ebx) /* clear old */ 761: 77 movl 8(%esp),%ecx /* New thread */ 78 movl TD_PCB(%ecx),%edx 79#ifdef SWTCH_OPTIM_STATS 80 incl tlb_flush_count 81#endif 82 movl PCB_CR3(%edx),%eax 83 movl %eax,%cr3 /* new address space */ 84 /* set bit in new pm_active */ 85 movl TD_PROC(%ecx),%eax 86 movl P_VMSPACE(%eax), %ebx 87#ifdef SMP 88 lock 89#endif 90 btsl %esi, VM_PMAP+PM_ACTIVE(%ebx) /* set new */ 91 jmp sw1 92 93/* 94 * cpu_switch(old, new) 95 * 96 * Save the current thread state, then select the next thread to run 97 * and load its state. 98 * 0(%esp) = ret 99 * 4(%esp) = oldtd 100 * 8(%esp) = newtd 101 */ 102ENTRY(cpu_switch) 103 104 /* Switch to new thread. First, save context. */ 105 movl 4(%esp),%ecx 106 107#ifdef INVARIANTS 108 testl %ecx,%ecx /* no thread? */ 109 jz badsw2 /* no, panic */ 110#endif 111 112 movl TD_PCB(%ecx),%edx 113 114 movl (%esp),%eax /* Hardware registers */ 115 movl %eax,PCB_EIP(%edx) 116 movl %ebx,PCB_EBX(%edx) 117 movl %esp,PCB_ESP(%edx) 118 movl %ebp,PCB_EBP(%edx) 119 movl %esi,PCB_ESI(%edx) 120 movl %edi,PCB_EDI(%edx) 121 movl %gs,PCB_GS(%edx) 122 pushfl /* PSL */ 123 popl PCB_PSL(%edx) 124 125 /* Test if debug registers should be saved. */ 126 testl $PCB_DBREGS,PCB_FLAGS(%edx) 127 jz 1f /* no, skip over */ 128 movl %dr7,%eax /* yes, do the save */ 129 movl %eax,PCB_DR7(%edx) 130 andl $0x0000fc00, %eax /* disable all watchpoints */ 131 movl %eax,%dr7 132 movl %dr6,%eax 133 movl %eax,PCB_DR6(%edx) 134 movl %dr3,%eax 135 movl %eax,PCB_DR3(%edx) 136 movl %dr2,%eax 137 movl %eax,PCB_DR2(%edx) 138 movl %dr1,%eax 139 movl %eax,PCB_DR1(%edx) 140 movl %dr0,%eax 141 movl %eax,PCB_DR0(%edx) 1421: 143 144#ifdef DEV_NPX 145 /* have we used fp, and need a save? */ 146 cmpl %ecx,PCPU(FPCURTHREAD) 147 jne 1f 148 addl $PCB_SAVEFPU,%edx /* h/w bugs make saving complicated */ 149 pushl %edx 150 call npxsave /* do it in a big C function */ 151 popl %eax 1521: 153#endif 154 155 /* Save is done. Now fire up new thread. Leave old vmspace. */ 156 movl %ecx,%edi 157 movl 8(%esp),%ecx /* New thread */ 158#ifdef INVARIANTS 159 testl %ecx,%ecx /* no thread? */ 160 jz badsw3 /* no, panic */ 161#endif 162 movl TD_PCB(%ecx),%edx 163 movl PCPU(CPUID), %esi 164 165 /* switch address space */ 166 movl PCB_CR3(%edx),%eax 167#ifdef LAZY_SWITCH 168 cmpl $0,lazy_flush_enable 169 je 1f 170 cmpl %eax,IdlePTD /* Kernel address space? */ 171#ifdef SWTCH_OPTIM_STATS 172 je 3f 173#else 174 je sw1 175#endif 1761: 177 movl %cr3,%ebx /* The same address space? */ 178 cmpl %ebx,%eax 179#ifdef SWTCH_OPTIM_STATS 180 je 2f /* Yes, skip all that cruft */ 181#else 182 je sw1 183#endif 184#endif 185 186#ifdef SWTCH_OPTIM_STATS 187 incl tlb_flush_count 188#endif 189 movl %eax,%cr3 /* new address space */ 190 191 /* Release bit from old pmap->pm_active */ 192 movl TD_PROC(%edi), %eax /* oldproc */ 193 movl P_VMSPACE(%eax), %ebx 194#ifdef SMP 195 lock 196#endif 197 btrl %esi, VM_PMAP+PM_ACTIVE(%ebx) /* clear old */ 198 199 /* Set bit in new pmap->pm_active */ 200 movl TD_PROC(%ecx),%eax /* newproc */ 201 movl P_VMSPACE(%eax), %ebx 202#ifdef SMP 203 lock 204#endif 205 btsl %esi, VM_PMAP+PM_ACTIVE(%ebx) /* set new */ 206 207#ifdef LAZY_SWITCH 208#ifdef SWTCH_OPTIM_STATS 209 jmp sw1 210 2112: /* same address space */ 212 incl swtch_optim_stats 213 jmp sw1 214 2153: /* kernel address space */ 216 incl lazy_flush_count 217#endif 218#endif 219 220sw1: 221 /* 222 * At this point, we've switched address spaces and are ready 223 * to load up the rest of the next context. 224 */ 225 cmpl $0, PCB_EXT(%edx) /* has pcb extension? */ 226 je 1f /* If not, use the default */ 227 btsl %esi, private_tss /* mark use of private tss */ 228 movl PCB_EXT(%edx), %edi /* new tss descriptor */ 229 jmp 2f /* Load it up */ 230 2311: /* 232 * Use the common default TSS instead of our own. 233 * Set our stack pointer into the TSS, it's set to just 234 * below the PCB. In C, common_tss.tss_esp0 = &pcb - 16; 235 */ 236 leal -16(%edx), %ebx /* leave space for vm86 */ 237 movl %ebx, PCPU(COMMON_TSS) + TSS_ESP0 238 239 /* 240 * Test this CPU's bit in the bitmap to see if this 241 * CPU was using a private TSS. 242 */ 243 btrl %esi, private_tss /* Already using the common? */ 244 jae 3f /* if so, skip reloading */ 245 PCPU_ADDR(COMMON_TSSD, %edi) 2462: 247 /* Move correct tss descriptor into GDT slot, then reload tr. */ 248 movl PCPU(TSS_GDT), %ebx /* entry in GDT */ 249 movl 0(%edi), %eax 250 movl %eax, 0(%ebx) 251 movl 4(%edi), %eax 252 movl %eax, 4(%ebx) 253 movl $GPROC0_SEL*8, %esi /* GSEL(entry, SEL_KPL) */ 254 ltr %si 2553: 256 257 /* Restore context. */ 258 movl PCB_EBX(%edx),%ebx 259 movl PCB_ESP(%edx),%esp 260 movl PCB_EBP(%edx),%ebp 261 movl PCB_ESI(%edx),%esi 262 movl PCB_EDI(%edx),%edi 263 movl PCB_EIP(%edx),%eax 264 movl %eax,(%esp) 265 pushl PCB_PSL(%edx) 266 popfl 267 268 movl %edx, PCPU(CURPCB) 269 movl %ecx, PCPU(CURTHREAD) /* into next thread */ 270 271 /* 272 * Determine the LDT to use and load it if is the default one and 273 * that is not the current one. 274 */ 275 movl TD_PROC(%ecx),%eax 276 cmpl $0,P_MD+MD_LDT(%eax) 277 jnz 1f 278 movl _default_ldt,%eax 279 cmpl PCPU(CURRENTLDT),%eax 280 je 2f 281 lldt _default_ldt 282 movl %eax,PCPU(CURRENTLDT) 283 jmp 2f 2841: 285 /* Load the LDT when it is not the default one. */ 286 pushl %edx /* Preserve pointer to pcb. */ 287 addl $P_MD,%eax /* Pointer to mdproc is arg. */ 288 pushl %eax 289 call set_user_ldt 290 addl $4,%esp 291 popl %edx 2922: 293 294 /* This must be done after loading the user LDT. */ 295 .globl cpu_switch_load_gs 296cpu_switch_load_gs: 297 movl PCB_GS(%edx),%gs 298 299 /* Test if debug registers should be restored. */ 300 testl $PCB_DBREGS,PCB_FLAGS(%edx) 301 jz 1f 302 303 /* 304 * Restore debug registers. The special code for dr7 is to 305 * preserve the current values of its reserved bits. 306 */ 307 movl PCB_DR6(%edx),%eax 308 movl %eax,%dr6 309 movl PCB_DR3(%edx),%eax 310 movl %eax,%dr3 311 movl PCB_DR2(%edx),%eax 312 movl %eax,%dr2 313 movl PCB_DR1(%edx),%eax 314 movl %eax,%dr1 315 movl PCB_DR0(%edx),%eax 316 movl %eax,%dr0 317 movl %dr7,%eax 318 andl $0x0000fc00,%eax 319 movl PCB_DR7(%edx),%ecx 320 andl $~0x0000fc00,%ecx 321 orl %ecx,%eax 322 movl %eax,%dr7 3231: 324 ret 325 326#ifdef INVARIANTS 327badsw1: 328 pushal 329 pushl $sw0_1 330 call panic 331sw0_1: .asciz "cpu_throw: no newthread supplied" 332 333badsw2: 334 pushal 335 pushl $sw0_2 336 call panic 337sw0_2: .asciz "cpu_switch: no curthread supplied" 338 339badsw3: 340 pushal 341 pushl $sw0_3 342 call panic 343sw0_3: .asciz "cpu_switch: no newthread supplied" 344#endif 345 346/* 347 * savectx(pcb) 348 * Update pcb, saving current processor state. 349 */ 350ENTRY(savectx) 351 /* Fetch PCB. */ 352 movl 4(%esp),%ecx 353 354 /* Save caller's return address. Child won't execute this routine. */ 355 movl (%esp),%eax 356 movl %eax,PCB_EIP(%ecx) 357 358 movl %cr3,%eax 359 movl %eax,PCB_CR3(%ecx) 360 361 movl %ebx,PCB_EBX(%ecx) 362 movl %esp,PCB_ESP(%ecx) 363 movl %ebp,PCB_EBP(%ecx) 364 movl %esi,PCB_ESI(%ecx) 365 movl %edi,PCB_EDI(%ecx) 366 movl %gs,PCB_GS(%ecx) 367 pushfl 368 popl PCB_PSL(%ecx) 369 370#ifdef DEV_NPX 371 /* 372 * If fpcurthread == NULL, then the npx h/w state is irrelevant and the 373 * state had better already be in the pcb. This is true for forks 374 * but not for dumps (the old book-keeping with FP flags in the pcb 375 * always lost for dumps because the dump pcb has 0 flags). 376 * 377 * If fpcurthread != NULL, then we have to save the npx h/w state to 378 * fpcurthread's pcb and copy it to the requested pcb, or save to the 379 * requested pcb and reload. Copying is easier because we would 380 * have to handle h/w bugs for reloading. We used to lose the 381 * parent's npx state for forks by forgetting to reload. 382 */ 383 pushfl 384 cli 385 movl PCPU(FPCURTHREAD),%eax 386 testl %eax,%eax 387 je 1f 388 389 pushl %ecx 390 movl TD_PCB(%eax),%eax 391 leal PCB_SAVEFPU(%eax),%eax 392 pushl %eax 393 pushl %eax 394 call npxsave 395 addl $4,%esp 396 popl %eax 397 popl %ecx 398 399 pushl $PCB_SAVEFPU_SIZE 400 leal PCB_SAVEFPU(%ecx),%ecx 401 pushl %ecx 402 pushl %eax 403 call bcopy 404 addl $12,%esp 4051: 406 popfl 407#endif /* DEV_NPX */ 408 409 ret 410